{"_id":"@capydb/kv","_rev":"2-444d4683408a2dfeea12b929d8f57e36","name":"@capydb/kv","dist-tags":{"latest":"2.0.0"},"versions":{"1.0.0":{"name":"@capydb/kv","version":"1.0.0","keywords":["capydb","kv","key-value","valkey","redis","rate-limit","cache","serverless"],"license":"MIT","_id":"@capydb/kv@1.0.0","maintainers":[{"name":"idominikos","email":"idominikos@outlook.com"}],"homepage":"https://capydb.dev","bugs":{"url":"https://github.com/capydatabase/capydb-kv/issues"},"dist":{"shasum":"ab9b505f2a1ddd30be59657e489889225074dc23","tarball":"https://registry.npmjs.org/@capydb/kv/-/kv-1.0.0.tgz","fileCount":6,"integrity":"sha512-I9Xw5O+PrVmpfO+HGo0fYYNvIOebPOItU6yzIKQI3SnbUK5kWSWhDGxnPWZmf1Pe+vbQWVtDvew2lCNM/CRHAw==","signatures":[{"sig":"MEUCIAiad7FSLyp7NIiO7TKNR+Q5c8aqmwtI68inewckSPzAAiEAqQnEJNUVCOx0DR/tTisVrKHRh0iCl//dbsW6Atn0MGs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":26265},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=24.20.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"e5462a549518cda5dffcb801ee4956208ffe755f","scripts":{"lint":"oxlint src test","test":"vitest run","build":"tsdown && tsc -p tsconfig.build.json && cp dist/index.d.ts dist/index.d.cts","format":"oxfmt --write src test","prepare":"if git rev-parse --git-dir >/dev/null 2>&1; then lefthook install; else echo 'no git repository - skipping lefthook install'; fi","typecheck":"tsc --noEmit","prepublishOnly":"pnpm typecheck && pnpm build"},"_npmUser":{"name":"idominikos","email":"idominikos@outlook.com"},"repository":{"url":"git+https://github.com/capydatabase/capydb-kv.git","type":"git"},"_npmVersion":"12.0.2","description":"Official CapyDB K/V helper - builds an @upstash/redis client from CapyDB environment variables, and fails fast when they are missing.","directories":{},"sideEffects":false,"_nodeVersion":"24.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@12.3.4","devDependencies":{"oxfmt":"^0.66.0","oxlint":"^1.81.0","tsdown":"0.23.0","vitest":"^5.0.0","lefthook":"^2.1.12","typescript":"next","@types/node":"^26.5.0","@upstash/redis":"^1.38.4","@commitlint/cli":"^21.2.2","@commitlint/config-conventional":"^21.2.2"},"peerDependencies":{"@upstash/redis":">=1.34.0 <2"},"_npmOperationalInternal":{"tmp":"tmp/kv_1.0.0_1788926381242_0.7094550071288348","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"_id":"@capydb/kv@2.0.0","bugs":{"url":"https://github.com/capydatabase/capydb-kv/issues"},"dist":{"shasum":"605adb6d13fd59cc0de80f75a03c07bb7d3a7b6e","tarball":"https://registry.npmjs.org/@capydb/kv/-/kv-2.0.0.tgz","fileCount":6,"integrity":"sha512-h6LfqZwUfbTiQbpeoU10Ad7wmJmLuXZaEuezvOpz0kT+RRK5Le48QWCrtzq01AXZLyx1k8vYqzp/ei6li3XdlQ==","signatures":[{"sig":"MEUCIG+Dk2fV6NQpxPjoAi0EQ5v8bPtQvsgUDDT8SxAHIrRlAiEAppv7CLZo11KCcmxwCd/DY+s7IIAcvz5u2D6mQq7K4sc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICAZZ3HmTLPDhnsYN9JAzkqb4EiK6VEqrCtewEeNVBMzAiAJfQbojgzCHlHJ4Kk08VgIQOxL8c48gdI+WzmBgF4i7Q=="}],"unpackedSize":26556},"main":"./dist/index.cjs","name":"@capydb/kv","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=24.21.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"1ee290875ff57e750b5911e81ca7cfd3f0edb92d","license":"MIT","scripts":{"lint":"oxlint src test","test":"vitest run","build":"tsdown && tsc -p tsconfig.build.json && cp dist/index.d.ts dist/index.d.cts","format":"oxfmt --write src test","prepare":"if git rev-parse --git-dir >/dev/null 2>&1; then lefthook install; else echo 'no git repository - skipping lefthook install'; fi","typecheck":"tsc --noEmit","prepublishOnly":"pnpm typecheck && pnpm build"},"version":"2.0.0","_npmUser":{"name":"idominikos","email":"idominikos@outlook.com"},"homepage":"https://capydb.dev","keywords":["capydb","kv","key-value","valkey","redis","rate-limit","cache","serverless"],"repository":{"url":"git+https://github.com/capydatabase/capydb-kv.git","type":"git"},"_npmVersion":"12.0.2","description":"Official CapyDB K/V helper - builds an @upstash/redis client from CapyDB environment variables, and fails fast when they are missing.","directories":{},"maintainers":[{"name":"idominikos","email":"idominikos@outlook.com"}],"sideEffects":false,"_nodeVersion":"26.8.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@12.4.2","devDependencies":{"oxfmt":"^0.68.0","oxlint":"^1.83.0","tsdown":"0.23.0","vitest":"^5.0.1","lefthook":"^2.1.14","typescript":"next","@types/node":"^26.6.1","@upstash/redis":"^1.38.4","@commitlint/cli":"^21.2.2","@commitlint/config-conventional":"^21.2.2"},"peerDependencies":{"@upstash/redis":">=1.34.0 <2"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/kv_2.0.0_1789517284350_0.35249093580535384"}}},"time":{"created":"2026-09-09T03:59:41.095Z","modified":"2026-09-16T00:08:04.620Z","1.0.0":"2026-09-09T03:59:41.373Z","2.0.0":"2026-09-16T00:08:04.446Z"},"bugs":{"url":"https://github.com/capydatabase/capydb-kv/issues"},"license":"MIT","homepage":"https://capydb.dev","keywords":["capydb","kv","key-value","valkey","redis","rate-limit","cache","serverless"],"repository":{"url":"git+https://github.com/capydatabase/capydb-kv.git","type":"git"},"description":"Official CapyDB K/V helper - builds an @upstash/redis client from CapyDB environment variables, and fails fast when they are missing.","maintainers":[{"name":"idominikos","email":"idominikos@outlook.com"}],"readme":"# @capydb/kv\n\nHelpers for **CapyDB Knight/Valkyrie** (K/V) — the key-value and rate-limiting\nstore that runs beside your cell, on the same node. Powered by Valkey ·\nRedis®-compatible.\n\n## You might not need this\n\nCapyDB K/V speaks the same HTTP protocol `@upstash/redis` already speaks, so it\nworks with no dependency on us at all:\n\n```ts\nimport { Redis } from \"@upstash/redis\"\n\nconst redis = new Redis({\n  url: process.env.CAPYKV_REST_URL!,\n  token: process.env.CAPYKV_REST_TOKEN!,\n})\n```\n\nThat is genuinely all it takes, and it is the recommended starting point. This\npackage exists for two narrow reasons:\n\n1. **It reads CapyDB's environment variables.** `Redis.fromEnv()` looks for\n   `UPSTASH_REDIS_REST_URL` / `_TOKEN`; CapyDB's deployment integrations push\n   `CAPYKV_REST_URL` / `_TOKEN`, so `fromEnv()` finds nothing.\n2. **It fails fast on missing configuration.** `new Redis({})` with an absent\n   url or token does not throw — it only logs a warning and then fails later, at\n   request time, from wherever the first command happens to run. A deploy\n   missing an env var should break at startup with a message naming the\n   variable.\n\n## Install\n\n`@upstash/redis` is a peer dependency:\n\n```bash\npnpm add @capydb/kv @upstash/redis\n```\n\n## Quickstart\n\n```bash\ncapydb link\ncapydb kv create --write-env   # provisions the store, writes both variables to your env file\n                               # (without --write-env it prints the token instead - once)\n```\n\n```ts\nimport { createKv } from \"@capydb/kv\"\n\nconst kv = createKv()\n\nawait kv.set(\"greeting\", \"hello\")\nawait kv.get(\"greeting\")     // \"hello\"\nawait kv.incr(\"visits\")      // 1\n```\n\nThe returned value is a plain `@upstash/redis` client — every method works, and\nso does every library that accepts one.\n\n## Rate limiting\n\nNothing extra is needed. Pass the client to `@upstash/ratelimit`:\n\n```bash\npnpm add @upstash/ratelimit\n```\n\n```ts\nimport { Ratelimit } from \"@upstash/ratelimit\"\nimport { createKv } from \"@capydb/kv\"\n\nconst ratelimit = new Ratelimit({\n  redis: createKv(),\n  limiter: Ratelimit.slidingWindow(10, \"10 s\"),\n})\n\nconst { success, remaining, reset } = await ratelimit.limit(userId)\nif (!success) return new Response(\"Rate limit exceeded\", { status: 429 })\n```\n\nSliding window, fixed window and token bucket all work.\n\n## API\n\n### `createKv(options?)`\n\nReturns a configured `@upstash/redis` client. Throws `CapyKVConfigError` when\nthe endpoint or token is missing or unusable.\n\n| Option | Default | Notes |\n|---|---|---|\n| `url` | `CAPYKV_REST_URL`, then `UPSTASH_REDIS_REST_URL` | The Upstash fallback keeps a migrating app working before its env is renamed |\n| `token` | `CAPYKV_REST_TOKEN`, then `UPSTASH_REDIS_REST_TOKEN` | |\n| `env` | `process.env` | Pass your own for tests, or for runtimes with no global `process` |\n| `allowInsecureHttp` | `false` | The token is a bearer credential on every request, so plaintext `http://` is refused unless you opt in for a local instance |\n\nAny other option is forwarded to the `@upstash/redis` constructor.\n\n### `resolveKVCredentials(options?)`\n\nResolves `{ url, token }` without building a client — useful for asserting a\ndeployment is wired correctly, or for constructing the client yourself.\n\n### `CapyKVConfigError`\n\nThrown for missing, malformed, or insecure configuration.\n\n## Connecting without HTTP\n\nThe REST endpoint cannot express blocking commands or pub/sub, so job queues\n(BullMQ, Celery, Sidekiq) need the RESP endpoint instead. Any Redis client works, pointed at\n`CAPYKV_REDIS_URL` (`rediss://default:<token>@<host>:6379`), which\n`capydb kv create --write-env` writes next to the REST pair:\n\n```ts\nimport Redis from \"ioredis\"\nconst url = process.env.CAPYKV_REDIS_URL!\nconst redis = new Redis(url, { tls: { servername: new URL(url).hostname } })\n```\n\nThe `servername` is not optional: the endpoint routes by TLS server name, and\nNode's `tls.connect` sends none unless told to, so a bare `new Redis(url)` is\nrefused. `redis-cli` needs `--sni <host>` for the same reason; redis-py and\ngo-redis send it on their own.\n\n```bash\ncapydb kv credentials    # prints the host and a password-free rediss:// URL\n```\n\n`capydb kv credentials` cannot fill in the password: only the token's hash is\nstored, so it is never returned again after create or rotate. The RESP URL is\nbuilt from the token you saved then.\n\nThe deployment integrations (Vercel, Netlify, Cloudflare) push\n`CAPYKV_REST_URL` only, for the same reason — set `CAPYKV_REST_TOKEN`\nyourself wherever your app runs. `capydb env pull` likewise refreshes the URL\nand leaves the token alone.\n\n## Notes\n\n- **Durability.** K/V snapshots to disk and survives a restart. There are no\n  backups and no point-in-time recovery — treat it as a cache and a coordination\n  store, not a system of record.\n- **No scale-to-zero.** Unlike a database cell, a K/V store is always warm; a\n  rate limiter cannot absorb a resume on the first request after an idle period.\n- **Rotation is immediate.** `capydb kv rotate-token` invalidates the previous\n  token with no grace window, so roll it out to your clients first.\n\n## License\n\nMIT\n\n---\n\nRedis is a registered trademark of Redis Ltd. Valkey is a trademark of LF\nProjects, LLC. Any rights therein are reserved to their respective owners. Use\nof these marks is for referential purposes only and does not indicate\nsponsorship or endorsement.\n","readmeFilename":"README.md"}