@homeflare/alchemy 0.42.0 → 0.44.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 (648) 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/budget-errors.d.ts +36 -0
  17. package/dist/litellm/budget-errors.d.ts.map +1 -0
  18. package/dist/litellm/budget-form.d.ts +50 -0
  19. package/dist/litellm/budget-form.d.ts.map +1 -0
  20. package/dist/litellm/budget-operations.d.ts +20 -0
  21. package/dist/litellm/budget-operations.d.ts.map +1 -0
  22. package/dist/litellm/budget.d.ts +61 -0
  23. package/dist/litellm/budget.d.ts.map +1 -0
  24. package/dist/litellm/credential-errors.d.ts +131 -0
  25. package/dist/litellm/credential-errors.d.ts.map +1 -0
  26. package/dist/litellm/credential-form.d.ts +79 -0
  27. package/dist/litellm/credential-form.d.ts.map +1 -0
  28. package/dist/litellm/credential-operations.d.ts +84 -0
  29. package/dist/litellm/credential-operations.d.ts.map +1 -0
  30. package/dist/litellm/credential-rewrite.d.ts +6 -0
  31. package/dist/litellm/credential-rewrite.d.ts.map +1 -0
  32. package/dist/litellm/credential-types.d.ts +87 -0
  33. package/dist/litellm/credential-types.d.ts.map +1 -0
  34. package/dist/litellm/credential-values.d.ts +53 -0
  35. package/dist/litellm/credential-values.d.ts.map +1 -0
  36. package/dist/litellm/credential.d.ts +83 -0
  37. package/dist/litellm/credential.d.ts.map +1 -0
  38. package/dist/litellm/fake-access-group-litellm.d.ts +34 -0
  39. package/dist/litellm/fake-access-group-litellm.d.ts.map +1 -0
  40. package/dist/litellm/fake-credential-litellm.d.ts +54 -0
  41. package/dist/litellm/fake-credential-litellm.d.ts.map +1 -0
  42. package/dist/litellm/fake-keys.d.ts +53 -0
  43. package/dist/litellm/fake-keys.d.ts.map +1 -0
  44. package/dist/litellm/fake-litellm.d.ts +9 -1
  45. package/dist/litellm/fake-litellm.d.ts.map +1 -1
  46. package/dist/litellm/fake-mcp-litellm.d.ts +67 -0
  47. package/dist/litellm/fake-mcp-litellm.d.ts.map +1 -0
  48. package/dist/litellm/fake-model-litellm-post-update.d.ts +49 -0
  49. package/dist/litellm/fake-model-litellm-post-update.d.ts.map +1 -0
  50. package/dist/litellm/fake-model-litellm.d.ts +48 -0
  51. package/dist/litellm/fake-model-litellm.d.ts.map +1 -0
  52. package/dist/litellm/fake-policy-attachment-litellm.d.ts +33 -0
  53. package/dist/litellm/fake-policy-attachment-litellm.d.ts.map +1 -0
  54. package/dist/litellm/fake-policy-litellm.d.ts +32 -0
  55. package/dist/litellm/fake-policy-litellm.d.ts.map +1 -0
  56. package/dist/litellm/fake-registry-base.d.ts +42 -0
  57. package/dist/litellm/fake-registry-base.d.ts.map +1 -0
  58. package/dist/litellm/fake-stack.d.ts +8 -1
  59. package/dist/litellm/fake-stack.d.ts.map +1 -1
  60. package/dist/litellm/fake-team-litellm.d.ts +47 -0
  61. package/dist/litellm/fake-team-litellm.d.ts.map +1 -0
  62. package/dist/litellm/fake-tool-litellm.d.ts +28 -0
  63. package/dist/litellm/fake-tool-litellm.d.ts.map +1 -0
  64. package/dist/litellm/fake-toolset-litellm.d.ts +29 -0
  65. package/dist/litellm/fake-toolset-litellm.d.ts.map +1 -0
  66. package/dist/litellm/index.d.ts +20 -0
  67. package/dist/litellm/index.d.ts.map +1 -1
  68. package/dist/litellm/index.js +3201 -2
  69. package/dist/litellm/index.js.map +70 -5
  70. package/dist/litellm/key-errors.d.ts +169 -0
  71. package/dist/litellm/key-errors.d.ts.map +1 -0
  72. package/dist/litellm/key-form.d.ts +119 -0
  73. package/dist/litellm/key-form.d.ts.map +1 -0
  74. package/dist/litellm/key-harness.d.ts +41 -0
  75. package/dist/litellm/key-harness.d.ts.map +1 -0
  76. package/dist/litellm/key-metadata-errors.d.ts +30 -0
  77. package/dist/litellm/key-metadata-errors.d.ts.map +1 -0
  78. package/dist/litellm/key-metadata.d.ts +22 -0
  79. package/dist/litellm/key-metadata.d.ts.map +1 -0
  80. package/dist/litellm/key-operations.d.ts +63 -0
  81. package/dist/litellm/key-operations.d.ts.map +1 -0
  82. package/dist/litellm/key-secret.d.ts +33 -0
  83. package/dist/litellm/key-secret.d.ts.map +1 -0
  84. package/dist/litellm/key.d.ts +78 -0
  85. package/dist/litellm/key.d.ts.map +1 -0
  86. package/dist/litellm/mcp-server-credential.d.ts +50 -0
  87. package/dist/litellm/mcp-server-credential.d.ts.map +1 -0
  88. package/dist/litellm/mcp-server-errors.d.ts +101 -0
  89. package/dist/litellm/mcp-server-errors.d.ts.map +1 -0
  90. package/dist/litellm/mcp-server-form.d.ts +56 -0
  91. package/dist/litellm/mcp-server-form.d.ts.map +1 -0
  92. package/dist/litellm/mcp-server-locate.d.ts +26 -0
  93. package/dist/litellm/mcp-server-locate.d.ts.map +1 -0
  94. package/dist/litellm/mcp-server-operations.d.ts +63 -0
  95. package/dist/litellm/mcp-server-operations.d.ts.map +1 -0
  96. package/dist/litellm/mcp-server-shadow.d.ts +24 -0
  97. package/dist/litellm/mcp-server-shadow.d.ts.map +1 -0
  98. package/dist/litellm/mcp-server-types.d.ts +118 -0
  99. package/dist/litellm/mcp-server-types.d.ts.map +1 -0
  100. package/dist/litellm/mcp-server-url.d.ts +11 -0
  101. package/dist/litellm/mcp-server-url.d.ts.map +1 -0
  102. package/dist/litellm/mcp-server.d.ts +95 -0
  103. package/dist/litellm/mcp-server.d.ts.map +1 -0
  104. package/dist/litellm/model-claim.d.ts +24 -0
  105. package/dist/litellm/model-claim.d.ts.map +1 -0
  106. package/dist/litellm/model-credential.d.ts +12 -0
  107. package/dist/litellm/model-credential.d.ts.map +1 -0
  108. package/dist/litellm/model-errors.d.ts +119 -0
  109. package/dist/litellm/model-errors.d.ts.map +1 -0
  110. package/dist/litellm/model-form.d.ts +78 -0
  111. package/dist/litellm/model-form.d.ts.map +1 -0
  112. package/dist/litellm/model-locate.d.ts +13 -0
  113. package/dist/litellm/model-locate.d.ts.map +1 -0
  114. package/dist/litellm/model-operations.d.ts +59 -0
  115. package/dist/litellm/model-operations.d.ts.map +1 -0
  116. package/dist/litellm/model-types.d.ts +91 -0
  117. package/dist/litellm/model-types.d.ts.map +1 -0
  118. package/dist/litellm/model.d.ts +88 -0
  119. package/dist/litellm/model.d.ts.map +1 -0
  120. package/dist/litellm/operations.d.ts +22 -0
  121. package/dist/litellm/operations.d.ts.map +1 -1
  122. package/dist/litellm/policy-attachment-form.d.ts +19 -0
  123. package/dist/litellm/policy-attachment-form.d.ts.map +1 -0
  124. package/dist/litellm/policy-attachment-operations.d.ts +31 -0
  125. package/dist/litellm/policy-attachment-operations.d.ts.map +1 -0
  126. package/dist/litellm/policy-attachment-types.d.ts +48 -0
  127. package/dist/litellm/policy-attachment-types.d.ts.map +1 -0
  128. package/dist/litellm/policy-attachment.d.ts +61 -0
  129. package/dist/litellm/policy-attachment.d.ts.map +1 -0
  130. package/dist/litellm/policy-form.d.ts +22 -0
  131. package/dist/litellm/policy-form.d.ts.map +1 -0
  132. package/dist/litellm/policy-operations.d.ts +42 -0
  133. package/dist/litellm/policy-operations.d.ts.map +1 -0
  134. package/dist/litellm/policy-types.d.ts +57 -0
  135. package/dist/litellm/policy-types.d.ts.map +1 -0
  136. package/dist/litellm/policy.d.ts +65 -0
  137. package/dist/litellm/policy.d.ts.map +1 -0
  138. package/dist/litellm/providers.d.ts +1 -1
  139. package/dist/litellm/providers.d.ts.map +1 -1
  140. package/dist/litellm/registry-errors.d.ts +62 -0
  141. package/dist/litellm/registry-errors.d.ts.map +1 -0
  142. package/dist/litellm/registry-support.d.ts +42 -0
  143. package/dist/litellm/registry-support.d.ts.map +1 -0
  144. package/dist/litellm/team-form.d.ts +26 -0
  145. package/dist/litellm/team-form.d.ts.map +1 -0
  146. package/dist/litellm/team-members.d.ts +10 -0
  147. package/dist/litellm/team-members.d.ts.map +1 -0
  148. package/dist/litellm/team-operations.d.ts +38 -0
  149. package/dist/litellm/team-operations.d.ts.map +1 -0
  150. package/dist/litellm/team-permission.d.ts +35 -0
  151. package/dist/litellm/team-permission.d.ts.map +1 -0
  152. package/dist/litellm/team-types.d.ts +101 -0
  153. package/dist/litellm/team-types.d.ts.map +1 -0
  154. package/dist/litellm/team.d.ts +67 -0
  155. package/dist/litellm/team.d.ts.map +1 -0
  156. package/dist/litellm/tool-policy-form.d.ts +21 -0
  157. package/dist/litellm/tool-policy-form.d.ts.map +1 -0
  158. package/dist/litellm/tool-policy-operations.d.ts +30 -0
  159. package/dist/litellm/tool-policy-operations.d.ts.map +1 -0
  160. package/dist/litellm/tool-policy-types.d.ts +46 -0
  161. package/dist/litellm/tool-policy-types.d.ts.map +1 -0
  162. package/dist/litellm/tool-policy.d.ts +62 -0
  163. package/dist/litellm/tool-policy.d.ts.map +1 -0
  164. package/dist/litellm/toolset-form.d.ts +20 -0
  165. package/dist/litellm/toolset-form.d.ts.map +1 -0
  166. package/dist/litellm/toolset-operations.d.ts +39 -0
  167. package/dist/litellm/toolset-operations.d.ts.map +1 -0
  168. package/dist/litellm/toolset-types.d.ts +46 -0
  169. package/dist/litellm/toolset-types.d.ts.map +1 -0
  170. package/dist/litellm/toolset.d.ts +55 -0
  171. package/dist/litellm/toolset.d.ts.map +1 -0
  172. package/dist/openbao/auth-role-form.d.ts +9 -0
  173. package/dist/openbao/auth-role-form.d.ts.map +1 -1
  174. package/dist/openbao/auth-role-wire.d.ts.map +1 -1
  175. package/dist/openbao/fake-engines-roles.d.ts.map +1 -1
  176. package/dist/openbao/index.js +43 -6
  177. package/dist/openbao/index.js.map +5 -5
  178. package/dist/ownership/rows.d.ts +11 -0
  179. package/dist/ownership/rows.d.ts.map +1 -1
  180. package/dist/postgres/connection.d.ts +32 -2
  181. package/dist/postgres/connection.d.ts.map +1 -1
  182. package/dist/postgres/database-sql.d.ts +28 -9
  183. package/dist/postgres/database-sql.d.ts.map +1 -1
  184. package/dist/postgres/database.d.ts +1 -1
  185. package/dist/postgres/database.d.ts.map +1 -1
  186. package/dist/postgres/fake-grants-parse.d.ts +37 -0
  187. package/dist/postgres/fake-grants-parse.d.ts.map +1 -0
  188. package/dist/postgres/fake-grants-read.d.ts +61 -0
  189. package/dist/postgres/fake-grants-read.d.ts.map +1 -0
  190. package/dist/postgres/fake-grants-sql.d.ts +46 -0
  191. package/dist/postgres/fake-grants-sql.d.ts.map +1 -0
  192. package/dist/postgres/fake-grants-write.d.ts +29 -0
  193. package/dist/postgres/fake-grants-write.d.ts.map +1 -0
  194. package/dist/postgres/fake-role-sql.d.ts +40 -0
  195. package/dist/postgres/fake-role-sql.d.ts.map +1 -0
  196. package/dist/postgres/fake-schema-sql.d.ts +42 -0
  197. package/dist/postgres/fake-schema-sql.d.ts.map +1 -0
  198. package/dist/postgres/fake-sql-parse.d.ts +23 -0
  199. package/dist/postgres/fake-sql-parse.d.ts.map +1 -0
  200. package/dist/postgres/fake-sql-quote.d.ts +12 -0
  201. package/dist/postgres/fake-sql-quote.d.ts.map +1 -0
  202. package/dist/postgres/fake-sql.d.ts +42 -0
  203. package/dist/postgres/fake-sql.d.ts.map +1 -1
  204. package/dist/postgres/grants-attrs.d.ts +115 -0
  205. package/dist/postgres/grants-attrs.d.ts.map +1 -0
  206. package/dist/postgres/grants-declare.d.ts +88 -0
  207. package/dist/postgres/grants-declare.d.ts.map +1 -0
  208. package/dist/postgres/grants-diff.d.ts +25 -0
  209. package/dist/postgres/grants-diff.d.ts.map +1 -0
  210. package/dist/postgres/grants-errors.d.ts +137 -0
  211. package/dist/postgres/grants-errors.d.ts.map +1 -0
  212. package/dist/postgres/grants-existence.d.ts +14 -0
  213. package/dist/postgres/grants-existence.d.ts.map +1 -0
  214. package/dist/postgres/grants-ops.d.ts +65 -0
  215. package/dist/postgres/grants-ops.d.ts.map +1 -0
  216. package/dist/postgres/grants-ownership.d.ts +18 -0
  217. package/dist/postgres/grants-ownership.d.ts.map +1 -0
  218. package/dist/postgres/grants-plan-restore.d.ts +22 -0
  219. package/dist/postgres/grants-plan-restore.d.ts.map +1 -0
  220. package/dist/postgres/grants-plan.d.ts +65 -0
  221. package/dist/postgres/grants-plan.d.ts.map +1 -0
  222. package/dist/postgres/grants-read-defaults.d.ts +17 -0
  223. package/dist/postgres/grants-read-defaults.d.ts.map +1 -0
  224. package/dist/postgres/grants-read.d.ts +77 -0
  225. package/dist/postgres/grants-read.d.ts.map +1 -0
  226. package/dist/postgres/grants-refuse.d.ts +25 -0
  227. package/dist/postgres/grants-refuse.d.ts.map +1 -0
  228. package/dist/postgres/grants-sql.d.ts +42 -0
  229. package/dist/postgres/grants-sql.d.ts.map +1 -0
  230. package/dist/postgres/grants-words.d.ts +29 -0
  231. package/dist/postgres/grants-words.d.ts.map +1 -0
  232. package/dist/postgres/grants.d.ts +37 -0
  233. package/dist/postgres/grants.d.ts.map +1 -0
  234. package/dist/postgres/index.d.ts +35 -8
  235. package/dist/postgres/index.d.ts.map +1 -1
  236. package/dist/postgres/index.js +2063 -39
  237. package/dist/postgres/index.js.map +47 -7
  238. package/dist/postgres/installed-classifier.d.ts +13 -0
  239. package/dist/postgres/installed-classifier.d.ts.map +1 -0
  240. package/dist/postgres/providers.d.ts +17 -7
  241. package/dist/postgres/providers.d.ts.map +1 -1
  242. package/dist/postgres/psql-executor.d.ts +25 -0
  243. package/dist/postgres/psql-executor.d.ts.map +1 -0
  244. package/dist/postgres/role-attrs.d.ts +109 -0
  245. package/dist/postgres/role-attrs.d.ts.map +1 -0
  246. package/dist/postgres/role-diff.d.ts +22 -0
  247. package/dist/postgres/role-diff.d.ts.map +1 -0
  248. package/dist/postgres/role-errors.d.ts +126 -0
  249. package/dist/postgres/role-errors.d.ts.map +1 -0
  250. package/dist/postgres/role-membership-sql.d.ts +75 -0
  251. package/dist/postgres/role-membership-sql.d.ts.map +1 -0
  252. package/dist/postgres/role-provider.d.ts +11 -0
  253. package/dist/postgres/role-provider.d.ts.map +1 -0
  254. package/dist/postgres/role-scram.d.ts +6 -0
  255. package/dist/postgres/role-scram.d.ts.map +1 -0
  256. package/dist/postgres/role-secrets.d.ts +30 -0
  257. package/dist/postgres/role-secrets.d.ts.map +1 -0
  258. package/dist/postgres/role-sql.d.ts +68 -0
  259. package/dist/postgres/role-sql.d.ts.map +1 -0
  260. package/dist/postgres/role.d.ts +57 -0
  261. package/dist/postgres/role.d.ts.map +1 -0
  262. package/dist/postgres/schema-assert.d.ts +50 -0
  263. package/dist/postgres/schema-assert.d.ts.map +1 -0
  264. package/dist/postgres/schema-attrs.d.ts +51 -0
  265. package/dist/postgres/schema-attrs.d.ts.map +1 -0
  266. package/dist/postgres/schema-delete.d.ts +28 -0
  267. package/dist/postgres/schema-delete.d.ts.map +1 -0
  268. package/dist/postgres/schema-diff.d.ts +14 -0
  269. package/dist/postgres/schema-diff.d.ts.map +1 -0
  270. package/dist/postgres/schema-errors.d.ts +139 -0
  271. package/dist/postgres/schema-errors.d.ts.map +1 -0
  272. package/dist/postgres/schema-identity.d.ts +8 -0
  273. package/dist/postgres/schema-identity.d.ts.map +1 -0
  274. package/dist/postgres/schema-reconcile.d.ts +12 -0
  275. package/dist/postgres/schema-reconcile.d.ts.map +1 -0
  276. package/dist/postgres/schema-sql.d.ts +45 -0
  277. package/dist/postgres/schema-sql.d.ts.map +1 -0
  278. package/dist/postgres/schema-test-kit.d.ts +67 -0
  279. package/dist/postgres/schema-test-kit.d.ts.map +1 -0
  280. package/dist/postgres/schema.d.ts +54 -0
  281. package/dist/postgres/schema.d.ts.map +1 -0
  282. package/dist/proxmox/ceph-flag-types.d.ts +57 -0
  283. package/dist/proxmox/ceph-flag-types.d.ts.map +1 -0
  284. package/dist/proxmox/ceph-flag-wire.d.ts +77 -0
  285. package/dist/proxmox/ceph-flag-wire.d.ts.map +1 -0
  286. package/dist/proxmox/ceph-flag.d.ts +15 -50
  287. package/dist/proxmox/ceph-flag.d.ts.map +1 -1
  288. package/dist/proxmox/ceph-osd-write.d.ts +10 -13
  289. package/dist/proxmox/ceph-osd-write.d.ts.map +1 -1
  290. package/dist/proxmox/index.js +806 -740
  291. package/dist/proxmox/index.js.map +9 -8
  292. package/dist/release/fake-engine.d.ts +10 -0
  293. package/dist/release/fake-engine.d.ts.map +1 -1
  294. package/dist/release/index.js +35 -1
  295. package/dist/release/index.js.map +3 -3
  296. package/dist/valkey/acl-attrs.d.ts +94 -0
  297. package/dist/valkey/acl-attrs.d.ts.map +1 -0
  298. package/dist/valkey/acl-compare.d.ts +28 -0
  299. package/dist/valkey/acl-compare.d.ts.map +1 -0
  300. package/dist/valkey/acl-connection.d.ts +6 -0
  301. package/dist/valkey/acl-connection.d.ts.map +1 -0
  302. package/dist/valkey/acl-form.d.ts +62 -0
  303. package/dist/valkey/acl-form.d.ts.map +1 -0
  304. package/dist/valkey/acl-managed.d.ts +4 -0
  305. package/dist/valkey/acl-managed.d.ts.map +1 -0
  306. package/dist/valkey/acl-ops.d.ts +37 -0
  307. package/dist/valkey/acl-ops.d.ts.map +1 -0
  308. package/dist/valkey/acl-profiles.d.ts +53 -0
  309. package/dist/valkey/acl-profiles.d.ts.map +1 -0
  310. package/dist/valkey/acl-reconcile.d.ts +26 -0
  311. package/dist/valkey/acl-reconcile.d.ts.map +1 -0
  312. package/dist/valkey/acl.d.ts +24 -0
  313. package/dist/valkey/acl.d.ts.map +1 -0
  314. package/dist/valkey/connection-service.d.ts +22 -0
  315. package/dist/valkey/connection-service.d.ts.map +1 -0
  316. package/dist/valkey/connection.d.ts +59 -0
  317. package/dist/valkey/connection.d.ts.map +1 -0
  318. package/dist/valkey/errors.d.ts +165 -0
  319. package/dist/valkey/errors.d.ts.map +1 -0
  320. package/dist/valkey/fake-acl.d.ts +29 -0
  321. package/dist/valkey/fake-acl.d.ts.map +1 -0
  322. package/dist/valkey/fake-valkey.d.ts +35 -0
  323. package/dist/valkey/fake-valkey.d.ts.map +1 -0
  324. package/dist/valkey/handler-test-fixture.d.ts +20 -0
  325. package/dist/valkey/handler-test-fixture.d.ts.map +1 -0
  326. package/dist/valkey/index.d.ts +18 -0
  327. package/dist/valkey/index.d.ts.map +1 -0
  328. package/dist/valkey/index.js +964 -0
  329. package/dist/valkey/index.js.map +27 -0
  330. package/dist/valkey/instance-attrs.d.ts +46 -0
  331. package/dist/valkey/instance-attrs.d.ts.map +1 -0
  332. package/dist/valkey/instance-form.d.ts +22 -0
  333. package/dist/valkey/instance-form.d.ts.map +1 -0
  334. package/dist/valkey/instance.d.ts +48 -0
  335. package/dist/valkey/instance.d.ts.map +1 -0
  336. package/dist/valkey/provider-socket-fixture.d.ts +8 -0
  337. package/dist/valkey/provider-socket-fixture.d.ts.map +1 -0
  338. package/dist/valkey/providers.d.ts +16 -0
  339. package/dist/valkey/providers.d.ts.map +1 -0
  340. package/dist/valkey/resp.d.ts +38 -0
  341. package/dist/valkey/resp.d.ts.map +1 -0
  342. package/dist/valkey/transport-error.d.ts +36 -0
  343. package/dist/valkey/transport-error.d.ts.map +1 -0
  344. package/dist/valkey/transport.d.ts +50 -0
  345. package/dist/valkey/transport.d.ts.map +1 -0
  346. package/docs/litellm-access-group.md +105 -0
  347. package/docs/litellm-budget.md +55 -0
  348. package/docs/litellm-credential.md +60 -0
  349. package/docs/litellm-key-metadata.md +56 -0
  350. package/docs/litellm-key.md +194 -0
  351. package/docs/litellm-mcp.md +187 -0
  352. package/docs/litellm-model.md +111 -0
  353. package/docs/litellm-policy-attachment.md +86 -0
  354. package/docs/litellm-policy.md +103 -0
  355. package/docs/litellm-team.md +117 -0
  356. package/docs/litellm-tool-policy.md +90 -0
  357. package/docs/litellm-toolset.md +92 -0
  358. package/docs/litellm.md +16 -8
  359. package/docs/peers.md +15 -6
  360. package/docs/postgres-grants-example.md +55 -0
  361. package/docs/postgres-grants.md +157 -0
  362. package/docs/postgres-role.md +114 -0
  363. package/docs/postgres-schema.md +139 -0
  364. package/docs/postgres.md +39 -0
  365. package/docs/proxmox-sdk-migration.md +45 -0
  366. package/docs/valkey-transport.md +29 -0
  367. package/docs/valkey.md +187 -0
  368. package/package.json +7 -3
  369. package/src/litellm/access-group-form.ts +105 -0
  370. package/src/litellm/access-group-operations.ts +92 -0
  371. package/src/litellm/access-group-types.ts +49 -0
  372. package/src/litellm/access-group.test.ts +197 -0
  373. package/src/litellm/access-group.ts +151 -0
  374. package/src/litellm/budget-errors.ts +38 -0
  375. package/src/litellm/budget-form.ts +94 -0
  376. package/src/litellm/budget-operations.ts +51 -0
  377. package/src/litellm/budget.test.ts +105 -0
  378. package/src/litellm/budget.ts +127 -0
  379. package/src/litellm/credential-errors.ts +183 -0
  380. package/src/litellm/credential-form.test.ts +151 -0
  381. package/src/litellm/credential-form.ts +211 -0
  382. package/src/litellm/credential-guards.test.ts +241 -0
  383. package/src/litellm/credential-operations.ts +208 -0
  384. package/src/litellm/credential-recovery.test.ts +93 -0
  385. package/src/litellm/credential-rewrite.test.ts +164 -0
  386. package/src/litellm/credential-rewrite.ts +50 -0
  387. package/src/litellm/credential-types.ts +117 -0
  388. package/src/litellm/credential-values.ts +94 -0
  389. package/src/litellm/credential.test.ts +218 -0
  390. package/src/litellm/credential.ts +227 -0
  391. package/src/litellm/fake-access-group-litellm.ts +127 -0
  392. package/src/litellm/fake-credential-litellm.ts +216 -0
  393. package/src/litellm/fake-keys.ts +190 -0
  394. package/src/litellm/fake-litellm.ts +66 -7
  395. package/src/litellm/fake-mcp-litellm.ts +214 -0
  396. package/src/litellm/fake-model-litellm-post-update.ts +103 -0
  397. package/src/litellm/fake-model-litellm.ts +233 -0
  398. package/src/litellm/fake-policy-attachment-litellm.ts +97 -0
  399. package/src/litellm/fake-policy-litellm.ts +186 -0
  400. package/src/litellm/fake-registry-base.ts +69 -0
  401. package/src/litellm/fake-stack.ts +17 -1
  402. package/src/litellm/fake-team-litellm.ts +229 -0
  403. package/src/litellm/fake-tool-litellm.ts +88 -0
  404. package/src/litellm/fake-toolset-litellm.ts +110 -0
  405. package/src/litellm/index.ts +153 -0
  406. package/src/litellm/key-delete.test.ts +212 -0
  407. package/src/litellm/key-errors.ts +241 -0
  408. package/src/litellm/key-form.ts +200 -0
  409. package/src/litellm/key-harness.ts +121 -0
  410. package/src/litellm/key-metadata-errors.ts +49 -0
  411. package/src/litellm/key-metadata.test.ts +156 -0
  412. package/src/litellm/key-metadata.ts +74 -0
  413. package/src/litellm/key-operations.ts +184 -0
  414. package/src/litellm/key-rotation.test.ts +110 -0
  415. package/src/litellm/key-scope.test.ts +135 -0
  416. package/src/litellm/key-secret.test.ts +248 -0
  417. package/src/litellm/key-secret.ts +137 -0
  418. package/src/litellm/key-transport.test.ts +166 -0
  419. package/src/litellm/key.test.ts +231 -0
  420. package/src/litellm/key.ts +213 -0
  421. package/src/litellm/mcp-server-adopt.test.ts +86 -0
  422. package/src/litellm/mcp-server-alias.test.ts +184 -0
  423. package/src/litellm/mcp-server-credential.ts +85 -0
  424. package/src/litellm/mcp-server-errors.ts +141 -0
  425. package/src/litellm/mcp-server-exports.test.ts +15 -0
  426. package/src/litellm/mcp-server-form.test.ts +241 -0
  427. package/src/litellm/mcp-server-form.ts +233 -0
  428. package/src/litellm/mcp-server-handlers.test.ts +150 -0
  429. package/src/litellm/mcp-server-locate.ts +66 -0
  430. package/src/litellm/mcp-server-operations.ts +111 -0
  431. package/src/litellm/mcp-server-registry.test.ts +146 -0
  432. package/src/litellm/mcp-server-retype.test.ts +114 -0
  433. package/src/litellm/mcp-server-shadow.test.ts +91 -0
  434. package/src/litellm/mcp-server-shadow.ts +45 -0
  435. package/src/litellm/mcp-server-tools.test.ts +85 -0
  436. package/src/litellm/mcp-server-types.ts +143 -0
  437. package/src/litellm/mcp-server-url.ts +66 -0
  438. package/src/litellm/mcp-server.test.ts +213 -0
  439. package/src/litellm/mcp-server.ts +225 -0
  440. package/src/litellm/model-claim.ts +69 -0
  441. package/src/litellm/model-credential.ts +45 -0
  442. package/src/litellm/model-errors.ts +168 -0
  443. package/src/litellm/model-foreign-row.test.ts +173 -0
  444. package/src/litellm/model-form.test.ts +228 -0
  445. package/src/litellm/model-form.ts +223 -0
  446. package/src/litellm/model-locate.test.ts +128 -0
  447. package/src/litellm/model-locate.ts +67 -0
  448. package/src/litellm/model-operations.test.ts +93 -0
  449. package/src/litellm/model-operations.ts +146 -0
  450. package/src/litellm/model-params-preserve.test.ts +119 -0
  451. package/src/litellm/model-recovery.test.ts +57 -0
  452. package/src/litellm/model-types.ts +112 -0
  453. package/src/litellm/model.test.ts +222 -0
  454. package/src/litellm/model.ts +237 -0
  455. package/src/litellm/operations.ts +1 -1
  456. package/src/litellm/policy-attachment-form.ts +103 -0
  457. package/src/litellm/policy-attachment-operations.ts +74 -0
  458. package/src/litellm/policy-attachment-types.ts +48 -0
  459. package/src/litellm/policy-attachment.test.ts +198 -0
  460. package/src/litellm/policy-attachment.ts +178 -0
  461. package/src/litellm/policy-form.ts +122 -0
  462. package/src/litellm/policy-operations.ts +112 -0
  463. package/src/litellm/policy-types.ts +58 -0
  464. package/src/litellm/policy.test.ts +214 -0
  465. package/src/litellm/policy.ts +146 -0
  466. package/src/litellm/providers.ts +25 -1
  467. package/src/litellm/registry-errors.ts +88 -0
  468. package/src/litellm/registry-exports.test.ts +66 -0
  469. package/src/litellm/registry-support.ts +109 -0
  470. package/src/litellm/team-form.ts +140 -0
  471. package/src/litellm/team-members.test.ts +150 -0
  472. package/src/litellm/team-members.ts +58 -0
  473. package/src/litellm/team-operations.ts +90 -0
  474. package/src/litellm/team-permission.ts +159 -0
  475. package/src/litellm/team-read.test.ts +89 -0
  476. package/src/litellm/team-types.ts +106 -0
  477. package/src/litellm/team.test.ts +206 -0
  478. package/src/litellm/team.ts +159 -0
  479. package/src/litellm/tool-policy-form.ts +94 -0
  480. package/src/litellm/tool-policy-operations.ts +58 -0
  481. package/src/litellm/tool-policy-types.ts +48 -0
  482. package/src/litellm/tool-policy.test.ts +172 -0
  483. package/src/litellm/tool-policy.ts +125 -0
  484. package/src/litellm/toolset-form.ts +104 -0
  485. package/src/litellm/toolset-operations.ts +90 -0
  486. package/src/litellm/toolset-types.ts +47 -0
  487. package/src/litellm/toolset.test.ts +198 -0
  488. package/src/litellm/toolset.ts +151 -0
  489. package/src/openbao/auth-role-form.test.ts +37 -0
  490. package/src/openbao/auth-role-form.ts +12 -1
  491. package/src/openbao/auth-role-wire.test.ts +1 -0
  492. package/src/openbao/auth-role-wire.ts +13 -1
  493. package/src/openbao/distilled-retry.test.ts +1 -0
  494. package/src/openbao/fake-engines-roles.ts +7 -1
  495. package/src/ownership/ownership.test.ts +33 -1
  496. package/src/ownership/rows.ts +53 -0
  497. package/src/postgres/connection.ts +72 -3
  498. package/src/postgres/database-bigint-serialization.test.ts +1 -0
  499. package/src/postgres/database-refusals.test.ts +2 -2
  500. package/src/postgres/database-sql.ts +40 -12
  501. package/src/postgres/database.ts +3 -2
  502. package/src/postgres/fake-grants-parse.ts +189 -0
  503. package/src/postgres/fake-grants-read.ts +250 -0
  504. package/src/postgres/fake-grants-sql.ts +207 -0
  505. package/src/postgres/fake-grants-write.ts +90 -0
  506. package/src/postgres/fake-role-sql.ts +220 -0
  507. package/src/postgres/fake-schema-semantics.test.ts +36 -0
  508. package/src/postgres/fake-schema-sql.ts +146 -0
  509. package/src/postgres/fake-sql-parse.ts +86 -0
  510. package/src/postgres/fake-sql-quote.ts +36 -0
  511. package/src/postgres/fake-sql.ts +161 -46
  512. package/src/postgres/fixtures/pg-REL_18_6-acl.h.txt +147 -0
  513. package/src/postgres/fixtures/pg-REL_18_6-aclexplode-func.txt +31 -0
  514. package/src/postgres/fixtures/pg-REL_18_6-alter-default-privileges-synopsis.txt +82 -0
  515. package/src/postgres/fixtures/pg-REL_18_6-alter_role.sgml.txt +44 -0
  516. package/src/postgres/fixtures/pg-REL_18_6-create_role.sgml.txt +26 -0
  517. package/src/postgres/fixtures/pg-REL_18_6-grant-synopsis.txt +95 -0
  518. package/src/postgres/fixtures/pg-REL_18_6-pg_attribute.h.txt +24 -0
  519. package/src/postgres/fixtures/pg-REL_18_6-pg_auth_members.h.txt +62 -0
  520. package/src/postgres/fixtures/pg-REL_18_6-pg_authid.h.txt +70 -0
  521. package/src/postgres/fixtures/pg-REL_18_6-pg_class.h.txt +33 -0
  522. package/src/postgres/fixtures/pg-REL_18_6-pg_default_acl.h.txt +81 -0
  523. package/src/postgres/fixtures/pg-REL_18_6-pg_namespace.h.txt +28 -0
  524. package/src/postgres/fixtures/pg-REL_18_6-revoke-synopsis.txt +124 -0
  525. package/src/postgres/grants-acl.test.ts +61 -0
  526. package/src/postgres/grants-attrs.ts +185 -0
  527. package/src/postgres/grants-database.test.ts +158 -0
  528. package/src/postgres/grants-declare.ts +172 -0
  529. package/src/postgres/grants-delete-proof.test.ts +154 -0
  530. package/src/postgres/grants-diff.ts +101 -0
  531. package/src/postgres/grants-errors.ts +215 -0
  532. package/src/postgres/grants-existence-guards.test.ts +113 -0
  533. package/src/postgres/grants-existence.ts +101 -0
  534. package/src/postgres/grants-lifecycle-guards.test.ts +208 -0
  535. package/src/postgres/grants-lifecycle-repair.test.ts +208 -0
  536. package/src/postgres/grants-lifecycle.test.ts +181 -0
  537. package/src/postgres/grants-ops.ts +229 -0
  538. package/src/postgres/grants-owner-revoke.test.ts +121 -0
  539. package/src/postgres/grants-ownership.ts +49 -0
  540. package/src/postgres/grants-plan-collateral.test.ts +202 -0
  541. package/src/postgres/grants-plan-diff.test.ts +204 -0
  542. package/src/postgres/grants-plan-restore.ts +43 -0
  543. package/src/postgres/grants-plan-revocations.test.ts +69 -0
  544. package/src/postgres/grants-plan.test.ts +148 -0
  545. package/src/postgres/grants-plan.ts +245 -0
  546. package/src/postgres/grants-provenance.test.ts +134 -0
  547. package/src/postgres/grants-psql-lifecycle.test.ts +105 -0
  548. package/src/postgres/grants-read-absent.test.ts +91 -0
  549. package/src/postgres/grants-read-defaults.ts +51 -0
  550. package/src/postgres/grants-read.ts +250 -0
  551. package/src/postgres/grants-refusals.test.ts +136 -0
  552. package/src/postgres/grants-refuse.ts +119 -0
  553. package/src/postgres/grants-relkind-wire.test.ts +111 -0
  554. package/src/postgres/grants-repair-plan.test.ts +201 -0
  555. package/src/postgres/grants-sql.test.ts +128 -0
  556. package/src/postgres/grants-sql.ts +144 -0
  557. package/src/postgres/grants-words.ts +34 -0
  558. package/src/postgres/grants.ts +110 -0
  559. package/src/postgres/index.ts +137 -7
  560. package/src/postgres/installed-classifier.ts +36 -0
  561. package/src/postgres/providers.ts +44 -7
  562. package/src/postgres/psql-executor-escaping.test.ts +67 -0
  563. package/src/postgres/psql-executor.test.ts +167 -0
  564. package/src/postgres/psql-executor.ts +170 -0
  565. package/src/postgres/role-adopt.test.ts +98 -0
  566. package/src/postgres/role-attrs.ts +134 -0
  567. package/src/postgres/role-diff.ts +89 -0
  568. package/src/postgres/role-errors.ts +198 -0
  569. package/src/postgres/role-guard.test.ts +60 -0
  570. package/src/postgres/role-identity.test.ts +185 -0
  571. package/src/postgres/role-lifecycle.test.ts +228 -0
  572. package/src/postgres/role-membership-sql.ts +198 -0
  573. package/src/postgres/role-password.test.ts +158 -0
  574. package/src/postgres/role-privilege.test.ts +80 -0
  575. package/src/postgres/role-provenance.test.ts +200 -0
  576. package/src/postgres/role-provider.ts +104 -0
  577. package/src/postgres/role-refusals.test.ts +250 -0
  578. package/src/postgres/role-scram.test.ts +38 -0
  579. package/src/postgres/role-scram.ts +38 -0
  580. package/src/postgres/role-secrets.ts +46 -0
  581. package/src/postgres/role-sql.ts +211 -0
  582. package/src/postgres/role-valid-until.test.ts +68 -0
  583. package/src/postgres/role.ts +218 -0
  584. package/src/postgres/schema-assert.ts +139 -0
  585. package/src/postgres/schema-attrs.ts +71 -0
  586. package/src/postgres/schema-delete-proofs.test.ts +192 -0
  587. package/src/postgres/schema-delete.ts +96 -0
  588. package/src/postgres/schema-diff.ts +44 -0
  589. package/src/postgres/schema-drop-2bp01.test.ts +146 -0
  590. package/src/postgres/schema-errors.ts +209 -0
  591. package/src/postgres/schema-handlers.test.ts +193 -0
  592. package/src/postgres/schema-identity.ts +39 -0
  593. package/src/postgres/schema-lifecycle.test.ts +246 -0
  594. package/src/postgres/schema-race.test.ts +70 -0
  595. package/src/postgres/schema-reconcile.ts +100 -0
  596. package/src/postgres/schema-refusals.test.ts +144 -0
  597. package/src/postgres/schema-socket.test.ts +161 -0
  598. package/src/postgres/schema-sql.ts +130 -0
  599. package/src/postgres/schema-takeover.test.ts +149 -0
  600. package/src/postgres/schema-test-kit.ts +85 -0
  601. package/src/postgres/schema.ts +130 -0
  602. package/src/proxmox/ceph-flag-types.ts +70 -0
  603. package/src/proxmox/ceph-flag-wire.test.ts +72 -0
  604. package/src/proxmox/ceph-flag-wire.ts +123 -0
  605. package/src/proxmox/ceph-flag.ts +77 -130
  606. package/src/proxmox/ceph-osd-write.ts +10 -13
  607. package/src/release/binary-alias.test.ts +3 -3
  608. package/src/release/binary-claims.test.ts +3 -3
  609. package/src/release/binary-directory-umask.test.ts +65 -0
  610. package/src/release/fake-engine.ts +15 -0
  611. package/src/valkey/acl-attrs.ts +98 -0
  612. package/src/valkey/acl-compare.ts +57 -0
  613. package/src/valkey/acl-connection.ts +13 -0
  614. package/src/valkey/acl-delete.test.ts +127 -0
  615. package/src/valkey/acl-form.ts +239 -0
  616. package/src/valkey/acl-managed.ts +13 -0
  617. package/src/valkey/acl-ops.ts +122 -0
  618. package/src/valkey/acl-ownership.test.ts +118 -0
  619. package/src/valkey/acl-prefix.test.ts +38 -0
  620. package/src/valkey/acl-profiles.ts +81 -0
  621. package/src/valkey/acl-reconcile.ts +180 -0
  622. package/src/valkey/acl-reserved.test.ts +69 -0
  623. package/src/valkey/acl-revocation.test.ts +94 -0
  624. package/src/valkey/acl-seat-prefix.test.ts +111 -0
  625. package/src/valkey/acl-validation.test.ts +54 -0
  626. package/src/valkey/acl.test.ts +215 -0
  627. package/src/valkey/acl.ts +169 -0
  628. package/src/valkey/connection-service.ts +46 -0
  629. package/src/valkey/connection.ts +180 -0
  630. package/src/valkey/errors.ts +239 -0
  631. package/src/valkey/fake-acl.ts +128 -0
  632. package/src/valkey/fake-valkey.ts +224 -0
  633. package/src/valkey/handler-test-fixture.ts +43 -0
  634. package/src/valkey/handlers.test.ts +205 -0
  635. package/src/valkey/index.ts +54 -0
  636. package/src/valkey/instance-attrs.ts +48 -0
  637. package/src/valkey/instance-form.ts +104 -0
  638. package/src/valkey/instance.test.ts +108 -0
  639. package/src/valkey/instance.ts +160 -0
  640. package/src/valkey/provider-socket-fixture.ts +71 -0
  641. package/src/valkey/providers.test.ts +129 -0
  642. package/src/valkey/providers.ts +21 -0
  643. package/src/valkey/resp.ts +212 -0
  644. package/src/valkey/transport-bounds.test.ts +150 -0
  645. package/src/valkey/transport-error.ts +37 -0
  646. package/src/valkey/transport.test.ts +158 -0
  647. package/src/valkey/transport.ts +109 -0
  648. package/src/valkey/upstream-bounds.test.ts +14 -0
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Apply-time ownership for `Postgres.Role`. The review of PR 336: `read` answers `Unowned` when
3
+ * the stack holds no state, but `reconcile` never called `refuseTakeover`, so a live role found
4
+ * between plan and apply (or one Alchemy skipped probing because a prop was still an Output) was
5
+ * altered and then recorded as ours.
6
+ *
7
+ * These tests construct `postgresRoleHandlers` — the lifecycle tests drive `reconcileWithClient`
8
+ * directly and cannot see this hole. The recording fake is reached the way the engine reaches a
9
+ * real cluster: `withPg` over the runner transport.
10
+ */
11
+ import { describe, expect, test } from 'bun:test';
12
+ import * as Effect from 'effect/Effect';
13
+ import { postgresRunnerConnection } from './connection.ts';
14
+ import { parseLiteral } from './fake-sql-quote.ts';
15
+ import { makeFakeSql } from './fake-sql.ts';
16
+ import { type PsqlRunner } from './psql-executor.ts';
17
+ import { postgresRoleHandlers } from './role-provider.ts';
18
+ import type { PostgresRoleAttributes, PostgresRoleProps } from './role-attrs.ts';
19
+
20
+ const baseProps: PostgresRoleProps = {
21
+ name: 'seat-observability',
22
+ login: false,
23
+ connectionLimit: 10,
24
+ inherit: true,
25
+ };
26
+
27
+ const liveRole = (): PostgresRoleAttributes => ({
28
+ name: 'seat-observability',
29
+ oid: 20000,
30
+ login: true,
31
+ connectionLimit: -1,
32
+ inherit: false,
33
+ validUntil: null,
34
+ memberOf: ['hf_agent'],
35
+ passwordSeal: '',
36
+ });
37
+
38
+ /**
39
+ * A runner that answers the handler's `psql` calls from the recording fake. `withPg` wraps a
40
+ * `SELECT` in `json_agg` and the executor inlines `$1` before the runner sees it, so this
41
+ * unwraps that envelope and binds the literal back to the param the fake's catalog read expects.
42
+ */
43
+ const runnerFor =
44
+ (fake: ReturnType<typeof makeFakeSql>): PsqlRunner =>
45
+ async ({ stdin }) => {
46
+ const wrapped = /^SELECT coalesce\(json_agg\(t\), '\[\]'::json\)::text FROM \((.*)\) t;$/s.exec(
47
+ stdin,
48
+ );
49
+ const body = (wrapped?.[1] ?? stdin).replace(/;\s*$/, '');
50
+ const bound = /^([\s\S]*) = (E'[\s\S]*')$/.exec(body);
51
+ const sql = bound === null ? body : `${bound[1]} = $1`;
52
+ const params = bound === null ? [] : [parseLiteral(bound[2] as string)];
53
+ const rows = await Effect.runPromise(fake.unsafe(sql, params));
54
+ return { code: 0, stdout: JSON.stringify(rows), stderr: '' };
55
+ };
56
+
57
+ const writes = (fake: ReturnType<typeof makeFakeSql>) =>
58
+ fake.statements.filter(
59
+ (s) =>
60
+ s.text.startsWith('ALTER') ||
61
+ s.text.startsWith('GRANT') ||
62
+ s.text.startsWith('REVOKE') ||
63
+ s.text.startsWith('CREATE'),
64
+ );
65
+
66
+ describe('adopt-off takeover', () => {
67
+ test('a live role and adopt-off produces no statement', async () => {
68
+ const fake = makeFakeSql({ roleRows: [liveRole()] });
69
+ fake.memberships.add('seat-observability\0hf_agent');
70
+ const refused = await Effect.runPromise(
71
+ postgresRoleHandlers
72
+ .reconcile({
73
+ id: 'seat-observability',
74
+ fqn: 'Postgres.Role/seat-observability',
75
+ instanceId: 'creating-1',
76
+ news: baseProps,
77
+ output: undefined,
78
+ olds: baseProps,
79
+ session: {} as never,
80
+ bindings: [],
81
+ })
82
+ .pipe(
83
+ Effect.provide(
84
+ postgresRunnerConnection({
85
+ run: runnerFor(fake),
86
+ database: 'postgres',
87
+ username: 'postgres',
88
+ }),
89
+ ),
90
+ Effect.exit,
91
+ ),
92
+ );
93
+ // refuseTakeover dies: a takeover is a defect, not a typed error the engine retries.
94
+ const rendered = refused._tag === 'Failure' ? String(refused.cause) : JSON.stringify(refused);
95
+ expect(rendered).toContain('already exists');
96
+ expect(writes(fake)).toEqual([]);
97
+ });
98
+ });
@@ -0,0 +1,134 @@
1
+ /**
2
+ * `Postgres.Role` props and attributes, and the plan-time checks that run before any statement.
3
+ *
4
+ * ⛔ EVERY ASSERTED PROP TRACES TO A REAL `CREATE ROLE` / `ALTER ROLE` OPTION AND A REAL CATALOG
5
+ * COLUMN. `role-provenance.test.ts` parses the committed fixtures (`create_role.sgml`,
6
+ * `alter_role.sgml`, `pg_authid.h`, `pg_auth_members.h`) and fails if that mapping breaks.
7
+ * ⛔ NO PASSWORD VALUE AS A PROP (S25). `password` is declared as
8
+ * `{ fromEnv: 'PG_SEAT_WIDGET_PASSWORD' }`, never a string. The name lives in state; the value
9
+ * is resolved from the deploying process's environment at reconcile time, wrapped in
10
+ * `Redacted`, and quoted into the one statement that needs it.
11
+ * ★ UNLIKE `Postgres.Database`, THIS FAMILY IS ALTER-CAPABLE: `LOGIN`/`NOLOGIN`,
12
+ * `INHERIT`/`NOINHERIT`, `CONNECTION LIMIT` and `VALID UNTIL` all have a safe atomic
13
+ * `ALTER ROLE` form (`alter_role.sgml`'s first synopsis variant), so a drifted live row is
14
+ * brought back to the declaration instead of refused.
15
+ */
16
+ import type { FromEnv } from '../secrets/write-only.ts';
17
+
18
+ /** Re-exported for tests that operate on identifier byte length. */
19
+ export { POSTGRES_NAME_MAX_BYTES, nameByteRefusal, utf8ByteLength } from './database-attrs.ts';
20
+
21
+ /**
22
+ * A declared `validUntil` must carry its own UTC designator (`Z`) or numeric offset (`+02:00`).
23
+ *
24
+ * ⛔ WHY A ZONE-FREE TIMESTAMP IS REFUSED AT PLAN. PostgreSQL reads `VALID UNTIL '2027-01-01'`
25
+ * as midnight in the SESSION's time zone; the deploying process parses the same string as UTC
26
+ * (ECMAScript's rule for date-only forms). The instants differ, so the value read back would
27
+ * never equal the declaration, and every plan would issue the same `ALTER ROLE … VALID UNTIL`
28
+ * forever — the exact silent loop this family refuses elsewhere. A string ECMAScript cannot
29
+ * parse at all reaches the same refusal; PostgreSQL would reject it too, only later and as an
30
+ * unclassified statement error.
31
+ */
32
+ const VALID_UNTIL_OFFSET = /(?:Z|[+-]\d{2}:\d{2})$/;
33
+
34
+ export const validUntilRefusal = (
35
+ value: string,
36
+ ): { readonly reason: 'unparseable' | 'zone-free' } | undefined => {
37
+ // A zone suffix is not a timestamp. `2027-13-01T00:00:00Z` and `not-a-dateZ` match the
38
+ // offset and `Date.parse` to NaN; accepting them sends a VALID UNTIL PostgreSQL rejects, or
39
+ // one that never compares equal on read-back, so every later reconcile re-issues the ALTER.
40
+ if (Number.isNaN(Date.parse(value))) return { reason: 'unparseable' };
41
+ if (VALID_UNTIL_OFFSET.test(value)) return undefined;
42
+ return { reason: 'zone-free' };
43
+ };
44
+
45
+ /** Whether a declared `validUntil` and the live serialised value name the same instant. Both
46
+ * sides are plan-checked parseable, so `NaN` cannot reach here. */
47
+ export const sameValidUntil = (declared: string, live: string | null): boolean =>
48
+ live !== null && Date.parse(declared) === Date.parse(live);
49
+
50
+ /**
51
+ * Declared properties of one role.
52
+ *
53
+ * `login` and `inherit` are required: the resource configures a LOGIN or NOLOGIN role with
54
+ * INHERIT explicit, per the seat-wiring spec's group-role-per-seat shape (`NOLOGIN`, member of
55
+ * `hf_agent`, owning its own schema). An undeclared boolean would silently adopt whatever the
56
+ * live row already says — the opposite of idempotent.
57
+ */
58
+ export interface PostgresRoleProps {
59
+ /** At most 63 UTF-8 bytes — refused at plan/reconcile if larger (`PostgresRoleNameRefused`). */
60
+ readonly name: string;
61
+ /** `LOGIN` or `NOLOGIN` — maps to `pg_roles.rolcanlogin`. */
62
+ readonly login: boolean;
63
+ /** `CONNECTION LIMIT n`, `-1` for unlimited — maps to `pg_roles.rolconnlimit`. */
64
+ readonly connectionLimit: number;
65
+ /** `INHERIT` or `NOINHERIT` — maps to `pg_roles.rolinherit`. */
66
+ readonly inherit: boolean;
67
+ /**
68
+ * `VALID UNTIL 'timestamp'` — maps to `pg_roles.rolvaliduntil`. ISO 8601 without a timezone is
69
+ * accepted by PostgreSQL's `timestamptz` input (it is interpreted in the client's time zone);
70
+ * an ISO 8601 string carrying `Z` is unambiguous, so the docs recommend declaring it. Omitted
71
+ * means "leave the live value alone" — the resource never clears an expiry it did not declare.
72
+ */
73
+ readonly validUntil?: string;
74
+ /**
75
+ * The roles this role is a member of (`GRANT parent TO this`). Backed by `pg_auth_members`.
76
+ * Compared when declared; omitted leaves live memberships untouched. Empty array means
77
+ * "ensure none".
78
+ */
79
+ readonly memberOf?: readonly string[];
80
+ /**
81
+ * `PASSWORD '...'` by the NAME of the environment variable holding it. Omitted means "leave
82
+ * the live password alone" (and `NOLOGIN` group roles need none). Required once, before the
83
+ * first create that declares it, and on every later write while the variable is set — a value
84
+ * the environment stopped providing is never guessed.
85
+ */
86
+ readonly password?: FromEnv;
87
+ }
88
+
89
+ /** Live attributes, read back after every reconcile. `passwordValue` is NEVER stored (S25). */
90
+ export interface PostgresRoleAttributes {
91
+ readonly name: string;
92
+ readonly oid: number;
93
+ readonly login: boolean;
94
+ readonly connectionLimit: number;
95
+ readonly inherit: boolean;
96
+ /** ISO 8601 (`...Z`), server-side serialised; `null` when the live row has no expiry. */
97
+ readonly validUntil: string | null;
98
+ /**
99
+ * Catalog flags this family never declares. A create lands on the server default (all false).
100
+ * An adopt or alter that finds any of them true is refused — they are not inherited, so a
101
+ * member who `SET ROLE`s to this role would exercise them. Absent on a seeded test row means
102
+ * false, the default a `CREATE ROLE` without the option lands on.
103
+ */
104
+ readonly superuser?: boolean;
105
+ readonly createrole?: boolean;
106
+ readonly createdb?: boolean;
107
+ readonly replication?: boolean;
108
+ readonly bypassrls?: boolean;
109
+ /**
110
+ * Parent role names, sorted. Empty array when the role is a member of nothing. Null means
111
+ * "memberships were not declared on this resource", so the field is not asserted.
112
+ */
113
+ readonly memberOf: readonly string[] | null;
114
+ /**
115
+ * The membership rows behind `memberOf`, including `admin_option` and `set_option`. Present
116
+ * on a catalog read; stripped before attributes are stored (`storedAttributes`), because the
117
+ * engine's diff receives stored attributes and would never see them there. The provider's
118
+ * diff reads these rows live. `grantor` is the role that made the grant
119
+ * (`pg_auth_members.grantor`), null when that role was dropped — a dropped-grantor row cannot
120
+ * be named in `REVOKE … GRANTED BY`, so reconcile can only fail typed on it.
121
+ */
122
+ readonly memberships?: readonly {
123
+ readonly parent: string;
124
+ readonly grantor: string | null;
125
+ readonly admin: boolean;
126
+ readonly set: boolean;
127
+ }[];
128
+ /**
129
+ * A seal of the last password value written, or `''` if none was ever written (`seal()` from
130
+ * `secrets/write-only.ts`). Lets a later reconcile notice a rotated environment variable
131
+ * without the state store holding anything a reader could replay.
132
+ */
133
+ readonly passwordSeal: string;
134
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Plan diff for `Postgres.Role`.
3
+ *
4
+ * ⛔ THE ENGINE PASSES STORED ATTRIBUTES, NOT A CATALOG READ. `Plan.ts` (alchemy beta.79,
5
+ * the `provider.diff` call that sets `output: oldState.attr`) hands `diff` whatever reconcile
6
+ * last stored. `storedAttributes` strips `memberships`, so a name-matched `memberOf` looks
7
+ * idle even when the live row is `WITH ADMIN` or still at the `SET TRUE` default. The provider
8
+ * reads the role and passes it as `observation`. Offline tests omit `observation` and compare
9
+ * the stored parent names only.
10
+ */
11
+ import { isResolved } from 'alchemy/Diff';
12
+ import type { Input } from 'alchemy/Input';
13
+ import * as Effect from 'effect/Effect';
14
+ import type { Environment } from '../secrets/write-only.ts';
15
+ import type { PostgresRoleAttributes, PostgresRoleProps } from './role-attrs.ts';
16
+ import {
17
+ PostgresRoleIdentityRefused,
18
+ type PostgresRoleNameRefused,
19
+ PostgresRoleRenameRefused,
20
+ type PostgresRoleValidUntilRefused,
21
+ } from './role-errors.ts';
22
+ import { membershipDrift, unsafeMemberships } from './role-membership-sql.ts';
23
+ import { passwordMatchesSeal, resolvePassword } from './role-secrets.ts';
24
+ import { scalarDrift } from './role-sql.ts';
25
+ import { refuseAtPlan } from './role.ts';
26
+
27
+ /** A catalog read the provider performed for this diff. `found: undefined` means the name is
28
+ * absent. Omitting the argument means no catalog read (the offline comparison). */
29
+ export interface RoleObservation {
30
+ readonly found: Omit<PostgresRoleAttributes, 'passwordSeal'> | undefined;
31
+ }
32
+
33
+ /**
34
+ * `news` against stored `output`, plus a live row when the provider has one. A rename is a
35
+ * plan-time refusal; so are an over-long name and an unusable `validUntil`. An oid that is not
36
+ * the stored one is refused — the name was recreated out of band. Every other change answers
37
+ * `update`. `delete` is a real drop here, so `diff` never answers `replace`.
38
+ */
39
+ export const diffPostgresRole = (
40
+ news: Input<PostgresRoleProps>,
41
+ output: PostgresRoleAttributes | undefined,
42
+ env: Environment = process.env,
43
+ observation?: RoleObservation,
44
+ ): Effect.Effect<
45
+ { readonly action: 'update' } | { readonly action: 'noop' } | undefined,
46
+ | PostgresRoleRenameRefused
47
+ | PostgresRoleNameRefused
48
+ | PostgresRoleValidUntilRefused
49
+ | PostgresRoleIdentityRefused
50
+ > =>
51
+ Effect.gen(function* () {
52
+ if (output === undefined || !isResolved(news)) return undefined;
53
+ if (news.name !== output.name) {
54
+ return yield* Effect.fail(
55
+ new PostgresRoleRenameRefused({ from: output.name, to: news.name }),
56
+ );
57
+ }
58
+ yield* refuseAtPlan(news);
59
+ const live = observation?.found;
60
+ if (live !== undefined && live.oid !== output.oid) {
61
+ return yield* Effect.fail(
62
+ new PostgresRoleIdentityRefused({
63
+ role: output.name,
64
+ storedOid: output.oid,
65
+ liveOid: live.oid,
66
+ }),
67
+ );
68
+ }
69
+ const compared = live ?? output;
70
+ const scalars = scalarDrift(news, compared);
71
+ // Option rows live only on the catalog read. Stored `memberships` is stripped before it
72
+ // reaches the engine, so it is not consulted here.
73
+ const membership = membershipDrift(
74
+ news.memberOf,
75
+ compared.memberOf ?? [],
76
+ live === undefined ? new Set() : unsafeMemberships(live),
77
+ );
78
+ const resolved = resolvePassword(news, env);
79
+ const passwordStale =
80
+ resolved.value !== undefined && !passwordMatchesSeal(resolved.value, output.passwordSeal);
81
+ const absent = observation !== undefined && live === undefined;
82
+ const changed =
83
+ absent ||
84
+ scalars.length > 0 ||
85
+ membership.grants.length > 0 ||
86
+ membership.revokes.length > 0 ||
87
+ passwordStale;
88
+ return changed ? ({ action: 'update' } as const) : ({ action: 'noop' } as const);
89
+ });
@@ -0,0 +1,198 @@
1
+ /**
2
+ * `Postgres.Role` failures as typed tags (S21).
3
+ *
4
+ * ⛔ NONE OF THESE IS A STATUS-CODE OR MESSAGE MATCH. Every one is raised from a fact the
5
+ * provider already checked (a byte count, a live catalog row, a missing environment variable,
6
+ * a vanished create) — never from sniffing a driver error's text. A `DROP ROLE` that the
7
+ * server refuses (the role still owns objects, `2BP01 dependent_objects_still_exist`) surfaces
8
+ * as the `SqlError` the client raised, unclassified by this family.
9
+ */
10
+ import * as Data from 'effect/Data';
11
+
12
+ /**
13
+ * A role name would be silently truncated by the server (`NAMEDATALEN` 64, so 63 UTF-8 bytes is
14
+ * the last one that survives whole) — refused at plan, before any statement runs.
15
+ *
16
+ * ⚠️ Same hazard as `PostgresDatabaseNameRefused`: `truncate_identifier` clips at 63 bytes and
17
+ * emits only a NOTICE. A create would succeed against the truncated name; the next plan would
18
+ * look for the original name and find nothing, looping forever.
19
+ */
20
+ export class PostgresRoleNameRefused extends Data.TaggedError('PostgresRoleNameRefused')<{
21
+ readonly name: string;
22
+ readonly byteLength: number;
23
+ readonly limit: number;
24
+ }> {
25
+ override get message(): string {
26
+ return (
27
+ `Postgres.Role "${this.name}": ${String(this.byteLength)} UTF-8 bytes, over the ` +
28
+ `${String(this.limit)}-byte limit (NAMEDATALEN 64, minus the terminator). PostgreSQL would ` +
29
+ 'accept the CREATE and silently truncate the name with only a NOTICE, so this plan refuses ' +
30
+ 'it instead of creating a role no later read will ever find again.'
31
+ );
32
+ }
33
+ }
34
+
35
+ /** A logical id's `name` changed. A role's name is its identity; this resource never replaces via
36
+ * `DROP` then `CREATE` — role memberships, grants and owned objects would be lost, and the
37
+ * seat-wiring shape depends on the group-role name staying stable. `ALTER ROLE … RENAME TO`
38
+ * exists in PostgreSQL; this family does not drive it, so a rename is refused at plan and the
39
+ * new name is declared as its own logical id. */
40
+ export class PostgresRoleRenameRefused extends Data.TaggedError('PostgresRoleRenameRefused')<{
41
+ readonly from: string;
42
+ readonly to: string;
43
+ }> {
44
+ override get message(): string {
45
+ return (
46
+ `Postgres.Role: the declared name changed from "${this.from}" to "${this.to}". A ` +
47
+ 'rename is refused at plan — this provider never replaces a role (that is DROP then ' +
48
+ 'CREATE, which loses memberships, grants and ownerships). Declare a new logical id for ' +
49
+ 'the new name, and remove the old one once its objects have moved.'
50
+ );
51
+ }
52
+ }
53
+
54
+ /** The password prop names an environment variable that is unset or empty at reconcile time, on a
55
+ * write that must send one (a create that declares it, or any reconcile while the variable is
56
+ * set). Never a guess: a group role left with a stale or empty password is exactly the
57
+ * unauthenticated path the refusal exists to prevent. */
58
+ export class PostgresRolePasswordEnvUnsetError extends Data.TaggedError(
59
+ 'PostgresRolePasswordEnvUnsetError',
60
+ )<{
61
+ readonly role: string;
62
+ readonly variable: string;
63
+ }> {
64
+ override get message(): string {
65
+ return (
66
+ `Postgres.Role "${this.role}": password.fromEnv "${this.variable}" is unset or empty ` +
67
+ 'in the deploying process, but this write requires a password this stack has not sealed.'
68
+ );
69
+ }
70
+ }
71
+
72
+ /** A live role carries a catalog flag this family never declares (`rolsuper`, `rolcreaterole`,
73
+ * `rolcreatedb`, `rolreplication`, `rolbypassrls`). Those are not inherited: a member who
74
+ * `SET ROLE`s to the parent exercises them. Refused before any write, adopt included. */
75
+ export class PostgresRolePrivilegedRefused extends Data.TaggedError(
76
+ 'PostgresRolePrivilegedRefused',
77
+ )<{
78
+ readonly role: string;
79
+ readonly flags: readonly string[];
80
+ }> {
81
+ override get message(): string {
82
+ return (
83
+ `Postgres.Role "${this.role}": the live role has ${this.flags.join(', ')} set. This ` +
84
+ 'family never declares those attributes (a create lands on the server default, all off), ' +
85
+ 'and they are not inherited — a member who SET ROLEs to this role would exercise them. ' +
86
+ 'Clear the flag on the cluster before this stack adopts the role.'
87
+ );
88
+ }
89
+ }
90
+
91
+ /** The declared `validUntil` is not a timestamp this family can compare: ECMAScript cannot parse
92
+ * it at all, or it names no time zone. Refused at plan, before any statement — see
93
+ * `role-attrs.ts#validUntilRefusal` for the churn loop this prevents. */
94
+ export class PostgresRoleValidUntilRefused extends Data.TaggedError(
95
+ 'PostgresRoleValidUntilRefused',
96
+ )<{
97
+ readonly role: string;
98
+ readonly value: string;
99
+ readonly reason: 'unparseable' | 'zone-free';
100
+ }> {
101
+ override get message(): string {
102
+ return (
103
+ `Postgres.Role "${this.role}": validUntil "${this.value}" is ${this.reason === 'unparseable' ? 'not a timestamp this kit can parse' : 'missing its UTC designator or numeric offset'}. ` +
104
+ 'Declare an explicit-zone ISO 8601 value such as "2027-01-01T00:00:00Z" — PostgreSQL would ' +
105
+ 'read a zone-free value in the session time zone while the plan compares UTC, and the two ' +
106
+ 'would never agree.'
107
+ );
108
+ }
109
+ }
110
+
111
+ /** `CREATE ROLE` raised no error, but the immediate re-read still finds nothing (S10/S20).
112
+ * Measured to have no real trigger in a correctly speaking client; still a typed failure the
113
+ * caller can see and retry, never a defect that crashes the engine. */
114
+ export class PostgresRoleCreateVanished extends Data.TaggedError('PostgresRoleCreateVanished')<{
115
+ readonly role: string;
116
+ }> {
117
+ override get message(): string {
118
+ return (
119
+ `Postgres.Role "${this.role}": CREATE ROLE raised no error, but the role is still ` +
120
+ 'absent on the immediate re-read. Re-plan — this is not a state this provider expects to reach.'
121
+ );
122
+ }
123
+ }
124
+
125
+ /** A `pg_auth_members` row survived reconcile in a shape the declaration does not allow — not
126
+ * wanted, still `ADMIN`, or still `SET` at the upstream default TRUE. A live grantor's row is
127
+ * named in `REVOKE … GRANTED BY`, which either binds or fails loudly; the one shape no
128
+ * statement this family may issue can touch is a row whose grantor role was dropped
129
+ * (`pg_auth_members` keeps it keyed on the dead oid, and no `GRANTED BY` name reaches it), so
130
+ * the message asks the operator to revoke it by hand as a bootstrap superuser and re-plan. */
131
+ export class PostgresRoleMembershipUnrepaired extends Data.TaggedError(
132
+ 'PostgresRoleMembershipUnrepaired',
133
+ )<{
134
+ readonly role: string;
135
+ readonly parent: string;
136
+ readonly grantor: string | null;
137
+ readonly declared: boolean;
138
+ }> {
139
+ override get message(): string {
140
+ const who =
141
+ this.grantor === null ? 'a grantor role that no longer exists' : `grantor "${this.grantor}"`;
142
+ return (
143
+ `Postgres.Role "${this.role}": membership of "${this.parent}" ` +
144
+ `(${this.declared ? 'declared, options unsafe' : 'not declared'}) could not be repaired ` +
145
+ `after its statements — the row still stands under ${who}. pg_auth_members keys a ` +
146
+ 'membership on (parent, member, grantor), and a grantor that no longer exists cannot be ' +
147
+ 'named in REVOKE … GRANTED BY. Revoke the row by hand as a bootstrap superuser ' +
148
+ '(`REVOKE role FROM member GRANTED BY <grantor>`; a grantor oid with no role name needs ' +
149
+ 'catalog surgery) and re-plan.'
150
+ );
151
+ }
152
+ }
153
+
154
+ /** A declared `memberOf` parent is not in `pg_roles`. Checked before any statement of a create,
155
+ * so a missing parent never leaves a committed LOGIN role behind a `42704` (`undefined_object`,
156
+ * `user.c@REL_18_6`) from `GRANT`. */
157
+ export class PostgresRoleParentMissing extends Data.TaggedError('PostgresRoleParentMissing')<{
158
+ readonly role: string;
159
+ readonly parent: string;
160
+ }> {
161
+ override get message(): string {
162
+ return (
163
+ `Postgres.Role "${this.role}": memberOf parent "${this.parent}" does not exist. ` +
164
+ 'Nothing was written — declare that role first (or drop it from memberOf). A GRANT of a ' +
165
+ 'missing role fails only after CREATE ROLE has committed, and the next plan then needs ' +
166
+ '--adopt to finish the orphan.'
167
+ );
168
+ }
169
+ }
170
+
171
+ /** The live role's oid is not the one this stack stored. A drop and recreate out of band reuses
172
+ * the name and would otherwise plan as ours: noop while the seal still matches, and a destroy
173
+ * would `DROP` a role this stack never created. */
174
+ export class PostgresRoleIdentityRefused extends Data.TaggedError('PostgresRoleIdentityRefused')<{
175
+ readonly role: string;
176
+ readonly storedOid: number;
177
+ readonly liveOid: number;
178
+ }> {
179
+ override get message(): string {
180
+ return (
181
+ `Postgres.Role "${this.role}": the live oid is ${String(this.liveOid)}, but state recorded ` +
182
+ `${String(this.storedOid)}. The name was dropped and recreated outside this stack. Nothing ` +
183
+ 'was altered and the live role was not dropped — remove the stale state, or adopt the new ' +
184
+ 'role under a new logical id once you mean to own it.'
185
+ );
186
+ }
187
+ }
188
+
189
+ export type PostgresRoleError =
190
+ | PostgresRoleNameRefused
191
+ | PostgresRoleRenameRefused
192
+ | PostgresRoleValidUntilRefused
193
+ | PostgresRolePasswordEnvUnsetError
194
+ | PostgresRolePrivilegedRefused
195
+ | PostgresRoleCreateVanished
196
+ | PostgresRoleMembershipUnrepaired
197
+ | PostgresRoleParentMissing
198
+ | PostgresRoleIdentityRefused;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The create must not survive a missing parent, and a membership whose grantor role is gone
3
+ * must fail typed rather than look repaired.
4
+ */
5
+ import { describe, expect, test } from 'bun:test';
6
+ import * as Effect from 'effect/Effect';
7
+ import { makeFakeSql } from './fake-sql.ts';
8
+ import { PostgresRoleMembershipUnrepaired, PostgresRoleParentMissing } from './role-errors.ts';
9
+ import type { PostgresRoleAttributes, PostgresRoleProps } from './role-attrs.ts';
10
+ import { reconcileWithClient } from './role.ts';
11
+
12
+ const fails = <A, E>(eff: Effect.Effect<A, E>): Promise<E> => Effect.runPromise(Effect.flip(eff));
13
+
14
+ const props: PostgresRoleProps = {
15
+ name: 'seat-observability',
16
+ login: true,
17
+ connectionLimit: 10,
18
+ inherit: true,
19
+ memberOf: ['hf_agent'],
20
+ };
21
+
22
+ const live = (): PostgresRoleAttributes => ({
23
+ name: 'seat-observability',
24
+ oid: 20000,
25
+ login: true,
26
+ connectionLimit: 10,
27
+ inherit: true,
28
+ validUntil: null,
29
+ memberOf: ['hf_agent'],
30
+ passwordSeal: '',
31
+ });
32
+
33
+ describe('missing parent and dead grantor', () => {
34
+ test('a missing parent fails typed and issues no CREATE', async () => {
35
+ const fake = makeFakeSql();
36
+ const error = await fails(reconcileWithClient(fake, props));
37
+ expect(error).toBeInstanceOf(PostgresRoleParentMissing);
38
+ expect((error as PostgresRoleParentMissing).parent).toBe('hf_agent');
39
+ expect(fake.statements.some((s) => s.text.startsWith('CREATE'))).toBe(false);
40
+ expect(fake.roleRows.has('seat-observability')).toBe(false);
41
+ });
42
+
43
+ test('a GRANT failure inside the create transaction rolls the CREATE back', async () => {
44
+ const fake = makeFakeSql({ roles: ['hf_agent'], failNext: 'GRANT' });
45
+ const error = await fails(reconcileWithClient(fake, props));
46
+ expect(error).not.toBeInstanceOf(PostgresRoleParentMissing);
47
+ expect(fake.roleRows.has('seat-observability')).toBe(false);
48
+ expect(fake.statements.some((s) => s.text.startsWith('CREATE ROLE'))).toBe(true);
49
+ });
50
+
51
+ test('a wanted membership whose grantor role is gone fails typed', async () => {
52
+ const fake = makeFakeSql({ roleRows: [live()], roles: ['hf_agent'] });
53
+ fake.memberships.add('seat-observability\0hf_agent\0');
54
+ fake.membershipOptions.set('seat-observability\0hf_agent\0', { admin: true, set: true });
55
+ const error = await fails(reconcileWithClient(fake, props));
56
+ expect(error).toBeInstanceOf(PostgresRoleMembershipUnrepaired);
57
+ expect((error as PostgresRoleMembershipUnrepaired).grantor).toBeNull();
58
+ expect(fake.memberships.has('seat-observability\0hf_agent\0')).toBe(true);
59
+ });
60
+ });