@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,71 @@
|
|
|
1
|
+
/** Test-only RESP listener around the stateful fake. Listener EPERM is a test failure. */
|
|
2
|
+
import * as Effect from 'effect/Effect';
|
|
3
|
+
import { type Socket, createServer } from 'node:net';
|
|
4
|
+
import { type FakeValkey, makeFakeValkey } from './fake-valkey.ts';
|
|
5
|
+
import type { ValkeyReply } from './transport.ts';
|
|
6
|
+
|
|
7
|
+
const bulk = (value: string | null): string =>
|
|
8
|
+
value === null ? '$-1\r\n' : `$${Buffer.byteLength(value)}\r\n${value}\r\n`;
|
|
9
|
+
const encode = (reply: ValkeyReply): string => {
|
|
10
|
+
if (reply.kind === 'error') return `-${reply.message}\r\n`;
|
|
11
|
+
if (reply.kind === 'bulk') return bulk(reply.value);
|
|
12
|
+
return `*${reply.values.length}\r\n${reply.values.map(bulk).join('')}`;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const command = (buf: Buffer): { args: string[]; bytes: number } | undefined => {
|
|
16
|
+
const end = buf.indexOf('\r\n');
|
|
17
|
+
if (end < 0) return undefined;
|
|
18
|
+
const count = Number(buf.subarray(1, end).toString());
|
|
19
|
+
const args: string[] = [];
|
|
20
|
+
let offset = end + 2;
|
|
21
|
+
for (let i = 0; i < count; i++) {
|
|
22
|
+
const lengthEnd = buf.indexOf('\r\n', offset);
|
|
23
|
+
if (lengthEnd < 0) return undefined;
|
|
24
|
+
const length = Number(buf.subarray(offset + 1, lengthEnd).toString());
|
|
25
|
+
const start = lengthEnd + 2;
|
|
26
|
+
if (buf.length < start + length + 2) return undefined;
|
|
27
|
+
args.push(buf.subarray(start, start + length).toString());
|
|
28
|
+
offset = start + length + 2;
|
|
29
|
+
}
|
|
30
|
+
return { args, bytes: offset };
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export interface ProviderServer {
|
|
34
|
+
readonly fake: FakeValkey;
|
|
35
|
+
readonly port: number;
|
|
36
|
+
readonly close: () => Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const providerServer = (user: string): Promise<ProviderServer> =>
|
|
40
|
+
new Promise((resolve, reject) => {
|
|
41
|
+
const fake = makeFakeValkey({ acl: { [user]: `user ${user} on nopass -@all` } });
|
|
42
|
+
const peers = new Set<Socket>();
|
|
43
|
+
const server = createServer((peer) => {
|
|
44
|
+
peers.add(peer);
|
|
45
|
+
peer.on('close', () => peers.delete(peer));
|
|
46
|
+
let pending = Buffer.alloc(0);
|
|
47
|
+
peer.on('data', (chunk) => {
|
|
48
|
+
pending = Buffer.concat([pending, chunk]);
|
|
49
|
+
let parsed = command(pending);
|
|
50
|
+
while (parsed !== undefined) {
|
|
51
|
+
pending = pending.subarray(parsed.bytes);
|
|
52
|
+
peer.write(encode(Effect.runSync(fake.send(parsed.args))));
|
|
53
|
+
parsed = command(pending);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
server.once('error', reject);
|
|
58
|
+
server.listen(0, '127.0.0.1', () => {
|
|
59
|
+
const address = server.address();
|
|
60
|
+
if (address === null || typeof address === 'string') return reject(new Error('no port'));
|
|
61
|
+
resolve({
|
|
62
|
+
fake,
|
|
63
|
+
port: address.port,
|
|
64
|
+
close: () =>
|
|
65
|
+
new Promise((done) => {
|
|
66
|
+
for (const peer of peers) peer.destroy();
|
|
67
|
+
server.close(() => done());
|
|
68
|
+
}),
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/** Resolve providers from the public layer, exactly as the engine does. */
|
|
2
|
+
import { expect, test } from 'bun:test';
|
|
3
|
+
import * as Effect from 'effect/Effect';
|
|
4
|
+
import * as Layer from 'effect/Layer';
|
|
5
|
+
import { ValkeyAclFile } from './acl.ts';
|
|
6
|
+
import { ValkeyInstance } from './instance.ts';
|
|
7
|
+
import { valkeyProviders } from './providers.ts';
|
|
8
|
+
import { resolveValkeyConnection } from './connection-service.ts';
|
|
9
|
+
import { context, required } from './handler-test-fixture.ts';
|
|
10
|
+
import { providerServer } from './provider-socket-fixture.ts';
|
|
11
|
+
|
|
12
|
+
const config = (instance: string, port: number) => ({ instance, host: '127.0.0.1', port });
|
|
13
|
+
|
|
14
|
+
test('valkeyProviders retains the named connection for an AclFile provider read', async () => {
|
|
15
|
+
const server = await providerServer('seat');
|
|
16
|
+
try {
|
|
17
|
+
const output = await Effect.runPromise(
|
|
18
|
+
Effect.gen(function* () {
|
|
19
|
+
const provider = yield* ValkeyAclFile.Provider;
|
|
20
|
+
return yield* required(provider.read)({
|
|
21
|
+
...context,
|
|
22
|
+
olds: { instance: 'seats', users: {} },
|
|
23
|
+
output: undefined,
|
|
24
|
+
});
|
|
25
|
+
}).pipe(Effect.provide(valkeyProviders(config('seats', server.port)))),
|
|
26
|
+
);
|
|
27
|
+
expect(Object.keys(required(output).users)).toEqual(['seat']);
|
|
28
|
+
expect(server.fake.commands.map(({ args }) => args)).toEqual([['ACL', 'LIST']]);
|
|
29
|
+
} finally {
|
|
30
|
+
await server.close();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('combined provider layers route both resources and exclusive ACL writes to their instance', async () => {
|
|
35
|
+
const seats = await providerServer('seat');
|
|
36
|
+
try {
|
|
37
|
+
const cache = await providerServer('cache');
|
|
38
|
+
try {
|
|
39
|
+
await Effect.runPromise(
|
|
40
|
+
Effect.gen(function* () {
|
|
41
|
+
const acl = yield* ValkeyAclFile.Provider;
|
|
42
|
+
const instance = yield* ValkeyInstance.Provider;
|
|
43
|
+
for (const [name, server, user] of [
|
|
44
|
+
['seats', seats, 'seat'],
|
|
45
|
+
['cache', cache, 'cache'],
|
|
46
|
+
] as const) {
|
|
47
|
+
const props = { instance: name, users: {}, exclusive: true };
|
|
48
|
+
const read = yield* required(acl.read)({ ...context, olds: props, output: undefined });
|
|
49
|
+
expect(Object.keys(required(read).users)).toEqual([user]);
|
|
50
|
+
const before = server.fake.commands.length;
|
|
51
|
+
yield* required(instance.read)({
|
|
52
|
+
...context,
|
|
53
|
+
olds: { name, port: server.port },
|
|
54
|
+
output: undefined,
|
|
55
|
+
});
|
|
56
|
+
expect(server.fake.commands.slice(before).map(({ args }) => args[0])).toEqual([
|
|
57
|
+
'INFO',
|
|
58
|
+
'CONFIG',
|
|
59
|
+
]);
|
|
60
|
+
yield* acl.reconcile({ ...context, news: props, olds: props, output: undefined });
|
|
61
|
+
expect(server.fake.acl.size).toBe(0);
|
|
62
|
+
if (name === 'seats') expect(cache.fake.acl.has('cache')).toBe(true);
|
|
63
|
+
}
|
|
64
|
+
}).pipe(
|
|
65
|
+
Effect.provide(
|
|
66
|
+
Layer.mergeAll(
|
|
67
|
+
valkeyProviders(config('seats', seats.port)),
|
|
68
|
+
valkeyProviders(config('cache', cache.port)),
|
|
69
|
+
),
|
|
70
|
+
),
|
|
71
|
+
),
|
|
72
|
+
);
|
|
73
|
+
} finally {
|
|
74
|
+
await cache.close();
|
|
75
|
+
}
|
|
76
|
+
} finally {
|
|
77
|
+
await seats.close();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('named connection selection survives either layer order without a listener', async () => {
|
|
82
|
+
const layers = [
|
|
83
|
+
valkeyProviders(config('seats', 56380)),
|
|
84
|
+
valkeyProviders(config('cache', 56381)),
|
|
85
|
+
] as const;
|
|
86
|
+
for (const providers of [Layer.mergeAll(...layers), Layer.mergeAll(layers[1], layers[0])]) {
|
|
87
|
+
await Effect.runPromise(
|
|
88
|
+
Effect.gen(function* () {
|
|
89
|
+
expect((yield* resolveValkeyConnection('seats')).port).toBe(56380);
|
|
90
|
+
expect((yield* resolveValkeyConnection('cache')).port).toBe(56381);
|
|
91
|
+
const provider = yield* ValkeyAclFile.Provider;
|
|
92
|
+
const error = yield* Effect.flip(
|
|
93
|
+
required(provider.read)({
|
|
94
|
+
...context,
|
|
95
|
+
olds: { instance: 'typo', users: {} },
|
|
96
|
+
output: undefined,
|
|
97
|
+
}),
|
|
98
|
+
);
|
|
99
|
+
expect(error._tag).toBe('ValkeyConnectionMissing');
|
|
100
|
+
expect(error.instance).toBe('typo');
|
|
101
|
+
}).pipe(Effect.provide(providers)),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('refused socket acquisition maps all ACL operations to ValkeyInstanceUnreachable', async () => {
|
|
107
|
+
const server = await providerServer('seat');
|
|
108
|
+
const port = server.port;
|
|
109
|
+
await server.close();
|
|
110
|
+
await Effect.runPromise(
|
|
111
|
+
Effect.gen(function* () {
|
|
112
|
+
const provider = yield* ValkeyAclFile.Provider;
|
|
113
|
+
const props = { instance: 'seats', users: {} };
|
|
114
|
+
for (const operation of [
|
|
115
|
+
required(provider.read)({ ...context, olds: props, output: undefined }),
|
|
116
|
+
provider.reconcile({ ...context, news: props, olds: props, output: undefined }),
|
|
117
|
+
provider.delete({ ...context, olds: props, output: props }),
|
|
118
|
+
]) {
|
|
119
|
+
const error = yield* Effect.flip(operation);
|
|
120
|
+
expect(error).toMatchObject({
|
|
121
|
+
_tag: 'ValkeyInstanceUnreachable',
|
|
122
|
+
instance: 'seats',
|
|
123
|
+
host: '127.0.0.1',
|
|
124
|
+
port,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}).pipe(Effect.provide(valkeyProviders(config('seats', port)))),
|
|
128
|
+
);
|
|
129
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Valkey provider, pre-wired to one instance:
|
|
3
|
+
*
|
|
4
|
+
* const providers = valkeyProviders({ instance: 'valkey-seats', host: '127.0.0.1', port: 6381 });
|
|
5
|
+
*
|
|
6
|
+
* ★ THE INSTANCE IS A PARAMETER, NOT A DEFAULT (S15). This kit is PUBLIC; CT100's host, the two
|
|
7
|
+
* ports (6380/6381) and the ACL user a seat authenticates as are the CONSUMING stack's values,
|
|
8
|
+
* never baked in here. Each resource selects the connection by its instance name, so the
|
|
9
|
+
* fixed provider tags cannot redirect one instance's ACL to another server.
|
|
10
|
+
*/
|
|
11
|
+
import * as Layer from 'effect/Layer';
|
|
12
|
+
import { ValkeyAclFileProvider } from './acl.ts';
|
|
13
|
+
import { type ValkeyConnectionConfig, valkeyConnection } from './connection.ts';
|
|
14
|
+
import { ValkeyInstanceProvider } from './instance.ts';
|
|
15
|
+
|
|
16
|
+
// ⚠️ As in postgres/providers.ts and caddy/providers.ts, plain provide hides the connection
|
|
17
|
+
// from lifecycle handlers invoked later by Alchemy. Keep the named service in the output.
|
|
18
|
+
export const valkeyProviders = (config: ValkeyConnectionConfig & { readonly instance: string }) =>
|
|
19
|
+
Layer.mergeAll(ValkeyInstanceProvider(), ValkeyAclFileProvider()).pipe(
|
|
20
|
+
Layer.provideMerge(valkeyConnection(config)),
|
|
21
|
+
);
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One RESP reply, pulled off the socket this family owns.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ THE SERVER'S `$N` / `*N` IS NOT A BUDGET. `INFO`, `ACL LIST` and `CONFIG GET` are
|
|
5
|
+
* small. `$2147483647` or `*1000000000` would grow memory or loop in the same process
|
|
6
|
+
* that holds every other provider (measured 2026-09-30: both reads stayed pending).
|
|
7
|
+
* Past the cap the read fails `ValkeySocketError` before it waits for the body.
|
|
8
|
+
* ★ THE PER-READ DEADLINE DOES NOT RESET WHEN A BYTE ARRIVES. `socket.setTimeout` is an
|
|
9
|
+
* idle timer; a peer that dribbles would stretch it. This abort does not.
|
|
10
|
+
*/
|
|
11
|
+
import * as Effect from 'effect/Effect';
|
|
12
|
+
import type { Socket } from 'node:net';
|
|
13
|
+
import {
|
|
14
|
+
type ValkeyReply,
|
|
15
|
+
ValkeyServerError,
|
|
16
|
+
ValkeySocketError,
|
|
17
|
+
type ValkeyTransportError,
|
|
18
|
+
} from './transport-error.ts';
|
|
19
|
+
|
|
20
|
+
/** 1 MiB. The three replies this family reads are kilobytes. */
|
|
21
|
+
export const MAX_BULK_BYTES = 1_048_576;
|
|
22
|
+
|
|
23
|
+
/** `ACL LIST` is one element per user; `CONFIG GET` is two per key. */
|
|
24
|
+
export const MAX_ARRAY_ITEMS = 10_000;
|
|
25
|
+
|
|
26
|
+
const CRLF = '\r\n';
|
|
27
|
+
|
|
28
|
+
/** A mutable buffer shared between recursive RESP reads so array items consume bytes
|
|
29
|
+
* from the same stream the outer `*` marker was read from. */
|
|
30
|
+
export interface ReadBuf {
|
|
31
|
+
buf: Buffer;
|
|
32
|
+
/** Bytes already accepted in this reply. A sum of in-cap bulks must not pass the cap either. */
|
|
33
|
+
taken: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The in-flight read, so a lifetime `error` listener can fail it. `fail` is `null` when
|
|
37
|
+
* no read is waiting — `undefined` is not assignable under `exactOptionalPropertyTypes`. */
|
|
38
|
+
export interface InFlight {
|
|
39
|
+
fail: ((reason: string) => void) | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type ParsedLine = { readonly value: string; readonly drop: number } | undefined;
|
|
43
|
+
|
|
44
|
+
/** `-1` (RESP nil) or a non-negative integer at most `cap`. Anything else is a refusal. */
|
|
45
|
+
const boundedCount = (raw: string, cap: number): number | undefined => {
|
|
46
|
+
if (!/^(?:-1|\d+)$/.test(raw)) return undefined;
|
|
47
|
+
const n = Number(raw);
|
|
48
|
+
if (!Number.isSafeInteger(n) || n > cap) return undefined;
|
|
49
|
+
return n;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const readUntil = (
|
|
53
|
+
socket: Socket,
|
|
54
|
+
state: ReadBuf,
|
|
55
|
+
inFlight: InFlight,
|
|
56
|
+
timeoutMs: number,
|
|
57
|
+
done: (buffer: Buffer) => ParsedLine,
|
|
58
|
+
): Effect.Effect<string, ValkeySocketError> =>
|
|
59
|
+
Effect.tryPromise({
|
|
60
|
+
try: (signal) =>
|
|
61
|
+
new Promise<string>((resolve, reject) => {
|
|
62
|
+
let settled = false;
|
|
63
|
+
// Assigned only when this read has to wait. The early path (bytes already buffered)
|
|
64
|
+
// never registers listeners, so they live on one object the finish path can clear.
|
|
65
|
+
const wait: {
|
|
66
|
+
timer?: ReturnType<typeof setTimeout>;
|
|
67
|
+
onData?: (chunk: Buffer) => void;
|
|
68
|
+
onEnd?: () => void;
|
|
69
|
+
} = {};
|
|
70
|
+
const finish = (fn: () => void) => {
|
|
71
|
+
if (settled) return;
|
|
72
|
+
settled = true;
|
|
73
|
+
if (wait.timer !== undefined) clearTimeout(wait.timer);
|
|
74
|
+
if (wait.onData !== undefined) socket.off('data', wait.onData);
|
|
75
|
+
if (wait.onEnd !== undefined) socket.off('end', wait.onEnd);
|
|
76
|
+
signal.removeEventListener('abort', onAbort);
|
|
77
|
+
if (inFlight.fail === failRead) inFlight.fail = null;
|
|
78
|
+
fn();
|
|
79
|
+
};
|
|
80
|
+
const failRead = (reason: string) => {
|
|
81
|
+
finish(() => reject(new ValkeySocketError({ reason })));
|
|
82
|
+
};
|
|
83
|
+
const onAbort = () => failRead('read interrupted');
|
|
84
|
+
const settle = (parsed: Exclude<ParsedLine, undefined>) => {
|
|
85
|
+
state.buf = state.buf.subarray(parsed.drop);
|
|
86
|
+
finish(() => resolve(parsed.value));
|
|
87
|
+
};
|
|
88
|
+
// Pull first: the bytes this read needs may already be in the buffer.
|
|
89
|
+
const immediate = done(state.buf);
|
|
90
|
+
if (immediate !== undefined) {
|
|
91
|
+
settle(immediate);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (socket.readableEnded || socket.destroyed) {
|
|
95
|
+
failRead('socket closed mid-reply');
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
wait.onData = (chunk: Buffer) => {
|
|
99
|
+
state.buf = Buffer.concat([state.buf, chunk]);
|
|
100
|
+
// A length line with no CRLF would otherwise buffer until the peer stopped.
|
|
101
|
+
if (state.buf.length > MAX_BULK_BYTES + 64) {
|
|
102
|
+
failRead(`reply exceeded ${String(MAX_BULK_BYTES)} bytes`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const parsed = done(state.buf);
|
|
106
|
+
if (parsed === undefined) return;
|
|
107
|
+
settle(parsed);
|
|
108
|
+
};
|
|
109
|
+
wait.onEnd = () => failRead('socket closed mid-reply');
|
|
110
|
+
wait.timer = setTimeout(() => failRead('socket timed out'), timeoutMs);
|
|
111
|
+
inFlight.fail = failRead;
|
|
112
|
+
signal.addEventListener('abort', onAbort);
|
|
113
|
+
socket.on('data', wait.onData);
|
|
114
|
+
socket.on('end', wait.onEnd);
|
|
115
|
+
}),
|
|
116
|
+
catch: (cause) =>
|
|
117
|
+
cause instanceof ValkeySocketError ? cause : new ValkeySocketError({ reason: String(cause) }),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Read one RESP value. Recursion depth is the protocol's nesting, and this family
|
|
122
|
+
* rejects nesting rather than flattening it — see the nested-array branch.
|
|
123
|
+
*
|
|
124
|
+
* ★ PULL FIRST, THEN LISTEN. A reply the socket delivered whole sits in `state.buf`
|
|
125
|
+
* before this read begins. Only a genuinely incomplete prefix waits for the next chunk.
|
|
126
|
+
*/
|
|
127
|
+
export const readReply = (
|
|
128
|
+
socket: Socket,
|
|
129
|
+
state: ReadBuf,
|
|
130
|
+
command: string,
|
|
131
|
+
inFlight: InFlight,
|
|
132
|
+
timeoutMs: number,
|
|
133
|
+
): Effect.Effect<ValkeyReply, ValkeyTransportError> => {
|
|
134
|
+
const readLine = (): Effect.Effect<string, ValkeySocketError> =>
|
|
135
|
+
readUntil(socket, state, inFlight, timeoutMs, (buffer) => {
|
|
136
|
+
const nl = buffer.indexOf(CRLF);
|
|
137
|
+
return nl === -1
|
|
138
|
+
? undefined
|
|
139
|
+
: { value: buffer.subarray(0, nl).toString('utf8'), drop: nl + 2 };
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const readBytes = (n: number): Effect.Effect<string, ValkeySocketError> =>
|
|
143
|
+
Effect.gen(function* () {
|
|
144
|
+
if (state.taken + n > MAX_BULK_BYTES) {
|
|
145
|
+
return yield* Effect.fail(
|
|
146
|
+
new ValkeySocketError({
|
|
147
|
+
reason: `reply exceeded ${String(MAX_BULK_BYTES)} bytes`,
|
|
148
|
+
}),
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
state.taken += n;
|
|
152
|
+
return yield* readUntil(socket, state, inFlight, timeoutMs, (buffer) =>
|
|
153
|
+
buffer.length < n + 2
|
|
154
|
+
? undefined
|
|
155
|
+
: { value: buffer.subarray(0, n).toString('utf8'), drop: n + 2 },
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
return Effect.gen(function* () {
|
|
160
|
+
const line = yield* readLine();
|
|
161
|
+
const prefix = line[0];
|
|
162
|
+
if (prefix === '+') return { kind: 'bulk', value: line.slice(1) } as const;
|
|
163
|
+
if (prefix === '-') {
|
|
164
|
+
return yield* Effect.fail(new ValkeyServerError({ detail: line.slice(1) }));
|
|
165
|
+
}
|
|
166
|
+
if (prefix === '$') {
|
|
167
|
+
const n = boundedCount(line.slice(1), MAX_BULK_BYTES);
|
|
168
|
+
if (n === undefined) {
|
|
169
|
+
return yield* Effect.fail(
|
|
170
|
+
new ValkeySocketError({
|
|
171
|
+
reason: `bulk length ${line.slice(1)} exceeds ${String(MAX_BULK_BYTES)}`,
|
|
172
|
+
}),
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
if (n === -1) return { kind: 'bulk', value: null } as const;
|
|
176
|
+
return { kind: 'bulk', value: yield* readBytes(n) } as const;
|
|
177
|
+
}
|
|
178
|
+
if (prefix === '*') {
|
|
179
|
+
const n = boundedCount(line.slice(1), MAX_ARRAY_ITEMS);
|
|
180
|
+
if (n === undefined) {
|
|
181
|
+
return yield* Effect.fail(
|
|
182
|
+
new ValkeySocketError({
|
|
183
|
+
reason: `array length ${line.slice(1)} exceeds ${String(MAX_ARRAY_ITEMS)}`,
|
|
184
|
+
}),
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
if (n === -1) return { kind: 'array', values: [] } as const;
|
|
188
|
+
const values: Array<string | null> = [];
|
|
189
|
+
for (let i = 0; i < n; i += 1) {
|
|
190
|
+
const item = yield* readReply(socket, state, command, inFlight, timeoutMs);
|
|
191
|
+
// ⛔ A NESTED ARRAY IS A REPLY THIS FAMILY DOES NOT UNDERSTAND — failing beats
|
|
192
|
+
// null-flattening it: `values` is typed flat, so a `*1` inside `ACL GETUSER` would
|
|
193
|
+
// silently lose every flag, password and command rule it carries. The commands this
|
|
194
|
+
// family reads (`ACL LIST`, `CONFIG GET`) answer flat arrays; a future command that
|
|
195
|
+
// needs nesting must extend this union with a test, not hope.
|
|
196
|
+
if (item.kind !== 'bulk') {
|
|
197
|
+
return yield* Effect.fail(
|
|
198
|
+
new ValkeyServerError({
|
|
199
|
+
detail: `nested array inside the reply to ${command}: this family reads only flat arrays`,
|
|
200
|
+
}),
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
values.push(item.value);
|
|
204
|
+
}
|
|
205
|
+
return { kind: 'array', values } as const;
|
|
206
|
+
}
|
|
207
|
+
if (prefix === ':') return { kind: 'bulk', value: line.slice(1) } as const;
|
|
208
|
+
return yield* Effect.fail(
|
|
209
|
+
new ValkeyServerError({ detail: `unexpected RESP prefix ${prefix}` }),
|
|
210
|
+
);
|
|
211
|
+
});
|
|
212
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounds on the socket this family owns. `transport.test.ts` covers split replies and
|
|
3
|
+
* `end`; these cases are the ones that stall or kill a deploy: an `error` after connect,
|
|
4
|
+
* a reply length the server names, and a deadline on a socket that never answers.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, expect, test } from 'bun:test';
|
|
7
|
+
import * as Effect from 'effect/Effect';
|
|
8
|
+
import { EventEmitter } from 'node:events';
|
|
9
|
+
import { type Socket as NetSocket, createServer } from 'node:net';
|
|
10
|
+
import { valkeyConnection, withValkey } from './connection.ts';
|
|
11
|
+
import { type ValkeyReply, makeSocketExecutor } from './transport.ts';
|
|
12
|
+
|
|
13
|
+
interface ScriptedSocket extends EventEmitter {
|
|
14
|
+
write: (data: string) => boolean;
|
|
15
|
+
readableEnded: boolean;
|
|
16
|
+
destroyed: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const scriptedSocket = (): ScriptedSocket => {
|
|
20
|
+
const socket = new EventEmitter() as ScriptedSocket;
|
|
21
|
+
socket.write = () => true;
|
|
22
|
+
socket.readableEnded = false;
|
|
23
|
+
socket.destroyed = false;
|
|
24
|
+
return socket;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const push = async (socket: ScriptedSocket, chunk: string): Promise<void> => {
|
|
28
|
+
await new Promise<void>((resolve) => {
|
|
29
|
+
setTimeout(() => {
|
|
30
|
+
socket.emit('data', Buffer.from(chunk));
|
|
31
|
+
resolve();
|
|
32
|
+
}, 0);
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const send = (socket: ScriptedSocket, chunks: ReadonlyArray<string>): Promise<ValkeyReply> => {
|
|
37
|
+
const pending = Effect.runPromise(makeSocketExecutor(socket as never).send(['INFO']));
|
|
38
|
+
return chunks
|
|
39
|
+
.reduce((chain, chunk) => chain.then(() => push(socket, chunk)), Promise.resolve())
|
|
40
|
+
.then(() => pending);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const tagOf = async (pending: Promise<unknown>): Promise<{ _tag?: string; reason?: string }> =>
|
|
44
|
+
(await pending.catch((cause: unknown) => cause)) as { _tag?: string; reason?: string };
|
|
45
|
+
|
|
46
|
+
const listen = (hold: boolean): Promise<{ port: number; close: () => Promise<void> }> =>
|
|
47
|
+
new Promise((resolve, reject) => {
|
|
48
|
+
const peers: NetSocket[] = [];
|
|
49
|
+
const server = createServer((peer) => {
|
|
50
|
+
peers.push(peer);
|
|
51
|
+
if (!hold) peer.resetAndDestroy();
|
|
52
|
+
});
|
|
53
|
+
server.once('error', reject);
|
|
54
|
+
server.listen(0, '127.0.0.1', () => {
|
|
55
|
+
const address = server.address();
|
|
56
|
+
const port = typeof address === 'object' && address !== null ? address.port : 0;
|
|
57
|
+
resolve({
|
|
58
|
+
port,
|
|
59
|
+
close: () =>
|
|
60
|
+
new Promise((done) => {
|
|
61
|
+
for (const peer of peers) peer.destroy();
|
|
62
|
+
server.close(() => done());
|
|
63
|
+
}),
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('socket error after connect', () => {
|
|
69
|
+
test('an error during a read is ValkeySocketError, not an uncaught throw', async () => {
|
|
70
|
+
const socket = scriptedSocket();
|
|
71
|
+
const executor = makeSocketExecutor(socket as never, 500);
|
|
72
|
+
const pending = Effect.runPromise(executor.send(['PING']));
|
|
73
|
+
await new Promise<void>((resolve) => setTimeout(resolve, 0));
|
|
74
|
+
expect(() => socket.emit('error', new Error('read ECONNRESET'))).not.toThrow();
|
|
75
|
+
const error = await tagOf(pending);
|
|
76
|
+
expect(error._tag).toBe('ValkeySocketError');
|
|
77
|
+
expect(error.reason).toContain('ECONNRESET');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('an error with no in-flight read does not throw', () => {
|
|
81
|
+
const socket = scriptedSocket();
|
|
82
|
+
makeSocketExecutor(socket as never);
|
|
83
|
+
expect(() => socket.emit('error', new Error('read ECONNRESET'))).not.toThrow();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('a peer RST during a read returns ValkeySocketError', async () => {
|
|
87
|
+
const server = await listen(false);
|
|
88
|
+
try {
|
|
89
|
+
const error = await Effect.runPromise(
|
|
90
|
+
Effect.flip(
|
|
91
|
+
withValkey((executor) => executor.send(['PING'])).pipe(
|
|
92
|
+
Effect.provide(
|
|
93
|
+
valkeyConnection({ host: '127.0.0.1', port: server.port, timeoutMs: 1_000 }),
|
|
94
|
+
),
|
|
95
|
+
),
|
|
96
|
+
),
|
|
97
|
+
);
|
|
98
|
+
expect((error as { _tag?: string })._tag).toBe('ValkeySocketError');
|
|
99
|
+
} finally {
|
|
100
|
+
await server.close();
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('reply caps', () => {
|
|
106
|
+
test('a bulk length of 2147483647 fails instead of waiting for the body', async () => {
|
|
107
|
+
const error = await tagOf(send(scriptedSocket(), ['$2147483647\r\n']));
|
|
108
|
+
expect(error._tag).toBe('ValkeySocketError');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('an array count of 1000000000 fails instead of looping', async () => {
|
|
112
|
+
const error = await tagOf(send(scriptedSocket(), ['*1000000000\r\n']));
|
|
113
|
+
expect(error._tag).toBe('ValkeySocketError');
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('deadlines on a real socket', () => {
|
|
118
|
+
test('connect to a blackholed host fails with ValkeySocketError', async () => {
|
|
119
|
+
const started = Date.now();
|
|
120
|
+
const error = await Effect.runPromise(
|
|
121
|
+
Effect.flip(
|
|
122
|
+
withValkey((executor) => executor.send(['PING'])).pipe(
|
|
123
|
+
Effect.provide(valkeyConnection({ host: '192.0.2.1', port: 1, timeoutMs: 200 })),
|
|
124
|
+
),
|
|
125
|
+
),
|
|
126
|
+
);
|
|
127
|
+
expect((error as { _tag?: string })._tag).toBe('ValkeySocketError');
|
|
128
|
+
expect(Date.now() - started).toBeLessThan(1_500);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('an accepted socket that sends nothing fails with ValkeySocketError', async () => {
|
|
132
|
+
const server = await listen(true);
|
|
133
|
+
const started = Date.now();
|
|
134
|
+
try {
|
|
135
|
+
const error = await Effect.runPromise(
|
|
136
|
+
Effect.flip(
|
|
137
|
+
withValkey((executor) => executor.send(['PING'])).pipe(
|
|
138
|
+
Effect.provide(
|
|
139
|
+
valkeyConnection({ host: '127.0.0.1', port: server.port, timeoutMs: 200 }),
|
|
140
|
+
),
|
|
141
|
+
),
|
|
142
|
+
),
|
|
143
|
+
);
|
|
144
|
+
expect((error as { _tag?: string })._tag).toBe('ValkeySocketError');
|
|
145
|
+
expect(Date.now() - started).toBeLessThan(1_500);
|
|
146
|
+
} finally {
|
|
147
|
+
await server.close();
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport failures and the reply shapes a `ValkeyExecutor` returns.
|
|
3
|
+
*
|
|
4
|
+
* Split from `transport.ts` so the RESP reader can fail these tags without importing
|
|
5
|
+
* the executor. `transport.ts` re-exports them; callers keep importing from there.
|
|
6
|
+
*/
|
|
7
|
+
import * as Data from 'effect/Data';
|
|
8
|
+
|
|
9
|
+
export type ValkeyReply =
|
|
10
|
+
| { readonly kind: 'bulk'; readonly value: string | null }
|
|
11
|
+
| { readonly kind: 'array'; readonly values: ReadonlyArray<string | null> }
|
|
12
|
+
| { readonly kind: 'error'; readonly message: string };
|
|
13
|
+
|
|
14
|
+
/** A reply that came back as an error line (`-ERR …`). `detail` is the server's own error text;
|
|
15
|
+
* the `message` getter names it without recursing. */
|
|
16
|
+
export class ValkeyServerError extends Data.TaggedError('ValkeyServerError')<{
|
|
17
|
+
readonly detail: string;
|
|
18
|
+
}> {
|
|
19
|
+
override get message(): string {
|
|
20
|
+
return this.detail;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** A socket-level failure (connection refused, reset, closed mid-reply, a reply past the cap,
|
|
25
|
+
* a deadline) — distinct from a protocol-level `ValkeyServerError` so callers can tell "server
|
|
26
|
+
* said no" from "never reached it" or "the socket this family owns was not bounded". */
|
|
27
|
+
export class ValkeySocketError extends Data.TaggedError('ValkeySocketError')<{
|
|
28
|
+
readonly reason: string;
|
|
29
|
+
}> {
|
|
30
|
+
override get message(): string {
|
|
31
|
+
return this.reason;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The two transport-level failures every `ValkeyExecutor` can surface, as one union so a
|
|
36
|
+
* caller's declared error channel stays short. */
|
|
37
|
+
export type ValkeyTransportError = ValkeyServerError | ValkeySocketError;
|