@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
|
@@ -11,12 +11,13 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { describe, expect, test } from 'bun:test';
|
|
13
13
|
import * as Effect from 'effect/Effect';
|
|
14
|
-
import type { SqlError } from 'effect/
|
|
14
|
+
import type { SqlError } from 'effect/sql/SqlError';
|
|
15
15
|
import type { PgExecutor } from './database-sql.ts';
|
|
16
16
|
import { classifyInstalled } from './installed-classifier.ts';
|
|
17
17
|
import { postgresRunnerConnection } from './connection.ts';
|
|
18
18
|
import { type PsqlRunner, makePsqlExecutor } from './psql-executor.ts';
|
|
19
19
|
import { isDependentObjectsError } from './schema-sql.ts';
|
|
20
|
+
import { stripPin } from './search-path.ts';
|
|
20
21
|
import { PostgresSchemaDropNotEmptyError } from './schema-errors.ts';
|
|
21
22
|
import { dropWithClient, postgresSchemaHandlers } from './schema.ts';
|
|
22
23
|
import type { PostgresSchemaAttributes, PostgresSchemaProps } from './schema-attrs.ts';
|
|
@@ -25,16 +26,16 @@ const baseProps: PostgresSchemaProps = { name: 'ledger', database: 'postgres', o
|
|
|
25
26
|
|
|
26
27
|
const socket2bp01 = (): Promise<SqlError> => classifyInstalled('2BP01');
|
|
27
28
|
|
|
28
|
-
/**
|
|
29
|
+
/** The live row is ours; the atomic `DO` drop is the server's real `2BP01`. */
|
|
29
30
|
const emptyThenRefuse = (drop: Effect.Effect<ReadonlyArray<object>, SqlError>): PgExecutor => ({
|
|
30
31
|
unsafe: <A extends object>(text: string) => {
|
|
31
|
-
if (text.startsWith('
|
|
32
|
-
|
|
32
|
+
if (text.startsWith('DO ')) return drop as Effect.Effect<ReadonlyArray<A>, SqlError>;
|
|
33
|
+
if (text.includes('FROM pg_catalog.pg_namespace')) {
|
|
34
|
+
return Effect.succeed([
|
|
35
|
+
{ name: 'ledger', oid: 1, owner: 'tim', comment: null, database: 'postgres' },
|
|
36
|
+
] as unknown as ReadonlyArray<A>);
|
|
33
37
|
}
|
|
34
|
-
|
|
35
|
-
return Effect.succeed([{ empty: true }] as unknown as ReadonlyArray<A>);
|
|
36
|
-
}
|
|
37
|
-
return drop as Effect.Effect<ReadonlyArray<A>, SqlError>;
|
|
38
|
+
return Effect.succeed([{ database: 'postgres' }] as unknown as ReadonlyArray<A>);
|
|
38
39
|
},
|
|
39
40
|
transaction: () => Effect.void,
|
|
40
41
|
});
|
|
@@ -54,12 +55,18 @@ describe('2BP01 classification', () => {
|
|
|
54
55
|
});
|
|
55
56
|
|
|
56
57
|
test('the runner transport classifies a verbose 2BP01 line as the typed not-empty tag', async () => {
|
|
57
|
-
const run: PsqlRunner = ({ stdin }) => {
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
const run: PsqlRunner = ({ stdin: raw }) => {
|
|
59
|
+
const stdin = stripPin(raw);
|
|
60
|
+
if (!stdin.startsWith('DO ') && stdin.includes('FROM pg_catalog.pg_namespace')) {
|
|
61
|
+
return Promise.resolve({
|
|
62
|
+
code: 0,
|
|
63
|
+
stdout:
|
|
64
|
+
'[{"name":"ledger","oid":"1","owner":"tim","comment":null,"database":"postgres"}]',
|
|
65
|
+
stderr: '',
|
|
66
|
+
});
|
|
60
67
|
}
|
|
61
|
-
if (stdin.
|
|
62
|
-
return Promise.resolve({ code: 0, stdout: '[{"
|
|
68
|
+
if (!stdin.startsWith('DO ') && stdin.includes('current_database()')) {
|
|
69
|
+
return Promise.resolve({ code: 0, stdout: '[{"database":"postgres"}]', stderr: '' });
|
|
63
70
|
}
|
|
64
71
|
return Promise.resolve({
|
|
65
72
|
code: 3,
|
|
@@ -92,18 +99,23 @@ describe('2BP01 classification', () => {
|
|
|
92
99
|
// Order is load-bearing (schema-handlers.test.ts#route): the probe must answer PRESENT so
|
|
93
100
|
// the absent-database short-circuit does not swallow the test, and the ownership re-read
|
|
94
101
|
// must answer the row the persisted output vouches for, so the delete reaches the DROP.
|
|
95
|
-
const run: PsqlRunner = ({ stdin }) => {
|
|
96
|
-
|
|
97
|
-
|
|
102
|
+
const run: PsqlRunner = ({ stdin: raw }) => {
|
|
103
|
+
const stdin = stripPin(raw);
|
|
104
|
+
if (stdin.startsWith('DO ')) {
|
|
105
|
+
return Promise.resolve({
|
|
106
|
+
code: 3,
|
|
107
|
+
stdout: '',
|
|
108
|
+
stderr: 'ERROR: 2BP01: dependent objects still exist\n',
|
|
109
|
+
});
|
|
98
110
|
}
|
|
99
|
-
if (stdin.includes('FROM pg_namespace')) {
|
|
111
|
+
if (stdin.includes('FROM pg_catalog.pg_namespace')) {
|
|
100
112
|
return Promise.resolve({
|
|
101
113
|
code: 0,
|
|
102
114
|
stdout: '[{"name":"ledger","oid":1,"owner":"tim"}]',
|
|
103
115
|
stderr: '',
|
|
104
116
|
});
|
|
105
117
|
}
|
|
106
|
-
if (stdin.includes('FROM pg_database')) {
|
|
118
|
+
if (stdin.includes('FROM pg_catalog.pg_database')) {
|
|
107
119
|
return Promise.resolve({ code: 0, stdout: '[{"present":1}]', stderr: '' });
|
|
108
120
|
}
|
|
109
121
|
if (stdin.includes('current_database()')) {
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The atomic schema drop against a REAL PostgreSQL. The in-memory fake substitutes a COUNT for the
|
|
3
|
+
* dependency scan and cannot lock, so it cannot see the three defects Codex found in PR 357:
|
|
4
|
+
* an advisory lock that DDL ignores, a closure that missed `pg_collation`, and a TOAST table
|
|
5
|
+
* counted as foreign.
|
|
6
|
+
*
|
|
7
|
+
* ⛔ SKIPPED LOUDLY WITHOUT A SERVER. Set `HF_TEST_POSTGRES=1` and the libpq variables
|
|
8
|
+
* (`PGHOST`, `PGUSER`, `PGDATABASE`, `PGPASSWORD` or a socket); the kit has no container
|
|
9
|
+
* harness of its own (measured 2026-10-06: no podman/initdb use in `packages/alchemy` tests).
|
|
10
|
+
* The connected role needs `CREATE` on the database. Every schema is uniquely named and dropped
|
|
11
|
+
* on every exit path; nothing else is touched.
|
|
12
|
+
*/
|
|
13
|
+
import { afterAll, describe, expect, test } from 'bun:test';
|
|
14
|
+
import * as Effect from 'effect/Effect';
|
|
15
|
+
import { type PsqlRunner, makePsqlExecutor } from './psql-executor.ts';
|
|
16
|
+
import { PostgresSchemaCascadeCrossSchemaRefused } from './schema-errors.ts';
|
|
17
|
+
import { PostgresSchemaCascadeSequencesRefused } from './schema-sequence-error.ts';
|
|
18
|
+
import { dropWithClient } from './schema.ts';
|
|
19
|
+
|
|
20
|
+
const enabled = process.env['HF_TEST_POSTGRES'] === '1';
|
|
21
|
+
if (!enabled) {
|
|
22
|
+
console.warn(
|
|
23
|
+
'SKIPPED schema-drop-real.test.ts: no PostgreSQL. Set HF_TEST_POSTGRES=1 (+ PG* env) to run it.',
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
const suite = enabled ? describe : describe.skip;
|
|
27
|
+
|
|
28
|
+
const database = process.env['PGDATABASE'] ?? 'postgres';
|
|
29
|
+
const username = process.env['PGUSER'] ?? 'postgres';
|
|
30
|
+
|
|
31
|
+
const spawnPsql = async (argv: readonly string[], stdin: string) => {
|
|
32
|
+
const proc = Bun.spawn([...argv], {
|
|
33
|
+
stdin: new TextEncoder().encode(stdin),
|
|
34
|
+
stdout: 'pipe',
|
|
35
|
+
stderr: 'pipe',
|
|
36
|
+
});
|
|
37
|
+
const [stdout, stderr, code] = await Promise.all([
|
|
38
|
+
new Response(proc.stdout).text(),
|
|
39
|
+
new Response(proc.stderr).text(),
|
|
40
|
+
proc.exited,
|
|
41
|
+
]);
|
|
42
|
+
return { code, stdout, stderr };
|
|
43
|
+
};
|
|
44
|
+
const runner: PsqlRunner = ({ argv, stdin }) => spawnPsql(argv, stdin);
|
|
45
|
+
const pg = makePsqlExecutor(runner, { database, username });
|
|
46
|
+
const psql = (sql: string) =>
|
|
47
|
+
spawnPsql(
|
|
48
|
+
['psql', '-X', '-q', '-A', '-t', '-v', 'ON_ERROR_STOP=1', '-U', username, '-d', database],
|
|
49
|
+
sql,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const stamp = `${Date.now().toString(36)}${Math.floor(Math.random() * 1e4)}`;
|
|
53
|
+
const names: string[] = [];
|
|
54
|
+
const fresh = (label: string): string => {
|
|
55
|
+
const name = `hf_${label}_${stamp}`;
|
|
56
|
+
names.push(name);
|
|
57
|
+
return name;
|
|
58
|
+
};
|
|
59
|
+
afterAll(async () => {
|
|
60
|
+
if (!enabled) return;
|
|
61
|
+
for (const name of names) await psql(`DROP SCHEMA IF EXISTS "${name}" CASCADE;`);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const present = async (name: string): Promise<boolean> =>
|
|
65
|
+
(await psql(`SELECT count(*) FROM pg_namespace WHERE nspname = '${name}';`)).stdout.trim() ===
|
|
66
|
+
'1';
|
|
67
|
+
const props = (name: string) => ({ name, database, cascade: true });
|
|
68
|
+
|
|
69
|
+
suite('atomic drop on a real PostgreSQL', () => {
|
|
70
|
+
test("an isolated text table cascades: its TOAST relation is the table's", async () => {
|
|
71
|
+
const a = fresh('toast');
|
|
72
|
+
await psql(`CREATE SCHEMA "${a}"; CREATE TABLE "${a}".t (x text);`);
|
|
73
|
+
await Effect.runPromise(dropWithClient(pg, props(a)));
|
|
74
|
+
expect(await present(a)).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("a collation used by another schema's table refuses the cascade", async () => {
|
|
78
|
+
const a = fresh('coll_a');
|
|
79
|
+
const b = fresh('coll_b');
|
|
80
|
+
await psql(
|
|
81
|
+
`CREATE SCHEMA "${a}"; CREATE COLLATION "${a}".c (provider = libc, locale = 'C');
|
|
82
|
+
CREATE SCHEMA "${b}"; CREATE TABLE "${b}".t (x text COLLATE "${a}".c);
|
|
83
|
+
INSERT INTO "${b}".t VALUES ('kept');`,
|
|
84
|
+
);
|
|
85
|
+
const error = await Effect.runPromise(Effect.flip(dropWithClient(pg, props(a))));
|
|
86
|
+
expect(error).toBeInstanceOf(PostgresSchemaCascadeCrossSchemaRefused);
|
|
87
|
+
expect(await present(a)).toBe(true);
|
|
88
|
+
expect((await psql(`SELECT x FROM "${b}".t;`)).stdout.trim()).toBe('kept');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('a replacement attempted while the drop holds its locks waits and is refused', async () => {
|
|
92
|
+
const a = fresh('race');
|
|
93
|
+
await psql(`CREATE SCHEMA "${a}"; CREATE TABLE "${a}".t (x text);`);
|
|
94
|
+
// Holder: an open transaction reading the table keeps our LOCK TABLE waiting AFTER the
|
|
95
|
+
// schema-object lock is taken — a deterministic pause between lock and drop.
|
|
96
|
+
const holder = spawnPsql(
|
|
97
|
+
['psql', '-X', '-q', '-v', 'ON_ERROR_STOP=1', '-U', username, '-d', database],
|
|
98
|
+
`BEGIN; SELECT 1 FROM "${a}".t; SELECT pg_sleep(4); COMMIT;`,
|
|
99
|
+
);
|
|
100
|
+
await Bun.sleep(700);
|
|
101
|
+
const drop = Effect.runPromise(dropWithClient(pg, props(a)));
|
|
102
|
+
// ⛔ MATCH THE WAIT BY ITS BLOCKER, NOT BY QUERY TEXT: `pg_stat_activity.query` is cut at
|
|
103
|
+
// `track_activity_query_size` (1024 bytes by default) and the `DO` body's `LOCK TABLE`
|
|
104
|
+
// sits past it, so a text match never fired and the rival ran after the drop had finished
|
|
105
|
+
// (measured on PostgreSQL 18, 2026-10-06). A backend blocked by another one is the drop.
|
|
106
|
+
let parked = false;
|
|
107
|
+
for (let i = 0; i < 30 && !parked; i += 1) {
|
|
108
|
+
const waiting = await psql(
|
|
109
|
+
`SELECT count(*) FROM pg_stat_activity WHERE wait_event_type = 'Lock' AND cardinality(pg_catalog.pg_blocking_pids(pid)) > 0 AND query LIKE 'DO %' AND pid <> pg_backend_pid();`,
|
|
110
|
+
);
|
|
111
|
+
parked = waiting.stdout.trim() !== '0';
|
|
112
|
+
if (!parked) await Bun.sleep(100);
|
|
113
|
+
}
|
|
114
|
+
expect(parked).toBe(true);
|
|
115
|
+
// The drop is now parked holding the namespace lock. A competing DROP + CREATE must not get in.
|
|
116
|
+
const rival = await psql(
|
|
117
|
+
`SET lock_timeout = '800ms'; BEGIN; DROP SCHEMA "${a}" CASCADE; CREATE SCHEMA "${a}"; COMMIT;`,
|
|
118
|
+
);
|
|
119
|
+
expect(rival.code).not.toBe(0);
|
|
120
|
+
expect(rival.stderr).toContain('lock timeout');
|
|
121
|
+
await holder;
|
|
122
|
+
await drop;
|
|
123
|
+
expect(await present(a)).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('a cascade of a schema holding a sequence refuses and loses nothing', async () => {
|
|
127
|
+
const a = fresh('seq_a');
|
|
128
|
+
const b = fresh('seq_b');
|
|
129
|
+
await psql(
|
|
130
|
+
`CREATE SCHEMA "${a}"; CREATE TABLE "${a}".t (id serial, x text);
|
|
131
|
+
INSERT INTO "${a}".t (x) VALUES ('kept');
|
|
132
|
+
CREATE SCHEMA "${b}"; CREATE VIEW "${b}".v AS SELECT last_value FROM "${a}".t_id_seq;`,
|
|
133
|
+
);
|
|
134
|
+
const error = await Effect.runPromise(Effect.flip(dropWithClient(pg, props(a))));
|
|
135
|
+
expect(error).toBeInstanceOf(PostgresSchemaCascadeSequencesRefused);
|
|
136
|
+
expect(error).toMatchObject({ schema: a, sequences: 1 });
|
|
137
|
+
expect(await present(a)).toBe(true);
|
|
138
|
+
expect((await psql(`SELECT x FROM "${a}".t;`)).stdout.trim()).toBe('kept');
|
|
139
|
+
expect((await psql(`SELECT count(*) FROM "${b}".v;`)).stdout.trim()).toBe('1');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('a table added to a foreign extension refuses the cascade (ownership edge leaves)', async () => {
|
|
143
|
+
const a = fresh('ext_a');
|
|
144
|
+
const b = fresh('ext_b');
|
|
145
|
+
await psql(
|
|
146
|
+
`CREATE SCHEMA "${b}"; CREATE EXTENSION hstore SCHEMA "${b}";
|
|
147
|
+
CREATE SCHEMA "${a}"; CREATE TABLE "${a}".t (id int);
|
|
148
|
+
ALTER EXTENSION hstore ADD TABLE "${a}".t;`,
|
|
149
|
+
);
|
|
150
|
+
try {
|
|
151
|
+
const error = await Effect.runPromise(Effect.flip(dropWithClient(pg, props(a))));
|
|
152
|
+
expect(error).toBeInstanceOf(PostgresSchemaCascadeCrossSchemaRefused);
|
|
153
|
+
expect(await present(a)).toBe(true);
|
|
154
|
+
const kept = await psql(`SELECT count(*) FROM pg_extension WHERE extname = 'hstore';`);
|
|
155
|
+
expect(kept.stdout.trim()).toBe('1');
|
|
156
|
+
} finally {
|
|
157
|
+
await psql(`ALTER EXTENSION hstore DROP TABLE "${a}".t; DROP EXTENSION IF EXISTS hstore;`);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('a rename-then-recreate while the drop is parked is refused, the replacement survives', async () => {
|
|
162
|
+
const a = fresh('ren');
|
|
163
|
+
const saved = fresh('ren_saved');
|
|
164
|
+
await psql(`CREATE SCHEMA "${a}"; CREATE TABLE "${a}".t (x text);`);
|
|
165
|
+
const holder = spawnPsql(
|
|
166
|
+
['psql', '-X', '-q', '-v', 'ON_ERROR_STOP=1', '-U', username, '-d', database],
|
|
167
|
+
`BEGIN; SELECT 1 FROM "${a}".t; SELECT pg_sleep(4); COMMIT;`,
|
|
168
|
+
);
|
|
169
|
+
await Bun.sleep(700);
|
|
170
|
+
const drop = Effect.runPromise(Effect.flip(dropWithClient(pg, props(a))));
|
|
171
|
+
let parked = false;
|
|
172
|
+
for (let i = 0; i < 30 && !parked; i += 1) {
|
|
173
|
+
const waiting = await psql(
|
|
174
|
+
`SELECT count(*) FROM pg_stat_activity WHERE wait_event_type = 'Lock' AND cardinality(pg_catalog.pg_blocking_pids(pid)) > 0 AND query LIKE 'DO %' AND pid <> pg_backend_pid();`,
|
|
175
|
+
);
|
|
176
|
+
parked = waiting.stdout.trim() !== '0';
|
|
177
|
+
if (!parked) await Bun.sleep(100);
|
|
178
|
+
}
|
|
179
|
+
expect(parked).toBe(true);
|
|
180
|
+
// RENAME takes no namespace object lock, so it commits while the drop waits.
|
|
181
|
+
const swap = await psql(
|
|
182
|
+
`ALTER SCHEMA "${a}" RENAME TO "${saved}"; CREATE SCHEMA "${a}"; CREATE TABLE "${a}".keep (id int);`,
|
|
183
|
+
);
|
|
184
|
+
expect(swap.code).toBe(0);
|
|
185
|
+
await holder;
|
|
186
|
+
await drop;
|
|
187
|
+
expect((await psql(`SELECT count(*) FROM "${a}".keep;`)).stdout.trim()).toBe('0');
|
|
188
|
+
expect(await present(saved)).toBe(true);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The atomic `DROP SCHEMA` `Postgres.Schema` issues: ONE `DO` statement that takes a lock,
|
|
3
|
+
* re-proves the schema inside the same transaction, refuses what must not be dropped, and only
|
|
4
|
+
* then drops.
|
|
5
|
+
*
|
|
6
|
+
* ⛔ PROOF AND DROP ARE ONE STATEMENT, ONE TRANSACTION. Before 2026-10-02 the re-read, the
|
|
7
|
+
* emptiness check and the `DROP` were three autocommit statements — three `psql` processes on
|
|
8
|
+
* the runner transport — so an out-of-band drop-and-recreate between them was dropped by name,
|
|
9
|
+
* `CASCADE` included (Opus read of PR 334). A `DO` block is atomic on both transports and needs
|
|
10
|
+
* no `BEGIN`/`COMMIT` the runner would have to thread through.
|
|
11
|
+
* ⛔ THE LOCK MUST CONFLICT WITH DDL, NOT ONLY WITH OTHER RUNS OF THIS PROVIDER. An advisory lock
|
|
12
|
+
* (`pg_advisory_xact_lock`) is cooperative: an ordinary `DROP SCHEMA` + `CREATE SCHEMA` ignores
|
|
13
|
+
* it, and the by-name `DROP` below would then drop the REPLACEMENT, `CASCADE` included (Codex
|
|
14
|
+
* read of PR 357, 2026-10-06). So, BEFORE the re-verify and held to the end of the transaction:
|
|
15
|
+
* (1) `COMMENT ON SCHEMA <name> IS <its current comment>` — `CommentObject` resolves the schema
|
|
16
|
+
* through `get_object_address(…, ShareUpdateExclusiveLock)`, which locks the namespace OBJECT
|
|
17
|
+
* (`LockDatabaseObject`), and `DROP SCHEMA` takes `AccessExclusiveLock` on that same object
|
|
18
|
+
* (`AcquireDeletionLock`), so a concurrent drop waits for us; (2) on a cascade, `LOCK TABLE …
|
|
19
|
+
* IN ACCESS EXCLUSIVE MODE` on every table-like relation in the schema, which holds off a
|
|
20
|
+
* concurrent creator of a dependent (a view or foreign key reads the table it references).
|
|
21
|
+
* Lock modes: PostgreSQL docs, explicit-locking.html §13.3; `COMMENT`: sql-comment.html.
|
|
22
|
+
* `lock_timeout` turns any wait into `55P03`, an unclassified error: the drop is REFUSED, never
|
|
23
|
+
* forced. A comment-less schema is rewritten as `IS NULL`, which still takes the lock.
|
|
24
|
+
* ⛔ RENAME TAKES NO SUCH LOCK (Codex read of PR 357, 2026-10-06): `ALTER SCHEMA … RENAME` updates
|
|
25
|
+
* `pg_namespace` without locking the object, so a rename-then-`CREATE SCHEMA` of the same name
|
|
26
|
+
* committed while we waited on a table lock would make the by-name `DROP` delete the
|
|
27
|
+
* REPLACEMENT. The last statement before the `DROP` re-reads the name of the verified OID and
|
|
28
|
+
* refuses `HF001` if it moved; only the statements between that read and the `DROP` remain.
|
|
29
|
+
* ⛔ SEQUENCES REFUSE THE CASCADE (`HF004`, KNOWN LIMIT; `schema-sequence-error.ts`). `LOCK TABLE`
|
|
30
|
+
* cannot lock a sequence, so an uncommitted `CREATE VIEW b.v AS SELECT last_value FROM a.s` is
|
|
31
|
+
* invisible to the closure scan, and PostgreSQL's deletion (locks first, THEN scans dependents)
|
|
32
|
+
* would cascade into `b.v` once it commits. A schema with serial or identity columns therefore
|
|
33
|
+
* cannot be cascade-dropped here: drop its contents first. The check runs before any table lock.
|
|
34
|
+
* ⚠️ NOT COVERED: a type,
|
|
35
|
+
* function or collation created in ANOTHER schema to use ours has no relation to wait on. The
|
|
36
|
+
* advisory lock stays: it serialises runs of this provider, which is cheap and correct.
|
|
37
|
+
* ★ THE VALUES ARE LITERALS INSIDE THE BODY, AND THE BODY IS ONE `E'…'` LITERAL. A `DO` body
|
|
38
|
+
* takes no bind parameters, and a dollar-quote tag could be forged by a schema NAME containing
|
|
39
|
+
* it; `quoteStringLiteral` doubles backslashes and quotes, so nesting it twice is exact.
|
|
40
|
+
* The `DROP` text inside is `buildDropSchemaSql` run through `EXECUTE`, so the quoting rule is
|
|
41
|
+
* the same single one.
|
|
42
|
+
* ★ REFUSALS ARE CUSTOM SQLSTATEs, read from `reason.cause.code` exactly like `2BP01`
|
|
43
|
+
* (`schema-sql.ts#isDependentObjectsError`): `HF001` identity changed, `HF002` not empty,
|
|
44
|
+
* `HF003` dependents in another schema, `HF004` a cascade over a schema holding a sequence. Class `HF` is not `42`, so both transports wrap it as
|
|
45
|
+
* `UnknownError` with the raw code. The `HF003` message carries a COUNT only: another owner's
|
|
46
|
+
* object names must not reach this stack's logs.
|
|
47
|
+
*/
|
|
48
|
+
import type { SqlError } from 'effect/sql/SqlError';
|
|
49
|
+
import { quoteStringLiteral } from './database-sql.ts';
|
|
50
|
+
import { OUTSIDE_DEPENDENTS_SQL } from './schema-closure-sql.ts';
|
|
51
|
+
import { buildDropSchemaSql, emptyPredicate } from './schema-sql.ts';
|
|
52
|
+
|
|
53
|
+
export const IDENTITY_CHANGED = 'HF001';
|
|
54
|
+
export const NOT_EMPTY = 'HF002';
|
|
55
|
+
export const CROSS_SCHEMA_DEPENDENTS = 'HF003';
|
|
56
|
+
export const CASCADE_SEQUENCES = 'HF004';
|
|
57
|
+
|
|
58
|
+
/** How long any lock in the block may wait before the drop is refused (`55P03`, fails closed). */
|
|
59
|
+
const LOCK_TIMEOUT = '10s';
|
|
60
|
+
|
|
61
|
+
export interface AtomicDrop {
|
|
62
|
+
readonly name: string;
|
|
63
|
+
readonly oid: number;
|
|
64
|
+
readonly owner: string;
|
|
65
|
+
readonly cascade: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The whole atomic drop: lock → re-verify → refuse or drop. Pure text, no client. */
|
|
69
|
+
export const buildAtomicDropSql = (drop: AtomicDrop): string => {
|
|
70
|
+
const body = `DECLARE
|
|
71
|
+
v_name text := ${quoteStringLiteral(drop.name)};
|
|
72
|
+
v_oid oid := ${String(Math.trunc(drop.oid))};
|
|
73
|
+
v_owner text := ${quoteStringLiteral(drop.owner)};
|
|
74
|
+
v_cascade boolean := ${drop.cascade ? 'true' : 'false'};
|
|
75
|
+
v_ns oid;
|
|
76
|
+
v_count bigint;
|
|
77
|
+
v_comment text;
|
|
78
|
+
v_rel text;
|
|
79
|
+
BEGIN
|
|
80
|
+
SELECT n.oid INTO v_ns FROM pg_catalog.pg_namespace n WHERE n.nspname = v_name;
|
|
81
|
+
IF v_ns IS NULL THEN RETURN; END IF;
|
|
82
|
+
PERFORM pg_catalog.set_config('lock_timeout', '${LOCK_TIMEOUT}', true);
|
|
83
|
+
PERFORM pg_catalog.pg_advisory_xact_lock(v_ns::pg_catalog.int8);
|
|
84
|
+
v_comment := pg_catalog.obj_description(v_ns, 'pg_namespace');
|
|
85
|
+
EXECUTE pg_catalog.format('COMMENT ON SCHEMA %I IS %L', v_name, v_comment);
|
|
86
|
+
SELECT n.oid INTO v_ns FROM pg_catalog.pg_namespace n
|
|
87
|
+
WHERE n.nspname = v_name AND n.oid = v_oid
|
|
88
|
+
AND pg_catalog.pg_get_userbyid(n.nspowner) = v_owner;
|
|
89
|
+
IF v_ns IS NULL THEN
|
|
90
|
+
RAISE EXCEPTION 'schema identity changed' USING ERRCODE = '${IDENTITY_CHANGED}';
|
|
91
|
+
END IF;
|
|
92
|
+
IF NOT v_cascade THEN
|
|
93
|
+
IF NOT (${emptyPredicate('v_ns')}) THEN
|
|
94
|
+
RAISE EXCEPTION 'schema not empty' USING ERRCODE = '${NOT_EMPTY}';
|
|
95
|
+
END IF;
|
|
96
|
+
ELSE
|
|
97
|
+
SELECT count(*) INTO v_count FROM pg_catalog.pg_class c
|
|
98
|
+
WHERE c.relnamespace = v_ns AND c.relkind = 'S';
|
|
99
|
+
IF v_count > 0 THEN
|
|
100
|
+
RAISE EXCEPTION 'cascade sequences: %', v_count USING ERRCODE = '${CASCADE_SEQUENCES}';
|
|
101
|
+
END IF;
|
|
102
|
+
FOR v_rel IN SELECT c.oid::pg_catalog.regclass::pg_catalog.text FROM pg_catalog.pg_class c
|
|
103
|
+
WHERE c.relnamespace = v_ns AND c.relkind IN ('r', 'p', 'v', 'm', 'f') ORDER BY c.oid LOOP
|
|
104
|
+
EXECUTE pg_catalog.format('LOCK TABLE %s IN ACCESS EXCLUSIVE MODE', v_rel);
|
|
105
|
+
END LOOP;
|
|
106
|
+
${OUTSIDE_DEPENDENTS_SQL};
|
|
107
|
+
IF v_count > 0 THEN
|
|
108
|
+
RAISE EXCEPTION 'cross-schema dependents: %', v_count USING ERRCODE = '${CROSS_SCHEMA_DEPENDENTS}';
|
|
109
|
+
END IF;
|
|
110
|
+
END IF;
|
|
111
|
+
PERFORM 1 FROM pg_catalog.pg_namespace n WHERE n.oid = v_oid AND n.nspname = v_name;
|
|
112
|
+
IF NOT FOUND THEN
|
|
113
|
+
RAISE EXCEPTION 'schema identity changed' USING ERRCODE = '${IDENTITY_CHANGED}';
|
|
114
|
+
END IF;
|
|
115
|
+
EXECUTE ${quoteStringLiteral(buildDropSchemaSql(drop.name, drop.cascade))};
|
|
116
|
+
END`;
|
|
117
|
+
return `DO ${quoteStringLiteral(body)}`;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/** The server's SQLSTATE on a failed statement, whichever transport raised it. */
|
|
121
|
+
export const sqlStateOf = (error: SqlError): string | undefined => {
|
|
122
|
+
const cause = error.reason.cause;
|
|
123
|
+
const code =
|
|
124
|
+
typeof cause === 'object' && cause !== null ? (cause as { code?: unknown }).code : '';
|
|
125
|
+
return typeof code === 'string' ? code : undefined;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/** The dependents COUNT out of an `HF003` error. An unreadable message still refuses: it
|
|
129
|
+
* answers 1, never 0 — the refusal is the fail-closed answer. */
|
|
130
|
+
export const dependentsCount = (error: SqlError): number => {
|
|
131
|
+
const cause = error.reason.cause;
|
|
132
|
+
const message = typeof cause === 'object' && cause !== null ? (cause as Error).message : '';
|
|
133
|
+
const parsed = /dependents:\s*(\d+)/.exec(message ?? '')?.[1];
|
|
134
|
+
return Math.max(1, Number(parsed ?? 1));
|
|
135
|
+
};
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Postgres.Schema`'s atomic drop (`schema-drop-sql.ts`) against `fake-sql.ts`: one `DO`
|
|
3
|
+
* statement that re-verifies identity under a lock, refuses a non-empty schema or cross-schema
|
|
4
|
+
* dependents, and only then drops. The Opus read of PR 334 (2026-10-02) found the old
|
|
5
|
+
* three-autocommit drop racy, and `cascade: true` reaching other stacks' objects.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, expect, test } from 'bun:test';
|
|
8
|
+
import * as Effect from 'effect/Effect';
|
|
9
|
+
import { SqlError, UnknownError } from 'effect/sql/SqlError';
|
|
10
|
+
import type { PgExecutor } from './database-sql.ts';
|
|
11
|
+
import { makeFakeSql } from './fake-sql.ts';
|
|
12
|
+
import type { PostgresSchemaAttributes, PostgresSchemaProps } from './schema-attrs.ts';
|
|
13
|
+
import { buildAtomicDropSql } from './schema-drop-sql.ts';
|
|
14
|
+
import {
|
|
15
|
+
PostgresSchemaCascadeCrossSchemaRefused,
|
|
16
|
+
PostgresSchemaDeleteForeignRefused,
|
|
17
|
+
PostgresSchemaDropNotEmptyError,
|
|
18
|
+
} from './schema-errors.ts';
|
|
19
|
+
import { PostgresSchemaCascadeSequencesRefused } from './schema-sequence-error.ts';
|
|
20
|
+
import { deleteWithClient, dropWithClient } from './schema.ts';
|
|
21
|
+
|
|
22
|
+
const run = <A, E>(eff: Effect.Effect<A, E>): Promise<A> => Effect.runPromise(eff);
|
|
23
|
+
const fails = <A, E>(eff: Effect.Effect<A, E>): Promise<E> => Effect.runPromise(Effect.flip(eff));
|
|
24
|
+
|
|
25
|
+
const baseProps: PostgresSchemaProps = { name: 'ledger', database: 'postgres', owner: 'tim' };
|
|
26
|
+
const liveRow = (over: Partial<PostgresSchemaAttributes> = {}): PostgresSchemaAttributes => ({
|
|
27
|
+
name: 'ledger',
|
|
28
|
+
database: 'postgres',
|
|
29
|
+
oid: 30000,
|
|
30
|
+
owner: 'tim',
|
|
31
|
+
comment: null,
|
|
32
|
+
...over,
|
|
33
|
+
});
|
|
34
|
+
const drops = (fake: ReturnType<typeof makeFakeSql>) =>
|
|
35
|
+
fake.statements.filter((s) => s.text.startsWith('DO '));
|
|
36
|
+
|
|
37
|
+
describe('drop: the proof and the DROP are one statement', () => {
|
|
38
|
+
test('an empty schema is dropped by ONE DO carrying the proven oid and owner', async () => {
|
|
39
|
+
const fake = makeFakeSql({ schemas: [liveRow()] });
|
|
40
|
+
await run(dropWithClient(fake, baseProps));
|
|
41
|
+
expect(drops(fake).map((s) => s.text)).toEqual([
|
|
42
|
+
buildAtomicDropSql({ name: 'ledger', oid: 30000, owner: 'tim', cascade: false }),
|
|
43
|
+
]);
|
|
44
|
+
// No bare DROP and no separate emptiness probe reached the server.
|
|
45
|
+
expect(fake.statements.some((s) => s.text.startsWith('DROP SCHEMA'))).toBe(false);
|
|
46
|
+
expect(fake.statements.some((s) => s.text.includes('AS empty'))).toBe(false);
|
|
47
|
+
expect(fake.schemas.get('ledger')).toBeUndefined();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('the block locks BEFORE it re-verifies, re-verifies oid + owner, and drops last', () => {
|
|
51
|
+
const sql = buildAtomicDropSql({ name: 'ledger', oid: 30000, owner: 'tim', cascade: false });
|
|
52
|
+
const at = (needle: string): number => sql.indexOf(needle);
|
|
53
|
+
expect(at('pg_advisory_xact_lock')).toBeGreaterThan(-1);
|
|
54
|
+
expect(at('pg_advisory_xact_lock')).toBeLessThan(at('n.oid = v_oid'));
|
|
55
|
+
expect(at('pg_get_userbyid(n.nspowner) = v_owner')).toBeGreaterThan(-1);
|
|
56
|
+
// The DDL-conflicting lock (a no-op COMMENT on the schema object) precedes the re-verify too.
|
|
57
|
+
expect(at('COMMENT ON SCHEMA %I')).toBeGreaterThan(-1);
|
|
58
|
+
expect(at('COMMENT ON SCHEMA %I')).toBeLessThan(at('n.oid = v_oid'));
|
|
59
|
+
expect(at("''lock_timeout''")).toBeLessThan(at('pg_advisory_xact_lock'));
|
|
60
|
+
expect(at('n.oid = v_oid')).toBeLessThan(sql.lastIndexOf('EXECUTE'));
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('a schema replaced between the read and the drop is refused, nothing dropped (HF001)', async () => {
|
|
64
|
+
const fake = makeFakeSql({ schemas: [liveRow()] });
|
|
65
|
+
// The read sees oid 30000; before the DO runs another role drops and recreates the name.
|
|
66
|
+
const racing: PgExecutor = {
|
|
67
|
+
...fake,
|
|
68
|
+
unsafe: (text, params) => {
|
|
69
|
+
if (text.startsWith('DO ')) {
|
|
70
|
+
fake.schemas.set('ledger', liveRow({ oid: 30001, owner: 'intruder' }));
|
|
71
|
+
}
|
|
72
|
+
return fake.unsafe(text, params);
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
const error = await fails(deleteWithClient(racing, baseProps, liveRow()));
|
|
76
|
+
expect(error).toBeInstanceOf(PostgresSchemaDeleteForeignRefused);
|
|
77
|
+
expect(error).toMatchObject({ liveOid: 30001, liveOwner: 'intruder', lastOid: 30000 });
|
|
78
|
+
expect(fake.schemas.get('ledger')).toMatchObject({ oid: 30001 });
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('a schema that vanished between the read and the drop is idempotent success', async () => {
|
|
82
|
+
const fake = makeFakeSql({ schemas: [liveRow()] });
|
|
83
|
+
const racing: PgExecutor = {
|
|
84
|
+
...fake,
|
|
85
|
+
unsafe: (text, params) => {
|
|
86
|
+
if (text.startsWith('DO ')) fake.schemas.delete('ledger');
|
|
87
|
+
return fake.unsafe(text, params);
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
await run(dropWithClient(racing, baseProps));
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('drop: emptiness', () => {
|
|
95
|
+
test('refuses a non-empty schema with cascade false (HF002 → typed tag)', async () => {
|
|
96
|
+
const fake = makeFakeSql({ schemas: [liveRow()], schemasWithRelations: ['ledger'] });
|
|
97
|
+
expect(await fails(dropWithClient(fake, baseProps))).toBeInstanceOf(
|
|
98
|
+
PostgresSchemaDropNotEmptyError,
|
|
99
|
+
);
|
|
100
|
+
expect(fake.schemas.get('ledger')).not.toBeUndefined();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('drops a non-empty schema with cascade true when nothing outside depends on it', async () => {
|
|
104
|
+
const fake = makeFakeSql({ schemas: [liveRow()], schemasWithRelations: ['ledger'] });
|
|
105
|
+
await run(dropWithClient(fake, { ...baseProps, cascade: true }));
|
|
106
|
+
expect(fake.schemas.get('ledger')).toBeUndefined();
|
|
107
|
+
expect(drops(fake)[0]?.text).toContain(`'DROP SCHEMA IF EXISTS "ledger" CASCADE'`);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("the server's own 2BP01 (an object kind the four catalogs miss) is the same typed tag", async () => {
|
|
111
|
+
const refusing: PgExecutor = {
|
|
112
|
+
unsafe: <A extends object>(text: string) =>
|
|
113
|
+
text.startsWith('SELECT current_database()')
|
|
114
|
+
? Effect.succeed([{ database: 'postgres' }] as unknown as ReadonlyArray<A>)
|
|
115
|
+
: !text.startsWith('DO ') && text.includes('FROM pg_catalog.pg_namespace')
|
|
116
|
+
? Effect.succeed([liveRow()] as unknown as ReadonlyArray<A>)
|
|
117
|
+
: Effect.fail(
|
|
118
|
+
new SqlError({
|
|
119
|
+
reason: new UnknownError({
|
|
120
|
+
cause: Object.assign(new Error('ERROR: 2BP01: dependent objects'), {
|
|
121
|
+
code: '2BP01',
|
|
122
|
+
}),
|
|
123
|
+
message: 'dependent_objects_still_exist',
|
|
124
|
+
operation: 'DO',
|
|
125
|
+
}),
|
|
126
|
+
}),
|
|
127
|
+
),
|
|
128
|
+
transaction: () => Effect.void,
|
|
129
|
+
};
|
|
130
|
+
expect(await fails(dropWithClient(refusing, baseProps))).toBeInstanceOf(
|
|
131
|
+
PostgresSchemaDropNotEmptyError,
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('drop: cascade never reaches another schema', () => {
|
|
137
|
+
test('refuses cascade when objects in other schemas depend on this one — count only (HF003)', async () => {
|
|
138
|
+
const fake = makeFakeSql({
|
|
139
|
+
schemas: [liveRow()],
|
|
140
|
+
schemasWithRelations: ['ledger'],
|
|
141
|
+
dependentsOutside: { ledger: 3 },
|
|
142
|
+
});
|
|
143
|
+
const error = await fails(dropWithClient(fake, { ...baseProps, cascade: true }));
|
|
144
|
+
expect(error).toBeInstanceOf(PostgresSchemaCascadeCrossSchemaRefused);
|
|
145
|
+
expect(error).toMatchObject({ schema: 'ledger', dependents: 3 });
|
|
146
|
+
// The message names a count and this schema — never another owner's object.
|
|
147
|
+
expect((error as Error).message).toContain('3 dependent object(s)');
|
|
148
|
+
expect(fake.schemas.get('ledger')).not.toBeUndefined();
|
|
149
|
+
expect(fake.relationsIn.has('ledger')).toBe(true);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('refuses cascade over a schema holding a sequence — count only (HF004), nothing dropped', async () => {
|
|
153
|
+
const fake = makeFakeSql({
|
|
154
|
+
schemas: [liveRow()],
|
|
155
|
+
schemasWithRelations: ['ledger'],
|
|
156
|
+
sequencesIn: { ledger: 2 },
|
|
157
|
+
});
|
|
158
|
+
const error = await fails(dropWithClient(fake, { ...baseProps, cascade: true }));
|
|
159
|
+
expect(error).toBeInstanceOf(PostgresSchemaCascadeSequencesRefused);
|
|
160
|
+
expect(error).toMatchObject({ schema: 'ledger', sequences: 2 });
|
|
161
|
+
expect((error as Error).message).toContain('2 sequence(s)');
|
|
162
|
+
expect(fake.schemas.get('ledger')).not.toBeUndefined();
|
|
163
|
+
expect(fake.relationsIn.has('ledger')).toBe(true);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test('the sequence check precedes every table lock in the generated block', () => {
|
|
167
|
+
const sql = buildAtomicDropSql({ name: 'ledger', oid: 1, owner: 'tim', cascade: true });
|
|
168
|
+
expect(sql).toContain("c.relkind = ''S''");
|
|
169
|
+
expect(sql.indexOf("c.relkind = ''S''")).toBeLessThan(sql.indexOf('LOCK TABLE'));
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('cascade false never runs the dependents check: the emptiness refusal comes first', async () => {
|
|
173
|
+
const fake = makeFakeSql({
|
|
174
|
+
schemas: [liveRow()],
|
|
175
|
+
schemasWithRelations: ['ledger'],
|
|
176
|
+
dependentsOutside: { ledger: 3 },
|
|
177
|
+
});
|
|
178
|
+
expect(await fails(dropWithClient(fake, baseProps))).toBeInstanceOf(
|
|
179
|
+
PostgresSchemaDropNotEmptyError,
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('the dependents query counts per catalog class and treats an unknown class as foreign', () => {
|
|
184
|
+
const sql = buildAtomicDropSql({ name: 'ledger', oid: 1, owner: 'tim', cascade: true });
|
|
185
|
+
expect(sql).toContain('pg_catalog.pg_depend');
|
|
186
|
+
// A class the CASE does not know yields NULL; IS DISTINCT FROM counts NULL as foreign, so an
|
|
187
|
+
// unclassifiable dependent refuses the cascade instead of slipping through.
|
|
188
|
+
expect(sql).toContain('IS NOT DISTINCT FROM v_ns');
|
|
189
|
+
// The walk follows every deptype except the pin (`p`): n a i P S e x each cascade or attribute.
|
|
190
|
+
expect(sql).toContain("d.deptype <> ''p''");
|
|
191
|
+
for (const catalog of [
|
|
192
|
+
'pg_collation',
|
|
193
|
+
'pg_conversion',
|
|
194
|
+
'pg_opclass',
|
|
195
|
+
'pg_opfamily',
|
|
196
|
+
'pg_ts_config',
|
|
197
|
+
'pg_statistic_ext',
|
|
198
|
+
'pg_extension',
|
|
199
|
+
'pg_constraint',
|
|
200
|
+
'pg_trigger',
|
|
201
|
+
'pg_rewrite',
|
|
202
|
+
'pg_attrdef',
|
|
203
|
+
'pg_policy',
|
|
204
|
+
]) {
|
|
205
|
+
// Inside the body's `E'…'` literal every quote is doubled.
|
|
206
|
+
expect(sql).toContain(`''pg_catalog.${catalog}''::pg_catalog.regclass`);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
describe('drop: the generated text cannot be broken out of by a name', () => {
|
|
212
|
+
test('a schema name holding quotes, backslashes and a dollar-quote tag stays inside its literal', () => {
|
|
213
|
+
const name = `x$$; DROP TABLE t; --'\\"`;
|
|
214
|
+
const sql = buildAtomicDropSql({ name, oid: 1, owner: `o'wn\\er`, cascade: false });
|
|
215
|
+
expect(sql.startsWith("DO E'")).toBe(true);
|
|
216
|
+
expect(sql.endsWith("'")).toBe(true);
|
|
217
|
+
// No unescaped quote before the final one: every `'` inside is doubled by the quoter.
|
|
218
|
+
expect(sql.slice(5, -1).replace(/''/g, '')).not.toContain("'");
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test('applyAtomicDrop reads such a name and owner back exactly (fake parses its own output)', async () => {
|
|
222
|
+
const name = `x$$; DROP TABLE t; --'\\"`;
|
|
223
|
+
const owner = `o'wn\\er$tag$`;
|
|
224
|
+
const fake = makeFakeSql({ schemas: [liveRow({ name, owner })] });
|
|
225
|
+
await run(dropWithClient(fake, { name, database: 'postgres' }));
|
|
226
|
+
expect(fake.schemas.get(name)).toBeUndefined();
|
|
227
|
+
// A different owner with the same tricky name is a refusal, not a by-name drop.
|
|
228
|
+
const other = makeFakeSql({ schemas: [liveRow({ name, owner: 'someone' })] });
|
|
229
|
+
const racing: PgExecutor = {
|
|
230
|
+
...other,
|
|
231
|
+
unsafe: (text, params) => {
|
|
232
|
+
if (text.startsWith('DO ')) other.schemas.set(name, liveRow({ name, owner }));
|
|
233
|
+
return other.unsafe(text, params);
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
await fails(
|
|
237
|
+
deleteWithClient(racing, { name, database: 'postgres' }, liveRow({ name, owner: 'someone' })),
|
|
238
|
+
);
|
|
239
|
+
expect(other.schemas.get(name)).toBeDefined();
|
|
240
|
+
});
|
|
241
|
+
});
|