@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
@@ -16,7 +16,7 @@ import {
16
16
  // src/postgres/database-sql.ts
17
17
  import * as Effect from "effect/Effect";
18
18
  var quoteIdent = (value) => `"${value.replace(/"/g, '""')}"`;
19
- var quoteStringLiteral = (value) => `'${value.replace(/'/g, "''")}'`;
19
+ var quoteStringLiteral = (value) => `E'${value.replace(/\\/g, "\\\\").replace(/'/g, "''")}'`;
20
20
  var withOptions = (props, template) => {
21
21
  const parts = [`OWNER ${quoteIdent(props.owner)}`];
22
22
  if (template !== undefined)
@@ -51,6 +51,8 @@ var isDuplicateDatabaseRace = (error) => {
51
51
  };
52
52
  var ROLE_EXISTS_SQL = "SELECT 1 AS present FROM pg_roles WHERE rolname = $1";
53
53
  var roleExists = (pg, role) => Effect.map(pg.unsafe(ROLE_EXISTS_SQL, [role]), (rows) => rows.length > 0);
54
+ var DATABASE_EXISTS_SQL = "SELECT 1 AS present FROM pg_database WHERE datname = $1";
55
+ var databaseExists = (pg, name) => Effect.map(pg.unsafe(DATABASE_EXISTS_SQL, [name]), (rows) => rows.length > 0);
54
56
  var SELECT_DATABASE_SQL = `SELECT
55
57
  d.oid AS oid,
56
58
  d.datname AS name,
@@ -97,48 +99,55 @@ var inlineParams = (sql, params) => params.length === 0 ? sql : sql.replace(/\$(
97
99
  return quoteStringLiteral(value);
98
100
  });
99
101
  var normalizeRow = (row) => typeof row === "object" && row !== null && typeof row.oid === "string" ? { ...row, oid: Number(row.oid) } : row;
100
- var isSelect = (sql) => /^\s*SELECT\b/i.test(sql);
102
+ var returnsRows = (sql) => /^\s*(SELECT|WITH)\b/i.test(sql);
101
103
  var wrapRows = (sql) => `SELECT coalesce(json_agg(t), '[]'::json)::text FROM (${sql}) t;`;
104
+ var redactPasswordLiterals = (text) => text.replace(/PASSWORD\s+(?:E'(?:[^'\\]|\\[\s\S]|'')*'|'(?:[^']|'')*')/gi, "PASSWORD '[redacted]'");
102
105
  var failure = (operation, result) => {
103
106
  const state = /ERROR:\s+([0-9A-Z]{5}):/.exec(result.stderr)?.[1];
107
+ const stderr = redactPasswordLiterals(result.stderr.trim());
104
108
  const fields = {
105
- cause: Object.assign(new Error(result.stderr.trim()), state === undefined ? {} : { code: state }),
106
- message: result.stderr.trim(),
107
- operation
109
+ cause: Object.assign(new Error(stderr), state === undefined ? {} : { code: state }),
110
+ message: stderr,
111
+ operation: redactPasswordLiterals(operation)
108
112
  };
109
113
  const reason = state === undefined ? new ConnectionError(fields) : state.startsWith("42") ? new SqlSyntaxError(fields) : new UnknownError(fields);
110
114
  return new SqlError({ reason });
111
115
  };
116
+ var runScript = (run, target, stdin, operation) => Effect2.gen(function* () {
117
+ const result = yield* Effect2.tryPromise({
118
+ try: () => run({
119
+ argv: [
120
+ "psql",
121
+ "-X",
122
+ "-q",
123
+ "-A",
124
+ "-t",
125
+ "-v",
126
+ "ON_ERROR_STOP=1",
127
+ "-v",
128
+ "VERBOSITY=verbose",
129
+ "-U",
130
+ target.username,
131
+ "-d",
132
+ target.database
133
+ ],
134
+ stdin
135
+ }),
136
+ catch: (cause) => new SqlError({ reason: new ConnectionError({ cause, operation: "psql exec" }) })
137
+ });
138
+ if (result.code !== 0) {
139
+ return yield* Effect2.fail(failure(operation, result));
140
+ }
141
+ return result;
142
+ });
112
143
  var makePsqlExecutor = (run, target) => ({
113
144
  unsafe: (sql, params = []) => Effect2.gen(function* () {
114
145
  const inlined = yield* Effect2.try({
115
146
  try: () => inlineParams(sql, params),
116
147
  catch: (cause) => new SqlError({ reason: new UnknownError({ cause, operation: "psql inline params" }) })
117
148
  });
118
- const rows = isSelect(inlined);
119
- const result = yield* Effect2.tryPromise({
120
- try: () => run({
121
- argv: [
122
- "psql",
123
- "-X",
124
- "-q",
125
- "-A",
126
- "-t",
127
- "-v",
128
- "ON_ERROR_STOP=1",
129
- "-v",
130
- "VERBOSITY=verbose",
131
- "-U",
132
- target.username,
133
- "-d",
134
- target.database
135
- ],
136
- stdin: rows ? wrapRows(inlined) : `${inlined};`
137
- }),
138
- catch: (cause) => new SqlError({ reason: new ConnectionError({ cause, operation: "psql exec" }) })
139
- });
140
- if (result.code !== 0)
141
- return yield* Effect2.fail(failure(inlined.slice(0, 40), result));
149
+ const rows = returnsRows(inlined);
150
+ const result = yield* runScript(run, target, rows ? wrapRows(inlined) : `${inlined};`, redactPasswordLiterals(inlined).slice(0, 40));
142
151
  if (!rows)
143
152
  return [];
144
153
  const parsed = yield* Effect2.try({
@@ -146,6 +155,11 @@ var makePsqlExecutor = (run, target) => ({
146
155
  catch: (cause) => new SqlError({ reason: new UnknownError({ cause, operation: "psql parse output" }) })
147
156
  });
148
157
  return parsed;
158
+ }),
159
+ transaction: (statements) => Effect2.gen(function* () {
160
+ const script = ["BEGIN", ...statements, "COMMIT"].join(`;
161
+ `);
162
+ yield* runScript(run, target, `${script};`, "BEGIN");
149
163
  })
150
164
  });
151
165
 
@@ -154,16 +168,28 @@ class PostgresConnection extends Context.Service()("Postgres.Connection") {
154
168
  }
155
169
  var postgresConnection = (config) => Layer.succeed(PostgresConnection, Effect3.succeed(config));
156
170
  var postgresRunnerConnection = (config) => Layer.succeed(PostgresConnection, Effect3.succeed(config));
171
+ var PostgresPool = Context.Reference("homeflare/postgres/Pool", {
172
+ defaultValue: () => PgClient.layer
173
+ });
157
174
  var isRunner = (c) => ("run" in c);
158
- var withPg = (build) => Effect3.gen(function* () {
175
+ var withPg = (build, database) => Effect3.gen(function* () {
159
176
  const resolveConfig = yield* PostgresConnection;
160
177
  const config = yield* resolveConfig;
161
- if (isRunner(config)) {
162
- return yield* build(makePsqlExecutor(config.run, config), {
163
- template: config.template ?? "template0"
178
+ const target = database === undefined ? config : { ...config, database };
179
+ if (isRunner(target)) {
180
+ return yield* build(makePsqlExecutor(target.run, target), {
181
+ template: target.template ?? "template0",
182
+ database: target.database
164
183
  });
165
184
  }
166
- return yield* Effect3.provide(Effect3.flatMap(PgClient.PgClient, (pg) => build(pg, {})), PgClient.layer(config));
185
+ const pool = yield* PostgresPool;
186
+ return yield* Effect3.provide(Effect3.flatMap(PgClient.PgClient, (pg) => build({
187
+ unsafe: (sql, params) => pg.unsafe(sql, params),
188
+ transaction: (statements) => pg.withTransaction(Effect3.gen(function* () {
189
+ for (const sql of statements)
190
+ yield* pg.unsafe(sql).pipe(Effect3.asVoid);
191
+ }))
192
+ }, { database: target.database })), pool(target));
167
193
  });
168
194
  // src/postgres/database-attrs.ts
169
195
  var POSTGRES_NAME_MAX_BYTES = 63;
@@ -287,11 +313,1861 @@ var postgresDatabaseHandlers = PostgresDatabase.Provider.of({
287
313
  delete: ({ olds }) => Effect4.fail(new PostgresDatabaseDropRefused({ database: olds.name }))
288
314
  });
289
315
  var PostgresDatabaseProvider = () => Provider.succeed(PostgresDatabase, postgresDatabaseHandlers);
316
+ // src/postgres/schema-attrs.ts
317
+ var normalizedComment = (comment) => comment === "" ? undefined : comment;
318
+ var schemaNameByteRefusal = (name) => {
319
+ const byteLength = utf8ByteLength(name);
320
+ return byteLength > POSTGRES_NAME_MAX_BYTES ? { byteLength, limit: POSTGRES_NAME_MAX_BYTES } : undefined;
321
+ };
322
+ // src/postgres/schema-sql.ts
323
+ import * as Effect5 from "effect/Effect";
324
+ var buildCreateSchemaSql = (props) => `CREATE SCHEMA ${quoteIdent(props.name)}${props.owner === undefined ? "" : ` AUTHORIZATION ${quoteIdent(props.owner)}`}`;
325
+ var buildCommentSchemaSql = (name, comment) => `COMMENT ON SCHEMA ${quoteIdent(name)} IS ${quoteStringLiteral(comment)}`;
326
+ var buildDropSchemaSql = (name, cascade) => `DROP SCHEMA IF EXISTS ${quoteIdent(name)}${cascade ? " CASCADE" : ""}`;
327
+ var SELECT_SCHEMA_SQL = `SELECT
328
+ n.oid AS oid,
329
+ n.nspname AS name,
330
+ pg_get_userbyid(n.nspowner) AS owner,
331
+ obj_description(n.oid, 'pg_namespace') AS comment,
332
+ current_database() AS database
333
+ FROM pg_namespace n
334
+ WHERE n.nspname = $1`;
335
+ var selectSchema = (pg, name) => Effect5.map(pg.unsafe(SELECT_SCHEMA_SQL, [name]), (rows) => rows[0]);
336
+ var CURRENT_DATABASE_SQL = "SELECT current_database() AS database";
337
+ var currentDatabase = (pg) => Effect5.map(pg.unsafe(CURRENT_DATABASE_SQL), (rows) => rows[0]?.database ?? "");
338
+ var CURRENT_USER_SQL = "SELECT current_user AS role";
339
+ var currentUser = (pg) => Effect5.map(pg.unsafe(CURRENT_USER_SQL), (rows) => rows[0]?.role ?? "");
340
+ var SCHEMA_EMPTY_SQL = `WITH ns AS (SELECT oid FROM pg_namespace WHERE nspname = $1)
341
+ SELECT NOT EXISTS (
342
+ SELECT 1 FROM pg_class c WHERE c.relnamespace = (SELECT oid FROM ns)
343
+ ) AND NOT EXISTS (
344
+ SELECT 1 FROM pg_proc p WHERE p.pronamespace = (SELECT oid FROM ns)
345
+ ) AND NOT EXISTS (
346
+ SELECT 1 FROM pg_type t WHERE t.typnamespace = (SELECT oid FROM ns)
347
+ ) AND NOT EXISTS (
348
+ SELECT 1 FROM pg_operator o WHERE o.oprnamespace = (SELECT oid FROM ns)
349
+ ) AS empty`;
350
+ var schemaIsEmpty = (pg, name) => Effect5.map(pg.unsafe(SCHEMA_EMPTY_SQL, [name]), (rows) => rows[0]?.empty ?? true);
351
+ var isDependentObjectsError = (error) => {
352
+ const cause = error.reason.cause;
353
+ return typeof cause === "object" && cause !== null && cause.code === "2BP01";
354
+ };
355
+ var isDuplicateSchemaError = (error) => {
356
+ const cause = error.reason.cause;
357
+ return typeof cause === "object" && cause !== null && cause.code === "42P06";
358
+ };
359
+ // src/postgres/schema.ts
360
+ import { Resource as Resource2 } from "alchemy";
361
+ import { Unowned as Unowned2 } from "alchemy/AdoptPolicy";
362
+ import * as Provider2 from "alchemy/Provider";
363
+ import * as Effect11 from "effect/Effect";
364
+
365
+ // src/postgres/schema-identity.ts
366
+ import * as Effect6 from "effect/Effect";
367
+
368
+ // src/postgres/schema-errors.ts
369
+ import * as Data2 from "effect/Data";
370
+
371
+ class PostgresSchemaNameRefused extends Data2.TaggedError("PostgresSchemaNameRefused") {
372
+ get message() {
373
+ return `Postgres.Schema "${this.name}": ${String(this.byteLength)} UTF-8 bytes, over the ` + `${String(this.limit)}-byte limit (NAMEDATALEN 64, minus the terminator). PostgreSQL would ` + "accept the CREATE and silently truncate the name with only a NOTICE, so this plan refuses " + "it instead of creating a schema no later read will ever find again.";
374
+ }
375
+ }
376
+
377
+ class PostgresSchemaRenameRefused extends Data2.TaggedError("PostgresSchemaRenameRefused") {
378
+ get message() {
379
+ return `Postgres.Schema: the declared name changed from "${this.from}" to "${this.to}". A ` + "rename is refused during plan or apply — this provider never runs ALTER SCHEMA … RENAME. Declare a new " + "logical id for the new name and remove the old one once its contents have moved.";
380
+ }
381
+ }
382
+
383
+ class PostgresSchemaOwnerMissing extends Data2.TaggedError("PostgresSchemaOwnerMissing") {
384
+ get message() {
385
+ return `Postgres.Schema "${this.schema}": AUTHORIZATION "${this.owner}" is not a role in pg_roles.`;
386
+ }
387
+ }
388
+
389
+ class PostgresSchemaDrift extends Data2.TaggedError("PostgresSchemaDrift") {
390
+ get message() {
391
+ return `Postgres.Schema "${this.schema}": ${this.prop} is declared as ` + `${JSON.stringify(this.declared)} but the live schema has ${JSON.stringify(this.live)}. ` + "This provider never issues ALTER SCHEMA — update the declaration to match the live value, " + "or change it by hand and re-plan.";
392
+ }
393
+ }
394
+
395
+ class PostgresSchemaCreateVanished extends Data2.TaggedError("PostgresSchemaCreateVanished") {
396
+ get message() {
397
+ return `Postgres.Schema "${this.schema}": CREATE SCHEMA raised no error, but the schema is still ` + "absent on the immediate re-read. Re-plan — this is not a state this provider expects to reach.";
398
+ }
399
+ }
400
+
401
+ class PostgresSchemaDropNotEmptyError extends Data2.TaggedError("PostgresSchemaDropNotEmptyError") {
402
+ get message() {
403
+ return `Postgres.Schema "${this.schema}": DROP SCHEMA refused because the schema is not empty and ` + "cascade is false. Declare cascade: true to drop its contents, or empty it by hand first.";
404
+ }
405
+ }
406
+
407
+ class PostgresSchemaWrongDatabase extends Data2.TaggedError("PostgresSchemaWrongDatabase") {
408
+ get message() {
409
+ return `Postgres.Schema "${this.schema}": declared in database "${this.declared}" but the ` + `connection answers current_database() "${this.connected}". The handlers open the declared ` + "database themselves, so this points at broken provider wiring — fix it before touching any " + "schema, or the next create would land in the wrong database.";
410
+ }
411
+ }
412
+
413
+ class PostgresSchemaDeleteForeignRefused extends Data2.TaggedError("PostgresSchemaDeleteForeignRefused") {
414
+ get message() {
415
+ const proof = this.lastOid === undefined || this.lastOwner === undefined ? "delete carried no persisted oid/owner to prove ownership" : `the persisted proof is oid ${String(this.lastOid)}, owner "${this.lastOwner}"`;
416
+ return `Postgres.Schema "${this.schema}": the live schema in database "${this.database}" (oid ` + `${String(this.liveOid)}, owner "${this.liveOwner}") is not the one this resource created — ` + `${proof}. No DROP was issued: a schema with this name was created out of band. Drop it by ` + "hand, or adopt it on a resource whose declared props match it.";
417
+ }
418
+ }
419
+
420
+ class PostgresSchemaDatabaseRefused extends Data2.TaggedError("PostgresSchemaDatabaseRefused") {
421
+ get message() {
422
+ return `Postgres.Schema: the declared database changed from "${this.from}" to "${this.to}". A ` + "schema is never moved across databases — declare a new logical id in the new database and " + "remove the old one once its contents have moved.";
423
+ }
424
+ }
425
+
426
+ class PostgresSchemaExistsRefused extends Data2.TaggedError("PostgresSchemaExistsRefused") {
427
+ get message() {
428
+ return `Postgres.Schema "${this.schema}" in "${this.database}" already exists. Re-plan with ` + "--adopt (adopt(true)) to adopt it explicitly; crash recovery also requires adoption. " + "No existing schema was changed.";
429
+ }
430
+ }
431
+
432
+ class PostgresSchemaIdentityRefused extends Data2.TaggedError("PostgresSchemaIdentityRefused") {
433
+ get message() {
434
+ return `Postgres.Schema "${this.schema}": live oid ${String(this.liveOid)} differs from stored oid ` + `${String(this.storedOid)}. The schema was dropped and recreated outside this stack. ` + "Remove stale state and explicitly adopt the replacement before managing it.";
435
+ }
436
+ }
437
+
438
+ // src/postgres/schema-identity.ts
439
+ var assertSchemaTarget = (props, output) => Effect6.gen(function* () {
440
+ if (output === undefined)
441
+ return;
442
+ if (props.name !== output.name)
443
+ return yield* Effect6.fail(new PostgresSchemaRenameRefused({ from: output.name, to: props.name }));
444
+ if (props.database !== output.database)
445
+ return yield* Effect6.fail(new PostgresSchemaDatabaseRefused({ from: output.database, to: props.database }));
446
+ });
447
+ var assertSchemaIdentity = (live, output) => live.oid === output.oid ? Effect6.void : Effect6.fail(new PostgresSchemaIdentityRefused({
448
+ schema: live.name,
449
+ storedOid: output.oid,
450
+ liveOid: live.oid
451
+ }));
452
+
453
+ // src/postgres/schema-diff.ts
454
+ import { isResolved as isResolved2 } from "alchemy/Diff";
455
+ import * as Effect7 from "effect/Effect";
456
+ var diffPostgresSchema = (news, output, olds) => Effect7.gen(function* () {
457
+ if (output === undefined || !isResolved2(news))
458
+ return;
459
+ yield* assertSchemaTarget(news, output);
460
+ const nameRefusal = schemaNameByteRefusal(news.name);
461
+ if (nameRefusal !== undefined) {
462
+ return yield* Effect7.fail(new PostgresSchemaNameRefused({ name: news.name, ...nameRefusal }));
463
+ }
464
+ const comment = normalizedComment(news.comment);
465
+ const changed = news.owner !== undefined && news.owner !== output.owner || comment !== undefined && comment !== (output.comment ?? undefined) || (news.cascade ?? false) !== (olds?.cascade ?? false);
466
+ return changed ? { action: "update" } : { action: "noop" };
467
+ });
468
+
469
+ // src/postgres/schema-reconcile.ts
470
+ import * as Effect9 from "effect/Effect";
471
+
472
+ // src/postgres/schema-assert.ts
473
+ import * as Effect8 from "effect/Effect";
474
+ var assertDatabase = (props, pg) => Effect8.flatMap(currentDatabase(pg), (connected) => connected === props.database ? Effect8.void : Effect8.fail(new PostgresSchemaWrongDatabase({
475
+ schema: props.name,
476
+ declared: props.database,
477
+ connected
478
+ })));
479
+ var declaredOwner = (props, executingRole) => props.owner ?? executingRole;
480
+ var ownerDrift = (props, live, executingRole) => {
481
+ const declared = declaredOwner(props, executingRole);
482
+ return declared === live.owner ? undefined : { prop: "owner", declared, live: live.owner };
483
+ };
484
+ var liveDrift2 = (props, live, executingRole) => {
485
+ const owner = ownerDrift(props, live, executingRole);
486
+ if (owner !== undefined)
487
+ return owner;
488
+ const comment = normalizedComment(props.comment);
489
+ if (comment !== undefined && comment !== (live.comment ?? undefined)) {
490
+ return { prop: "comment", declared: comment, live: live.comment };
491
+ }
492
+ return;
493
+ };
494
+ var refuse = (props, drift) => Effect8.fail(new PostgresSchemaDrift({
495
+ schema: props.name,
496
+ prop: drift.prop,
497
+ declared: drift.declared,
498
+ live: drift.live
499
+ }));
500
+ var assertOwner = (props, row, executingRole) => {
501
+ const drift = ownerDrift(props, row, executingRole);
502
+ return drift === undefined ? Effect8.void : refuse(props, drift);
503
+ };
504
+ var assertLive = (props, row, executingRole) => {
505
+ const drift = liveDrift2(props, row, executingRole);
506
+ return drift === undefined ? Effect8.succeed(row) : refuse(props, drift);
507
+ };
508
+ var deleteForeignRefusal = (props, live, output) => {
509
+ if (output !== undefined && output.oid === live.oid && output.owner === live.owner) {
510
+ return;
511
+ }
512
+ return new PostgresSchemaDeleteForeignRefused({
513
+ schema: props.name,
514
+ database: props.database,
515
+ liveOid: live.oid,
516
+ liveOwner: live.owner,
517
+ lastOid: output?.oid,
518
+ lastOwner: output?.owner
519
+ });
520
+ };
521
+
522
+ // src/postgres/schema-reconcile.ts
523
+ var reconcileWithClient2 = (pg, props, output) => Effect9.gen(function* () {
524
+ yield* assertSchemaTarget(props, output);
525
+ yield* assertDatabase(props, pg);
526
+ const executingRole = props.owner !== undefined ? props.owner : yield* currentUser(pg);
527
+ const observed = yield* selectSchema(pg, props.name);
528
+ if (observed === undefined) {
529
+ if (props.owner !== undefined) {
530
+ const ownerPresent = yield* roleExists(pg, props.owner);
531
+ if (!ownerPresent) {
532
+ return yield* Effect9.fail(new PostgresSchemaOwnerMissing({ schema: props.name, owner: props.owner }));
533
+ }
534
+ }
535
+ const createRefusal = (error) => isDuplicateSchemaError(error) ? new PostgresSchemaExistsRefused({ schema: props.name, database: props.database }) : error;
536
+ yield* pg.unsafe(buildCreateSchemaSql(props)).pipe(Effect9.asVoid, Effect9.catchTag("SqlError", (error) => Effect9.fail(createRefusal(error))));
537
+ const created = yield* selectSchema(pg, props.name);
538
+ if (created === undefined) {
539
+ return yield* Effect9.fail(new PostgresSchemaCreateVanished({ schema: props.name }));
540
+ }
541
+ yield* assertOwner(props, created, executingRole);
542
+ const comment = normalizedComment(props.comment);
543
+ if (comment !== undefined && comment !== (created.comment ?? undefined)) {
544
+ yield* pg.unsafe(buildCommentSchemaSql(props.name, comment)).pipe(Effect9.asVoid);
545
+ const commented = yield* selectSchema(pg, props.name);
546
+ if (commented === undefined) {
547
+ return yield* Effect9.fail(new PostgresSchemaCreateVanished({ schema: props.name }));
548
+ }
549
+ return yield* assertLive(props, commented, executingRole);
550
+ }
551
+ return yield* assertLive(props, created, executingRole);
552
+ }
553
+ if (output === undefined) {
554
+ return yield* Effect9.fail(new PostgresSchemaExistsRefused({ schema: props.name, database: props.database }));
555
+ }
556
+ yield* assertSchemaIdentity(observed, output);
557
+ return yield* assertLive(props, observed, executingRole);
558
+ });
559
+
560
+ // src/postgres/schema-delete.ts
561
+ import * as Effect10 from "effect/Effect";
562
+ var dropGuarded = (pg, props) => Effect10.gen(function* () {
563
+ if (props.cascade !== true) {
564
+ const empty = yield* schemaIsEmpty(pg, props.name);
565
+ if (!empty) {
566
+ return yield* Effect10.fail(new PostgresSchemaDropNotEmptyError({ schema: props.name }));
567
+ }
568
+ }
569
+ const dropRefusal = (error) => props.cascade !== true && isDependentObjectsError(error) ? new PostgresSchemaDropNotEmptyError({ schema: props.name }) : error;
570
+ yield* pg.unsafe(buildDropSchemaSql(props.name, props.cascade === true)).pipe(Effect10.asVoid, Effect10.catchTag("SqlError", (error) => Effect10.fail(dropRefusal(error))));
571
+ });
572
+ var deleteWithClient = (pg, props, output) => Effect10.gen(function* () {
573
+ yield* assertDatabase(props, pg);
574
+ const live = yield* selectSchema(pg, props.name);
575
+ if (live === undefined)
576
+ return;
577
+ const refusal = deleteForeignRefusal(props, live, output);
578
+ if (refusal !== undefined)
579
+ return yield* Effect10.fail(refusal);
580
+ yield* dropGuarded(pg, props);
581
+ });
582
+
583
+ // src/postgres/schema.ts
584
+ var PostgresSchema = Resource2("Postgres.Schema", {
585
+ defaultRemovalPolicy: "retain"
586
+ });
587
+ var isPostgresSchema = (value) => (typeof value === "object" || typeof value === "function") && value !== null && value.Type === "Postgres.Schema";
588
+ var readWithClient2 = (pg, name) => selectSchema(pg, name);
589
+ var postgresSchemaHandlers = PostgresSchema.Provider.of({
590
+ list: () => Effect11.succeed([]),
591
+ nuke: { skip: true },
592
+ read: ({ olds, output }) => Effect11.gen(function* () {
593
+ const name = output?.name ?? olds.name;
594
+ const database = output?.database ?? olds.database;
595
+ const present = yield* withPg((pg) => databaseExists(pg, database));
596
+ if (!present)
597
+ return;
598
+ const live = yield* withPg((pg) => readWithClient2(pg, name), database);
599
+ if (live !== undefined && live.database !== database) {
600
+ return yield* Effect11.fail(new PostgresSchemaWrongDatabase({
601
+ schema: name,
602
+ declared: database,
603
+ connected: live.database
604
+ }));
605
+ }
606
+ if (live !== undefined && output !== undefined)
607
+ yield* assertSchemaIdentity(live, output);
608
+ return live === undefined ? undefined : Unowned2(live);
609
+ }),
610
+ diff: ({ news, output, olds }) => diffPostgresSchema(news, output, olds),
611
+ reconcile: ({ news, output }) => Effect11.gen(function* () {
612
+ const nameRefusal = schemaNameByteRefusal(news.name);
613
+ if (nameRefusal !== undefined) {
614
+ return yield* Effect11.fail(new PostgresSchemaNameRefused({ name: news.name, ...nameRefusal }));
615
+ }
616
+ yield* assertSchemaTarget(news, output);
617
+ return yield* withPg((pg) => reconcileWithClient2(pg, news, output), news.database);
618
+ }),
619
+ delete: ({ olds, output }) => Effect11.gen(function* () {
620
+ const present = yield* withPg((pg) => databaseExists(pg, olds.database));
621
+ if (!present)
622
+ return;
623
+ return yield* withPg((pg) => deleteWithClient(pg, olds, output), olds.database);
624
+ })
625
+ });
626
+ var PostgresSchemaProvider = () => Provider2.succeed(PostgresSchema, postgresSchemaHandlers);
627
+ // src/postgres/grants-attrs.ts
628
+ var SCHEMA_PRIVILEGES = ["usage", "create"];
629
+ var TABLE_PRIVILEGES = [
630
+ "select",
631
+ "insert",
632
+ "update",
633
+ "delete",
634
+ "truncate",
635
+ "references",
636
+ "trigger",
637
+ "maintain"
638
+ ];
639
+ var COLUMN_PRIVILEGES = ["select", "insert", "update", "references"];
640
+ var ALL_GRANT_WORDS = [...SCHEMA_PRIVILEGES, ...TABLE_PRIVILEGES];
641
+ var LETTER_FOR_PRIVILEGE = {
642
+ insert: "a",
643
+ select: "r",
644
+ update: "w",
645
+ delete: "d",
646
+ truncate: "D",
647
+ references: "x",
648
+ trigger: "t",
649
+ maintain: "m",
650
+ usage: "U",
651
+ create: "C"
652
+ };
653
+ var letterForPrivilege = (word) => {
654
+ const letter = LETTER_FOR_PRIVILEGE[word];
655
+ if (letter === undefined)
656
+ throw new Error(`grants-attrs: no ACL letter for "${word}"`);
657
+ return letter;
658
+ };
659
+ var grantsNameByteRefusal = (name) => {
660
+ const byteLength = utf8ByteLength(name);
661
+ return byteLength > POSTGRES_NAME_MAX_BYTES ? { byteLength, limit: POSTGRES_NAME_MAX_BYTES } : undefined;
662
+ };
663
+ // src/postgres/grants-sql.ts
664
+ var splitParts = (words) => {
665
+ const parts = [];
666
+ let optionCount = 0;
667
+ for (const word of words) {
668
+ const base = word.endsWith("*") ? word.slice(0, -1) : word;
669
+ if (!ALL_GRANT_WORDS.includes(base)) {
670
+ throw new Error(`grants-sql: "${base}" is not a privilege this family can grant — see the three ` + "vocabularies in grants-attrs.ts (traced to acl.h at REL_18_6)");
671
+ }
672
+ if (word.endsWith("*"))
673
+ optionCount += 1;
674
+ parts.push(base);
675
+ }
676
+ if (optionCount > 0 && optionCount < words.length) {
677
+ throw new Error(`grants-sql: a mixed grant-option list cannot be one GRANT statement: ${words.join(", ")}` + " — split it with splitGrantWords first (one option clause grants the option to every word)");
678
+ }
679
+ return { parts, anyOption: optionCount > 0 };
680
+ };
681
+ var optionClause = (anyOption) => anyOption ? " WITH GRANT OPTION" : "";
682
+ var grantTableSql = (schema, table, role, words) => {
683
+ const { parts, anyOption } = splitParts(words);
684
+ return `GRANT ${parts.join(", ")} ON ${quoteIdent(schema)}.${quoteIdent(table)} TO ${quoteIdent(role)}${optionClause(anyOption)}`;
685
+ };
686
+ var grantColumnSql = (schema, table, column, role, words) => {
687
+ const { parts, anyOption } = splitParts(words);
688
+ const columnList = `(${quoteIdent(column)})`;
689
+ return `GRANT ${parts.map((word) => `${word} ${columnList}`).join(", ")} ON ${quoteIdent(schema)}.${quoteIdent(table)} TO ${quoteIdent(role)}${optionClause(anyOption)}`;
690
+ };
691
+ var grantSchemaSql = (schema, role, words) => {
692
+ const { parts, anyOption } = splitParts(words);
693
+ return `GRANT ${parts.join(", ")} ON SCHEMA ${quoteIdent(schema)} TO ${quoteIdent(role)}${optionClause(anyOption)}`;
694
+ };
695
+ var grantDefaultSql = (schema, forRole, role, words) => {
696
+ const { parts, anyOption } = splitParts(words);
697
+ return `ALTER DEFAULT PRIVILEGES FOR ROLE ${quoteIdent(forRole)} IN SCHEMA ${quoteIdent(schema)} GRANT ${parts.join(", ")} ON TABLES TO ${quoteIdent(role)}${optionClause(anyOption)}`;
698
+ };
699
+ var revokeTableSql = (schema, table, role) => `REVOKE ALL ON ${quoteIdent(schema)}.${quoteIdent(table)} FROM ${quoteIdent(role)}`;
700
+ var revokeColumnSql = (schema, table, column, role) => `REVOKE ALL (${quoteIdent(column)}) ON ${quoteIdent(schema)}.${quoteIdent(table)} FROM ${quoteIdent(role)}`;
701
+ var revokeSchemaSql = (schema, role) => `REVOKE ALL ON SCHEMA ${quoteIdent(schema)} FROM ${quoteIdent(role)}`;
702
+ var revokeDefaultSql = (schema, forRole, role) => `ALTER DEFAULT PRIVILEGES FOR ROLE ${quoteIdent(forRole)} IN SCHEMA ${quoteIdent(schema)} REVOKE ALL ON TABLES FROM ${quoteIdent(role)}`;
703
+ var revokePublicSchemaSql = (schema) => `REVOKE ALL ON SCHEMA ${quoteIdent(schema)} FROM PUBLIC`;
704
+ var revokePublicTablesSql = (schema) => `REVOKE ALL ON ALL TABLES IN SCHEMA ${quoteIdent(schema)} FROM PUBLIC`;
705
+ // src/postgres/grants.ts
706
+ import { Resource as Resource3 } from "alchemy";
707
+ import { Unowned as Unowned3 } from "alchemy/AdoptPolicy";
708
+ import * as Provider3 from "alchemy/Provider";
709
+ import * as Effect17 from "effect/Effect";
710
+
711
+ // src/postgres/grants-declare.ts
712
+ var cleanWords = (words) => [...new Set(words)].sort();
713
+ var baseWord = (word) => word.endsWith("*") ? word.slice(0, -1) : word;
714
+ var resolveProps = (props) => ({
715
+ role: props.role,
716
+ database: props.database,
717
+ schema: props.schema,
718
+ schemaPrivileges: cleanWords([
719
+ ...props.schemaUsage === false ? [] : ["usage"],
720
+ ...props.schemaCreate === true ? ["create"] : []
721
+ ]),
722
+ tables: [...props.tables ?? []].map((table) => ({ table: table.table, privileges: cleanWords(table.privileges) })).sort((a, b) => a.table.localeCompare(b.table)),
723
+ columns: [...props.columnGrants ?? []].map((column) => ({
724
+ table: column.table,
725
+ column: column.column,
726
+ privileges: cleanWords(column.privileges)
727
+ })).sort((a, b) => a.table.localeCompare(b.table) || a.column.localeCompare(b.column)),
728
+ defaults: [...props.defaultPrivileges ?? []].map((entry) => ({ forRole: entry.forRole, privileges: cleanWords(entry.privileges) })).sort((a, b) => a.forRole.localeCompare(b.forRole)),
729
+ publicSchemaRevoked: props.revokeFromPublic === true,
730
+ publicTablesRevoked: props.revokeFromPublic === true
731
+ });
732
+ var splitGrantWords = (words) => ({
733
+ plain: words.filter((word) => !word.endsWith("*")),
734
+ grantable: words.filter((word) => word.endsWith("*"))
735
+ });
736
+ var declaredNames = (declared) => ({
737
+ role: declared.role,
738
+ database: declared.database,
739
+ schema: declared.schema,
740
+ tables: declared.tables.map((table) => table.table),
741
+ columns: declared.columns.map(({ table, column }) => ({ table, column })),
742
+ defaults: declared.defaults.map((entry) => entry.forRole)
743
+ });
744
+ var namesFromAttrs = (attrs) => ({
745
+ role: attrs.role,
746
+ database: attrs.database,
747
+ schema: attrs.schema,
748
+ tables: attrs.tables.map((table) => table.table),
749
+ columns: attrs.columns.map(({ table, column }) => ({ table, column })),
750
+ defaults: attrs.defaults.map((entry) => entry.forRole)
751
+ });
752
+ var clearedDeclaration = (declared) => ({
753
+ ...declared,
754
+ schemaPrivileges: [],
755
+ tables: declared.tables.map((table) => ({ table: table.table, privileges: [] })),
756
+ columns: declared.columns.map((column) => ({
757
+ table: column.table,
758
+ column: column.column,
759
+ privileges: []
760
+ })),
761
+ defaults: declared.defaults.map((entry) => ({ forRole: entry.forRole, privileges: [] })),
762
+ publicSchemaRevoked: false,
763
+ publicTablesRevoked: false
764
+ });
765
+ var removedNames = (output, declared) => {
766
+ if (output === undefined)
767
+ return;
768
+ const keepTables = new Set(declared.tables.map((table) => table.table));
769
+ const keepColumns = new Set(declared.columns.map((column) => `${column.table}\x00${column.column}`));
770
+ const keepDefaults = new Set(declared.defaults.map((entry) => entry.forRole));
771
+ const tables = output.tables.filter((table) => !keepTables.has(table.table)).map((table) => ({ table: table.table, privileges: [] }));
772
+ const columns = output.columns.filter((column) => !keepColumns.has(`${column.table}\x00${column.column}`)).map((column) => ({
773
+ table: column.table,
774
+ column: column.column,
775
+ privileges: []
776
+ }));
777
+ const defaults = output.defaults.filter((entry) => !keepDefaults.has(entry.forRole)).map((entry) => ({ forRole: entry.forRole, privileges: [] }));
778
+ if (tables.length === 0 && columns.length === 0 && defaults.length === 0)
779
+ return;
780
+ return { schema: declared.schema, role: declared.role, tables, columns, defaults };
781
+ };
782
+
783
+ // src/postgres/grants-ops.ts
784
+ import * as Effect16 from "effect/Effect";
785
+ import { isResolved as isResolved3 } from "alchemy/Diff";
786
+
787
+ // src/postgres/grants-refuse.ts
788
+ var wordRefusal = (words, vocabulary) => {
789
+ for (const word of words) {
790
+ if (word === "*")
791
+ return word;
792
+ const bare = baseWord(word);
793
+ if (!vocabulary.includes(bare))
794
+ return bare;
795
+ }
796
+ return;
797
+ };
798
+ var listRefusal = (words, vocabulary, prop) => {
799
+ const word = wordRefusal(words, vocabulary);
800
+ if (word !== undefined)
801
+ return { kind: "privilege", prop, word };
802
+ const seen = new Set;
803
+ for (const entry of words) {
804
+ const base = baseWord(entry);
805
+ if (seen.has(base))
806
+ return { kind: "duplicate", prop, name: base };
807
+ seen.add(base);
808
+ }
809
+ return;
810
+ };
811
+ var declarationRefusal = (declared) => {
812
+ const schema = listRefusal(declared.schemaPrivileges, SCHEMA_PRIVILEGES, "schemaPrivileges");
813
+ if (schema !== undefined)
814
+ return schema;
815
+ for (const table2 of declared.tables) {
816
+ const refusal = listRefusal(table2.privileges, TABLE_PRIVILEGES, `tables.${table2.table}`);
817
+ if (refusal !== undefined)
818
+ return refusal;
819
+ }
820
+ for (const column2 of declared.columns) {
821
+ const refusal = listRefusal(column2.privileges, COLUMN_PRIVILEGES, `columnGrants.${column2.table}.${column2.column}`);
822
+ if (refusal !== undefined)
823
+ return refusal;
824
+ }
825
+ for (const entry of declared.defaults) {
826
+ const refusal = listRefusal(entry.privileges, TABLE_PRIVILEGES, `defaultPrivileges.${entry.forRole}`);
827
+ if (refusal !== undefined)
828
+ return refusal;
829
+ }
830
+ const duplicate = (names) => {
831
+ const seen = new Set;
832
+ for (const name of names) {
833
+ if (seen.has(name))
834
+ return name;
835
+ seen.add(name);
836
+ }
837
+ return;
838
+ };
839
+ const table = duplicate(declared.tables.map((entry) => entry.table));
840
+ if (table !== undefined)
841
+ return { kind: "duplicate", prop: "tables", name: table };
842
+ const column = duplicate(declared.columns.map((entry) => `${entry.table}\x00${entry.column}`));
843
+ if (column !== undefined)
844
+ return { kind: "duplicate", prop: "columnGrants", name: column.replace("\x00", ".") };
845
+ const forRole = duplicate(declared.defaults.map((entry) => entry.forRole));
846
+ if (forRole !== undefined)
847
+ return { kind: "duplicate", prop: "defaultPrivileges", name: forRole };
848
+ return;
849
+ };
850
+ var grantsNamesRefusal = (declared) => {
851
+ const names = [
852
+ declared.role,
853
+ declared.database,
854
+ declared.schema,
855
+ ...declared.tables.map((table) => table.table),
856
+ ...declared.columns.flatMap((column) => [column.table, column.column]),
857
+ ...declared.defaults.map((entry) => entry.forRole)
858
+ ];
859
+ for (const name of names) {
860
+ const refusal = grantsNameByteRefusal(name);
861
+ if (refusal !== undefined)
862
+ return { name, ...refusal };
863
+ }
864
+ return;
865
+ };
866
+
867
+ // src/postgres/grants-plan-restore.ts
868
+ var tableWords = (live, table) => live.tables.find((entry) => entry.table === table)?.role ?? [];
869
+ var columnWords = (live, table, column) => live.columns.find((entry) => entry.table === table && entry.column === column)?.role ?? [];
870
+ var removalRevokes = (removed, live) => {
871
+ const ownedTables = new Set(live.ownedTables);
872
+ return {
873
+ tables: removed.tables.filter((table) => !ownedTables.has(table.table) && tableWords(live, table.table).length > 0).map((table) => table.table),
874
+ columns: removed.columns.filter((column) => !ownedTables.has(column.table) && columnWords(live, column.table, column.column).length > 0).map(({ table, column }) => ({ table, column }))
875
+ };
876
+ };
877
+
878
+ // src/postgres/grants-words.ts
879
+ var encodeWord = (row) => `${row.privilege.toLowerCase()}${row.grantable ? "*" : ""}`;
880
+ var wordsOf = (rows, wantPublic) => [...new Set(rows.filter((row) => row.public === wantPublic).map(encodeWord))].sort();
881
+ var PUBLIC_REVOCABLE_RELKINDS = ["r", "v", "m", "f", "p"];
882
+ var relkindIsPublicRevocable = (relkind) => PUBLIC_REVOCABLE_RELKINDS.includes(relkind);
883
+
884
+ // src/postgres/grants-plan.ts
885
+ var wordsDiffer = (declared, live) => declared.length !== live.length || declared.some((word, index) => word !== live[index]);
886
+ var grantStatements = (words, grant) => {
887
+ const { plain, grantable } = splitGrantWords(words);
888
+ return [
889
+ ...plain.length > 0 ? [grant(plain)] : [],
890
+ ...grantable.length > 0 ? [grant(grantable)] : []
891
+ ];
892
+ };
893
+ var liveTableWords = (live, table) => live.tables.find((entry) => entry.table === table)?.role ?? [];
894
+ var liveColumnWords = (live, table, column) => live.columns.find((entry) => entry.table === table && entry.column === column)?.role ?? [];
895
+ var liveDefaultWords = (live, forRole) => live.defaults.find((entry) => entry.forRole === forRole)?.role ?? [];
896
+ var columnKey = (table, column) => `${table}\x00${column}`;
897
+ var restoredColumnGrants = (declared, live, table, skip) => {
898
+ const statements = [];
899
+ for (const column of live.columns) {
900
+ if (column.table !== table || skip.has(columnKey(column.table, column.column)))
901
+ continue;
902
+ if (column.restorable.length === 0)
903
+ continue;
904
+ statements.push(...grantStatements(column.restorable, (words) => grantColumnSql(declared.schema, column.table, column.column, declared.role, words)));
905
+ }
906
+ return statements;
907
+ };
908
+ var planRepair = (declared, live, prior = {}) => {
909
+ const statements = [];
910
+ const ownedTables = new Set(live.ownedTables);
911
+ if (!live.schemaOwnedByRole && wordsDiffer(declared.schemaPrivileges, live.schema.role)) {
912
+ statements.push(revokeSchemaSql(declared.schema, declared.role));
913
+ statements.push(...grantStatements(declared.schemaPrivileges, (words) => grantSchemaSql(declared.schema, declared.role, words)));
914
+ }
915
+ const revokedTables = new Set(prior.revokedTables ?? []);
916
+ const skipColumns = new Set([
917
+ ...declared.columns.map((column) => columnKey(column.table, column.column)),
918
+ ...(prior.revokedColumns ?? []).map((column) => columnKey(column.table, column.column))
919
+ ]);
920
+ const declaredTableNames = new Set(declared.tables.map((table) => table.table));
921
+ for (const table of declared.tables) {
922
+ if (ownedTables.has(table.table))
923
+ continue;
924
+ if (wordsDiffer(table.privileges, liveTableWords(live, table.table))) {
925
+ revokedTables.add(table.table);
926
+ statements.push(revokeTableSql(declared.schema, table.table, declared.role));
927
+ statements.push(...grantStatements(table.privileges, (words) => grantTableSql(declared.schema, table.table, declared.role, words)));
928
+ statements.push(...restoredColumnGrants(declared, live, table.table, skipColumns));
929
+ }
930
+ }
931
+ for (const table of prior.revokedTables ?? []) {
932
+ if (declaredTableNames.has(table) || ownedTables.has(table))
933
+ continue;
934
+ statements.push(...restoredColumnGrants(declared, live, table, skipColumns));
935
+ }
936
+ for (const column of declared.columns) {
937
+ if (ownedTables.has(column.table))
938
+ continue;
939
+ const liveWords = revokedTables.has(column.table) ? [] : liveColumnWords(live, column.table, column.column);
940
+ if (wordsDiffer(column.privileges, liveWords)) {
941
+ statements.push(revokeColumnSql(declared.schema, column.table, column.column, declared.role));
942
+ statements.push(...grantStatements(column.privileges, (words) => grantColumnSql(declared.schema, column.table, column.column, declared.role, words)));
943
+ }
944
+ }
945
+ for (const entry of declared.defaults) {
946
+ if (wordsDiffer(entry.privileges, liveDefaultWords(live, entry.forRole))) {
947
+ statements.push(revokeDefaultSql(declared.schema, entry.forRole, declared.role));
948
+ statements.push(...grantStatements(entry.privileges, (words) => grantDefaultSql(declared.schema, entry.forRole, declared.role, words)));
949
+ }
950
+ }
951
+ if (declared.publicSchemaRevoked && live.schema.public.length > 0) {
952
+ statements.push(revokePublicSchemaSql(declared.schema));
953
+ }
954
+ if (declared.publicTablesRevoked && (live.tables.some((table) => relkindIsPublicRevocable(table.relkind) && table.public.length > 0) || live.columns.some((column) => column.public.length > 0))) {
955
+ statements.push(revokePublicTablesSql(declared.schema));
956
+ }
957
+ return statements;
958
+ };
959
+ var planRevocations = (removed, live) => {
960
+ const statements = [];
961
+ const revoked = removalRevokes(removed, live);
962
+ for (const table of revoked.tables) {
963
+ statements.push(revokeTableSql(removed.schema, table, removed.role));
964
+ }
965
+ for (const column of revoked.columns) {
966
+ statements.push(revokeColumnSql(removed.schema, column.table, column.column, removed.role));
967
+ }
968
+ for (const entry of removed.defaults) {
969
+ if (liveDefaultWords(live, entry.forRole).length > 0) {
970
+ statements.push(revokeDefaultSql(removed.schema, entry.forRole, removed.role));
971
+ }
972
+ }
973
+ return statements;
974
+ };
975
+ var planReconcile = (declared, live, removed) => {
976
+ const revoked = removed === undefined ? undefined : removalRevokes(removed, live);
977
+ return [
978
+ ...removed !== undefined ? planRevocations(removed, live) : [],
979
+ ...planRepair(declared, live, {
980
+ revokedTables: revoked?.tables ?? [],
981
+ revokedColumns: removed?.columns ?? []
982
+ })
983
+ ];
984
+ };
985
+
986
+ // src/postgres/grants-diff.ts
987
+ var attributesOf = (live, names) => ({
988
+ role: names.role,
989
+ database: names.database,
990
+ schema: names.schema,
991
+ schemaPrivileges: live.schema.role,
992
+ tables: names.tables.map((table) => ({
993
+ table,
994
+ privileges: liveTableWords(live, table)
995
+ })),
996
+ columns: names.columns.map(({ table, column }) => ({
997
+ table,
998
+ column,
999
+ privileges: liveColumnWords(live, table, column)
1000
+ })),
1001
+ defaults: names.defaults.map((forRole) => ({
1002
+ forRole,
1003
+ privileges: liveDefaultWords(live, forRole)
1004
+ })),
1005
+ publicSchemaRevoked: live.schema.public.length === 0,
1006
+ publicTablesRevoked: live.tables.every((table) => !relkindIsPublicRevocable(table.relkind) || table.public.length === 0) && live.columns.every((column) => column.public.length === 0),
1007
+ schemaOwnedByRole: live.schemaOwnedByRole,
1008
+ ownedTables: names.tables.filter((table) => live.ownedTables.includes(table))
1009
+ });
1010
+ var grantsDiffer = (declared, live) => {
1011
+ if (declared.role !== live.role || declared.database !== live.database || declared.schema !== live.schema) {
1012
+ return true;
1013
+ }
1014
+ if (declared.publicSchemaRevoked && !live.publicSchemaRevoked)
1015
+ return true;
1016
+ if (declared.publicTablesRevoked && !live.publicTablesRevoked)
1017
+ return true;
1018
+ if (!live.schemaOwnedByRole && wordsDiffer(declared.schemaPrivileges, live.schemaPrivileges)) {
1019
+ return true;
1020
+ }
1021
+ if (declared.tables.length !== live.tables.length)
1022
+ return true;
1023
+ const ownedTables = new Set(live.ownedTables);
1024
+ if (declared.tables.some((table, index) => table.table !== (live.tables[index]?.table ?? null) || !ownedTables.has(table.table) && wordsDiffer(table.privileges, live.tables[index]?.privileges ?? []))) {
1025
+ return true;
1026
+ }
1027
+ if (declared.columns.length !== live.columns.length)
1028
+ return true;
1029
+ if (declared.columns.some((column, index) => column.table !== (live.columns[index]?.table ?? null) || column.column !== (live.columns[index]?.column ?? null) || !ownedTables.has(column.table) && wordsDiffer(column.privileges, live.columns[index]?.privileges ?? []))) {
1030
+ return true;
1031
+ }
1032
+ if (declared.defaults.length !== live.defaults.length)
1033
+ return true;
1034
+ if (declared.defaults.some((entry, index) => entry.forRole !== (live.defaults[index]?.forRole ?? null) || wordsDiffer(entry.privileges, live.defaults[index]?.privileges ?? []))) {
1035
+ return true;
1036
+ }
1037
+ return false;
1038
+ };
1039
+
1040
+ // src/postgres/grants-existence.ts
1041
+ import * as Effect12 from "effect/Effect";
1042
+
1043
+ // src/postgres/grants-errors.ts
1044
+ import * as Data3 from "effect/Data";
1045
+
1046
+ class PostgresGrantsPrivilegeRefused extends Data3.TaggedError("PostgresGrantsPrivilegeRefused") {
1047
+ get message() {
1048
+ return `Postgres.Grants: "${this.prop}" declares "${this.word}", which is not in the vocabulary ` + "PostgreSQL 18 grants on that object class (see grants-attrs.ts for the three sets, traced " + "to acl.h at REL_18_6). Column grants take select/insert/update/references only — TRUNCATE, " + "TRIGGER and MAINTAIN are table-wide.";
1049
+ }
1050
+ }
1051
+
1052
+ class PostgresGrantsNameRefused extends Data3.TaggedError("PostgresGrantsNameRefused") {
1053
+ get message() {
1054
+ return `Postgres.Grants: the name "${this.name}" is ${String(this.byteLength)} UTF-8 bytes, over the ` + `${String(this.limit)}-byte limit (NAMEDATALEN 64, minus the terminator). PostgreSQL would ` + "accept the statement and silently truncate the name with only a NOTICE, so this plan refuses " + "it instead of granting on an object no later read will ever find again.";
1055
+ }
1056
+ }
1057
+
1058
+ class PostgresGrantsDuplicateObject extends Data3.TaggedError("PostgresGrantsDuplicateObject") {
1059
+ get message() {
1060
+ return `Postgres.Grants: "${this.prop}" declares "${this.name}" twice. Merge the entries into one — ` + "the declaration must state each object at most once, and each privilege word in exactly one " + "form: plain, or WITH GRANT OPTION (`select` and `select*` cannot both hold, the server keeps " + "one aclitem per grantee per grantor).";
1061
+ }
1062
+ }
1063
+
1064
+ class PostgresGrantsRetargetRefused extends Data3.TaggedError("PostgresGrantsRetargetRefused") {
1065
+ get message() {
1066
+ return `Postgres.Grants: the declared ${this.prop} changed from "${this.from}" to "${this.to}". A ` + "retarget is refused at plan — declare a new logical id for the new target and delete the old " + "one (its retain policy keeps the grants until a destroy revokes them).";
1067
+ }
1068
+ }
1069
+
1070
+ class PostgresGrantsDatabaseMismatch extends Data3.TaggedError("PostgresGrantsDatabaseMismatch") {
1071
+ get message() {
1072
+ return `Postgres.Grants: the declaration names database "${this.declared}" but ` + `current_database() is "${this.connected}". The handlers open the declared database and ` + "prove it before any GRANT or REVOKE — a mismatch means that open did not land there.";
1073
+ }
1074
+ }
1075
+
1076
+ class PostgresGrantsSchemaMissing extends Data3.TaggedError("PostgresGrantsSchemaMissing") {
1077
+ get message() {
1078
+ return `Postgres.Grants: schema "${this.schema}" is not in pg_namespace. Grants are written into a ` + "schema that already exists — declare a Postgres.Schema (or create it by hand) before the " + "grant set that scopes to it.";
1079
+ }
1080
+ }
1081
+
1082
+ class PostgresGrantsDatabaseMissing extends Data3.TaggedError("PostgresGrantsDatabaseMissing") {
1083
+ get message() {
1084
+ return `Postgres.Grants: database "${this.database}" is not in pg_database. Grants are written into ` + "a database that already exists — declare a Postgres.Database (or create it by hand) before " + "the grant set that scopes to it.";
1085
+ }
1086
+ }
1087
+
1088
+ class PostgresGrantsRoleMissing extends Data3.TaggedError("PostgresGrantsRoleMissing") {
1089
+ get message() {
1090
+ return `Postgres.Grants: role "${this.role}" is not in pg_roles. GRANT and ALTER DEFAULT PRIVILEGES ` + "would fail against it — create the role (a group role per seat, membership owned by a human) " + "before declaring its grants.";
1091
+ }
1092
+ }
1093
+
1094
+ class PostgresGrantsTableMissing extends Data3.TaggedError("PostgresGrantsTableMissing") {
1095
+ get message() {
1096
+ return `Postgres.Grants: table "${this.schema}"."${this.table}" does not exist. The declaration ` + "names a relation before it is created; create the table (or fix the name) before granting.";
1097
+ }
1098
+ }
1099
+
1100
+ class PostgresGrantsColumnMissing extends Data3.TaggedError("PostgresGrantsColumnMissing") {
1101
+ get message() {
1102
+ return `Postgres.Grants: column "${this.column}" of "${this.schema}"."${this.table}" does not ` + "exist. Declare the column (or fix its name) before granting on it.";
1103
+ }
1104
+ }
1105
+
1106
+ class PostgresGrantsRepairRefused extends Data3.TaggedError("PostgresGrantsRepairRefused") {
1107
+ get message() {
1108
+ return `Postgres.Grants: the repair on schema "${this.schema}" for role "${this.role}" re-read as ` + `still drifted; these statements would run again: ${this.remaining.join(" ; ")}. A grant made ` + "by another role survives this resource’s REVOKE (only its grantor or the object’s " + "owner can revoke it) — revoke it as that grantor or the owner, or declare the privilege so " + "the set matches.";
1109
+ }
1110
+ }
1111
+
1112
+ // src/postgres/grants-existence.ts
1113
+ var TABLES_PRESENT_SQL = `SELECT
1114
+ c.relname AS table
1115
+ FROM pg_class c
1116
+ WHERE c.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = $1)
1117
+ AND c.relname::text IN (SELECT json_array_elements_text($2::json))`;
1118
+ var findMissingTable = (pg, schema, tables) => tables.length === 0 ? Effect12.succeed(undefined) : Effect12.map(pg.unsafe(TABLES_PRESENT_SQL, [schema, JSON.stringify(tables)]), (rows) => {
1119
+ const present = new Set(rows.map((row) => row.table));
1120
+ return tables.find((table) => !present.has(table));
1121
+ });
1122
+ var COLUMNS_PRESENT_SQL = `SELECT
1123
+ c.relname AS table,
1124
+ a.attname AS column
1125
+ FROM pg_attribute a
1126
+ JOIN pg_class c ON c.oid = a.attrelid
1127
+ WHERE c.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = $1)
1128
+ AND a.attnum > 0
1129
+ AND NOT a.attisdropped
1130
+ AND (c.relname, a.attname) IN (
1131
+ SELECT x->>0, x->>1 FROM json_array_elements($2::json) x
1132
+ )`;
1133
+ var findMissingColumn = (pg, schema, columns) => columns.length === 0 ? Effect12.succeed(undefined) : Effect12.map(pg.unsafe(COLUMNS_PRESENT_SQL, [
1134
+ schema,
1135
+ JSON.stringify(columns.map(({ table, column }) => [table, column]))
1136
+ ]), (rows) => {
1137
+ const present = new Set(rows.map((row) => `${row.table}\x00${row.column}`));
1138
+ return columns.find(({ table, column }) => !present.has(`${table}\x00${column}`));
1139
+ });
1140
+ var requireDeclaredObjectsExist = (pg, declared) => Effect12.gen(function* () {
1141
+ const missingTable = yield* findMissingTable(pg, declared.schema, declared.tables.map((table) => table.table));
1142
+ if (missingTable !== undefined) {
1143
+ return yield* Effect12.fail(new PostgresGrantsTableMissing({ schema: declared.schema, table: missingTable }));
1144
+ }
1145
+ const missingColumn = yield* findMissingColumn(pg, declared.schema, declared.columns.map((column) => ({ table: column.table, column: column.column })));
1146
+ if (missingColumn !== undefined) {
1147
+ return yield* Effect12.fail(new PostgresGrantsColumnMissing({
1148
+ schema: declared.schema,
1149
+ table: missingColumn.table,
1150
+ column: missingColumn.column
1151
+ }));
1152
+ }
1153
+ });
1154
+
1155
+ // src/postgres/grants-read.ts
1156
+ import * as Effect15 from "effect/Effect";
1157
+
1158
+ // src/postgres/grants-ownership.ts
1159
+ import * as Effect13 from "effect/Effect";
1160
+ var SCHEMA_OWNER_SQL = `SELECT
1161
+ n.nspowner = (SELECT oid FROM pg_roles WHERE rolname = $2) AS role_owns
1162
+ FROM pg_namespace n
1163
+ WHERE n.nspname = $1`;
1164
+ var readSchemaOwnership = (pg, schema, role) => Effect13.map(pg.unsafe(SCHEMA_OWNER_SQL, [schema, role]), (rows) => rows[0]?.role_owns === true);
1165
+ var OWNED_TABLES_SQL = `SELECT c.relname AS table
1166
+ FROM pg_class c
1167
+ WHERE c.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = $1)
1168
+ AND c.relowner = (SELECT oid FROM pg_roles WHERE rolname = $2)
1169
+ ORDER BY c.relname`;
1170
+ var readOwnedTables = (pg, schema, role) => Effect13.map(pg.unsafe(OWNED_TABLES_SQL, [schema, role]), (rows) => rows.map((row) => row.table).sort((a, b) => a.localeCompare(b)));
1171
+
1172
+ // src/postgres/grants-read-defaults.ts
1173
+ import * as Effect14 from "effect/Effect";
1174
+ var DEFAULTS_SQL = `SELECT
1175
+ pg_get_userbyid(d.defaclrole) AS for_role,
1176
+ a.privilege_type AS privilege,
1177
+ a.is_grantable AS grantable
1178
+ FROM pg_default_acl d
1179
+ CROSS JOIN LATERAL aclexplode(d.defaclacl) AS a
1180
+ WHERE d.defaclnamespace = (SELECT oid FROM pg_namespace WHERE nspname = $1)
1181
+ AND d.defaclobjtype = 'r'
1182
+ AND a.grantee = (SELECT oid FROM pg_roles WHERE rolname = $2)
1183
+ ORDER BY for_role`;
1184
+ var readDefaultAcls = (pg, schema, role) => Effect14.map(pg.unsafe(DEFAULTS_SQL, [schema, role]), (rows) => {
1185
+ const byRole = new Map;
1186
+ for (const row of rows) {
1187
+ const words = byRole.get(row.for_role) ?? [];
1188
+ words.push(encodeWord(row));
1189
+ byRole.set(row.for_role, words);
1190
+ }
1191
+ return [...byRole.entries()].map(([forRole, words]) => ({ forRole, role: [...new Set(words)].sort() })).sort((a, b) => a.forRole.localeCompare(b.forRole));
1192
+ });
1193
+
1194
+ // src/postgres/grants-read.ts
1195
+ var SCHEMA_PRESENT_SQL = "SELECT 1 AS present FROM pg_namespace WHERE nspname = $1";
1196
+ var schemaExists = (pg, schema) => Effect15.map(pg.unsafe(SCHEMA_PRESENT_SQL, [schema]), (rows) => rows.length > 0);
1197
+ var SCHEMA_ACL_SQL = `SELECT
1198
+ a.grantee = 0 AS public,
1199
+ a.privilege_type AS privilege,
1200
+ a.is_grantable AS grantable
1201
+ FROM pg_namespace n
1202
+ CROSS JOIN LATERAL aclexplode(n.nspacl) AS a
1203
+ WHERE n.nspname = $1
1204
+ AND (a.grantee = 0 OR a.grantee = (SELECT oid FROM pg_roles WHERE rolname = $2))`;
1205
+ var readSchemaAcl = (pg, schema, role) => Effect15.map(pg.unsafe(SCHEMA_ACL_SQL, [schema, role]), (rows) => ({
1206
+ role: wordsOf(rows, false),
1207
+ public: wordsOf(rows, true)
1208
+ }));
1209
+ var TABLES_SQL = `SELECT
1210
+ c.relname AS table,
1211
+ c.relkind::text AS relkind,
1212
+ a.grantee = 0 AS public,
1213
+ a.privilege_type AS privilege,
1214
+ a.is_grantable AS grantable
1215
+ FROM pg_class c
1216
+ CROSS JOIN LATERAL aclexplode(c.relacl) AS a
1217
+ WHERE c.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = $1)
1218
+ AND (a.grantee = 0 OR a.grantee = (SELECT oid FROM pg_roles WHERE rolname = $2))
1219
+ ORDER BY c.relname`;
1220
+ var readTableAcls = (pg, schema, role) => Effect15.map(pg.unsafe(TABLES_SQL, [
1221
+ schema,
1222
+ role
1223
+ ]), (rows) => {
1224
+ const byTable = new Map;
1225
+ for (const row of rows) {
1226
+ const entry = byTable.get(row.table) ?? { relkind: row.relkind, role: [], public: [] };
1227
+ entry[row.public ? "public" : "role"].push(encodeWord(row));
1228
+ byTable.set(row.table, entry);
1229
+ }
1230
+ return [...byTable.entries()].map(([table, entry]) => ({
1231
+ table,
1232
+ relkind: entry.relkind,
1233
+ role: [...new Set(entry.role)].sort(),
1234
+ public: [...new Set(entry.public)].sort()
1235
+ })).sort((a, b) => a.table.localeCompare(b.table));
1236
+ });
1237
+ var COLUMNS_SQL = `SELECT
1238
+ c.relname AS table,
1239
+ v.attname AS column,
1240
+ a.grantee = 0 AS public,
1241
+ a.privilege_type AS privilege,
1242
+ a.is_grantable AS grantable,
1243
+ pg_get_userbyid(a.grantor) AS grantor,
1244
+ pg_get_userbyid(c.relowner) AS owner,
1245
+ current_user AS revoker,
1246
+ pg_has_role(current_user, c.relowner, 'USAGE') AS revoker_as_owner
1247
+ FROM pg_attribute v
1248
+ JOIN pg_class c ON c.oid = v.attrelid
1249
+ CROSS JOIN LATERAL aclexplode(v.attacl) AS a
1250
+ WHERE c.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = $1)
1251
+ AND v.attnum > 0
1252
+ AND NOT v.attisdropped
1253
+ AND (a.grantee = 0 OR a.grantee = (SELECT oid FROM pg_roles WHERE rolname = $2))
1254
+ ORDER BY c.relname, v.attname`;
1255
+ var readColumnAcls = (pg, schema, role) => Effect15.map(pg.unsafe(COLUMNS_SQL, [schema, role]), (rows) => {
1256
+ const byColumn = new Map;
1257
+ for (const row of rows) {
1258
+ const key = `${row.table}\x00${row.column}`;
1259
+ const entry = byColumn.get(key) ?? { role: [], public: [], restorable: [] };
1260
+ const word = encodeWord(row);
1261
+ entry[row.public ? "public" : "role"].push(word);
1262
+ if (!row.public && (row.grantor === row.revoker || row.grantor === row.owner && row.revoker_as_owner)) {
1263
+ entry.restorable.push(word);
1264
+ }
1265
+ byColumn.set(key, entry);
1266
+ }
1267
+ return [...byColumn.entries()].map(([key, entry]) => {
1268
+ const [table, column] = key.split("\x00");
1269
+ return {
1270
+ table,
1271
+ column,
1272
+ role: [...new Set(entry.role)].sort(),
1273
+ public: [...new Set(entry.public)].sort(),
1274
+ restorable: [...new Set(entry.restorable)].sort()
1275
+ };
1276
+ }).sort((a, b) => a.table.localeCompare(b.table) || a.column.localeCompare(b.column));
1277
+ });
1278
+ var readGrants = (pg, schema, role) => Effect15.all({
1279
+ schemaExists: schemaExists(pg, schema),
1280
+ schema: readSchemaAcl(pg, schema, role),
1281
+ tables: readTableAcls(pg, schema, role),
1282
+ columns: readColumnAcls(pg, schema, role),
1283
+ defaults: readDefaultAcls(pg, schema, role),
1284
+ schemaOwnedByRole: readSchemaOwnership(pg, schema, role),
1285
+ ownedTables: readOwnedTables(pg, schema, role)
1286
+ });
1287
+
1288
+ // src/postgres/grants-ops.ts
1289
+ var failRefusal = (refusal) => refusal.kind === "privilege" ? Effect16.fail(new PostgresGrantsPrivilegeRefused({ prop: refusal.prop, word: refusal.word })) : Effect16.fail(new PostgresGrantsDuplicateObject({ prop: refusal.prop, name: refusal.name }));
1290
+ var proveDatabase = (pg, declared) => Effect16.flatMap(currentDatabase(pg), (connected) => connected === declared ? Effect16.void : Effect16.fail(new PostgresGrantsDatabaseMismatch({ declared, connected })));
1291
+ var refuseNames = (props) => {
1292
+ const declared = resolveProps(props);
1293
+ const names = grantsNamesRefusal(declared);
1294
+ return names === undefined ? undefined : new PostgresGrantsNameRefused({
1295
+ name: names.name,
1296
+ byteLength: names.byteLength,
1297
+ limit: names.limit
1298
+ });
1299
+ };
1300
+ var reconcileWithClient3 = (pg, props, output) => Effect16.gen(function* () {
1301
+ const declared = resolveProps(props);
1302
+ const declRefusal = declarationRefusal(declared);
1303
+ if (declRefusal !== undefined)
1304
+ return yield* failRefusal(declRefusal);
1305
+ const nameRefusal = refuseNames(props);
1306
+ if (nameRefusal !== undefined)
1307
+ return yield* Effect16.fail(nameRefusal);
1308
+ for (const [prop, from, to] of [
1309
+ ["role", output?.role, declared.role],
1310
+ ["database", output?.database, declared.database],
1311
+ ["schema", output?.schema, declared.schema]
1312
+ ]) {
1313
+ if (from !== undefined && from !== to) {
1314
+ return yield* Effect16.fail(new PostgresGrantsRetargetRefused({ prop, from, to }));
1315
+ }
1316
+ }
1317
+ yield* proveDatabase(pg, declared.database);
1318
+ if (!(yield* schemaExists(pg, declared.schema))) {
1319
+ return yield* Effect16.fail(new PostgresGrantsSchemaMissing({ schema: declared.schema }));
1320
+ }
1321
+ for (const role of [declared.role, ...declared.defaults.map((entry) => entry.forRole)]) {
1322
+ if (!(yield* roleExists(pg, role))) {
1323
+ return yield* Effect16.fail(new PostgresGrantsRoleMissing({ role }));
1324
+ }
1325
+ }
1326
+ yield* requireDeclaredObjectsExist(pg, declared);
1327
+ const removed = removedNames(output, declared);
1328
+ const current = yield* readGrants(pg, declared.schema, declared.role);
1329
+ const planned = planReconcile(declared, current, removed);
1330
+ if (planned.length > 0)
1331
+ yield* pg.transaction(planned);
1332
+ const after = yield* readGrants(pg, declared.schema, declared.role);
1333
+ const remaining = planReconcile(declared, after, removed);
1334
+ if (remaining.length > 0) {
1335
+ return yield* Effect16.fail(new PostgresGrantsRepairRefused({
1336
+ schema: declared.schema,
1337
+ role: declared.role,
1338
+ remaining
1339
+ }));
1340
+ }
1341
+ return attributesOf(after, declaredNames(declared));
1342
+ });
1343
+ var readWithClient3 = (pg, names, stored) => Effect16.gen(function* () {
1344
+ yield* proveDatabase(pg, names.database);
1345
+ if (!(yield* schemaExists(pg, names.schema)))
1346
+ return;
1347
+ const live = yield* readGrants(pg, names.schema, names.role);
1348
+ const projected = attributesOf(live, names);
1349
+ const holdsAnything = projected.schemaPrivileges.length > 0 || projected.tables.some((table) => table.privileges.length > 0) || projected.columns.some((column) => column.privileges.length > 0) || projected.defaults.some((entry) => entry.privileges.length > 0);
1350
+ return stored || holdsAnything ? projected : undefined;
1351
+ });
1352
+ var deleteWithClient2 = (pg, props) => Effect16.gen(function* () {
1353
+ yield* proveDatabase(pg, props.database);
1354
+ if (!(yield* roleExists(pg, props.role)))
1355
+ return;
1356
+ if (!(yield* schemaExists(pg, props.schema)))
1357
+ return;
1358
+ const cleared = clearedDeclaration(resolveProps(props));
1359
+ const live = yield* readGrants(pg, cleared.schema, cleared.role);
1360
+ const planned = planRepair(cleared, live);
1361
+ if (planned.length > 0)
1362
+ yield* pg.transaction(planned);
1363
+ const after = yield* readGrants(pg, cleared.schema, cleared.role);
1364
+ const remaining = planRepair(cleared, after);
1365
+ if (remaining.length > 0) {
1366
+ return yield* Effect16.fail(new PostgresGrantsRepairRefused({
1367
+ schema: cleared.schema,
1368
+ role: cleared.role,
1369
+ remaining
1370
+ }));
1371
+ }
1372
+ });
1373
+ var diffPostgresGrants = (news, output) => Effect16.gen(function* () {
1374
+ if (output === undefined || !isResolved3(news))
1375
+ return;
1376
+ const declared = resolveProps(news);
1377
+ for (const [prop, from, to] of [
1378
+ ["role", output.role, declared.role],
1379
+ ["database", output.database, declared.database],
1380
+ ["schema", output.schema, declared.schema]
1381
+ ]) {
1382
+ if (from !== to) {
1383
+ return yield* Effect16.fail(new PostgresGrantsRetargetRefused({ prop, from, to }));
1384
+ }
1385
+ }
1386
+ const nameRefusal = refuseNames(news);
1387
+ if (nameRefusal !== undefined)
1388
+ return yield* Effect16.fail(nameRefusal);
1389
+ const declRefusal = declarationRefusal(declared);
1390
+ if (declRefusal !== undefined)
1391
+ return yield* failRefusal(declRefusal);
1392
+ return grantsDiffer(declared, output) ? { action: "update" } : { action: "noop" };
1393
+ });
1394
+
1395
+ // src/postgres/grants.ts
1396
+ var PostgresGrants = Resource3("Postgres.Grants", {
1397
+ defaultRemovalPolicy: "retain"
1398
+ });
1399
+ var isPostgresGrants = (value) => (typeof value === "object" || typeof value === "function") && value !== null && value.Type === "Postgres.Grants";
1400
+ var postgresGrantsHandlers = PostgresGrants.Provider.of({
1401
+ list: () => Effect17.succeed([]),
1402
+ nuke: { skip: true },
1403
+ read: ({ olds, output }) => Effect17.gen(function* () {
1404
+ const names = output !== undefined ? namesFromAttrs(output) : declaredNames(resolveProps(olds));
1405
+ const present = yield* withPg((pg) => databaseExists(pg, names.database));
1406
+ if (!present)
1407
+ return;
1408
+ const live = yield* withPg((pg) => readWithClient3(pg, names, output !== undefined), names.database);
1409
+ return live === undefined ? undefined : Unowned3(live);
1410
+ }),
1411
+ diff: ({ news, output }) => diffPostgresGrants(news, output),
1412
+ reconcile: ({ news, output }) => Effect17.gen(function* () {
1413
+ const present = yield* withPg((pg) => databaseExists(pg, news.database));
1414
+ if (!present) {
1415
+ return yield* Effect17.fail(new PostgresGrantsDatabaseMissing({ database: news.database }));
1416
+ }
1417
+ return yield* withPg((pg) => reconcileWithClient3(pg, news, output), news.database);
1418
+ }),
1419
+ delete: ({ olds }) => Effect17.gen(function* () {
1420
+ const present = yield* withPg((pg) => databaseExists(pg, olds.database));
1421
+ if (!present)
1422
+ return;
1423
+ return yield* withPg((pg) => deleteWithClient2(pg, olds), olds.database);
1424
+ })
1425
+ });
1426
+ var PostgresGrantsProvider = () => Provider3.succeed(PostgresGrants, postgresGrantsHandlers);
1427
+ // src/postgres/role-attrs.ts
1428
+ var VALID_UNTIL_OFFSET = /(?:Z|[+-]\d{2}:\d{2})$/;
1429
+ var validUntilRefusal = (value) => {
1430
+ if (Number.isNaN(Date.parse(value)))
1431
+ return { reason: "unparseable" };
1432
+ if (VALID_UNTIL_OFFSET.test(value))
1433
+ return;
1434
+ return { reason: "zone-free" };
1435
+ };
1436
+ var sameValidUntil = (declared, live) => live !== null && Date.parse(declared) === Date.parse(live);
1437
+ // src/postgres/role-sql.ts
1438
+ import * as Effect19 from "effect/Effect";
1439
+
1440
+ // src/postgres/role-membership-sql.ts
1441
+ import * as Effect18 from "effect/Effect";
1442
+
1443
+ // src/postgres/role-errors.ts
1444
+ import * as Data4 from "effect/Data";
1445
+
1446
+ class PostgresRoleNameRefused extends Data4.TaggedError("PostgresRoleNameRefused") {
1447
+ get message() {
1448
+ return `Postgres.Role "${this.name}": ${String(this.byteLength)} UTF-8 bytes, over the ` + `${String(this.limit)}-byte limit (NAMEDATALEN 64, minus the terminator). PostgreSQL would ` + "accept the CREATE and silently truncate the name with only a NOTICE, so this plan refuses " + "it instead of creating a role no later read will ever find again.";
1449
+ }
1450
+ }
1451
+
1452
+ class PostgresRoleRenameRefused extends Data4.TaggedError("PostgresRoleRenameRefused") {
1453
+ get message() {
1454
+ return `Postgres.Role: the declared name changed from "${this.from}" to "${this.to}". A ` + "rename is refused at plan — this provider never replaces a role (that is DROP then " + "CREATE, which loses memberships, grants and ownerships). Declare a new logical id for " + "the new name, and remove the old one once its objects have moved.";
1455
+ }
1456
+ }
1457
+
1458
+ class PostgresRolePasswordEnvUnsetError extends Data4.TaggedError("PostgresRolePasswordEnvUnsetError") {
1459
+ get message() {
1460
+ return `Postgres.Role "${this.role}": password.fromEnv "${this.variable}" is unset or empty ` + "in the deploying process, but this write requires a password this stack has not sealed.";
1461
+ }
1462
+ }
1463
+
1464
+ class PostgresRolePrivilegedRefused extends Data4.TaggedError("PostgresRolePrivilegedRefused") {
1465
+ get message() {
1466
+ return `Postgres.Role "${this.role}": the live role has ${this.flags.join(", ")} set. This ` + "family never declares those attributes (a create lands on the server default, all off), " + "and they are not inherited — a member who SET ROLEs to this role would exercise them. " + "Clear the flag on the cluster before this stack adopts the role.";
1467
+ }
1468
+ }
1469
+
1470
+ class PostgresRoleValidUntilRefused extends Data4.TaggedError("PostgresRoleValidUntilRefused") {
1471
+ get message() {
1472
+ return `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"}. ` + 'Declare an explicit-zone ISO 8601 value such as "2027-01-01T00:00:00Z" — PostgreSQL would ' + "read a zone-free value in the session time zone while the plan compares UTC, and the two " + "would never agree.";
1473
+ }
1474
+ }
1475
+
1476
+ class PostgresRoleCreateVanished extends Data4.TaggedError("PostgresRoleCreateVanished") {
1477
+ get message() {
1478
+ return `Postgres.Role "${this.role}": CREATE ROLE raised no error, but the role is still ` + "absent on the immediate re-read. Re-plan — this is not a state this provider expects to reach.";
1479
+ }
1480
+ }
1481
+
1482
+ class PostgresRoleMembershipUnrepaired extends Data4.TaggedError("PostgresRoleMembershipUnrepaired") {
1483
+ get message() {
1484
+ const who = this.grantor === null ? "an unresolvable grantor (inconsistent catalog)" : `grantor "${this.grantor}"`;
1485
+ return `Postgres.Role "${this.role}": membership of "${this.parent}" ` + `(${this.declared ? "declared, options unsafe" : "not declared"}) could not be repaired ` + `under ${who}. The server refused the repair or the catalog re-read still found the row ` + "outside the declaration. pg_auth_members keys a membership on (parent, member, grantor). " + "Check dependent grants, executor permissions and grantor identity before re-planning.";
1486
+ }
1487
+ }
1488
+
1489
+ class PostgresRoleParentMissing extends Data4.TaggedError("PostgresRoleParentMissing") {
1490
+ get message() {
1491
+ return `Postgres.Role "${this.role}": memberOf parent "${this.parent}" does not exist. ` + "Nothing was written — declare that role first (or drop it from memberOf). A GRANT of a " + "missing role fails only after CREATE ROLE has committed, and the next plan then needs " + "--adopt to finish the orphan.";
1492
+ }
1493
+ }
1494
+
1495
+ class PostgresRoleIdentityRefused extends Data4.TaggedError("PostgresRoleIdentityRefused") {
1496
+ get message() {
1497
+ return `Postgres.Role "${this.role}": the live oid is ${String(this.liveOid)}, but state recorded ` + `${String(this.storedOid)}. The name was dropped and recreated outside this stack. Nothing ` + "was altered and the live role was not dropped — remove the stale state, or adopt the new " + "role under a new logical id once you mean to own it.";
1498
+ }
1499
+ }
1500
+
1501
+ class PostgresRolePasswordNonAsciiRefused extends Data4.TaggedError("PostgresRolePasswordNonAsciiRefused") {
1502
+ get message() {
1503
+ return `Postgres.Role "${this.role}": password must be ASCII; this provider does not implement PostgreSQL SASLprep normalization. Nothing was written.`;
1504
+ }
1505
+ }
1506
+
1507
+ // src/postgres/role-membership-sql.ts
1508
+ var MEMBERSHIP_SQL = `SELECT parent.rolname AS parent,
1509
+ grantor.rolname AS grantor,
1510
+ m.admin_option AS admin,
1511
+ m.set_option AS set,
1512
+ m.inherit_option AS inherit
1513
+ FROM pg_auth_members m
1514
+ JOIN pg_roles member ON member.oid = m.member
1515
+ JOIN pg_roles parent ON parent.oid = m.roleid
1516
+ LEFT JOIN pg_roles grantor ON grantor.oid = m.grantor
1517
+ WHERE member.rolname = $1
1518
+ ORDER BY parent.rolname, grantor.rolname NULLS FIRST`;
1519
+ var selectRoleMemberships = (pg, name) => Effect18.map(pg.unsafe(MEMBERSHIP_SQL, [name]), (rows) => rows);
1520
+ var buildGrantMembershipSql = (member, parent) => `GRANT ${quoteIdent(parent)} TO ${quoteIdent(member)} WITH SET FALSE`;
1521
+ var buildRepairMembershipSql = (member, parent, grantor, option) => `REVOKE ${option} OPTION FOR ${quoteIdent(parent)} FROM ${quoteIdent(member)} GRANTED BY ${quoteIdent(grantor)} RESTRICT`;
1522
+ var buildRevokeGrantorMembershipSql = (member, parent, grantor) => `REVOKE ${quoteIdent(parent)} FROM ${quoteIdent(member)} GRANTED BY ${quoteIdent(grantor)}`;
1523
+ var unsafeMemberships = (live, inherit) => new Set((live.memberships ?? []).filter((row) => row.admin || row.set || row.inherit && !inherit).map((row) => row.parent));
1524
+ var membershipDrift = (declared, live, unsafe = new Set) => {
1525
+ if (declared === undefined)
1526
+ return { grants: [], revokes: [] };
1527
+ const wanted = [...new Set(declared)].sort();
1528
+ const current = [...new Set(live)].sort();
1529
+ return {
1530
+ grants: wanted.filter((parent) => !current.includes(parent) || unsafe.has(parent)),
1531
+ revokes: [
1532
+ ...current.filter((parent) => !wanted.includes(parent)),
1533
+ ...wanted.filter((parent) => current.includes(parent) && unsafe.has(parent))
1534
+ ]
1535
+ };
1536
+ };
1537
+ var assertParentsExist = (pg, role, parents) => Effect18.gen(function* () {
1538
+ if (parents === undefined)
1539
+ return;
1540
+ for (const parent of [...new Set(parents)].sort()) {
1541
+ if (!(yield* roleExists(pg, parent))) {
1542
+ return yield* Effect18.fail(new PostgresRoleParentMissing({ role, parent }));
1543
+ }
1544
+ }
1545
+ });
1546
+ var syncMemberships = (pg, props, rows) => Effect18.gen(function* () {
1547
+ if (props.memberOf === undefined) {
1548
+ return [...new Set(rows.map((row) => row.parent))].sort();
1549
+ }
1550
+ yield* assertParentsExist(pg, props.name, props.memberOf);
1551
+ const wanted = [...new Set(props.memberOf)].sort();
1552
+ const revokes = [];
1553
+ for (const row of rows) {
1554
+ if (!wanted.includes(row.parent) && row.grantor !== null) {
1555
+ revokes.push(buildRevokeGrantorMembershipSql(props.name, row.parent, row.grantor));
1556
+ }
1557
+ }
1558
+ if (revokes.length > 0)
1559
+ yield* pg.transaction(revokes);
1560
+ for (const row of rows) {
1561
+ if (row.grantor === null || !wanted.includes(row.parent))
1562
+ continue;
1563
+ const statements = [];
1564
+ for (const option of ["ADMIN", "SET", "INHERIT"]) {
1565
+ const unsafe = option === "ADMIN" ? row.admin : option === "SET" ? row.set : row.inherit && !props.inherit;
1566
+ if (unsafe) {
1567
+ statements.push(buildRepairMembershipSql(props.name, row.parent, row.grantor, option));
1568
+ }
1569
+ }
1570
+ if (statements.length > 0)
1571
+ yield* pg.transaction(statements).pipe(Effect18.catchTag("SqlError", (error) => {
1572
+ const cause = error.reason.cause;
1573
+ const code = typeof cause === "object" && cause !== null ? cause.code : undefined;
1574
+ return Effect18.fail(code === "2BP01" || code === "42501" ? new PostgresRoleMembershipUnrepaired({
1575
+ role: props.name,
1576
+ parent: row.parent,
1577
+ grantor: row.grantor,
1578
+ declared: true
1579
+ }) : error);
1580
+ }));
1581
+ }
1582
+ const grants = wanted.filter((p) => !rows.some((row) => row.parent === p)).map((parent) => buildGrantMembershipSql(props.name, parent));
1583
+ if (grants.length > 0)
1584
+ yield* pg.transaction(grants);
1585
+ for (const row of yield* selectRoleMemberships(pg, props.name)) {
1586
+ if (!wanted.includes(row.parent) || row.admin || row.set || row.inherit && !props.inherit) {
1587
+ return yield* Effect18.fail(new PostgresRoleMembershipUnrepaired({
1588
+ role: props.name,
1589
+ parent: row.parent,
1590
+ grantor: row.grantor,
1591
+ declared: wanted.includes(row.parent)
1592
+ }));
1593
+ }
1594
+ }
1595
+ return wanted;
1596
+ });
1597
+
1598
+ // src/postgres/role-sql.ts
1599
+ var SELECT_ROLE_SQL = `SELECT
1600
+ r.oid AS oid,
1601
+ r.rolname AS name,
1602
+ r.rolcanlogin AS login,
1603
+ r.rolconnlimit AS "connectionLimit",
1604
+ r.rolinherit AS inherit,
1605
+ r.rolsuper AS superuser,
1606
+ r.rolcreaterole AS createrole,
1607
+ r.rolcreatedb AS createdb,
1608
+ r.rolreplication AS replication,
1609
+ r.rolbypassrls AS bypassrls,
1610
+ to_char(r.rolvaliduntil AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') AS "validUntil"
1611
+ FROM pg_roles r
1612
+ WHERE r.rolname = $1`;
1613
+ var selectRole = (pg, name) => Effect19.map(pg.unsafe(SELECT_ROLE_SQL, [name]), (rows) => rows[0]);
1614
+ var readRoleWithClient = (pg, name) => Effect19.gen(function* () {
1615
+ const row = yield* selectRole(pg, name);
1616
+ if (row === undefined)
1617
+ return;
1618
+ const memberships = yield* selectRoleMemberships(pg, name);
1619
+ return {
1620
+ ...row,
1621
+ memberOf: [...new Set(memberships.map((membership) => membership.parent))].sort(),
1622
+ memberships
1623
+ };
1624
+ });
1625
+ var withOptions2 = (props) => {
1626
+ const parts = [];
1627
+ parts.push(props.login ? "LOGIN" : "NOLOGIN");
1628
+ parts.push(props.inherit ? "INHERIT" : "NOINHERIT");
1629
+ parts.push(`CONNECTION LIMIT ${String(Math.trunc(props.connectionLimit))}`);
1630
+ if (props.validUntil !== undefined) {
1631
+ parts.push(`VALID UNTIL ${quoteStringLiteral(props.validUntil)}`);
1632
+ }
1633
+ return parts.join(" ");
1634
+ };
1635
+ var buildCreateRoleSql = (props) => `CREATE ROLE ${quoteIdent(props.name)} WITH ${withOptions2(props)}`;
1636
+ var buildAlterRoleSql = (name, change) => {
1637
+ const who = quoteIdent(name);
1638
+ switch (change.prop) {
1639
+ case "login":
1640
+ return `ALTER ROLE ${who} WITH ${change.value ? "LOGIN" : "NOLOGIN"}`;
1641
+ case "inherit":
1642
+ return `ALTER ROLE ${who} WITH ${change.value ? "INHERIT" : "NOINHERIT"}`;
1643
+ case "connectionLimit":
1644
+ return `ALTER ROLE ${who} WITH CONNECTION LIMIT ${String(Math.trunc(change.value))}`;
1645
+ case "validUntil":
1646
+ return `ALTER ROLE ${who} WITH VALID UNTIL ${quoteStringLiteral(change.value)}`;
1647
+ }
1648
+ };
1649
+ var buildSetPasswordSql = (name, verifier) => `ALTER ROLE ${quoteIdent(name)} WITH PASSWORD ${quoteStringLiteral(verifier)}`;
1650
+ var buildDropRoleSql = (name) => `DROP ROLE IF EXISTS ${quoteIdent(name)}`;
1651
+ var buildCreateStatements = (props, verifier) => [
1652
+ buildCreateRoleSql(props),
1653
+ ...verifier === undefined ? [] : [buildSetPasswordSql(props.name, verifier)],
1654
+ ...[...new Set(props.memberOf ?? [])].sort().map((parent) => buildGrantMembershipSql(props.name, parent))
1655
+ ];
1656
+ var scalarDrift = (props, live) => {
1657
+ const changes = [];
1658
+ if (props.login !== live.login)
1659
+ changes.push({ prop: "login", value: props.login });
1660
+ if (props.inherit !== live.inherit)
1661
+ changes.push({ prop: "inherit", value: props.inherit });
1662
+ if (props.connectionLimit !== live.connectionLimit) {
1663
+ changes.push({ prop: "connectionLimit", value: props.connectionLimit });
1664
+ }
1665
+ if (props.validUntil !== undefined && !sameValidUntil(props.validUntil, live.validUntil)) {
1666
+ changes.push({ prop: "validUntil", value: props.validUntil });
1667
+ }
1668
+ return changes;
1669
+ };
1670
+ var declarationMatches = (props, live) => {
1671
+ const wanted = props.memberOf === undefined ? undefined : [...new Set(props.memberOf)].sort().join("\x00");
1672
+ const found = [...live.memberOf ?? []].sort().join("\x00");
1673
+ return props.name === live.name && props.login === live.login && props.inherit === live.inherit && props.connectionLimit === live.connectionLimit && (props.validUntil === undefined || sameValidUntil(props.validUntil, live.validUntil)) && (wanted === undefined || wanted === found) && privilegedFlags(live).length === 0 && (live.memberships ?? []).every((row) => !row.admin && !row.set && (!row.inherit || props.inherit));
1674
+ };
1675
+ var storedAttributes = (live) => {
1676
+ const { memberships: _observeOnly, ...stored } = live;
1677
+ return stored;
1678
+ };
1679
+ var privilegedFlags = (live) => {
1680
+ const flags = [];
1681
+ if (live.superuser === true)
1682
+ flags.push("SUPERUSER");
1683
+ if (live.createrole === true)
1684
+ flags.push("CREATEROLE");
1685
+ if (live.createdb === true)
1686
+ flags.push("CREATEDB");
1687
+ if (live.replication === true)
1688
+ flags.push("REPLICATION");
1689
+ if (live.bypassrls === true)
1690
+ flags.push("BYPASSRLS");
1691
+ return flags;
1692
+ };
1693
+ // src/postgres/role.ts
1694
+ import { Resource as Resource4 } from "alchemy";
1695
+ import * as Effect21 from "effect/Effect";
1696
+ import * as Redacted2 from "effect/Redacted";
1697
+
1698
+ // src/postgres/role-secrets.ts
1699
+ import * as Redacted from "effect/Redacted";
1700
+ import * as Effect20 from "effect/Effect";
1701
+
1702
+ // src/secrets/write-only.ts
1703
+ import { Buffer } from "node:buffer";
1704
+ import { randomBytes, scryptSync, timingSafeEqual } from "node:crypto";
1705
+ var resolveAll = (refs, env = process.env) => {
1706
+ const values = {};
1707
+ const missing = [];
1708
+ for (const [key, ref] of Object.entries(refs)) {
1709
+ const value = env[ref.fromEnv];
1710
+ if (value === undefined || value === "")
1711
+ missing.push(ref.fromEnv);
1712
+ else
1713
+ values[key] = value;
1714
+ }
1715
+ return { missing: [...new Set(missing)].sort(), values };
1716
+ };
1717
+ var COST = { N: 16384, maxmem: 64 * 1024 * 1024, p: 1, r: 8 };
1718
+ var PREFIX = "scrypt";
1719
+ var canonical = (values) => JSON.stringify(Object.keys(values).sort().map((key) => [key, values[key]]));
1720
+ var digest = (salt, values) => scryptSync(canonical(values), salt, 32, COST);
1721
+ var seal = (values, salt) => {
1722
+ if (Object.keys(values).length === 0)
1723
+ return "";
1724
+ const bytes = salt === undefined ? randomBytes(16) : Buffer.from(salt, "utf8");
1725
+ return `${PREFIX}:${bytes.toString("base64url")}:${digest(bytes, values).toString("base64url")}`;
1726
+ };
1727
+ var sealMatches = (sealed, values) => {
1728
+ if (sealed === "")
1729
+ return Object.keys(values).length === 0;
1730
+ const [prefix, salt, stored, extra] = sealed.split(":");
1731
+ if (prefix !== PREFIX || salt === undefined || stored === undefined || extra !== undefined) {
1732
+ return false;
1733
+ }
1734
+ const expected = Buffer.from(stored, "base64url");
1735
+ const actual = digest(Buffer.from(salt, "base64url"), values);
1736
+ return expected.length === actual.length && timingSafeEqual(expected, actual);
1737
+ };
1738
+
1739
+ // src/postgres/role-secrets.ts
1740
+ var assertPasswordAscii = (role, resolved) => resolved.value !== undefined && /[\u0080-\uffff]/.test(Redacted.value(resolved.value)) ? Effect20.fail(new PostgresRolePasswordNonAsciiRefused({ role })) : Effect20.void;
1741
+ var resolvePassword = (props, env = process.env) => {
1742
+ if (props.password === undefined)
1743
+ return { variable: undefined, value: undefined };
1744
+ const { values } = resolveAll({ password: props.password }, env);
1745
+ const raw = values["password"];
1746
+ return {
1747
+ variable: props.password.fromEnv,
1748
+ value: raw === undefined ? undefined : Redacted.make(raw)
1749
+ };
1750
+ };
1751
+ var sealPassword = (value2) => seal({ password: Redacted.value(value2) });
1752
+ var passwordMatchesSeal = (value2, sealed) => sealMatches(sealed, { password: Redacted.value(value2) });
1753
+
1754
+ // src/postgres/role-scram.ts
1755
+ import { createHash, createHmac, pbkdf2Sync, randomBytes as randomBytes2 } from "node:crypto";
1756
+ var ITERATIONS = 4096;
1757
+ var SALT_BYTES = 16;
1758
+ var KEY_BYTES = 32;
1759
+ var hmac = (key, label) => createHmac("sha256", key).update(label, "utf8").digest();
1760
+ var b64 = (value2) => value2.toString("base64");
1761
+ var scramSha256Verifier = (password, salt) => {
1762
+ const bytes = salt ?? randomBytes2(SALT_BYTES);
1763
+ const saltedPassword = pbkdf2Sync(password, bytes, ITERATIONS, KEY_BYTES, "sha256");
1764
+ const storedKey = createHash("sha256").update(hmac(saltedPassword, "Client Key")).digest();
1765
+ const serverKey = hmac(saltedPassword, "Server Key");
1766
+ return `SCRAM-SHA-256$${ITERATIONS}:${b64(bytes)}$${b64(storedKey)}:${b64(serverKey)}`;
1767
+ };
1768
+
1769
+ // src/postgres/role.ts
1770
+ var PostgresRole = Resource4("Postgres.Role", {
1771
+ defaultRemovalPolicy: "retain"
1772
+ });
1773
+ var isPostgresRole = (value3) => (typeof value3 === "object" || typeof value3 === "function") && value3 !== null && value3.Type === "Postgres.Role";
1774
+ var reconcileWithClient4 = (pg, props, env = process.env, previousSeal = "", storedOid) => Effect21.gen(function* () {
1775
+ const resolved = resolvePassword(props, env);
1776
+ yield* assertPasswordAscii(props.name, resolved);
1777
+ const observed = yield* readRoleWithClient(pg, props.name);
1778
+ if (observed === undefined) {
1779
+ if (resolved.variable !== undefined && resolved.value === undefined) {
1780
+ return yield* Effect21.fail(new PostgresRolePasswordEnvUnsetError({ role: props.name, variable: resolved.variable }));
1781
+ }
1782
+ yield* assertParentsExist(pg, props.name, props.memberOf);
1783
+ const verifier = resolved.value === undefined ? undefined : scramSha256Verifier(Redacted2.value(resolved.value));
1784
+ yield* pg.transaction(buildCreateStatements(props, verifier));
1785
+ const created = yield* readRoleWithClient(pg, props.name);
1786
+ if (created === undefined) {
1787
+ return yield* Effect21.fail(new PostgresRoleCreateVanished({ role: props.name }));
1788
+ }
1789
+ return {
1790
+ ...storedAttributes(created),
1791
+ passwordSeal: resolved.value === undefined ? "" : sealPassword(resolved.value)
1792
+ };
1793
+ }
1794
+ if (storedOid !== undefined && observed.oid !== storedOid) {
1795
+ return yield* Effect21.fail(new PostgresRoleIdentityRefused({
1796
+ role: props.name,
1797
+ storedOid,
1798
+ liveOid: observed.oid
1799
+ }));
1800
+ }
1801
+ const flags = privilegedFlags(observed);
1802
+ if (flags.length > 0) {
1803
+ return yield* Effect21.fail(new PostgresRolePrivilegedRefused({ role: props.name, flags }));
1804
+ }
1805
+ if (resolved.variable !== undefined && resolved.value === undefined && previousSeal === "") {
1806
+ return yield* Effect21.fail(new PostgresRolePasswordEnvUnsetError({ role: props.name, variable: resolved.variable }));
1807
+ }
1808
+ yield* assertParentsExist(pg, props.name, props.memberOf);
1809
+ for (const change of scalarDrift(props, observed)) {
1810
+ yield* pg.unsafe(buildAlterRoleSql(props.name, change)).pipe(Effect21.asVoid);
1811
+ }
1812
+ const passwordMatched = resolved.value !== undefined && passwordMatchesSeal(resolved.value, previousSeal);
1813
+ if (resolved.value !== undefined && !passwordMatched) {
1814
+ yield* pg.unsafe(buildSetPasswordSql(props.name, scramSha256Verifier(Redacted2.value(resolved.value)))).pipe(Effect21.asVoid);
1815
+ }
1816
+ yield* syncMemberships(pg, props, observed.memberships ?? []);
1817
+ const after = yield* readRoleWithClient(pg, props.name);
1818
+ if (after === undefined) {
1819
+ return yield* Effect21.fail(new PostgresRoleCreateVanished({ role: props.name }));
1820
+ }
1821
+ return {
1822
+ ...storedAttributes(after),
1823
+ passwordSeal: resolved.value === undefined || passwordMatched ? previousSeal : sealPassword(resolved.value)
1824
+ };
1825
+ });
1826
+ var readRole = (pg, name) => readRoleWithClient(pg, name);
1827
+ var refuseAtPlan = (props) => Effect21.gen(function* () {
1828
+ const nameRefusal = nameByteRefusal(props.name);
1829
+ if (nameRefusal !== undefined) {
1830
+ return yield* Effect21.fail(new PostgresRoleNameRefused({ name: props.name, ...nameRefusal }));
1831
+ }
1832
+ if (props.validUntil !== undefined) {
1833
+ const untilRefusal = validUntilRefusal(props.validUntil);
1834
+ if (untilRefusal !== undefined) {
1835
+ return yield* Effect21.fail(new PostgresRoleValidUntilRefused({
1836
+ role: props.name,
1837
+ value: props.validUntil,
1838
+ reason: untilRefusal.reason
1839
+ }));
1840
+ }
1841
+ }
1842
+ });
1843
+ // src/postgres/role-provider.ts
1844
+ import * as Provider4 from "alchemy/Provider";
1845
+ import { isResolved as isResolved5 } from "alchemy/Diff";
1846
+ import * as Effect28 from "effect/Effect";
1847
+
1848
+ // src/ownership/probe.ts
1849
+ import { Unowned as Unowned4 } from "alchemy/AdoptPolicy";
1850
+ import * as Effect24 from "effect/Effect";
1851
+
1852
+ // src/ownership/rows.ts
1853
+ import { Stack as Stack2 } from "alchemy/Stack";
1854
+ import { State, isActionState } from "alchemy/State/State";
1855
+ import * as Effect23 from "effect/Effect";
1856
+ import * as Option2 from "effect/Option";
1857
+
1858
+ // src/ownership/whole.ts
1859
+ import { Stack } from "alchemy/Stack";
1860
+ import * as Effect22 from "effect/Effect";
1861
+ import * as Option from "effect/Option";
1862
+ var absent = (value3) => value3 === undefined || value3 === null;
1863
+ var isPlainObject = (value3) => {
1864
+ if (typeof value3 !== "object" || value3 === null || Array.isArray(value3))
1865
+ return false;
1866
+ const proto = Object.getPrototypeOf(value3);
1867
+ return proto === Object.prototype || proto === null;
1868
+ };
1869
+ var carries = (declared, recorded) => {
1870
+ if (absent(declared))
1871
+ return true;
1872
+ if (absent(recorded))
1873
+ return false;
1874
+ if (Array.isArray(declared)) {
1875
+ if (!Array.isArray(recorded))
1876
+ return false;
1877
+ return declared.every((each, index) => carries(each, recorded[index]));
1878
+ }
1879
+ if (isPlainObject(declared)) {
1880
+ if (!isPlainObject(recorded))
1881
+ return false;
1882
+ return Object.entries(declared).every(([key, each]) => carries(each, recorded[key]));
1883
+ }
1884
+ return true;
1885
+ };
1886
+ var wholeDeclaration = (fqn, props) => Effect22.map(Effect22.serviceOption(Stack), (stack) => {
1887
+ const declared = Option.isSome(stack) ? stack.value.resources[fqn] : undefined;
1888
+ return declared !== undefined && carries(declared.Props, props);
1889
+ });
1890
+
1891
+ // src/ownership/rows.ts
1892
+ var storeOf = Effect23.gen(function* () {
1893
+ const stack = yield* Effect23.serviceOption(Stack2);
1894
+ const state = yield* Effect23.serviceOption(State);
1895
+ if (Option2.isNone(stack) || Option2.isNone(state))
1896
+ return;
1897
+ const store = yield* state.value;
1898
+ const where = { stack: stack.value.name, stage: stack.value.stage };
1899
+ return { resources: stack.value.resources, store, where };
1900
+ });
1901
+ var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(Effect23.orElseSucceed(() => {
1902
+ return;
1903
+ }));
1904
+ var generationOf = (row, instanceId) => {
1905
+ let at = row;
1906
+ while (typeof at === "object" && at !== null && !isActionState(at)) {
1907
+ const generation = at;
1908
+ if (generation.instanceId === instanceId)
1909
+ return generation;
1910
+ at = generation.old;
1911
+ }
1912
+ return;
1913
+ };
1914
+ var recordedGeneration = (fqn, instanceId) => Effect23.gen(function* () {
1915
+ const found = yield* storeOf;
1916
+ if (found === undefined)
1917
+ return;
1918
+ for (const at of [fqn, ...found.resources[fqn]?.FormerFqns ?? []]) {
1919
+ const generation = generationOf(yield* rowAt(found.store, found.where, at), instanceId);
1920
+ if (generation !== undefined)
1921
+ return generation;
1922
+ }
1923
+ return;
1924
+ });
1925
+ var recordedInstance = (fqn, instanceId) => Effect23.gen(function* () {
1926
+ const generation = yield* recordedGeneration(fqn, instanceId);
1927
+ if (generation === undefined)
1928
+ return "absent";
1929
+ return (yield* wholeDeclaration(fqn, generation.props)) ? "whole" : "partial";
1930
+ });
1931
+ var isCreate = (fqn, instanceId) => Effect23.map(recordedGeneration(fqn, instanceId), (generation) => generation?.status === "creating");
1932
+ var idOf = (attr) => {
1933
+ if (typeof attr !== "object" || attr === null)
1934
+ return;
1935
+ const id = attr.id;
1936
+ return typeof id === "string" && id !== "" ? id : undefined;
1937
+ };
1938
+ var olderGenerationIds = (fqn, instanceId) => Effect23.gen(function* () {
1939
+ const found = yield* storeOf;
1940
+ if (found === undefined)
1941
+ return [];
1942
+ const ids = [];
1943
+ for (const at of [fqn, ...found.resources[fqn]?.FormerFqns ?? []]) {
1944
+ collectOlder(yield* rowAt(found.store, found.where, at), instanceId, ids);
1945
+ }
1946
+ return ids;
1947
+ });
1948
+ var collectOlder = (row, instanceId, ids) => {
1949
+ let at = row;
1950
+ let seen = false;
1951
+ while (typeof at === "object" && at !== null && !isActionState(at)) {
1952
+ const generation = at;
1953
+ if (!seen) {
1954
+ seen = generation.instanceId === instanceId;
1955
+ at = generation.old;
1956
+ continue;
1957
+ }
1958
+ if (generation.instanceId !== instanceId) {
1959
+ const id = idOf(generation.attr);
1960
+ if (id !== undefined)
1961
+ ids.push(id);
1962
+ }
1963
+ at = generation.old;
1964
+ }
1965
+ };
1966
+ var forgetRefusedCreate = (fqn, instanceId) => Effect23.gen(function* () {
1967
+ const found = yield* storeOf;
1968
+ if (found === undefined)
1969
+ return;
1970
+ const row = yield* rowAt(found.store, found.where, fqn);
1971
+ const mine = row;
1972
+ if (mine?.status !== "creating" || mine.instanceId !== instanceId || mine.attr !== undefined) {
1973
+ return;
1974
+ }
1975
+ yield* found.store.delete({ ...found.where, fqn }).pipe(Effect23.ignore);
1976
+ });
1977
+
1978
+ // src/ownership/probe.ts
1979
+ var provenOurs = (ask, settled) => Effect24.gen(function* () {
1980
+ const recorded = yield* recordedInstance(ask.fqn, ask.instanceId);
1981
+ if (recorded === "absent")
1982
+ return false;
1983
+ if (recorded === "partial") {
1984
+ yield* Effect24.logWarning(`${ask.fqn}: the interrupted create's state row lacks part of the declaration (a prop ` + "was still an Output when it was written), so the live object is not proven ours");
1985
+ return false;
1986
+ }
1987
+ const unproven = (reason) => Effect24.as(Effect24.logWarning(`${ask.fqn}: the interrupted create's object could not be compared with its props ` + `(${reason instanceof Error ? reason.message : String(reason)}), so it is not treated as ours`), false);
1988
+ return yield* settled.pipe(Effect24.catch(unproven), Effect24.catchDefect(unproven));
1989
+ });
1990
+ var ownedRead = (ask, found, settled) => Effect24.gen(function* () {
1991
+ if (found === undefined || ask.output !== undefined)
1992
+ return found;
1993
+ return (yield* provenOurs(ask, settled)) ? found : Unowned4(found);
1994
+ });
1995
+
1996
+ // src/ownership/resume.ts
1997
+ import { Unowned as Unowned5 } from "alchemy/AdoptPolicy";
1998
+ import { Artifacts } from "alchemy/Artifacts";
1999
+ import * as Effect25 from "effect/Effect";
2000
+ import * as Option3 from "effect/Option";
2001
+ var PROVEN = "homeflare/ownership/resumes";
2002
+ var UNFINISHED = "homeflare/ownership/unfinished";
2003
+ var note = (key, instanceId) => Effect25.gen(function* () {
2004
+ const bag = yield* Effect25.serviceOption(Artifacts);
2005
+ if (Option3.isSome(bag))
2006
+ yield* bag.value.set(key, instanceId);
2007
+ });
2008
+ var noted = (key, instanceId) => Effect25.gen(function* () {
2009
+ const bag = yield* Effect25.serviceOption(Artifacts);
2010
+ if (Option3.isNone(bag))
2011
+ return false;
2012
+ return (yield* bag.value.get(key)) === instanceId;
2013
+ });
2014
+ var noteUnfinished = (instanceId) => Effect25.as(note(UNFINISHED, instanceId), undefined);
2015
+ var noteResume = (instanceId, recovered) => Effect25.gen(function* () {
2016
+ yield* noteUnfinished(instanceId);
2017
+ const unproven = () => Effect25.succeed(undefined);
2018
+ const found = yield* recovered.pipe(Effect25.catch(unproven), Effect25.catchDefect(unproven));
2019
+ if (found === undefined || found === null || Unowned5.is(found))
2020
+ return;
2021
+ yield* note(PROVEN, instanceId);
2022
+ });
2023
+ var resumes = (instanceId) => noted(PROVEN, instanceId);
2024
+ var unfinished = (instanceId) => noted(UNFINISHED, instanceId);
2025
+ var provingResumes = (service) => {
2026
+ const { diff, read } = service;
2027
+ if (diff === undefined || read === undefined)
2028
+ return service;
2029
+ const proving = (input) => Effect25.gen(function* () {
2030
+ if (input.output === undefined) {
2031
+ yield* noteResume(input.instanceId, read({ ...input, output: undefined }));
2032
+ }
2033
+ return yield* diff(input);
2034
+ });
2035
+ return { ...service, diff: proving };
2036
+ };
2037
+
2038
+ // src/ownership/adopt.ts
2039
+ import { AdoptPolicy } from "alchemy/AdoptPolicy";
2040
+ import { AlchemyContext } from "alchemy/AlchemyContext";
2041
+ import { Stack as Stack3 } from "alchemy/Stack";
2042
+ import * as Effect26 from "effect/Effect";
2043
+ import * as Option4 from "effect/Option";
2044
+ var adoptEnabled = (fqn) => Effect26.gen(function* () {
2045
+ const stack = yield* Effect26.serviceOption(Stack3);
2046
+ const scoped = Option4.isSome(stack) ? stack.value.resources[fqn]?.Adopt : undefined;
2047
+ if (scoped !== undefined)
2048
+ return scoped;
2049
+ const policy = yield* Effect26.serviceOption(AdoptPolicy);
2050
+ if (Option4.isSome(policy))
2051
+ return policy.value;
2052
+ const context = yield* Effect26.serviceOption(AlchemyContext);
2053
+ return Option4.isSome(context) ? context.value.adopt : false;
2054
+ });
2055
+ var adoptable = (owner) => Effect26.gen(function* () {
2056
+ if (yield* isCreate(owner.fqn, owner.instanceId))
2057
+ return true;
2058
+ return yield* unfinished(owner.instanceId);
2059
+ });
2060
+ var adoptsAtApply = (owner) => Effect26.gen(function* () {
2061
+ if (!(yield* adoptEnabled(owner.fqn)))
2062
+ return false;
2063
+ return yield* adoptable(owner);
2064
+ });
2065
+ var refuseTakeover = (owner, what) => Effect26.gen(function* () {
2066
+ if (owner.output !== undefined)
2067
+ return;
2068
+ if (yield* resumes(owner.instanceId))
2069
+ return;
2070
+ const may = yield* adoptable(owner);
2071
+ if (may && (yield* adoptEnabled(owner.fqn)))
2072
+ return;
2073
+ yield* forgetRefusedCreate(owner.fqn, owner.instanceId);
2074
+ const remedy = may ? "Deploy with --adopt (or wrap the resource in adopt(true)) to take it over, or remove it first." : "It is the new identity of a replace, which --adopt does not cover: remove it first.";
2075
+ return yield* Effect26.die(new Error(`${what}: already exists, and this stack holds no state for it — the plan could not ask ` + "first (a prop was still an Output, a replace moved onto it, or it appeared after the " + `plan). Nothing was written. ${remedy}`));
2076
+ });
2077
+ var claimFor = (owner) => (what) => refuseTakeover(owner, what);
2078
+
2079
+ // src/postgres/role-diff.ts
2080
+ import { isResolved as isResolved4 } from "alchemy/Diff";
2081
+ import * as Effect27 from "effect/Effect";
2082
+ var diffPostgresRole = (news, output, env = process.env, observation) => Effect27.gen(function* () {
2083
+ if (output === undefined || !isResolved4(news))
2084
+ return;
2085
+ if (news.name !== output.name) {
2086
+ return yield* Effect27.fail(new PostgresRoleRenameRefused({ from: output.name, to: news.name }));
2087
+ }
2088
+ yield* refuseAtPlan(news);
2089
+ const live = observation?.found;
2090
+ if (live !== undefined && live.oid !== output.oid) {
2091
+ return yield* Effect27.fail(new PostgresRoleIdentityRefused({
2092
+ role: output.name,
2093
+ storedOid: output.oid,
2094
+ liveOid: live.oid
2095
+ }));
2096
+ }
2097
+ const compared = live ?? output;
2098
+ const scalars = scalarDrift(news, compared);
2099
+ const membership = membershipDrift(news.memberOf, compared.memberOf ?? [], live === undefined ? new Set : unsafeMemberships(live, news.inherit));
2100
+ const resolved = resolvePassword(news, env);
2101
+ yield* assertPasswordAscii(news.name, resolved);
2102
+ const passwordStale = resolved.value !== undefined && !passwordMatchesSeal(resolved.value, output.passwordSeal);
2103
+ const absent2 = observation !== undefined && live === undefined;
2104
+ const changed = absent2 || privilegedFlags(compared).length > 0 || scalars.length > 0 || membership.grants.length > 0 || membership.revokes.length > 0 || passwordStale;
2105
+ return changed ? { action: "update" } : { action: "noop" };
2106
+ });
2107
+
2108
+ // src/postgres/role-provider.ts
2109
+ var postgresRoleHandlers = PostgresRole.Provider.of({
2110
+ list: () => Effect28.succeed([]),
2111
+ nuke: { skip: true },
2112
+ read: ({ fqn, instanceId, olds, output }) => Effect28.gen(function* () {
2113
+ const name = output?.name ?? olds.name;
2114
+ const live = yield* withPg((pg) => readRole(pg, name));
2115
+ if (live === undefined)
2116
+ return;
2117
+ if (output !== undefined && live.oid !== output.oid) {
2118
+ return yield* Effect28.fail(new PostgresRoleIdentityRefused({
2119
+ role: name,
2120
+ storedOid: output.oid,
2121
+ liveOid: live.oid
2122
+ }));
2123
+ }
2124
+ const passwordSeal = output?.passwordSeal ?? "";
2125
+ const found = { ...storedAttributes(live), passwordSeal };
2126
+ return yield* ownedRead({ fqn, instanceId, output }, found, Effect28.succeed(declarationMatches(olds, live)));
2127
+ }),
2128
+ diff: ({ news, output, instanceId }) => Effect28.gen(function* () {
2129
+ if (output === undefined)
2130
+ return yield* noteUnfinished(instanceId);
2131
+ if (!isResolved5(news))
2132
+ return;
2133
+ const found = yield* withPg((pg) => readRole(pg, output.name));
2134
+ return yield* diffPostgresRole(news, output, process.env, { found });
2135
+ }),
2136
+ reconcile: ({ fqn, instanceId, news, output }) => Effect28.gen(function* () {
2137
+ yield* refuseAtPlan(news);
2138
+ return yield* withPg((pg) => Effect28.gen(function* () {
2139
+ const observed = yield* readRole(pg, news.name);
2140
+ if (observed !== undefined && output === undefined) {
2141
+ yield* refuseTakeover({ fqn, instanceId, output }, "Postgres.Role");
2142
+ }
2143
+ return yield* reconcileWithClient4(pg, news, process.env, output?.passwordSeal, output?.oid);
2144
+ }));
2145
+ }),
2146
+ delete: ({ output }) => withPg((pg) => Effect28.gen(function* () {
2147
+ if (output === undefined)
2148
+ return;
2149
+ const live = yield* readRole(pg, output.name);
2150
+ if (live === undefined)
2151
+ return;
2152
+ if (live.oid !== output.oid) {
2153
+ return yield* Effect28.fail(new PostgresRoleIdentityRefused({
2154
+ role: output.name,
2155
+ storedOid: output.oid,
2156
+ liveOid: live.oid
2157
+ }));
2158
+ }
2159
+ yield* pg.unsafe(buildDropRoleSql(output.name)).pipe(Effect28.asVoid);
2160
+ }))
2161
+ });
2162
+ var PostgresRoleProvider = () => Provider4.succeed(PostgresRole, postgresRoleHandlers);
290
2163
  // src/postgres/providers.ts
