@homeflare/alchemy 0.46.0 → 0.47.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.
- package/README.md +10 -10
- package/dist/argocd/cluster.d.ts +5 -5
- package/dist/argocd/cluster.d.ts.map +1 -1
- package/dist/argocd/fake-argocd.d.ts +1 -1
- package/dist/argocd/fake-argocd.d.ts.map +1 -1
- package/dist/argocd/index.js.map +1 -1
- package/dist/argocd/providers.d.ts +1 -1
- package/dist/argocd/providers.d.ts.map +1 -1
- package/dist/argocd/repo-creds.d.ts +5 -5
- package/dist/argocd/repo-creds.d.ts.map +1 -1
- package/dist/argocd/repository.d.ts +5 -5
- package/dist/argocd/repository.d.ts.map +1 -1
- package/dist/argocd/resource.d.ts +1 -1
- package/dist/argocd/resource.d.ts.map +1 -1
- package/dist/caddy/admin.d.ts +1 -1
- package/dist/caddy/admin.d.ts.map +1 -1
- package/dist/caddy/caddy-http-client.d.ts +2 -2
- package/dist/caddy/caddy-http-client.d.ts.map +1 -1
- package/dist/caddy/format.d.ts +1 -1
- package/dist/caddy/format.d.ts.map +1 -1
- package/dist/caddy/index.js +6 -6
- package/dist/caddy/index.js.map +4 -4
- package/dist/ceph/auth-entity.d.ts +1 -1
- package/dist/ceph/auth-entity.d.ts.map +1 -1
- package/dist/ceph/ceph-auth-reconcile.d.ts +1 -1
- package/dist/ceph/ceph-auth-reconcile.d.ts.map +1 -1
- package/dist/ceph/index.js +5 -5
- package/dist/ceph/index.js.map +4 -4
- package/dist/cloudflare/fake-mesh.d.ts +2 -2
- package/dist/cloudflare/fake-mesh.d.ts.map +1 -1
- package/dist/cloudflare/fake-r2-lock.d.ts +1 -1
- package/dist/cloudflare/fake-r2-lock.d.ts.map +1 -1
- package/dist/cloudflare/index.js +3 -3
- package/dist/cloudflare/index.js.map +3 -3
- package/dist/cloudflare/mesh-node-token.d.ts +1 -1
- package/dist/cloudflare/mesh-node-token.d.ts.map +1 -1
- package/dist/cloudflare/mesh-node.d.ts +1 -1
- package/dist/cloudflare/mesh-node.d.ts.map +1 -1
- package/dist/cloudflare/providers.d.ts +1 -1
- package/dist/cloudflare/providers.d.ts.map +1 -1
- package/dist/cloudflare/r2-bucket-lock.d.ts +1 -1
- package/dist/cloudflare/r2-bucket-lock.d.ts.map +1 -1
- package/dist/discord/application-command.d.ts +6 -6
- package/dist/discord/application-command.d.ts.map +1 -1
- package/dist/discord/fake-discord.d.ts +1 -1
- package/dist/discord/fake-discord.d.ts.map +1 -1
- package/dist/discord/guild-application-command.d.ts +6 -6
- package/dist/discord/guild-application-command.d.ts.map +1 -1
- package/dist/discord/index.js.map +1 -1
- package/dist/discord/providers.d.ts +1 -1
- package/dist/discord/providers.d.ts.map +1 -1
- package/dist/discord/resource.d.ts +1 -1
- package/dist/discord/resource.d.ts.map +1 -1
- package/dist/forgejo/branch-protection.d.ts +5 -5
- package/dist/forgejo/branch-protection.d.ts.map +1 -1
- package/dist/forgejo/fake-forgejo.d.ts +1 -1
- package/dist/forgejo/fake-forgejo.d.ts.map +1 -1
- package/dist/forgejo/index.js.map +1 -1
- package/dist/forgejo/org-actions-secrets.d.ts +5 -5
- package/dist/forgejo/org-actions-secrets.d.ts.map +1 -1
- package/dist/forgejo/org-label.d.ts +5 -5
- package/dist/forgejo/org-label.d.ts.map +1 -1
- package/dist/forgejo/org-team.d.ts +5 -5
- package/dist/forgejo/org-team.d.ts.map +1 -1
- package/dist/forgejo/repo-webhook.d.ts +5 -5
- package/dist/forgejo/repo-webhook.d.ts.map +1 -1
- package/dist/forgejo/repository.d.ts +5 -5
- package/dist/forgejo/repository.d.ts.map +1 -1
- package/dist/forgejo/resource.d.ts +1 -1
- package/dist/forgejo/resource.d.ts.map +1 -1
- package/dist/forgejo/team-member.d.ts +5 -5
- package/dist/forgejo/team-member.d.ts.map +1 -1
- package/dist/github/repository-ruleset-providers.d.ts +1 -1
- package/dist/github/repository-ruleset-providers.d.ts.map +1 -1
- package/dist/google-workspace/domain-alias.d.ts +5 -5
- package/dist/google-workspace/domain-alias.d.ts.map +1 -1
- package/dist/google-workspace/fake-google-workspace.d.ts +1 -1
- package/dist/google-workspace/fake-google-workspace.d.ts.map +1 -1
- package/dist/google-workspace/group-member.d.ts +5 -5
- package/dist/google-workspace/group-member.d.ts.map +1 -1
- package/dist/google-workspace/group.d.ts +5 -5
- package/dist/google-workspace/group.d.ts.map +1 -1
- package/dist/google-workspace/index.js.map +1 -1
- package/dist/google-workspace/org-unit.d.ts +5 -5
- package/dist/google-workspace/org-unit.d.ts.map +1 -1
- package/dist/google-workspace/providers.d.ts +1 -1
- package/dist/google-workspace/providers.d.ts.map +1 -1
- package/dist/google-workspace/resource.d.ts +1 -1
- package/dist/google-workspace/resource.d.ts.map +1 -1
- package/dist/grafana/alert-rule-group.d.ts +4 -4
- package/dist/grafana/alert-rule-group.d.ts.map +1 -1
- package/dist/grafana/contact-point.d.ts +4 -4
- package/dist/grafana/dashboard.d.ts +4 -4
- package/dist/grafana/datasource.d.ts +4 -4
- package/dist/grafana/fake-grafana.d.ts +1 -1
- package/dist/grafana/fake-grafana.d.ts.map +1 -1
- package/dist/grafana/folder.d.ts +4 -4
- package/dist/grafana/message-template.d.ts +4 -4
- package/dist/grafana/mute-timing.d.ts +4 -4
- package/dist/grafana/notification-policy.d.ts +4 -4
- package/dist/grafana/notification-policy.d.ts.map +1 -1
- package/dist/grafana/providers.d.ts +1 -1
- package/dist/grafana/providers.d.ts.map +1 -1
- package/dist/litellm/access-group.d.ts +1 -1
- package/dist/litellm/budget-operations.d.ts +1 -1
- package/dist/litellm/budget-operations.d.ts.map +1 -1
- package/dist/litellm/budget.d.ts +2 -2
- package/dist/litellm/credential-errors.d.ts +1 -1
- package/dist/litellm/credential-errors.d.ts.map +1 -1
- package/dist/litellm/credential.d.ts +1 -1
- package/dist/litellm/index.js +26 -5
- package/dist/litellm/index.js.map +8 -8
- package/dist/litellm/key-errors.d.ts +1 -1
- package/dist/litellm/key-errors.d.ts.map +1 -1
- package/dist/litellm/key.d.ts +2 -2
- package/dist/litellm/mcp-server-locate.d.ts +1 -1
- package/dist/litellm/mcp-server-locate.d.ts.map +1 -1
- package/dist/litellm/mcp-server.d.ts +2 -2
- package/dist/litellm/model-operations.d.ts +1 -1
- package/dist/litellm/model-operations.d.ts.map +1 -1
- package/dist/litellm/model.d.ts +2 -2
- package/dist/litellm/operations.d.ts +1 -1
- package/dist/litellm/operations.d.ts.map +1 -1
- package/dist/litellm/policy-attachment.d.ts +1 -1
- package/dist/litellm/policy.d.ts +1 -1
- package/dist/litellm/providers.d.ts +1 -1
- package/dist/litellm/providers.d.ts.map +1 -1
- package/dist/litellm/team-operations.d.ts +1 -1
- package/dist/litellm/team-operations.d.ts.map +1 -1
- package/dist/litellm/team.d.ts +1 -1
- package/dist/litellm/tool-policy-operations.d.ts +1 -1
- package/dist/litellm/tool-policy-operations.d.ts.map +1 -1
- package/dist/litellm/tool-policy.d.ts +1 -1
- package/dist/litellm/toolset.d.ts +1 -1
- package/dist/netbox/fake-netbox.d.ts +1 -1
- package/dist/netbox/fake-netbox.d.ts.map +1 -1
- package/dist/netbox/index.js.map +1 -1
- package/dist/netbox/prefix.d.ts +5 -5
- package/dist/netbox/prefix.d.ts.map +1 -1
- package/dist/netbox/resource.d.ts +1 -1
- package/dist/netbox/resource.d.ts.map +1 -1
- package/dist/openbao/approle-login.d.ts +1 -1
- package/dist/openbao/approle-login.d.ts.map +1 -1
- package/dist/openbao/auth-method-reconcile.d.ts +1 -1
- package/dist/openbao/auth-method-reconcile.d.ts.map +1 -1
- package/dist/openbao/auth-method-wire.d.ts +4 -4
- package/dist/openbao/auth-method-wire.d.ts.map +1 -1
- package/dist/openbao/auth-method.d.ts +1 -1
- package/dist/openbao/auth-method.d.ts.map +1 -1
- package/dist/openbao/auth-role-identity.d.ts +1 -1
- package/dist/openbao/auth-role-identity.d.ts.map +1 -1
- package/dist/openbao/auth-role-wire.d.ts +4 -4
- package/dist/openbao/auth-role-wire.d.ts.map +1 -1
- package/dist/openbao/auth-role.d.ts +1 -1
- package/dist/openbao/auth-role.d.ts.map +1 -1
- package/dist/openbao/bao-http.d.ts +1 -1
- package/dist/openbao/bao-http.d.ts.map +1 -1
- package/dist/openbao/bao-identity.d.ts +1 -1
- package/dist/openbao/bao-identity.d.ts.map +1 -1
- package/dist/openbao/cloudflare-permission-groups.d.ts +1 -1
- package/dist/openbao/cloudflare-permission-groups.d.ts.map +1 -1
- package/dist/openbao/cloudflare-role-wire.d.ts +1 -1
- package/dist/openbao/cloudflare-role-wire.d.ts.map +1 -1
- package/dist/openbao/cloudflare-role.d.ts +1 -1
- package/dist/openbao/cloudflare-role.d.ts.map +1 -1
- package/dist/openbao/distilled.d.ts +1 -1
- package/dist/openbao/distilled.d.ts.map +1 -1
- package/dist/openbao/fake-bao.d.ts +1 -1
- package/dist/openbao/fake-bao.d.ts.map +1 -1
- package/dist/openbao/fake-core.d.ts +1 -1
- package/dist/openbao/fake-core.d.ts.map +1 -1
- package/dist/openbao/fake-engines.d.ts +1 -1
- package/dist/openbao/fake-engines.d.ts.map +1 -1
- package/dist/openbao/fake-families.d.ts +2 -2
- package/dist/openbao/fake-families.d.ts.map +1 -1
- package/dist/openbao/fake-stack.d.ts +5 -0
- package/dist/openbao/fake-stack.d.ts.map +1 -1
- package/dist/openbao/forgejo-bootstrap.d.ts +1 -1
- package/dist/openbao/forgejo-bootstrap.d.ts.map +1 -1
- package/dist/openbao/index.js +31 -10
- package/dist/openbao/index.js.map +31 -31
- package/dist/openbao/jwt-config.d.ts +1 -1
- package/dist/openbao/jwt-config.d.ts.map +1 -1
- package/dist/openbao/jwt-role.d.ts +1 -1
- package/dist/openbao/jwt-role.d.ts.map +1 -1
- package/dist/openbao/kubernetes-role.d.ts +1 -1
- package/dist/openbao/kubernetes-role.d.ts.map +1 -1
- package/dist/openbao/mfa-enforcement.d.ts +1 -1
- package/dist/openbao/mfa-enforcement.d.ts.map +1 -1
- package/dist/openbao/mfa-totp.d.ts +1 -1
- package/dist/openbao/mfa-totp.d.ts.map +1 -1
- package/dist/openbao/mfa-wire.d.ts +1 -1
- package/dist/openbao/mfa-wire.d.ts.map +1 -1
- package/dist/openbao/mount-move.d.ts +1 -1
- package/dist/openbao/mount-move.d.ts.map +1 -1
- package/dist/openbao/mount-reconcile.d.ts +1 -1
- package/dist/openbao/mount-reconcile.d.ts.map +1 -1
- package/dist/openbao/mount-wire.d.ts +4 -4
- package/dist/openbao/mount-wire.d.ts.map +1 -1
- package/dist/openbao/mount.d.ts +1 -1
- package/dist/openbao/mount.d.ts.map +1 -1
- package/dist/openbao/pki-role.d.ts +1 -1
- package/dist/openbao/pki-role.d.ts.map +1 -1
- package/dist/openbao/plugin-reconcile.d.ts +1 -1
- package/dist/openbao/plugin-reconcile.d.ts.map +1 -1
- package/dist/openbao/plugin-wire.d.ts +1 -1
- package/dist/openbao/plugin-wire.d.ts.map +1 -1
- package/dist/openbao/plugin.d.ts +1 -1
- package/dist/openbao/plugin.d.ts.map +1 -1
- package/dist/openbao/policy-identity.d.ts +1 -1
- package/dist/openbao/policy-identity.d.ts.map +1 -1
- package/dist/openbao/policy-wire.d.ts +4 -4
- package/dist/openbao/policy-wire.d.ts.map +1 -1
- package/dist/openbao/policy.d.ts +1 -1
- package/dist/openbao/policy.d.ts.map +1 -1
- package/dist/openbao/proxmox-role-wire.d.ts +1 -1
- package/dist/openbao/proxmox-role-wire.d.ts.map +1 -1
- package/dist/openbao/proxmox-role.d.ts +1 -1
- package/dist/openbao/proxmox-role.d.ts.map +1 -1
- package/dist/openbao/remount-wire.d.ts +1 -1
- package/dist/openbao/remount-wire.d.ts.map +1 -1
- package/dist/openbao/rename-identity.d.ts +1 -1
- package/dist/openbao/rename-identity.d.ts.map +1 -1
- package/dist/openbao/rename.d.ts +1 -1
- package/dist/openbao/rename.d.ts.map +1 -1
- package/dist/openbao/role-reconcile.d.ts +1 -1
- package/dist/openbao/role-reconcile.d.ts.map +1 -1
- package/dist/openbao/ssh-role.d.ts +1 -1
- package/dist/openbao/ssh-role.d.ts.map +1 -1
- package/dist/opnsense/alias.d.ts +5 -5
- package/dist/opnsense/alias.d.ts.map +1 -1
- package/dist/opnsense/category.d.ts +5 -5
- package/dist/opnsense/category.d.ts.map +1 -1
- package/dist/opnsense/fake-opnsense.d.ts +1 -1
- package/dist/opnsense/fake-opnsense.d.ts.map +1 -1
- package/dist/opnsense/group.d.ts +5 -5
- package/dist/opnsense/group.d.ts.map +1 -1
- package/dist/opnsense/index.js.map +1 -1
- package/dist/opnsense/providers.d.ts +1 -1
- package/dist/opnsense/providers.d.ts.map +1 -1
- package/dist/opnsense/resource.d.ts +1 -1
- package/dist/opnsense/resource.d.ts.map +1 -1
- package/dist/ownership/probe.d.ts.map +1 -1
- package/dist/paperless/custom-field.d.ts +5 -5
- package/dist/paperless/custom-field.d.ts.map +1 -1
- package/dist/paperless/document-type.d.ts +5 -5
- package/dist/paperless/document-type.d.ts.map +1 -1
- package/dist/paperless/fake-paperless.d.ts +1 -1
- package/dist/paperless/fake-paperless.d.ts.map +1 -1
- package/dist/paperless/matching-types.d.ts +1 -1
- package/dist/paperless/matching-types.d.ts.map +1 -1
- package/dist/paperless/matching.d.ts +4 -4
- package/dist/paperless/providers.d.ts +1 -1
- package/dist/paperless/providers.d.ts.map +1 -1
- package/dist/paperless/storage-path.d.ts +5 -5
- package/dist/paperless/storage-path.d.ts.map +1 -1
- package/dist/paperless/tag.d.ts +5 -5
- package/dist/paperless/tag.d.ts.map +1 -1
- package/dist/postgres/connection.d.ts +2 -2
- package/dist/postgres/connection.d.ts.map +1 -1
- package/dist/postgres/database-sql.d.ts +1 -1
- package/dist/postgres/database-sql.d.ts.map +1 -1
- package/dist/postgres/database.d.ts +1 -1
- package/dist/postgres/database.d.ts.map +1 -1
- package/dist/postgres/fake-grants-read.d.ts +1 -1
- package/dist/postgres/fake-grants-read.d.ts.map +1 -1
- package/dist/postgres/fake-grants-write.d.ts +1 -1
- package/dist/postgres/fake-grants-write.d.ts.map +1 -1
- package/dist/postgres/fake-role-sql.d.ts +1 -1
- package/dist/postgres/fake-role-sql.d.ts.map +1 -1
- package/dist/postgres/fake-schema-drop.d.ts +13 -0
- package/dist/postgres/fake-schema-drop.d.ts.map +1 -0
- package/dist/postgres/fake-schema-sql.d.ts +6 -1
- package/dist/postgres/fake-schema-sql.d.ts.map +1 -1
- package/dist/postgres/fake-sql.d.ts +4 -0
- package/dist/postgres/fake-sql.d.ts.map +1 -1
- package/dist/postgres/grants-existence.d.ts +1 -1
- package/dist/postgres/grants-existence.d.ts.map +1 -1
- package/dist/postgres/grants-ops.d.ts +1 -1
- package/dist/postgres/grants-ops.d.ts.map +1 -1
- package/dist/postgres/grants-ownership.d.ts +1 -1
- package/dist/postgres/grants-ownership.d.ts.map +1 -1
- package/dist/postgres/grants-read-defaults.d.ts +1 -1
- package/dist/postgres/grants-read-defaults.d.ts.map +1 -1
- package/dist/postgres/grants-read.d.ts +1 -1
- package/dist/postgres/grants-read.d.ts.map +1 -1
- package/dist/postgres/index.d.ts +2 -1
- package/dist/postgres/index.d.ts.map +1 -1
- package/dist/postgres/index.js +523 -324
- package/dist/postgres/index.js.map +26 -22
- package/dist/postgres/installed-classifier.d.ts +1 -1
- package/dist/postgres/installed-classifier.d.ts.map +1 -1
- package/dist/postgres/psql-executor.d.ts.map +1 -1
- package/dist/postgres/role-membership-sql.d.ts +1 -1
- package/dist/postgres/role-membership-sql.d.ts.map +1 -1
- package/dist/postgres/role-sql.d.ts +1 -1
- package/dist/postgres/role-sql.d.ts.map +1 -1
- package/dist/postgres/role.d.ts +1 -1
- package/dist/postgres/role.d.ts.map +1 -1
- package/dist/postgres/schema-assert.d.ts +1 -1
- package/dist/postgres/schema-assert.d.ts.map +1 -1
- package/dist/postgres/schema-closure-sql.d.ts +45 -0
- package/dist/postgres/schema-closure-sql.d.ts.map +1 -0
- package/dist/postgres/schema-delete.d.ts +18 -9
- package/dist/postgres/schema-delete.d.ts.map +1 -1
- package/dist/postgres/schema-drop-sql.d.ts +66 -0
- package/dist/postgres/schema-drop-sql.d.ts.map +1 -0
- package/dist/postgres/schema-errors.d.ts +19 -1
- package/dist/postgres/schema-errors.d.ts.map +1 -1
- package/dist/postgres/schema-reconcile.d.ts +1 -1
- package/dist/postgres/schema-reconcile.d.ts.map +1 -1
- package/dist/postgres/schema-sequence-error.d.ts +29 -0
- package/dist/postgres/schema-sequence-error.d.ts.map +1 -0
- package/dist/postgres/schema-sql.d.ts +17 -1
- package/dist/postgres/schema-sql.d.ts.map +1 -1
- package/dist/postgres/schema-test-kit.d.ts +3 -1
- package/dist/postgres/schema-test-kit.d.ts.map +1 -1
- package/dist/postgres/schema.d.ts +1 -1
- package/dist/postgres/schema.d.ts.map +1 -1
- package/dist/postgres/search-path.d.ts +38 -0
- package/dist/postgres/search-path.d.ts.map +1 -0
- package/dist/proxmox/acl.d.ts +1 -1
- package/dist/proxmox/acl.d.ts.map +1 -1
- package/dist/proxmox/api-token.d.ts +1 -1
- package/dist/proxmox/api-token.d.ts.map +1 -1
- package/dist/proxmox/backup-job-distilled.d.ts +2 -2
- package/dist/proxmox/backup-job-distilled.d.ts.map +1 -1
- package/dist/proxmox/backup-job.d.ts +1 -1
- package/dist/proxmox/backup-job.d.ts.map +1 -1
- package/dist/proxmox/ceph-daemon-wire.d.ts +3 -3
- package/dist/proxmox/ceph-daemon-wire.d.ts.map +1 -1
- package/dist/proxmox/ceph-daemon.d.ts +2 -2
- package/dist/proxmox/ceph-daemon.d.ts.map +1 -1
- package/dist/proxmox/ceph-flag-wire.d.ts +3 -3
- package/dist/proxmox/ceph-flag-wire.d.ts.map +1 -1
- package/dist/proxmox/ceph-flag.d.ts +1 -1
- package/dist/proxmox/ceph-flag.d.ts.map +1 -1
- package/dist/proxmox/ceph-fs-distilled.d.ts +3 -3
- package/dist/proxmox/ceph-fs-distilled.d.ts.map +1 -1
- package/dist/proxmox/ceph-fs.d.ts +1 -1
- package/dist/proxmox/ceph-fs.d.ts.map +1 -1
- package/dist/proxmox/ceph-osd-tree.d.ts +1 -1
- package/dist/proxmox/ceph-osd-tree.d.ts.map +1 -1
- package/dist/proxmox/ceph-osd-write.d.ts +2 -2
- package/dist/proxmox/ceph-osd-write.d.ts.map +1 -1
- package/dist/proxmox/ceph-osd.d.ts +1 -1
- package/dist/proxmox/ceph-osd.d.ts.map +1 -1
- package/dist/proxmox/ceph-pool-settle.d.ts +2 -2
- package/dist/proxmox/ceph-pool-settle.d.ts.map +1 -1
- package/dist/proxmox/ceph-pool-wire.d.ts +1 -1
- package/dist/proxmox/ceph-pool-wire.d.ts.map +1 -1
- package/dist/proxmox/ceph-pool.d.ts +1 -1
- package/dist/proxmox/ceph-pool.d.ts.map +1 -1
- package/dist/proxmox/client.d.ts +1 -1
- package/dist/proxmox/client.d.ts.map +1 -1
- package/dist/proxmox/distilled-pbs.d.ts +1 -1
- package/dist/proxmox/distilled-pbs.d.ts.map +1 -1
- package/dist/proxmox/distilled-pve.d.ts +1 -1
- package/dist/proxmox/distilled-pve.d.ts.map +1 -1
- package/dist/proxmox/fake-pbs-jobs.d.ts +1 -1
- package/dist/proxmox/fake-pve.d.ts +1 -1
- package/dist/proxmox/fake-pve.d.ts.map +1 -1
- package/dist/proxmox/firewall-alias-lifecycle.d.ts +6 -6
- package/dist/proxmox/firewall-alias-lifecycle.d.ts.map +1 -1
- package/dist/proxmox/firewall-alias.d.ts +1 -1
- package/dist/proxmox/firewall-alias.d.ts.map +1 -1
- package/dist/proxmox/group-wire.d.ts +2 -2
- package/dist/proxmox/group-wire.d.ts.map +1 -1
- package/dist/proxmox/group.d.ts +1 -1
- package/dist/proxmox/group.d.ts.map +1 -1
- package/dist/proxmox/ha-resource.d.ts +1 -1
- package/dist/proxmox/ha-resource.d.ts.map +1 -1
- package/dist/proxmox/ha-rule.d.ts +1 -1
- package/dist/proxmox/ha-rule.d.ts.map +1 -1
- package/dist/proxmox/index.js +38 -17
- package/dist/proxmox/index.js.map +9 -9
- package/dist/proxmox/lease-cache.d.ts +2 -2
- package/dist/proxmox/lease-cache.d.ts.map +1 -1
- package/dist/proxmox/live-core-fixtures.d.ts +1 -1
- package/dist/proxmox/live-core-fixtures.d.ts.map +1 -1
- package/dist/proxmox/lxc-lifecycle.d.ts +3 -3
- package/dist/proxmox/lxc-lifecycle.d.ts.map +1 -1
- package/dist/proxmox/lxc-read.d.ts +1 -1
- package/dist/proxmox/lxc-read.d.ts.map +1 -1
- package/dist/proxmox/lxc-task.d.ts +1 -1
- package/dist/proxmox/lxc-task.d.ts.map +1 -1
- package/dist/proxmox/lxc.d.ts +1 -1
- package/dist/proxmox/lxc.d.ts.map +1 -1
- package/dist/proxmox/members.d.ts +3 -3
- package/dist/proxmox/members.d.ts.map +1 -1
- package/dist/proxmox/metric-server-distilled.d.ts +3 -3
- package/dist/proxmox/metric-server-distilled.d.ts.map +1 -1
- package/dist/proxmox/metric-server.d.ts +1 -1
- package/dist/proxmox/metric-server.d.ts.map +1 -1
- package/dist/proxmox/mint.d.ts +1 -1
- package/dist/proxmox/mint.d.ts.map +1 -1
- package/dist/proxmox/network-apply-read.d.ts +3 -3
- package/dist/proxmox/network-apply-read.d.ts.map +1 -1
- package/dist/proxmox/network-apply-run.d.ts +1 -1
- package/dist/proxmox/network-apply-run.d.ts.map +1 -1
- package/dist/proxmox/network-apply.d.ts +1 -1
- package/dist/proxmox/network-apply.d.ts.map +1 -1
- package/dist/proxmox/node-network-wire.d.ts +2 -2
- package/dist/proxmox/node-network-wire.d.ts.map +1 -1
- package/dist/proxmox/node-network.d.ts +1 -1
- package/dist/proxmox/node-network.d.ts.map +1 -1
- package/dist/proxmox/notification-matcher-distilled.d.ts +4 -4
- package/dist/proxmox/notification-matcher-distilled.d.ts.map +1 -1
- package/dist/proxmox/notification-matcher-lifecycle.d.ts +4 -4
- package/dist/proxmox/notification-matcher.d.ts +1 -1
- package/dist/proxmox/notification-matcher.d.ts.map +1 -1
- package/dist/proxmox/notification-target-distilled.d.ts +4 -4
- package/dist/proxmox/notification-target-distilled.d.ts.map +1 -1
- package/dist/proxmox/notification-target-lifecycle.d.ts +4 -4
- package/dist/proxmox/notification-target.d.ts +1 -1
- package/dist/proxmox/notification-target.d.ts.map +1 -1
- package/dist/proxmox/pbs-datastore-lifecycle.d.ts +4 -4
- package/dist/proxmox/pbs-datastore-wire.d.ts +4 -4
- package/dist/proxmox/pbs-datastore-wire.d.ts.map +1 -1
- package/dist/proxmox/pbs-datastore.d.ts +1 -1
- package/dist/proxmox/pbs-datastore.d.ts.map +1 -1
- package/dist/proxmox/pbs-notification-matcher-distilled.d.ts +4 -4
- package/dist/proxmox/pbs-notification-matcher-distilled.d.ts.map +1 -1
- package/dist/proxmox/pbs-notification-matcher.d.ts +1 -1
- package/dist/proxmox/pbs-notification-matcher.d.ts.map +1 -1
- package/dist/proxmox/pbs-notification-target-distilled.d.ts +4 -4
- package/dist/proxmox/pbs-notification-target-distilled.d.ts.map +1 -1
- package/dist/proxmox/pbs-notification-target-lifecycle.d.ts +4 -4
- package/dist/proxmox/pbs-notification-target.d.ts +1 -1
- package/dist/proxmox/pbs-notification-target.d.ts.map +1 -1
- package/dist/proxmox/pbs-prune-job-lifecycle.d.ts +4 -4
- package/dist/proxmox/pbs-prune-job-wire.d.ts +4 -4
- package/dist/proxmox/pbs-prune-job-wire.d.ts.map +1 -1
- package/dist/proxmox/pbs-prune-job.d.ts +1 -1
- package/dist/proxmox/pbs-prune-job.d.ts.map +1 -1
- package/dist/proxmox/pbs-sync-job-lifecycle.d.ts +4 -4
- package/dist/proxmox/pbs-sync-job-wire.d.ts +4 -4
- package/dist/proxmox/pbs-sync-job-wire.d.ts.map +1 -1
- package/dist/proxmox/pbs-sync-job.d.ts +1 -1
- package/dist/proxmox/pbs-sync-job.d.ts.map +1 -1
- package/dist/proxmox/pbs-verify-job-lifecycle.d.ts +4 -4
- package/dist/proxmox/pbs-verify-job-wire.d.ts +4 -4
- package/dist/proxmox/pbs-verify-job-wire.d.ts.map +1 -1
- package/dist/proxmox/pbs-verify-job.d.ts +1 -1
- package/dist/proxmox/pbs-verify-job.d.ts.map +1 -1
- package/dist/proxmox/pool.d.ts +3 -3
- package/dist/proxmox/pool.d.ts.map +1 -1
- package/dist/proxmox/provision-declare.d.ts +1 -1
- package/dist/proxmox/provision-declare.d.ts.map +1 -1
- package/dist/proxmox/qemu-lifecycle.d.ts +3 -3
- package/dist/proxmox/qemu-lifecycle.d.ts.map +1 -1
- package/dist/proxmox/qemu-read.d.ts +1 -1
- package/dist/proxmox/qemu-read.d.ts.map +1 -1
- package/dist/proxmox/qemu-resize.d.ts +1 -1
- package/dist/proxmox/qemu-resize.d.ts.map +1 -1
- package/dist/proxmox/qemu-task.d.ts +1 -1
- package/dist/proxmox/qemu-task.d.ts.map +1 -1
- package/dist/proxmox/qemu.d.ts +1 -1
- package/dist/proxmox/qemu.d.ts.map +1 -1
- package/dist/proxmox/replication-job-lifecycle.d.ts +6 -6
- package/dist/proxmox/replication-job-lifecycle.d.ts.map +1 -1
- package/dist/proxmox/replication-job.d.ts +1 -1
- package/dist/proxmox/replication-job.d.ts.map +1 -1
- package/dist/proxmox/resource-spec.d.ts +1 -1
- package/dist/proxmox/resource-spec.d.ts.map +1 -1
- package/dist/proxmox/resource.d.ts +8 -8
- package/dist/proxmox/role.d.ts +1 -1
- package/dist/proxmox/role.d.ts.map +1 -1
- package/dist/proxmox/sdn-apply-read.d.ts +1 -1
- package/dist/proxmox/sdn-apply-read.d.ts.map +1 -1
- package/dist/proxmox/sdn-apply.d.ts +1 -1
- package/dist/proxmox/sdn-apply.d.ts.map +1 -1
- package/dist/proxmox/sdn-subnet.d.ts +1 -1
- package/dist/proxmox/sdn-subnet.d.ts.map +1 -1
- package/dist/proxmox/sdn-vnet.d.ts +1 -1
- package/dist/proxmox/sdn-vnet.d.ts.map +1 -1
- package/dist/proxmox/sdn-zone.d.ts +1 -1
- package/dist/proxmox/sdn-zone.d.ts.map +1 -1
- package/dist/proxmox/storage-download-lifecycle.d.ts +6 -6
- package/dist/proxmox/storage-download-lifecycle.d.ts.map +1 -1
- package/dist/proxmox/storage-download-task.d.ts +1 -1
- package/dist/proxmox/storage-download-task.d.ts.map +1 -1
- package/dist/proxmox/storage-download.d.ts +1 -1
- package/dist/proxmox/storage-download.d.ts.map +1 -1
- package/dist/proxmox/storage-wire.d.ts +2 -2
- package/dist/proxmox/storage-wire.d.ts.map +1 -1
- package/dist/proxmox/storage.d.ts +1 -1
- package/dist/proxmox/storage.d.ts.map +1 -1
- package/dist/proxmox/user-wire.d.ts +2 -2
- package/dist/proxmox/user-wire.d.ts.map +1 -1
- package/dist/proxmox/user.d.ts +1 -1
- package/dist/proxmox/user.d.ts.map +1 -1
- package/dist/proxmox/zfs-pool-form.d.ts +2 -2
- package/dist/proxmox/zfs-pool-form.d.ts.map +1 -1
- package/dist/proxmox/zfs-pool-wire.d.ts +2 -2
- package/dist/proxmox/zfs-pool-wire.d.ts.map +1 -1
- package/dist/proxmox/zfs-pool.d.ts +1 -1
- package/dist/proxmox/zfs-pool.d.ts.map +1 -1
- package/dist/release/binary.d.ts +1 -1
- package/dist/release/binary.d.ts.map +1 -1
- package/dist/release/download.d.ts +1 -1
- package/dist/release/download.d.ts.map +1 -1
- package/dist/release/index.js +6 -5
- package/dist/release/index.js.map +5 -5
- package/dist/talos/credentials-write.d.ts +1 -1
- package/dist/talos/credentials-write.d.ts.map +1 -1
- package/dist/talos/credentials.d.ts +1 -1
- package/dist/talos/credentials.d.ts.map +1 -1
- package/dist/talos/fake-process.d.ts +2 -2
- package/dist/talos/fake-process.d.ts.map +1 -1
- package/dist/talos/index.js +7 -7
- package/dist/talos/index.js.map +4 -4
- package/dist/talos/kubeconfig.d.ts +4 -4
- package/dist/talos/kubeconfig.d.ts.map +1 -1
- package/dist/talos/machine-config-fixtures.d.ts +1 -1
- package/dist/talos/machine-config-fixtures.d.ts.map +1 -1
- package/dist/talos/machine-config-read.d.ts +3 -3
- package/dist/talos/machine-config-read.d.ts.map +1 -1
- package/dist/talos/resource.d.ts +1 -1
- package/dist/talos/resource.d.ts.map +1 -1
- package/dist/talos/talos-bootstrap-etcd.d.ts +2 -2
- package/dist/talos/talos-bootstrap-etcd.d.ts.map +1 -1
- package/dist/talos/talos-bootstrap.d.ts +4 -4
- package/dist/talos/talos-bootstrap.d.ts.map +1 -1
- package/dist/talos/talos-cluster-health.d.ts +4 -4
- package/dist/talos/talos-cluster-health.d.ts.map +1 -1
- package/dist/talos/talos-machine-config.d.ts +2 -2
- package/dist/talos/talos-machine-config.d.ts.map +1 -1
- package/dist/talos/talosctl.d.ts +1 -1
- package/dist/talos/talosctl.d.ts.map +1 -1
- package/dist/telemetry/index.js +4 -4
- package/dist/telemetry/index.js.map +3 -3
- package/dist/telemetry/redact-serialization.d.ts +2 -2
- package/dist/telemetry/redact-serialization.d.ts.map +1 -1
- package/dist/unifi/acl-rule-ordering.d.ts +6 -6
- package/dist/unifi/acl-rule-ordering.d.ts.map +1 -1
- package/dist/unifi/acl-rule.d.ts +6 -6
- package/dist/unifi/acl-rule.d.ts.map +1 -1
- package/dist/unifi/dns-policy.d.ts +6 -6
- package/dist/unifi/dns-policy.d.ts.map +1 -1
- package/dist/unifi/fake-unifi.d.ts +1 -1
- package/dist/unifi/fake-unifi.d.ts.map +1 -1
- package/dist/unifi/firewall-policy-ordering.d.ts +6 -6
- package/dist/unifi/firewall-policy-ordering.d.ts.map +1 -1
- package/dist/unifi/firewall-policy.d.ts +6 -6
- package/dist/unifi/firewall-policy.d.ts.map +1 -1
- package/dist/unifi/firewall-zone.d.ts +6 -6
- package/dist/unifi/firewall-zone.d.ts.map +1 -1
- package/dist/unifi/index.js +2 -2
- package/dist/unifi/index.js.map +2 -2
- package/dist/unifi/network.d.ts +6 -6
- package/dist/unifi/network.d.ts.map +1 -1
- package/dist/unifi/providers.d.ts +1 -1
- package/dist/unifi/providers.d.ts.map +1 -1
- package/dist/unifi/resource.d.ts +1 -1
- package/dist/unifi/resource.d.ts.map +1 -1
- package/dist/unifi/traffic-matching-list.d.ts +6 -6
- package/dist/unifi/traffic-matching-list.d.ts.map +1 -1
- package/dist/unifi/wifi-broadcast.d.ts +6 -6
- package/dist/unifi/wifi-broadcast.d.ts.map +1 -1
- package/dist/verify/cli.js.map +1 -1
- package/dist/verify/index.js.map +1 -1
- package/docs/distilled-interim.md +1 -1
- package/docs/mesh-node.md +1 -1
- package/docs/paperless.md +1 -1
- package/docs/peers.md +8 -8
- package/docs/postgres-schema.md +22 -1
- package/docs/telemetry.md +1 -1
- package/package.json +20 -20
- package/src/argocd/fake-argocd.ts +1 -1
- package/src/argocd/resource.ts +1 -1
- package/src/caddy/admin-transport-scope.test.ts +2 -2
- package/src/caddy/admin.ts +1 -1
- package/src/caddy/caddy-http-client.ts +5 -5
- package/src/caddy/config-adopt.test.ts +2 -2
- package/src/caddy/format.ts +2 -2
- package/src/caddy/local-admin.test.ts +1 -1
- package/src/ceph/auth-entity.ts +1 -1
- package/src/ceph/ceph-auth-reconcile.ts +1 -1
- package/src/cloudflare/fake-mesh.ts +1 -1
- package/src/cloudflare/fake-r2-lock.ts +1 -1
- package/src/cloudflare/mesh-node-harness.ts +1 -1
- package/src/cloudflare/mesh-node.ts +1 -1
- package/src/cloudflare/r2-bucket-lock.ts +1 -1
- package/src/discord/fake-discord.ts +1 -1
- package/src/discord/resource.ts +1 -1
- package/src/forgejo/fake-forgejo.ts +1 -1
- package/src/forgejo/resource.ts +1 -1
- package/src/google-workspace/fake-google-workspace.ts +1 -1
- package/src/google-workspace/resource.ts +1 -1
- package/src/grafana/fake-grafana.ts +1 -1
- package/src/litellm/access-group.test.ts +1 -1
- package/src/litellm/credential-errors.ts +1 -1
- package/src/litellm/credential-guards.test.ts +1 -1
- package/src/litellm/credential-operations.ts +1 -1
- package/src/litellm/credential-rewrite.test.ts +1 -1
- package/src/litellm/fake-stack.ts +1 -1
- package/src/litellm/key-errors.ts +1 -1
- package/src/litellm/key-harness.ts +1 -1
- package/src/litellm/key-operations.ts +1 -1
- package/src/litellm/key.test.ts +2 -1
- package/src/litellm/mcp-server-handlers.test.ts +1 -1
- package/src/litellm/mcp-server-registry.test.ts +1 -1
- package/src/litellm/model-operations.test.ts +1 -1
- package/src/litellm/operations.ts +1 -1
- package/src/litellm/pass-through-endpoint-delete-swallow.test.ts +1 -1
- package/src/litellm/pass-through-endpoint.test.ts +1 -1
- package/src/litellm/pass-through-fetch-client.test.ts +2 -2
- package/src/litellm/policy-attachment.test.ts +1 -1
- package/src/litellm/policy.test.ts +1 -1
- package/src/litellm/team-members.test.ts +1 -1
- package/src/litellm/team-read.test.ts +1 -1
- package/src/litellm/tool-policy.test.ts +1 -1
- package/src/litellm/toolset.test.ts +1 -1
- package/src/netbox/fake-netbox.ts +1 -1
- package/src/netbox/resource.ts +1 -1
- package/src/openbao/adopt-core.test.ts +4 -1
- package/src/openbao/adopt-families.test.ts +4 -1
- package/src/openbao/adopt-holes.test.ts +1 -1
- package/src/openbao/adopt-recovery.test.ts +24 -0
- package/src/openbao/approle-login.ts +2 -2
- package/src/openbao/auth-method-reconcile.ts +1 -1
- package/src/openbao/auth-method.ts +1 -1
- package/src/openbao/auth-role-sdk.test.ts +1 -1
- package/src/openbao/auth-role.ts +1 -1
- package/src/openbao/bao-http.test.ts +3 -3
- package/src/openbao/bao-http.ts +4 -4
- package/src/openbao/bao-identity.ts +2 -2
- package/src/openbao/cloudflare-permission-groups.ts +1 -1
- package/src/openbao/cloudflare-role-wire.ts +1 -1
- package/src/openbao/cloudflare-role.ts +1 -1
- package/src/openbao/distilled-retry.test.ts +3 -3
- package/src/openbao/distilled.test.ts +3 -3
- package/src/openbao/distilled.ts +2 -2
- package/src/openbao/fake-bao.ts +2 -2
- package/src/openbao/fake-core.ts +1 -1
- package/src/openbao/fake-engines.ts +1 -1
- package/src/openbao/fake-families.ts +1 -1
- package/src/openbao/fake-stack.ts +13 -3
- package/src/openbao/jwt-config.ts +1 -1
- package/src/openbao/jwt-role.ts +1 -1
- package/src/openbao/kubernetes-role.ts +1 -1
- package/src/openbao/mfa-enforcement.ts +1 -1
- package/src/openbao/mfa-totp.ts +1 -1
- package/src/openbao/mfa-wire.ts +1 -1
- package/src/openbao/mount-move.ts +1 -1
- package/src/openbao/mount-reconcile.ts +1 -1
- package/src/openbao/mount.ts +1 -1
- package/src/openbao/pki-role.ts +1 -1
- package/src/openbao/plugin-reconcile.ts +1 -1
- package/src/openbao/plugin-wire.ts +1 -1
- package/src/openbao/plugin.ts +1 -1
- package/src/openbao/policy.ts +1 -1
- package/src/openbao/proxmox-role.ts +1 -1
- package/src/openbao/remount-wire.ts +1 -1
- package/src/openbao/rename-occupied.test.ts +1 -1
- package/src/openbao/role-reconcile.ts +1 -1
- package/src/openbao/role-rename.test.ts +1 -1
- package/src/openbao/ssh-role.ts +1 -1
- package/src/opnsense/fake-opnsense.ts +1 -1
- package/src/opnsense/resource.ts +1 -1
- package/src/ownership/probe.ts +51 -2
- package/src/paperless/fake-paperless.ts +1 -1
- package/src/paperless/matching-types.ts +1 -1
- package/src/postgres/connection.ts +11 -15
- package/src/postgres/database-bigint-serialization.test.ts +3 -3
- package/src/postgres/database-sql.ts +7 -7
- package/src/postgres/database.ts +1 -1
- package/src/postgres/fake-grants-read.ts +4 -4
- package/src/postgres/fake-grants-sql.ts +1 -1
- package/src/postgres/fake-grants-write.ts +1 -1
- package/src/postgres/fake-role-sql.ts +3 -3
- package/src/postgres/fake-schema-drop.ts +75 -0
- package/src/postgres/fake-schema-sql.ts +11 -2
- package/src/postgres/fake-sql.ts +11 -4
- package/src/postgres/grants-database.test.ts +5 -5
- package/src/postgres/grants-existence.ts +8 -8
- package/src/postgres/grants-ops.ts +1 -1
- package/src/postgres/grants-ownership.ts +6 -6
- package/src/postgres/grants-psql-lifecycle.test.ts +7 -3
- package/src/postgres/grants-read-defaults.ts +6 -6
- package/src/postgres/grants-read.ts +17 -17
- package/src/postgres/grants-relkind-wire.test.ts +6 -4
- package/src/postgres/index.ts +2 -0
- package/src/postgres/installed-classifier.ts +1 -1
- package/src/postgres/psql-executor-escaping.test.ts +3 -1
- package/src/postgres/psql-executor.test.ts +11 -5
- package/src/postgres/psql-executor.ts +5 -7
- package/src/postgres/role-adopt.test.ts +3 -1
- package/src/postgres/role-membership-options.test.ts +3 -1
- package/src/postgres/role-membership-repair.test.ts +3 -3
- package/src/postgres/role-membership-sql.ts +5 -5
- package/src/postgres/role-password-transports.test.ts +6 -4
- package/src/postgres/role-sql.ts +2 -2
- package/src/postgres/role.ts +1 -1
- package/src/postgres/schema-assert.ts +1 -1
- package/src/postgres/schema-closure-sql.ts +108 -0
- package/src/postgres/schema-delete-proofs.test.ts +7 -7
- package/src/postgres/schema-delete.ts +89 -47
- package/src/postgres/schema-drop-2bp01.test.ts +30 -18
- package/src/postgres/schema-drop-real.test.ts +190 -0
- package/src/postgres/schema-drop-sql.ts +135 -0
- package/src/postgres/schema-drop.test.ts +241 -0
- package/src/postgres/schema-errors.ts +26 -0
- package/src/postgres/schema-handlers.test.ts +21 -14
- package/src/postgres/schema-lifecycle.test.ts +6 -60
- package/src/postgres/schema-reconcile.ts +1 -1
- package/src/postgres/schema-sequence-error.ts +38 -0
- package/src/postgres/schema-socket.test.ts +20 -6
- package/src/postgres/schema-sql.ts +17 -12
- package/src/postgres/schema-takeover.test.ts +7 -4
- package/src/postgres/schema-test-kit.ts +18 -4
- package/src/postgres/search-path.test.ts +100 -0
- package/src/postgres/search-path.ts +62 -0
- package/src/proxmox/ceph-flag-wire.test.ts +1 -1
- package/src/proxmox/ceph-fs-destroy-sdk.test.ts +1 -1
- package/src/proxmox/ceph-pool-write.test.ts +1 -1
- package/src/proxmox/client.test.ts +2 -2
- package/src/proxmox/client.ts +2 -2
- package/src/proxmox/credentials.test.ts +1 -1
- package/src/proxmox/distilled-pbs.test.ts +1 -1
- package/src/proxmox/distilled-pbs.ts +1 -1
- package/src/proxmox/distilled-pve.test.ts +2 -2
- package/src/proxmox/distilled-pve.ts +1 -1
- package/src/proxmox/fake-pbs-jobs.ts +1 -1
- package/src/proxmox/fake-pve.ts +1 -1
- package/src/proxmox/lease-cache-mint.test.ts +1 -1
- package/src/proxmox/live-core-fixtures.ts +1 -1
- package/src/proxmox/lxc-distilled.test.ts +1 -1
- package/src/proxmox/lxc-harness.ts +1 -1
- package/src/proxmox/members-timeout.test.ts +3 -3
- package/src/proxmox/members.test.ts +1 -1
- package/src/proxmox/members.ts +4 -4
- package/src/proxmox/mint.ts +4 -4
- package/src/proxmox/node-network-read-failure.test.ts +1 -1
- package/src/proxmox/qemu-lifecycle.ts +1 -1
- package/src/proxmox/read-failure-propagation.test.ts +1 -1
- package/src/proxmox/resource-spec.ts +1 -1
- package/src/proxmox/storage-read-failure.test.ts +1 -1
- package/src/proxmox/typed-absence.test.ts +2 -2
- package/src/proxmox/unreadable-read.test.ts +1 -1
- package/src/proxmox/user.test.ts +1 -1
- package/src/proxmox/zfs-pool-read-failure.test.ts +1 -1
- package/src/release/adopt-parity.test.ts +3 -1
- package/src/release/apply-pins.test.ts +2 -2
- package/src/release/binary-root.test.ts +2 -2
- package/src/release/binary.ts +9 -2
- package/src/release/download.test.ts +3 -3
- package/src/release/download.ts +1 -1
- package/src/release/fake-deploy.ts +2 -2
- package/src/release/fake-engine.ts +2 -2
- package/src/release/fake-provider.ts +2 -2
- package/src/release/plan.test.ts +2 -2
- package/src/release/providers.ts +1 -1
- package/src/talos/credentials-write.test.ts +1 -1
- package/src/talos/credentials-write.ts +2 -2
- package/src/talos/credentials.test.ts +1 -1
- package/src/talos/credentials.ts +2 -2
- package/src/talos/fake-process.ts +2 -2
- package/src/talos/kubeconfig.test.ts +1 -1
- package/src/talos/machine-config-fixtures.ts +1 -1
- package/src/talos/resource.ts +1 -1
- package/src/talos/talos-bootstrap.test.ts +1 -1
- package/src/talos/talos-cluster-health.test.ts +1 -1
- package/src/talos/talosctl.ts +2 -2
- package/src/telemetry/http-client-sealing.test.ts +2 -2
- package/src/telemetry/layer.test.ts +3 -3
- package/src/telemetry/layer.ts +2 -2
- package/src/telemetry/redact-serialization.ts +2 -2
- package/src/unifi/fake-unifi.ts +1 -1
- package/src/unifi/get-only-guard.test.ts +3 -3
- package/src/unifi/resource.ts +1 -1
- package/src/verify/args.ts +1 -1
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Refusals use checked catalog facts or raw SQLSTATE (42P06 / 2BP01), never driver message text.
|
|
5
5
|
*/
|
|
6
6
|
import * as Data from 'effect/Data';
|
|
7
|
+
import type { PostgresSchemaCascadeSequencesRefused } from './schema-sequence-error.ts';
|
|
7
8
|
|
|
8
9
|
/** A declared name would be silently truncated (same as `PostgresDatabaseNameRefused`). */
|
|
9
10
|
export class PostgresSchemaNameRefused extends Data.TaggedError('PostgresSchemaNameRefused')<{
|
|
@@ -94,6 +95,29 @@ export class PostgresSchemaDropNotEmptyError extends Data.TaggedError(
|
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
97
|
|
|
98
|
+
/**
|
|
99
|
+
* `cascade: true` refused: `DROP SCHEMA … CASCADE` would also drop objects that live in OTHER
|
|
100
|
+
* schemas because they depend on something in this one (a view, a foreign key or a trigger
|
|
101
|
+
* another stack owns). The refusal carries the COUNT only — never an object name, so the error
|
|
102
|
+
* cannot disclose another owner's catalog. Nothing was dropped: move or drop those dependents
|
|
103
|
+
* deliberately, or empty this schema by hand and declare `cascade: false`.
|
|
104
|
+
*/
|
|
105
|
+
export class PostgresSchemaCascadeCrossSchemaRefused extends Data.TaggedError(
|
|
106
|
+
'PostgresSchemaCascadeCrossSchemaRefused',
|
|
107
|
+
)<{
|
|
108
|
+
readonly schema: string;
|
|
109
|
+
/** How many dependent objects live outside this schema (at least one when raised). */
|
|
110
|
+
readonly dependents: number;
|
|
111
|
+
}> {
|
|
112
|
+
override get message(): string {
|
|
113
|
+
return (
|
|
114
|
+
`Postgres.Schema "${this.schema}": cascade: true refused — ${String(this.dependents)} ` +
|
|
115
|
+
'dependent object(s) outside this schema would be dropped with it. No DROP was issued. ' +
|
|
116
|
+
'Remove or move those dependents first.'
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
97
121
|
/** Every statement this resource issues must run against the DECLARED database. The family
|
|
98
122
|
* connection points at a maintenance database, so the schema handlers open `props.database`
|
|
99
123
|
* themselves (`withPg`'s database override); this failure means the server answered
|
|
@@ -204,6 +228,8 @@ export type PostgresSchemaError =
|
|
|
204
228
|
| PostgresSchemaDrift
|
|
205
229
|
| PostgresSchemaCreateVanished
|
|
206
230
|
| PostgresSchemaDropNotEmptyError
|
|
231
|
+
| PostgresSchemaCascadeCrossSchemaRefused
|
|
232
|
+
| PostgresSchemaCascadeSequencesRefused
|
|
207
233
|
| PostgresSchemaDeleteForeignRefused
|
|
208
234
|
| PostgresSchemaWrongDatabase
|
|
209
235
|
| PostgresSchemaDatabaseRefused;
|
|
@@ -14,6 +14,7 @@ import type { PostgresSchemaAttributes, PostgresSchemaProps } from './schema-att
|
|
|
14
14
|
import { Unowned } from 'alchemy/AdoptPolicy';
|
|
15
15
|
import { postgresSchemaHandlers } from './schema.ts';
|
|
16
16
|
import { PostgresSchemaDropNotEmptyError, PostgresSchemaWrongDatabase } from './schema-errors.ts';
|
|
17
|
+
import { buildAtomicDropSql } from './schema-drop-sql.ts';
|
|
17
18
|
import { deleteArgs, ok, readArgs, route, router, runnerWith } from './schema-test-kit.ts';
|
|
18
19
|
|
|
19
20
|
const sampleOutput: PostgresSchemaAttributes = {
|
|
@@ -51,10 +52,12 @@ describe('delete handler (runner transport)', () => {
|
|
|
51
52
|
),
|
|
52
53
|
),
|
|
53
54
|
);
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
// The drop is ONE `DO` statement carrying the proven oid + owner (`schema-drop-sql.ts`),
|
|
56
|
+
// not a re-read followed by a separate `DROP`.
|
|
57
|
+
expect(stdins.find((s) => s.startsWith('DO '))).toBe(
|
|
58
|
+
`${buildAtomicDropSql({ name: 'ledger', oid: 1, owner: 'tim', cascade: true })};`,
|
|
56
59
|
);
|
|
57
|
-
// One `psql` argv per STATEMENT: probe, proof, ownership re-read, drop. The probe's argv
|
|
60
|
+
// One `psql` argv per STATEMENT: probe, proof, ownership re-read, atomic drop. The probe's argv
|
|
58
61
|
// targets the family database; the delete's argvs target the declared one — the `-d` value
|
|
59
62
|
// is the argv's last element.
|
|
60
63
|
expect(argvs.length).toBe(4);
|
|
@@ -80,17 +83,19 @@ describe('delete handler (runner transport)', () => {
|
|
|
80
83
|
),
|
|
81
84
|
),
|
|
82
85
|
);
|
|
83
|
-
expect(stdins.find((s) => s.startsWith('
|
|
86
|
+
expect(stdins.find((s) => s.startsWith('DO '))).toBe(
|
|
87
|
+
`${buildAtomicDropSql({ name: 'ledger', oid: 1, owner: 'tim', cascade: false })};`,
|
|
88
|
+
);
|
|
84
89
|
});
|
|
85
90
|
|
|
86
|
-
test('
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
test('a non-empty schema is the typed refusal the atomic drop raises (HF002), over one psql', async () => {
|
|
92
|
+
const answers = router({
|
|
93
|
+
probe: '[{"present":1}]',
|
|
94
|
+
schema: '[{"name":"ledger","oid":1,"owner":"tim"}]',
|
|
95
|
+
proof,
|
|
96
|
+
});
|
|
97
|
+
const { run, stdins } = runnerWith(answers, (stdin) =>
|
|
98
|
+
stdin.startsWith('DO ') ? 'ERROR: HF002: schema not empty' : undefined,
|
|
94
99
|
);
|
|
95
100
|
const error = await Effect.runPromise(
|
|
96
101
|
Effect.flip(
|
|
@@ -104,7 +109,9 @@ describe('delete handler (runner transport)', () => {
|
|
|
104
109
|
),
|
|
105
110
|
);
|
|
106
111
|
expect(error).toBeInstanceOf(PostgresSchemaDropNotEmptyError);
|
|
107
|
-
|
|
112
|
+
// Proof and drop were ONE statement: exactly one `DO`, and no separate emptiness probe.
|
|
113
|
+
expect(stdins.filter((s) => s.startsWith('DO ')).length).toBe(1);
|
|
114
|
+
expect(stdins.some((s) => s.includes('AS empty'))).toBe(false);
|
|
108
115
|
});
|
|
109
116
|
|
|
110
117
|
test('the real handler refuses a wrong connected database before any DROP', async () => {
|
|
@@ -128,7 +135,7 @@ describe('delete handler (runner transport)', () => {
|
|
|
128
135
|
),
|
|
129
136
|
);
|
|
130
137
|
expect(error).toBeInstanceOf(PostgresSchemaWrongDatabase);
|
|
131
|
-
expect(stdins.some((s) => s.startsWith('
|
|
138
|
+
expect(stdins.some((s) => s.startsWith('DO '))).toBe(false);
|
|
132
139
|
});
|
|
133
140
|
|
|
134
141
|
test('defaultRemovalPolicy is retain — declared on the Resource for Postgres.Schema itself', () => {
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `reconcile`, `read`, and `drop` for `Postgres.Schema` against `fake-sql.ts`'s recording fake.
|
|
3
3
|
* Tests the greenfield create, comment creation, already-present/drift, owner-missing guard,
|
|
4
|
-
* quoting,
|
|
4
|
+
* quoting, and the `current_database()` proof every write path runs first. The drop rules live
|
|
5
|
+
* in `schema-drop.test.ts`.
|
|
5
6
|
*/
|
|
6
7
|
import { describe, expect, test } from 'bun:test';
|
|
7
8
|
import * as Effect from 'effect/Effect';
|
|
8
|
-
import { SqlError, UnknownError } from 'effect/unstable/sql/SqlError';
|
|
9
9
|
import { makeFakeSql } from './fake-sql.ts';
|
|
10
10
|
import { type PostgresSchemaAttributes, type PostgresSchemaProps } from './schema-attrs.ts';
|
|
11
|
-
import type { PgExecutor } from './database-sql.ts';
|
|
12
11
|
import {
|
|
13
12
|
PostgresSchemaCreateVanished,
|
|
14
13
|
PostgresSchemaDrift,
|
|
15
|
-
PostgresSchemaDropNotEmptyError,
|
|
16
14
|
PostgresSchemaExistsRefused,
|
|
17
15
|
PostgresSchemaOwnerMissing,
|
|
18
16
|
PostgresSchemaWrongDatabase,
|
|
@@ -42,7 +40,9 @@ describe('the current_database() proof', () => {
|
|
|
42
40
|
expect(refusal.declared).toBe('agents');
|
|
43
41
|
expect(refusal.connected).toBe('postgres');
|
|
44
42
|
// The refusal fires before the first pg_namespace read, so nothing but the proof ran.
|
|
45
|
-
expect(fake.statements.some((s) => s.text.includes('FROM pg_namespace'))).toBe(
|
|
43
|
+
expect(fake.statements.some((s) => s.text.includes('FROM pg_catalog.pg_namespace'))).toBe(
|
|
44
|
+
false,
|
|
45
|
+
);
|
|
46
46
|
expect(fake.statements.some((s) => s.text.startsWith('CREATE SCHEMA'))).toBe(false);
|
|
47
47
|
});
|
|
48
48
|
|
|
@@ -50,7 +50,7 @@ describe('the current_database() proof', () => {
|
|
|
50
50
|
const fake = makeFakeSql({ schemas: [liveRow()] });
|
|
51
51
|
const error = await fails(dropWithClient(fake, { ...baseProps, database: 'agents' }));
|
|
52
52
|
expect(error).toBeInstanceOf(PostgresSchemaWrongDatabase);
|
|
53
|
-
expect(fake.statements.some((s) => s.text.startsWith('
|
|
53
|
+
expect(fake.statements.some((s) => s.text.startsWith('DO '))).toBe(false);
|
|
54
54
|
expect(fake.schemas.get('ledger')).not.toBeUndefined();
|
|
55
55
|
});
|
|
56
56
|
|
|
@@ -190,57 +190,3 @@ describe('read', () => {
|
|
|
190
190
|
expect(await run(readWithClient(fake, 'ledger'))).toEqual(liveRow());
|
|
191
191
|
});
|
|
192
192
|
});
|
|
193
|
-
|
|
194
|
-
describe('drop', () => {
|
|
195
|
-
test('drops an empty schema with cascade false', async () => {
|
|
196
|
-
const fake = makeFakeSql({ schemas: [liveRow()] });
|
|
197
|
-
await run(dropWithClient(fake, baseProps));
|
|
198
|
-
const drops = fake.statements.filter((s) => s.text.startsWith('DROP SCHEMA'));
|
|
199
|
-
expect(drops.length).toBe(1);
|
|
200
|
-
expect(drops[0]?.text).toBe('DROP SCHEMA IF EXISTS "ledger"');
|
|
201
|
-
expect(fake.schemas.get('ledger')).toBeUndefined();
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
test('refuses to drop a non-empty schema with cascade false', async () => {
|
|
205
|
-
const fake = makeFakeSql({ schemas: [liveRow()], schemasWithRelations: ['ledger'] });
|
|
206
|
-
const error = await fails(dropWithClient(fake, baseProps));
|
|
207
|
-
expect(error).toBeInstanceOf(PostgresSchemaDropNotEmptyError);
|
|
208
|
-
expect(fake.schemas.get('ledger')).not.toBeUndefined();
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
test('drops a non-empty schema with cascade true', async () => {
|
|
212
|
-
const fake = makeFakeSql({ schemas: [liveRow()], schemasWithRelations: ['ledger'] });
|
|
213
|
-
await run(dropWithClient(fake, { ...baseProps, cascade: true }));
|
|
214
|
-
const drop = fake.statements.find((s) => s.text.startsWith('DROP SCHEMA'));
|
|
215
|
-
expect(drop?.text).toBe('DROP SCHEMA IF EXISTS "ledger" CASCADE');
|
|
216
|
-
expect(fake.schemas.get('ledger')).toBeUndefined();
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
test('classifies the server 2BP01 refusal as the typed not-empty tag', async () => {
|
|
220
|
-
// The emptiness check said empty, but the server refuses with SQLSTATE 2BP01 — an object
|
|
221
|
-
// kind the check's four catalogs do not cover. Class `2B` is not `42`, so both drivers
|
|
222
|
-
// wrap it as UnknownError with the raw code on the cause; this executor replays that shape.
|
|
223
|
-
const refusing: PgExecutor = {
|
|
224
|
-
unsafe: <A extends object>(text: string) =>
|
|
225
|
-
text.startsWith('SELECT current_database()')
|
|
226
|
-
? Effect.succeed([{ database: 'postgres' }] as unknown as ReadonlyArray<A>)
|
|
227
|
-
: text.includes('AS empty')
|
|
228
|
-
? Effect.succeed([{ empty: true }] as unknown as ReadonlyArray<A>)
|
|
229
|
-
: Effect.fail(
|
|
230
|
-
new SqlError({
|
|
231
|
-
reason: new UnknownError({
|
|
232
|
-
cause: Object.assign(
|
|
233
|
-
new Error('ERROR: 2BP01: dependent objects still exist'),
|
|
234
|
-
{ code: '2BP01' },
|
|
235
|
-
),
|
|
236
|
-
message: 'dependent_objects_still_exist',
|
|
237
|
-
operation: 'DROP SCHEMA',
|
|
238
|
-
}),
|
|
239
|
-
}),
|
|
240
|
-
),
|
|
241
|
-
transaction: () => Effect.void,
|
|
242
|
-
};
|
|
243
|
-
const error = await fails(dropWithClient(refusing, baseProps));
|
|
244
|
-
expect(error).toBeInstanceOf(PostgresSchemaDropNotEmptyError);
|
|
245
|
-
});
|
|
246
|
-
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Schema reconcile: prove the stored target and identity before asserting a live row. */
|
|
2
2
|
import * as Effect from 'effect/Effect';
|
|
3
|
-
import type { SqlError } from 'effect/
|
|
3
|
+
import type { SqlError } from 'effect/sql/SqlError';
|
|
4
4
|
import { type PgExecutor, roleExists } from './database-sql.ts';
|
|
5
5
|
import {
|
|
6
6
|
type PostgresSchemaAttributes,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as Data from 'effect/Data';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `cascade: true` on a schema that holds a SEQUENCE is refused (`HF004`, `schema-drop-sql.ts`).
|
|
5
|
+
*
|
|
6
|
+
* ⛔ A sequence cannot be locked by `LOCK TABLE`, so the drop's closure proof has a hole there: a
|
|
7
|
+
* concurrent, uncommitted `CREATE VIEW other.v AS SELECT last_value FROM ours.seq` is invisible
|
|
8
|
+
* to the scan, and when it commits while the drop waits on the sequence, PostgreSQL's deletion
|
|
9
|
+
* cascades into `other.v` (Codex read of PR 357, 2026-10-06). Serial and identity columns each
|
|
10
|
+
* own a sequence, so such a schema cannot be cascade-dropped by this provider.
|
|
11
|
+
* The message carries a COUNT only. Nothing was dropped: drop the schema's contents deliberately
|
|
12
|
+
* (the sequences included), then declare `cascade: false`.
|
|
13
|
+
*/
|
|
14
|
+
export class PostgresSchemaCascadeSequencesRefused extends Data.TaggedError(
|
|
15
|
+
'PostgresSchemaCascadeSequencesRefused',
|
|
16
|
+
)<{
|
|
17
|
+
readonly schema: string;
|
|
18
|
+
/** How many sequences the schema holds (at least one when raised). */
|
|
19
|
+
readonly sequences: number;
|
|
20
|
+
}> {
|
|
21
|
+
override get message(): string {
|
|
22
|
+
return (
|
|
23
|
+
`Postgres.Schema "${this.schema}": cascade: true refused — the schema holds ` +
|
|
24
|
+
`${String(this.sequences)} sequence(s) (serial/identity columns own one), which cannot be ` +
|
|
25
|
+
'locked, so a concurrent dependent could escape the cascade proof. No DROP was issued. ' +
|
|
26
|
+
'Drop the schema contents first, then declare cascade: false.'
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The sequence COUNT out of an `HF004` error; unreadable still refuses (1, never 0). */
|
|
32
|
+
export const sequencesCount = (error: {
|
|
33
|
+
readonly reason: { readonly cause?: unknown };
|
|
34
|
+
}): number => {
|
|
35
|
+
const cause = error.reason.cause;
|
|
36
|
+
const message = typeof cause === 'object' && cause !== null ? (cause as Error).message : '';
|
|
37
|
+
return Math.max(1, Number(/sequences:\s*(\d+)/.exec(message ?? '')?.[1] ?? 1));
|
|
38
|
+
};
|
|
@@ -11,7 +11,8 @@ import * as PgClientModule from '@effect/sql-pg/PgClient';
|
|
|
11
11
|
import type { PgClient as PgClientService } from '@effect/sql-pg/PgClient';
|
|
12
12
|
import * as Effect from 'effect/Effect';
|
|
13
13
|
import * as Layer from 'effect/Layer';
|
|
14
|
-
import { SqlClient } from 'effect/
|
|
14
|
+
import { SqlClient, TransactionConnection } from 'effect/sql/SqlClient';
|
|
15
|
+
import { PIN_SEARCH_PATH_SQL } from './search-path.ts';
|
|
15
16
|
import { isDependentObjectsError } from './schema-sql.ts';
|
|
16
17
|
import { classifyInstalled } from './installed-classifier.ts';
|
|
17
18
|
import { PostgresPool, postgresConnection } from './connection.ts';
|
|
@@ -31,6 +32,8 @@ const schemaRead = postgresSchemaHandlers.read;
|
|
|
31
32
|
const pools: Array<{ database?: string | undefined }> = [];
|
|
32
33
|
/** Every SQL text the fake client ran, in order. */
|
|
33
34
|
const queries: string[] = [];
|
|
35
|
+
/** The pool count at each `SET search_path`: one pin per pool, before that pool's statements. */
|
|
36
|
+
const pins: number[] = [];
|
|
34
37
|
/** The router answers for the current test (`schema-test-kit.ts` markers, JSON strings). */
|
|
35
38
|
let answers: RouteAnswers = {};
|
|
36
39
|
|
|
@@ -38,7 +41,15 @@ let answers: RouteAnswers = {};
|
|
|
38
41
|
// callbacks), and `Statement<A>` is an `Effect<ReadonlyArray<A>, SqlError>` — a plain succeed
|
|
39
42
|
// satisfies the same call shape.
|
|
40
43
|
const fakeClient = {
|
|
44
|
+
// `pinnedSocketExecutor` reserves one connection and routes every statement through the
|
|
45
|
+
// client's own `transactionService` — the fake only has to answer both.
|
|
46
|
+
reserve: Effect.succeed({}),
|
|
47
|
+
transactionService: TransactionConnection(0),
|
|
41
48
|
unsafe: (text: string) => {
|
|
49
|
+
if (text === PIN_SEARCH_PATH_SQL) {
|
|
50
|
+
pins.push(pools.length);
|
|
51
|
+
return Effect.succeed([] as never);
|
|
52
|
+
}
|
|
42
53
|
queries.push(text);
|
|
43
54
|
const answer = route(text, answers);
|
|
44
55
|
return Effect.succeed(
|
|
@@ -72,6 +83,7 @@ describe('socket transport: a missing declared database is schema absent', () =>
|
|
|
72
83
|
beforeEach(() => {
|
|
73
84
|
pools.length = 0;
|
|
74
85
|
queries.length = 0;
|
|
86
|
+
pins.length = 0;
|
|
75
87
|
answers = {};
|
|
76
88
|
});
|
|
77
89
|
|
|
@@ -85,8 +97,8 @@ describe('socket transport: a missing declared database is schema absent', () =>
|
|
|
85
97
|
expect(pools.length).toBe(1);
|
|
86
98
|
expect(pools[0]?.database).toBe('postgres');
|
|
87
99
|
expect(queries.length).toBe(1);
|
|
88
|
-
expect(queries[0]).toContain('FROM pg_database');
|
|
89
|
-
expect(queries.some((q) => q.startsWith('
|
|
100
|
+
expect(queries[0]).toContain('FROM pg_catalog.pg_database');
|
|
101
|
+
expect(queries.some((q) => q.startsWith('DO '))).toBe(false);
|
|
90
102
|
});
|
|
91
103
|
|
|
92
104
|
test('read answers absent: the untyped 3D000 connect never happens', async () => {
|
|
@@ -98,7 +110,7 @@ describe('socket transport: a missing declared database is schema absent', () =>
|
|
|
98
110
|
expect(pools.length).toBe(1);
|
|
99
111
|
expect(pools[0]?.database).toBe('postgres');
|
|
100
112
|
expect(queries.length).toBe(1);
|
|
101
|
-
expect(queries[0]).toContain('FROM pg_database');
|
|
113
|
+
expect(queries[0]).toContain('FROM pg_catalog.pg_database');
|
|
102
114
|
});
|
|
103
115
|
|
|
104
116
|
test('the `database` override reaches the pool: probe over the family pool, statements over the declared one', async () => {
|
|
@@ -113,7 +125,9 @@ describe('socket transport: a missing declared database is schema absent', () =>
|
|
|
113
125
|
);
|
|
114
126
|
// The probe opened the family database; every statement after it opened `agents`.
|
|
115
127
|
expect(pools.map((pool) => pool.database)).toEqual(['postgres', 'agents']);
|
|
116
|
-
|
|
128
|
+
// Each pool's reserved connection is pinned before its first statement: pool 1, then pool 2.
|
|
129
|
+
expect(pins).toEqual([1, 2]);
|
|
130
|
+
expect(queries.some((q) => q.startsWith('DO '))).toBe(true);
|
|
117
131
|
});
|
|
118
132
|
|
|
119
133
|
test('read over an existing declared database answers the row from the overridden pool', async () => {
|
|
@@ -147,7 +161,7 @@ describe('socket transport: a missing declared database is schema absent', () =>
|
|
|
147
161
|
);
|
|
148
162
|
expect(error).toBeInstanceOf(PostgresSchemaDeleteForeignRefused);
|
|
149
163
|
expect(error).toMatchObject({ liveOid: 16443, liveOwner: 'other_role', lastOid: 16442 });
|
|
150
|
-
expect(queries.some((q) => q.startsWith('
|
|
164
|
+
expect(queries.some((q) => q.startsWith('DO '))).toBe(false);
|
|
151
165
|
});
|
|
152
166
|
|
|
153
167
|
test('the installed classifier keeps 3D000 as UnknownError with the raw code — the untyped failure the probe prevents', async () => {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* ★ The schema name is a single `ColId` token, so `quoteIdent` (single-token quoting) is
|
|
10
10
|
* correct — same rule as `Postgres.Database`'s name.
|
|
11
11
|
*/
|
|
12
|
-
import type { SqlError } from 'effect/
|
|
12
|
+
import type { SqlError } from 'effect/sql/SqlError';
|
|
13
13
|
import * as Effect from 'effect/Effect';
|
|
14
14
|
import type { PostgresSchemaAttributes, PostgresSchemaProps } from './schema-attrs.ts';
|
|
15
15
|
import { quoteIdent, quoteStringLiteral } from './database-sql.ts';
|
|
@@ -32,10 +32,10 @@ export const buildDropSchemaSql = (name: string, cascade: boolean): string =>
|
|
|
32
32
|
const SELECT_SCHEMA_SQL = `SELECT
|
|
33
33
|
n.oid AS oid,
|
|
34
34
|
n.nspname AS name,
|
|
35
|
-
pg_get_userbyid(n.nspowner) AS owner,
|
|
36
|
-
obj_description(n.oid, 'pg_namespace') AS comment,
|
|
35
|
+
pg_catalog.pg_get_userbyid(n.nspowner) AS owner,
|
|
36
|
+
pg_catalog.obj_description(n.oid, 'pg_namespace') AS comment,
|
|
37
37
|
current_database() AS database
|
|
38
|
-
FROM pg_namespace n
|
|
38
|
+
FROM pg_catalog.pg_namespace n
|
|
39
39
|
WHERE n.nspname = $1`;
|
|
40
40
|
|
|
41
41
|
/** The read: one bound `SELECT` on `pg_namespace`. `undefined` when absent. The row carries
|
|
@@ -84,18 +84,23 @@ export const currentUser = (pg: PgExecutor): Effect.Effect<string, SqlError> =>
|
|
|
84
84
|
*
|
|
85
85
|
* ⚠️ THE FAKE (`fake-schema-sql.ts`) ROUTES THIS STATEMENT BY `AS empty` + `pg_class` — keep both
|
|
86
86
|
* markers in any rewrite, and keep this check matched BEFORE the plain `pg_namespace` branch
|
|
87
|
-
* (this SQL contains `FROM pg_namespace` too, inside its `WITH`).
|
|
87
|
+
* (this SQL contains `FROM pg_catalog.pg_namespace` too, inside its `WITH`).
|
|
88
88
|
*/
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
/** The four-catalog emptiness predicate over a namespace oid expression — shared by this read
|
|
90
|
+
* and by the atomic drop's in-transaction check (`schema-drop-sql.ts`), so the two cannot
|
|
91
|
+
* disagree about what "empty" means. */
|
|
92
|
+
export const emptyPredicate = (ns: string): string => `NOT EXISTS (
|
|
93
|
+
SELECT 1 FROM pg_catalog.pg_class c WHERE c.relnamespace = ${ns}
|
|
92
94
|
) AND NOT EXISTS (
|
|
93
|
-
SELECT 1 FROM pg_proc p WHERE p.pronamespace =
|
|
95
|
+
SELECT 1 FROM pg_catalog.pg_proc p WHERE p.pronamespace = ${ns}
|
|
94
96
|
) AND NOT EXISTS (
|
|
95
|
-
SELECT 1 FROM pg_type t WHERE t.typnamespace =
|
|
97
|
+
SELECT 1 FROM pg_catalog.pg_type t WHERE t.typnamespace = ${ns}
|
|
96
98
|
) AND NOT EXISTS (
|
|
97
|
-
SELECT 1 FROM pg_operator o WHERE o.oprnamespace =
|
|
98
|
-
)
|
|
99
|
+
SELECT 1 FROM pg_catalog.pg_operator o WHERE o.oprnamespace = ${ns}
|
|
100
|
+
)`;
|
|
101
|
+
|
|
102
|
+
const SCHEMA_EMPTY_SQL = `WITH ns AS (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = $1)
|
|
103
|
+
SELECT ${emptyPredicate('(SELECT oid FROM ns)')} AS empty`;
|
|
99
104
|
|
|
100
105
|
/** `true` when the schema holds none of the four object catalogs above (used before a
|
|
101
106
|
* non-cascade `DROP SCHEMA`). */
|
|
@@ -5,6 +5,7 @@ import { postgresSchemaHandlers, reconcileWithClient } from './schema.ts';
|
|
|
5
5
|
import { postgresRunnerConnection } from './connection.ts';
|
|
6
6
|
import { makeFakeSql } from './fake-sql.ts';
|
|
7
7
|
import { readArgs, router, runnerWith } from './schema-test-kit.ts';
|
|
8
|
+
import { stripPin } from './search-path.ts';
|
|
8
9
|
import type { PostgresSchemaAttributes } from './schema-attrs.ts';
|
|
9
10
|
|
|
10
11
|
const props = { name: 'ledger', database: 'postgres', owner: 'postgres', cascade: true };
|
|
@@ -101,21 +102,23 @@ test('same-owner create race is a typed exists refusal, never a comment or adopt
|
|
|
101
102
|
|
|
102
103
|
test('runner 42P06 becomes an adoption refusal; other SQLSTATEs remain SqlError', async () => {
|
|
103
104
|
for (const code of ['42P06', '42501']) {
|
|
104
|
-
const run = ({ stdin }: { stdin: string }) =>
|
|
105
|
-
|
|
105
|
+
const run = ({ stdin: raw }: { stdin: string }) => {
|
|
106
|
+
const stdin = stripPin(raw);
|
|
107
|
+
return Promise.resolve(
|
|
106
108
|
stdin.startsWith('CREATE')
|
|
107
109
|
? { code: 3, stdout: '', stderr: `ERROR: ${code}: refused` }
|
|
108
110
|
: {
|
|
109
111
|
code: 0,
|
|
110
112
|
stdout:
|
|
111
|
-
stdin.includes('current_database()') && !stdin.includes('pg_namespace')
|
|
113
|
+
stdin.includes('current_database()') && !stdin.includes('pg_catalog.pg_namespace')
|
|
112
114
|
? '[{"database":"postgres"}]'
|
|
113
|
-
: stdin.includes('pg_roles')
|
|
115
|
+
: stdin.includes('pg_catalog.pg_roles')
|
|
114
116
|
? '[{"present":1}]'
|
|
115
117
|
: '[]',
|
|
116
118
|
stderr: '',
|
|
117
119
|
},
|
|
118
120
|
);
|
|
121
|
+
};
|
|
119
122
|
const error = await Effect.runPromise(
|
|
120
123
|
postgresSchemaHandlers
|
|
121
124
|
.reconcile(args())
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* branch's marker and matches its own.
|
|
12
12
|
*/
|
|
13
13
|
import type { PsqlRunner } from './psql-executor.ts';
|
|
14
|
+
import { stripPin } from './search-path.ts';
|
|
14
15
|
import type { PostgresSchemaAttributes, PostgresSchemaProps } from './schema-attrs.ts';
|
|
15
16
|
|
|
16
17
|
export const ok = (stdout: string) => Promise.resolve({ code: 0, stdout, stderr: '' });
|
|
@@ -29,9 +30,12 @@ export interface RouteAnswers {
|
|
|
29
30
|
|
|
30
31
|
/** Route one stdin to its answer by the markers above, `undefined` when no branch matches. */
|
|
31
32
|
export const route = (stdin: string, answers: RouteAnswers): string | undefined => {
|
|
33
|
+
// The atomic drop (`schema-drop-sql.ts`) quotes every marker below inside its body: it answers
|
|
34
|
+
// nothing, and a refusal is injected by `runnerWith`'s `failOn`, never routed.
|
|
35
|
+
if (stdin.startsWith('DO ')) return undefined;
|
|
32
36
|
if (stdin.includes('AS empty')) return answers.empty;
|
|
33
|
-
if (stdin.includes('FROM pg_namespace')) return answers.schema;
|
|
34
|
-
if (stdin.includes('FROM pg_database')) return answers.probe;
|
|
37
|
+
if (stdin.includes('FROM pg_catalog.pg_namespace')) return answers.schema;
|
|
38
|
+
if (stdin.includes('FROM pg_catalog.pg_database')) return answers.probe;
|
|
35
39
|
if (stdin.includes('current_database()')) return answers.proof;
|
|
36
40
|
return undefined;
|
|
37
41
|
};
|
|
@@ -46,14 +50,24 @@ export const router = (answers: RouteAnswers) => (stdin: string) => route(stdin,
|
|
|
46
50
|
*/
|
|
47
51
|
export const runnerWith = (
|
|
48
52
|
answers: (stdin: string) => string,
|
|
53
|
+
/** The `psql` stderr a statement fails with (exit 3), or `undefined` to answer normally. */
|
|
54
|
+
failOn: (stdin: string) => string | undefined = () => undefined,
|
|
49
55
|
): { run: PsqlRunner; stdins: string[]; argvs: Array<readonly string[]> } => {
|
|
50
56
|
const stdins: string[] = [];
|
|
51
57
|
const argvs: Array<readonly string[]> = [];
|
|
52
58
|
return {
|
|
53
|
-
|
|
59
|
+
// The pin prefix (`search-path.ts`) is stripped: these tests assert the STATEMENT. The pin
|
|
60
|
+
// itself is asserted once, on the raw stdin, in `search-path.test.ts`.
|
|
61
|
+
run: ({ stdin: raw, argv }) => {
|
|
62
|
+
const stdin = stripPin(raw);
|
|
54
63
|
stdins.push(stdin);
|
|
55
64
|
argvs.push(argv);
|
|
56
|
-
|
|
65
|
+
const stderr = failOn(stdin);
|
|
66
|
+
return Promise.resolve(
|
|
67
|
+
stderr === undefined
|
|
68
|
+
? { code: 0, stdout: answers(stdin), stderr: '' }
|
|
69
|
+
: { code: 3, stdout: '', stderr },
|
|
70
|
+
);
|
|
57
71
|
},
|
|
58
72
|
stdins,
|
|
59
73
|
argvs,
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `search_path` pin (`search-path.ts`): every session this family opens starts with
|
|
3
|
+
* `SET search_path = pg_catalog, pg_temp`, on both transports, and the SQL builders only name
|
|
4
|
+
* catalogs as `pg_catalog.*`. The Opus read of PR 334 (2026-10-02) found that a role allowed to
|
|
5
|
+
* create objects in a schema on the path could forge `pg_namespace` and fake the delete proof.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
8
|
+
import { describe, expect, test } from 'bun:test';
|
|
9
|
+
import * as Effect from 'effect/Effect';
|
|
10
|
+
import { currentDatabase } from './schema-sql.ts';
|
|
11
|
+
import { makePsqlExecutor } from './psql-executor.ts';
|
|
12
|
+
import { PIN_SCRIPT_PREFIX, PIN_SEARCH_PATH_SQL } from './search-path.ts';
|
|
13
|
+
|
|
14
|
+
describe('runner transport', () => {
|
|
15
|
+
const target = { database: 'postgres', username: 'postgres' };
|
|
16
|
+
const record = () => {
|
|
17
|
+
const stdins: string[] = [];
|
|
18
|
+
const run = ({ stdin }: { stdin: string }) => {
|
|
19
|
+
stdins.push(stdin);
|
|
20
|
+
return Promise.resolve({ code: 0, stdout: '[]', stderr: '' });
|
|
21
|
+
};
|
|
22
|
+
return { stdins, run };
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
test('a read starts with the pin, in the same psql session as the statement', async () => {
|
|
26
|
+
const { stdins, run } = record();
|
|
27
|
+
await Effect.runPromise(currentDatabase(makePsqlExecutor(run, target)));
|
|
28
|
+
expect(stdins).toHaveLength(1);
|
|
29
|
+
expect(stdins[0]?.startsWith('SET search_path = pg_catalog, pg_temp;\n')).toBe(true);
|
|
30
|
+
expect(stdins[0]?.startsWith(PIN_SCRIPT_PREFIX)).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('a write and a transaction start with the pin too', async () => {
|
|
34
|
+
const { stdins, run } = record();
|
|
35
|
+
const pg = makePsqlExecutor(run, target);
|
|
36
|
+
await Effect.runPromise(pg.unsafe('COMMENT ON SCHEMA "a" IS E\'b\''));
|
|
37
|
+
await Effect.runPromise(pg.transaction(['CREATE ROLE "r"']));
|
|
38
|
+
expect(stdins).toHaveLength(2);
|
|
39
|
+
for (const stdin of stdins) expect(stdin.startsWith(PIN_SCRIPT_PREFIX)).toBe(true);
|
|
40
|
+
// The pin comes BEFORE `BEGIN`: it is a session setting, not part of the transaction.
|
|
41
|
+
expect(stdins[1]).toBe(`${PIN_SCRIPT_PREFIX}BEGIN;\nCREATE ROLE "r";\nCOMMIT;`);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('the pin is the one statement text both transports share', () => {
|
|
45
|
+
expect(PIN_SEARCH_PATH_SQL).toBe('SET search_path = pg_catalog, pg_temp');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
/** Every hand-written SQL builder in this family. The `*errors.ts` files are excluded: they name
|
|
50
|
+
* catalogs in error-message PROSE, which no server ever parses. The fakes match on the qualified
|
|
51
|
+
* text the builders emit, so their own tests pin them. */
|
|
52
|
+
const sources = readdirSync(import.meta.dir).filter(
|
|
53
|
+
(f) =>
|
|
54
|
+
f.endsWith('.ts') &&
|
|
55
|
+
!f.endsWith('.test.ts') &&
|
|
56
|
+
!f.startsWith('fake-') &&
|
|
57
|
+
!/errors?\.ts$/.test(f),
|
|
58
|
+
);
|
|
59
|
+
const CATALOGS =
|
|
60
|
+
/(?<![\w.])(pg_(?:namespace|class|proc|type|operator|roles|authid|auth_members|database|tablespace|attribute|default_acl|depend|constraint|trigger|rewrite|attrdef|policy))\b/g;
|
|
61
|
+
|
|
62
|
+
describe('catalog references', () => {
|
|
63
|
+
test('no SQL builder names a catalog without the pg_catalog. qualifier', () => {
|
|
64
|
+
const offenders: string[] = [];
|
|
65
|
+
for (const file of sources) {
|
|
66
|
+
const text = readFileSync(`${import.meta.dir}/${file}`, 'utf8');
|
|
67
|
+
// Only template/quoted SQL is judged: skip comment lines and `'pg_namespace'` literals
|
|
68
|
+
// (an `obj_description` class argument is a string, not a relation reference).
|
|
69
|
+
for (const [index, line] of text.split('\n').entries()) {
|
|
70
|
+
const trimmed = line.trim();
|
|
71
|
+
if (/^(\/\/|\*|\/\*)/.test(trimmed)) continue;
|
|
72
|
+
for (const match of line.matchAll(CATALOGS)) {
|
|
73
|
+
const before = line[(match.index ?? 0) - 1];
|
|
74
|
+
if (before === "'" || before === '`') continue;
|
|
75
|
+
offenders.push(`${file}:${String(index + 1)} ${match[1] ?? ''}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
expect(offenders).toEqual([]);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('the scan sees the catalogs it is meant to police', () => {
|
|
83
|
+
const joined = sources.map((f) => readFileSync(`${import.meta.dir}/${f}`, 'utf8')).join('\n');
|
|
84
|
+
for (const needle of [
|
|
85
|
+
'pg_catalog.pg_namespace',
|
|
86
|
+
'pg_catalog.pg_class',
|
|
87
|
+
'pg_catalog.pg_depend',
|
|
88
|
+
'pg_catalog.pg_roles',
|
|
89
|
+
'pg_catalog.pg_database',
|
|
90
|
+
]) {
|
|
91
|
+
expect(joined).toContain(needle);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('the scan itself fails on an unqualified reference', () => {
|
|
96
|
+
const bad = ' FROM pg_namespace n\n JOIN pg_catalog.pg_class c ON true';
|
|
97
|
+
const hits = [...bad.matchAll(CATALOGS)].map((m) => m[1]);
|
|
98
|
+
expect(hits).toEqual(['pg_namespace']);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The session setup every `Postgres.*` connection runs first: `search_path = pg_catalog, pg_temp`.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ AN UNPINNED SESSION READS ITS OWN CATALOGS THROUGH `search_path`. Postgres searches
|
|
5
|
+
* `pg_catalog` first only while the path does not NAME it (ddl.sgml 5.10.3); a role- or
|
|
6
|
+
* database-level `ALTER … SET search_path = public, pg_catalog` (or a path naming any schema
|
|
7
|
+
* another role may create in) puts a writable schema ahead of it, and a `public.pg_namespace`
|
|
8
|
+
* TABLE then answers the delete proof's `oid`/`owner` re-read. Found by the Opus read of PR 334
|
|
9
|
+
* (2026-10-02). The pin makes the order ours whatever the role, database or server says; the SQL
|
|
10
|
+
* builders ALSO write `pg_catalog.<name>` (`catalog-qualified.test.ts` greps for it), so no
|
|
11
|
+
* single layer is the whole defence.
|
|
12
|
+
* ★ `pg_temp` goes LAST on purpose: unnamed, it is searched FIRST for relations.
|
|
13
|
+
* ★ TWO TRANSPORTS, TWO MECHANISMS. The runner path prefixes the stdin script: `psql` argv is
|
|
14
|
+
* the consumer's to join into a remote command, and a value with a space would split. The
|
|
15
|
+
* socket path pins the ONE connection an operation reserves, because the driver's startup
|
|
16
|
+
* message carries user, database and application_name only (`PgConnection.ts`, 4.0.0-rc.115)
|
|
17
|
+
* and a pool hands each statement a different, unpinned connection.
|
|
18
|
+
*/
|
|
19
|
+
import type { PgClient } from '@effect/sql-pg/PgClient';
|
|
20
|
+
import * as Effect from 'effect/Effect';
|
|
21
|
+
import type * as Scope from 'effect/Scope';
|
|
22
|
+
import type { SqlError } from 'effect/sql/SqlError';
|
|
23
|
+
import type { PgExecutor } from './database-sql.ts';
|
|
24
|
+
|
|
25
|
+
export const PIN_SEARCH_PATH_SQL = 'SET search_path = pg_catalog, pg_temp';
|
|
26
|
+
|
|
27
|
+
/** The runner path's script prefix. */
|
|
28
|
+
export const PIN_SCRIPT_PREFIX = `${PIN_SEARCH_PATH_SQL};\n`;
|
|
29
|
+
|
|
30
|
+
/** A runner stdin without its pin — for tests that assert the statement that follows it. */
|
|
31
|
+
export const stripPin = (stdin: string): string =>
|
|
32
|
+
stdin.startsWith(PIN_SCRIPT_PREFIX) ? stdin.slice(PIN_SCRIPT_PREFIX.length) : stdin;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A `PgExecutor` over ONE reserved, pinned connection, for the life of the surrounding scope.
|
|
36
|
+
* `unsafe` runs on it (the client routes any statement under `transactionService` to that
|
|
37
|
+
* connection, so spans and error classification stay the driver's); `transaction` is a plain
|
|
38
|
+
* `BEGIN` … `COMMIT` on it — `withTransaction` would open a SAVEPOINT, because the service is
|
|
39
|
+
* already provided. A failure rolls back, and the pin is re-asserted by the next operation's own
|
|
40
|
+
* reservation, never assumed to survive one.
|
|
41
|
+
*/
|
|
42
|
+
export const pinnedSocketExecutor = (
|
|
43
|
+
pg: PgClient,
|
|
44
|
+
): Effect.Effect<PgExecutor, SqlError, Scope.Scope> =>
|
|
45
|
+
Effect.gen(function* () {
|
|
46
|
+
const connection = yield* pg.reserve;
|
|
47
|
+
const run = <A extends object>(sql: string, params?: ReadonlyArray<unknown>) =>
|
|
48
|
+
Effect.provideService(pg.unsafe<A>(sql, params), pg.transactionService, [connection, 0]);
|
|
49
|
+
yield* run(PIN_SEARCH_PATH_SQL);
|
|
50
|
+
return {
|
|
51
|
+
unsafe: run,
|
|
52
|
+
transaction: (statements) =>
|
|
53
|
+
Effect.gen(function* () {
|
|
54
|
+
yield* run('BEGIN');
|
|
55
|
+
for (const sql of statements) yield* run(sql);
|
|
56
|
+
yield* run('COMMIT');
|
|
57
|
+
}).pipe(
|
|
58
|
+
Effect.onError(() => run('ROLLBACK').pipe(Effect.ignore)),
|
|
59
|
+
Effect.asVoid,
|
|
60
|
+
),
|
|
61
|
+
};
|
|
62
|
+
});
|