@homeflare/alchemy 0.43.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -1
- package/dist/caddy/index.js +35 -1
- package/dist/caddy/index.js.map +3 -3
- package/dist/launchd/index.js +35 -1
- package/dist/launchd/index.js.map +3 -3
- package/dist/linux/index.js +35 -1
- package/dist/linux/index.js.map +3 -3
- package/dist/litellm/access-group-form.d.ts +26 -0
- package/dist/litellm/access-group-form.d.ts.map +1 -0
- package/dist/litellm/access-group-operations.d.ts +37 -0
- package/dist/litellm/access-group-operations.d.ts.map +1 -0
- package/dist/litellm/access-group-types.d.ts +49 -0
- package/dist/litellm/access-group-types.d.ts.map +1 -0
- package/dist/litellm/access-group.d.ts +55 -0
- package/dist/litellm/access-group.d.ts.map +1 -0
- package/dist/litellm/credential-errors.d.ts +131 -0
- package/dist/litellm/credential-errors.d.ts.map +1 -0
- package/dist/litellm/credential-form.d.ts +79 -0
- package/dist/litellm/credential-form.d.ts.map +1 -0
- package/dist/litellm/credential-operations.d.ts +84 -0
- package/dist/litellm/credential-operations.d.ts.map +1 -0
- package/dist/litellm/credential-rewrite.d.ts +6 -0
- package/dist/litellm/credential-rewrite.d.ts.map +1 -0
- package/dist/litellm/credential-types.d.ts +87 -0
- package/dist/litellm/credential-types.d.ts.map +1 -0
- package/dist/litellm/credential-values.d.ts +53 -0
- package/dist/litellm/credential-values.d.ts.map +1 -0
- package/dist/litellm/credential.d.ts +83 -0
- package/dist/litellm/credential.d.ts.map +1 -0
- package/dist/litellm/fake-access-group-litellm.d.ts +34 -0
- package/dist/litellm/fake-access-group-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-credential-litellm.d.ts +54 -0
- package/dist/litellm/fake-credential-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-keys.d.ts +53 -0
- package/dist/litellm/fake-keys.d.ts.map +1 -0
- package/dist/litellm/fake-litellm.d.ts +4 -1
- package/dist/litellm/fake-litellm.d.ts.map +1 -1
- package/dist/litellm/fake-mcp-litellm.d.ts +67 -0
- package/dist/litellm/fake-mcp-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-model-litellm-post-update.d.ts +49 -0
- package/dist/litellm/fake-model-litellm-post-update.d.ts.map +1 -0
- package/dist/litellm/fake-model-litellm.d.ts +48 -0
- package/dist/litellm/fake-model-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-policy-attachment-litellm.d.ts +33 -0
- package/dist/litellm/fake-policy-attachment-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-policy-litellm.d.ts +32 -0
- package/dist/litellm/fake-policy-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-registry-base.d.ts +42 -0
- package/dist/litellm/fake-registry-base.d.ts.map +1 -0
- package/dist/litellm/fake-stack.d.ts +8 -1
- package/dist/litellm/fake-stack.d.ts.map +1 -1
- package/dist/litellm/fake-team-litellm.d.ts +47 -0
- package/dist/litellm/fake-team-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-tool-litellm.d.ts +28 -0
- package/dist/litellm/fake-tool-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-toolset-litellm.d.ts +29 -0
- package/dist/litellm/fake-toolset-litellm.d.ts.map +1 -0
- package/dist/litellm/index.d.ts +18 -0
- package/dist/litellm/index.d.ts.map +1 -1
- package/dist/litellm/index.js +3078 -2
- package/dist/litellm/index.js.map +65 -4
- package/dist/litellm/key-errors.d.ts +169 -0
- package/dist/litellm/key-errors.d.ts.map +1 -0
- package/dist/litellm/key-form.d.ts +119 -0
- package/dist/litellm/key-form.d.ts.map +1 -0
- package/dist/litellm/key-harness.d.ts +41 -0
- package/dist/litellm/key-harness.d.ts.map +1 -0
- package/dist/litellm/key-metadata-errors.d.ts +30 -0
- package/dist/litellm/key-metadata-errors.d.ts.map +1 -0
- package/dist/litellm/key-metadata.d.ts +22 -0
- package/dist/litellm/key-metadata.d.ts.map +1 -0
- package/dist/litellm/key-operations.d.ts +63 -0
- package/dist/litellm/key-operations.d.ts.map +1 -0
- package/dist/litellm/key-secret.d.ts +33 -0
- package/dist/litellm/key-secret.d.ts.map +1 -0
- package/dist/litellm/key.d.ts +78 -0
- package/dist/litellm/key.d.ts.map +1 -0
- package/dist/litellm/mcp-server-credential.d.ts +50 -0
- package/dist/litellm/mcp-server-credential.d.ts.map +1 -0
- package/dist/litellm/mcp-server-errors.d.ts +101 -0
- package/dist/litellm/mcp-server-errors.d.ts.map +1 -0
- package/dist/litellm/mcp-server-form.d.ts +56 -0
- package/dist/litellm/mcp-server-form.d.ts.map +1 -0
- package/dist/litellm/mcp-server-locate.d.ts +26 -0
- package/dist/litellm/mcp-server-locate.d.ts.map +1 -0
- package/dist/litellm/mcp-server-operations.d.ts +63 -0
- package/dist/litellm/mcp-server-operations.d.ts.map +1 -0
- package/dist/litellm/mcp-server-shadow.d.ts +24 -0
- package/dist/litellm/mcp-server-shadow.d.ts.map +1 -0
- package/dist/litellm/mcp-server-types.d.ts +118 -0
- package/dist/litellm/mcp-server-types.d.ts.map +1 -0
- package/dist/litellm/mcp-server-url.d.ts +11 -0
- package/dist/litellm/mcp-server-url.d.ts.map +1 -0
- package/dist/litellm/mcp-server.d.ts +95 -0
- package/dist/litellm/mcp-server.d.ts.map +1 -0
- package/dist/litellm/model-claim.d.ts +24 -0
- package/dist/litellm/model-claim.d.ts.map +1 -0
- package/dist/litellm/model-credential.d.ts +12 -0
- package/dist/litellm/model-credential.d.ts.map +1 -0
- package/dist/litellm/model-errors.d.ts +119 -0
- package/dist/litellm/model-errors.d.ts.map +1 -0
- package/dist/litellm/model-form.d.ts +78 -0
- package/dist/litellm/model-form.d.ts.map +1 -0
- package/dist/litellm/model-locate.d.ts +13 -0
- package/dist/litellm/model-locate.d.ts.map +1 -0
- package/dist/litellm/model-operations.d.ts +59 -0
- package/dist/litellm/model-operations.d.ts.map +1 -0
- package/dist/litellm/model-types.d.ts +91 -0
- package/dist/litellm/model-types.d.ts.map +1 -0
- package/dist/litellm/model.d.ts +88 -0
- package/dist/litellm/model.d.ts.map +1 -0
- package/dist/litellm/policy-attachment-form.d.ts +19 -0
- package/dist/litellm/policy-attachment-form.d.ts.map +1 -0
- package/dist/litellm/policy-attachment-operations.d.ts +31 -0
- package/dist/litellm/policy-attachment-operations.d.ts.map +1 -0
- package/dist/litellm/policy-attachment-types.d.ts +48 -0
- package/dist/litellm/policy-attachment-types.d.ts.map +1 -0
- package/dist/litellm/policy-attachment.d.ts +61 -0
- package/dist/litellm/policy-attachment.d.ts.map +1 -0
- package/dist/litellm/policy-form.d.ts +22 -0
- package/dist/litellm/policy-form.d.ts.map +1 -0
- package/dist/litellm/policy-operations.d.ts +42 -0
- package/dist/litellm/policy-operations.d.ts.map +1 -0
- package/dist/litellm/policy-types.d.ts +57 -0
- package/dist/litellm/policy-types.d.ts.map +1 -0
- package/dist/litellm/policy.d.ts +65 -0
- package/dist/litellm/policy.d.ts.map +1 -0
- package/dist/litellm/providers.d.ts +1 -1
- package/dist/litellm/providers.d.ts.map +1 -1
- package/dist/litellm/registry-errors.d.ts +62 -0
- package/dist/litellm/registry-errors.d.ts.map +1 -0
- package/dist/litellm/registry-support.d.ts +42 -0
- package/dist/litellm/registry-support.d.ts.map +1 -0
- package/dist/litellm/team-form.d.ts +26 -0
- package/dist/litellm/team-form.d.ts.map +1 -0
- package/dist/litellm/team-members.d.ts +10 -0
- package/dist/litellm/team-members.d.ts.map +1 -0
- package/dist/litellm/team-operations.d.ts +38 -0
- package/dist/litellm/team-operations.d.ts.map +1 -0
- package/dist/litellm/team-permission.d.ts +35 -0
- package/dist/litellm/team-permission.d.ts.map +1 -0
- package/dist/litellm/team-types.d.ts +101 -0
- package/dist/litellm/team-types.d.ts.map +1 -0
- package/dist/litellm/team.d.ts +67 -0
- package/dist/litellm/team.d.ts.map +1 -0
- package/dist/litellm/tool-policy-form.d.ts +21 -0
- package/dist/litellm/tool-policy-form.d.ts.map +1 -0
- package/dist/litellm/tool-policy-operations.d.ts +30 -0
- package/dist/litellm/tool-policy-operations.d.ts.map +1 -0
- package/dist/litellm/tool-policy-types.d.ts +46 -0
- package/dist/litellm/tool-policy-types.d.ts.map +1 -0
- package/dist/litellm/tool-policy.d.ts +62 -0
- package/dist/litellm/tool-policy.d.ts.map +1 -0
- package/dist/litellm/toolset-form.d.ts +20 -0
- package/dist/litellm/toolset-form.d.ts.map +1 -0
- package/dist/litellm/toolset-operations.d.ts +39 -0
- package/dist/litellm/toolset-operations.d.ts.map +1 -0
- package/dist/litellm/toolset-types.d.ts +46 -0
- package/dist/litellm/toolset-types.d.ts.map +1 -0
- package/dist/litellm/toolset.d.ts +55 -0
- package/dist/litellm/toolset.d.ts.map +1 -0
- package/dist/openbao/index.js +35 -1
- package/dist/openbao/index.js.map +3 -3
- package/dist/ownership/rows.d.ts +11 -0
- package/dist/ownership/rows.d.ts.map +1 -1
- package/dist/postgres/connection.d.ts +14 -2
- package/dist/postgres/connection.d.ts.map +1 -1
- package/dist/postgres/database-sql.d.ts +27 -8
- package/dist/postgres/database-sql.d.ts.map +1 -1
- package/dist/postgres/fake-grants-parse.d.ts +37 -0
- package/dist/postgres/fake-grants-parse.d.ts.map +1 -0
- package/dist/postgres/fake-grants-read.d.ts +61 -0
- package/dist/postgres/fake-grants-read.d.ts.map +1 -0
- package/dist/postgres/fake-grants-sql.d.ts +46 -0
- package/dist/postgres/fake-grants-sql.d.ts.map +1 -0
- package/dist/postgres/fake-grants-write.d.ts +29 -0
- package/dist/postgres/fake-grants-write.d.ts.map +1 -0
- package/dist/postgres/fake-role-sql.d.ts +41 -0
- package/dist/postgres/fake-role-sql.d.ts.map +1 -0
- package/dist/postgres/fake-schema-sql.d.ts +42 -0
- package/dist/postgres/fake-schema-sql.d.ts.map +1 -0
- package/dist/postgres/fake-sql-parse.d.ts +23 -0
- package/dist/postgres/fake-sql-parse.d.ts.map +1 -0
- package/dist/postgres/fake-sql-quote.d.ts +12 -0
- package/dist/postgres/fake-sql-quote.d.ts.map +1 -0
- package/dist/postgres/fake-sql.d.ts +42 -0
- package/dist/postgres/fake-sql.d.ts.map +1 -1
- package/dist/postgres/grants-attrs.d.ts +115 -0
- package/dist/postgres/grants-attrs.d.ts.map +1 -0
- package/dist/postgres/grants-declare.d.ts +88 -0
- package/dist/postgres/grants-declare.d.ts.map +1 -0
- package/dist/postgres/grants-diff.d.ts +25 -0
- package/dist/postgres/grants-diff.d.ts.map +1 -0
- package/dist/postgres/grants-errors.d.ts +137 -0
- package/dist/postgres/grants-errors.d.ts.map +1 -0
- package/dist/postgres/grants-existence.d.ts +14 -0
- package/dist/postgres/grants-existence.d.ts.map +1 -0
- package/dist/postgres/grants-ops.d.ts +65 -0
- package/dist/postgres/grants-ops.d.ts.map +1 -0
- package/dist/postgres/grants-ownership.d.ts +18 -0
- package/dist/postgres/grants-ownership.d.ts.map +1 -0
- package/dist/postgres/grants-plan-restore.d.ts +22 -0
- package/dist/postgres/grants-plan-restore.d.ts.map +1 -0
- package/dist/postgres/grants-plan.d.ts +65 -0
- package/dist/postgres/grants-plan.d.ts.map +1 -0
- package/dist/postgres/grants-read-defaults.d.ts +17 -0
- package/dist/postgres/grants-read-defaults.d.ts.map +1 -0
- package/dist/postgres/grants-read.d.ts +77 -0
- package/dist/postgres/grants-read.d.ts.map +1 -0
- package/dist/postgres/grants-refuse.d.ts +25 -0
- package/dist/postgres/grants-refuse.d.ts.map +1 -0
- package/dist/postgres/grants-sql.d.ts +42 -0
- package/dist/postgres/grants-sql.d.ts.map +1 -0
- package/dist/postgres/grants-words.d.ts +29 -0
- package/dist/postgres/grants-words.d.ts.map +1 -0
- package/dist/postgres/grants.d.ts +37 -0
- package/dist/postgres/grants.d.ts.map +1 -0
- package/dist/postgres/index.d.ts +31 -6
- package/dist/postgres/index.d.ts.map +1 -1
- package/dist/postgres/index.js +1998 -37
- package/dist/postgres/index.js.map +46 -7
- package/dist/postgres/installed-classifier.d.ts +13 -0
- package/dist/postgres/installed-classifier.d.ts.map +1 -0
- package/dist/postgres/providers.d.ts +16 -8
- package/dist/postgres/providers.d.ts.map +1 -1
- package/dist/postgres/psql-executor.d.ts +4 -0
- package/dist/postgres/psql-executor.d.ts.map +1 -1
- package/dist/postgres/role-attrs.d.ts +112 -0
- package/dist/postgres/role-attrs.d.ts.map +1 -0
- package/dist/postgres/role-diff.d.ts +22 -0
- package/dist/postgres/role-diff.d.ts.map +1 -0
- package/dist/postgres/role-errors.d.ts +132 -0
- package/dist/postgres/role-errors.d.ts.map +1 -0
- package/dist/postgres/role-membership-sql.d.ts +78 -0
- package/dist/postgres/role-membership-sql.d.ts.map +1 -0
- package/dist/postgres/role-provider.d.ts +11 -0
- package/dist/postgres/role-provider.d.ts.map +1 -0
- package/dist/postgres/role-scram.d.ts +8 -0
- package/dist/postgres/role-scram.d.ts.map +1 -0
- package/dist/postgres/role-secrets.d.ts +38 -0
- package/dist/postgres/role-secrets.d.ts.map +1 -0
- package/dist/postgres/role-sql.d.ts +68 -0
- package/dist/postgres/role-sql.d.ts.map +1 -0
- package/dist/postgres/role.d.ts +57 -0
- package/dist/postgres/role.d.ts.map +1 -0
- package/dist/postgres/schema-assert.d.ts +50 -0
- package/dist/postgres/schema-assert.d.ts.map +1 -0
- package/dist/postgres/schema-attrs.d.ts +51 -0
- package/dist/postgres/schema-attrs.d.ts.map +1 -0
- package/dist/postgres/schema-delete.d.ts +28 -0
- package/dist/postgres/schema-delete.d.ts.map +1 -0
- package/dist/postgres/schema-diff.d.ts +14 -0
- package/dist/postgres/schema-diff.d.ts.map +1 -0
- package/dist/postgres/schema-errors.d.ts +139 -0
- package/dist/postgres/schema-errors.d.ts.map +1 -0
- package/dist/postgres/schema-identity.d.ts +8 -0
- package/dist/postgres/schema-identity.d.ts.map +1 -0
- package/dist/postgres/schema-reconcile.d.ts +12 -0
- package/dist/postgres/schema-reconcile.d.ts.map +1 -0
- package/dist/postgres/schema-sql.d.ts +45 -0
- package/dist/postgres/schema-sql.d.ts.map +1 -0
- package/dist/postgres/schema-test-kit.d.ts +67 -0
- package/dist/postgres/schema-test-kit.d.ts.map +1 -0
- package/dist/postgres/schema.d.ts +54 -0
- package/dist/postgres/schema.d.ts.map +1 -0
- package/dist/proxmox/index.js +35 -1
- package/dist/proxmox/index.js.map +3 -3
- package/dist/release/fake-engine.d.ts +10 -0
- package/dist/release/fake-engine.d.ts.map +1 -1
- package/dist/release/index.js +35 -1
- package/dist/release/index.js.map +3 -3
- package/dist/valkey/acl-attrs.d.ts +94 -0
- package/dist/valkey/acl-attrs.d.ts.map +1 -0
- package/dist/valkey/acl-compare.d.ts +28 -0
- package/dist/valkey/acl-compare.d.ts.map +1 -0
- package/dist/valkey/acl-connection.d.ts +6 -0
- package/dist/valkey/acl-connection.d.ts.map +1 -0
- package/dist/valkey/acl-form.d.ts +62 -0
- package/dist/valkey/acl-form.d.ts.map +1 -0
- package/dist/valkey/acl-managed.d.ts +4 -0
- package/dist/valkey/acl-managed.d.ts.map +1 -0
- package/dist/valkey/acl-ops.d.ts +37 -0
- package/dist/valkey/acl-ops.d.ts.map +1 -0
- package/dist/valkey/acl-profiles.d.ts +53 -0
- package/dist/valkey/acl-profiles.d.ts.map +1 -0
- package/dist/valkey/acl-reconcile.d.ts +26 -0
- package/dist/valkey/acl-reconcile.d.ts.map +1 -0
- package/dist/valkey/acl.d.ts +24 -0
- package/dist/valkey/acl.d.ts.map +1 -0
- package/dist/valkey/connection-service.d.ts +22 -0
- package/dist/valkey/connection-service.d.ts.map +1 -0
- package/dist/valkey/connection.d.ts +59 -0
- package/dist/valkey/connection.d.ts.map +1 -0
- package/dist/valkey/errors.d.ts +165 -0
- package/dist/valkey/errors.d.ts.map +1 -0
- package/dist/valkey/fake-acl.d.ts +29 -0
- package/dist/valkey/fake-acl.d.ts.map +1 -0
- package/dist/valkey/fake-valkey.d.ts +35 -0
- package/dist/valkey/fake-valkey.d.ts.map +1 -0
- package/dist/valkey/handler-test-fixture.d.ts +20 -0
- package/dist/valkey/handler-test-fixture.d.ts.map +1 -0
- package/dist/valkey/index.d.ts +18 -0
- package/dist/valkey/index.d.ts.map +1 -0
- package/dist/valkey/index.js +964 -0
- package/dist/valkey/index.js.map +27 -0
- package/dist/valkey/instance-attrs.d.ts +46 -0
- package/dist/valkey/instance-attrs.d.ts.map +1 -0
- package/dist/valkey/instance-form.d.ts +22 -0
- package/dist/valkey/instance-form.d.ts.map +1 -0
- package/dist/valkey/instance.d.ts +48 -0
- package/dist/valkey/instance.d.ts.map +1 -0
- package/dist/valkey/provider-socket-fixture.d.ts +8 -0
- package/dist/valkey/provider-socket-fixture.d.ts.map +1 -0
- package/dist/valkey/providers.d.ts +16 -0
- package/dist/valkey/providers.d.ts.map +1 -0
- package/dist/valkey/resp.d.ts +38 -0
- package/dist/valkey/resp.d.ts.map +1 -0
- package/dist/valkey/transport-error.d.ts +36 -0
- package/dist/valkey/transport-error.d.ts.map +1 -0
- package/dist/valkey/transport.d.ts +50 -0
- package/dist/valkey/transport.d.ts.map +1 -0
- package/docs/litellm-access-group.md +105 -0
- package/docs/litellm-credential.md +60 -0
- package/docs/litellm-key-metadata.md +56 -0
- package/docs/litellm-key.md +194 -0
- package/docs/litellm-mcp.md +187 -0
- package/docs/litellm-model.md +111 -0
- package/docs/litellm-policy-attachment.md +86 -0
- package/docs/litellm-policy.md +103 -0
- package/docs/litellm-team.md +117 -0
- package/docs/litellm-tool-policy.md +90 -0
- package/docs/litellm-toolset.md +92 -0
- package/docs/litellm.md +16 -9
- package/docs/peers.md +15 -6
- package/docs/postgres-grants-example.md +55 -0
- package/docs/postgres-grants.md +157 -0
- package/docs/postgres-role.md +126 -0
- package/docs/postgres-schema.md +139 -0
- package/docs/postgres.md +20 -3
- package/docs/valkey-transport.md +29 -0
- package/docs/valkey.md +187 -0
- package/package.json +6 -2
- package/src/ceph/ceph-argv.test.ts +1 -1
- package/src/launchd/sudo-allowlist.test.ts +2 -2
- package/src/linux/sudo-allowlist.test.ts +3 -3
- package/src/linux/sudo-runner.test.ts +2 -2
- package/src/litellm/access-group-form.ts +105 -0
- package/src/litellm/access-group-operations.ts +92 -0
- package/src/litellm/access-group-types.ts +49 -0
- package/src/litellm/access-group.test.ts +197 -0
- package/src/litellm/access-group.ts +151 -0
- package/src/litellm/credential-errors.ts +183 -0
- package/src/litellm/credential-form.test.ts +151 -0
- package/src/litellm/credential-form.ts +211 -0
- package/src/litellm/credential-guards.test.ts +241 -0
- package/src/litellm/credential-operations.ts +208 -0
- package/src/litellm/credential-recovery.test.ts +93 -0
- package/src/litellm/credential-rewrite.test.ts +164 -0
- package/src/litellm/credential-rewrite.ts +50 -0
- package/src/litellm/credential-types.ts +117 -0
- package/src/litellm/credential-values.ts +94 -0
- package/src/litellm/credential.test.ts +218 -0
- package/src/litellm/credential.ts +227 -0
- package/src/litellm/fake-access-group-litellm.ts +127 -0
- package/src/litellm/fake-credential-litellm.ts +216 -0
- package/src/litellm/fake-keys.ts +190 -0
- package/src/litellm/fake-litellm.ts +19 -9
- package/src/litellm/fake-mcp-litellm.ts +214 -0
- package/src/litellm/fake-model-litellm-post-update.ts +103 -0
- package/src/litellm/fake-model-litellm.ts +233 -0
- package/src/litellm/fake-policy-attachment-litellm.ts +97 -0
- package/src/litellm/fake-policy-litellm.ts +186 -0
- package/src/litellm/fake-registry-base.ts +69 -0
- package/src/litellm/fake-stack.ts +17 -1
- package/src/litellm/fake-team-litellm.ts +229 -0
- package/src/litellm/fake-tool-litellm.ts +88 -0
- package/src/litellm/fake-toolset-litellm.ts +110 -0
- package/src/litellm/index.ts +140 -0
- package/src/litellm/key-delete.test.ts +212 -0
- package/src/litellm/key-errors.ts +241 -0
- package/src/litellm/key-form.ts +200 -0
- package/src/litellm/key-harness.ts +121 -0
- package/src/litellm/key-metadata-errors.ts +49 -0
- package/src/litellm/key-metadata.test.ts +156 -0
- package/src/litellm/key-metadata.ts +74 -0
- package/src/litellm/key-operations.ts +184 -0
- package/src/litellm/key-rotation.test.ts +110 -0
- package/src/litellm/key-scope.test.ts +135 -0
- package/src/litellm/key-secret.test.ts +248 -0
- package/src/litellm/key-secret.ts +137 -0
- package/src/litellm/key-transport.test.ts +166 -0
- package/src/litellm/key.test.ts +231 -0
- package/src/litellm/key.ts +213 -0
- package/src/litellm/mcp-server-adopt.test.ts +86 -0
- package/src/litellm/mcp-server-alias.test.ts +184 -0
- package/src/litellm/mcp-server-credential.ts +85 -0
- package/src/litellm/mcp-server-errors.ts +141 -0
- package/src/litellm/mcp-server-exports.test.ts +15 -0
- package/src/litellm/mcp-server-form.test.ts +241 -0
- package/src/litellm/mcp-server-form.ts +233 -0
- package/src/litellm/mcp-server-handlers.test.ts +150 -0
- package/src/litellm/mcp-server-locate.ts +66 -0
- package/src/litellm/mcp-server-operations.ts +111 -0
- package/src/litellm/mcp-server-registry.test.ts +146 -0
- package/src/litellm/mcp-server-retype.test.ts +114 -0
- package/src/litellm/mcp-server-shadow.test.ts +91 -0
- package/src/litellm/mcp-server-shadow.ts +45 -0
- package/src/litellm/mcp-server-tools.test.ts +85 -0
- package/src/litellm/mcp-server-types.ts +143 -0
- package/src/litellm/mcp-server-url.ts +66 -0
- package/src/litellm/mcp-server.test.ts +213 -0
- package/src/litellm/mcp-server.ts +225 -0
- package/src/litellm/model-claim.ts +69 -0
- package/src/litellm/model-credential.ts +45 -0
- package/src/litellm/model-errors.ts +168 -0
- package/src/litellm/model-foreign-row.test.ts +173 -0
- package/src/litellm/model-form.test.ts +228 -0
- package/src/litellm/model-form.ts +223 -0
- package/src/litellm/model-locate.test.ts +128 -0
- package/src/litellm/model-locate.ts +67 -0
- package/src/litellm/model-operations.test.ts +93 -0
- package/src/litellm/model-operations.ts +146 -0
- package/src/litellm/model-params-preserve.test.ts +119 -0
- package/src/litellm/model-recovery.test.ts +57 -0
- package/src/litellm/model-types.ts +112 -0
- package/src/litellm/model.test.ts +222 -0
- package/src/litellm/model.ts +237 -0
- package/src/litellm/policy-attachment-form.ts +103 -0
- package/src/litellm/policy-attachment-operations.ts +74 -0
- package/src/litellm/policy-attachment-types.ts +48 -0
- package/src/litellm/policy-attachment.test.ts +198 -0
- package/src/litellm/policy-attachment.ts +178 -0
- package/src/litellm/policy-form.ts +122 -0
- package/src/litellm/policy-operations.ts +112 -0
- package/src/litellm/policy-types.ts +58 -0
- package/src/litellm/policy.test.ts +214 -0
- package/src/litellm/policy.ts +146 -0
- package/src/litellm/providers.ts +24 -3
- package/src/litellm/registry-errors.ts +88 -0
- package/src/litellm/registry-exports.test.ts +66 -0
- package/src/litellm/registry-support.ts +109 -0
- package/src/litellm/team-form.ts +140 -0
- package/src/litellm/team-members.test.ts +150 -0
- package/src/litellm/team-members.ts +58 -0
- package/src/litellm/team-operations.ts +90 -0
- package/src/litellm/team-permission.ts +159 -0
- package/src/litellm/team-read.test.ts +89 -0
- package/src/litellm/team-types.ts +106 -0
- package/src/litellm/team.test.ts +206 -0
- package/src/litellm/team.ts +159 -0
- package/src/litellm/tool-policy-form.ts +94 -0
- package/src/litellm/tool-policy-operations.ts +58 -0
- package/src/litellm/tool-policy-types.ts +48 -0
- package/src/litellm/tool-policy.test.ts +172 -0
- package/src/litellm/tool-policy.ts +125 -0
- package/src/litellm/toolset-form.ts +104 -0
- package/src/litellm/toolset-operations.ts +90 -0
- package/src/litellm/toolset-types.ts +47 -0
- package/src/litellm/toolset.test.ts +198 -0
- package/src/litellm/toolset.ts +151 -0
- package/src/ownership/ownership.test.ts +33 -1
- package/src/ownership/rows.ts +53 -0
- package/src/postgres/connection.ts +42 -7
- package/src/postgres/database-bigint-serialization.test.ts +1 -0
- package/src/postgres/database-refusals.test.ts +2 -2
- package/src/postgres/database-sql.ts +36 -9
- package/src/postgres/fake-grants-parse.ts +189 -0
- package/src/postgres/fake-grants-read.ts +250 -0
- package/src/postgres/fake-grants-sql.ts +207 -0
- package/src/postgres/fake-grants-write.ts +90 -0
- package/src/postgres/fake-role-sql.ts +240 -0
- package/src/postgres/fake-schema-semantics.test.ts +36 -0
- package/src/postgres/fake-schema-sql.ts +146 -0
- package/src/postgres/fake-sql-parse.ts +86 -0
- package/src/postgres/fake-sql-quote.ts +36 -0
- package/src/postgres/fake-sql.ts +166 -46
- package/src/postgres/fixtures/pg-REL_18_6-acl.h.txt +147 -0
- package/src/postgres/fixtures/pg-REL_18_6-aclexplode-func.txt +31 -0
- package/src/postgres/fixtures/pg-REL_18_6-alter-default-privileges-synopsis.txt +82 -0
- package/src/postgres/fixtures/pg-REL_18_6-alter_role.sgml.txt +44 -0
- package/src/postgres/fixtures/pg-REL_18_6-create_role.sgml.txt +26 -0
- package/src/postgres/fixtures/pg-REL_18_6-grant-synopsis.txt +95 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_attribute.h.txt +24 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_auth_members.h.txt +62 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_authid.h.txt +70 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_class.h.txt +33 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_default_acl.h.txt +81 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_namespace.h.txt +28 -0
- package/src/postgres/fixtures/pg-REL_18_6-revoke-synopsis.txt +124 -0
- package/src/postgres/grants-acl.test.ts +61 -0
- package/src/postgres/grants-attrs.ts +185 -0
- package/src/postgres/grants-database.test.ts +158 -0
- package/src/postgres/grants-declare.ts +172 -0
- package/src/postgres/grants-delete-proof.test.ts +154 -0
- package/src/postgres/grants-diff.ts +101 -0
- package/src/postgres/grants-errors.ts +215 -0
- package/src/postgres/grants-existence-guards.test.ts +113 -0
- package/src/postgres/grants-existence.ts +101 -0
- package/src/postgres/grants-lifecycle-guards.test.ts +208 -0
- package/src/postgres/grants-lifecycle-repair.test.ts +208 -0
- package/src/postgres/grants-lifecycle.test.ts +181 -0
- package/src/postgres/grants-ops.ts +229 -0
- package/src/postgres/grants-owner-revoke.test.ts +121 -0
- package/src/postgres/grants-ownership.ts +49 -0
- package/src/postgres/grants-plan-collateral.test.ts +202 -0
- package/src/postgres/grants-plan-diff.test.ts +204 -0
- package/src/postgres/grants-plan-restore.ts +43 -0
- package/src/postgres/grants-plan-revocations.test.ts +69 -0
- package/src/postgres/grants-plan.test.ts +148 -0
- package/src/postgres/grants-plan.ts +245 -0
- package/src/postgres/grants-provenance.test.ts +134 -0
- package/src/postgres/grants-psql-lifecycle.test.ts +105 -0
- package/src/postgres/grants-read-absent.test.ts +91 -0
- package/src/postgres/grants-read-defaults.ts +51 -0
- package/src/postgres/grants-read.ts +250 -0
- package/src/postgres/grants-refusals.test.ts +136 -0
- package/src/postgres/grants-refuse.ts +119 -0
- package/src/postgres/grants-relkind-wire.test.ts +111 -0
- package/src/postgres/grants-repair-plan.test.ts +201 -0
- package/src/postgres/grants-sql.test.ts +128 -0
- package/src/postgres/grants-sql.ts +144 -0
- package/src/postgres/grants-words.ts +34 -0
- package/src/postgres/grants.ts +110 -0
- package/src/postgres/index.ts +129 -5
- package/src/postgres/installed-classifier.ts +36 -0
- package/src/postgres/providers.ts +33 -9
- package/src/postgres/psql-executor-escaping.test.ts +67 -0
- package/src/postgres/psql-executor.test.ts +63 -2
- package/src/postgres/psql-executor.ts +74 -34
- package/src/postgres/role-adopt.test.ts +98 -0
- package/src/postgres/role-attrs.ts +137 -0
- package/src/postgres/role-diff.ts +93 -0
- package/src/postgres/role-errors.ts +206 -0
- package/src/postgres/role-guard.test.ts +60 -0
- package/src/postgres/role-identity.test.ts +197 -0
- package/src/postgres/role-lifecycle.test.ts +228 -0
- package/src/postgres/role-membership-options.test.ts +73 -0
- package/src/postgres/role-membership-repair.test.ts +153 -0
- package/src/postgres/role-membership-sql.ts +223 -0
- package/src/postgres/role-password-ascii.test.ts +53 -0
- package/src/postgres/role-password-transports.test.ts +123 -0
- package/src/postgres/role-password.test.ts +158 -0
- package/src/postgres/role-privilege.test.ts +107 -0
- package/src/postgres/role-provenance.test.ts +200 -0
- package/src/postgres/role-provider.ts +105 -0
- package/src/postgres/role-refusals.test.ts +225 -0
- package/src/postgres/role-scram.test.ts +38 -0
- package/src/postgres/role-scram.ts +40 -0
- package/src/postgres/role-secrets.ts +61 -0
- package/src/postgres/role-sql.ts +213 -0
- package/src/postgres/role-valid-until.test.ts +68 -0
- package/src/postgres/role.ts +226 -0
- package/src/postgres/schema-assert.ts +139 -0
- package/src/postgres/schema-attrs.ts +71 -0
- package/src/postgres/schema-delete-proofs.test.ts +192 -0
- package/src/postgres/schema-delete.ts +96 -0
- package/src/postgres/schema-diff.ts +44 -0
- package/src/postgres/schema-drop-2bp01.test.ts +146 -0
- package/src/postgres/schema-errors.ts +209 -0
- package/src/postgres/schema-handlers.test.ts +193 -0
- package/src/postgres/schema-identity.ts +39 -0
- package/src/postgres/schema-lifecycle.test.ts +246 -0
- package/src/postgres/schema-race.test.ts +70 -0
- package/src/postgres/schema-reconcile.ts +100 -0
- package/src/postgres/schema-refusals.test.ts +144 -0
- package/src/postgres/schema-socket.test.ts +162 -0
- package/src/postgres/schema-sql.ts +130 -0
- package/src/postgres/schema-takeover.test.ts +149 -0
- package/src/postgres/schema-test-kit.ts +85 -0
- package/src/postgres/schema.ts +130 -0
- package/src/release/binary-alias.test.ts +3 -3
- package/src/release/binary-claims.test.ts +3 -3
- package/src/release/binary-directory-umask.test.ts +65 -0
- package/src/release/fake-engine.ts +15 -0
- package/src/valkey/acl-attrs.ts +98 -0
- package/src/valkey/acl-compare.ts +57 -0
- package/src/valkey/acl-connection.ts +13 -0
- package/src/valkey/acl-delete.test.ts +127 -0
- package/src/valkey/acl-form.ts +239 -0
- package/src/valkey/acl-managed.ts +13 -0
- package/src/valkey/acl-ops.ts +122 -0
- package/src/valkey/acl-ownership.test.ts +118 -0
- package/src/valkey/acl-prefix.test.ts +38 -0
- package/src/valkey/acl-profiles.ts +81 -0
- package/src/valkey/acl-reconcile.ts +180 -0
- package/src/valkey/acl-reserved.test.ts +69 -0
- package/src/valkey/acl-revocation.test.ts +94 -0
- package/src/valkey/acl-seat-prefix.test.ts +111 -0
- package/src/valkey/acl-validation.test.ts +54 -0
- package/src/valkey/acl.test.ts +215 -0
- package/src/valkey/acl.ts +169 -0
- package/src/valkey/connection-service.ts +46 -0
- package/src/valkey/connection.ts +180 -0
- package/src/valkey/errors.ts +239 -0
- package/src/valkey/fake-acl.ts +128 -0
- package/src/valkey/fake-valkey.ts +224 -0
- package/src/valkey/handler-test-fixture.ts +43 -0
- package/src/valkey/handlers.test.ts +205 -0
- package/src/valkey/index.ts +54 -0
- package/src/valkey/instance-attrs.ts +48 -0
- package/src/valkey/instance-form.ts +104 -0
- package/src/valkey/instance.test.ts +108 -0
- package/src/valkey/instance.ts +160 -0
- package/src/valkey/provider-socket-fixture.ts +71 -0
- package/src/valkey/providers.test.ts +129 -0
- package/src/valkey/providers.ts +21 -0
- package/src/valkey/resp.ts +212 -0
- package/src/valkey/transport-bounds.test.ts +150 -0
- package/src/valkey/transport-error.ts +37 -0
- package/src/valkey/transport.test.ts +158 -0
- package/src/valkey/transport.ts +109 -0
- package/src/valkey/upstream-bounds.test.ts +14 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `reconcile`, `read`, and `drop` for `Postgres.Schema` against `fake-sql.ts`'s recording fake.
|
|
3
|
+
* Tests the greenfield create, comment creation, already-present/drift, owner-missing guard,
|
|
4
|
+
* quoting, the safe-drop rules, and the `current_database()` proof every write path runs first.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, expect, test } from 'bun:test';
|
|
7
|
+
import * as Effect from 'effect/Effect';
|
|
8
|
+
import { SqlError, UnknownError } from 'effect/unstable/sql/SqlError';
|
|
9
|
+
import { makeFakeSql } from './fake-sql.ts';
|
|
10
|
+
import { type PostgresSchemaAttributes, type PostgresSchemaProps } from './schema-attrs.ts';
|
|
11
|
+
import type { PgExecutor } from './database-sql.ts';
|
|
12
|
+
import {
|
|
13
|
+
PostgresSchemaCreateVanished,
|
|
14
|
+
PostgresSchemaDrift,
|
|
15
|
+
PostgresSchemaDropNotEmptyError,
|
|
16
|
+
PostgresSchemaExistsRefused,
|
|
17
|
+
PostgresSchemaOwnerMissing,
|
|
18
|
+
PostgresSchemaWrongDatabase,
|
|
19
|
+
} from './schema-errors.ts';
|
|
20
|
+
import { dropWithClient, readWithClient, reconcileWithClient } 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
|
+
|
|
27
|
+
const liveRow = (over: Partial<PostgresSchemaAttributes> = {}): PostgresSchemaAttributes => ({
|
|
28
|
+
name: 'ledger',
|
|
29
|
+
database: 'postgres',
|
|
30
|
+
oid: 30000,
|
|
31
|
+
owner: 'tim',
|
|
32
|
+
comment: null,
|
|
33
|
+
...over,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('the current_database() proof', () => {
|
|
37
|
+
test('reconcile refuses a declared database the connection does not answer, before any write', async () => {
|
|
38
|
+
const fake = makeFakeSql({ roles: ['tim'] });
|
|
39
|
+
const error = await fails(reconcileWithClient(fake, { ...baseProps, database: 'agents' }));
|
|
40
|
+
expect(error).toBeInstanceOf(PostgresSchemaWrongDatabase);
|
|
41
|
+
const refusal = error as PostgresSchemaWrongDatabase;
|
|
42
|
+
expect(refusal.declared).toBe('agents');
|
|
43
|
+
expect(refusal.connected).toBe('postgres');
|
|
44
|
+
// 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(false);
|
|
46
|
+
expect(fake.statements.some((s) => s.text.startsWith('CREATE SCHEMA'))).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('drop refuses the same mismatch before any DROP or emptiness check', async () => {
|
|
50
|
+
const fake = makeFakeSql({ schemas: [liveRow()] });
|
|
51
|
+
const error = await fails(dropWithClient(fake, { ...baseProps, database: 'agents' }));
|
|
52
|
+
expect(error).toBeInstanceOf(PostgresSchemaWrongDatabase);
|
|
53
|
+
expect(fake.statements.some((s) => s.text.startsWith('DROP SCHEMA'))).toBe(false);
|
|
54
|
+
expect(fake.schemas.get('ledger')).not.toBeUndefined();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('every read row carries the connected database', async () => {
|
|
58
|
+
const fake = makeFakeSql({ schemas: [liveRow()] });
|
|
59
|
+
const row = await run(readWithClient(fake, 'ledger'));
|
|
60
|
+
expect(row?.database).toBe('postgres');
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('reconcile: greenfield', () => {
|
|
65
|
+
test('issues exactly one CREATE SCHEMA with an AUTHORIZATION clause', async () => {
|
|
66
|
+
const fake = makeFakeSql({ roles: ['tim'] });
|
|
67
|
+
const attrs = await run(reconcileWithClient(fake, baseProps));
|
|
68
|
+
expect(attrs.name).toBe('ledger');
|
|
69
|
+
expect(attrs.owner).toBe('tim');
|
|
70
|
+
const creates = fake.statements.filter((s) => s.text.startsWith('CREATE SCHEMA'));
|
|
71
|
+
expect(creates.length).toBe(1);
|
|
72
|
+
expect(creates[0]?.text).toBe('CREATE SCHEMA "ledger" AUTHORIZATION "tim"');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('no owner clause falls back to the executing role', async () => {
|
|
76
|
+
const fake = makeFakeSql({ roles: ['postgres'] });
|
|
77
|
+
const attrs = await run(reconcileWithClient(fake, { name: 'plain', database: 'postgres' }));
|
|
78
|
+
expect(attrs.owner).toBe('postgres');
|
|
79
|
+
const create = fake.statements.find((s) => s.text.startsWith('CREATE SCHEMA'));
|
|
80
|
+
expect(create?.text).toBe('CREATE SCHEMA "plain"');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('declared comment is issued with a single COMMENT ON SCHEMA statement', async () => {
|
|
84
|
+
const fake = makeFakeSql({ roles: ['tim'] });
|
|
85
|
+
const attrs = await run(reconcileWithClient(fake, { ...baseProps, comment: 'seat ledger' }));
|
|
86
|
+
expect(attrs.comment).toBe('seat ledger');
|
|
87
|
+
const comments = fake.statements.filter((s) => s.text.startsWith('COMMENT ON SCHEMA'));
|
|
88
|
+
expect(comments.length).toBe(1);
|
|
89
|
+
expect(comments[0]?.text).toBe('COMMENT ON SCHEMA "ledger" IS E\'seat ledger\'');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('a declared empty comment IS "no comment": no statement issued, no drift later', async () => {
|
|
93
|
+
const fake = makeFakeSql({ roles: ['tim'] });
|
|
94
|
+
const attrs = await run(reconcileWithClient(fake, { ...baseProps, comment: '' }));
|
|
95
|
+
expect(attrs.comment).toBeNull();
|
|
96
|
+
expect(fake.statements.some((s) => s.text.startsWith('COMMENT ON SCHEMA'))).toBe(false);
|
|
97
|
+
// Postgres stores IS E'' as NULL, so a second plan must stay a noop — the permanent drift
|
|
98
|
+
// loop an unnormalized '' used to cause. Reads (the current_database() and schema-row
|
|
99
|
+
// SELECT proofs) always run, so compare write statements only.
|
|
100
|
+
const writeCount = () =>
|
|
101
|
+
fake.statements.filter((s) => !/^\s*(SELECT|WITH)\b/i.test(s.text)).length;
|
|
102
|
+
const writesBefore = writeCount();
|
|
103
|
+
await run(reconcileWithClient(fake, { ...baseProps, comment: '' }, attrs));
|
|
104
|
+
expect(writeCount()).toBe(writesBefore);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('refuses before any CREATE when the owner role does not exist', async () => {
|
|
108
|
+
const fake = makeFakeSql({ roles: [] });
|
|
109
|
+
const error = await fails(reconcileWithClient(fake, baseProps));
|
|
110
|
+
expect(error).toBeInstanceOf(PostgresSchemaOwnerMissing);
|
|
111
|
+
expect(fake.statements.some((s) => s.text.startsWith('CREATE SCHEMA'))).toBe(false);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("fails with the vanish tag when the create's re-read finds nothing (S10)", async () => {
|
|
115
|
+
const fake = makeFakeSql({ roles: ['tim'], swallowNextCreateSchema: true });
|
|
116
|
+
const error = await fails(reconcileWithClient(fake, baseProps));
|
|
117
|
+
expect(error).toBeInstanceOf(PostgresSchemaCreateVanished);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('a concurrent creator that won the IF NOT EXISTS race is refused, not adopted', async () => {
|
|
121
|
+
const fake = makeFakeSql({ roles: ['tim'], raceNextCreateSchema: 'someone-else' });
|
|
122
|
+
const error = await fails(reconcileWithClient(fake, baseProps));
|
|
123
|
+
expect(error._tag).toBe('PostgresSchemaExistsRefused');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('hostile names are quoted exactly: embedded quote and a hyphen', async () => {
|
|
127
|
+
const fake = makeFakeSql({ roles: ['tim'] });
|
|
128
|
+
await run(reconcileWithClient(fake, { name: 'a"b-c', database: 'postgres', owner: 'tim' }));
|
|
129
|
+
const create = fake.statements.find((s) => s.text.startsWith('CREATE SCHEMA'));
|
|
130
|
+
expect(create?.text).toBe('CREATE SCHEMA "a""b-c" AUTHORIZATION "tim"');
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe('reconcile: already present', () => {
|
|
135
|
+
test('adopted output with no drift records zero write statements', async () => {
|
|
136
|
+
const fake = makeFakeSql({ roles: ['tim'], schemas: [liveRow()] });
|
|
137
|
+
const attrs = await run(reconcileWithClient(fake, baseProps, liveRow()));
|
|
138
|
+
expect(attrs).toEqual(liveRow());
|
|
139
|
+
expect(
|
|
140
|
+
fake.statements.every(
|
|
141
|
+
(s) => !s.text.startsWith('CREATE SCHEMA') && !s.text.startsWith('COMMENT ON SCHEMA'),
|
|
142
|
+
),
|
|
143
|
+
).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('an owner mismatch fails with the typed drift tag and issues no ALTER', async () => {
|
|
147
|
+
const fake = makeFakeSql({
|
|
148
|
+
roles: ['tim', 'someone-else'],
|
|
149
|
+
schemas: [liveRow({ owner: 'someone-else' })],
|
|
150
|
+
});
|
|
151
|
+
const error = await fails(reconcileWithClient(fake, baseProps, liveRow()));
|
|
152
|
+
expect(error).toBeInstanceOf(PostgresSchemaDrift);
|
|
153
|
+
expect((error as PostgresSchemaDrift).prop).toBe('owner');
|
|
154
|
+
expect(fake.statements.some((s) => s.text.startsWith('ALTER'))).toBe(false);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('a comment mismatch fails with the typed drift tag naming "comment"', async () => {
|
|
158
|
+
const fake = makeFakeSql({ roles: ['tim'], schemas: [liveRow({ comment: 'old comment' })] });
|
|
159
|
+
const error = await fails(
|
|
160
|
+
reconcileWithClient(fake, { ...baseProps, comment: 'new comment' }, liveRow()),
|
|
161
|
+
);
|
|
162
|
+
expect(error).toBeInstanceOf(PostgresSchemaDrift);
|
|
163
|
+
expect((error as PostgresSchemaDrift).prop).toBe('comment');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test('a missing comment declaration does not drift against an existing comment', async () => {
|
|
167
|
+
const fake = makeFakeSql({ roles: ['tim'], schemas: [liveRow({ comment: 'legacy' })] });
|
|
168
|
+
const attrs = await run(reconcileWithClient(fake, baseProps, liveRow()));
|
|
169
|
+
expect(attrs.comment).toBe('legacy');
|
|
170
|
+
expect(fake.statements.some((s) => s.text.startsWith('COMMENT'))).toBe(false);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test('pre-existing same-owner schema without output is refused', async () => {
|
|
175
|
+
const fake = makeFakeSql({ schemas: [liveRow()], schemasWithRelations: ['ledger'] });
|
|
176
|
+
const error = await fails(reconcileWithClient(fake, baseProps));
|
|
177
|
+
expect(error).toBeInstanceOf(PostgresSchemaExistsRefused);
|
|
178
|
+
expect(fake.schemas.get('ledger')).toEqual(liveRow());
|
|
179
|
+
expect(fake.relationsIn.has('ledger')).toBe(true);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('read', () => {
|
|
183
|
+
test('answers undefined when the schema is absent', async () => {
|
|
184
|
+
const fake = makeFakeSql();
|
|
185
|
+
expect(await run(readWithClient(fake, 'ledger'))).toBeUndefined();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("answers the live row when present (ownership branding is the caller's job)", async () => {
|
|
189
|
+
const fake = makeFakeSql({ schemas: [liveRow()] });
|
|
190
|
+
expect(await run(readWithClient(fake, 'ledger'))).toEqual(liveRow());
|
|
191
|
+
});
|
|
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
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `IF NOT EXISTS` race the lifecycle test does not pin: a concurrent creator wins
|
|
3
|
+
* between the not-found SELECT and CREATE SCHEMA. Plain CREATE now refuses it with 42P06.
|
|
4
|
+
*
|
|
5
|
+
* Two shapes the existing race test skips. A declared comment must not `COMMENT ON` that
|
|
6
|
+
* row before the refusal — the kit role is superuser, so the comment would land and
|
|
7
|
+
* a failed effect would not roll it back. An omitted `owner` must not adopt the foreign
|
|
8
|
+
* row: a fresh CREATE without AUTHORIZATION is owned by current_user. Even a matching owner
|
|
9
|
+
* now fails with PostgresSchemaExistsRefused, never a row a later destroy could drop.
|
|
10
|
+
*/
|
|
11
|
+
import { describe, expect, test } from 'bun:test';
|
|
12
|
+
import * as Effect from 'effect/Effect';
|
|
13
|
+
import { makeFakeSql } from './fake-sql.ts';
|
|
14
|
+
import { PostgresSchemaExistsRefused } from './schema-errors.ts';
|
|
15
|
+
import { reconcileWithClient } from './schema.ts';
|
|
16
|
+
|
|
17
|
+
const fails = <A, E>(eff: Effect.Effect<A, E>): Promise<E> => Effect.runPromise(Effect.flip(eff));
|
|
18
|
+
|
|
19
|
+
describe('IF NOT EXISTS race, the shapes the owner-only test skips', () => {
|
|
20
|
+
test('a race plus a declared comment issues zero COMMENT ON', async () => {
|
|
21
|
+
const fake = makeFakeSql({ roles: ['seat_role'], raceNextCreateSchema: 'other_role' });
|
|
22
|
+
const error = await fails(
|
|
23
|
+
reconcileWithClient(fake, {
|
|
24
|
+
name: 'ledger',
|
|
25
|
+
database: 'postgres',
|
|
26
|
+
owner: 'seat_role',
|
|
27
|
+
comment: 'seat ledger',
|
|
28
|
+
}),
|
|
29
|
+
);
|
|
30
|
+
expect(error).toBeInstanceOf(PostgresSchemaExistsRefused);
|
|
31
|
+
expect(fake.statements.some((s) => s.text.startsWith('COMMENT ON SCHEMA'))).toBe(false);
|
|
32
|
+
// The foreign row is untouched: comment still absent, owner still the racer's.
|
|
33
|
+
expect(fake.schemas.get('ledger')?.comment).toBeNull();
|
|
34
|
+
expect(fake.schemas.get('ledger')?.owner).toBe('other_role');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('a race plus an omitted owner refuses takeover and does not return the foreign row', async () => {
|
|
38
|
+
// `current_user` is `seat_role`, not the fake's historical `'postgres'` default, so a
|
|
39
|
+
// hardcoded owner cannot satisfy `declared`.
|
|
40
|
+
const fake = makeFakeSql({
|
|
41
|
+
roles: ['seat_role'],
|
|
42
|
+
raceNextCreateSchema: 'other_role',
|
|
43
|
+
currentUser: 'seat_role',
|
|
44
|
+
});
|
|
45
|
+
const error = await fails(reconcileWithClient(fake, { name: 'ledger', database: 'postgres' }));
|
|
46
|
+
expect(error).toBeInstanceOf(PostgresSchemaExistsRefused);
|
|
47
|
+
expect(fake.schemas.get('ledger')?.owner).toBe('other_role');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('a later plan sees the foreign row as already present and still refuses it', async () => {
|
|
51
|
+
// The failed race leaves the racer's schema in place. The next reconcile takes the
|
|
52
|
+
// already-present path, which must refuse too — otherwise that plan adopts the row.
|
|
53
|
+
const fake = makeFakeSql({
|
|
54
|
+
currentUser: 'seat_role',
|
|
55
|
+
schemas: [
|
|
56
|
+
{
|
|
57
|
+
name: 'ledger',
|
|
58
|
+
database: 'postgres',
|
|
59
|
+
oid: 30000,
|
|
60
|
+
owner: 'other_role',
|
|
61
|
+
comment: null,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
});
|
|
65
|
+
const error = await fails(reconcileWithClient(fake, { name: 'ledger', database: 'postgres' }));
|
|
66
|
+
expect(error).toBeInstanceOf(PostgresSchemaExistsRefused);
|
|
67
|
+
expect(fake.statements.some((s) => s.text.startsWith('CREATE SCHEMA'))).toBe(false);
|
|
68
|
+
expect(fake.statements.some((s) => s.text.startsWith('COMMENT ON SCHEMA'))).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/** Schema reconcile: prove the stored target and identity before asserting a live row. */
|
|
2
|
+
import * as Effect from 'effect/Effect';
|
|
3
|
+
import type { SqlError } from 'effect/unstable/sql/SqlError';
|
|
4
|
+
import { type PgExecutor, roleExists } from './database-sql.ts';
|
|
5
|
+
import {
|
|
6
|
+
type PostgresSchemaAttributes,
|
|
7
|
+
type PostgresSchemaProps,
|
|
8
|
+
normalizedComment,
|
|
9
|
+
} from './schema-attrs.ts';
|
|
10
|
+
import { assertDatabase, assertLive, assertOwner } from './schema-assert.ts';
|
|
11
|
+
import { assertSchemaIdentity, assertSchemaTarget } from './schema-identity.ts';
|
|
12
|
+
import {
|
|
13
|
+
buildCommentSchemaSql,
|
|
14
|
+
buildCreateSchemaSql,
|
|
15
|
+
currentUser,
|
|
16
|
+
isDuplicateSchemaError,
|
|
17
|
+
selectSchema,
|
|
18
|
+
} from './schema-sql.ts';
|
|
19
|
+
import {
|
|
20
|
+
PostgresSchemaCreateVanished,
|
|
21
|
+
type PostgresSchemaDatabaseRefused,
|
|
22
|
+
type PostgresSchemaDrift,
|
|
23
|
+
PostgresSchemaExistsRefused,
|
|
24
|
+
type PostgresSchemaIdentityRefused,
|
|
25
|
+
PostgresSchemaOwnerMissing,
|
|
26
|
+
type PostgresSchemaRenameRefused,
|
|
27
|
+
type PostgresSchemaWrongDatabase,
|
|
28
|
+
} from './schema-errors.ts';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The core of `reconcile`, against any {@link PgExecutor} — the real pooled client through
|
|
32
|
+
* `withPg`, or `fake-sql.ts`'s recording fake in tests.
|
|
33
|
+
*/
|
|
34
|
+
export const reconcileWithClient = (
|
|
35
|
+
pg: PgExecutor,
|
|
36
|
+
props: PostgresSchemaProps,
|
|
37
|
+
output?: PostgresSchemaAttributes,
|
|
38
|
+
): Effect.Effect<
|
|
39
|
+
PostgresSchemaAttributes,
|
|
40
|
+
| PostgresSchemaWrongDatabase
|
|
41
|
+
| PostgresSchemaOwnerMissing
|
|
42
|
+
| PostgresSchemaDrift
|
|
43
|
+
| PostgresSchemaCreateVanished
|
|
44
|
+
| PostgresSchemaExistsRefused
|
|
45
|
+
| PostgresSchemaIdentityRefused
|
|
46
|
+
| PostgresSchemaRenameRefused
|
|
47
|
+
| PostgresSchemaDatabaseRefused
|
|
48
|
+
| SqlError
|
|
49
|
+
> =>
|
|
50
|
+
Effect.gen(function* () {
|
|
51
|
+
yield* assertSchemaTarget(props, output);
|
|
52
|
+
yield* assertDatabase(props, pg);
|
|
53
|
+
// Only an omitted owner needs the session role: a declared owner is the comparison itself.
|
|
54
|
+
const executingRole = props.owner !== undefined ? props.owner : yield* currentUser(pg);
|
|
55
|
+
const observed = yield* selectSchema(pg, props.name);
|
|
56
|
+
if (observed === undefined) {
|
|
57
|
+
if (props.owner !== undefined) {
|
|
58
|
+
const ownerPresent = yield* roleExists(pg, props.owner);
|
|
59
|
+
if (!ownerPresent) {
|
|
60
|
+
return yield* Effect.fail(
|
|
61
|
+
new PostgresSchemaOwnerMissing({ schema: props.name, owner: props.owner }),
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// REL_18_6 pg_namespace.c: duplicate_schema is 42P06. Never turn a race into adoption.
|
|
66
|
+
const createRefusal = (error: SqlError): PostgresSchemaExistsRefused | SqlError =>
|
|
67
|
+
isDuplicateSchemaError(error)
|
|
68
|
+
? new PostgresSchemaExistsRefused({ schema: props.name, database: props.database })
|
|
69
|
+
: error;
|
|
70
|
+
yield* pg.unsafe(buildCreateSchemaSql(props)).pipe(
|
|
71
|
+
Effect.asVoid,
|
|
72
|
+
Effect.catchTag('SqlError', (error) => Effect.fail(createRefusal(error))),
|
|
73
|
+
);
|
|
74
|
+
// Never trust the write's own report (S10): re-read what the server actually stored.
|
|
75
|
+
const created = yield* selectSchema(pg, props.name);
|
|
76
|
+
if (created === undefined) {
|
|
77
|
+
return yield* Effect.fail(new PostgresSchemaCreateVanished({ schema: props.name }));
|
|
78
|
+
}
|
|
79
|
+
// Owner before COMMENT ON (schema-assert.ts): preserve the post-write assertion too;
|
|
80
|
+
// duplicate CREATE already refused the race before this re-read.
|
|
81
|
+
yield* assertOwner(props, created, executingRole);
|
|
82
|
+
const comment = normalizedComment(props.comment);
|
|
83
|
+
if (comment !== undefined && comment !== (created.comment ?? undefined)) {
|
|
84
|
+
yield* pg.unsafe(buildCommentSchemaSql(props.name, comment)).pipe(Effect.asVoid);
|
|
85
|
+
const commented = yield* selectSchema(pg, props.name);
|
|
86
|
+
if (commented === undefined) {
|
|
87
|
+
return yield* Effect.fail(new PostgresSchemaCreateVanished({ schema: props.name }));
|
|
88
|
+
}
|
|
89
|
+
return yield* assertLive(props, commented, executingRole);
|
|
90
|
+
}
|
|
91
|
+
return yield* assertLive(props, created, executingRole);
|
|
92
|
+
}
|
|
93
|
+
if (output === undefined) {
|
|
94
|
+
return yield* Effect.fail(
|
|
95
|
+
new PostgresSchemaExistsRefused({ schema: props.name, database: props.database }),
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
yield* assertSchemaIdentity(observed, output);
|
|
99
|
+
return yield* assertLive(props, observed, executingRole);
|
|
100
|
+
});
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every plan-time refusal for `Postgres.Schema`, checked without a client: name-byte
|
|
3
|
+
* precision, identifier and literal quoting, the rename and database-move refusals, and the
|
|
4
|
+
* sweep proving `diff` never answers `replace` — including the `cascade` flip compared against
|
|
5
|
+
* `olds` so the change reaches the persisted props. The real handlers over the runner transport
|
|
6
|
+
* live in `schema-handlers.test.ts`.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, expect, test } from 'bun:test';
|
|
9
|
+
import * as Effect from 'effect/Effect';
|
|
10
|
+
import { POSTGRES_NAME_MAX_BYTES, utf8ByteLength } from './database-attrs.ts';
|
|
11
|
+
import { quoteIdent, quoteStringLiteral } from './database-sql.ts';
|
|
12
|
+
import { buildCommentSchemaSql, buildCreateSchemaSql } from './schema-sql.ts';
|
|
13
|
+
import { schemaNameByteRefusal } from './schema-attrs.ts';
|
|
14
|
+
import type { PostgresSchemaAttributes, PostgresSchemaProps } from './schema-attrs.ts';
|
|
15
|
+
import { diffPostgresSchema } from './schema-diff.ts';
|
|
16
|
+
import {
|
|
17
|
+
PostgresSchemaDatabaseRefused,
|
|
18
|
+
PostgresSchemaNameRefused,
|
|
19
|
+
PostgresSchemaRenameRefused,
|
|
20
|
+
} from './schema-errors.ts';
|
|
21
|
+
|
|
22
|
+
describe('name byte length', () => {
|
|
23
|
+
test('a 63-byte ASCII name is accepted', () => {
|
|
24
|
+
const name = 'a'.repeat(63);
|
|
25
|
+
expect(utf8ByteLength(name)).toBe(63);
|
|
26
|
+
expect(schemaNameByteRefusal(name)).toBeUndefined();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('a 64-byte ASCII name is refused', () => {
|
|
30
|
+
const name = 'a'.repeat(64);
|
|
31
|
+
const refusal = schemaNameByteRefusal(name);
|
|
32
|
+
expect(refusal).toEqual({ byteLength: 64, limit: POSTGRES_NAME_MAX_BYTES });
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('a 63-byte multibyte name (21 three-byte characters) is accepted, though .length is 21', () => {
|
|
36
|
+
// U+4E2D ("中") is 3 UTF-8 bytes. 21 * 3 = 63 bytes exactly — the last size that survives.
|
|
37
|
+
const name = '中'.repeat(21);
|
|
38
|
+
expect(name.length).toBe(21);
|
|
39
|
+
expect(utf8ByteLength(name)).toBe(63);
|
|
40
|
+
expect(schemaNameByteRefusal(name)).toBeUndefined();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('one more multibyte character (64 bytes) is refused', () => {
|
|
44
|
+
const name = `${'中'.repeat(21)}a`;
|
|
45
|
+
expect(utf8ByteLength(name)).toBe(64);
|
|
46
|
+
expect(schemaNameByteRefusal(name)).toEqual({ byteLength: 64, limit: POSTGRES_NAME_MAX_BYTES });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('the typed refusal names the truncation danger in its message', () => {
|
|
50
|
+
expect(
|
|
51
|
+
new PostgresSchemaNameRefused({ name: 'x', byteLength: 64, limit: 63 }).message,
|
|
52
|
+
).toContain('silently truncate');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('quoting', () => {
|
|
57
|
+
test('quoteIdent doubles an embedded double quote and leaves a hyphen alone', () => {
|
|
58
|
+
expect(quoteIdent('a"b-c')).toBe('"a""b-c"');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('quoteStringLiteral doubles an embedded single quote', () => {
|
|
62
|
+
expect(quoteStringLiteral("seat ledger'; DROP")).toBe("E'seat ledger''; DROP'");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('buildCreateSchemaSql never string-concatenates an unescaped value', () => {
|
|
66
|
+
const sql = buildCreateSchemaSql({ name: 'sc"1', database: 'postgres', owner: 'ro"le' });
|
|
67
|
+
expect(sql).toBe('CREATE SCHEMA "sc""1" AUTHORIZATION "ro""le"');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('buildCommentSchemaSql never string-concatenates an unescaped comment', () => {
|
|
71
|
+
const sql = buildCommentSchemaSql('sc"1', "it'; DROP");
|
|
72
|
+
expect(sql).toBe(`COMMENT ON SCHEMA "sc""1" IS E'it''; DROP'`);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const sampleOutput: PostgresSchemaAttributes = {
|
|
77
|
+
name: 'ledger',
|
|
78
|
+
database: 'postgres',
|
|
79
|
+
oid: 1,
|
|
80
|
+
owner: 'tim',
|
|
81
|
+
comment: null,
|
|
82
|
+
};
|
|
83
|
+
const base: PostgresSchemaProps = { name: 'ledger', database: 'postgres', owner: 'tim' };
|
|
84
|
+
|
|
85
|
+
describe('diff', () => {
|
|
86
|
+
test('a rename is refused at plan, before reconcile ever runs', async () => {
|
|
87
|
+
const error = await Effect.runPromise(
|
|
88
|
+
Effect.flip(diffPostgresSchema({ ...base, name: 'gadgets' }, sampleOutput, base)),
|
|
89
|
+
);
|
|
90
|
+
expect(error).toBeInstanceOf(PostgresSchemaRenameRefused);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('a database move is refused at plan, the same way a rename is', async () => {
|
|
94
|
+
const error = await Effect.runPromise(
|
|
95
|
+
Effect.flip(diffPostgresSchema({ ...base, database: 'agents' }, sampleOutput, base)),
|
|
96
|
+
);
|
|
97
|
+
expect(error).toBeInstanceOf(PostgresSchemaDatabaseRefused);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('an unchanged declaration answers noop', async () => {
|
|
101
|
+
const result = await Effect.runPromise(diffPostgresSchema(base, sampleOutput, base));
|
|
102
|
+
expect(result).toEqual({ action: 'noop' });
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('an unchanged cascade answers noop even when the previous declaration carried it', async () => {
|
|
106
|
+
const result = await Effect.runPromise(
|
|
107
|
+
diffPostgresSchema({ ...base, cascade: true }, sampleOutput, { ...base, cascade: true }),
|
|
108
|
+
);
|
|
109
|
+
expect(result).toEqual({ action: 'noop' });
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('a change in any single prop answers update, and NEVER replace — swept across every prop', async () => {
|
|
113
|
+
const variants: ReadonlyArray<Partial<PostgresSchemaProps>> = [
|
|
114
|
+
{ owner: 'someone-else' },
|
|
115
|
+
{ comment: 'changed' },
|
|
116
|
+
{ cascade: true },
|
|
117
|
+
];
|
|
118
|
+
for (const variant of variants) {
|
|
119
|
+
const result = await Effect.runPromise(
|
|
120
|
+
diffPostgresSchema({ ...base, ...variant }, sampleOutput, base),
|
|
121
|
+
);
|
|
122
|
+
expect(result?.action, `variant ${JSON.stringify(variant)}`).toBe('update');
|
|
123
|
+
expect(result).not.toEqual({ action: 'replace' });
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('a cascade flip answers update in BOTH directions — compared against olds, so the flip reaches the persisted props', async () => {
|
|
128
|
+
// The engine's noop branch commits the old props (Apply.ts), so a cascade flip that
|
|
129
|
+
// answered noop would leave the persisted props carrying the OLD value forever: a schema
|
|
130
|
+
// first declared cascade: true would keep dropping with CASCADE even after the
|
|
131
|
+
// declaration said false.
|
|
132
|
+
const down = await Effect.runPromise(
|
|
133
|
+
diffPostgresSchema(base, sampleOutput, { ...base, cascade: true }),
|
|
134
|
+
);
|
|
135
|
+
expect(down?.action).toBe('update');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('a declared empty comment diffs as "no comment", never as a change against a NULL', async () => {
|
|
139
|
+
const result = await Effect.runPromise(
|
|
140
|
+
diffPostgresSchema({ ...base, comment: '' }, sampleOutput, base),
|
|
141
|
+
);
|
|
142
|
+
expect(result).toEqual({ action: 'noop' });
|
|
143
|
+
});
|
|
144
|
+
});
|