291
2164
  import * as Layer2 from "effect/Layer";
292
- var postgresProviders = (config) => PostgresDatabaseProvider().pipe(Layer2.provideMerge(postgresConnection(config)));
293
- var postgresRunnerProviders = (config) => PostgresDatabaseProvider().pipe(Layer2.provideMerge(postgresRunnerConnection(config)));
2165
+ var postgresProviders = (config) => Layer2.mergeAll(PostgresDatabaseProvider().pipe(Layer2.provideMerge(postgresConnection(config))), PostgresSchemaProvider().pipe(Layer2.provideMerge(postgresConnection(config))), PostgresGrantsProvider().pipe(Layer2.provideMerge(postgresConnection(config))));
2166
+ var postgresRunnerProviders = (config) => Layer2.mergeAll(PostgresDatabaseProvider().pipe(Layer2.provideMerge(postgresRunnerConnection(config))), PostgresSchemaProvider().pipe(Layer2.provideMerge(postgresRunnerConnection(config))), PostgresGrantsProvider().pipe(Layer2.provideMerge(postgresRunnerConnection(config))));
2167
+ var postgresRoleProviders = (config) => PostgresRoleProvider().pipe(Layer2.provideMerge(postgresConnection(config)));
2168
+ var postgresRoleRunnerProviders = (config) => PostgresRoleProvider().pipe(Layer2.provideMerge(postgresRunnerConnection(config)));
294
2169
  export {
2170
+ COLUMN_PRIVILEGES,
295
2171
  POSTGRES_NAME_MAX_BYTES,
296
2172
  PostgresConnection,
297
2173
  PostgresDatabase,
@@ -301,24 +2177,109 @@ export {
301
2177
  PostgresDatabaseOwnerMissing,
302
2178
  PostgresDatabaseProvider,
303
2179
  PostgresDatabaseRenameRefused,
2180
+ PostgresGrants,
2181
+ PostgresGrantsColumnMissing,
2182
+ PostgresGrantsDatabaseMismatch,
2183
+ PostgresGrantsDatabaseMissing,
2184
+ PostgresGrantsDuplicateObject,
2185
+ PostgresGrantsNameRefused,
2186
+ PostgresGrantsPrivilegeRefused,
2187
+ PostgresGrantsProvider,
2188
+ PostgresGrantsRepairRefused,
2189
+ PostgresGrantsRetargetRefused,
2190
+ PostgresGrantsRoleMissing,
2191
+ PostgresGrantsSchemaMissing,
2192
+ PostgresGrantsTableMissing,
2193
+ PostgresRole,
2194
+ PostgresRoleCreateVanished,
2195
+ PostgresRoleIdentityRefused,
2196
+ PostgresRoleMembershipUnrepaired,
2197
+ PostgresRoleNameRefused,
2198
+ PostgresRoleParentMissing,
2199
+ PostgresRolePasswordEnvUnsetError,
2200
+ PostgresRolePasswordNonAsciiRefused,
2201
+ PostgresRolePrivilegedRefused,
2202
+ PostgresRoleProvider,
2203
+ PostgresRoleRenameRefused,
2204
+ PostgresRoleValidUntilRefused,
2205
+ PostgresSchema,
2206
+ PostgresSchemaCreateVanished,
2207
+ PostgresSchemaDatabaseRefused,
2208
+ PostgresSchemaDeleteForeignRefused,
2209
+ PostgresSchemaDrift,
2210
+ PostgresSchemaDropNotEmptyError,
2211
+ PostgresSchemaExistsRefused,
2212
+ PostgresSchemaIdentityRefused,
2213
+ PostgresSchemaNameRefused,
2214
+ PostgresSchemaOwnerMissing,
2215
+ PostgresSchemaProvider,
2216
+ PostgresSchemaRenameRefused,
2217
+ PostgresSchemaWrongDatabase,
2218
+ SCHEMA_PRIVILEGES,
2219
+ TABLE_PRIVILEGES,
2220
+ buildAlterRoleSql,
2221
+ buildCommentSchemaSql,
304
2222
  buildCreateDatabaseSql,
2223
+ buildCreateRoleSql,
2224
+ buildCreateSchemaSql,
2225
+ buildDropRoleSql,
2226
+ buildDropSchemaSql,
2227
+ buildGrantMembershipSql,
2228
+ buildRepairMembershipSql,
2229
+ buildRevokeGrantorMembershipSql,
2230
+ buildSetPasswordSql,
2231
+ currentDatabase,
2232
+ currentUser,
2233
+ databaseExists,
2234
+ diffPostgresSchema,
305
2235
  firstDrift,
2236
+ grantColumnSql,
2237
+ grantDefaultSql,
2238
+ grantSchemaSql,
2239
+ grantTableSql,
2240
+ grantsNameByteRefusal,
306
2241
  inlineParams,
2242
+ isDependentObjectsError,
307
2243
  isDuplicateDatabaseRace,
308
2244
  isPostgresDatabase,
2245
+ isPostgresGrants,
2246
+ isPostgresRole,
2247
+ isPostgresSchema,
2248
+ letterForPrivilege,
309
2249
  makePsqlExecutor,
2250
+ membershipDrift,
310
2251
  nameByteRefusal,
2252
+ normalizedComment,
311
2253
  postgresConnection,
312
2254
  postgresProviders,
2255
+ postgresRoleProviders,
2256
+ postgresRoleRunnerProviders,
313
2257
  postgresRunnerConnection,
314
2258
  postgresRunnerProviders,
315
2259
  quoteIdent,
316
2260
  quoteStringLiteral,
2261
+ resolvePassword,
2262
+ revokeColumnSql,
2263
+ revokeDefaultSql,
2264
+ revokePublicSchemaSql,
2265
+ revokePublicTablesSql,
2266
+ revokeSchemaSql,
2267
+ revokeTableSql,
317
2268
  roleExists,
2269
+ sameValidUntil,
2270
+ scalarDrift,
2271
+ schemaIsEmpty,
2272
+ schemaNameByteRefusal,
318
2273
  selectDatabase,
2274
+ selectRole,
2275
+ selectRoleMemberships,
2276
+ selectSchema,
2277
+ syncMemberships,
2278
+ unsafeMemberships,
319
2279
  utf8ByteLength,
2280
+ validUntilRefusal,
320
2281
  withPg
321
2282
  };
322
2283
 
323
- //# debugId=0FE6812621B3699B64756E2164756E21
2284
+ //# debugId=584CB763916085BE64756E2164756E21
324
2285
  //# sourceMappingURL=index.js.map