@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,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/valkey/acl.ts", "../src/valkey/acl-form.ts", "../src/valkey/acl-profiles.ts", "../src/valkey/errors.ts", "../src/secrets/write-only.ts", "../src/valkey/acl-compare.ts", "../src/valkey/acl-connection.ts", "../src/valkey/connection-service.ts", "../src/valkey/acl-managed.ts", "../src/valkey/acl-ops.ts", "../src/valkey/transport.ts", "../src/valkey/resp.ts", "../src/valkey/transport-error.ts", "../src/valkey/acl-reconcile.ts", "../src/valkey/connection.ts", "../src/valkey/instance.ts", "../src/valkey/instance-form.ts", "../src/valkey/providers.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * `Valkey.AclFile` — the per-instance ACL users, each limited to a key prefix and a fixed command\n * profile, with passwords by reference (never in state). The operations live in `acl-ops.ts`\n * (extracted to keep both files under the cap); this file is the Resource and its handlers.\n *\n * ⛔ ONE ACL FILE PER INSTANCE, SEAT USERS ONLY. A shared file would let a seat user exist on the\n * LiteLLM cache (:6380) and `litellm` on the seat store (:6381) — every seat could poison the\n * response cache. The consuming stack declares one `Valkey.AclFile` per instance, holding only\n * that instance's users.\n * ★ NO OWNERSHIP MARK (H1). `read` answers `Unowned` for a match (the same rule as\n * `Postgres.Database`), so an adopted instance's ACL needs `adopt(true)`.\n */\nimport { Resource } from 'alchemy';\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport type {\n ValkeyAclFileAttributes,\n ValkeyAclFileProps,\n ValkeyAclUserAttributes,\n} from './acl-attrs.ts';\nimport { buildDelUserArgs, passwordState, refusalBeforeWrite } from './acl-form.ts';\nimport { sameSet } from './acl-compare.ts';\nimport { aclUnreachable } from './acl-connection.ts';\nimport { managedUserNames } from './acl-managed.ts';\nimport { readAclFileConfig, readParsedUsers, readWithExecutor } from './acl-ops.ts';\nimport { reconcileWithExecutor } from './acl-reconcile.ts';\nimport { declaredChannels, effectiveKeyPrefix } from './acl-profiles.ts';\nimport { type ValkeyConnection, withValkey } from './connection.ts';\nimport { ValkeyAclFileRendered } from './errors.ts';\nimport { resolveAll } from '../secrets/write-only.ts';\nimport { ValkeyServerError } from './transport.ts';\n\nexport interface ValkeyAclFile extends Resource<\n 'Valkey.AclFile',\n ValkeyAclFileProps,\n ValkeyAclFileAttributes\n> {}\n\nexport const ValkeyAclFile = Resource<ValkeyAclFile>('Valkey.AclFile', {\n defaultRemovalPolicy: 'retain',\n});\n\nexport const isValkeyAclFile = (value: unknown): value is ValkeyAclFile =>\n (typeof value === 'object' || typeof value === 'function') &&\n value !== null &&\n (value as { Type?: unknown }).Type === 'Valkey.AclFile';\n\nexport const makeValkeyAclFileHandlers = (\n connect: typeof withValkey = withValkey,\n): Provider.ProviderService<\n ValkeyAclFile,\n ValkeyConnection,\n never,\n never,\n ValkeyConnection,\n ValkeyConnection\n> =>\n ValkeyAclFile.Provider.of({\n list: () => Effect.succeed([]),\n nuke: { skip: true },\n\n read: ({ output, olds }) =>\n Effect.gen(function* () {\n const props = output ?? olds;\n // Catch outside connect: acquisition can fail before the executor callback runs.\n // AUTH server refusals keep ValkeyServerError; no failure is reported as absence.\n const live = yield* connect(\n (ex, config) => readWithExecutor(ex, props.instance, config.username),\n props.instance,\n ).pipe(Effect.catchTag('ValkeySocketError', aclUnreachable(props.instance)));\n // ★ THE STORED SEALS SURVIVE THE RE-READ (`mcp-server.ts`'s rule): a read must never\n // mint, replace or drop a seal — it reports live users wearing the seals the store\n // already holds, so an unchanged plan stays a no-op instead of churning the row.\n if (output === undefined) return Unowned(live);\n const users: Record<string, ValkeyAclUserAttributes> = {};\n for (const [name, user] of Object.entries(live.users)) {\n users[name] = { ...user, passwordSeal: output.users[name]?.passwordSeal ?? '' };\n }\n return { instance: live.instance, users, managedUsers: managedUserNames(output, olds) };\n }),\n\n diff: ({ news, output, olds }) =>\n Effect.gen(function* () {\n if (!isResolved(news)) return undefined;\n const refused = refusalBeforeWrite(news.instance, news.users);\n if (refused !== undefined) return yield* Effect.fail(refused);\n if (output === undefined) return undefined;\n const declared = Object.keys(news.users).sort();\n // Ownership changes need an apply even if live rules already match or a removed user\n // is already absent, so the next plan has the correct managed-user inventory.\n if (!sameSet(declared, [...managedUserNames(output, olds)]))\n return { action: 'update' } as const;\n const liveNames = Object.keys(output.users).sort();\n if (news.exclusive && declared.length !== liveNames.length)\n return { action: 'update' } as const;\n for (const [name, user] of Object.entries(news.users)) {\n const live = output.users[name];\n if (\n live === undefined ||\n live.keyPrefix !== effectiveKeyPrefix(user) ||\n live.extraKeyPatterns.length > 0 ||\n !sameSet(live.channelPatterns, [...declaredChannels(user)]) ||\n live.profile !== user.profile ||\n !live.on ||\n live.nopass ||\n !live.hasPassword\n ) {\n return { action: 'update' } as const;\n }\n // A password only drifts when this process holds it and the seal disagrees — an unset\n // variable is `unknown`, and a plan-only environment must not report every user rotated.\n const { values } = resolveAll({ password: user.password });\n if (passwordState(live.passwordSeal, values.password) === 'stale') {\n return { action: 'update' } as const;\n }\n }\n return { action: 'noop' } as const;\n }),\n\n reconcile: ({ news, output, olds }) =>\n // The catch covers connect and the whole operation. A server refusal keeps its own tag:\n // the server was reached, it refused. A socket failure names the selected endpoint.\n connect(\n (ex, config) => reconcileWithExecutor(ex, news, output, process.env, config.username, olds),\n news.instance,\n ).pipe(Effect.catchTag('ValkeySocketError', aclUnreachable(news.instance))),\n\n delete: ({ olds, output }) =>\n connect(\n (ex, config) =>\n Effect.gen(function* () {\n // ⛔ GATE FIRST (F3): a rendered aclfile is the source of truth, so this family deletes\n // nothing there either — the file's renderer re-adds what the kit would DELUSER.\n const aclfile = yield* readAclFileConfig(ex);\n if (aclfile !== '') {\n return yield* Effect.fail(\n new ValkeyAclFileRendered({ instance: olds.instance, path: aclfile }),\n );\n }\n // Removing the declaration removes every user it managed (the engine runs this only\n // when a human opts out of retain); the instance itself is untouched, and undeclared\n // users are preserved (F1). `ACL DELUSER` ignores absent names (Valkey docs);\n // reading first also avoids an empty DELUSER command on a repeated delete (S14).\n // ★ OWNERSHIP IS THE STORED RECORD, THE SAME HELPER REVOCATION USES. `olds` is the last\n // declaration the engine committed — it is written BEFORE reconcile runs, so after a\n // failed update it names users this resource never managed — and `output.users` is\n // every observed user. `managedUserNames` reads `output.managedUsers` (written only by\n // a successful reconcile) and falls back to `olds` for state that predates it. `live`\n // already hides `default` and the connection user, so neither can be a target.\n const live = yield* readParsedUsers(ex, config.username);\n const targets = managedUserNames(output, olds).filter(\n (name) => live[name] !== undefined,\n );\n if (targets.length === 0) return;\n const reply = yield* ex.send(buildDelUserArgs(targets));\n if (reply.kind === 'error') {\n return yield* Effect.fail(new ValkeyServerError({ detail: reply.message }));\n }\n return;\n }),\n olds.instance,\n ).pipe(Effect.catchTag('ValkeySocketError', aclUnreachable(olds.instance))),\n });\n\nexport const valkeyAclFileHandlers = makeValkeyAclFileHandlers();\n\nexport const ValkeyAclFileProvider = () => Provider.succeed(ValkeyAclFile, valkeyAclFileHandlers);\n",
|
|
6
|
+
"/**\n * Pure helpers for `Valkey.AclFile`: building `ACL SETUSER` argument lists, parsing `ACL LIST`\n * lines, computing the create/update/remove plan, and the refusals that run before any write.\n *\n * ⛔ THE COMMAND ALLOW-LIST IS A FIXED PROFILE, NOT PROP (see `acl-profiles.ts`). Widening in the\n * declaration would let a seat request `FLUSHALL` or `ACL SETUSER` and defeat the isolation the\n * resource exists to enforce.\n * ★ `reset` IS LOAD-BEARING, MEASURED ON VALKEY 9.1.1 (2026-09-29). `ACL SETUSER` ADDS rules; a\n * second `>pw` without `reset` left TWO valid password hashes and two key patterns — a rotated\n * password kept authenticating forever. `reset` first, rebuild, makes the command idempotent:\n * after it the user holds exactly one password, one key pattern and one channel pattern.\n * ★ THE PASSWORD GOES ON THE WIRE AS `#<sha256>`, NOT `>plaintext` (round-3). Valkey accepts the\n * hash form (ct100#117's templates render `#{{ . | sha256Hex }}` and the 9.1.1 scratch\n * authenticated against it, measured 2026-09-30): the plaintext never leaves the deploying\n * process's memory at all, and a command echo or audit log holds only an unreversible hash.\n * ★ THE SERVER'S ECHO IS NORMALISED, ALSO MEASURED. `ACL LIST` answers the stored rule list, and:\n * the `reset` baseline appears as an explicit `-@all` token (which survives until `+@all`\n * cancels it — a fresh user written WITHOUT `reset` echoed `-@all` too); `allchannels` echoes\n * as the `&*` pattern; an empty channel list echoes the bare `resetchannels` marker (which\n * means NO channel access — PUBLISH as such a user answered `NOPERM`); `reset` itself is\n * dropped from the echo. Every comparison normalises that: drop `-@all` before comparing\n * `SEAT_RULES` (the seat set never grants it back), compare the other sets directly, ignore\n * the bare `resetchannels` marker when parsing.\n */\nimport { sha256 } from 'alchemy/Util/sha256';\nimport * as Effect from 'effect/Effect';\nimport type { ValkeyAclUser } from './acl-attrs.ts';\nimport { declaredChannels, declaredKeyPatterns, profileRules } from './acl-profiles.ts';\nimport {\n ValkeyAclChannelPatterns,\n ValkeyAclMonitorKeyPrefix,\n ValkeyAclNameGlob,\n ValkeyAclParseError,\n ValkeyAclReservedUser,\n ValkeyAclSeatKeyPrefix,\n ValkeyAclUserNameMismatch,\n} from './errors.ts';\nimport { type Environment, type FromEnv, resolveAll, sealMatches } from '../secrets/write-only.ts';\nimport { matchesDeclared } from './acl-compare.ts';\n\n/** Glob characters a username must not carry: the name is interpolated into patterns. */\nconst NAME_GLOB = /[*?[\\]\\\\]/;\n\n/** The `#<sha256>` token `ACL SETUSER` accepts in place of `>plaintext`. */\nexport const passwordToken = (password: string): Effect.Effect<string> =>\n Effect.map(sha256(password), (hash) => `#${hash}`);\n\n/**\n * The exact `ACL SETUSER` argument list the family issues for one user: reset the blank user,\n * enable it, one password hash, channels reset to the profile's scope, the declared key pattern,\n * then the profile's rules. `password` is the RESOLVED value (already checked present by the\n * caller), never stored, never returned; only its sha256 crosses the wire.\n */\nexport const buildSetUserArgs = (\n user: ValkeyAclUser,\n password: string,\n): Effect.Effect<ReadonlyArray<string>> =>\n Effect.map(passwordToken(password), (token) => [\n 'ACL',\n 'SETUSER',\n user.name,\n 'reset',\n 'on',\n token,\n 'resetchannels',\n ...declaredChannels(user),\n ...declaredKeyPatterns(user),\n ...profileRules(user.profile),\n ]);\n\n/** One `ACL LIST` line, parsed. `rules` holds the `+`/`-` tokens the server echoes (normalised,\n * see the header); `keyPatterns`/`channelPatterns` keep their `~`/`&`; flags are booleans. */\nexport interface ParsedAclUser {\n readonly name: string;\n readonly on: boolean;\n readonly nopass: boolean;\n /** Any password token (`#<hash>`, `>pw`, `<pw`) present. */\n readonly hasPassword: boolean;\n readonly keyPatterns: ReadonlyArray<string>;\n readonly channelPatterns: ReadonlyArray<string>;\n readonly rules: ReadonlyArray<string>;\n}\n\n/** Tokens the server echoes as state markers, never as rules or patterns. */\nconst MARKER_TOKENS = new Set(['resetchannels', 'resetkeys', 'resetpass', 'reseton', 'resetoff']);\n\n/** Parse one `ACL LIST` line into `ParsedAclUser`. Throws `ValkeyAclParseError` on a malformed\n * line. */\nexport const parseAclLine = (line: string): ParsedAclUser => {\n const parts = line.split(' ');\n if (parts[0] !== 'user' || parts[1] === undefined || parts[1] === '') {\n throw new ValkeyAclParseError();\n }\n const on = parts[2] === 'on';\n const nopass = parts.includes('nopass');\n const keyPatterns: string[] = [];\n const channelPatterns: string[] = [];\n const rules: string[] = [];\n let hasPassword = false;\n for (const part of parts.slice(2)) {\n if (part.startsWith('~')) keyPatterns.push(part);\n else if (part.startsWith('&')) channelPatterns.push(part);\n else if (part.startsWith('>') || part.startsWith('<') || part.startsWith('#'))\n hasPassword = true;\n else if (part.startsWith('+') || part.startsWith('-')) rules.push(part);\n else if (MARKER_TOKENS.has(part)) continue;\n // Anything else (a flag a future Valkey adds) is neither a rule nor a pattern nor a\n // credential; ignored.\n }\n return { name: parts[1], on, nopass, hasPassword, keyPatterns, channelPatterns, rules };\n};\n\n/** Whether a seat-profile user's key prefix escapes its own keyspace. `keyPrefix` goes on the\n * wire as `~<keyPrefix>`, so read-back compares the declaration to itself: `*`, `grok:*` or\n * `claude:grok:*` would converge and grant keys the seat does not own. CT100's template renders\n * exactly `~${user}:*`. A service user is exempt: owning the whole instance is what that\n * profile is for (LiteLLM's cache user, `*`). A monitor user is keyless and may not carry a\n * prefix at all. */\nexport const seatKeyPrefixEscapes = (user: ValkeyAclUser): boolean =>\n user.profile === 'seat' && (user.keyPrefix ?? `${user.name}:*`) !== `${user.name}:*`;\n\n/** Refusals must run before any `ACL SETUSER`. A mismatched record key would split one user into\n * two records; a reserved name would reset the credential this kit authenticates with; a glob in\n * a name would widen the patterns the profile fixes; a seat prefix or monitor key prefix\n * contradicts the profile's fixed shape. */\nexport const refusalBeforeWrite = (\n instance: string,\n users: Readonly<Record<string, ValkeyAclUser>>,\n self?: string,\n):\n | ValkeyAclUserNameMismatch\n | ValkeyAclReservedUser\n | ValkeyAclSeatKeyPrefix\n | ValkeyAclMonitorKeyPrefix\n | ValkeyAclNameGlob\n | ValkeyAclChannelPatterns\n | undefined => {\n for (const [key, user] of Object.entries(users)) {\n if (user.name !== key) {\n return new ValkeyAclUserNameMismatch({ instance, recordKey: key, user: user.name });\n }\n if (user.name === 'default' || user.name === self) {\n return new ValkeyAclReservedUser({ instance, user: user.name });\n }\n const glob = NAME_GLOB.exec(user.name);\n if (glob !== null) {\n return new ValkeyAclNameGlob({ instance, user: user.name, chars: glob[0] });\n }\n if (seatKeyPrefixEscapes(user)) {\n return new ValkeyAclSeatKeyPrefix({\n instance,\n user: user.name,\n keyPrefix: user.keyPrefix ?? `${user.name}:*`,\n });\n }\n if (\n user.channelPatterns !== undefined &&\n ((user.profile === 'monitor' && user.channelPatterns.length > 0) ||\n (user.profile === 'seat' &&\n (user.channelPatterns.length !== 1 || user.channelPatterns[0] !== `${user.name}:*`)))\n )\n return new ValkeyAclChannelPatterns({ instance, user: user.name });\n if (user.profile === 'monitor' && user.keyPrefix !== undefined) {\n return new ValkeyAclMonitorKeyPrefix({\n instance,\n user: user.name,\n keyPrefix: user.keyPrefix,\n });\n }\n }\n return undefined;\n};\n\n/** How the environment compares to what the family last wrote for one user, by seal.\n * `unknown` — the variable is unset here, so the plan cannot tell (never drift: a plan-only\n * environment must not report every user as rotated). `stale` — no seal yet (an adopted user) or\n * the seal disagrees with the current value. `match` — the seal was made from this value. */\nexport type PasswordState = 'unknown' | 'stale' | 'match';\n\nexport const passwordState = (sealed: string, value: string | undefined): PasswordState => {\n if (value === undefined) return 'unknown';\n if (sealed === '') return 'stale';\n return sealMatches(sealed, { password: value }) ? 'match' : 'stale';\n};\n\n/** The password value of one user in `env`, or undefined when the reference is missing. */\nexport const resolvedPassword = (user: ValkeyAclUser, env: Environment): string | undefined => {\n const ref: Record<string, FromEnv> = { [user.name]: user.password };\n const { values, missing } = resolveAll(ref, env);\n if (missing.length > 0) return undefined;\n return values[user.name];\n};\n\n/** The plan reconcile walks: `create` (absent live), `update` (present but not exactly as\n * declared — see `matchesDeclared`), `remove` (previously managed users removed from the\n * declaration, plus all undeclared users under `exclusive: true`) and `undeclared` (all live\n * names outside the current declaration, reported with their deletion/preservation decision). */\nexport interface AclUserPlan {\n readonly create: ReadonlyArray<ValkeyAclUser>;\n readonly update: ReadonlyArray<ValkeyAclUser>;\n readonly remove: ReadonlyArray<string>;\n readonly undeclared: ReadonlyArray<string>;\n}\n\nexport const planAclUsers = (\n declared: Readonly<Record<string, ValkeyAclUser>>,\n live: Readonly<Record<string, ParsedAclUser>>,\n exclusive = false,\n managed: ReadonlyArray<string> = [],\n): AclUserPlan => {\n const create: ValkeyAclUser[] = [];\n const update: ValkeyAclUser[] = [];\n const undeclared: string[] = [];\n for (const [name, user] of Object.entries(declared)) {\n const liveUser = live[name];\n // ⛔ UPDATE MUST CATCH EVERY VISIBLE DISAGREEMENT, NOT JUST A CHANGED PREFIX. A user whose\n // rules were widened outside the stack (or disabled, or stripped of its password) is exactly\n // what this family exists to restore; a prefix-only criterion would leave it failing\n // read-back forever instead of converging on the next plan.\n if (liveUser === undefined) create.push(user);\n else if (!matchesDeclared(liveUser, user)) update.push(user);\n }\n for (const name of Object.keys(live)) {\n if (declared[name] === undefined) undeclared.push(name);\n }\n return {\n create,\n update,\n remove: exclusive ? undeclared : undeclared.filter((name) => managed.includes(name)),\n undeclared,\n };\n};\n\n/** The exact `ACL DELUSER` argument list. */\nexport const buildDelUserArgs = (users: ReadonlyArray<string>): ReadonlyArray<string> => [\n 'ACL',\n 'DELUSER',\n ...users,\n];\n",
|
|
7
|
+
"/**\n * The fixed ACL profiles this family can express, as the exact rule tokens the wire carries.\n *\n * ⛔ THE COMMAND ALLOW-LIST IS A FIXED PROFILE, NOT A PROP (see `acl-attrs.ts`). Every rule set\n * here mirrors `homeflare-ct100/src/valkey-acl.ts` (ct100#117) token for token, measured on the\n * 9.1.1 scratch; a free-form rules prop would let a declaration widen itself and request\n * `FLUSHALL` or `ACL SETUSER`, defeating the isolation the resource exists to enforce.\n *\n * ★ THE `monitor` PROFILE EXISTS BECAUSE CT100 CARRIES A KEY-LESS `monitor` USER FOR\n * `redis_exporter` (ct100#117). Before it existed, `seat` and `service` were the only profiles,\n * so `monitor` was undeclared on every CT100-style instance and reconcile DELETED it —\n * exporter's connection closed (measured 2026-09-30). `MONITOR_COMMANDS` is exactly what\n * `redis_exporter` v1.92.1 issues, measured: `client|setname`, `info`, `command|info`,\n * `commandlog|len` (Valkey 8.1+), `config|get`, `latency|latest`, `latency|histogram`,\n * `slowlog|get`, `slowlog|len`, plus `ping`. No keys (`resetkeys`), no channels\n * (`resetchannels`), no `client|list` (this exporter version never sends it, so it is not\n * granted).\n */\nimport type { ValkeyAclProfile, ValkeyAclUser } from './acl-attrs.ts';\n\n/** The seat command allow-list — fixed, mirrored from `homeflare-ct100/src/valkey-acl.ts`. */\nexport const SEAT_COMMANDS =\n '+@read +@write +@string +@hash +@list +@set +@sortedset +@stream +@pubsub +@connection ' +\n '+@transaction -@dangerous -@admin -keys -flushall -flushdb -monitor -acl -config ' +\n '-shutdown -debug';\n\n/** Commands that close keyspace/channel enumeration, measured as a leak without them\n * (seat-wiring-spec §5). */\nconst DENY = '-scan -randomkey -dbsize -pubsub';\n\n/** What `redis_exporter` v1.92.1 needs and nothing else (ct100#117's `monitor` note). */\nexport const MONITOR_COMMANDS =\n '-@all +ping +client|setname +info +command|info +commandlog|len +config|get ' +\n '+latency|latest +latency|histogram +slowlog|get +slowlog|len';\n\n/** The fixed rule sets, as the exact tokens sent after `reset`. */\nexport const SEAT_RULES: ReadonlyArray<string> = [...SEAT_COMMANDS.split(' '), ...DENY.split(' ')];\nexport const SERVICE_RULES: ReadonlyArray<string> = ['+@all', '-@dangerous', ...DENY.split(' ')];\nexport const MONITOR_RULES: ReadonlyArray<string> = MONITOR_COMMANDS.split(' ');\n\n/** The rule set one profile carries. */\nexport const profileRules = (profile: ValkeyAclProfile): ReadonlyArray<string> =>\n profile === 'seat' ? SEAT_RULES : profile === 'service' ? SERVICE_RULES : MONITOR_RULES;\n\n/**\n * The effective key prefix of one declared user.\n *\n * - `monitor` is key-less (ct100#117's `resetkeys`): it has no prefix, and a declaration that\n * gives it one is refused (`ValkeyAclMonitorKeyPrefix`).\n * - `seat` is pinned to `<name>:*` — the CT100 template's `~${user}:*`; anything else is refused\n * (`ValkeyAclSeatKeyPrefix`). The optional prop defaults to the pin.\n * - `service` owns the instance's declared prefix, `*` by default (LiteLLM's cache user on CT100\n * declares `hf-litellm:*`).\n */\nexport const effectiveKeyPrefix = (user: ValkeyAclUser): string =>\n user.profile === 'monitor'\n ? ''\n : user.profile === 'seat'\n ? (user.keyPrefix ?? `${user.name}:*`)\n : (user.keyPrefix ?? '*');\n\n/** The key patterns the declaration expects for one user, in the server's own echo form. */\nexport const declaredKeyPatterns = (user: ValkeyAclUser): ReadonlyArray<string> => {\n const prefix = effectiveKeyPrefix(user);\n return prefix === '' ? [] : [`~${prefix}`];\n};\n\n/**\n * The channel patterns the declaration expects for one user, in the server's own echo form.\n *\n * ★ CHANNELS COME FROM THE DECLARATION, NEVER `allchannels` (round-3 finding 2). `seat` gets\n * `&<name>:*`, `service` gets `&<keyPrefix>` (ct100#117's litellm line: `&${namespace}:*`),\n * `monitor` gets none. The old unconditional `allchannels` on `service` widened LiteLLM from\n * `&hf-litellm:*` to every channel; the \"mirrors the CT100 template\" comment next to it was\n * never true. A server normalises `&*` back to `allchannels` internally and echoes `&*`.\n */\nexport const declaredChannels = (user: ValkeyAclUser): ReadonlyArray<string> => {\n if (user.profile === 'monitor') return [];\n const prefix = effectiveKeyPrefix(user);\n return (user.channelPatterns ?? [prefix]).map((pattern) => `&${pattern}`);\n};\n",
|
|
8
|
+
"/**\n * `Valkey` family's failures as typed tags (S21).\n *\n * ⛔ NONE OF THESE IS A STATUS-CODE OR MESSAGE MATCH. They are raised by this family's own code\n * from facts it already checked — missing env vars, mismatched live state, failed read-back —\n * never from sniffing a server error's text. Socket/protocol errors use their own tags in\n * `transport.ts`.\n */\nimport * as Data from 'effect/Data';\n\n/** The live instance does not match an asserted property. Like `Postgres.DatabaseDrift`, this\n * family asserts properties at create and refuses drift rather than silently issuing a\n * reconfiguration that could drop state. */\nexport class ValkeyInstanceDrift extends Data.TaggedError('ValkeyInstanceDrift')<{\n readonly instance: string;\n readonly prop: string;\n readonly declared: unknown;\n readonly live: unknown;\n}> {\n override get message(): string {\n return (\n `Valkey.Instance \"${this.instance}\": ${this.prop} is declared as ` +\n `${JSON.stringify(this.declared)} but the live instance has ${JSON.stringify(this.live)}. ` +\n 'Update the declaration to match the live value, or change it by hand and re-plan.'\n );\n }\n}\n\n/** `Valkey.Instance` found the instance unreachable or refused authentication when reading it.\n * `host` is optional because it is the connection layer's value (`connection.ts`), never a prop\n * this resource holds — a reconcile that only knows its own declaration names the port. */\nexport class ValkeyInstanceUnreachable extends Data.TaggedError('ValkeyInstanceUnreachable')<{\n readonly instance: string;\n readonly host?: string;\n readonly port: number;\n}> {\n override get message(): string {\n return (\n `Valkey.Instance \"${this.instance}\" at ${this.host === undefined ? '' : `${this.host}:`}` +\n `${String(this.port)}: could not verify the instance. Check that the Quadlet container is ` +\n 'running and the ACL/password is correct.'\n );\n }\n}\n\n/** An ACL LIST line could not be parsed. Neither the message nor error data retain the\n * raw line: error serialization must not expose its password hash. */\nexport class ValkeyAclParseError extends Data.TaggedError('ValkeyAclParseError')<{\n // No raw ACL data.\n}> {\n override get message(): string {\n return (\n 'Valkey.AclFile: cannot parse an ACL LIST line. The line is withheld from this message ' +\n 'because it can carry a password hash.'\n );\n }\n}\n\n/** A declared ACL user is missing a referenced password; a user with no password can authenticate\n * as nobody, which would bypass isolation. */\nexport class ValkeyAclPasswordMissing extends Data.TaggedError('ValkeyAclPasswordMissing')<{\n readonly instance: string;\n readonly user: string;\n readonly variable: string;\n}> {\n override get message(): string {\n return (\n `Valkey.AclFile \"${this.instance}\": ACL user \"${this.user}\" references environment ` +\n `variable ${this.variable}, which is missing or empty at call time.`\n );\n }\n}\n\n/** The stack's own connection password is a `{ fromEnv }` reference that resolved to nothing at\n * call time. Distinct from `ValkeyInstanceUnreachable` (nothing answered on the wire) and from\n * `ValkeyAclPasswordMissing` (a DECLARED ACL user's password): this one is the credential the\n * stack itself authenticates with to reach an instance. */\nexport class ValkeyAuthPasswordMissing extends Data.TaggedError('ValkeyAuthPasswordMissing')<{\n readonly username: string | undefined;\n readonly variable: string;\n}> {\n override get message(): string {\n const who = this.username === undefined ? '' : ` as \"${this.username}\"`;\n return (\n `Valkey connection${who}: password environment variable ${this.variable} is missing or ` +\n 'empty at call time. Render the secret into the deploying process environment and re-plan.'\n );\n }\n}\n\n/** The live ACL after a reconcile does not match the declaration on read-back — the write was lost\n * or a concurrent mutator changed it before we could verify. */\nexport class ValkeyAclReadbackFailed extends Data.TaggedError('ValkeyAclReadbackFailed')<{\n readonly instance: string;\n readonly user: string;\n}> {\n override get message(): string {\n return (\n `Valkey.AclFile \"${this.instance}\": ACL reconciliation for \"${this.user}\" reported success, ` +\n 'but readback contradicts the declared rules or removal. Re-plan.'\n );\n }\n}\n\n/** A `seat`-profile user's key prefix escapes its own keyspace. The seat profile exists to pin\n * each seat to keys beginning with its own name (`~<name>:…`); `keyPrefix` goes on the wire as\n * `~<keyPrefix>`, so `*` is every key and `grok:*` is another seat's. Refused before any write —\n * a `service` user is the profile that owns a whole instance. */\nexport class ValkeyAclSeatKeyPrefix extends Data.TaggedError('ValkeyAclSeatKeyPrefix')<{\n readonly instance: string;\n readonly user: string;\n readonly keyPrefix: string;\n}> {\n override get message(): string {\n return (\n `Valkey.AclFile \"${this.instance}\": ACL user \"${this.user}\" declares key prefix ` +\n `\"${this.keyPrefix}\". A seat user is limited to \"${this.user}:*\"; a user that owns ` +\n 'every key on the instance declares profile \"service\".'\n );\n }\n}\n\n/** A declaration names `default` or the connection username. `ACL SETUSER` `reset` would replace\n * the credential this kit authenticates with (`~* +@all`) with a profile that cannot run `ACL`,\n * and the next `ACL LIST` answers `NOPERM`. Refused before any write. */\nexport class ValkeyAclReservedUser extends Data.TaggedError('ValkeyAclReservedUser')<{\n readonly instance: string;\n readonly user: string;\n}> {\n override get message(): string {\n return (\n `Valkey.AclFile \"${this.instance}\": refusing to manage ACL user \"${this.user}\". ` +\n 'That name is `default` or the connection username. ACL SETUSER reset would replace the ' +\n 'credential this kit authenticates with, and the next ACL LIST would answer NOPERM.'\n );\n }\n}\n\n/** A live or stored ACL user is keyed under one name but its line carries another — the parsed\n * `user` name disagrees with the record key this family stores it under. The record key is this\n * family's identity, so the mismatch means the ACL was changed by something else. */\nexport class ValkeyAclUserNameMismatch extends Data.TaggedError('ValkeyAclUserNameMismatch')<{\n readonly instance: string;\n readonly recordKey: string;\n readonly user: string;\n}> {\n override get message(): string {\n return (\n `Valkey.AclFile \"${this.instance}\": ACL user \"${this.user}\" was found keyed under ` +\n `\"${this.recordKey}\". This family keys every user by its own name; re-plan or fix the ` +\n 'ACL by hand on the instance.'\n );\n }\n}\n\nexport type ValkeyError =\n | ValkeyInstanceDrift\n | ValkeyInstanceUnreachable\n | ValkeyAclParseError\n | ValkeyAclPasswordMissing\n | ValkeyAclReadbackFailed\n | ValkeyAclUserNameMismatch\n | ValkeyAclReservedUser\n | ValkeyAclSeatKeyPrefix\n | ValkeyAclMonitorKeyPrefix\n | ValkeyAclNameGlob\n | ValkeyAclChannelPatterns\n | ValkeyAclFileRendered\n | ValkeyAuthPasswordMissing;\n\n/** A `monitor`-profile user was declared with a key prefix. The monitor profile exists for the\n * key-less `redis_exporter` identity (ct100#117): its rules carry `resetkeys`, and a `~` pattern\n * next to them would contradict the key-less shape the profile is fixed to. Refused before any\n * write. */\nexport class ValkeyAclMonitorKeyPrefix extends Data.TaggedError('ValkeyAclMonitorKeyPrefix')<{\n readonly instance: string;\n readonly user: string;\n readonly keyPrefix: string;\n}> {\n override get message(): string {\n return (\n `Valkey.AclFile \"${this.instance}\": ACL user \"${this.user}\" declares profile \"monitor\" ` +\n `with key prefix \"${this.keyPrefix}\". The monitor profile is key-less (resetkeys); omit ` +\n 'the keyPrefix.'\n );\n }\n}\n\n/** A username carries glob characters (`*`, `?`, `[`, `]`). Names are interpolated into key and\n * channel patterns (`~<name>:*`, `&<name>:*`), so a glob in the name would widen the patterns\n * the profile is fixed to — `seat*` would reach `seat2:*`. Refused before any write. */\nexport class ValkeyAclNameGlob extends Data.TaggedError('ValkeyAclNameGlob')<{\n readonly instance: string;\n readonly user: string;\n readonly chars: string;\n}> {\n override get message(): string {\n return (\n `Valkey.AclFile \"${this.instance}\": ACL user \"${this.user}\" contains glob characters ` +\n `(${this.chars}). A name is interpolated into key and channel patterns, so a glob would ` +\n 'widen the isolation the profile fixes. Use a glob-free name.'\n );\n }\n}\n\n/** The instance renders its ACL from a file (`--aclfile`, answered by `CONFIG GET aclfile`), so\n * the FILE is the source of truth and this family cannot manage it: the kit speaks RESP only, it\n * can neither write the file nor outlast the renderer that owns it (CT100: openbao-agent renders\n * `/etc/valkey/*-acl.conf` and mounts it read-only — a runtime `ACL SETUSER` there is reverted by\n * the next render/restart, measured 2026-09-30 as a WRONGPASS seat). Declare those users in the\n * rendering template instead, or use kit-owned runtime ACL management without a configured ACL file. */\nexport class ValkeyAclFileRendered extends Data.TaggedError('ValkeyAclFileRendered')<{\n readonly instance: string;\n readonly path: string;\n}> {\n override get message(): string {\n return (\n `Valkey.AclFile \"${this.instance}\": the instance loads its ACL from \"${this.path}\" ` +\n '(CONFIG GET aclfile), so the file — not this family — is the source of truth. The kit ' +\n 'speaks RESP only and can neither write that file nor survive the renderer that owns it; ' +\n 'a runtime ACL SETUSER there is reverted on the next render or restart. Declare the users ' +\n 'in the rendering template. Adoption also reconciles and is refused. ACL reads require ' +\n 'ACL LIST permission, which CT100 rendered users do not have.'\n );\n }\n}\n\n/** A fixed profile cannot widen its channel boundary by declaration. */\nexport class ValkeyAclChannelPatterns extends Data.TaggedError('ValkeyAclChannelPatterns')<{\n readonly instance: string;\n readonly user: string;\n}> {\n override get message(): string {\n return (\n `Valkey.AclFile \"${this.instance}\": invalid channel patterns for \"${this.user}\". ` +\n 'Seats require <name>:*; monitors require no channels.'\n );\n }\n}\n",
|
|
9
|
+
"/**\n * Values a declaration may SEND and must never STORE — a webhook secret, a header carrying a\n * credential, an smtp password — and the one form in which a plan may still remember them.\n *\n * ★ FAMILY-NEUTRAL ON PURPOSE, MOVED HERE FROM `proxmox/` 2026-09-22. The rule it encodes is not\n * about Proxmox: Alchemy's state store is the same unencrypted store for every provider, so the\n * Cloudflare MCP server entry's `auth_credentials` needs exactly this shape. A second copy under\n * `cloudflare/` would be two seal formats for one rule; a cross-family import would make\n * Cloudflare depend on Proxmox. Neither; it lives on its own.\n *\n * ⛔ A SECRET PROP IS A SECRET IN THE STATE STORE, WHATEVER TYPE IT HAS. Alchemy persists props as\n * well as attributes — `delete` is handed `olds`, which can only have come from the store — and\n * it does not encrypt either: `StateEncoding.ts` TAGS a `Redacted` value and writes the inner\n * value beside the tag. So `Redacted` hides a secret from a log line and from nothing else.\n * notification-target.ts (PVE) answered that by making no secret declarable at all, and paid\n * for it: an authenticated smtp target or a signed webhook could not be created from code.\n * ★ SO THE PROP HOLDS A NAME, NOT A VALUE. `{ fromEnv: 'PBS_HOOK_TOKEN' }` names the variable the\n * DEPLOYING process reads at call time — the shape forgejo/org-actions-secrets.ts already uses.\n * The name is what lands in state; the value exists only in that process's memory and on the\n * wire to the server.\n *\n * ⚠️ A PLAN STILL NEEDS TO KNOW WHETHER A VALUE CHANGED, AND IT CANNOT ASK THE SERVER. PBS returns a\n * webhook secret's NAME and never its value, and never returns an smtp password at all. So what\n * was last written is remembered as a SEAL: a salted scrypt digest, which lets the next plan\n * check \"is the value in my environment the one I wrote\" without the store holding anything a\n * reader could send to PBS.\n * ⚠️ A DIGEST OF A GUESSABLE SECRET IS STILL GUESSABLE. The salt defeats precomputed tables and\n * scrypt makes each guess cost memory and time; neither makes a dictionary word safe. The\n * remedy is a secret worth the name — a random token — not a faster hash.\n */\nimport { Buffer } from 'node:buffer';\nimport { randomBytes, scryptSync, timingSafeEqual } from 'node:crypto';\n\n/** A write-only value, by the NAME of the environment variable holding it. Never the value. */\nexport interface FromEnv {\n readonly fromEnv: string;\n}\n\n/** Where a value is looked up. Defaults to `process.env`, read at call time, never at load. */\nexport type Environment = Readonly<Record<string, string | undefined>>;\n\n/**\n * Each `name → FromEnv` resolved, or the variable names that could not be.\n *\n * ⚠️ AN EMPTY VARIABLE IS A MISSING ONE. A secret renderer that failed writes `NAME=` rather than\n * omitting the line, and sending `''` would store an empty secret on the server — a webhook that\n * authenticates with nothing, reported as a successful deploy.\n * ⚠️ THE VALUE IS NOT TRIMMED. A token with a trailing newline is a broken header either way, and\n * trimming here would make the seal disagree with a value written by any other tool.\n */\nexport const resolveAll = (\n refs: Readonly<Record<string, FromEnv>>,\n env: Environment = process.env,\n): { readonly values: Readonly<Record<string, string>>; readonly missing: readonly string[] } => {\n const values: Record<string, string> = {};\n const missing: string[] = [];\n for (const [key, ref] of Object.entries(refs)) {\n const value = env[ref.fromEnv];\n if (value === undefined || value === '') missing.push(ref.fromEnv);\n else values[key] = value;\n }\n return { missing: [...new Set(missing)].sort(), values };\n};\n\n/**\n * ★ PARAMETERS ARE NODE'S OWN SCRYPT DEFAULTS (N 2^14, r 8, p 1) — 16 MiB and tens of\n * milliseconds per digest. Plan runs it a handful of times per resource, which is noise, and a\n * guesser runs it once per guess, which is the point.\n */\nconst COST = { N: 16_384, maxmem: 64 * 1024 * 1024, p: 1, r: 8 } as const;\nconst PREFIX = 'scrypt';\n\n/** Key order must not change a digest, so entries are sorted and serialised as JSON pairs. */\nconst canonical = (values: Readonly<Record<string, string>>): string =>\n JSON.stringify(\n Object.keys(values)\n .sort()\n .map((key) => [key, values[key]]),\n );\n\nconst digest = (salt: Buffer, values: Readonly<Record<string, string>>): Buffer =>\n scryptSync(canonical(values), salt, 32, COST);\n\n/**\n * `scrypt:<salt>:<digest>`, or `''` when there is nothing to seal.\n *\n * ★ A RANDOM SALT BY DEFAULT, FOR WHAT THIS PROVIDER WROTE. Pass a fixed one only for a value the\n * server returns anyway (a header), where a fresh salt per read would churn the state row on\n * every plan without protecting anything the server does not already show its auditors.\n */\nexport const seal = (values: Readonly<Record<string, string>>, salt?: string): string => {\n if (Object.keys(values).length === 0) return '';\n const bytes = salt === undefined ? randomBytes(16) : Buffer.from(salt, 'utf8');\n return `${PREFIX}:${bytes.toString('base64url')}:${digest(bytes, values).toString('base64url')}`;\n};\n\n/**\n * Whether `values` are the ones `sealed` was made from.\n *\n * ⚠️ A MALFORMED OR EMPTY SEAL ANSWERS FALSE, never throws: callers decide what \"no evidence\"\n * means, and every caller here treats it as \"cannot tell\" rather than as drift.\n */\nexport const sealMatches = (sealed: string, values: Readonly<Record<string, string>>): boolean => {\n if (sealed === '') return Object.keys(values).length === 0;\n const [prefix, salt, stored, extra] = sealed.split(':');\n if (prefix !== PREFIX || salt === undefined || stored === undefined || extra !== undefined) {\n return false;\n }\n const expected = Buffer.from(stored, 'base64url');\n const actual = digest(Buffer.from(salt, 'base64url'), values);\n return expected.length === actual.length && timingSafeEqual(expected, actual);\n};\n",
|
|
10
|
+
"/**\n * Comparison logic between a live `ACL LIST` line and the declaration: whether a user matches,\n * what profile its rules were recognised as, and set equality over the token lists.\n *\n * Extracted from `acl-form.ts` (which owns building and parsing) to keep both files under the cap;\n * tests import these comparisons directly.\n */\nimport type { ValkeyAclProfile, ValkeyAclUser } from './acl-attrs.ts';\nimport type { ParsedAclUser } from './acl-form.ts';\nimport {\n MONITOR_RULES,\n SEAT_RULES,\n SERVICE_RULES,\n declaredChannels,\n declaredKeyPatterns,\n} from './acl-profiles.ts';\n\n/** Whether two string lists carry the same members — order ignored, duplicates and extras caught\n * by the length guard (the echo's order is the server's business; a wider or duplicated list is\n * not). */\nexport const sameSet = (a: ReadonlyArray<string>, b: ReadonlyArray<string>): boolean => {\n if (a.length !== b.length) return false;\n const sa = new Set(a);\n const sb = new Set(b);\n if (sa.size !== sb.size) return false;\n for (const x of sa) if (!sb.has(x)) return false;\n return true;\n};\n\n/**\n * Recognise a live rule set as one of the three fixed profiles. The echo is normalised first\n * (`acl-form.ts`'s header): drop the `-@all` baseline the server echoes for a reset user, since\n * the seat set never grants it back and a `+@all` cancels it. `unrecognized` means something\n * outside the stack edited the rules — that user drifts.\n */\nexport const inferProfile = (rules: ReadonlyArray<string>): ValkeyAclProfile | 'unrecognized' => {\n const normalised = rules.filter((rule) => rule !== '-@all');\n if (sameSet(normalised, SEAT_RULES)) return 'seat';\n if (sameSet(normalised, SERVICE_RULES)) return 'service';\n if (sameSet(rules, MONITOR_RULES)) return 'monitor';\n return 'unrecognized';\n};\n\n/**\n * Whether a live user line is exactly as declared — every visible disagreement is a match failure,\n * not just a changed prefix (a widened rule set, a disabled flag, or a stripped password is exactly\n * what reconcile exists to restore). Passwords are never compared here: the live line holds only a\n * hash, and the stored seal is what `diff`/`reconcile` check for rotation.\n */\nexport const matchesDeclared = (live: ParsedAclUser, user: ValkeyAclUser): boolean =>\n live.name === user.name &&\n live.on &&\n !live.nopass &&\n live.hasPassword &&\n sameSet(live.keyPatterns, declaredKeyPatterns(user)) &&\n sameSet(live.channelPatterns, declaredChannels(user)) &&\n inferProfile(live.rules) === user.profile;\n",
|
|
11
|
+
"/** Map the whole connection operation, including socket acquisition and AUTH transport errors. */\nimport * as Effect from 'effect/Effect';\nimport { resolveValkeyConnection } from './connection-service.ts';\nimport { ValkeyInstanceUnreachable } from './errors.ts';\nimport type { ValkeySocketError } from './transport.ts';\n\nexport const aclUnreachable = (instance: string) => (_error: ValkeySocketError) =>\n Effect.gen(function* () {\n const config = yield* resolveValkeyConnection(instance);\n return yield* Effect.fail(\n new ValkeyInstanceUnreachable({ instance, host: config.host, port: config.port }),\n );\n });\n",
|
|
12
|
+
"/** Named connections keep each resource on its own server when provider layers are combined. */\nimport * as Context from 'effect/Context';\nimport * as Data from 'effect/Data';\nimport * as Effect from 'effect/Effect';\nimport * as Layer from 'effect/Layer';\nimport * as Option from 'effect/Option';\nimport type { ValkeyConnectionConfig } from './connection.ts';\n\nexport class ValkeyConnectionMissing extends Data.TaggedError('ValkeyConnectionMissing')<{\n readonly instance: string;\n}> {\n override get message(): string {\n return `Valkey connection for \"${this.instance}\" is missing; configure that instance in valkeyProviders.`;\n }\n}\n\n/** The unnamed service is only for direct transport calls; resources always select a name. */\nexport class ValkeyConnection extends Context.Service<\n ValkeyConnection,\n Effect.Effect<ValkeyConnectionConfig>\n>()('Valkey.Connection') {}\n\nconst connectionService = (\n instance?: string,\n): Context.Service<ValkeyConnection, Effect.Effect<ValkeyConnectionConfig>> =>\n instance === undefined\n ? ValkeyConnection\n : Context.Service<ValkeyConnection, Effect.Effect<ValkeyConnectionConfig>>(\n `Valkey.Connection:${instance}`,\n );\n\n/** Use `Layer.provideMerge` on providers: their handlers resolve this service later. */\nexport const valkeyConnection = (config: ValkeyConnectionConfig): Layer.Layer<ValkeyConnection> =>\n Layer.succeed(connectionService(config.instance), Effect.succeed(config));\n\nexport const resolveValkeyConnection = (\n instance?: string,\n): Effect.Effect<ValkeyConnectionConfig, ValkeyConnectionMissing, ValkeyConnection> =>\n Effect.gen(function* () {\n // No unnamed fallback: a typo must never send a seat ACL to another instance's cache.\n const service = yield* Effect.serviceOption(connectionService(instance));\n if (Option.isNone(service)) {\n return yield* Effect.fail(new ValkeyConnectionMissing({ instance: instance ?? '<unnamed>' }));\n }\n return yield* service.value;\n });\n",
|
|
13
|
+
"/** Observed users are not ownership: remember the declaration independently of ACL LIST. */\nimport type { ValkeyAclFileAttributes, ValkeyAclFileProps } from './acl-attrs.ts';\n\nexport const managedUserNames = (\n stored: ValkeyAclFileAttributes | undefined,\n previous?: ValkeyAclFileProps,\n): ReadonlyArray<string> =>\n stored?.managedUsers ??\n // Compatibility with state written before managedUsers existed: the prior declaration is\n // authoritative. Executor-only callers can identify users we wrote by their stored seals.\n (previous === undefined\n ? Object.keys(stored?.users ?? {}).filter((name) => stored?.users[name]?.passwordSeal !== '')\n : Object.keys(previous.users));\n",
|
|
14
|
+
"/**\n * `Valkey.AclFile` READ operations, engine-independent: reading the live ACL into attributes,\n * and the aclfile probe the write path gates on. Extracted from `acl.ts` (the Resource and its\n * handlers) to keep files under the cap; `acl.test.ts` drives these against `fake-valkey.ts`.\n *\n * ⛔ PASSWORD BY REFERENCE. Each `user.password` is a `{ fromEnv }` reference; state stores a\n * scrypt seal so the next plan can tell whether the value changed without holding the value.\n * See `secrets/write-only.ts` and `acl-attrs.ts`.\n * ★ NO OWNERSHIP MARK (H1). `read` answers `Unowned` for a match (the same rule as\n * `Postgres.Database`), so an adopted instance's ACL needs `adopt(true)`.\n * ★ THE SEALS THREAD THROUGH `output` THE WAY `LiteLLM.MCPServer`'s `credentialSeal` DOES\n * (`mcp-server.ts`): `read` never re-seals — it keeps the seal the store already holds. The\n * write path (`acl-reconcile.ts`) mints a fresh one only for a user it actually wrote.\n */\nimport * as Effect from 'effect/Effect';\nimport { inferProfile } from './acl-compare.ts';\nimport type { ValkeyAclFileAttributes, ValkeyAclUserAttributes } from './acl-attrs.ts';\nimport { type ParsedAclUser, parseAclLine } from './acl-form.ts';\nimport { ValkeyAclParseError } from './errors.ts';\nimport {\n type ValkeyExecutor,\n ValkeyServerError,\n type ValkeyTransportError,\n arrayPairs,\n} from './transport.ts';\n\n/** Parse the live `ACL LIST` into its user lines, keyed by username. `self` is the username the\n * stack's own connection authenticates as (the instance's admin user) — never this family's to\n * manage: removing it would lock the kit out of the instance, and a `service` user cannot carry\n * ACL commands anyway, so it is skipped exactly like `default`. A line that does not parse fails\n * the whole read: a user this family cannot see is a user it cannot converge. */\nexport const readParsedUsers = (\n executor: ValkeyExecutor,\n self?: string,\n): Effect.Effect<\n Readonly<Record<string, ParsedAclUser>>,\n ValkeyAclParseError | ValkeyTransportError\n> =>\n Effect.gen(function* () {\n const reply = yield* executor.send(['ACL', 'LIST']);\n if (reply.kind === 'error') {\n return yield* Effect.fail(new ValkeyServerError({ detail: reply.message }));\n }\n if (reply.kind !== 'array') {\n return yield* Effect.fail(\n new ValkeyServerError({ detail: 'ACL LIST did not answer an array' }),\n );\n }\n const users: Record<string, ParsedAclUser> = {};\n for (const line of reply.values) {\n if (line === null) continue;\n const parsed = yield* Effect.try({\n try: () => parseAclLine(line),\n catch: () => new ValkeyAclParseError(),\n });\n if (parsed.name === 'default' || parsed.name === self) continue;\n users[parsed.name] = parsed;\n }\n return users;\n });\n\n/** One user's live attributes from its parsed line. Passwords are never read (Valkey returns only\n * hashes, and this family stores seals, not values), so the seal starts empty and `read` threads\n * the stored one through `output`. */\nexport const toUserAttributes = (line: ParsedAclUser): ValkeyAclUserAttributes => ({\n name: line.name,\n keyPrefix: line.keyPatterns[0]?.slice(1) ?? '',\n extraKeyPatterns: line.keyPatterns.slice(1).map((pattern) => pattern.slice(1)),\n channelPatterns: [...line.channelPatterns],\n profile: inferProfile(line.rules),\n on: line.on,\n nopass: line.nopass,\n hasPassword: line.hasPassword,\n passwordSeal: '',\n});\n\n/** Live attributes from parsed lines. Passwords are never read (Valkey returns only hashes, and\n * this family stores seals, not values), so the seal starts empty and `read` threads the stored\n * one through `output`. */\nconst toAttributes = (\n instance: string,\n parsed: Readonly<Record<string, ParsedAclUser>>,\n): ValkeyAclFileAttributes => {\n const users: Record<string, ValkeyAclUserAttributes> = {};\n for (const [name, line] of Object.entries(parsed)) {\n users[name] = toUserAttributes(line);\n }\n return { instance, users };\n};\n\n/** Read the live ACL users into attributes, against any executor. `self` (the connection's own\n * username) is skipped along with `default` — see `readParsedUsers`. */\nexport const readWithExecutor = (\n executor: ValkeyExecutor,\n instance: string,\n self?: string,\n): Effect.Effect<ValkeyAclFileAttributes, ValkeyAclParseError | ValkeyTransportError> =>\n Effect.map(readParsedUsers(executor, self), (parsed) => toAttributes(instance, parsed));\n\n/** Probe `CONFIG GET aclfile`: empty means the kit owns ACL state (runtime SETUSER/DELUSER only —\n * never `ACL SAVE`, which errors without a configured aclfile); non-empty means a rendered file\n * is the source of truth and the write path refuses (see `ValkeyAclFileRendered`). */\nexport const readAclFileConfig = (\n executor: ValkeyExecutor,\n): Effect.Effect<string, ValkeyTransportError> =>\n Effect.gen(function* () {\n const reply = yield* executor.send(['CONFIG', 'GET', 'aclfile']);\n if (reply.kind === 'error') {\n return yield* Effect.fail(new ValkeyServerError({ detail: reply.message }));\n }\n if (reply.kind !== 'array') {\n return yield* Effect.fail(\n new ValkeyServerError({ detail: 'CONFIG GET aclfile did not answer an array' }),\n );\n }\n const path = arrayPairs(reply.values).get('aclfile');\n if (path === undefined || path === null)\n return yield* Effect.fail(\n new ValkeyServerError({ detail: 'CONFIG GET aclfile omitted aclfile' }),\n );\n return path;\n });\n",
|
|
15
|
+
"/**\n * The RESP wire the `Valkey` family speaks, hand-rolled the same way `postgres/database-sql.ts`\n * hand-rolls its four statements: there is no `@distilled.cloud/valkey` SDK (registry 404, checked\n * 2026-09-29), and `effect@4.0.0-rc.115` ships no Valkey/Redis client, so the family declares a\n * `ValkeyExecutor` seam — the real socket client below, or `fake-valkey.ts` in tests — and every\n * lifecycle function depends only on that small interface, never on `node:net`.\n * ★ Alchemy beta.79 DOES ship Redis.connect. Its internal parser/queue cannot enforce our\n * reply caps before allocating, and it exposes no deadline settings. The audited exception\n * and executable measurements are in `docs/valkey-transport.md`.\n *\n * ⛔ RESP, NOT A TAG TO VALKEY'S C VERSION. The protocol is line-and-prefix oriented and stable\n * across Valkey's releases; the commands this family issues (`INFO`, `CONFIG GET`, `ACL LIST`,\n * `ACL SETUSER`, `PING`) are all long-settled. Nothing here parses a version-specific field.\n * ★ RUNTIME-NEUTRAL. The executor is a function of `node:net` sockets, no Bun API; a Bun-only\n * consumer would be a regression the postgres family's own `PgClient` avoided.\n * ⛔ AN `error` EVENT WITH NO LISTENER EXITS THE PROCESS. `openSocket` listens only until\n * `connect`. This executor installs the listener that stays for the life of the socket, and\n * fails the in-flight read with `ValkeySocketError` (measured 2026-09-30: a peer RST after\n * the connect listener was dropped was `uncaughtException` `read ECONNRESET`, exit 1).\n */\nimport * as Effect from 'effect/Effect';\nimport type { Socket } from 'node:net';\nimport { type InFlight, type ReadBuf, readReply } from './resp.ts';\nimport {\n type ValkeyReply,\n ValkeySocketError,\n type ValkeyTransportError,\n} from './transport-error.ts';\n\nexport type { ValkeyReply, ValkeyTransportError } from './transport-error.ts';\nexport { ValkeyServerError, ValkeySocketError } from './transport-error.ts';\n\n/** One command with its arguments, as the byte strings that go on the wire. */\nexport interface ValkeyCommand {\n readonly args: ReadonlyArray<string>;\n}\n\n/** The smallest thing a lifecycle function needs from a client: send one command, read the reply\n * as one of the three RESP top-level shapes the family interprets — a bulk string, a flat array of\n * bulk strings, or an error. `null` is the RESP \"nil\" reply (a missing key). Failures are the two\n * transport tags: a server error reply, or a socket failure. */\nexport interface ValkeyExecutor {\n readonly send: (args: ReadonlyArray<string>) => Effect.Effect<ValkeyReply, ValkeyTransportError>;\n}\n\n/** The per-read abort, and the idle/`connect` deadline `withValkey` sets on the socket. */\nexport const DEFAULT_SOCKET_TIMEOUT_MS = 10_000;\n\nconst CRLF = '\\r\\n';\n\nconst encodeArgs = (args: ReadonlyArray<string>): string =>\n `*${args.length}${CRLF}${args.map((a) => `$${Buffer.byteLength(a)}${CRLF}${a}`).join(CRLF)}${CRLF}`;\n\n/**\n * The real executor over a `node:net` socket. `socket` is supplied by the caller (which owns\n * connect/close), so `withValkey` can open one socket per operation and close it after.\n * `timeoutMs` bounds each read; the default matches the socket deadline in `connection.ts`.\n */\nexport const makeSocketExecutor = (\n socket: Socket,\n timeoutMs: number = DEFAULT_SOCKET_TIMEOUT_MS,\n): ValkeyExecutor => {\n const inFlight: InFlight = { fail: null };\n let socketFailure: string | undefined;\n const onError = (error: Error) => {\n socketFailure = error.message;\n inFlight.fail?.(error.message);\n };\n socket.on('error', onError);\n return {\n send: (args) => {\n if (socketFailure !== undefined || socket.destroyed) {\n return Effect.fail(new ValkeySocketError({ reason: socketFailure ?? 'socket closed' }));\n }\n const state: ReadBuf = { buf: Buffer.alloc(0), taken: 0 };\n // Command name only. The full argv of `AUTH` / `ACL SETUSER` includes `>password`.\n const command = args[0] ?? 'command';\n try {\n socket.write(encodeArgs(args));\n } catch (cause) {\n return Effect.fail(\n new ValkeySocketError({\n reason: cause instanceof Error ? cause.message : String(cause),\n }),\n );\n }\n // `write` can emit `error` synchronously (a peer RST already in the buffer). The\n // lifetime listener stashes it; failing here beats starting a read nothing will answer.\n if (socketFailure !== undefined || socket.destroyed) {\n return Effect.fail(new ValkeySocketError({ reason: socketFailure ?? 'socket closed' }));\n }\n return readReply(socket, state, command, inFlight, timeoutMs);\n },\n };\n};\n\n/**\n * Decode a flat array reply into `Map<string, string | null>` for the `key value key value`\n * shape `CONFIG GET` answers. Null values are preserved; a missing key is `null`, never `''`.\n */\nexport const arrayPairs = (\n values: ReadonlyArray<string | null>,\n): ReadonlyMap<string, string | null> => {\n const out = new Map<string, string | null>();\n for (let i = 0; i + 1 < values.length; i += 2) {\n out.set(values[i] ?? '', values[i + 1] ?? null);\n }\n return out;\n};\n",
|
|
16
|
+
"/**\n * One RESP reply, pulled off the socket this family owns.\n *\n * ⛔ THE SERVER'S `$N` / `*N` IS NOT A BUDGET. `INFO`, `ACL LIST` and `CONFIG GET` are\n * small. `$2147483647` or `*1000000000` would grow memory or loop in the same process\n * that holds every other provider (measured 2026-09-30: both reads stayed pending).\n * Past the cap the read fails `ValkeySocketError` before it waits for the body.\n * ★ THE PER-READ DEADLINE DOES NOT RESET WHEN A BYTE ARRIVES. `socket.setTimeout` is an\n * idle timer; a peer that dribbles would stretch it. This abort does not.\n */\nimport * as Effect from 'effect/Effect';\nimport type { Socket } from 'node:net';\nimport {\n type ValkeyReply,\n ValkeyServerError,\n ValkeySocketError,\n type ValkeyTransportError,\n} from './transport-error.ts';\n\n/** 1 MiB. The three replies this family reads are kilobytes. */\nexport const MAX_BULK_BYTES = 1_048_576;\n\n/** `ACL LIST` is one element per user; `CONFIG GET` is two per key. */\nexport const MAX_ARRAY_ITEMS = 10_000;\n\nconst CRLF = '\\r\\n';\n\n/** A mutable buffer shared between recursive RESP reads so array items consume bytes\n * from the same stream the outer `*` marker was read from. */\nexport interface ReadBuf {\n buf: Buffer;\n /** Bytes already accepted in this reply. A sum of in-cap bulks must not pass the cap either. */\n taken: number;\n}\n\n/** The in-flight read, so a lifetime `error` listener can fail it. `fail` is `null` when\n * no read is waiting — `undefined` is not assignable under `exactOptionalPropertyTypes`. */\nexport interface InFlight {\n fail: ((reason: string) => void) | null;\n}\n\ntype ParsedLine = { readonly value: string; readonly drop: number } | undefined;\n\n/** `-1` (RESP nil) or a non-negative integer at most `cap`. Anything else is a refusal. */\nconst boundedCount = (raw: string, cap: number): number | undefined => {\n if (!/^(?:-1|\\d+)$/.test(raw)) return undefined;\n const n = Number(raw);\n if (!Number.isSafeInteger(n) || n > cap) return undefined;\n return n;\n};\n\nconst readUntil = (\n socket: Socket,\n state: ReadBuf,\n inFlight: InFlight,\n timeoutMs: number,\n done: (buffer: Buffer) => ParsedLine,\n): Effect.Effect<string, ValkeySocketError> =>\n Effect.tryPromise({\n try: (signal) =>\n new Promise<string>((resolve, reject) => {\n let settled = false;\n // Assigned only when this read has to wait. The early path (bytes already buffered)\n // never registers listeners, so they live on one object the finish path can clear.\n const wait: {\n timer?: ReturnType<typeof setTimeout>;\n onData?: (chunk: Buffer) => void;\n onEnd?: () => void;\n } = {};\n const finish = (fn: () => void) => {\n if (settled) return;\n settled = true;\n if (wait.timer !== undefined) clearTimeout(wait.timer);\n if (wait.onData !== undefined) socket.off('data', wait.onData);\n if (wait.onEnd !== undefined) socket.off('end', wait.onEnd);\n signal.removeEventListener('abort', onAbort);\n if (inFlight.fail === failRead) inFlight.fail = null;\n fn();\n };\n const failRead = (reason: string) => {\n finish(() => reject(new ValkeySocketError({ reason })));\n };\n const onAbort = () => failRead('read interrupted');\n const settle = (parsed: Exclude<ParsedLine, undefined>) => {\n state.buf = state.buf.subarray(parsed.drop);\n finish(() => resolve(parsed.value));\n };\n // Pull first: the bytes this read needs may already be in the buffer.\n const immediate = done(state.buf);\n if (immediate !== undefined) {\n settle(immediate);\n return;\n }\n if (socket.readableEnded || socket.destroyed) {\n failRead('socket closed mid-reply');\n return;\n }\n wait.onData = (chunk: Buffer) => {\n state.buf = Buffer.concat([state.buf, chunk]);\n // A length line with no CRLF would otherwise buffer until the peer stopped.\n if (state.buf.length > MAX_BULK_BYTES + 64) {\n failRead(`reply exceeded ${String(MAX_BULK_BYTES)} bytes`);\n return;\n }\n const parsed = done(state.buf);\n if (parsed === undefined) return;\n settle(parsed);\n };\n wait.onEnd = () => failRead('socket closed mid-reply');\n wait.timer = setTimeout(() => failRead('socket timed out'), timeoutMs);\n inFlight.fail = failRead;\n signal.addEventListener('abort', onAbort);\n socket.on('data', wait.onData);\n socket.on('end', wait.onEnd);\n }),\n catch: (cause) =>\n cause instanceof ValkeySocketError ? cause : new ValkeySocketError({ reason: String(cause) }),\n });\n\n/**\n * Read one RESP value. Recursion depth is the protocol's nesting, and this family\n * rejects nesting rather than flattening it — see the nested-array branch.\n *\n * ★ PULL FIRST, THEN LISTEN. A reply the socket delivered whole sits in `state.buf`\n * before this read begins. Only a genuinely incomplete prefix waits for the next chunk.\n */\nexport const readReply = (\n socket: Socket,\n state: ReadBuf,\n command: string,\n inFlight: InFlight,\n timeoutMs: number,\n): Effect.Effect<ValkeyReply, ValkeyTransportError> => {\n const readLine = (): Effect.Effect<string, ValkeySocketError> =>\n readUntil(socket, state, inFlight, timeoutMs, (buffer) => {\n const nl = buffer.indexOf(CRLF);\n return nl === -1\n ? undefined\n : { value: buffer.subarray(0, nl).toString('utf8'), drop: nl + 2 };\n });\n\n const readBytes = (n: number): Effect.Effect<string, ValkeySocketError> =>\n Effect.gen(function* () {\n if (state.taken + n > MAX_BULK_BYTES) {\n return yield* Effect.fail(\n new ValkeySocketError({\n reason: `reply exceeded ${String(MAX_BULK_BYTES)} bytes`,\n }),\n );\n }\n state.taken += n;\n return yield* readUntil(socket, state, inFlight, timeoutMs, (buffer) =>\n buffer.length < n + 2\n ? undefined\n : { value: buffer.subarray(0, n).toString('utf8'), drop: n + 2 },\n );\n });\n\n return Effect.gen(function* () {\n const line = yield* readLine();\n const prefix = line[0];\n if (prefix === '+') return { kind: 'bulk', value: line.slice(1) } as const;\n if (prefix === '-') {\n return yield* Effect.fail(new ValkeyServerError({ detail: line.slice(1) }));\n }\n if (prefix === '$') {\n const n = boundedCount(line.slice(1), MAX_BULK_BYTES);\n if (n === undefined) {\n return yield* Effect.fail(\n new ValkeySocketError({\n reason: `bulk length ${line.slice(1)} exceeds ${String(MAX_BULK_BYTES)}`,\n }),\n );\n }\n if (n === -1) return { kind: 'bulk', value: null } as const;\n return { kind: 'bulk', value: yield* readBytes(n) } as const;\n }\n if (prefix === '*') {\n const n = boundedCount(line.slice(1), MAX_ARRAY_ITEMS);\n if (n === undefined) {\n return yield* Effect.fail(\n new ValkeySocketError({\n reason: `array length ${line.slice(1)} exceeds ${String(MAX_ARRAY_ITEMS)}`,\n }),\n );\n }\n if (n === -1) return { kind: 'array', values: [] } as const;\n const values: Array<string | null> = [];\n for (let i = 0; i < n; i += 1) {\n const item = yield* readReply(socket, state, command, inFlight, timeoutMs);\n // ⛔ A NESTED ARRAY IS A REPLY THIS FAMILY DOES NOT UNDERSTAND — failing beats\n // null-flattening it: `values` is typed flat, so a `*1` inside `ACL GETUSER` would\n // silently lose every flag, password and command rule it carries. The commands this\n // family reads (`ACL LIST`, `CONFIG GET`) answer flat arrays; a future command that\n // needs nesting must extend this union with a test, not hope.\n if (item.kind !== 'bulk') {\n return yield* Effect.fail(\n new ValkeyServerError({\n detail: `nested array inside the reply to ${command}: this family reads only flat arrays`,\n }),\n );\n }\n values.push(item.value);\n }\n return { kind: 'array', values } as const;\n }\n if (prefix === ':') return { kind: 'bulk', value: line.slice(1) } as const;\n return yield* Effect.fail(\n new ValkeyServerError({ detail: `unexpected RESP prefix ${prefix}` }),\n );\n });\n};\n",
|
|
17
|
+
"/**\n * Transport failures and the reply shapes a `ValkeyExecutor` returns.\n *\n * Split from `transport.ts` so the RESP reader can fail these tags without importing\n * the executor. `transport.ts` re-exports them; callers keep importing from there.\n */\nimport * as Data from 'effect/Data';\n\nexport type ValkeyReply =\n | { readonly kind: 'bulk'; readonly value: string | null }\n | { readonly kind: 'array'; readonly values: ReadonlyArray<string | null> }\n | { readonly kind: 'error'; readonly message: string };\n\n/** A reply that came back as an error line (`-ERR …`). `detail` is the server's own error text;\n * the `message` getter names it without recursing. */\nexport class ValkeyServerError extends Data.TaggedError('ValkeyServerError')<{\n readonly detail: string;\n}> {\n override get message(): string {\n return this.detail;\n }\n}\n\n/** A socket-level failure (connection refused, reset, closed mid-reply, a reply past the cap,\n * a deadline) — distinct from a protocol-level `ValkeyServerError` so callers can tell \"server\n * said no\" from \"never reached it\" or \"the socket this family owns was not bounded\". */\nexport class ValkeySocketError extends Data.TaggedError('ValkeySocketError')<{\n readonly reason: string;\n}> {\n override get message(): string {\n return this.reason;\n }\n}\n\n/** The two transport-level failures every `ValkeyExecutor` can surface, as one union so a\n * caller's declared error channel stays short. */\nexport type ValkeyTransportError = ValkeyServerError | ValkeySocketError;\n",
|
|
18
|
+
"/**\n * `Valkey.AclFile` reconcile core, against any executor. Extracted from the old single-file\n * `acl.ts` and reworked for the round-3 findings:\n *\n * ★ GATE FIRST (F3). `CONFIG GET aclfile` non-empty → `ValkeyAclFileRendered`, BEFORE any write.\n * The rendered file is the source of truth (CT100: openbao-agent renders it and a runtime\n * write reverts — the measured WRONGPASS seat). `delete` runs the same gate (`acl.ts`).\n * ★ RUNTIME-ONLY WRITES, NO `ACL SAVE` (F3). In kit-owned mode (empty aclfile) `ACL SAVE`\n * measurably errors on 8.1.10 — \"This instance is not configured to use an ACL file\" — so the\n * kit-owned family never calls it. ACL state is runtime state, not data: it does not survive\n * a restart even with appendonly (measured), so this family re-asserts on every reconcile.\n * ★ NEVER-MANAGED USERS ARE PRESERVED BY DEFAULT (F1). `planAclUsers` only DELUSERs those\n * under `exclusive: true`; removing a previously declared user always revokes it.\n */\nimport * as Effect from 'effect/Effect';\nimport { type Environment, type FromEnv, resolveAll, seal } from '../secrets/write-only.ts';\nimport type {\n ValkeyAclFileAttributes,\n ValkeyAclFileProps,\n ValkeyAclUser,\n ValkeyAclUserAttributes,\n} from './acl-attrs.ts';\nimport { managedUserNames } from './acl-managed.ts';\nimport { matchesDeclared } from './acl-compare.ts';\nimport {\n buildDelUserArgs,\n buildSetUserArgs,\n passwordState,\n planAclUsers,\n refusalBeforeWrite,\n} from './acl-form.ts';\nimport { readAclFileConfig, readParsedUsers, toUserAttributes } from './acl-ops.ts';\nimport {\n ValkeyAclFileRendered,\n ValkeyAclPasswordMissing,\n ValkeyAclReadbackFailed,\n type ValkeyError,\n} from './errors.ts';\nimport { type ValkeyExecutor, ValkeyServerError, type ValkeyTransportError } from './transport.ts';\n\n/** The reconcile core: gate on the rendered aclfile, refuse bad declarations, resolve every\n * password ONCE, plan against the live ACL, write, then re-read and verify (S10: never trust\n * the write). `stored` is the state this stack already holds (`output`); it is what adoption\n * lacks, and its absence is what makes the first plan a write. `self` is the connection's own\n * username — never managed, like `default`. */\nexport const reconcileWithExecutor = (\n executor: ValkeyExecutor,\n props: ValkeyAclFileProps,\n stored: ValkeyAclFileAttributes | undefined,\n env: Environment = process.env,\n self?: string,\n previous?: ValkeyAclFileProps,\n): Effect.Effect<ValkeyAclFileAttributes, ValkeyError | ValkeyTransportError> =>\n Effect.gen(function* () {\n // ⛔ BEFORE ANY WRITE. The aclfile gate stands in front of the declaration refusals and the\n // writes: on a rendered instance this family writes nothing at all, good or bad.\n const aclfile = yield* readAclFileConfig(executor);\n if (aclfile !== '') {\n return yield* Effect.fail(\n new ValkeyAclFileRendered({ instance: props.instance, path: aclfile }),\n );\n }\n\n // Declaration refusals (reserved names, split record key, seat prefix escapes, glob chars in\n // names, monitor-with-keyPrefix). Read hides `default` and the connection username, so a\n // declaration of either would otherwise look absent and `reset` the kit's own credential.\n const refused = refusalBeforeWrite(props.instance, props.users, self);\n if (refused !== undefined) return yield* Effect.fail(refused);\n\n // Resolve every declared password ONCE, at call time. A user the plan does not need to write\n // does not demand its variable — that is the `unknown` state, and it never drifts (diff).\n const refs: Record<string, FromEnv> = {};\n for (const [name, user] of Object.entries(props.users)) refs[name] = user.password;\n const { values } = resolveAll(refs, env);\n\n const live = yield* readParsedUsers(executor, self);\n const { create, update, remove, undeclared } = planAclUsers(\n props.users,\n live,\n props.exclusive,\n managedUserNames(stored, previous),\n );\n\n // Preserve-by-default (F1): never-managed users need exclusive ownership to be removed.\n // Previously managed users are revoked; `default`/`self` never appear in this live map.\n for (const name of undeclared) {\n yield* Effect.logWarning(\n `Valkey.AclFile \"${props.instance}\": live ACL user \"${name}\" is not declared; ${\n remove.includes(name)\n ? 'it is scheduled for deletion (previously managed or exclusive: true).'\n : 'it was left alone (exclusive: false).'\n }`,\n );\n }\n\n // Rotate: live and shaped exactly as declared, but the stored seal is absent (an adopted\n // user) or disagrees with the value this process holds — the reset-first rewrite is what\n // retires the old password, so the seal change must reach the server, not only the state.\n const rotate = Object.entries(props.users)\n .filter(([name, user]) => {\n const liveUser = live[name];\n const sealed = stored?.users[name]?.passwordSeal ?? '';\n return (\n liveUser !== undefined &&\n matchesDeclared(liveUser, user) &&\n passwordState(sealed, values[name]) === 'stale'\n );\n })\n .map(([, user]) => user);\n\n // Every write re-sends the password (`reset` clears it), so a write without the value would\n // leave the user unable to authenticate — refused, naming the first user and variable. The\n // refusal is checked for EVERY user before the first write, so a missing variable never\n // leaves half of the plan applied.\n const writes = [...create, ...update, ...rotate];\n const writeList: Array<{ readonly user: ValkeyAclUser; readonly password: string }> = [];\n for (const user of writes) {\n const password = values[user.name];\n if (password === undefined) {\n return yield* Effect.fail(\n new ValkeyAclPasswordMissing({\n instance: props.instance,\n user: user.name,\n variable: user.password.fromEnv,\n }),\n );\n }\n writeList.push({ user, password });\n }\n\n // ⛔ RUNTIME-ONLY. `reset`-first SETUSER per write; DELUSER for removed managed users or `exclusive`. No\n // `ACL SAVE`: in the only mode this family writes (empty aclfile) it measurably errors on\n // 8.1.10. See the header note for the restart semantics that follow.\n for (const { user, password } of writeList) {\n const reply = yield* executor.send(yield* buildSetUserArgs(user, password));\n if (reply.kind === 'error') {\n return yield* Effect.fail(new ValkeyServerError({ detail: reply.message }));\n }\n }\n if (remove.length > 0) {\n const reply = yield* executor.send(buildDelUserArgs(remove));\n if (reply.kind === 'error') {\n return yield* Effect.fail(new ValkeyServerError({ detail: reply.message }));\n }\n }\n\n // Read back and verify: every declared user, exactly as declared (S10). Undeclared leftovers\n // fail under `exclusive` or when a previously managed user was scheduled for revocation.\n const after = yield* readParsedUsers(executor, self);\n for (const [name, user] of Object.entries(props.users)) {\n const parsed = after[name];\n if (parsed === undefined || !matchesDeclared(parsed, user)) {\n return yield* Effect.fail(\n new ValkeyAclReadbackFailed({ instance: props.instance, user: name }),\n );\n }\n }\n if (props.exclusive || remove.length > 0) {\n for (const name of Object.keys(after)) {\n if (props.users[name] === undefined && (props.exclusive || remove.includes(name))) {\n return yield* Effect.fail(\n new ValkeyAclReadbackFailed({ instance: props.instance, user: name }),\n );\n }\n }\n }\n\n // Seals: keep what the store held for users this run did not write, mint a fresh one for\n // every user it did. A user absent from `stored` (adoption) gets its first seal here.\n const seals: Record<string, string> = {};\n for (const name of Object.keys(props.users)) {\n seals[name] = stored?.users[name]?.passwordSeal ?? '';\n }\n for (const { user, password } of writeList) seals[user.name] = seal({ password });\n const users: Record<string, ValkeyAclUserAttributes> = {};\n for (const [name, parsed] of Object.entries(after)) {\n users[name] = { ...toUserAttributes(parsed), passwordSeal: seals[name] ?? '' };\n }\n return { instance: props.instance, users, managedUsers: Object.keys(props.users).sort() };\n });\n",
|
|
19
|
+
"/**\n * Where a Valkey instance's socket comes from, and how each lifecycle operation gets an executor.\n *\n * ⛔ NO ESTATE VALUE LIVES HERE. The kit is PUBLIC; CT100's host, the two ports (6380/6381) and the\n * ACL user a seat authenticates as are the CONSUMING stack's values, passed to\n * `valkeyConnection(...)` in a house stack — never a default, never a fallback. The mini's own\n * Valkey, or any later host, is a different `valkeyConnection` call, not a constant.\n * ★ A LAZY `Context.Service` WHOSE VALUE IS AN `Effect` (S24) — the same shape `PostgresConnection`\n * uses. `host` and `port` carry no secret; the ACL user's PASSWORD is not a prop here (S25): the\n * `Valkey.AclFile` resource seals passwords by reference, and the connection itself authenticates\n * with `AUTH` only when a stack supplies a password through `valkeyConnection`'s `password`\n * reference — resolved at call time, never stored.\n * ★ ONE SOCKET PER OPERATION, OPENED AND CLOSED IN A SCOPE. `withValkey` opens a `node:net` socket\n * for the operation, runs it, and closes the socket when it finishes — one connection per\n * `reconcile`, never held across a whole plan.\n */\nimport * as Effect from 'effect/Effect';\nimport {\n type ValkeyConnection,\n type ValkeyConnectionMissing,\n resolveValkeyConnection,\n} from './connection-service.ts';\nexport {\n ValkeyConnection,\n ValkeyConnectionMissing,\n valkeyConnection,\n} from './connection-service.ts';\nimport { type Socket, connect } from 'node:net';\nimport { type Environment, type FromEnv, resolveAll } from '../secrets/write-only.ts';\nimport { ValkeyAuthPasswordMissing } from './errors.ts';\nimport {\n DEFAULT_SOCKET_TIMEOUT_MS,\n type ValkeyExecutor,\n ValkeyServerError,\n ValkeySocketError,\n type ValkeyTransportError,\n makeSocketExecutor,\n} from './transport.ts';\n\n/** What a stack passes to reach one instance. `host`/`port` are the TCP endpoint; the ACL\n * `username` and optional `password` (a `FromEnv` reference, never a value) are the credentials a\n * stack authenticates with. Omitting both sends no `AUTH` — a bare `default` connection, which is\n * correct only for a scratch instance a test owns.\n *\n * ⛔ A USERNAME REQUIRES A PASSWORD. The discriminated union makes `username` without `password`\n * unrepresentable: on an instance whose `default` user is off (CT100's ACL files ship `user\n * default off`), a username-only connection cannot authenticate at all, and silently skipping\n * `AUTH` would surface much later as a confusing `NOAUTH` from the first command. The bare\n * branch carries `username?: undefined` — a type marker, not a prop — so `config.username`\n * reads on the whole union without narrowing first.\n *\n * `timeoutMs` bounds connect, each read, and the socket idle timer (default 10s). A blackholed\n * host must stall a plan no longer than that deadline. */\nexport type ValkeyConnectionConfig = {\n /** Resource routing key: required by valkeyProviders; omit only for direct transport calls. */\n readonly instance?: string;\n readonly host: string;\n readonly port: number;\n /** Per-connect/per-read deadline in ms. */\n readonly timeoutMs?: number;\n} & (\n | { readonly username?: undefined }\n | {\n readonly username?: string;\n readonly password: FromEnv;\n }\n);\n\nconst openSocket = (\n host: string,\n port: number,\n timeoutMs: number,\n): Effect.Effect<Socket, ValkeySocketError> =>\n Effect.tryPromise({\n try: (signal) =>\n new Promise<Socket>((resolve, reject) => {\n const socket = connect({ host, port });\n let settled = false;\n const onConnect = () => {\n if (settled) return;\n settled = true;\n socket.off('error', onError);\n socket.off('timeout', onTimeout);\n signal.removeEventListener('abort', onAbort);\n clearTimeout(timer);\n socket.setTimeout(0);\n resolve(socket);\n };\n const onError = (error: Error) => {\n if (settled) return;\n settled = true;\n socket.off('connect', onConnect);\n socket.off('timeout', onTimeout);\n signal.removeEventListener('abort', onAbort);\n clearTimeout(timer);\n socket.destroy();\n reject(new ValkeySocketError({ reason: error.message }));\n };\n const onTimeout = () => onError(new Error('connect timed out'));\n const onAbort = () => onError(new Error('connect interrupted'));\n // `node:net`'s own connect timeout is minutes-long and not configurable. Both the\n // abort and `socket.setTimeout` bound the handshake; the read deadline is separate\n // (a host that accepts and then sends nothing has already connected).\n const timer = setTimeout(onTimeout, timeoutMs);\n timer.unref();\n socket.setTimeout(timeoutMs);\n signal.addEventListener('abort', onAbort);\n socket.once('connect', onConnect);\n socket.once('error', onError);\n socket.once('timeout', onTimeout);\n }),\n catch: (cause) =>\n cause instanceof ValkeySocketError ? cause : new ValkeySocketError({ reason: String(cause) }),\n });\n\n/** `AUTH` when the config carries a password, resolved at call time; a missing or empty variable\n * is the typed `ValkeyAuthPasswordMissing` (S21: a fact this family checked, not a sniffed server\n * reply). A password without a username authenticates as `default`. */\nexport const auth = (\n executor: ValkeyExecutor,\n config: ValkeyConnectionConfig,\n env: Environment,\n): Effect.Effect<void, ValkeyTransportError | ValkeyAuthPasswordMissing> => {\n if (!('password' in config)) return Effect.void;\n const { username, password } = config;\n return Effect.gen(function* () {\n const { values, missing } = resolveAll({ password }, env);\n const value = values.password;\n if (missing.length > 0 || value === undefined) {\n return yield* Effect.fail(\n new ValkeyAuthPasswordMissing({ username, variable: password.fromEnv }),\n );\n }\n const reply = yield* executor.send(\n username === undefined ? ['AUTH', value] : ['AUTH', username, value],\n );\n if (reply.kind === 'error')\n return yield* Effect.fail(new ValkeyServerError({ detail: 'Valkey AUTH was refused' }));\n });\n};\n\n/**\n * Run one operation against a freshly opened, freshly closed socket. Every `read` and `reconcile`\n * in this family calls this once for the whole operation, never once per command.\n */\nexport const withValkey = <A, E>(\n build: (\n executor: ValkeyExecutor,\n config: ValkeyConnectionConfig,\n ) => Effect.Effect<A, E | ValkeyTransportError>,\n instance?: string,\n): Effect.Effect<\n A,\n E | ValkeyTransportError | ValkeyAuthPasswordMissing | ValkeyConnectionMissing,\n ValkeyConnection\n> =>\n Effect.gen(function* () {\n const config = yield* resolveValkeyConnection(instance);\n const timeoutMs = config.timeoutMs ?? DEFAULT_SOCKET_TIMEOUT_MS;\n const socket = yield* openSocket(config.host, config.port, timeoutMs);\n // Listener first, then the idle deadline. A timeout destroys with an error, and Node\n // exits the process if that `error` event has no listener.\n const executor = makeSocketExecutor(socket, timeoutMs);\n socket.setTimeout(timeoutMs);\n const onIdle = () => {\n socket.destroy(new Error('socket timed out'));\n };\n socket.on('timeout', onIdle);\n return yield* Effect.ensuring(\n Effect.gen(function* () {\n yield* auth(executor, config, process.env);\n return yield* build(executor, config);\n }),\n Effect.sync(() => {\n socket.setTimeout(0);\n socket.off('timeout', onIdle);\n socket.destroy();\n }),\n );\n });\n",
|
|
20
|
+
"/**\n * `Valkey.Instance` — assert-and-read over a running Valkey server, reached over RESP.\n *\n * ⛔ ADOPT-AND-ASSERT, NEVER CREATE OR RECONFIGURE. The instance itself is a container the\n * `Podman.Container` family owns (`homeflare-ct100/src/valkey.ts` declares the two dormant\n * Quadlets). This resource reads `INFO` and `CONFIG GET` to confirm the live instance matches the\n * declaration, and refuses drift — it never issues `CONFIG SET` (which could drop in-flight\n * data) and never starts a process.\n * ⛔ `delete` NEVER STOPS AN INSTANCE. `ValkeyInstanceDeleteRefused` and\n * `defaultRemovalPolicy: 'retain'` are two independent reasons the engine never shuts a server\n * down through this provider.\n * ★ NO OWNERSHIP MARK (H1). `read` always answers `Unowned` for a match, the same rule\n * `Postgres.Database` uses, so every already-live instance needs `adopt(true)` in the stack\n * that declares it.\n */\nimport { Resource } from 'alchemy';\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport * as Provider from 'alchemy/Provider';\nimport * as Data from 'effect/Data';\nimport * as Effect from 'effect/Effect';\nimport { type ValkeyConnection, withValkey } from './connection.ts';\nimport { type ValkeyError, ValkeyInstanceDrift } from './errors.ts';\nimport { type ValkeyInstanceAttributes, type ValkeyInstanceProps } from './instance-attrs.ts';\nimport { buildAttributes, firstDrift, parseInfo } from './instance-form.ts';\nimport {\n type ValkeyExecutor,\n ValkeyServerError,\n type ValkeyTransportError,\n arrayPairs,\n} from './transport.ts';\n\nexport interface ValkeyInstance extends Resource<\n 'Valkey.Instance',\n ValkeyInstanceProps,\n ValkeyInstanceAttributes\n> {}\n\nexport const ValkeyInstance = Resource<ValkeyInstance>('Valkey.Instance', {\n defaultRemovalPolicy: 'retain',\n});\n\nexport const isValkeyInstance = (value: unknown): value is ValkeyInstance =>\n (typeof value === 'object' || typeof value === 'function') &&\n value !== null &&\n (value as { Type?: unknown }).Type === 'Valkey.Instance';\n\n/** `delete` never stops an instance; dropping a server is a human act on the host. */\nexport class ValkeyInstanceDeleteRefused extends Data.TaggedError('ValkeyInstanceDeleteRefused')<{\n readonly instance: string;\n}> {\n override get message(): string {\n return (\n `Valkey.Instance \"${this.instance}\": delete is refused. This provider never stops a ` +\n 'running Valkey server. Stop it by hand on the host if you mean to remove it, then remove ' +\n 'the declaration.'\n );\n }\n}\n\nexport type ValkeyInstanceError = ValkeyError | ValkeyInstanceDeleteRefused;\n\n/** Read the live instance: `INFO` then `CONFIG GET`, folded into attributes. */\nexport const readWithExecutor = (\n executor: ValkeyExecutor,\n props: Pick<ValkeyInstanceProps, 'name' | 'port'>,\n): Effect.Effect<ValkeyInstanceAttributes, ValkeyTransportError> =>\n Effect.gen(function* () {\n const infoReply = yield* executor.send(['INFO']);\n if (infoReply.kind === 'error') {\n return yield* Effect.fail(new ValkeyServerError({ detail: infoReply.message }));\n }\n const info = parseInfo(infoReply.kind === 'bulk' ? infoReply.value : null);\n const configReply = yield* executor.send([\n 'CONFIG',\n 'GET',\n 'maxmemory',\n 'maxmemory-policy',\n 'appendonly',\n ]);\n if (configReply.kind === 'error') {\n return yield* Effect.fail(new ValkeyServerError({ detail: configReply.message }));\n }\n const pairs = configReply.kind === 'array' ? arrayPairs(configReply.values) : new Map();\n return buildAttributes(props, info, {\n maxmemory: pairs.get('maxmemory') ?? undefined,\n maxmemoryPolicy: pairs.get('maxmemory-policy') ?? undefined,\n appendonly: pairs.get('appendonly') ?? undefined,\n });\n });\n\n/**\n * The five lifecycle handlers. Exported separately from the provider layer so tests can call\n * `valkeyInstanceHandlers.read(...)` directly against the real implementation without standing up\n * the engine's provider machinery.\n */\nexport const makeValkeyInstanceHandlers = (\n connect: typeof withValkey = withValkey,\n): Provider.ProviderService<\n ValkeyInstance,\n ValkeyConnection,\n never,\n never,\n ValkeyConnection,\n ValkeyConnection\n> =>\n ValkeyInstance.Provider.of({\n // ⚠️ EMPTY, NOT A LIVE SWEEP. A Valkey has no instance enumeration; adoption is one\n // declaration at a time, matching `Postgres.Database`.\n list: () => Effect.succeed([]),\n nuke: { skip: true },\n\n read: ({ output, olds }) =>\n Effect.gen(function* () {\n const props = output ?? olds;\n // Neither AUTH failure nor an unreachable endpoint proves absence. Propagate the\n // typed failure so Plan.ts cannot bypass the adoption gate with a CREATE.\n return yield* connect(\n (ex) => Effect.map(readWithExecutor(ex, { name: props.name, port: props.port }), Unowned),\n props.name,\n );\n }),\n\n diff: ({ news, output }) =>\n Effect.sync(() => {\n if (!isResolved(news)) return undefined;\n if (output === undefined) return undefined;\n const drift = firstDrift(news, output);\n return drift !== undefined\n ? ({ action: 'update' } as const)\n : ({ action: 'noop' } as const);\n }),\n\n reconcile: ({ news }) =>\n Effect.gen(function* () {\n const live = yield* connect(\n (ex) => readWithExecutor(ex, { name: news.name, port: news.port }),\n news.name,\n );\n const drift = firstDrift(news, live);\n if (drift !== undefined) {\n return yield* Effect.fail(\n new ValkeyInstanceDrift({\n instance: news.name,\n prop: drift.prop,\n declared: drift.declared,\n live: drift.live,\n }),\n );\n }\n return live;\n }),\n\n delete: ({ olds }) => Effect.fail(new ValkeyInstanceDeleteRefused({ instance: olds.name })),\n });\n\nexport const valkeyInstanceHandlers = makeValkeyInstanceHandlers();\n\nexport const ValkeyInstanceProvider = () =>\n Provider.succeed(ValkeyInstance, valkeyInstanceHandlers);\n",
|
|
21
|
+
"/**\n * Pure helpers for `Valkey.Instance`: parsing `INFO`/`CONFIG GET` replies, building drift checks,\n * and normalising optional props.\n *\n * ⛔ NO SIDE EFFECTS. Everything in this file is a pure function that can be unit-tested without a\n * socket or a fake executor.\n */\nimport type {\n ValkeyInstanceAttributes,\n ValkeyInstanceConfig,\n ValkeyInstanceInfo,\n ValkeyInstanceProps,\n} from './instance-attrs.ts';\n\n/** Parse the `INFO` bulk reply into the fields `Instance` cares about. A missing `tcp_port`\n * is 0, so a declaration of a real port drifts rather than matching a reply that omitted it. */\nexport const parseInfo = (value: string | null): ValkeyInstanceInfo => {\n const fallback = '0.0.0';\n if (value === null) return { version: fallback, port: 0 };\n const match = /valkey_version:([0-9a-zA-Z._-]+)/.exec(value);\n const port = /tcp_port:([0-9]+)/.exec(value);\n return {\n version: match?.[1] ?? fallback,\n port: port?.[1] === undefined ? 0 : Number(port[1]),\n };\n};\n\n/** Valkey `memtoull` (`src/util.c`, same on 8.1.10 and 9.1.1). `mb` is 1024², so `512mb` is\n * 536870912 and `256mb` is 268435456 — the byte string `CONFIG GET` returns for `MEMORY_CONFIG`.\n * Undefined on a parse error: a zero from the C function is \"unlimited\" only for the string `0`. */\nconst MEMORY_UNITS: Readonly<Record<string, bigint>> = {\n '': 1n,\n b: 1n,\n k: 1000n,\n kb: 1024n,\n m: 1_000_000n,\n mb: 1024n * 1024n,\n g: 1_000_000_000n,\n gb: 1024n * 1024n * 1024n,\n};\n\nexport const memoryBytes = (value: string): bigint | undefined => {\n if (value.startsWith('-')) return undefined;\n const match = /^(\\d+)([a-z]*)$/i.exec(value);\n if (match === null) return undefined;\n const digits = match[1];\n const unit = (match[2] ?? '').toLowerCase();\n const mul = MEMORY_UNITS[unit];\n if (digits === undefined || mul === undefined) return undefined;\n return BigInt(digits) * mul;\n};\n\n/** Byte equality for `maxmemory`. An unparseable side does not match, so a typo still drifts. */\nconst sameMemory = (declared: string, live: string): boolean => {\n const left = memoryBytes(declared);\n const right = memoryBytes(live);\n return left !== undefined && right !== undefined && left === right;\n};\n\n/** Fill defaults for absent optional config so `diff` and drift checks compare a concrete string. */\nconst defaultedConfig = (raw: Partial<ValkeyInstanceConfig>): ValkeyInstanceConfig => ({\n maxmemory: raw.maxmemory ?? '0',\n maxmemoryPolicy: raw.maxmemoryPolicy ?? 'noeviction',\n appendonly: raw.appendonly ?? 'no',\n});\n\n/** Build a live `ValkeyInstanceAttributes` from parsed `INFO` and `CONFIG GET` pairs. */\nexport const buildAttributes = (\n props: Omit<ValkeyInstanceProps, 'maxmemory' | 'maxmemoryPolicy' | 'appendonly'>,\n info: ValkeyInstanceInfo,\n config: Partial<ValkeyInstanceConfig>,\n): ValkeyInstanceAttributes => ({\n name: props.name,\n port: info.port,\n version: info.version,\n ...defaultedConfig(config),\n});\n\n/** Compare every declared assert-once prop against the live row. Returns the first drift, or\n * `undefined` when every declared prop matches. */\nexport const firstDrift = (\n props: ValkeyInstanceProps,\n live: ValkeyInstanceAttributes,\n): { readonly prop: string; readonly declared: unknown; readonly live: unknown } | undefined => {\n const checks: ReadonlyArray<readonly [string, unknown, unknown]> = [\n ['port', props.port, live.port],\n ['maxmemory', props.maxmemory, live.maxmemory],\n ['maxmemoryPolicy', props.maxmemoryPolicy, live.maxmemoryPolicy],\n ['appendonly', props.appendonly, live.appendonly],\n ];\n for (const [prop, declared, liveValue] of checks) {\n if (declared === undefined) continue;\n if (\n prop === 'maxmemory' &&\n typeof declared === 'string' &&\n typeof liveValue === 'string' &&\n sameMemory(declared, liveValue)\n ) {\n continue;\n }\n if (declared !== liveValue) return { prop, declared, live: liveValue };\n }\n return undefined;\n};\n",
|
|
22
|
+
"/**\n * The Valkey provider, pre-wired to one instance:\n *\n * const providers = valkeyProviders({ instance: 'valkey-seats', host: '127.0.0.1', port: 6381 });\n *\n * ★ THE INSTANCE IS A PARAMETER, NOT A DEFAULT (S15). This kit is PUBLIC; CT100's host, the two\n * ports (6380/6381) and the ACL user a seat authenticates as are the CONSUMING stack's values,\n * never baked in here. Each resource selects the connection by its instance name, so the\n * fixed provider tags cannot redirect one instance's ACL to another server.\n */\nimport * as Layer from 'effect/Layer';\nimport { ValkeyAclFileProvider } from './acl.ts';\nimport { type ValkeyConnectionConfig, valkeyConnection } from './connection.ts';\nimport { ValkeyInstanceProvider } from './instance.ts';\n\n// ⚠️ As in postgres/providers.ts and caddy/providers.ts, plain provide hides the connection\n// from lifecycle handlers invoked later by Alchemy. Keep the named service in the output.\nexport const valkeyProviders = (config: ValkeyConnectionConfig & { readonly instance: string }) =>\n Layer.mergeAll(ValkeyInstanceProvider(), ValkeyAclFileProvider()).pipe(\n Layer.provideMerge(valkeyConnection(config)),\n );\n"
|
|
23
|
+
],
|
|
24
|
+
"mappings": "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",
|
|
25
|
+
"debugId": "AD3AB456863A1F6E64756E2164756E21",
|
|
26
|
+
"names": []
|
|
27
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Valkey.Instance` props and attributes.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ CREATE-AND-ASSERT, NEVER CREATE. A Valkey instance is a container the `Podman.Container`
|
|
5
|
+
* family owns (`homeflare-ct100/src/valkey.ts` already declares the two dormant Quadlets). This
|
|
6
|
+
* resource asserts a running instance's identity and configuration — it reads `INFO` and
|
|
7
|
+
* `CONFIG GET` — and refuses drift, never issues a `CONFIG SET` that could drop in-flight data.
|
|
8
|
+
* ⛔ NO PASSWORD AS A PROP (S25). An instance's own admin password is the connection's concern
|
|
9
|
+
* (`connection.ts`), not a field stored in state; `Valkey.AclFile` owns the per-seat users.
|
|
10
|
+
*/
|
|
11
|
+
/** Declared, at-most-once-asserted properties of one running Valkey instance. */
|
|
12
|
+
export interface ValkeyInstanceProps {
|
|
13
|
+
/** The instance's logical identity — matches the Quadlet's `containerName`. */
|
|
14
|
+
readonly name: string;
|
|
15
|
+
/** The TCP port the instance listens on (the Quadlet's `--port`). */
|
|
16
|
+
readonly port: number;
|
|
17
|
+
/** `maxmemory` (`CONFIG GET maxmemory`). A declaration may be unit form (`"512mb"`);
|
|
18
|
+
* the live reply is the byte count. Compared with Valkey's `memtoull` rule. */
|
|
19
|
+
readonly maxmemory?: string | undefined;
|
|
20
|
+
/** `maxmemory-policy`, one of Valkey's eviction policies. */
|
|
21
|
+
readonly maxmemoryPolicy?: string | undefined;
|
|
22
|
+
/** `appendonly` (`yes`/`no`) — seat data is AOF, the LiteLLM cache is not. */
|
|
23
|
+
readonly appendonly?: string | undefined;
|
|
24
|
+
}
|
|
25
|
+
/** Live attributes read back after reconcile. Never holds a password (see file header). */
|
|
26
|
+
export interface ValkeyInstanceAttributes {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
readonly port: number;
|
|
29
|
+
readonly version: string;
|
|
30
|
+
readonly maxmemory: string;
|
|
31
|
+
readonly maxmemoryPolicy: string;
|
|
32
|
+
readonly appendonly: string;
|
|
33
|
+
}
|
|
34
|
+
/** The subset of `INFO` fields `read` needs. `port` is `tcp_port`, not the declared prop:
|
|
35
|
+
* copying the declaration back would make a 6381 claim match a server on 6380. */
|
|
36
|
+
export interface ValkeyInstanceInfo {
|
|
37
|
+
readonly version: string;
|
|
38
|
+
readonly port: number;
|
|
39
|
+
}
|
|
40
|
+
/** The subset of `CONFIG GET` values `read` needs. */
|
|
41
|
+
export interface ValkeyInstanceConfig {
|
|
42
|
+
readonly maxmemory: string;
|
|
43
|
+
readonly maxmemoryPolicy: string;
|
|
44
|
+
readonly appendonly: string;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=instance-attrs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance-attrs.d.ts","sourceRoot":"","sources":["../../src/valkey/instance-attrs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,iFAAiF;AACjF,MAAM,WAAW,mBAAmB;IAClC,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;mFAC+E;IAC/E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,6DAA6D;IAC7D,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9C,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C;AAED,2FAA2F;AAC3F,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;kFACkF;AAClF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,sDAAsD;AACtD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers for `Valkey.Instance`: parsing `INFO`/`CONFIG GET` replies, building drift checks,
|
|
3
|
+
* and normalising optional props.
|
|
4
|
+
*
|
|
5
|
+
* ⛔ NO SIDE EFFECTS. Everything in this file is a pure function that can be unit-tested without a
|
|
6
|
+
* socket or a fake executor.
|
|
7
|
+
*/
|
|
8
|
+
import type { ValkeyInstanceAttributes, ValkeyInstanceConfig, ValkeyInstanceInfo, ValkeyInstanceProps } from './instance-attrs.ts';
|
|
9
|
+
/** Parse the `INFO` bulk reply into the fields `Instance` cares about. A missing `tcp_port`
|
|
10
|
+
* is 0, so a declaration of a real port drifts rather than matching a reply that omitted it. */
|
|
11
|
+
export declare const parseInfo: (value: string | null) => ValkeyInstanceInfo;
|
|
12
|
+
export declare const memoryBytes: (value: string) => bigint | undefined;
|
|
13
|
+
/** Build a live `ValkeyInstanceAttributes` from parsed `INFO` and `CONFIG GET` pairs. */
|
|
14
|
+
export declare const buildAttributes: (props: Omit<ValkeyInstanceProps, 'maxmemory' | 'maxmemoryPolicy' | 'appendonly'>, info: ValkeyInstanceInfo, config: Partial<ValkeyInstanceConfig>) => ValkeyInstanceAttributes;
|
|
15
|
+
/** Compare every declared assert-once prop against the live row. Returns the first drift, or
|
|
16
|
+
* `undefined` when every declared prop matches. */
|
|
17
|
+
export declare const firstDrift: (props: ValkeyInstanceProps, live: ValkeyInstanceAttributes) => {
|
|
18
|
+
readonly prop: string;
|
|
19
|
+
readonly declared: unknown;
|
|
20
|
+
readonly live: unknown;
|
|
21
|
+
} | undefined;
|
|
22
|
+
//# sourceMappingURL=instance-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance-form.d.ts","sourceRoot":"","sources":["../../src/valkey/instance-form.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EACV,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC;AAE7B;gGACgG;AAChG,eAAO,MAAM,SAAS,UAAW,MAAM,GAAG,IAAI,KAAG,kBAShD,CAAC;AAgBF,eAAO,MAAM,WAAW,UAAW,MAAM,KAAG,MAAM,GAAG,SASpD,CAAC;AAgBF,yFAAyF;AACzF,eAAO,MAAM,eAAe,UACnB,IAAI,CAAC,mBAAmB,EAAE,WAAW,GAAG,iBAAiB,GAAG,YAAY,CAAC,QAC1E,kBAAkB,UAChB,OAAO,CAAC,oBAAoB,CAAC,KACpC,wBAKD,CAAC;AAEH;mDACmD;AACnD,eAAO,MAAM,UAAU,UACd,mBAAmB,QACpB,wBAAwB,KAC7B;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG,SAoBlF,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Valkey.Instance` — assert-and-read over a running Valkey server, reached over RESP.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ ADOPT-AND-ASSERT, NEVER CREATE OR RECONFIGURE. The instance itself is a container the
|
|
5
|
+
* `Podman.Container` family owns (`homeflare-ct100/src/valkey.ts` declares the two dormant
|
|
6
|
+
* Quadlets). This resource reads `INFO` and `CONFIG GET` to confirm the live instance matches the
|
|
7
|
+
* declaration, and refuses drift — it never issues `CONFIG SET` (which could drop in-flight
|
|
8
|
+
* data) and never starts a process.
|
|
9
|
+
* ⛔ `delete` NEVER STOPS AN INSTANCE. `ValkeyInstanceDeleteRefused` and
|
|
10
|
+
* `defaultRemovalPolicy: 'retain'` are two independent reasons the engine never shuts a server
|
|
11
|
+
* down through this provider.
|
|
12
|
+
* ★ NO OWNERSHIP MARK (H1). `read` always answers `Unowned` for a match, the same rule
|
|
13
|
+
* `Postgres.Database` uses, so every already-live instance needs `adopt(true)` in the stack
|
|
14
|
+
* that declares it.
|
|
15
|
+
*/
|
|
16
|
+
import { Resource } from 'alchemy';
|
|
17
|
+
import * as Provider from 'alchemy/Provider';
|
|
18
|
+
import * as Effect from 'effect/Effect';
|
|
19
|
+
import { type ValkeyConnection, withValkey } from './connection.ts';
|
|
20
|
+
import { type ValkeyError } from './errors.ts';
|
|
21
|
+
import { type ValkeyInstanceAttributes, type ValkeyInstanceProps } from './instance-attrs.ts';
|
|
22
|
+
import { type ValkeyExecutor, type ValkeyTransportError } from './transport.ts';
|
|
23
|
+
export interface ValkeyInstance extends Resource<'Valkey.Instance', ValkeyInstanceProps, ValkeyInstanceAttributes> {
|
|
24
|
+
}
|
|
25
|
+
export declare const ValkeyInstance: import("alchemy").ResourceClass<ValkeyInstance>;
|
|
26
|
+
export declare const isValkeyInstance: (value: unknown) => value is ValkeyInstance;
|
|
27
|
+
declare const ValkeyInstanceDeleteRefused_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
28
|
+
readonly _tag: "ValkeyInstanceDeleteRefused";
|
|
29
|
+
} & Readonly<A>;
|
|
30
|
+
/** `delete` never stops an instance; dropping a server is a human act on the host. */
|
|
31
|
+
export declare class ValkeyInstanceDeleteRefused extends ValkeyInstanceDeleteRefused_base<{
|
|
32
|
+
readonly instance: string;
|
|
33
|
+
}> {
|
|
34
|
+
get message(): string;
|
|
35
|
+
}
|
|
36
|
+
export type ValkeyInstanceError = ValkeyError | ValkeyInstanceDeleteRefused;
|
|
37
|
+
/** Read the live instance: `INFO` then `CONFIG GET`, folded into attributes. */
|
|
38
|
+
export declare const readWithExecutor: (executor: ValkeyExecutor, props: Pick<ValkeyInstanceProps, 'name' | 'port'>) => Effect.Effect<ValkeyInstanceAttributes, ValkeyTransportError>;
|
|
39
|
+
/**
|
|
40
|
+
* The five lifecycle handlers. Exported separately from the provider layer so tests can call
|
|
41
|
+
* `valkeyInstanceHandlers.read(...)` directly against the real implementation without standing up
|
|
42
|
+
* the engine's provider machinery.
|
|
43
|
+
*/
|
|
44
|
+
export declare const makeValkeyInstanceHandlers: (connect?: typeof withValkey) => Provider.ProviderService<ValkeyInstance, ValkeyConnection, never, never, ValkeyConnection, ValkeyConnection>;
|
|
45
|
+
export declare const valkeyInstanceHandlers: Provider.ProviderService<ValkeyInstance, ValkeyConnection, never, never, ValkeyConnection, ValkeyConnection, never, never, never>;
|
|
46
|
+
export declare const ValkeyInstanceProvider: () => import("effect/Layer").Layer<Provider.Provider<ValkeyInstance>, never, ValkeyConnection>;
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=instance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/valkey/instance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,KAAK,gBAAgB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,KAAK,WAAW,EAAuB,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,KAAK,wBAAwB,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE9F,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,oBAAoB,EAE1B,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,cAAe,SAAQ,QAAQ,CAC9C,iBAAiB,EACjB,mBAAmB,EACnB,wBAAwB,CACzB;CAAG;AAEJ,eAAO,MAAM,cAAc,iDAEzB,CAAC;AAEH,eAAO,MAAM,gBAAgB,UAAW,OAAO,KAAG,KAAK,IAAI,cAGD,CAAC;;;;AAE3D,sFAAsF;AACtF,qBAAa,2BAA4B,SAAQ,iCAAgD;IAC/F,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAAC;IACA,IAAa,OAAO,IAAI,MAAM,CAM7B;CACF;AAED,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG,2BAA2B,CAAC;AAE5E,gFAAgF;AAChF,eAAO,MAAM,gBAAgB,aACjB,cAAc,SACjB,IAAI,CAAC,mBAAmB,EAAE,MAAM,GAAG,MAAM,CAAC,KAChD,MAAM,CAAC,MAAM,CAAC,wBAAwB,EAAE,oBAAoB,CAuB3D,CAAC;AAEL;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,aAC5B,OAAO,UAAU,KACzB,QAAQ,CAAC,eAAe,CACzB,cAAc,EACd,gBAAgB,EAChB,KAAK,EACL,KAAK,EACL,gBAAgB,EAChB,gBAAgB,CAkDd,CAAC;AAEL,eAAO,MAAM,sBAAsB,mIAA+B,CAAC;AAEnE,eAAO,MAAM,sBAAsB,gGACuB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type FakeValkey } from './fake-valkey.ts';
|
|
2
|
+
export interface ProviderServer {
|
|
3
|
+
readonly fake: FakeValkey;
|
|
4
|
+
readonly port: number;
|
|
5
|
+
readonly close: () => Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
export declare const providerServer: (user: string) => Promise<ProviderServer>;
|
|
8
|
+
//# sourceMappingURL=provider-socket-fixture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-socket-fixture.d.ts","sourceRoot":"","sources":["../../src/valkey/provider-socket-fixture.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,UAAU,EAAkB,MAAM,kBAAkB,CAAC;AA6BnE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,eAAO,MAAM,cAAc,SAAU,MAAM,KAAG,OAAO,CAAC,cAAc,CAgChE,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
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 { type ValkeyConnectionConfig } from './connection.ts';
|
|
13
|
+
export declare const valkeyProviders: (config: ValkeyConnectionConfig & {
|
|
14
|
+
readonly instance: string;
|
|
15
|
+
}) => Layer.Layer<import("alchemy").Provider<import("./acl.ts").ValkeyAclFile> | import("alchemy").Provider<import("./instance.ts").ValkeyInstance> | import("./connection-service.ts").ValkeyConnection, never, never>;
|
|
16
|
+
//# sourceMappingURL=providers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/valkey/providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,OAAO,EAAE,KAAK,sBAAsB,EAAoB,MAAM,iBAAiB,CAAC;AAKhF,eAAO,MAAM,eAAe,WAAY,sBAAsB,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,sNAG3F,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
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 { type ValkeyReply, type ValkeyTransportError } from './transport-error.ts';
|
|
14
|
+
/** 1 MiB. The three replies this family reads are kilobytes. */
|
|
15
|
+
export declare const MAX_BULK_BYTES = 1048576;
|
|
16
|
+
/** `ACL LIST` is one element per user; `CONFIG GET` is two per key. */
|
|
17
|
+
export declare const MAX_ARRAY_ITEMS = 10000;
|
|
18
|
+
/** A mutable buffer shared between recursive RESP reads so array items consume bytes
|
|
19
|
+
* from the same stream the outer `*` marker was read from. */
|
|
20
|
+
export interface ReadBuf {
|
|
21
|
+
buf: Buffer;
|
|
22
|
+
/** Bytes already accepted in this reply. A sum of in-cap bulks must not pass the cap either. */
|
|
23
|
+
taken: number;
|
|
24
|
+
}
|
|
25
|
+
/** The in-flight read, so a lifetime `error` listener can fail it. `fail` is `null` when
|
|
26
|
+
* no read is waiting — `undefined` is not assignable under `exactOptionalPropertyTypes`. */
|
|
27
|
+
export interface InFlight {
|
|
28
|
+
fail: ((reason: string) => void) | null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Read one RESP value. Recursion depth is the protocol's nesting, and this family
|
|
32
|
+
* rejects nesting rather than flattening it — see the nested-array branch.
|
|
33
|
+
*
|
|
34
|
+
* ★ PULL FIRST, THEN LISTEN. A reply the socket delivered whole sits in `state.buf`
|
|
35
|
+
* before this read begins. Only a genuinely incomplete prefix waits for the next chunk.
|
|
36
|
+
*/
|
|
37
|
+
export declare const readReply: (socket: Socket, state: ReadBuf, command: string, inFlight: InFlight, timeoutMs: number) => Effect.Effect<ValkeyReply, ValkeyTransportError>;
|
|
38
|
+
//# sourceMappingURL=resp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resp.d.ts","sourceRoot":"","sources":["../../src/valkey/resp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EACL,KAAK,WAAW,EAGhB,KAAK,oBAAoB,EAC1B,MAAM,sBAAsB,CAAC;AAE9B,gEAAgE;AAChE,eAAO,MAAM,cAAc,UAAY,CAAC;AAExC,uEAAuE;AACvE,eAAO,MAAM,eAAe,QAAS,CAAC;AAItC;8DAC8D;AAC9D,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,gGAAgG;IAChG,KAAK,EAAE,MAAM,CAAC;CACf;AAED;4FAC4F;AAC5F,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;CACzC;AAgFD;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,WACZ,MAAM,SACP,OAAO,WACL,MAAM,YACL,QAAQ,aACP,MAAM,KAChB,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,oBAAoB,CA+EjD,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type ValkeyReply = {
|
|
2
|
+
readonly kind: 'bulk';
|
|
3
|
+
readonly value: string | null;
|
|
4
|
+
} | {
|
|
5
|
+
readonly kind: 'array';
|
|
6
|
+
readonly values: ReadonlyArray<string | null>;
|
|
7
|
+
} | {
|
|
8
|
+
readonly kind: 'error';
|
|
9
|
+
readonly message: string;
|
|
10
|
+
};
|
|
11
|
+
declare const ValkeyServerError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
12
|
+
readonly _tag: "ValkeyServerError";
|
|
13
|
+
} & Readonly<A>;
|
|
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 declare class ValkeyServerError extends ValkeyServerError_base<{
|
|
17
|
+
readonly detail: string;
|
|
18
|
+
}> {
|
|
19
|
+
get message(): string;
|
|
20
|
+
}
|
|
21
|
+
declare const ValkeySocketError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
22
|
+
readonly _tag: "ValkeySocketError";
|
|
23
|
+
} & Readonly<A>;
|
|
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 declare class ValkeySocketError extends ValkeySocketError_base<{
|
|
28
|
+
readonly reason: string;
|
|
29
|
+
}> {
|
|
30
|
+
get message(): string;
|
|
31
|
+
}
|
|
32
|
+
/** The two transport-level failures every `ValkeyExecutor` can surface, as one union so a
|
|
33
|
+
* caller's declared error channel stays short. */
|
|
34
|
+
export type ValkeyTransportError = ValkeyServerError | ValkeySocketError;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=transport-error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport-error.d.ts","sourceRoot":"","sources":["../../src/valkey/transport-error.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACxD;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;CAAE,GACzE;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;;;;AAEzD;sDACsD;AACtD,qBAAa,iBAAkB,SAAQ,uBAAsC;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;IACA,IAAa,OAAO,IAAI,MAAM,CAE7B;CACF;;;;AAED;;wFAEwF;AACxF,qBAAa,iBAAkB,SAAQ,uBAAsC;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;IACA,IAAa,OAAO,IAAI,MAAM,CAE7B;CACF;AAED;kDACkD;AAClD,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,GAAG,iBAAiB,CAAC"}
|