@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,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The statement parser half of the `Postgres.Grants` test fake: it understands EXACTLY the
|
|
3
|
+
* ten statement shapes `grants-sql.ts` generates — `quoteIdent`'s quoting, one clause
|
|
4
|
+
* order — and nothing else, so a quoting or clause-order bug in a builder trips the parser
|
|
5
|
+
* instead of hiding behind lenient matching. The server-model half (entries, grant/revoke
|
|
6
|
+
* semantics, the aclexplode-shaped reads) lives in `fake-grants-sql.ts`.
|
|
7
|
+
*
|
|
8
|
+
* ⛔ IT PARSES ITS OWN OUTPUT, NOT SQL IN GENERAL (`fake-sql.ts`'s own rule): every value
|
|
9
|
+
* the builders `quoteIdent` is read back through the same `"…""…"` unquoting, and every
|
|
10
|
+
* statement ends in exactly one of the accepted shapes or the parse refuses.
|
|
11
|
+
* ⛔ A COLUMN GRANT CARRIES ONE COLUMN LIST PER WORD — `GRANT select ("id"), update ("id")`:
|
|
12
|
+
* a single trailing list binds only to the privilege it follows (gram.y@REL_18_6), so
|
|
13
|
+
* `GRANT select, update ("id")` is a table-level SELECT on a real server. The parser
|
|
14
|
+
* refuses that shape instead of filing it as a column-only grant.
|
|
15
|
+
*/
|
|
16
|
+
export interface AclObject {
|
|
17
|
+
readonly schema: string;
|
|
18
|
+
readonly table?: string;
|
|
19
|
+
readonly column?: string;
|
|
20
|
+
readonly defaultFor?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** One parsed write statement, already reduced to what the model applies. */
|
|
24
|
+
export interface ParsedWrite {
|
|
25
|
+
readonly kind: 'grant' | 'revoke';
|
|
26
|
+
/** `REVOKE ALL ON ALL TABLES IN SCHEMA … FROM PUBLIC` — applied per known relation. */
|
|
27
|
+
readonly allTables?: boolean;
|
|
28
|
+
readonly object: AclObject;
|
|
29
|
+
/** `'PUBLIC'` or a role name. */
|
|
30
|
+
readonly grantee: string;
|
|
31
|
+
/** Declared privilege words, lowercase, no `*` marks (the option clause carries the flag). */
|
|
32
|
+
readonly words: ReadonlyArray<string>;
|
|
33
|
+
readonly option: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const unquote = (raw: string): string => raw.replace(/""/g, '"');
|
|
37
|
+
|
|
38
|
+
const IDENT = String.raw`"((?:[^"]|"")*)"`;
|
|
39
|
+
const WORDS = String.raw`([a-z]+(?:, [a-z]+)*)`;
|
|
40
|
+
/** One column-grant atom, `word ("col")` — the shape every builder column statement is made of. */
|
|
41
|
+
const SYNOPSIS = String.raw`[a-z]+ \("(?:[^"]|"")*"\)`;
|
|
42
|
+
const GRANTEE = String.raw`(?:"((?:[^"]|"")*)"|PUBLIC)`;
|
|
43
|
+
const OPTION = String.raw`(?:( WITH GRANT OPTION))?`;
|
|
44
|
+
|
|
45
|
+
interface Pattern {
|
|
46
|
+
readonly re: RegExp;
|
|
47
|
+
/** `undefined` when the regex matched but the shape is one no builder emits — the loop
|
|
48
|
+
* falls through and the parse refuses (a builder regression, not a tolerated statement). */
|
|
49
|
+
readonly parse: (m: RegExpExecArray) => Omit<ParsedWrite, 'kind'> | undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const role = (raw: string | undefined): string => (raw === undefined ? 'PUBLIC' : unquote(raw));
|
|
53
|
+
|
|
54
|
+
/** Ordered: a column grant/revoke must be tried before the table form its text starts like,
|
|
55
|
+
* and the two default-privileges forms before the plain ones they begin as. */
|
|
56
|
+
const PATTERNS: ReadonlyArray<Pattern & { readonly kind: 'grant' | 'revoke' }> = [
|
|
57
|
+
{
|
|
58
|
+
kind: 'grant',
|
|
59
|
+
re: new RegExp(
|
|
60
|
+
`^ALTER DEFAULT PRIVILEGES FOR ROLE ${IDENT} IN SCHEMA ${IDENT} GRANT ${WORDS} ON TABLES TO ${GRANTEE}${OPTION}$`,
|
|
61
|
+
),
|
|
62
|
+
parse: (m) => ({
|
|
63
|
+
object: { schema: unquote(m[2] as string), defaultFor: unquote(m[1] as string) },
|
|
64
|
+
grantee: role(m[4] as string | undefined),
|
|
65
|
+
words: (m[3] as string).split(', '),
|
|
66
|
+
option: m[5] !== undefined,
|
|
67
|
+
}),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
kind: 'revoke',
|
|
71
|
+
re: new RegExp(
|
|
72
|
+
`^ALTER DEFAULT PRIVILEGES FOR ROLE ${IDENT} IN SCHEMA ${IDENT} REVOKE ALL ON TABLES FROM ${GRANTEE}$`,
|
|
73
|
+
),
|
|
74
|
+
parse: (m) => ({
|
|
75
|
+
object: { schema: unquote(m[2] as string), defaultFor: unquote(m[1] as string) },
|
|
76
|
+
grantee: role(m[3] as string | undefined),
|
|
77
|
+
words: [],
|
|
78
|
+
option: false,
|
|
79
|
+
}),
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
kind: 'grant',
|
|
83
|
+
// ⛔ Every word carries its OWN column list — `GRANT select ("id"), update ("id") …`.
|
|
84
|
+
// A single trailing list binds only to the privilege it follows (gram.y@REL_18_6), so
|
|
85
|
+
// `GRANT select, update ("id")` is a table-level SELECT on a real server; the parser
|
|
86
|
+
// refuses that shape instead of filing it as a column-only grant (a builder that
|
|
87
|
+
// emits it would over-grant at the table level while the suite stays green).
|
|
88
|
+
re: new RegExp(
|
|
89
|
+
`^GRANT (${SYNOPSIS}(?:, ${SYNOPSIS})*) ON ${IDENT}\\.${IDENT} TO ${GRANTEE}${OPTION}$`,
|
|
90
|
+
),
|
|
91
|
+
parse: (m) => {
|
|
92
|
+
const pairs = [...(m[1] as string).matchAll(/([a-z]+) \("((?:[^"]|"")*)"\)/g)];
|
|
93
|
+
const first = pairs[0];
|
|
94
|
+
if (first === undefined) return undefined;
|
|
95
|
+
const column = unquote(first[2] as string);
|
|
96
|
+
// One statement grants ONE column: every synopsis pair must name the same one (the
|
|
97
|
+
// builders emit one column per statement) — mixed columns are a foreign shape.
|
|
98
|
+
if (pairs.some(([, , raw]) => unquote(raw as string) !== column)) return undefined;
|
|
99
|
+
return {
|
|
100
|
+
object: {
|
|
101
|
+
schema: unquote(m[2] as string),
|
|
102
|
+
table: unquote(m[3] as string),
|
|
103
|
+
column,
|
|
104
|
+
},
|
|
105
|
+
grantee: role(m[4] as string | undefined),
|
|
106
|
+
words: pairs.map(([, word]) => word as string),
|
|
107
|
+
option: m[5] !== undefined,
|
|
108
|
+
};
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
kind: 'revoke',
|
|
113
|
+
re: new RegExp(`^REVOKE ALL \\(${IDENT}\\) ON ${IDENT}\\.${IDENT} FROM ${GRANTEE}$`),
|
|
114
|
+
parse: (m) => ({
|
|
115
|
+
object: {
|
|
116
|
+
schema: unquote(m[2] as string),
|
|
117
|
+
table: unquote(m[3] as string),
|
|
118
|
+
column: unquote(m[1] as string),
|
|
119
|
+
},
|
|
120
|
+
grantee: role(m[4] as string | undefined),
|
|
121
|
+
words: [],
|
|
122
|
+
option: false,
|
|
123
|
+
}),
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
kind: 'grant',
|
|
127
|
+
re: new RegExp(`^GRANT ${WORDS} ON SCHEMA ${IDENT} TO ${GRANTEE}${OPTION}$`),
|
|
128
|
+
parse: (m) => ({
|
|
129
|
+
object: { schema: unquote(m[2] as string) },
|
|
130
|
+
grantee: role(m[3] as string | undefined),
|
|
131
|
+
words: (m[1] as string).split(', '),
|
|
132
|
+
option: m[4] !== undefined,
|
|
133
|
+
}),
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
kind: 'grant',
|
|
137
|
+
re: new RegExp(`^GRANT ${WORDS} ON ${IDENT}\\.${IDENT} TO ${GRANTEE}${OPTION}$`),
|
|
138
|
+
parse: (m) => ({
|
|
139
|
+
object: { schema: unquote(m[2] as string), table: unquote(m[3] as string) },
|
|
140
|
+
grantee: role(m[4] as string | undefined),
|
|
141
|
+
words: (m[1] as string).split(', '),
|
|
142
|
+
option: m[5] !== undefined,
|
|
143
|
+
}),
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
kind: 'revoke',
|
|
147
|
+
re: new RegExp(`^REVOKE ALL ON ${IDENT}\\.${IDENT} FROM ${GRANTEE}$`),
|
|
148
|
+
parse: (m) => ({
|
|
149
|
+
object: { schema: unquote(m[1] as string), table: unquote(m[2] as string) },
|
|
150
|
+
grantee: role(m[3] as string | undefined),
|
|
151
|
+
words: [],
|
|
152
|
+
option: false,
|
|
153
|
+
}),
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
kind: 'revoke',
|
|
157
|
+
re: new RegExp(`^REVOKE ALL ON SCHEMA ${IDENT} FROM ${GRANTEE}$`),
|
|
158
|
+
parse: (m) => ({
|
|
159
|
+
object: { schema: unquote(m[1] as string) },
|
|
160
|
+
grantee: role(m[2] as string | undefined),
|
|
161
|
+
words: [],
|
|
162
|
+
option: false,
|
|
163
|
+
}),
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
kind: 'revoke',
|
|
167
|
+
re: new RegExp(`^REVOKE ALL ON ALL TABLES IN SCHEMA ${IDENT} FROM ${GRANTEE}$`),
|
|
168
|
+
parse: (m) => ({
|
|
169
|
+
allTables: true,
|
|
170
|
+
object: { schema: unquote(m[1] as string) },
|
|
171
|
+
grantee: role(m[2] as string | undefined),
|
|
172
|
+
words: [],
|
|
173
|
+
option: false,
|
|
174
|
+
}),
|
|
175
|
+
},
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
/** `undefined` when the text is no statement this family generates — the model refuses it
|
|
179
|
+
* rather than guessing (a parse miss on a GRANT-shaped text means a builder regressed). */
|
|
180
|
+
export const parseGrantStatement = (text: string): ParsedWrite | undefined => {
|
|
181
|
+
for (const pattern of PATTERNS) {
|
|
182
|
+
const m = pattern.re.exec(text);
|
|
183
|
+
if (m === null) continue;
|
|
184
|
+
const parsed = pattern.parse(m);
|
|
185
|
+
if (parsed === undefined) continue;
|
|
186
|
+
return { kind: pattern.kind, ...parsed };
|
|
187
|
+
}
|
|
188
|
+
return undefined;
|
|
189
|
+
};
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The read-answering half of the `Postgres.Grants` test fake: the `aclexplode`-shaped
|
|
3
|
+
* queries and the existence/ownership checks, answered from the server model's ACL
|
|
4
|
+
* (`fake-grants-sql.ts`), never from the statement list (S28). The entry and model shape
|
|
5
|
+
* are defined HERE so the write half (`fake-grants-write.ts`) can import them without a
|
|
6
|
+
* cycle.
|
|
7
|
+
*/
|
|
8
|
+
import * as Effect from 'effect/Effect';
|
|
9
|
+
import type { SqlError } from 'effect/unstable/sql/SqlError';
|
|
10
|
+
import type { AclObject } from './fake-grants-parse.ts';
|
|
11
|
+
|
|
12
|
+
/** One `aclitem` as the fake stores it: one (object, grantee, grantor) with the words that
|
|
13
|
+
* grantor granted, `*`-marked when WITH GRANT OPTION. `words` is mutable: grants and
|
|
14
|
+
* revokes edit it. */
|
|
15
|
+
export interface AclEntry {
|
|
16
|
+
readonly object: AclObject;
|
|
17
|
+
readonly grantee: string;
|
|
18
|
+
readonly grantor: string;
|
|
19
|
+
words: string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** One relation in the fake's catalog: every relation a table/column statement may name,
|
|
23
|
+
* with its columns, its `relkind` (default `'r'`) and its owner (default: the executor
|
|
24
|
+
* role, which no declared seat role owns). A sequence is a row with `relkind: 'S'`. */
|
|
25
|
+
export interface FakeCatalogTable {
|
|
26
|
+
readonly schema: string;
|
|
27
|
+
readonly table: string;
|
|
28
|
+
readonly columns?: ReadonlyArray<string>;
|
|
29
|
+
readonly relkind?: string;
|
|
30
|
+
readonly owner?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** What the read half needs from the factory, keyed per schema so two schemas may hold
|
|
34
|
+
* same-named relations: `relkinds`/`tableOwners` map `"schema\0table"`, `schemaOwners`
|
|
35
|
+
* maps the schema to its owning role. */
|
|
36
|
+
export interface FakeGrantsModel {
|
|
37
|
+
readonly schemas: ReadonlySet<string>;
|
|
38
|
+
readonly roles: ReadonlySet<string>;
|
|
39
|
+
readonly databases: ReadonlySet<string>;
|
|
40
|
+
readonly tables: ReadonlyArray<FakeCatalogTable>;
|
|
41
|
+
readonly acl: AclEntry[];
|
|
42
|
+
readonly relkinds: ReadonlyMap<string, string>;
|
|
43
|
+
readonly tableOwners: ReadonlyMap<string, string>;
|
|
44
|
+
readonly schemaOwners: ReadonlyMap<string, string>;
|
|
45
|
+
/** `current_user` — the grantor a write records, and the revoker the column read reports. */
|
|
46
|
+
readonly executor: string;
|
|
47
|
+
/** Whether `current_user` is a superuser: one arm of `revokesAsOwner` (a superuser revokes
|
|
48
|
+
* as the object owner, `revoke.sgml`). */
|
|
49
|
+
readonly executorSuper: boolean;
|
|
50
|
+
/** The roles `current_user` is a member of (recursively): a member of the owning role revokes
|
|
51
|
+
* as that role (`revokesAsOwner`). */
|
|
52
|
+
readonly executorMemberships: ReadonlySet<string>;
|
|
53
|
+
/** What `SELECT current_database()` answers. */
|
|
54
|
+
readonly connected: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const aclShapeRow = (marked: string): { privilege: string; grantable: boolean } => ({
|
|
58
|
+
privilege: marked.replace(/\*$/, ''),
|
|
59
|
+
grantable: marked.endsWith('*'),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/** Whether a `REVOKE` from the executor is performed as the object's owner (`select_best_grantor`
|
|
63
|
+
* acl.c@REL_18_6): a superuser, the owner itself, or a member of the owning role. Gates the
|
|
64
|
+
* column read's `grantor === owner` arm; the write path removes the owner's grant under it. */
|
|
65
|
+
export const revokesAsOwner = (model: FakeGrantsModel, owner: string): boolean =>
|
|
66
|
+
model.executorSuper || model.executor === owner || model.executorMemberships.has(owner);
|
|
67
|
+
|
|
68
|
+
/** Explode the model into aclexplode-shaped rows, filtered to one read's object shape
|
|
69
|
+
* and grantee set. */
|
|
70
|
+
const rows = <R>(
|
|
71
|
+
acl: ReadonlyArray<AclEntry>,
|
|
72
|
+
pickObject: (object: AclObject) => boolean,
|
|
73
|
+
pickGrantee: (grantee: string) => boolean,
|
|
74
|
+
shape: (entry: AclEntry, word: string) => R,
|
|
75
|
+
): R[] => {
|
|
76
|
+
const out: R[] = [];
|
|
77
|
+
for (const entry of acl) {
|
|
78
|
+
if (!pickObject(entry.object) || !pickGrantee(entry.grantee)) continue;
|
|
79
|
+
for (const word of entry.words) out.push(shape(entry, word));
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const granteeFilter =
|
|
85
|
+
(role: string) =>
|
|
86
|
+
(grantee: string): boolean =>
|
|
87
|
+
grantee === role || grantee === 'PUBLIC';
|
|
88
|
+
|
|
89
|
+
/** Answer the reads the family issues: the two existence checks, the schema/table/column/
|
|
90
|
+
* default `aclexplode` reads, and the two ownership reads. `undefined` when the text is no
|
|
91
|
+
* read this fake answers — the caller applies it as a write. */
|
|
92
|
+
export const answerRead = <A extends object>(
|
|
93
|
+
text: string,
|
|
94
|
+
params: ReadonlyArray<unknown>,
|
|
95
|
+
model: FakeGrantsModel,
|
|
96
|
+
): Effect.Effect<ReadonlyArray<A>, SqlError> | undefined => {
|
|
97
|
+
const { acl, schemas, roles, databases, tables } = model;
|
|
98
|
+
|
|
99
|
+
if (text.startsWith('SELECT 1 AS present FROM pg_roles')) {
|
|
100
|
+
return Effect.succeed(
|
|
101
|
+
(roles.has(params[0] as string) ? [{ present: 1 }] : []) as unknown as ReadonlyArray<A>,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (text.startsWith('SELECT 1 AS present FROM pg_namespace')) {
|
|
105
|
+
return Effect.succeed(
|
|
106
|
+
(schemas.has(params[0] as string) ? [{ present: 1 }] : []) as unknown as ReadonlyArray<A>,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
if (text.startsWith('SELECT 1 AS present FROM pg_database')) {
|
|
110
|
+
return Effect.succeed(
|
|
111
|
+
(databases.has(params[0] as string) ? [{ present: 1 }] : []) as unknown as ReadonlyArray<A>,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
if (text.startsWith('SELECT current_database()')) {
|
|
115
|
+
return Effect.succeed([{ database: model.connected }] as unknown as ReadonlyArray<A>);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const jsonNames = (value: unknown): unknown => {
|
|
119
|
+
if (typeof value !== 'string') {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`fake-grants-read: existence param must be a JSON string, got ${typeof value}`,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
return JSON.parse(value) as unknown;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const [schema, role] = [params[0] as string, params[1] as string];
|
|
128
|
+
if (
|
|
129
|
+
text.includes('c.relname AS table') &&
|
|
130
|
+
text.includes('pg_class c') &&
|
|
131
|
+
text.includes('json_array_elements_text') &&
|
|
132
|
+
!text.includes('pg_attribute')
|
|
133
|
+
) {
|
|
134
|
+
const want = new Set(jsonNames(params[1]) as ReadonlyArray<string>);
|
|
135
|
+
return Effect.succeed(
|
|
136
|
+
tables
|
|
137
|
+
.filter((t) => t.schema === schema && want.has(t.table))
|
|
138
|
+
.map((t) => ({ table: t.table })) as unknown as ReadonlyArray<A>,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
if (text.includes('a.attname AS column') && text.includes('json_array_elements($2::json)')) {
|
|
142
|
+
const pairs = jsonNames(params[1]) as ReadonlyArray<readonly [string, string]>;
|
|
143
|
+
const want = new Set(pairs.map(([table, column]) => `${table}\u0000${column}`));
|
|
144
|
+
return Effect.succeed(
|
|
145
|
+
tables
|
|
146
|
+
.filter((t) => t.schema === schema)
|
|
147
|
+
.flatMap((t) =>
|
|
148
|
+
(t.columns ?? [])
|
|
149
|
+
.filter((c) => want.has(`${t.table}\u0000${c}`))
|
|
150
|
+
.map((c) => ({ table: t.table, column: c })),
|
|
151
|
+
) as unknown as ReadonlyArray<A>,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
if (text.includes('aclexplode(n.nspacl)')) {
|
|
155
|
+
return Effect.succeed(
|
|
156
|
+
rows(
|
|
157
|
+
acl,
|
|
158
|
+
(o) =>
|
|
159
|
+
o.schema === schema &&
|
|
160
|
+
o.table === undefined &&
|
|
161
|
+
o.column === undefined &&
|
|
162
|
+
o.defaultFor === undefined,
|
|
163
|
+
granteeFilter(role),
|
|
164
|
+
(entry, marked) => ({ public: entry.grantee === 'PUBLIC', ...aclShapeRow(marked) }),
|
|
165
|
+
) as unknown as ReadonlyArray<A>,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
if (text.includes('aclexplode(c.relacl)')) {
|
|
169
|
+
// The relkind comes back shaped exactly as the query TEXT dictates: the production
|
|
170
|
+
// SQL casts `c.relkind::text` (OID 25, a string); without the cast the `"char"` OID 18
|
|
171
|
+
// column decodes to raw bytes on the socket transport (`PgTypes` has no OID 18 codec),
|
|
172
|
+
// so this fake hands back the same `Uint8Array` the real wire would — one byte per
|
|
173
|
+
// relkind letter — to keep a regression test honest.
|
|
174
|
+
const relkindFor = (object: AclObject): unknown => {
|
|
175
|
+
const relkind = model.relkinds.get(`${object.schema}\u0000${object.table ?? ''}`) ?? 'r';
|
|
176
|
+
if (text.includes('c.relkind::text')) return relkind;
|
|
177
|
+
return new Uint8Array(relkind.split('').map((ch) => ch.charCodeAt(0)));
|
|
178
|
+
};
|
|
179
|
+
return Effect.succeed(
|
|
180
|
+
rows(
|
|
181
|
+
acl,
|
|
182
|
+
(o) =>
|
|
183
|
+
o.schema === schema &&
|
|
184
|
+
o.table !== undefined &&
|
|
185
|
+
o.column === undefined &&
|
|
186
|
+
o.defaultFor === undefined,
|
|
187
|
+
granteeFilter(role),
|
|
188
|
+
(entry, marked) => ({
|
|
189
|
+
table: entry.object.table,
|
|
190
|
+
relkind: relkindFor(entry.object),
|
|
191
|
+
public: entry.grantee === 'PUBLIC',
|
|
192
|
+
...aclShapeRow(marked),
|
|
193
|
+
}),
|
|
194
|
+
) as unknown as ReadonlyArray<A>,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
if (text.includes('aclexplode(v.attacl)')) {
|
|
198
|
+
return Effect.succeed(
|
|
199
|
+
rows(
|
|
200
|
+
acl,
|
|
201
|
+
(o) => o.schema === schema && o.column !== undefined,
|
|
202
|
+
granteeFilter(role),
|
|
203
|
+
(entry, marked) => {
|
|
204
|
+
const owner =
|
|
205
|
+
model.tableOwners.get(`${entry.object.schema}\u0000${entry.object.table ?? ''}`) ??
|
|
206
|
+
model.executor;
|
|
207
|
+
return {
|
|
208
|
+
table: entry.object.table,
|
|
209
|
+
column: entry.object.column,
|
|
210
|
+
public: entry.grantee === 'PUBLIC',
|
|
211
|
+
grantor: entry.grantor,
|
|
212
|
+
owner,
|
|
213
|
+
revoker: model.executor,
|
|
214
|
+
revoker_as_owner: revokesAsOwner(model, owner),
|
|
215
|
+
...aclShapeRow(marked),
|
|
216
|
+
};
|
|
217
|
+
},
|
|
218
|
+
) as unknown as ReadonlyArray<A>,
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
if (text.includes('aclexplode(d.defaclacl)')) {
|
|
222
|
+
// The defaults read selects ONLY the declared role's words — never PUBLIC's.
|
|
223
|
+
return Effect.succeed(
|
|
224
|
+
rows(
|
|
225
|
+
acl,
|
|
226
|
+
(o) => o.schema === schema && o.defaultFor !== undefined,
|
|
227
|
+
(grantee) => grantee === role,
|
|
228
|
+
(entry, marked) => ({ for_role: entry.object.defaultFor, ...aclShapeRow(marked) }),
|
|
229
|
+
) as unknown as ReadonlyArray<A>,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// The ownership reads: `nspowner` answers per schema, `relowner` per relation, all
|
|
234
|
+
// relkinds — from the catalog and the owner maps, never from ACL rows (a fresh owned
|
|
235
|
+
// object carries none).
|
|
236
|
+
if (text.includes('nspowner')) {
|
|
237
|
+
const owns = model.schemaOwners.get(schema) === role;
|
|
238
|
+
return Effect.succeed([{ role_owns: owns }] as unknown as ReadonlyArray<A>);
|
|
239
|
+
}
|
|
240
|
+
if (text.includes('relowner')) {
|
|
241
|
+
const owned = tables
|
|
242
|
+
.filter(
|
|
243
|
+
(t) => t.schema === schema && model.tableOwners.get(`${schema}\u0000${t.table}`) === role,
|
|
244
|
+
)
|
|
245
|
+
.map((t) => ({ table: t.table }));
|
|
246
|
+
return Effect.succeed(owned as unknown as ReadonlyArray<A>);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return undefined;
|
|
250
|
+
};
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The server-model half of the `Postgres.Grants` test fake: an in-memory ACL the way
|
|
3
|
+
* PostgreSQL 18 packs it — one entry per (object, grantee, grantor) pair holding that
|
|
4
|
+
* grantor's granted words — plus the ten statements the parser (`fake-grants-parse.ts`)
|
|
5
|
+
* reduces, applied with the server rules the plan's convergence depends on. The
|
|
6
|
+
* `aclexplode`-shaped reads are answered by `fake-grants-read.ts` from the same model; the
|
|
7
|
+
* grant/revoke semantics (the "revokes as owner" rule included) live in
|
|
8
|
+
* `fake-grants-write.ts`.
|
|
9
|
+
*
|
|
10
|
+
* ★ `REVOKE ALL ON <table>` ALSO clears that grantee's column entries on the table
|
|
11
|
+
* (measured on PG 18.6 — the plan's convergence proof depends on the model answering
|
|
12
|
+
* the same), and `REVOKE ALL ON ALL TABLES IN SCHEMA` skips the relkinds the server
|
|
13
|
+
* skips — a sequence keeps its grants (measured, `grants-plan.ts`).
|
|
14
|
+
* ★ A statement naming a relation or column the catalog does not know fails `42P01` /
|
|
15
|
+
* `42703`, the way the server refuses a missing object.
|
|
16
|
+
*/
|
|
17
|
+
import * as Effect from 'effect/Effect';
|
|
18
|
+
import { SqlError, SqlSyntaxError } from 'effect/unstable/sql/SqlError';
|
|
19
|
+
import type { PgExecutor } from './database-sql.ts';
|
|
20
|
+
import { type AclEntry, type FakeCatalogTable, answerRead } from './fake-grants-read.ts';
|
|
21
|
+
import { parseGrantStatement } from './fake-grants-parse.ts';
|
|
22
|
+
import { grantWords, revoke } from './fake-grants-write.ts';
|
|
23
|
+
import { relkindIsPublicRevocable } from './grants-words.ts';
|
|
24
|
+
|
|
25
|
+
export type { AclEntry, FakeCatalogTable };
|
|
26
|
+
|
|
27
|
+
export interface RecordedStatement {
|
|
28
|
+
readonly text: string;
|
|
29
|
+
readonly params: ReadonlyArray<unknown>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface FakeGrants extends PgExecutor {
|
|
33
|
+
readonly statements: ReadonlyArray<RecordedStatement>;
|
|
34
|
+
/** Each `transaction` call, in order — the statement list that committed together. */
|
|
35
|
+
readonly transactions: ReadonlyArray<ReadonlyArray<string>>;
|
|
36
|
+
/** Mutable on purpose: a test seeds a "competing" grant the same way `fake-sql.ts`'s
|
|
37
|
+
* `databases` map is seeded before the reconcile ever runs. */
|
|
38
|
+
readonly acl: AclEntry[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface FakeGrantsOptions {
|
|
42
|
+
/** Schemas the existence check (`pg_namespace`) answers yes for. */
|
|
43
|
+
readonly schemas?: ReadonlyArray<string>;
|
|
44
|
+
/** Roles the existence check (`pg_roles`) answers yes for. */
|
|
45
|
+
readonly roles?: ReadonlyArray<string>;
|
|
46
|
+
/** Databases the existence check (`pg_database`) answers yes for. */
|
|
47
|
+
readonly databases?: ReadonlyArray<string>;
|
|
48
|
+
/** The catalog: every relation a table/column statement may name, with its columns,
|
|
49
|
+
* relkind and owner (see `fake-grants-read.ts#FakeCatalogTable`). */
|
|
50
|
+
readonly tables?: ReadonlyArray<FakeCatalogTable>;
|
|
51
|
+
/** The role that owns each seeded schema; unset means the executor owns it. */
|
|
52
|
+
readonly schemaOwner?: string;
|
|
53
|
+
/** Seed grants, each carrying its own grantor (the third-grantor survival cases). */
|
|
54
|
+
readonly acl?: ReadonlyArray<Omit<AclEntry, 'words'> & { readonly words: ReadonlyArray<string> }>;
|
|
55
|
+
/** The role the statements run AS — the grantor recorded on every write, and
|
|
56
|
+
* `current_user` on the column read (default `postgres`, the owner the fixtures assume). */
|
|
57
|
+
readonly executor?: string;
|
|
58
|
+
/** Whether the executor is a superuser (default `true`: `postgres` is). A superuser, the
|
|
59
|
+
* owner itself, or a member of the owning role revokes as the owner — `revoke.sgml`'s rule,
|
|
60
|
+
* modelled by `revokesAsOwner` (fake-grants-read.ts) on both the read and the write. */
|
|
61
|
+
readonly executorSuper?: boolean;
|
|
62
|
+
/** The roles the executor is a member of (recursively): a member of a table's owning role
|
|
63
|
+
* revokes that table's grants as the owner, the same as a superuser. */
|
|
64
|
+
readonly executorMemberships?: ReadonlyArray<string>;
|
|
65
|
+
/** What `current_database()` answers (default `agents`, the declaration the fixtures use). */
|
|
66
|
+
readonly connected?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const failSql = (code: string, message: string, operation: string) =>
|
|
70
|
+
Effect.fail(
|
|
71
|
+
new SqlError({
|
|
72
|
+
reason: new SqlSyntaxError({
|
|
73
|
+
cause: Object.assign(new Error(message), { code }),
|
|
74
|
+
message,
|
|
75
|
+
operation,
|
|
76
|
+
}),
|
|
77
|
+
}),
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
export const makeFakeGrants = (options: FakeGrantsOptions = {}): FakeGrants => {
|
|
81
|
+
const statements: RecordedStatement[] = [];
|
|
82
|
+
const transactions: string[][] = [];
|
|
83
|
+
const schemas = new Set(options.schemas ?? []);
|
|
84
|
+
const roles = new Set(options.roles ?? []);
|
|
85
|
+
const databases = new Set(options.databases ?? []);
|
|
86
|
+
const tables = options.tables ?? [];
|
|
87
|
+
const executor = options.executor ?? 'postgres';
|
|
88
|
+
const executorSuper = options.executorSuper ?? true;
|
|
89
|
+
const executorMemberships = new Set(options.executorMemberships ?? []);
|
|
90
|
+
const connected = options.connected ?? 'agents';
|
|
91
|
+
const acl: AclEntry[] = (options.acl ?? []).map((seed) => ({ ...seed, words: [...seed.words] }));
|
|
92
|
+
|
|
93
|
+
// The catalog as pg_class really holds it: relkind and owner per (schema, table), the
|
|
94
|
+
// schema owned by `schemaOwner` or the executor. Default table owner is the schema
|
|
95
|
+
// owner — a CREATE inside a namespace makes its creator the relation's owner too.
|
|
96
|
+
const schemaOwner = options.schemaOwner ?? executor;
|
|
97
|
+
const relkinds = new Map<string, string>();
|
|
98
|
+
const tableOwners = new Map<string, string>();
|
|
99
|
+
for (const t of tables) {
|
|
100
|
+
relkinds.set(`${t.schema}\u0000${t.table}`, t.relkind ?? 'r');
|
|
101
|
+
tableOwners.set(`${t.schema}\u0000${t.table}`, t.owner ?? schemaOwner);
|
|
102
|
+
}
|
|
103
|
+
const schemaOwners = new Map<string, string>();
|
|
104
|
+
for (const schema of schemas) schemaOwners.set(schema, schemaOwner);
|
|
105
|
+
const model = {
|
|
106
|
+
schemas,
|
|
107
|
+
roles,
|
|
108
|
+
databases,
|
|
109
|
+
tables,
|
|
110
|
+
acl,
|
|
111
|
+
relkinds,
|
|
112
|
+
tableOwners,
|
|
113
|
+
schemaOwners,
|
|
114
|
+
executor,
|
|
115
|
+
executorSuper,
|
|
116
|
+
executorMemberships,
|
|
117
|
+
connected,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const knownTable = (schema: string, table: string) =>
|
|
121
|
+
tables.find((t) => t.schema === schema && t.table === table);
|
|
122
|
+
|
|
123
|
+
/** Apply one parsed write, validating its target against the catalog first. */
|
|
124
|
+
const applyWrite = (text: string): Effect.Effect<ReadonlyArray<never>, SqlError> => {
|
|
125
|
+
const parsed = parseGrantStatement(text);
|
|
126
|
+
if (parsed === undefined) {
|
|
127
|
+
throw new Error(`fake-grants-sql: unrecognised statement: ${text}`);
|
|
128
|
+
}
|
|
129
|
+
if (parsed.object.table !== undefined) {
|
|
130
|
+
const known = knownTable(parsed.object.schema, parsed.object.table);
|
|
131
|
+
if (known === undefined) {
|
|
132
|
+
return failSql(
|
|
133
|
+
'42P01',
|
|
134
|
+
`relation "${parsed.object.table}" does not exist`,
|
|
135
|
+
text.slice(0, 30),
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
if (
|
|
139
|
+
parsed.object.column !== undefined &&
|
|
140
|
+
!(known.columns ?? []).includes(parsed.object.column)
|
|
141
|
+
) {
|
|
142
|
+
return failSql(
|
|
143
|
+
'42703',
|
|
144
|
+
`column "${parsed.object.column}" does not exist`,
|
|
145
|
+
text.slice(0, 30),
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const operation = text.slice(0, 30);
|
|
150
|
+
if (parsed.allTables === true) {
|
|
151
|
+
// `ON ALL TABLES IN SCHEMA` reaches only the revocable relkinds — a sequence keeps
|
|
152
|
+
// its grants (measured on PG 18.6, `grants-plan.ts`).
|
|
153
|
+
for (const table of tables.filter(
|
|
154
|
+
(t) => t.schema === parsed.object.schema && relkindIsPublicRevocable(t.relkind ?? 'r'),
|
|
155
|
+
)) {
|
|
156
|
+
const tableObject = { schema: parsed.object.schema, table: table.table };
|
|
157
|
+
const tableError = revoke(model, tableObject, parsed.grantee, operation);
|
|
158
|
+
if (tableError !== undefined) return Effect.fail(tableError);
|
|
159
|
+
for (const column of table.columns ?? []) {
|
|
160
|
+
const columnError = revoke(model, { ...tableObject, column }, parsed.grantee, operation);
|
|
161
|
+
if (columnError !== undefined) return Effect.fail(columnError);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return Effect.succeed([]);
|
|
165
|
+
}
|
|
166
|
+
if (parsed.kind === 'grant') {
|
|
167
|
+
grantWords(model, parsed.object, parsed.grantee, parsed.words, parsed.option);
|
|
168
|
+
return Effect.succeed([]);
|
|
169
|
+
}
|
|
170
|
+
const error = revoke(model, parsed.object, parsed.grantee, operation);
|
|
171
|
+
if (error !== undefined) return Effect.fail(error);
|
|
172
|
+
// A table-level `REVOKE ALL` also clears that grantee's column entries on the table
|
|
173
|
+
// (measured on PG 18.6 — a table revoke reaches its columns, unlike `ON ALL TABLES`).
|
|
174
|
+
if (parsed.object.table !== undefined && parsed.object.column === undefined) {
|
|
175
|
+
const known = knownTable(parsed.object.schema, parsed.object.table);
|
|
176
|
+
for (const column of known?.columns ?? []) {
|
|
177
|
+
const columnError = revoke(
|
|
178
|
+
model,
|
|
179
|
+
{ schema: parsed.object.schema, table: parsed.object.table, column },
|
|
180
|
+
parsed.grantee,
|
|
181
|
+
operation,
|
|
182
|
+
);
|
|
183
|
+
if (columnError !== undefined) return Effect.fail(columnError);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return Effect.succeed([]);
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const unsafe = <A extends object>(
|
|
190
|
+
text: string,
|
|
191
|
+
params: ReadonlyArray<unknown> = [],
|
|
192
|
+
): Effect.Effect<ReadonlyArray<A>, SqlError> =>
|
|
193
|
+
Effect.suspend(() => {
|
|
194
|
+
statements.push({ text, params });
|
|
195
|
+
const read = answerRead<A>(text, params, model);
|
|
196
|
+
if (read !== undefined) return read;
|
|
197
|
+
return applyWrite(text) as unknown as Effect.Effect<ReadonlyArray<A>, SqlError>;
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const transaction = (sqls: readonly string[]): Effect.Effect<void, SqlError> =>
|
|
201
|
+
Effect.gen(function* () {
|
|
202
|
+
transactions.push([...sqls]);
|
|
203
|
+
for (const sql of sqls) yield* unsafe(sql).pipe(Effect.asVoid);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
return { unsafe, transaction, statements, transactions, acl };
|
|
207
|
+
};
|