├── .env.example
├── .github
└── workflows
│ ├── release.yml
│ └── tests.yaml
├── .gitignore
├── .husky
├── commit-msg
├── pre-commit
└── pre-push
├── .prettierignore
├── .vscode
├── extensions.json
└── settings.json
├── LICENSE
├── README.md
├── bun.lockb
├── commitlint.config.js
├── eslint.config.mjs
├── examples
├── auto-pipeline
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── README.md
│ ├── app
│ │ ├── components
│ │ │ └── DataComponent.tsx
│ │ ├── data
│ │ │ ├── getEvents.ts
│ │ │ ├── getUsers.ts
│ │ │ └── redis.ts
│ │ ├── favicon.ico
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ └── page.tsx
│ ├── next.config.mjs
│ ├── package.json
│ ├── postcss.config.mjs
│ ├── public
│ │ ├── next.svg
│ │ └── vercel.svg
│ ├── tailwind.config.ts
│ └── tsconfig.json
├── aws-cdk-python
│ ├── .gitignore
│ ├── .npmignore
│ ├── README.md
│ ├── bin
│ │ └── aws-cdk-python.ts
│ ├── cdk.json
│ ├── jest.config.js
│ ├── lib
│ │ ├── api
│ │ │ ├── index.py
│ │ │ └── requirements.txt
│ │ └── aws-cdk-python-stack.ts
│ ├── package-lock.json
│ ├── package.json
│ ├── test
│ │ └── aws-cdk-python.test.ts
│ └── tsconfig.json
├── aws-cdk-typescript
│ ├── .gitignore
│ ├── .npmignore
│ ├── README.md
│ ├── api
│ │ └── counter.ts
│ ├── bin
│ │ └── aws-cdk-typescript.ts
│ ├── cdk.json
│ ├── jest.config.js
│ ├── lib
│ │ └── aws-cdk-typescript-stack.ts
│ ├── package-lock.json
│ ├── package.json
│ ├── test
│ │ └── aws-cdk-typescript.test.ts
│ └── tsconfig.json
├── aws-lambda
│ ├── .gitignore
│ ├── README.md
│ ├── index.js
│ ├── package-lock.json
│ └── package.json
├── aws-sam
│ ├── .gitignore
│ ├── README.md
│ ├── __init__.py
│ ├── events
│ │ └── event.json
│ ├── hello_world
│ │ ├── __init__.py
│ │ ├── app.py
│ │ └── requirements.txt
│ ├── samconfig.toml
│ ├── template.yaml
│ └── tests
│ │ ├── __init__.py
│ │ ├── integration
│ │ ├── __init__.py
│ │ └── test_api_gateway.py
│ │ ├── requirements.txt
│ │ └── unit
│ │ ├── __init__.py
│ │ └── test_handler.py
├── azure-functions
│ ├── .funcignore
│ ├── .gitignore
│ ├── .vscode
│ │ └── extensions.json
│ ├── README.md
│ ├── host.json
│ ├── package-lock.json
│ ├── package.json
│ ├── src
│ │ └── functions
│ │ │ └── CounterFunction.ts
│ └── tsconfig.json
├── cloudflare-workers-with-typescript
│ ├── README.md
│ ├── ci.test.ts
│ ├── package.json
│ ├── src
│ │ └── index.ts
│ ├── tsconfig.json
│ └── wrangler.toml
├── cloudflare-workers
│ ├── README.md
│ ├── bun.lockb
│ ├── ci.test.ts
│ ├── index.js
│ ├── package.json
│ └── wrangler.toml
├── deno
│ ├── main.test.ts
│ └── main.ts
├── fastapi
│ ├── README.md
│ ├── main.py
│ └── requirements.txt
├── fastly
│ ├── .gitignore
│ ├── README.md
│ ├── fastly.toml
│ ├── package.json
│ ├── src
│ │ └── index.js
│ └── webpack.config.js
├── google-cloud-functions
│ ├── README.md
│ ├── index.js
│ └── package.json
├── ion
│ ├── .env.example
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── README.md
│ ├── app
│ │ ├── favicon.ico
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ └── page.tsx
│ ├── next.config.mjs
│ ├── package-lock.json
│ ├── package.json
│ ├── postcss.config.mjs
│ ├── public
│ │ ├── next.svg
│ │ └── vercel.svg
│ ├── sst-env.d.ts
│ ├── sst.config.ts
│ ├── tailwind.config.ts
│ └── tsconfig.json
├── nextjs-app-router
│ ├── .env.example
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── README.md
│ ├── app
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ └── page.tsx
│ ├── next.config.mjs
│ ├── package.json
│ ├── postcss.config.mjs
│ ├── tailwind.config.ts
│ └── tsconfig.json
├── nextjs-pages-router
│ ├── .env.example
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── README.md
│ ├── next.config.mjs
│ ├── package.json
│ ├── pages
│ │ ├── _app.tsx
│ │ ├── _document.tsx
│ │ ├── api
│ │ │ └── hello.ts
│ │ └── index.tsx
│ ├── postcss.config.mjs
│ ├── styles
│ │ └── globals.css
│ ├── tailwind.config.ts
│ └── tsconfig.json
├── nodejs
│ ├── .env.example
│ ├── README.md
│ ├── index.js
│ └── package.json
├── serverless-framework
│ └── counter
│ │ ├── .env.example
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── handler.js
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ └── serverless.yml
├── sst-v2
│ ├── .gitignore
│ ├── .vscode
│ │ ├── launch.json
│ │ └── settings.json
│ ├── README.md
│ ├── package.json
│ ├── packages
│ │ ├── core
│ │ │ ├── package.json
│ │ │ ├── sst-env.d.ts
│ │ │ └── tsconfig.json
│ │ ├── functions
│ │ │ ├── package.json
│ │ │ ├── sst-env.d.ts
│ │ │ └── tsconfig.json
│ │ └── web
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── next.config.mjs
│ │ │ ├── package.json
│ │ │ ├── pages
│ │ │ ├── _app.tsx
│ │ │ ├── _document.tsx
│ │ │ ├── api
│ │ │ │ └── hello.ts
│ │ │ └── index.tsx
│ │ │ ├── public
│ │ │ ├── favicon.ico
│ │ │ ├── next.svg
│ │ │ └── vercel.svg
│ │ │ ├── sst-env.d.ts
│ │ │ ├── styles
│ │ │ ├── Home.module.css
│ │ │ └── globals.css
│ │ │ └── tsconfig.json
│ ├── pnpm-workspace.yaml
│ ├── sst.config.ts
│ ├── stacks
│ │ └── Default.ts
│ └── tsconfig.json
├── terraform
│ ├── .gitignore
│ ├── README.md
│ ├── counter
│ │ ├── counter.js
│ │ └── package.json
│ ├── main.tf
│ ├── outputs.tf
│ ├── terraform.tf
│ └── variables.tf
├── vercel-functions-app-router
│ ├── .env.example
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── README.md
│ ├── app
│ │ ├── api
│ │ │ └── hello
│ │ │ │ └── route.ts
│ │ ├── globals.css
│ │ └── layout.tsx
│ ├── ci.test.ts
│ ├── next.config.mjs
│ ├── package.json
│ ├── postcss.config.mjs
│ ├── tailwind.config.ts
│ └── tsconfig.json
├── vercel-functions-pages-router
│ ├── .env.example
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── README.md
│ ├── ci.test.ts
│ ├── next.config.mjs
│ ├── package.json
│ ├── pages
│ │ ├── _app.tsx
│ │ ├── _document.tsx
│ │ └── api
│ │ │ └── hello.ts
│ ├── postcss.config.mjs
│ ├── styles
│ │ └── globals.css
│ ├── tailwind.config.ts
│ └── tsconfig.json
├── vercel-python-runtime-django
│ ├── .env.example
│ ├── .gitignore
│ ├── README.md
│ ├── api
│ │ ├── __init__.py
│ │ ├── asgi.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ ├── example
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ ├── apps.py
│ │ ├── urls.py
│ │ └── views.py
│ ├── manage.py
│ ├── package.json
│ ├── requirements.txt
│ └── vercel.json
└── with-sentry
│ ├── index.ts
│ └── package.json
├── package.json
├── pkg
├── auto-pipeline.test.ts
├── auto-pipeline.ts
├── commands
│ ├── append.test.ts
│ ├── append.ts
│ ├── bitcount.test.ts
│ ├── bitcount.ts
│ ├── bitfield.test.ts
│ ├── bitfield.ts
│ ├── bitop.test.ts
│ ├── bitop.ts
│ ├── bitpos.test.ts
│ ├── bitpos.ts
│ ├── command.test.ts
│ ├── command.ts
│ ├── copy.test.ts
│ ├── copy.ts
│ ├── dbsize.test.ts
│ ├── dbsize.ts
│ ├── decr.test.ts
│ ├── decr.ts
│ ├── decrby.test.ts
│ ├── decrby.ts
│ ├── del.test.ts
│ ├── del.ts
│ ├── echo.test.ts
│ ├── echo.ts
│ ├── eval.test.ts
│ ├── eval.ts
│ ├── evalRo.test.ts
│ ├── evalRo.ts
│ ├── evalsha.test.ts
│ ├── evalsha.ts
│ ├── evalshaRo.test.ts
│ ├── evalshaRo.ts
│ ├── exec.test.ts
│ ├── exec.ts
│ ├── exists.test.ts
│ ├── exists.ts
│ ├── expire.test.ts
│ ├── expire.ts
│ ├── expireat.test.ts
│ ├── expireat.ts
│ ├── flushall.test.ts
│ ├── flushall.ts
│ ├── flushdb.test.ts
│ ├── flushdb.ts
│ ├── geo_add.test.ts
│ ├── geo_add.ts
│ ├── geo_dist.test.ts
│ ├── geo_dist.ts
│ ├── geo_hash.test.ts
│ ├── geo_hash.ts
│ ├── geo_pos.test.ts
│ ├── geo_pos.ts
│ ├── geo_search.test.ts
│ ├── geo_search.ts
│ ├── geo_search_store.test.ts
│ ├── geo_search_store.ts
│ ├── get.test.ts
│ ├── get.ts
│ ├── getbit.test.ts
│ ├── getbit.ts
│ ├── getdel.test.ts
│ ├── getdel.ts
│ ├── getex.test.ts
│ ├── getex.ts
│ ├── getrange.test.ts
│ ├── getrange.ts
│ ├── getset.test.ts
│ ├── getset.ts
│ ├── hdel.test.ts
│ ├── hdel.ts
│ ├── hexists.test.ts
│ ├── hexists.ts
│ ├── hexpire.test.ts
│ ├── hexpire.ts
│ ├── hexpireat.test.ts
│ ├── hexpireat.ts
│ ├── hexpiretime.test.ts
│ ├── hexpiretime.ts
│ ├── hget.test.ts
│ ├── hget.ts
│ ├── hgetall.test.ts
│ ├── hgetall.ts
│ ├── hincrby.test.ts
│ ├── hincrby.ts
│ ├── hincrbyfloat.test.ts
│ ├── hincrbyfloat.ts
│ ├── hkeys.test.ts
│ ├── hkeys.ts
│ ├── hlen.test.ts
│ ├── hlen.ts
│ ├── hmget.test.ts
│ ├── hmget.ts
│ ├── hmset.test.ts
│ ├── hmset.ts
│ ├── hpersist.test.ts
│ ├── hpersist.ts
│ ├── hpexpire.test.ts
│ ├── hpexpire.ts
│ ├── hpexpireat.test.ts
│ ├── hpexpireat.ts
│ ├── hpexpiretime.test.ts
│ ├── hpexpiretime.ts
│ ├── hpttl.test.ts
│ ├── hpttl.ts
│ ├── hrandfield.test.ts
│ ├── hrandfield.ts
│ ├── hscan.test.ts
│ ├── hscan.ts
│ ├── hset.test.ts
│ ├── hset.ts
│ ├── hsetnx.test.ts
│ ├── hsetnx.ts
│ ├── hstrlen.test.ts
│ ├── hstrlen.ts
│ ├── httl.test.ts
│ ├── httl.ts
│ ├── hvals.test.ts
│ ├── hvals.ts
│ ├── incr.test.ts
│ ├── incr.ts
│ ├── incrby.test.ts
│ ├── incrby.ts
│ ├── incrbyfloat.test.ts
│ ├── incrbyfloat.ts
│ ├── json_arrappend.test.ts
│ ├── json_arrappend.ts
│ ├── json_arrindex.test.ts
│ ├── json_arrindex.ts
│ ├── json_arrinsert.test.ts
│ ├── json_arrinsert.ts
│ ├── json_arrlen.test.ts
│ ├── json_arrlen.ts
│ ├── json_arrpop.test.ts
│ ├── json_arrpop.ts
│ ├── json_arrtrim.test.ts
│ ├── json_arrtrim.ts
│ ├── json_clear.test.ts
│ ├── json_clear.ts
│ ├── json_del.test.ts
│ ├── json_del.ts
│ ├── json_forget.test.ts
│ ├── json_forget.ts
│ ├── json_get.test.ts
│ ├── json_get.ts
│ ├── json_merge.test.ts
│ ├── json_merge.ts
│ ├── json_mget.test.ts
│ ├── json_mget.ts
│ ├── json_mset.test.ts
│ ├── json_mset.ts
│ ├── json_numincrby.test.ts
│ ├── json_numincrby.ts
│ ├── json_nummultby.test.ts
│ ├── json_nummultby.ts
│ ├── json_objkeys.test.ts
│ ├── json_objkeys.ts
│ ├── json_objlen.test.ts
│ ├── json_objlen.ts
│ ├── json_resp.test.ts
│ ├── json_resp.ts
│ ├── json_set.test.ts
│ ├── json_set.ts
│ ├── json_strappend.test.ts
│ ├── json_strappend.ts
│ ├── json_strlen.test.ts
│ ├── json_strlen.ts
│ ├── json_toggle.test.ts
│ ├── json_toggle.ts
│ ├── json_type.test.ts
│ ├── json_type.ts
│ ├── keys.test.ts
│ ├── keys.ts
│ ├── lindex.test.ts
│ ├── lindex.ts
│ ├── linsert.test.ts
│ ├── linsert.ts
│ ├── llen.test.ts
│ ├── llen.ts
│ ├── lmove.test.ts
│ ├── lmove.ts
│ ├── lmpop.test.ts
│ ├── lmpop.ts
│ ├── lpop.test.ts
│ ├── lpop.ts
│ ├── lpos.test.ts
│ ├── lpos.ts
│ ├── lpush.test.ts
│ ├── lpush.ts
│ ├── lpushx.test.ts
│ ├── lpushx.ts
│ ├── lrange.test.ts
│ ├── lrange.ts
│ ├── lrem.test.ts
│ ├── lrem.ts
│ ├── lset.test.ts
│ ├── lset.ts
│ ├── ltrim.test.ts
│ ├── ltrim.ts
│ ├── mget.test.ts
│ ├── mget.ts
│ ├── mod.ts
│ ├── mset.test.ts
│ ├── mset.ts
│ ├── msetnx.test.ts
│ ├── msetnx.ts
│ ├── persist.test.ts
│ ├── persist.ts
│ ├── pexpire.test.ts
│ ├── pexpire.ts
│ ├── pexpireat.test.ts
│ ├── pexpireat.ts
│ ├── pfadd.test.ts
│ ├── pfadd.ts
│ ├── pfcount.test.ts
│ ├── pfcount.ts
│ ├── pfmerge.test.ts
│ ├── pfmerge.ts
│ ├── ping.test.ts
│ ├── ping.ts
│ ├── psetex.test.ts
│ ├── psetex.ts
│ ├── psubscribe.test.ts
│ ├── psubscribe.ts
│ ├── pttl.test.ts
│ ├── pttl.ts
│ ├── publish.test.ts
│ ├── publish.ts
│ ├── randomkey.test.ts
│ ├── randomkey.ts
│ ├── rename.test.ts
│ ├── rename.ts
│ ├── renamenx.test.ts
│ ├── renamenx.ts
│ ├── rpop.test.ts
│ ├── rpop.ts
│ ├── rpush.test.ts
│ ├── rpush.ts
│ ├── rpushx.test.ts
│ ├── rpushx.ts
│ ├── sadd.test.ts
│ ├── sadd.ts
│ ├── scan.test.ts
│ ├── scan.ts
│ ├── scard.test.ts
│ ├── scard.ts
│ ├── script_exists.test.ts
│ ├── script_exists.ts
│ ├── script_flush.ts
│ ├── script_load.test.ts
│ ├── script_load.ts
│ ├── sdiff.test.ts
│ ├── sdiff.ts
│ ├── sdiffstore.test.ts
│ ├── sdiffstore.ts
│ ├── set.test.ts
│ ├── set.ts
│ ├── setbit.test.ts
│ ├── setbit.ts
│ ├── setex.test.ts
│ ├── setex.ts
│ ├── setnx.test.ts
│ ├── setnx.ts
│ ├── setrange.test.ts
│ ├── setrange.ts
│ ├── sinter.test.ts
│ ├── sinter.ts
│ ├── sinterstore.test.ts
│ ├── sinterstore.ts
│ ├── sismember.test.ts
│ ├── sismember.ts
│ ├── smembers.test.ts
│ ├── smembers.ts
│ ├── smismember.test.ts
│ ├── smismember.ts
│ ├── smove.test.ts
│ ├── smove.ts
│ ├── spop.test.ts
│ ├── spop.ts
│ ├── srandmember.test.ts
│ ├── srandmember.ts
│ ├── srem.test.ts
│ ├── srem.ts
│ ├── sscan.test.ts
│ ├── sscan.ts
│ ├── strlen.test.ts
│ ├── strlen.ts
│ ├── subscribe.test.ts
│ ├── subscribe.ts
│ ├── sunion.test.ts
│ ├── sunion.ts
│ ├── sunionstore.test.ts
│ ├── sunionstore.ts
│ ├── time.test.ts
│ ├── time.ts
│ ├── touch.test.ts
│ ├── touch.ts
│ ├── ttl.test.ts
│ ├── ttl.ts
│ ├── type.test.ts
│ ├── type.ts
│ ├── types.ts
│ ├── unlink.test.ts
│ ├── unlink.ts
│ ├── xack.test.ts
│ ├── xack.ts
│ ├── xadd.test.ts
│ ├── xadd.ts
│ ├── xautoclaim.test.ts
│ ├── xautoclaim.ts
│ ├── xclaim.test.ts
│ ├── xclaim.ts
│ ├── xdel.test.ts
│ ├── xdel.ts
│ ├── xgroup.test.ts
│ ├── xgroup.ts
│ ├── xinfo.test.ts
│ ├── xinfo.ts
│ ├── xlen.test.ts
│ ├── xlen.ts
│ ├── xpending.test.ts
│ ├── xpending.ts
│ ├── xrange.test.ts
│ ├── xrange.ts
│ ├── xread.test.ts
│ ├── xread.ts
│ ├── xreadgroup.test.ts
│ ├── xreadgroup.ts
│ ├── xrevrange.test.ts
│ ├── xrevrange.ts
│ ├── xtrim.test.ts
│ ├── xtrim.ts
│ ├── zadd.test.ts
│ ├── zadd.ts
│ ├── zcard.test.ts
│ ├── zcard.ts
│ ├── zcount.test.ts
│ ├── zcount.ts
│ ├── zdiffstore.test.ts
│ ├── zdiffstore.ts
│ ├── zincrby.test.ts
│ ├── zincrby.ts
│ ├── zinterstore.test.ts
│ ├── zinterstore.ts
│ ├── zlexcount.test.ts
│ ├── zlexcount.ts
│ ├── zmscore.test.ts
│ ├── zmscore.ts
│ ├── zpopmax.test.ts
│ ├── zpopmax.ts
│ ├── zpopmin.test.ts
│ ├── zpopmin.ts
│ ├── zrange.test.ts
│ ├── zrange.ts
│ ├── zrank.test.ts
│ ├── zrank.ts
│ ├── zrem.test.ts
│ ├── zrem.ts
│ ├── zremrangebylex.test.ts
│ ├── zremrangebylex.ts
│ ├── zremrangebyrank.test.ts
│ ├── zremrangebyrank.ts
│ ├── zremrangebyscore.test.ts
│ ├── zremrangebyscore.ts
│ ├── zrevrank.test.ts
│ ├── zrevrank.ts
│ ├── zscan.test.ts
│ ├── zscan.ts
│ ├── zscore.test.ts
│ ├── zscore.ts
│ ├── zunion.test.ts
│ ├── zunion.ts
│ ├── zunionstore.test.ts
│ └── zunionstore.ts
├── error.ts
├── http.test.ts
├── http.ts
├── index.ts
├── pipeline.test.ts
├── pipeline.ts
├── read-your-writes.test.ts
├── redis.test.ts
├── redis.ts
├── script.test.ts
├── script.ts
├── scriptRo.test.ts
├── scriptRo.ts
├── test-utils.test.ts
├── test-utils.ts
├── types.ts
└── util.ts
├── platforms
├── cloudflare.ts
├── fastly.ts
└── nodejs.ts
├── prettier.config.mjs
├── tsconfig.json
├── tsup.config.ts
└── version.ts
/.env.example:
--------------------------------------------------------------------------------
1 | UPSTASH_REDIS_REST_URL=
2 | UPSTASH_REDIS_REST_TOKEN=
3 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | bun --no -- commitlint --edit ""
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 |
2 | bun run fmt
--------------------------------------------------------------------------------
/.husky/pre-push:
--------------------------------------------------------------------------------
1 | bun run build
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | **/examples
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
3 | }
4 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": true,
3 | "git.autofetch": true,
4 | "editor.codeActionsOnSave": {
5 | "source.fixAll": "explicit"
6 | },
7 | "[json]": {
8 | "editor.defaultFormatter": "esbenp.prettier-vscode"
9 | },
10 | "[javascript]": {
11 | "editor.defaultFormatter": "esbenp.prettier-vscode"
12 | },
13 | "[jsonc]": {
14 | "editor.defaultFormatter": "esbenp.prettier-vscode"
15 | },
16 | "[typescript]": {
17 | "editor.defaultFormatter": "esbenp.prettier-vscode"
18 | },
19 | "editor.defaultFormatter": "esbenp.prettier-vscode"
20 | }
21 |
--------------------------------------------------------------------------------
/bun.lockb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/upstash/redis-js/09f2eba6fd7ea2ed890386d10f7bef17d10e1769/bun.lockb
--------------------------------------------------------------------------------
/examples/auto-pipeline/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/examples/auto-pipeline/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/examples/auto-pipeline/app/data/getEvents.ts:
--------------------------------------------------------------------------------
1 |
2 | import client from "./redis"
3 |
4 | export async function getEvents() {
5 | const keys = await client.scan(0, { match: 'event:*' });
6 |
7 | if (keys[1].length === 0) {
8 | // If no keys found, insert sample data
9 | client.hmset('event:1', {'name': 'Sample Event 1', 'date': '2024-05-13'});
10 | client.hmset('event:2', {'name': 'Sample Event 2', 'date': '2024-05-14'});
11 | // Add more sample events as needed
12 | }
13 |
14 | const events = await Promise.all(keys[1].map(async key => {
15 | return client.hgetall(key) ?? {name: "default", date: "2000-01-01"};
16 | }));
17 | return events as {name: string, date: string}[]
18 | };
19 |
--------------------------------------------------------------------------------
/examples/auto-pipeline/app/data/getUsers.ts:
--------------------------------------------------------------------------------
1 |
2 | import client from "./redis"
3 |
4 | export async function getUsers() {
5 | const keys = await client.scan(0, { match: 'user:*' });
6 |
7 | if (keys[1].length === 0) {
8 | // If no keys found, insert sample data
9 | client.hmset('user:1', {'username': 'Adam', 'birthday': '1990-01-01'});
10 | client.hmset('user:2', {'username': 'Eve', 'birthday': '1980-01-05'});
11 | // Add more sample users as needed
12 | }
13 |
14 | const users = await Promise.all(keys[1].map(async key => {
15 | return client.hgetall(key) ?? {username: "default", birthday: "2000-01-01"};
16 | }));
17 | return users as {username: string, birthday: string}[]
18 | }
--------------------------------------------------------------------------------
/examples/auto-pipeline/app/data/redis.ts:
--------------------------------------------------------------------------------
1 |
2 | import { Redis } from '@upstash/redis'
3 |
4 | export const LATENCY_LOGGING = true
5 | export const ENABLE_AUTO_PIPELINING = true
6 |
7 | const client = Redis.fromEnv({
8 | latencyLogging: LATENCY_LOGGING,
9 | enableAutoPipelining: ENABLE_AUTO_PIPELINING
10 | });
11 |
12 | export default client;
13 |
--------------------------------------------------------------------------------
/examples/auto-pipeline/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/upstash/redis-js/09f2eba6fd7ea2ed890386d10f7bef17d10e1769/examples/auto-pipeline/app/favicon.ico
--------------------------------------------------------------------------------
/examples/auto-pipeline/app/globals.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/upstash/redis-js/09f2eba6fd7ea2ed890386d10f7bef17d10e1769/examples/auto-pipeline/app/globals.css
--------------------------------------------------------------------------------
/examples/auto-pipeline/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Inter } from "next/font/google";
3 | import "./globals.css";
4 |
5 | const inter = Inter({ subsets: ["latin"] });
6 |
7 | export const metadata: Metadata = {
8 | title: "Create Next App",
9 | description: "Generated by create next app",
10 | };
11 |
12 | export default function RootLayout({
13 | children,
14 | }: Readonly<{
15 | children: React.ReactNode;
16 | }>) {
17 | return (
18 |
19 |
{children}
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/examples/auto-pipeline/app/page.tsx:
--------------------------------------------------------------------------------
1 | "use server"
2 |
3 | import React from 'react';
4 | import DataComponent from './components/DataComponent'
5 |
6 | const HomePage = () => {
7 | return (
8 |
9 |
Home Page
10 |
11 |
12 | );
13 | };
14 |
15 | export default HomePage;
16 |
--------------------------------------------------------------------------------
/examples/auto-pipeline/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/examples/auto-pipeline/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "auto-pipeline",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@upstash/redis": "latest",
13 | "next": "14.2.3",
14 | "react": "^18",
15 | "react-dom": "^18"
16 | },
17 | "devDependencies": {
18 | "@types/node": "^20",
19 | "@types/react": "^18",
20 | "@types/react-dom": "^18",
21 | "eslint": "^8",
22 | "eslint-config-next": "14.2.3",
23 | "postcss": "^8",
24 | "tailwindcss": "^3.4.1",
25 | "typescript": "^5"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/auto-pipeline/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/examples/auto-pipeline/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/auto-pipeline/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | const config: Config = {
4 | content: [
5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | backgroundImage: {
12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
13 | "gradient-conic":
14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
15 | },
16 | },
17 | },
18 | plugins: [],
19 | };
20 | export default config;
21 |
--------------------------------------------------------------------------------
/examples/auto-pipeline/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------
/examples/aws-cdk-python/.gitignore:
--------------------------------------------------------------------------------
1 | *.js
2 | !jest.config.js
3 | *.d.ts
4 | node_modules
5 |
6 | # CDK asset staging directory
7 | .cdk.staging
8 | cdk.out
9 |
--------------------------------------------------------------------------------
/examples/aws-cdk-python/.npmignore:
--------------------------------------------------------------------------------
1 | *.ts
2 | !*.d.ts
3 |
4 | # CDK asset staging directory
5 | .cdk.staging
6 | cdk.out
7 |
--------------------------------------------------------------------------------
/examples/aws-cdk-python/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testEnvironment: 'node',
3 | roots: ['/test'],
4 | testMatch: ['**/*.test.ts'],
5 | transform: {
6 | '^.+\\.tsx?$': 'ts-jest'
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/examples/aws-cdk-python/lib/api/index.py:
--------------------------------------------------------------------------------
1 | from upstash_redis import Redis
2 |
3 | redis = Redis.from_env()
4 |
5 | def handler(event, context):
6 | count = redis.incr('counter')
7 | return {
8 | 'statusCode': 200,
9 | 'body': f'Counter: {count}'
10 | }
--------------------------------------------------------------------------------
/examples/aws-cdk-python/lib/api/requirements.txt:
--------------------------------------------------------------------------------
1 | upstash-redis
--------------------------------------------------------------------------------
/examples/aws-cdk-python/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aws-cdk-python",
3 | "version": "0.1.0",
4 | "bin": {
5 | "aws-cdk-python": "bin/aws-cdk-python.js"
6 | },
7 | "scripts": {
8 | "build": "tsc",
9 | "watch": "tsc -w",
10 | "test": "jest",
11 | "cdk": "cdk"
12 | },
13 | "devDependencies": {
14 | "@types/jest": "^29.5.12",
15 | "@types/node": "20.14.2",
16 | "jest": "^29.7.0",
17 | "ts-jest": "^29.1.4",
18 | "aws-cdk": "2.147.2",
19 | "ts-node": "^10.9.2",
20 | "typescript": "~5.4.5"
21 | },
22 | "dependencies": {
23 | "aws-cdk-lib": "2.147.2",
24 | "constructs": "^10.0.0",
25 | "source-map-support": "^0.5.21"
26 | }
27 | }
--------------------------------------------------------------------------------
/examples/aws-cdk-python/test/aws-cdk-python.test.ts:
--------------------------------------------------------------------------------
1 | // import * as cdk from 'aws-cdk-lib';
2 | // import { Template } from 'aws-cdk-lib/assertions';
3 | // import * as AwsCdkPython from '../lib/aws-cdk-python-stack';
4 |
5 | // example test. To run these tests, uncomment this file along with the
6 | // example resource in lib/aws-cdk-python-stack.ts
7 | test('SQS Queue Created', () => {
8 | // const app = new cdk.App();
9 | // // WHEN
10 | // const stack = new AwsCdkPython.AwsCdkPythonStack(app, 'MyTestStack');
11 | // // THEN
12 | // const template = Template.fromStack(stack);
13 |
14 | // template.hasResourceProperties('AWS::SQS::Queue', {
15 | // VisibilityTimeout: 300
16 | // });
17 | });
18 |
--------------------------------------------------------------------------------
/examples/aws-cdk-typescript/.gitignore:
--------------------------------------------------------------------------------
1 | *.js
2 | !jest.config.js
3 | *.d.ts
4 | node_modules
5 |
6 | # CDK asset staging directory
7 | .cdk.staging
8 | cdk.out
9 |
--------------------------------------------------------------------------------
/examples/aws-cdk-typescript/.npmignore:
--------------------------------------------------------------------------------
1 | *.ts
2 | !*.d.ts
3 |
4 | # CDK asset staging directory
5 | .cdk.staging
6 | cdk.out
7 |
--------------------------------------------------------------------------------
/examples/aws-cdk-typescript/api/counter.ts:
--------------------------------------------------------------------------------
1 | import { Redis } from '@upstash/redis';
2 |
3 | const redis = Redis.fromEnv();
4 |
5 | export const handler = async function() {
6 | const count = await redis.incr("counter");
7 | return {
8 | statusCode: 200,
9 | body: JSON.stringify('Counter: ' + count),
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/examples/aws-cdk-typescript/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testEnvironment: 'node',
3 | roots: ['/test'],
4 | testMatch: ['**/*.test.ts'],
5 | transform: {
6 | '^.+\\.tsx?$': 'ts-jest'
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/examples/aws-cdk-typescript/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aws-cdk-typescript",
3 | "version": "0.1.0",
4 | "bin": {
5 | "aws-cdk-typescript": "bin/aws-cdk-typescript.js"
6 | },
7 | "scripts": {
8 | "build": "tsc",
9 | "watch": "tsc -w",
10 | "test": "jest",
11 | "cdk": "cdk"
12 | },
13 | "devDependencies": {
14 | "@types/jest": "^29.5.12",
15 | "@types/node": "20.14.2",
16 | "aws-cdk": "2.147.2",
17 | "jest": "^29.7.0",
18 | "ts-jest": "^29.1.4",
19 | "ts-node": "^10.9.2",
20 | "typescript": "~5.4.5"
21 | },
22 | "dependencies": {
23 | "@upstash/redis": "latest",
24 | "aws-cdk-lib": "2.147.2",
25 | "constructs": "^10.0.0",
26 | "source-map-support": "^0.5.21"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/examples/aws-cdk-typescript/test/aws-cdk-typescript.test.ts:
--------------------------------------------------------------------------------
1 | // import * as cdk from 'aws-cdk-lib';
2 | // import { Template } from 'aws-cdk-lib/assertions';
3 | // import * as AwsCdkTypescript from '../lib/aws-cdk-typescript-stack';
4 |
5 | // example test. To run these tests, uncomment this file along with the
6 | // example resource in lib/aws-cdk-typescript-stack.ts
7 | test('SQS Queue Created', () => {
8 | // const app = new cdk.App();
9 | // // WHEN
10 | // const stack = new AwsCdkTypescript.AwsCdkTypescriptStack(app, 'MyTestStack');
11 | // // THEN
12 | // const template = Template.fromStack(stack);
13 |
14 | // template.hasResourceProperties('AWS::SQS::Queue', {
15 | // VisibilityTimeout: 300
16 | // });
17 | });
18 |
--------------------------------------------------------------------------------
/examples/aws-lambda/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.zip
3 | .env.local
--------------------------------------------------------------------------------
/examples/aws-lambda/index.js:
--------------------------------------------------------------------------------
1 | const { Redis } = require('@upstash/redis');
2 |
3 | const redis = Redis.fromEnv();
4 |
5 | exports.handler = async (event) => {
6 | const count = await redis.incr("counter");
7 | return {
8 | statusCode: 200,
9 | body: JSON.stringify('Counter: ' + count),
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/examples/aws-lambda/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "@upstash/redis": "latest"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/aws-sam/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/upstash/redis-js/09f2eba6fd7ea2ed890386d10f7bef17d10e1769/examples/aws-sam/__init__.py
--------------------------------------------------------------------------------
/examples/aws-sam/hello_world/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/upstash/redis-js/09f2eba6fd7ea2ed890386d10f7bef17d10e1769/examples/aws-sam/hello_world/__init__.py
--------------------------------------------------------------------------------
/examples/aws-sam/hello_world/app.py:
--------------------------------------------------------------------------------
1 | from upstash_redis import Redis
2 |
3 | redis = Redis.from_env()
4 |
5 | def lambda_handler(event, context):
6 | count = redis.incr('counter')
7 | return {
8 | 'statusCode': 200,
9 | 'body': f'Counter: {count}'
10 | }
--------------------------------------------------------------------------------
/examples/aws-sam/hello_world/requirements.txt:
--------------------------------------------------------------------------------
1 | requests
2 | upstash-redis
--------------------------------------------------------------------------------
/examples/aws-sam/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/upstash/redis-js/09f2eba6fd7ea2ed890386d10f7bef17d10e1769/examples/aws-sam/tests/__init__.py
--------------------------------------------------------------------------------
/examples/aws-sam/tests/integration/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/upstash/redis-js/09f2eba6fd7ea2ed890386d10f7bef17d10e1769/examples/aws-sam/tests/integration/__init__.py
--------------------------------------------------------------------------------
/examples/aws-sam/tests/requirements.txt:
--------------------------------------------------------------------------------
1 | pytest
2 | boto3
3 | requests
4 |
--------------------------------------------------------------------------------
/examples/aws-sam/tests/unit/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/upstash/redis-js/09f2eba6fd7ea2ed890386d10f7bef17d10e1769/examples/aws-sam/tests/unit/__init__.py
--------------------------------------------------------------------------------
/examples/azure-functions/.funcignore:
--------------------------------------------------------------------------------
1 | *.js.map
2 | *.ts
3 | .git*
4 | .vscode
5 | local.settings.json
6 | test
7 | getting_started.md
8 | node_modules/@types/
9 | node_modules/azure-functions-core-tools/
10 | node_modules/typescript/
--------------------------------------------------------------------------------
/examples/azure-functions/.gitignore:
--------------------------------------------------------------------------------
1 | bin
2 | obj
3 | csx
4 | .vs
5 | edge
6 | Publish
7 |
8 | *.user
9 | *.suo
10 | *.cscfg
11 | *.Cache
12 | project.lock.json
13 |
14 | /packages
15 | /TestResults
16 |
17 | /tools/NuGet.exe
18 | /App_Data
19 | /secrets
20 | /data
21 | .secrets
22 | appsettings.json
23 | local.settings.json
24 |
25 | node_modules
26 | dist
27 |
28 | # Local python packages
29 | .python_packages/
30 |
31 | # Python Environments
32 | .env
33 | .venv
34 | env/
35 | venv/
36 | ENV/
37 | env.bak/
38 | venv.bak/
39 |
40 | # Byte-compiled / optimized / DLL files
41 | __pycache__/
42 | *.py[cod]
43 | *$py.class
44 |
45 | # Azurite artifacts
46 | __blobstorage__
47 | __queuestorage__
48 | __azurite_db*__.json
--------------------------------------------------------------------------------
/examples/azure-functions/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "ms-azuretools.vscode-azurefunctions"
4 | ]
5 | }
--------------------------------------------------------------------------------
/examples/azure-functions/host.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0",
3 | "logging": {
4 | "applicationInsights": {
5 | "samplingSettings": {
6 | "isEnabled": true,
7 | "excludedTypes": "Request"
8 | }
9 | }
10 | },
11 | "extensionBundle": {
12 | "id": "Microsoft.Azure.Functions.ExtensionBundle",
13 | "version": "[4.*, 5.0.0)"
14 | }
15 | }
--------------------------------------------------------------------------------
/examples/azure-functions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "dist/src/functions/*.js",
6 | "scripts": {
7 | "build": "tsc",
8 | "watch": "tsc -w",
9 | "clean": "rimraf dist",
10 | "prestart": "npm run clean && npm run build",
11 | "start": "func start",
12 | "test": "echo \"No tests yet...\""
13 | },
14 | "dependencies": {
15 | "@azure/functions": "^4.0.0",
16 | "@upstash/redis": "latest"
17 | },
18 | "devDependencies": {
19 | "@types/node": "18.x",
20 | "azure-functions-core-tools": "^4.x",
21 | "rimraf": "^5.0.0",
22 | "typescript": "^4.0.0"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/azure-functions/src/functions/CounterFunction.ts:
--------------------------------------------------------------------------------
1 | import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions";
2 | import { Redis } from "@upstash/redis";
3 |
4 | const redis = new Redis({
5 | url: process.env.UPSTASH_REDIS_REST_URL,
6 | token: process.env.UPSTASH_REDIS_REST_TOKEN
7 | });
8 |
9 | export async function CounterFunction(request: HttpRequest, context: InvocationContext): Promise {
10 | const count = await redis.incr("counter");
11 |
12 | return { status: 200, body: `Counter: ${count}` };
13 | };
14 |
15 | app.http('CounterFunction', {
16 | methods: ['GET', 'POST'],
17 | authLevel: 'anonymous',
18 | handler: CounterFunction
19 | });
--------------------------------------------------------------------------------
/examples/azure-functions/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es6",
5 | "outDir": "dist",
6 | "rootDir": ".",
7 | "sourceMap": true,
8 | "strict": false
9 | }
10 | }
--------------------------------------------------------------------------------
/examples/cloudflare-workers-with-typescript/README.md:
--------------------------------------------------------------------------------
1 | # Cloudflare Worker Typescript Example
2 |
3 | This example uses
4 | [Wrangler](https://developers.cloudflare.com/workers/wrangler/) to create a
5 | typescript worker
6 |
7 | ## How to use
8 |
9 | 1. Clone and install the example
10 |
11 | ```bash
12 | git clone https://github.com/upstash/upstash-redis.git
13 | cd upstash-redis/examples/cloudflare-workers-with-typescript
14 | npm install
15 | ```
16 |
17 | 2. Create a free Database on [upstash.com](https://console.upstash.com/redis)
18 | 3. Copy the `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` to
19 | `wrangler.toml`
20 | 4. Start the development server
21 |
22 | ```bash
23 | npm run start
24 | ```
25 |
26 | 5. Open your browser at [localhost:8787](http://localhost:8787)
27 |
--------------------------------------------------------------------------------
/examples/cloudflare-workers-with-typescript/ci.test.ts:
--------------------------------------------------------------------------------
1 | import {test, expect} from "bun:test"
2 | const deploymentURL = process.env.DEPLOYMENT_URL;
3 | if (!deploymentURL) {
4 | throw new Error("DEPLOYMENT_URL not set");
5 | }
6 |
7 | test("works", async () => {
8 | console.log({ deploymentURL });
9 | const url = `${deploymentURL}/`;
10 | const res = await fetch(url);
11 | if (res.status !== 200) {
12 | console.log(await res.text());
13 | }
14 | expect(res.status).toEqual(200);
15 | const json = (await res.json()) as { count: number };
16 | expect(typeof json.count).toEqual("number");
17 | });
18 |
--------------------------------------------------------------------------------
/examples/cloudflare-workers-with-typescript/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cloudflare-workers-with-typescript",
3 | "version": "0.0.0",
4 | "devDependencies": {
5 | "@cloudflare/workers-types": "^4.20221111.1",
6 | "typescript": "^4.8.4",
7 | "wrangler": "^2.4.4"
8 | },
9 | "private": true,
10 | "scripts": {
11 | "start": "wrangler dev",
12 | "publish": "wrangler publish"
13 | },
14 | "dependencies": {
15 | "@upstash/redis": "latest"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/examples/cloudflare-workers-with-typescript/src/index.ts:
--------------------------------------------------------------------------------
1 | import { Redis } from "@upstash/redis/cloudflare";
2 |
3 | export interface Env {
4 | UPSTASH_REDIS_REST_URL: string;
5 | UPSTASH_REDIS_REST_TOKEN: string;
6 | }
7 |
8 | export default {
9 | async fetch(_request: Request, env: Env, _ctx: ExecutionContext): Promise {
10 | const redis = Redis.fromEnv(env);
11 |
12 | const count = await redis.incr("cloudflare-workers-with-typescript-count");
13 |
14 | return new Response(JSON.stringify({ count }));
15 | },
16 | };
17 |
--------------------------------------------------------------------------------
/examples/cloudflare-workers-with-typescript/wrangler.toml:
--------------------------------------------------------------------------------
1 | name = "cloudflare-workers-with-typescript"
2 | main = "src/index.ts"
3 | compatibility_date = "2022-05-31"
4 |
5 |
6 | # Set variables here or on cloudflare
7 | # [vars]
8 | # UPSTASH_REDIS_REST_URL = "REPLACE_THIS"
9 | # UPSTASH_REDIS_REST_TOKEN = "REPLACE_THIS"
10 |
--------------------------------------------------------------------------------
/examples/cloudflare-workers/README.md:
--------------------------------------------------------------------------------
1 | # Cloudflare Workers Example
2 |
3 | This example uses
4 | [Wrangler](https://developers.cloudflare.com/workers/wrangler/) to create a
5 | javascript worker.
6 |
7 | ## How to use
8 |
9 | 1. Clone and install the example
10 |
11 | ```bash
12 | git clone https://github.com/upstash/upstash-redis.git
13 | cd upstash-redis/examples/cloudflare-workers
14 | npm install
15 | ```
16 |
17 | 2. Create a free Database on [upstash.com](https://console.upstash.com/redis)
18 | 3. Copy the `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` to
19 | `wrangler.toml`
20 | 4. Start the development server
21 |
22 | ```bash
23 | npm run start
24 | ```
25 |
26 | 5. Open your browser at [localhost:8787](http://localhost:8787)
27 |
--------------------------------------------------------------------------------
/examples/cloudflare-workers/bun.lockb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/upstash/redis-js/09f2eba6fd7ea2ed890386d10f7bef17d10e1769/examples/cloudflare-workers/bun.lockb
--------------------------------------------------------------------------------
/examples/cloudflare-workers/ci.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from "bun:test";
2 |
3 | const deploymentURL = process.env.DEPLOYMENT_URL;
4 | if (!deploymentURL) {
5 | throw new Error("DEPLOYMENT_URL not set");
6 | }
7 |
8 | test("works", async () => {
9 | console.log({ deploymentURL });
10 | const url = `${deploymentURL}/`;
11 | const res = await fetch(url);
12 | if (res.status !== 200) {
13 | console.log(await res.text());
14 | }
15 | expect(res.status).toEqual(200);
16 | const json = (await res.json()) as { count: number };
17 | expect(typeof json.count).toEqual("number");
18 | });
19 |
--------------------------------------------------------------------------------
/examples/cloudflare-workers/index.js:
--------------------------------------------------------------------------------
1 | import { Redis } from "@upstash/redis/cloudflare";
2 |
3 | export default {
4 | async fetch(_request, env) {
5 | const redis = Redis.fromEnv(env);
6 |
7 | const count = await redis.incr("cloudflare-workers-count");
8 |
9 | return new Response(JSON.stringify({ count }));
10 | },
11 | };
12 |
--------------------------------------------------------------------------------
/examples/cloudflare-workers/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cloudflare-workers",
3 | "version": "1.0.0",
4 | "description": "Example project using wrangler2",
5 | "author": "Andreas Thomas ",
6 | "license": "MIT",
7 | "scripts": {
8 | "start": "wrangler dev",
9 | "publish": "wrangler publish"
10 | },
11 | "devDependencies": {
12 | "wrangler": "^2.20.0"
13 | },
14 | "dependencies": {
15 | "@upstash/redis": "latest"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/examples/cloudflare-workers/wrangler.toml:
--------------------------------------------------------------------------------
1 |
2 |
3 | name = "upstash-redis"
4 | main = "index.js"
5 | compatibility_date = "2022-05-27"
6 |
7 |
8 | # Set variables here or on cloudflare
9 | # [vars]
10 | # UPSTASH_REDIS_REST_URL = "REPLACE_THIS"
11 | # UPSTASH_REDIS_REST_TOKEN = "REPLACE_THIS"
12 |
--------------------------------------------------------------------------------
/examples/deno/main.test.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from "bun:test";
2 |
3 | const deploymentURL = process.env.DEPLOYMENT_URL;
4 | if (!deploymentURL) {
5 | throw new Error("DEPLOYMENT_URL not set");
6 | }
7 |
8 | test("works", async () => {
9 | console.log({ deploymentURL });
10 | const res = await fetch(deploymentURL);
11 | const body = await res.text();
12 | console.log({ body });
13 | expect(res.status).toBe(200);
14 | const json = JSON.parse(body) as { counter: number };
15 | expect(typeof json.counter).toBe("number");
16 | });
17 |
--------------------------------------------------------------------------------
/examples/deno/main.ts:
--------------------------------------------------------------------------------
1 | import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
2 | import { Redis } from "https://esm.sh/@upstash/redis@latest";
3 |
4 | serve(async (_req: Request) => {
5 | const redis = Redis.fromEnv();
6 | const counter = await redis.incr("deno deploy counter");
7 |
8 | return new Response(JSON.stringify({ counter }), { status: 200 });
9 | });
10 |
--------------------------------------------------------------------------------
/examples/fastapi/README.md:
--------------------------------------------------------------------------------
1 | # FastAPI Example
2 |
3 | ### Project Setup
4 |
5 | Clone the example and install dependencies
6 |
7 | ```shell
8 | git clone https://github.com/upstash/redis-js.git
9 | cd redis-js/examples/fastapi
10 | pip install -r requirements.txt
11 | ```
12 |
13 | ### Database Setup
14 |
15 | Create a Redis database using [Upstash Console](https://console.upstash.com) or [Upstash CLI](https://github.com/upstash/cli) and export the `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` to your environment.
16 |
17 | ```shell
18 | export UPSTASH_REDIS_REST_URL=
19 | export UPSTASH_REDIS_REST_TOKEN=
20 | ```
21 |
22 | ### Run
23 | Run the app locally with `fastapi dev main.py`, check `http://127.0.0.1:8000/`
24 |
--------------------------------------------------------------------------------
/examples/fastapi/main.py:
--------------------------------------------------------------------------------
1 | from fastapi import FastAPI
2 |
3 | from upstash_redis import Redis
4 |
5 | app = FastAPI()
6 |
7 | redis = Redis.from_env()
8 |
9 | @app.get("/")
10 | def read_root():
11 | count = redis.incr('counter')
12 | return {"count": count}
13 |
--------------------------------------------------------------------------------
/examples/fastapi/requirements.txt:
--------------------------------------------------------------------------------
1 | fastapi
2 | upstash-redis
3 |
--------------------------------------------------------------------------------
/examples/fastly/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /bin
3 | /pkg
4 |
--------------------------------------------------------------------------------
/examples/fastly/README.md:
--------------------------------------------------------------------------------
1 | # Deploying to Fastly Compute
2 |
3 | You can use @upstash/redis in Fastly Compute as it accesses the Redis using REST
4 | calls.
5 |
6 | Check [this guide](https://blog.upstash.com/fastly-compute-edge-with-redis) for
7 | step by step instructions.
8 |
--------------------------------------------------------------------------------
/examples/fastly/fastly.toml:
--------------------------------------------------------------------------------
1 | # This file describes a Fastly Compute@Edge package. To learn more visit:
2 | # https://developer.fastly.com/reference/fastly-toml/
3 |
4 | authors = ["upstash"]
5 | description = "Example of using Upstash with Fastly Compute@Edge"
6 | language = "javascript"
7 | manifest_version = 2
8 | name = "fastly-upstash"
9 | service_id = ""
10 |
11 | [local_server.backends.upstash-db]
12 | url = ""
13 |
--------------------------------------------------------------------------------
/examples/fastly/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fastly-upstash-example",
3 | "version": "0.2.0",
4 | "main": "src/index.js",
5 | "author": "woverton@fastly.com",
6 | "license": "MIT",
7 | "devDependencies": {
8 | "core-js": "^3.19.1",
9 | "webpack": "^5.74.0",
10 | "webpack-cli": "^5.0.0"
11 | },
12 | "dependencies": {
13 | "@fastly/js-compute": "^0.5.5",
14 | "@upstash/redis": "latest",
15 | "flight-path": "^1.0.10"
16 | },
17 | "scripts": {
18 | "prebuild": "webpack",
19 | "build": "js-compute-runtime bin/index.js bin/main.wasm",
20 | "deploy": "npm run build && fastly compute deploy",
21 | "dev": "fastly compute serve --watch"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/fastly/src/index.js:
--------------------------------------------------------------------------------
1 | import { Redis } from "@upstash/redis/fastly";
2 |
3 | addEventListener("fetch", (event) => event.respondWith(handleRequest(event)));
4 |
5 | async function handleRequest(_event) {
6 | try {
7 | const redis = new Redis({
8 | url: "",
9 | token: "",
10 | backend: "upstash-db", // same name you used in `fastly.toml`
11 | });
12 | const count = await redis.incr("fastly");
13 | return new Response(JSON.stringify({ count }), {
14 | headers: { "Content-Type": "application/json" },
15 | });
16 | } catch (err) {
17 | return new Response(err.message, { status: 500 });
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/google-cloud-functions/index.js:
--------------------------------------------------------------------------------
1 | const { Redis } = require("@upstash/redis");
2 | const functions = require('@google-cloud/functions-framework');
3 |
4 | const redis = new Redis({
5 | url: process.env.UPSTASH_REDIS_REST_URL,
6 | token: process.env.UPSTASH_REDIS_REST_TOKEN
7 | });
8 |
9 | functions.http('counter', async (req, res) => {
10 | const count = await redis.incr("counter");
11 | res.send("Counter:" + count);
12 | });
13 |
--------------------------------------------------------------------------------
/examples/google-cloud-functions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "@google-cloud/functions-framework": "^3.0.0",
4 | "@upstash/redis": "latest"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/examples/ion/.env.example:
--------------------------------------------------------------------------------
1 | UPSTASH_REDIS_REST_URL=
2 | UPSTASH_REDIS_REST_TOKEN=
--------------------------------------------------------------------------------
/examples/ion/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/examples/ion/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
38 | # sst
39 | .sst
40 |
41 | # open-next
42 | .open-next
43 |
--------------------------------------------------------------------------------
/examples/ion/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/upstash/redis-js/09f2eba6fd7ea2ed890386d10f7bef17d10e1769/examples/ion/app/favicon.ico
--------------------------------------------------------------------------------
/examples/ion/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --foreground-rgb: 0, 0, 0;
7 | --background-start-rgb: 214, 219, 220;
8 | --background-end-rgb: 255, 255, 255;
9 | }
10 |
11 | @media (prefers-color-scheme: dark) {
12 | :root {
13 | --foreground-rgb: 255, 255, 255;
14 | --background-start-rgb: 0, 0, 0;
15 | --background-end-rgb: 0, 0, 0;
16 | }
17 | }
18 |
19 | body {
20 | color: rgb(var(--foreground-rgb));
21 | background: linear-gradient(
22 | to bottom,
23 | transparent,
24 | rgb(var(--background-end-rgb))
25 | )
26 | rgb(var(--background-start-rgb));
27 | }
28 |
29 | @layer utilities {
30 | .text-balance {
31 | text-wrap: balance;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/examples/ion/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Inter } from "next/font/google";
3 | import "./globals.css";
4 |
5 | const inter = Inter({ subsets: ["latin"] });
6 |
7 | export const metadata: Metadata = {
8 | title: "Create Next App",
9 | description: "Generated by create next app",
10 | };
11 |
12 | export default function RootLayout({
13 | children,
14 | }: Readonly<{
15 | children: React.ReactNode;
16 | }>) {
17 | return (
18 |
19 | {children}
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/examples/ion/app/page.tsx:
--------------------------------------------------------------------------------
1 | import { Redis } from "@upstash/redis";
2 |
3 | const redis = Redis.fromEnv();
4 |
5 | export default async function Home() {
6 | const count = await redis.incr("counter");
7 | return (
8 |
9 |
Counter: {count}
10 |
11 | )
12 | }
--------------------------------------------------------------------------------
/examples/ion/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/examples/ion/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ion",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "build": "next build",
7 | "dev": "sst dev next dev",
8 | "lint": "next lint",
9 | "start": "next start"
10 | },
11 | "dependencies": {
12 | "@upstash/redis": "latest",
13 | "next": "14.2.5",
14 | "react": "^18",
15 | "react-dom": "^18",
16 | "sst": "ion"
17 | },
18 | "devDependencies": {
19 | "@types/node": "^20",
20 | "@types/react": "^18",
21 | "@types/react-dom": "^18",
22 | "eslint": "^8",
23 | "eslint-config-next": "14.2.5",
24 | "postcss": "^8",
25 | "tailwindcss": "^3.4.1",
26 | "typescript": "^5"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/examples/ion/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/examples/ion/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/ion/sst-env.d.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable */
2 | /* eslint-disable */
3 | import "sst"
4 | declare module "sst" {
5 | export interface Resource {
6 | MyWeb: {
7 | type: "sst.aws.Nextjs"
8 | url: string
9 | }
10 | }
11 | }
12 | export {}
--------------------------------------------------------------------------------
/examples/ion/sst.config.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | export default $config({
4 | app(input) {
5 | return {
6 | name: "my-app",
7 | removal: input?.stage === "production" ? "retain" : "remove",
8 | home: "aws",
9 | };
10 | },
11 | async run() {
12 | new sst.aws.Nextjs("MyWeb", {
13 | environment: {
14 | UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL || "",
15 | UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN || "",
16 | },
17 | });
18 | },
19 | });
--------------------------------------------------------------------------------
/examples/ion/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | const config: Config = {
4 | content: [
5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | backgroundImage: {
12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
13 | "gradient-conic":
14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
15 | },
16 | },
17 | },
18 | plugins: [],
19 | };
20 | export default config;
21 |
--------------------------------------------------------------------------------
/examples/ion/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 | "exclude": ["node_modules","sst.config.ts"]
26 | }
27 |
--------------------------------------------------------------------------------
/examples/nextjs-app-router/.env.example:
--------------------------------------------------------------------------------
1 | UPSTASH_REDIS_REST_URL=
2 | UPSTASH_REDIS_REST_TOKEN=
--------------------------------------------------------------------------------
/examples/nextjs-app-router/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/examples/nextjs-app-router/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/examples/nextjs-app-router/README.md:
--------------------------------------------------------------------------------
1 | # Next.js 14 - App Router Example
2 |
3 | ### Project Setup
4 |
5 | Clone the example and install dependencies
6 |
7 | ```shell
8 | git clone https://github.com/upstash/redis-js.git
9 | cd redis-js/examples/nextjs-app-router
10 | npm install
11 | ```
12 |
13 | ### Database Setup
14 |
15 | Create a Redis database using [Upstash Console](https://console.upstash.com) or [Upstash CLI](https://github.com/upstash/cli) and copy the `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` into your `.env` file.
16 |
17 | ```shell
18 | UPSTASH_REDIS_REST_URL=
19 | UPSTASH_REDIS_REST_TOKEN=
20 | ```
21 |
22 |
23 | ### Run & Deploy
24 | Run the app locally with `npm run dev`, check `http://localhost:3000/`
25 |
26 | Deploy your app with `vercel`
27 |
--------------------------------------------------------------------------------
/examples/nextjs-app-router/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/examples/nextjs-app-router/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Inter } from "next/font/google";
3 | import "./globals.css";
4 |
5 | const inter = Inter({ subsets: ["latin"] });
6 |
7 | export const metadata: Metadata = {
8 | title: "Create Next App",
9 | description: "Generated by create next app",
10 | };
11 |
12 | export default function RootLayout({
13 | children,
14 | }: Readonly<{
15 | children: React.ReactNode;
16 | }>) {
17 | return (
18 |
19 | {children}
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/examples/nextjs-app-router/app/page.tsx:
--------------------------------------------------------------------------------
1 | import { Redis } from "@upstash/redis";
2 |
3 | const redis = Redis.fromEnv();
4 |
5 | export default async function Home() {
6 | const count = await redis.incr("counter");
7 | return (
8 |
9 |
Counter: {count}
10 |
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/examples/nextjs-app-router/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/examples/nextjs-app-router/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nextjs-app-router",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@upstash/redis": "latest",
13 | "next": "14.2.5",
14 | "react": "^18",
15 | "react-dom": "^18"
16 | },
17 | "devDependencies": {
18 | "@types/node": "^20",
19 | "@types/react": "^18",
20 | "@types/react-dom": "^18",
21 | "eslint": "^8",
22 | "eslint-config-next": "14.2.5",
23 | "postcss": "^8",
24 | "tailwindcss": "^3.4.1",
25 | "typescript": "^5"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/nextjs-app-router/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/examples/nextjs-app-router/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | const config: Config = {
4 | content: [
5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | backgroundImage: {
12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
13 | "gradient-conic":
14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
15 | },
16 | },
17 | },
18 | plugins: [],
19 | };
20 | export default config;
21 |
--------------------------------------------------------------------------------
/examples/nextjs-app-router/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------
/examples/nextjs-pages-router/.env.example:
--------------------------------------------------------------------------------
1 | UPSTASH_REDIS_REST_URL=
2 | UPSTASH_REDIS_REST_TOKEN=
--------------------------------------------------------------------------------
/examples/nextjs-pages-router/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/examples/nextjs-pages-router/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/examples/nextjs-pages-router/README.md:
--------------------------------------------------------------------------------
1 | # Next.js 14 - Pages Router Example
2 |
3 | ### Project Setup
4 |
5 | Clone the example and install dependencies
6 |
7 | ```shell
8 | git clone https://github.com/upstash/redis-js.git
9 | cd redis-js/examples/nextjs-pages-router
10 | npm install
11 | ```
12 |
13 | ### Database Setup
14 |
15 | Create a Redis database using [Upstash Console](https://console.upstash.com) or [Upstash CLI](https://github.com/upstash/cli) and copy the `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` into your `.env` file.
16 |
17 | ```shell
18 | UPSTASH_REDIS_REST_URL=
19 | UPSTASH_REDIS_REST_TOKEN=
20 | ```
21 |
22 |
23 | ### Run & Deploy
24 | Run the app locally with `npm run dev`, check `http://localhost:3000/`
25 |
26 | Deploy your app with `vercel`
27 |
--------------------------------------------------------------------------------
/examples/nextjs-pages-router/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | };
5 |
6 | export default nextConfig;
7 |
--------------------------------------------------------------------------------
/examples/nextjs-pages-router/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nextjs-pages-router",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@upstash/redis": "latest",
13 | "next": "14.2.5",
14 | "react": "^18",
15 | "react-dom": "^18"
16 | },
17 | "devDependencies": {
18 | "@types/node": "^20",
19 | "@types/react": "^18",
20 | "@types/react-dom": "^18",
21 | "eslint": "^8",
22 | "eslint-config-next": "14.2.5",
23 | "postcss": "^8",
24 | "tailwindcss": "^3.4.1",
25 | "typescript": "^5"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/nextjs-pages-router/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import "@/styles/globals.css";
2 | import type { AppProps } from "next/app";
3 |
4 | export default function App({ Component, pageProps }: AppProps) {
5 | return ;
6 | }
7 |
--------------------------------------------------------------------------------
/examples/nextjs-pages-router/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import { Html, Head, Main, NextScript } from "next/document";
2 |
3 | export default function Document() {
4 | return (
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/examples/nextjs-pages-router/pages/api/hello.ts:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 | import type { NextApiRequest, NextApiResponse } from "next";
3 |
4 | type Data = {
5 | name: string;
6 | };
7 |
8 | export default function handler(
9 | req: NextApiRequest,
10 | res: NextApiResponse,
11 | ) {
12 | res.status(200).json({ name: "John Doe" });
13 | }
14 |
--------------------------------------------------------------------------------
/examples/nextjs-pages-router/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import type { InferGetServerSidePropsType, GetServerSideProps } from 'next'
2 | import { Redis } from "@upstash/redis";
3 |
4 | const redis = Redis.fromEnv();
5 |
6 | export const getServerSideProps = (async () => {
7 | const count = await redis.incr("counter");
8 | return { props: { count } }
9 | }) satisfies GetServerSideProps<{ count: number }>
10 |
11 | export default function Home({
12 | count,
13 | }: InferGetServerSidePropsType) {
14 | return (
15 |
16 |
Counter: {count}
17 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/examples/nextjs-pages-router/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/examples/nextjs-pages-router/styles/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/examples/nextjs-pages-router/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | const config: Config = {
4 | content: [
5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | backgroundImage: {
12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
13 | "gradient-conic":
14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
15 | },
16 | },
17 | },
18 | plugins: [],
19 | };
20 | export default config;
21 |
--------------------------------------------------------------------------------
/examples/nextjs-pages-router/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "paths": {
16 | "@/*": ["./*"]
17 | }
18 | },
19 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
20 | "exclude": ["node_modules"]
21 | }
22 |
--------------------------------------------------------------------------------
/examples/nodejs/.env.example:
--------------------------------------------------------------------------------
1 | UPSTASH_REDIS_REST_URL=
2 | UPSTASH_REDIS_REST_TOKEN=
--------------------------------------------------------------------------------
/examples/nodejs/README.md:
--------------------------------------------------------------------------------
1 | # Node.js v20 Example
2 |
3 | ### Project Setup
4 |
5 | Clone the example and install dependencies
6 |
7 | ```shell
8 | git clone https://github.com/upstash/redis-js.git
9 | cd redis-js/examples/nodejs
10 | npm install
11 | ```
12 |
13 | ### Database Setup
14 |
15 | Create a Redis database using [Upstash Console](https://console.upstash.com) or [Upstash CLI](https://github.com/upstash/cli) and copy the `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` into your `.env` file.
16 |
17 | ```shell
18 | UPSTASH_REDIS_REST_URL=
19 | UPSTASH_REDIS_REST_TOKEN=
20 | ```
21 |
22 |
23 | ### Run
24 | Run the script with `node index.js`
25 |
--------------------------------------------------------------------------------
/examples/nodejs/index.js:
--------------------------------------------------------------------------------
1 | import { Redis } from "@upstash/redis";
2 |
3 | const redis = Redis.fromEnv();
4 |
5 | async function run() {
6 | const count = await redis.incr("counter");
7 | console.log("Counter:", count);
8 | }
9 |
10 | run();
--------------------------------------------------------------------------------
/examples/nodejs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nodejs",
3 | "version": "1.0.0",
4 | "type": "module",
5 | "main": "index.js",
6 | "license": "MIT",
7 | "dependencies": {
8 | "@upstash/redis": "latest"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/examples/serverless-framework/counter/.env.example:
--------------------------------------------------------------------------------
1 | UPSTASH_REDIS_REST_URL=
2 | UPSTASH_REDIS_REST_TOKEN=
--------------------------------------------------------------------------------
/examples/serverless-framework/counter/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .serverless
3 | .env
--------------------------------------------------------------------------------
/examples/serverless-framework/counter/handler.js:
--------------------------------------------------------------------------------
1 | const { Redis } = require('@upstash/redis');
2 |
3 | const redis = Redis.fromEnv();
4 |
5 | exports.counter = async (event) => {
6 | const count = await redis.incr("counter");
7 | return {
8 | statusCode: 200,
9 | body: JSON.stringify('Counter: ' + count),
10 | };
11 | };
12 |
13 |
--------------------------------------------------------------------------------
/examples/serverless-framework/counter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "@upstash/redis": "latest"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/serverless-framework/counter/serverless.yml:
--------------------------------------------------------------------------------
1 | service: counter
2 |
3 | provider:
4 | name: aws
5 | runtime: nodejs20.x
6 | environment:
7 | UPSTASH_REDIS_REST_URL: ${env:UPSTASH_REDIS_REST_URL}
8 | UPSTASH_REDIS_REST_TOKEN: ${env:UPSTASH_REDIS_REST_TOKEN}
9 |
10 | functions:
11 | counter:
12 | handler: handler.counter
13 | events:
14 | - httpApi:
15 | path: /
16 | method: get
17 |
--------------------------------------------------------------------------------
/examples/sst-v2/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | node_modules
3 |
4 | # sst
5 | .sst
6 | .build
7 |
8 | # opennext
9 | .open-next
10 |
11 | # misc
12 | .DS_Store
13 |
14 | # local env files
15 | .env*.local
16 |
--------------------------------------------------------------------------------
/examples/sst-v2/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Debug SST Start",
6 | "type": "node",
7 | "request": "launch",
8 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/sst",
9 | "runtimeArgs": ["start", "--increase-timeout"],
10 | "console": "integratedTerminal",
11 | "skipFiles": ["/**"],
12 | "env": {}
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/examples/sst-v2/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "search.exclude": {
3 | "**/.sst": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/sst-v2/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sst-v2",
3 | "version": "0.0.0",
4 | "private": true,
5 | "type": "module",
6 | "scripts": {
7 | "dev": "sst dev",
8 | "build": "sst build",
9 | "deploy": "sst deploy",
10 | "remove": "sst remove",
11 | "console": "sst console",
12 | "typecheck": "tsc --noEmit"
13 | },
14 | "devDependencies": {
15 | "@tsconfig/node18": "^18.2.4",
16 | "aws-cdk-lib": "2.142.1",
17 | "constructs": "10.3.0",
18 | "sst": "^2.43.4",
19 | "typescript": "^5.5.4"
20 | },
21 | "workspaces": [
22 | "packages/*"
23 | ],
24 | "dependencies": {
25 | "@upstash/redis": "latest"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/sst-v2/packages/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sst-v2/core",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "scripts": {
6 | "test": "sst bind vitest",
7 | "typecheck": "tsc -noEmit"
8 | },
9 | "devDependencies": {
10 | "vitest": "^2.0.5",
11 | "@types/node": "^22.0.0",
12 | "sst": "^2.43.4"
13 | }
14 | }
--------------------------------------------------------------------------------
/examples/sst-v2/packages/core/sst-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/examples/sst-v2/packages/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@tsconfig/node18/tsconfig.json",
3 | "compilerOptions": {
4 | "module": "esnext",
5 | "moduleResolution": "node"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/examples/sst-v2/packages/functions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sst-v2/functions",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "scripts": {
6 | "test": "sst bind vitest",
7 | "typecheck": "tsc -noEmit"
8 | },
9 | "devDependencies": {
10 | "@types/node": "^22.0.0",
11 | "@types/aws-lambda": "^8.10.142",
12 | "vitest": "^2.0.5",
13 | "sst": "^2.43.4"
14 | }
15 | }
--------------------------------------------------------------------------------
/examples/sst-v2/packages/functions/sst-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/examples/sst-v2/packages/functions/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@tsconfig/node18/tsconfig.json",
3 | "compilerOptions": {
4 | "module": "esnext",
5 | "moduleResolution": "node",
6 | "baseUrl": ".",
7 | "paths": {
8 | "@sst-v2/core/*": ["../core/src/*"]
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/examples/sst-v2/packages/web/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/examples/sst-v2/packages/web/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | };
5 |
6 | export default nextConfig;
7 |
--------------------------------------------------------------------------------
/examples/sst-v2/packages/web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "web",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "sst bind next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "react": "^18",
13 | "react-dom": "^18",
14 | "next": "14.2.5"
15 | },
16 | "devDependencies": {
17 | "typescript": "^5",
18 | "@types/node": "^20",
19 | "@types/react": "^18",
20 | "@types/react-dom": "^18",
21 | "sst": "^2.43.4"
22 | }
23 | }
--------------------------------------------------------------------------------
/examples/sst-v2/packages/web/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import "@/styles/globals.css";
2 | import type { AppProps } from "next/app";
3 |
4 | export default function App({ Component, pageProps }: AppProps) {
5 | return ;
6 | }
7 |
--------------------------------------------------------------------------------
/examples/sst-v2/packages/web/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import { Html, Head, Main, NextScript } from "next/document";
2 |
3 | export default function Document() {
4 | return (
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/examples/sst-v2/packages/web/pages/api/hello.ts:
--------------------------------------------------------------------------------
1 | import { Redis } from "@upstash/redis";
2 | import type { NextApiRequest, NextApiResponse } from "next";
3 | import { Config } from "sst/node/config";
4 |
5 | const redis = new Redis({
6 | url: Config.UPSTASH_REDIS_REST_URL,
7 | token: Config.UPSTASH_REDIS_REST_TOKEN,
8 | });
9 |
10 | export default async function handler(
11 | req: NextApiRequest,
12 | res: NextApiResponse,
13 | ) {
14 | const count = await redis.incr("counter");
15 | res.status(200).json({ count });
16 | }
--------------------------------------------------------------------------------
/examples/sst-v2/packages/web/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/upstash/redis-js/09f2eba6fd7ea2ed890386d10f7bef17d10e1769/examples/sst-v2/packages/web/public/favicon.ico
--------------------------------------------------------------------------------
/examples/sst-v2/packages/web/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/sst-v2/packages/web/sst-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/examples/sst-v2/packages/web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": [
4 | "dom",
5 | "dom.iterable",
6 | "esnext"
7 | ],
8 | "allowJs": true,
9 | "skipLibCheck": true,
10 | "strict": true,
11 | "noEmit": true,
12 | "esModuleInterop": true,
13 | "module": "esnext",
14 | "moduleResolution": "bundler",
15 | "resolveJsonModule": true,
16 | "isolatedModules": true,
17 | "jsx": "preserve",
18 | "incremental": true,
19 | "paths": {
20 | "@/*": [
21 | "./*"
22 | ],
23 | "@sst-v2/core/*": [
24 | "../core/src/*"
25 | ]
26 | }
27 | },
28 | "include": [
29 | "next-env.d.ts",
30 | "**/*.ts",
31 | "**/*.tsx"
32 | ],
33 | "exclude": [
34 | "node_modules"
35 | ]
36 | }
--------------------------------------------------------------------------------
/examples/sst-v2/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - "packages/**/*"
3 |
--------------------------------------------------------------------------------
/examples/sst-v2/sst.config.ts:
--------------------------------------------------------------------------------
1 | import { SSTConfig } from "sst";
2 | import { Default } from "./stacks/Default";
3 |
4 | export default {
5 | config(_input) {
6 | return {
7 | name: "sst-v2",
8 | region: "us-east-1",
9 | };
10 | },
11 | stacks(app) {
12 | app.stack(Default);
13 | }
14 | } satisfies SSTConfig;
15 |
--------------------------------------------------------------------------------
/examples/sst-v2/stacks/Default.ts:
--------------------------------------------------------------------------------
1 | import { Config, StackContext, NextjsSite } from "sst/constructs";
2 |
3 | export function Default({ stack }: StackContext) {
4 | const UPSTASH_REDIS_REST_URL = new Config.Secret(stack, "UPSTASH_REDIS_REST_URL");
5 | const UPSTASH_REDIS_REST_TOKEN = new Config.Secret(stack, "UPSTASH_REDIS_REST_TOKEN");
6 | const site = new NextjsSite(stack, "site", {
7 | bind: [UPSTASH_REDIS_REST_URL, UPSTASH_REDIS_REST_TOKEN],
8 | path: "packages/web",
9 | });
10 | stack.addOutputs({
11 | SiteUrl: site.url,
12 | });
13 | }
--------------------------------------------------------------------------------
/examples/sst-v2/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@tsconfig/node18/tsconfig.json",
3 | "exclude": ["packages"],
4 | "compilerOptions": {
5 | "module": "esnext",
6 | "moduleResolution": "node"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/terraform/counter/counter.js:
--------------------------------------------------------------------------------
1 | const { Redis } = require('@upstash/redis');
2 |
3 | const redis = Redis.fromEnv();
4 |
5 | module.exports.handler = async (event) => {
6 | const count = await redis.incr("counter");
7 | return {
8 | statusCode: 200,
9 | body: JSON.stringify('Counter: ' + count),
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/examples/terraform/counter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "@upstash/redis": "latest"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/terraform/outputs.tf:
--------------------------------------------------------------------------------
1 | output "lambda_bucket_name" {
2 | description = "Name of the S3 bucket used to store function code."
3 |
4 | value = aws_s3_bucket.lambda_bucket.id
5 | }
6 |
7 | output "function_name" {
8 | description = "Name of the Lambda function."
9 |
10 | value = aws_lambda_function.counter.function_name
11 | }
12 |
13 | output "base_url" {
14 | description = "Base URL for API Gateway stage."
15 |
16 | value = aws_apigatewayv2_stage.lambda.invoke_url
17 | }
18 |
--------------------------------------------------------------------------------
/examples/terraform/terraform.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | aws = {
4 | source = "hashicorp/aws"
5 | version = "~> 5.38.0"
6 | }
7 | random = {
8 | source = "hashicorp/random"
9 | version = "~> 3.6.0"
10 | }
11 | archive = {
12 | source = "hashicorp/archive"
13 | version = "~> 2.4.2"
14 | }
15 | }
16 |
17 | required_version = "~> 1.2"
18 | }
--------------------------------------------------------------------------------
/examples/terraform/variables.tf:
--------------------------------------------------------------------------------
1 | variable "aws_region" {
2 | description = "AWS region for all resources."
3 |
4 | type = string
5 | default = "us-east-1"
6 | }
--------------------------------------------------------------------------------
/examples/vercel-functions-app-router/.env.example:
--------------------------------------------------------------------------------
1 | UPSTASH_REDIS_REST_URL=
2 | UPSTASH_REDIS_REST_TOKEN=
--------------------------------------------------------------------------------
/examples/vercel-functions-app-router/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/examples/vercel-functions-app-router/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/examples/vercel-functions-app-router/app/api/hello/route.ts:
--------------------------------------------------------------------------------
1 | import { Redis } from "@upstash/redis";
2 | import { NextResponse } from "next/server";
3 |
4 | const redis = Redis.fromEnv();
5 |
6 | export async function GET() {
7 | const count = await redis.incr("counter");
8 | return NextResponse.json({ count });
9 | }
10 |
11 | export const dynamic = 'force-dynamic'
12 |
--------------------------------------------------------------------------------
/examples/vercel-functions-app-router/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/examples/vercel-functions-app-router/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Inter } from "next/font/google";
3 | import "./globals.css";
4 |
5 | const inter = Inter({ subsets: ["latin"] });
6 |
7 | export const metadata: Metadata = {
8 | title: "Create Next App",
9 | description: "Generated by create next app",
10 | };
11 |
12 | export default function RootLayout({
13 | children,
14 | }: Readonly<{
15 | children: React.ReactNode;
16 | }>) {
17 | return (
18 |
19 | {children}
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/examples/vercel-functions-app-router/ci.test.ts:
--------------------------------------------------------------------------------
1 | import {test,expect} from "bun:test"
2 | const deploymentURL = process.env.DEPLOYMENT_URL;
3 | if (!deploymentURL) {
4 | throw new Error("DEPLOYMENT_URL not set");
5 | }
6 |
7 | test("works", async () => {
8 | console.log({ deploymentURL });
9 | const url = `${deploymentURL}/api/hello`;
10 | const res = await fetch(url);
11 | expect(res.status).toEqual(200);
12 | const json = (await res.json()) as { count: number };
13 | expect(typeof json.count).toEqual("number");
14 | });
--------------------------------------------------------------------------------
/examples/vercel-functions-app-router/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/examples/vercel-functions-app-router/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vercel-functions-app-router",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@upstash/redis": "latest",
13 | "next": "14.2.5",
14 | "react": "^18",
15 | "react-dom": "^18"
16 | },
17 | "devDependencies": {
18 | "@types/node": "^20",
19 | "@types/react": "^18",
20 | "@types/react-dom": "^18",
21 | "eslint": "^8",
22 | "eslint-config-next": "14.2.5",
23 | "postcss": "^8",
24 | "tailwindcss": "^3.4.1",
25 | "typescript": "^5"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/vercel-functions-app-router/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/examples/vercel-functions-app-router/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | const config: Config = {
4 | content: [
5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | backgroundImage: {
12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
13 | "gradient-conic":
14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
15 | },
16 | },
17 | },
18 | plugins: [],
19 | };
20 | export default config;
21 |
--------------------------------------------------------------------------------
/examples/vercel-functions-app-router/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------
/examples/vercel-functions-pages-router/.env.example:
--------------------------------------------------------------------------------
1 | UPSTASH_REDIS_REST_URL=
2 | UPSTASH_REDIS_REST_TOKEN=
--------------------------------------------------------------------------------
/examples/vercel-functions-pages-router/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/examples/vercel-functions-pages-router/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/examples/vercel-functions-pages-router/ci.test.ts:
--------------------------------------------------------------------------------
1 | import {test,expect} from "bun:test"
2 | const deploymentURL = process.env.DEPLOYMENT_URL;
3 | if (!deploymentURL) {
4 | throw new Error("DEPLOYMENT_URL not set");
5 | }
6 |
7 | test("works", async () => {
8 | console.log({ deploymentURL });
9 | const url = `${deploymentURL}/api/hello`;
10 | const res = await fetch(url);
11 | expect(res.status).toEqual(200);
12 | const json = (await res.json()) as { count: number };
13 | expect(typeof json.count).toEqual("number");
14 | });
--------------------------------------------------------------------------------
/examples/vercel-functions-pages-router/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | };
5 |
6 | export default nextConfig;
7 |
--------------------------------------------------------------------------------
/examples/vercel-functions-pages-router/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vercel-functions-pages-router",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@upstash/redis": "latest",
13 | "next": "14.2.5",
14 | "react": "^18",
15 | "react-dom": "^18"
16 | },
17 | "devDependencies": {
18 | "@types/node": "^20",
19 | "@types/react": "^18",
20 | "@types/react-dom": "^18",
21 | "eslint": "^8",
22 | "eslint-config-next": "14.2.5",
23 | "postcss": "^8",
24 | "tailwindcss": "^3.4.1",
25 | "typescript": "^5"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/vercel-functions-pages-router/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import "@/styles/globals.css";
2 | import type { AppProps } from "next/app";
3 |
4 | export default function App({ Component, pageProps }: AppProps) {
5 | return ;
6 | }
7 |
--------------------------------------------------------------------------------
/examples/vercel-functions-pages-router/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import { Html, Head, Main, NextScript } from "next/document";
2 |
3 | export default function Document() {
4 | return (
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/examples/vercel-functions-pages-router/pages/api/hello.ts:
--------------------------------------------------------------------------------
1 | import { Redis } from "@upstash/redis";
2 | import type { NextApiRequest, NextApiResponse } from "next";
3 |
4 | const redis = Redis.fromEnv();
5 |
6 | export default async function handler(
7 | req: NextApiRequest,
8 | res: NextApiResponse,
9 | ) {
10 | const count = await redis.incr("counter");
11 | res.status(200).json({ count });
12 | }
13 |
--------------------------------------------------------------------------------
/examples/vercel-functions-pages-router/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/examples/vercel-functions-pages-router/styles/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/examples/vercel-functions-pages-router/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | const config: Config = {
4 | content: [
5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | backgroundImage: {
12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
13 | "gradient-conic":
14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
15 | },
16 | },
17 | },
18 | plugins: [],
19 | };
20 | export default config;
21 |
--------------------------------------------------------------------------------
/examples/vercel-functions-pages-router/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "paths": {
16 | "@/*": ["./*"]
17 | }
18 | },
19 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
20 | "exclude": ["node_modules"]
21 | }
22 |
--------------------------------------------------------------------------------
/examples/vercel-python-runtime-django/.env.example:
--------------------------------------------------------------------------------
1 | UPSTASH_REDIS_REST_URL=
2 | UPSTASH_REDIS_REST_TOKEN=
--------------------------------------------------------------------------------
/examples/vercel-python-runtime-django/.gitignore:
--------------------------------------------------------------------------------
1 | .vercel
2 | *.log
3 | *.pyc
4 | __pycache__
5 | db.sqlite3
6 | media
7 |
8 | # Environments
9 | .env
10 | .venv
11 | env/
12 | venv/
13 | ENV/
14 | env.bak/
15 | venv.bak/
--------------------------------------------------------------------------------
/examples/vercel-python-runtime-django/api/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/upstash/redis-js/09f2eba6fd7ea2ed890386d10f7bef17d10e1769/examples/vercel-python-runtime-django/api/__init__.py
--------------------------------------------------------------------------------
/examples/vercel-python-runtime-django/api/asgi.py:
--------------------------------------------------------------------------------
1 | """
2 | ASGI config for api project.
3 |
4 | It exposes the ASGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.asgi import get_asgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api.settings')
15 |
16 | application = get_asgi_application()
17 |
--------------------------------------------------------------------------------
/examples/vercel-python-runtime-django/api/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for api project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``app``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api.settings')
15 |
16 | app = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/examples/vercel-python-runtime-django/example/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/upstash/redis-js/09f2eba6fd7ea2ed890386d10f7bef17d10e1769/examples/vercel-python-runtime-django/example/__init__.py
--------------------------------------------------------------------------------
/examples/vercel-python-runtime-django/example/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/examples/vercel-python-runtime-django/example/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class ExampleConfig(AppConfig):
5 | default_auto_field = 'django.db.models.BigAutoField'
6 | name = 'example'
7 |
--------------------------------------------------------------------------------
/examples/vercel-python-runtime-django/example/urls.py:
--------------------------------------------------------------------------------
1 | # example/urls.py
2 | from django.urls import path
3 |
4 | from example.views import index
5 |
6 |
7 | urlpatterns = [
8 | path('', index),
9 | ]
--------------------------------------------------------------------------------
/examples/vercel-python-runtime-django/example/views.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 |
3 | from django.http import HttpResponse
4 |
5 | from upstash_redis import Redis
6 |
7 | redis = Redis.from_env()
8 |
9 | def index(request):
10 | count = redis.incr('counter')
11 | html = f'''
12 |
13 |
14 | Counter: { count }
15 |
16 |
17 | '''
18 | return HttpResponse(html)
--------------------------------------------------------------------------------
/examples/vercel-python-runtime-django/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 |
7 | def main():
8 | """Run administrative tasks."""
9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api.settings')
10 | try:
11 | from django.core.management import execute_from_command_line
12 | except ImportError as exc:
13 | raise ImportError(
14 | "Couldn't import Django. Are you sure it's installed and "
15 | "available on your PYTHONPATH environment variable? Did you "
16 | "forget to activate a virtual environment?"
17 | ) from exc
18 | execute_from_command_line(sys.argv)
19 |
20 |
21 | if __name__ == '__main__':
22 | main()
23 |
--------------------------------------------------------------------------------
/examples/vercel-python-runtime-django/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "engines": {
3 | "node": "18.x"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/vercel-python-runtime-django/requirements.txt:
--------------------------------------------------------------------------------
1 | Django==4.1.3
2 | upstash-redis
--------------------------------------------------------------------------------
/examples/vercel-python-runtime-django/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [
3 | {
4 | "src": "/(.*)",
5 | "dest": "api/wsgi.py"
6 | }
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/examples/with-sentry/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "with-sentry",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "Andreas Thomas",
11 | "license": "ISC",
12 | "dependencies": {
13 | "@sentry/node": "^7.14.2",
14 | "@upstash/redis": "latest",
15 | "isomorphic-fetch": "^3.0.0"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/commands/append.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/append
6 | */
7 | export class AppendCommand extends Command {
8 | constructor(cmd: [key: string, value: string], opts?: CommandOptions) {
9 | super(["append", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/bitop.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/bitop
6 | */
7 | export class BitOpCommand extends Command {
8 | constructor(
9 | cmd: [op: "and" | "or" | "xor", destinationKey: string, ...sourceKeys: string[]],
10 | opts?: CommandOptions
11 | );
12 | constructor(
13 | cmd: [op: "not", destinationKey: string, sourceKey: string],
14 | opts?: CommandOptions
15 | );
16 | constructor(
17 | cmd: [op: "and" | "or" | "xor" | "not", destinationKey: string, ...sourceKeys: string[]],
18 | opts?: CommandOptions
19 | ) {
20 | super(["bitop", ...cmd], opts);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/pkg/commands/bitpos.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/bitpos
6 | */
7 | export class BitPosCommand extends Command {
8 | constructor(
9 | cmd: [key: string, bit: 0 | 1, start?: number, end?: number],
10 | opts?: CommandOptions
11 | ) {
12 | super(["bitpos", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/command.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 | import { Command } from "./command";
3 |
4 | import { afterAll, describe, expect, test } from "bun:test";
5 |
6 | const client = newHttpClient();
7 |
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 |
11 | describe("deserialize large numbers", () => {
12 | test("returns the correct number", async () => {
13 | const key = newKey();
14 | const field = randomID();
15 | const value = "101600000000150081467";
16 |
17 | await new Command(["hset", key, field, value]).exec(client);
18 |
19 | const res = await new Command(["hget", key, field]).exec(client);
20 | expect(res).toEqual(value);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/pkg/commands/copy.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/copy
6 | */
7 | export class CopyCommand extends Command {
8 | constructor(
9 | [key, destinationKey, opts]: [key: string, destinationKey: string, opts?: { replace: boolean }],
10 | commandOptions?: CommandOptions
11 | ) {
12 | super(["COPY", key, destinationKey, ...(opts?.replace ? ["REPLACE"] : [])], {
13 | ...commandOptions,
14 | deserialize(result) {
15 | if (result > 0) {
16 | return "COPIED";
17 | }
18 | return "NOT_COPIED";
19 | },
20 | });
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/pkg/commands/dbsize.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 | import { DBSizeCommand } from "./dbsize";
5 | import { SetCommand } from "./set";
6 |
7 | const client = newHttpClient();
8 |
9 | const { newKey, cleanup } = keygen();
10 | afterAll(cleanup);
11 |
12 | test("returns the db size", async () => {
13 | const key = newKey();
14 | const value = randomID();
15 | await new SetCommand([key, value]).exec(client);
16 | const res = await new DBSizeCommand().exec(client);
17 | expect(res > 0).toEqual(true);
18 | });
19 |
--------------------------------------------------------------------------------
/pkg/commands/dbsize.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/dbsize
6 | */
7 | export class DBSizeCommand extends Command {
8 | constructor(opts?: CommandOptions) {
9 | super(["dbsize"], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/decr.test.ts:
--------------------------------------------------------------------------------
1 | import { afterAll, expect, test } from "bun:test";
2 | import { keygen, newHttpClient } from "../test-utils";
3 | import { DecrCommand } from "./decr";
4 | import { SetCommand } from "./set";
5 |
6 | const client = newHttpClient();
7 |
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 | test("decrements a non-existing value", async () => {
11 | const key = newKey();
12 | const res = await new DecrCommand([key]).exec(client);
13 |
14 | expect(res).toEqual(-1);
15 | });
16 |
17 | test("decrements and existing value", async () => {
18 | const key = newKey();
19 | await new SetCommand([key, 4]).exec(client);
20 | const res = await new DecrCommand([key]).exec(client);
21 |
22 | expect(res).toEqual(3);
23 | });
24 |
--------------------------------------------------------------------------------
/pkg/commands/decr.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/decr
6 | */
7 | export class DecrCommand extends Command {
8 | constructor(cmd: [key: string], opts?: CommandOptions) {
9 | super(["decr", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/decrby.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient } from "../test-utils";
2 | import { SetCommand } from "./set";
3 |
4 | import { afterAll, expect, test } from "bun:test";
5 | import { DecrByCommand } from "./decrby";
6 | const client = newHttpClient();
7 |
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 |
11 | test("decrements a non-existing value", async () => {
12 | const key = newKey();
13 | const res = await new DecrByCommand([key, 2]).exec(client);
14 |
15 | expect(res).toEqual(-2);
16 | });
17 |
18 | test("decrements and existing value", async () => {
19 | const key = newKey();
20 | await new SetCommand([key, 5]).exec(client);
21 | const res = await new DecrByCommand([key, 2]).exec(client);
22 |
23 | expect(res).toEqual(3);
24 | });
25 |
--------------------------------------------------------------------------------
/pkg/commands/decrby.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/decrby
6 | */
7 | export class DecrByCommand extends Command {
8 | constructor(cmd: [key: string, decrement: number], opts?: CommandOptions) {
9 | super(["decrby", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/del.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/del
6 | */
7 | export class DelCommand extends Command {
8 | constructor(cmd: [...keys: string[]], opts?: CommandOptions) {
9 | super(["del", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/echo.test.ts:
--------------------------------------------------------------------------------
1 | import { newHttpClient, randomID } from "../test-utils";
2 |
3 | import { expect, test } from "bun:test";
4 | import { EchoCommand } from "./echo";
5 | const client = newHttpClient();
6 |
7 | test("returns the message", async () => {
8 | const message = randomID();
9 | const res = await new EchoCommand([message]).exec(client);
10 | expect(res).toEqual(message);
11 | });
12 |
--------------------------------------------------------------------------------
/pkg/commands/echo.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/echo
6 | */
7 | export class EchoCommand extends Command {
8 | constructor(cmd: [message: string], opts?: CommandOptions) {
9 | super(["echo", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/eval.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/eval
6 | */
7 | export class EvalCommand extends Command {
8 | constructor(
9 | [script, keys, args]: [script: string, keys: string[], args: TArgs],
10 | opts?: CommandOptions
11 | ) {
12 | super(["eval", script, keys.length, ...keys, ...(args ?? [])], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/evalRo.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/eval_ro
6 | */
7 | export class EvalROCommand extends Command {
8 | constructor(
9 | [script, keys, args]: [script: string, keys: string[], args: TArgs],
10 | opts?: CommandOptions
11 | ) {
12 | super(["eval_ro", script, keys.length, ...keys, ...(args ?? [])], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/evalsha.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, describe, expect, test } from "bun:test";
4 | import { EvalshaCommand } from "./evalsha";
5 | import { ScriptLoadCommand } from "./script_load";
6 |
7 | const client = newHttpClient();
8 |
9 | const { cleanup } = keygen();
10 | afterAll(cleanup);
11 |
12 | describe("without keys", () => {
13 | test("returns something", async () => {
14 | const value = randomID();
15 | const sha1 = await new ScriptLoadCommand([`return {ARGV[1], "${value}"}`]).exec(client);
16 | const res = await new EvalshaCommand([sha1, [], [value]]).exec(client);
17 | expect(res).toEqual([value, value]);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/pkg/commands/evalsha.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/evalsha
6 | */
7 | export class EvalshaCommand extends Command {
8 | constructor(
9 | [sha, keys, args]: [sha: string, keys: string[], args?: TArgs],
10 | opts?: CommandOptions
11 | ) {
12 | super(["evalsha", sha, keys.length, ...keys, ...(args ?? [])], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/evalshaRo.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/evalsha_ro
6 | */
7 | export class EvalshaROCommand extends Command {
8 | constructor(
9 | [sha, keys, args]: [sha: string, keys: string[], args?: TArgs],
10 | opts?: CommandOptions
11 | ) {
12 | super(["evalsha_ro", sha, keys.length, ...keys, ...(args ?? [])], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/exists.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/exists
6 | */
7 | export class ExistsCommand extends Command {
8 | constructor(cmd: [...keys: string[]], opts?: CommandOptions) {
9 | super(["exists", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/expire.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | export type ExpireOption = "NX" | "nx" | "XX" | "xx" | "GT" | "gt" | "LT" | "lt";
5 |
6 | export class ExpireCommand extends Command<"0" | "1", 0 | 1> {
7 | constructor(
8 | cmd: [key: string, seconds: number, option?: ExpireOption],
9 | opts?: CommandOptions<"0" | "1", 0 | 1>
10 | ) {
11 | super(["expire", ...cmd.filter(Boolean)], opts);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/pkg/commands/expireat.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | import type { ExpireOption } from "./expire";
4 |
5 | /**
6 | * @see https://redis.io/commands/expireat
7 | */
8 | export class ExpireAtCommand extends Command<"0" | "1", 0 | 1> {
9 | constructor(
10 | cmd: [key: string, unix: number, option?: ExpireOption],
11 | opts?: CommandOptions<"0" | "1", 0 | 1>
12 | ) {
13 | super(["expireat", ...cmd], opts);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/pkg/commands/flushall.test.ts:
--------------------------------------------------------------------------------
1 | import { newHttpClient } from "../test-utils";
2 | import { FlushAllCommand } from "./flushall";
3 |
4 | import { describe, expect, test } from "bun:test";
5 | const client = newHttpClient();
6 |
7 | describe("without options", () => {
8 | test("flushes the db", async () => {
9 | const res = await new FlushAllCommand().exec(client);
10 | expect(res).toEqual("OK");
11 | });
12 | });
13 | describe("async", () => {
14 | test("flushes the db", async () => {
15 | const res = await new FlushAllCommand([{ async: true }]).exec(client);
16 | expect(res).toEqual("OK");
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/pkg/commands/flushall.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | /**
4 | * @see https://redis.io/commands/flushall
5 | */
6 | export class FlushAllCommand extends Command<"OK", "OK"> {
7 | constructor(args?: [{ async?: boolean }], opts?: CommandOptions<"OK", "OK">) {
8 | const command = ["flushall"];
9 | if (args && args.length > 0 && args[0].async) {
10 | command.push("async");
11 | }
12 | super(command, opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/flushdb.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, test } from "bun:test";
2 | import { newHttpClient } from "../test-utils";
3 | import { FlushDBCommand } from "./flushdb";
4 | const client = newHttpClient();
5 |
6 | describe("without options", () => {
7 | test("flushes the db", async () => {
8 | const res = await new FlushDBCommand([]).exec(client);
9 | expect(res).toEqual("OK");
10 | });
11 | });
12 | describe("async", () => {
13 | test("flushes the db", async () => {
14 | const res = await new FlushDBCommand([{ async: true }]).exec(client);
15 | expect(res).toEqual("OK");
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/pkg/commands/flushdb.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | /**
4 | * @see https://redis.io/commands/flushdb
5 | */
6 | export class FlushDBCommand extends Command<"OK", "OK"> {
7 | constructor([opts]: [opts?: { async?: boolean }], cmdOpts?: CommandOptions<"OK", "OK">) {
8 | const command = ["flushdb"];
9 | if (opts?.async) {
10 | command.push("async");
11 | }
12 | super(command, cmdOpts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/geo_dist.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/geodist
6 | */
7 | export class GeoDistCommand extends Command {
8 | constructor(
9 | [key, member1, member2, unit = "M"]: [
10 | key: string,
11 | member1: TMemberType,
12 | member2: TMemberType,
13 | unit?: "M" | "KM" | "FT" | "MI",
14 | ],
15 | opts?: CommandOptions
16 | ) {
17 | super(["GEODIST", key, member1, member2, unit], opts);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/pkg/commands/get.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/get
6 | */
7 | export class GetCommand extends Command {
8 | constructor(cmd: [key: string], opts?: CommandOptions) {
9 | super(["get", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/getbit.test.ts:
--------------------------------------------------------------------------------
1 | import { afterAll, expect, test } from "bun:test";
2 | import { keygen, newHttpClient } from "../test-utils";
3 | import { SetBitCommand } from "./setbit";
4 |
5 | import { GetBitCommand } from "./getbit";
6 | const client = newHttpClient();
7 |
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 |
11 | test("returns the bit at offset", async () => {
12 | const key = newKey();
13 |
14 | await new SetBitCommand([key, 0, 1]).exec(client);
15 | const res = await new GetBitCommand([key, 0]).exec(client);
16 | expect(res).toEqual(1);
17 | });
18 |
--------------------------------------------------------------------------------
/pkg/commands/getbit.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/getbit
6 | */
7 | export class GetBitCommand extends Command<"0" | "1", 0 | 1> {
8 | constructor(cmd: [key: string, offset: number], opts?: CommandOptions<"0" | "1", 0 | 1>) {
9 | super(["getbit", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/getdel.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/getdel
6 | */
7 | export class GetDelCommand extends Command {
8 | constructor(cmd: [key: string], opts?: CommandOptions) {
9 | super(["getdel", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/getrange.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/getrange
6 | */
7 | export class GetRangeCommand extends Command {
8 | constructor(
9 | cmd: [key: string, start: number, end: number],
10 | opts?: CommandOptions
11 | ) {
12 | super(["getrange", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/getset.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/getset
6 | */
7 | export class GetSetCommand extends Command {
8 | constructor(
9 | cmd: [key: string, value: TData],
10 | opts?: CommandOptions
11 | ) {
12 | super(["getset", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/hdel.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/hdel
6 | */
7 | export class HDelCommand extends Command<"0" | "1", 0 | 1> {
8 | constructor(cmd: [key: string, ...fields: string[]], opts?: CommandOptions<"0" | "1", 0 | 1>) {
9 | super(["hdel", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/hexists.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/hexists
6 | */
7 | export class HExistsCommand extends Command {
8 | constructor(cmd: [key: string, field: string], opts?: CommandOptions) {
9 | super(["hexists", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/hexpiretime.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | export class HExpireTimeCommand extends Command {
5 | constructor(
6 | cmd: [key: string, fields: (string | number) | (string | number)[]],
7 | opts?: CommandOptions
8 | ) {
9 | const [key, fields] = cmd;
10 | const fieldArray = Array.isArray(fields) ? fields : [fields];
11 | super(["hexpiretime", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/pkg/commands/hget.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/hget
6 | */
7 | export class HGetCommand extends Command {
8 | constructor(
9 | cmd: [key: string, field: string],
10 | opts?: CommandOptions
11 | ) {
12 | super(["hget", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/hincrby.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/hincrby
6 | */
7 | export class HIncrByCommand extends Command {
8 | constructor(
9 | cmd: [key: string, field: string, increment: number],
10 | opts?: CommandOptions
11 | ) {
12 | super(["hincrby", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/hincrbyfloat.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/hincrbyfloat
6 | */
7 | export class HIncrByFloatCommand extends Command {
8 | constructor(
9 | cmd: [key: string, field: string, increment: number],
10 | opts?: CommandOptions
11 | ) {
12 | super(["hincrbyfloat", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/hkeys.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, describe, expect, test } from "bun:test";
4 | import { HKeysCommand } from "./hkeys";
5 | import { HMSetCommand } from "./hmset";
6 |
7 | const client = newHttpClient();
8 |
9 | const { newKey, cleanup } = keygen();
10 | afterAll(cleanup);
11 |
12 | describe("with existing hash", () => {
13 | test("returns all keys", async () => {
14 | const key = newKey();
15 | const kv = {
16 | [randomID()]: randomID(),
17 | [randomID()]: randomID(),
18 | };
19 | await new HMSetCommand([key, kv]).exec(client);
20 | const res = await new HKeysCommand([key]).exec(client);
21 | expect(res.sort()).toEqual(Object.keys(kv).sort());
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/pkg/commands/hkeys.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/hkeys
6 | */
7 | export class HKeysCommand extends Command {
8 | constructor([key]: [key: string], opts?: CommandOptions) {
9 | super(["hkeys", key], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/hlen.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/hlen
6 | */
7 | export class HLenCommand extends Command {
8 | constructor(cmd: [key: string], opts?: CommandOptions) {
9 | super(["hlen", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/hmset.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 | import { HMGetCommand } from "./hmget";
5 | import { HMSetCommand } from "./hmset";
6 |
7 | const client = newHttpClient();
8 |
9 | const { newKey, cleanup } = keygen();
10 | afterAll(cleanup);
11 | test("gets exiting values", async () => {
12 | const key = newKey();
13 | const kv = {
14 | [randomID()]: randomID(),
15 | [randomID()]: randomID(),
16 | };
17 | const res = await new HMSetCommand([key, kv]).exec(client);
18 |
19 | expect(res).toEqual("OK");
20 | const res2 = await new HMGetCommand([key, ...Object.keys(kv)]).exec(client);
21 |
22 | expect(res2).toEqual(kv);
23 | });
24 |
--------------------------------------------------------------------------------
/pkg/commands/hmset.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/hmset
6 | */
7 | export class HMSetCommand extends Command<"OK", "OK"> {
8 | constructor(
9 | [key, kv]: [key: string, kv: Record],
10 | opts?: CommandOptions<"OK", "OK">
11 | ) {
12 | super(["hmset", key, ...Object.entries(kv).flatMap(([field, value]) => [field, value])], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/hpersist.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | export class HPersistCommand extends Command<(-2 | -1 | 1)[], (-2 | -1 | 1)[]> {
5 | constructor(
6 | cmd: [key: string, fields: (string | number) | (string | number)[]],
7 | opts?: CommandOptions<(-2 | -1 | 1)[], (-2 | -1 | 1)[]>
8 | ) {
9 | const [key, fields] = cmd;
10 | const fieldArray = Array.isArray(fields) ? fields : [fields];
11 | super(["hpersist", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/pkg/commands/hpexpiretime.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | export class HPExpireTimeCommand extends Command {
5 | constructor(
6 | cmd: [key: string, fields: (string | number) | (string | number)[]],
7 | opts?: CommandOptions
8 | ) {
9 | const [key, fields] = cmd;
10 | const fieldArray = Array.isArray(fields) ? fields : [fields];
11 | super(["hpexpiretime", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/pkg/commands/hpttl.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | export class HPTtlCommand extends Command {
5 | constructor(
6 | cmd: [key: string, fields: (string | number) | (string | number)[]],
7 | opts?: CommandOptions
8 | ) {
9 | const [key, fields] = cmd;
10 | const fieldArray = Array.isArray(fields) ? fields : [fields];
11 | super(["hpttl", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/pkg/commands/hset.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 | import { HGetCommand } from "./hget";
5 | import { HSetCommand } from "./hset";
6 | const client = newHttpClient();
7 |
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 | test("sets value", async () => {
11 | const key = newKey();
12 | const field = randomID();
13 | const value = randomID();
14 |
15 | const res = await new HSetCommand([key, { [field]: value }]).exec(client);
16 |
17 | expect(res).toEqual(1);
18 | const res2 = await new HGetCommand([key, field]).exec(client);
19 |
20 | expect(res2).toEqual(value);
21 | });
22 |
--------------------------------------------------------------------------------
/pkg/commands/hset.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/hset
6 | */
7 | export class HSetCommand extends Command {
8 | constructor(
9 | [key, kv]: [key: string, kv: Record],
10 | opts?: CommandOptions
11 | ) {
12 | super(["hset", key, ...Object.entries(kv).flatMap(([field, value]) => [field, value])], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/hsetnx.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/hsetnx
6 | */
7 | export class HSetNXCommand extends Command<"0" | "1", 0 | 1> {
8 | constructor(
9 | cmd: [key: string, field: string, value: TData],
10 | opts?: CommandOptions<"0" | "1", 0 | 1>
11 | ) {
12 | super(["hsetnx", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/hstrlen.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/hstrlen
6 | */
7 | export class HStrLenCommand extends Command {
8 | constructor(cmd: [key: string, field: string], opts?: CommandOptions) {
9 | super(["hstrlen", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/httl.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 | import { afterAll, expect, test } from "bun:test";
3 | import { HSetCommand } from "./hset";
4 | import { HExpireCommand } from "./hexpire";
5 | import { HTtlCommand } from "./httl";
6 |
7 | const client = newHttpClient();
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 |
11 | test("retrieves the TTL of a hash key", async () => {
12 | const key = newKey();
13 | const hashKey = newKey();
14 | const value = randomID();
15 |
16 | await new HSetCommand([key, { [hashKey]: value }]).exec(client);
17 | await new HExpireCommand([key, hashKey, 5]).exec(client);
18 |
19 | const res = await new HTtlCommand([key, hashKey]).exec(client);
20 | expect(res[0]).toBeGreaterThan(0);
21 | });
22 |
--------------------------------------------------------------------------------
/pkg/commands/httl.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | export class HTtlCommand extends Command {
5 | constructor(
6 | cmd: [key: string, fields: (string | number) | (string | number)[]],
7 | opts?: CommandOptions
8 | ) {
9 | const [key, fields] = cmd;
10 | const fieldArray = Array.isArray(fields) ? fields : [fields];
11 | super(["httl", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/pkg/commands/hvals.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 |
5 | import { HSetCommand } from "./hset";
6 | import { HValsCommand } from "./hvals";
7 | const client = newHttpClient();
8 |
9 | const { newKey, cleanup } = keygen();
10 | afterAll(cleanup);
11 |
12 | test("returns correct length", async () => {
13 | const key = newKey();
14 | const field = randomID();
15 | const value = randomID();
16 |
17 | const res = await new HValsCommand([key]).exec(client);
18 | expect(res).toEqual([]);
19 | await new HSetCommand([key, { [field]: value }]).exec(client);
20 |
21 | const res2 = await new HValsCommand([key]).exec(client);
22 |
23 | expect(res2).toEqual([value]);
24 | });
25 |
--------------------------------------------------------------------------------
/pkg/commands/hvals.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/hvals
6 | */
7 | export class HValsCommand extends Command {
8 | constructor(cmd: [key: string], opts?: CommandOptions) {
9 | super(["hvals", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/incr.test.ts:
--------------------------------------------------------------------------------
1 | import { afterAll, expect, test } from "bun:test";
2 | import { keygen, newHttpClient } from "../test-utils";
3 | import { SetCommand } from "./set";
4 |
5 | import { IncrCommand } from "./incr";
6 | const client = newHttpClient();
7 |
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 |
11 | test("increments a non-existing value", async () => {
12 | const key = newKey();
13 | const res = await new IncrCommand([key]).exec(client);
14 |
15 | expect(res).toEqual(1);
16 | });
17 |
18 | test("increments and existing value", async () => {
19 | const key = newKey();
20 | await new SetCommand([key, 4]).exec(client);
21 | const res = await new IncrCommand([key]).exec(client);
22 |
23 | expect(res).toEqual(5);
24 | });
25 |
--------------------------------------------------------------------------------
/pkg/commands/incr.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/incr
6 | */
7 | export class IncrCommand extends Command {
8 | constructor(cmd: [key: string], opts?: CommandOptions) {
9 | super(["incr", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/incrby.test.ts:
--------------------------------------------------------------------------------
1 | import { afterAll, expect, test } from "bun:test";
2 | import { keygen, newHttpClient } from "../test-utils";
3 | import { IncrByCommand } from "./incrby";
4 |
5 | import { SetCommand } from "./set";
6 | const client = newHttpClient();
7 |
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 |
11 | test("increments a non-existing value", async () => {
12 | const key = newKey();
13 | const res = await new IncrByCommand([key, 2]).exec(client);
14 |
15 | expect(res).toEqual(2);
16 | });
17 |
18 | test("increments and existing value", async () => {
19 | const key = newKey();
20 | await new SetCommand([key, 5]).exec(client);
21 | const res = await new IncrByCommand([key, 2]).exec(client);
22 |
23 | expect(res).toEqual(7);
24 | });
25 |
--------------------------------------------------------------------------------
/pkg/commands/incrby.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/incrby
6 | */
7 | export class IncrByCommand extends Command {
8 | constructor(cmd: [key: string, value: number], opts?: CommandOptions) {
9 | super(["incrby", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/incrbyfloat.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/incrbyfloat
6 | */
7 | export class IncrByFloatCommand extends Command {
8 | constructor(cmd: [key: string, value: number], opts?: CommandOptions) {
9 | super(["incrbyfloat", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/json_arrappend.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.arrappend
6 | */
7 | export class JsonArrAppendCommand extends Command<
8 | (null | string)[],
9 | (null | number)[]
10 | > {
11 | constructor(
12 | cmd: [key: string, path: string, ...values: TData],
13 | opts?: CommandOptions<(null | string)[], (null | number)[]>
14 | ) {
15 | super(["JSON.ARRAPPEND", ...cmd], opts);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/commands/json_arrindex.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.arrindex
6 | */
7 | export class JsonArrIndexCommand extends Command<(null | string)[], (null | number)[]> {
8 | constructor(
9 | cmd: [key: string, path: string, value: TValue, start?: number, stop?: number],
10 | opts?: CommandOptions<(null | string)[], (null | number)[]>
11 | ) {
12 | super(["JSON.ARRINDEX", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/json_arrinsert.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.arrinsert
6 | */
7 | export class JsonArrInsertCommand extends Command<
8 | (null | string)[],
9 | (null | number)[]
10 | > {
11 | constructor(
12 | cmd: [key: string, path: string, index: number, ...values: TData],
13 | opts?: CommandOptions<(null | string)[], (null | number)[]>
14 | ) {
15 | super(["JSON.ARRINSERT", ...cmd], opts);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/commands/json_arrlen.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.arrlen
6 | */
7 | export class JsonArrLenCommand extends Command<(null | string)[], (null | number)[]> {
8 | constructor(
9 | cmd: [key: string, path?: string],
10 | opts?: CommandOptions<(null | string)[], (null | number)[]>
11 | ) {
12 | super(["JSON.ARRLEN", cmd[0], cmd[1] ?? "$"], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/json_arrpop.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.arrpop
6 | */
7 | export class JsonArrPopCommand extends Command<(null | string)[], (TData | null)[]> {
8 | constructor(
9 | cmd: [key: string, path?: string, index?: number],
10 | opts?: CommandOptions<(null | string)[], (TData | null)[]>
11 | ) {
12 | super(["JSON.ARRPOP", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/json_arrtrim.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.arrtrim
6 | */
7 | export class JsonArrTrimCommand extends Command<(null | string)[], (null | number)[]> {
8 | constructor(
9 | cmd: [key: string, path?: string, start?: number, stop?: number],
10 | opts?: CommandOptions<(null | string)[], (null | number)[]>
11 | ) {
12 | const path = cmd[1] ?? "$";
13 | const start = cmd[2] ?? 0;
14 | const stop = cmd[3] ?? 0;
15 |
16 | super(["JSON.ARRTRIM", cmd[0], path, start, stop], opts);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/pkg/commands/json_clear.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.clear
6 | */
7 | export class JsonClearCommand extends Command {
8 | constructor(cmd: [key: string, path?: string], opts?: CommandOptions) {
9 | super(["JSON.CLEAR", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/json_del.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.del
6 | */
7 | export class JsonDelCommand extends Command {
8 | constructor(cmd: [key: string, path?: string], opts?: CommandOptions) {
9 | super(["JSON.DEL", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/json_forget.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.forget
6 | */
7 | export class JsonForgetCommand extends Command {
8 | constructor(cmd: [key: string, path?: string], opts?: CommandOptions) {
9 | super(["JSON.FORGET", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/json_merge.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.merge
6 | */
7 | export class JsonMergeCommand<
8 | TData extends string | number | Record | Array,
9 | > extends Command<"OK" | null, "OK" | null> {
10 | constructor(
11 | cmd: [key: string, path: string, value: TData],
12 | opts?: CommandOptions<"OK" | null, "OK" | null>
13 | ) {
14 | const command: unknown[] = ["JSON.MERGE", ...cmd];
15 | super(command, opts);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/commands/json_mget.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.mget
6 | */
7 | export class JsonMGetCommand extends Command {
8 | constructor(cmd: [keys: string[], path: string], opts?: CommandOptions) {
9 | super(["JSON.MGET", ...cmd[0], cmd[1]], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/json_mset.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.mset
6 | */
7 | export class JsonMSetCommand<
8 | TData extends
9 | | number
10 | | string
11 | | boolean
12 | | Record
13 | | (number | string | boolean | Record)[],
14 | > extends Command<"OK" | null, "OK" | null> {
15 | constructor(
16 | cmd: { key: string; path: string; value: TData }[],
17 | opts?: CommandOptions<"OK" | null, "OK" | null>
18 | ) {
19 | const command: unknown[] = ["JSON.MSET"];
20 |
21 | for (const c of cmd) {
22 | command.push(c.key, c.path, c.value);
23 | }
24 | super(command, opts);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/pkg/commands/json_numincrby.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.numincrby
6 | */
7 | export class JsonNumIncrByCommand extends Command<(null | string)[], (null | number)[]> {
8 | constructor(
9 | cmd: [key: string, path: string, value: number],
10 | opts?: CommandOptions<(null | string)[], (null | number)[]>
11 | ) {
12 | super(["JSON.NUMINCRBY", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/json_nummultby.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.nummultby
6 | */
7 | export class JsonNumMultByCommand extends Command<(null | string)[], (null | number)[]> {
8 | constructor(
9 | cmd: [key: string, path: string, value: number],
10 | opts?: CommandOptions<(null | string)[], (null | number)[]>
11 | ) {
12 | super(["JSON.NUMMULTBY", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/json_objkeys.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.objkeys
6 | */
7 | export class JsonObjKeysCommand extends Command<(string[] | null)[], (string[] | null)[]> {
8 | constructor(
9 | cmd: [key: string, path?: string],
10 | opts?: CommandOptions<(string[] | null)[], (string[] | null)[]>
11 | ) {
12 | super(["JSON.OBJKEYS", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/json_objlen.test.ts:
--------------------------------------------------------------------------------
1 | import { afterAll, expect, test } from "bun:test";
2 | import { keygen, newHttpClient } from "../test-utils";
3 |
4 | import { JsonSetCommand } from "./json_set";
5 |
6 | import { JsonObjLenCommand } from "./json_objlen";
7 |
8 | const client = newHttpClient();
9 |
10 | const { newKey, cleanup } = keygen();
11 | afterAll(cleanup);
12 |
13 | test("return the length", async () => {
14 | const key = newKey();
15 | const res1 = await new JsonSetCommand([
16 | key,
17 | "$",
18 | {
19 | a: [3],
20 | nested: { a: { b: 2, c: 1 } },
21 | },
22 | ]).exec(client);
23 | expect(res1).toBe("OK");
24 | const res2 = await new JsonObjLenCommand([key, "$..a"]).exec(client);
25 | expect(res2).toEqual([2, null]);
26 | });
27 |
--------------------------------------------------------------------------------
/pkg/commands/json_objlen.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.objlen
6 | */
7 | export class JsonObjLenCommand extends Command<(number | null)[], (number | null)[]> {
8 | constructor(
9 | cmd: [key: string, path?: string],
10 | opts?: CommandOptions<(number | null)[], (number | null)[]>
11 | ) {
12 | super(["JSON.OBJLEN", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/json_resp.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.resp
6 | */
7 | export class JsonRespCommand extends Command {
8 | constructor(cmd: [key: string, path?: string], opts?: CommandOptions) {
9 | super(["JSON.RESP", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/json_strappend.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.strappend
6 | */
7 | export class JsonStrAppendCommand extends Command<(null | string)[], (null | number)[]> {
8 | constructor(
9 | cmd: [key: string, path: string, value: string],
10 | opts?: CommandOptions<(null | string)[], (null | number)[]>
11 | ) {
12 | super(["JSON.STRAPPEND", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/json_strlen.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.strlen
6 | */
7 | export class JsonStrLenCommand extends Command<(number | null)[], (number | null)[]> {
8 | constructor(
9 | cmd: [key: string, path?: string],
10 | opts?: CommandOptions<(number | null)[], (number | null)[]>
11 | ) {
12 | super(["JSON.STRLEN", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/json_toggle.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.toggle
6 | */
7 | export class JsonToggleCommand extends Command {
8 | constructor(cmd: [key: string, path: string], opts?: CommandOptions) {
9 | super(["JSON.TOGGLE", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/json_type.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/json.type
6 | */
7 | export class JsonTypeCommand extends Command {
8 | constructor(cmd: [key: string, path?: string], opts?: CommandOptions) {
9 | super(["JSON.TYPE", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/keys.test.ts:
--------------------------------------------------------------------------------
1 | import { afterAll, expect, test } from "bun:test";
2 | import { keygen, newHttpClient } from "../test-utils";
3 | import { KeysCommand } from "./keys";
4 | import { SetCommand } from "./set";
5 |
6 | const client = newHttpClient();
7 |
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 |
11 | test("when keys are found", () => {
12 | test("returns keys", async () => {
13 | const key = newKey();
14 | await new SetCommand([key, "value"]).exec(client);
15 | const res = await new KeysCommand([key]).exec(client);
16 | expect(res).toEqual([key]);
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/pkg/commands/keys.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/keys
6 | */
7 | export class KeysCommand extends Command {
8 | constructor(cmd: [pattern: string], opts?: CommandOptions) {
9 | super(["keys", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/lindex.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | export class LIndexCommand extends Command {
5 | constructor(
6 | cmd: [key: string, index: number],
7 | opts?: CommandOptions
8 | ) {
9 | super(["lindex", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/linsert.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | export class LInsertCommand extends Command {
4 | constructor(
5 | cmd: [key: string, direction: "before" | "after", pivot: TData, value: TData],
6 | opts?: CommandOptions
7 | ) {
8 | super(["linsert", ...cmd], opts);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/pkg/commands/llen.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/llen
6 | */
7 | export class LLenCommand extends Command {
8 | constructor(cmd: [key: string], opts?: CommandOptions) {
9 | super(["llen", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/lmove.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/lmove
6 | */
7 | export class LMoveCommand extends Command {
8 | constructor(
9 | cmd: [
10 | source: string,
11 | destination: string,
12 | whereFrom: "left" | "right",
13 | whereTo: "left" | "right",
14 | ],
15 | opts?: CommandOptions
16 | ) {
17 | super(["lmove", ...cmd], opts);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/pkg/commands/lmpop.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/lmpop
6 | */
7 | export class LmPopCommand extends Command<
8 | [string, TValues[]] | null,
9 | [string, TValues[]] | null
10 | > {
11 | constructor(
12 | cmd: [numkeys: number, keys: string[], "LEFT" | "RIGHT", count?: number],
13 | opts?: CommandOptions<[string, TValues[]] | null, [string, TValues[]] | null>
14 | ) {
15 | const [numkeys, keys, direction, count] = cmd;
16 |
17 | super(["LMPOP", numkeys, ...keys, direction, ...(count ? ["COUNT", count] : [])], opts);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/pkg/commands/lpop.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/lpop
6 | */
7 | export class LPopCommand extends Command {
8 | constructor(
9 | cmd: [key: string, count?: number],
10 | opts?: CommandOptions
11 | ) {
12 | super(["lpop", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/lpush.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 | import { LPushCommand } from "./lpush";
5 |
6 | const client = newHttpClient();
7 |
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 |
11 | test("returns the length after command", async () => {
12 | const key = newKey();
13 | const res = await new LPushCommand([key, randomID()]).exec(client);
14 | expect(res).toEqual(1);
15 | const res2 = await new LPushCommand([key, randomID(), randomID()]).exec(client);
16 |
17 | expect(res2).toEqual(3);
18 | });
19 |
--------------------------------------------------------------------------------
/pkg/commands/lpush.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/lpush
6 | */
7 | export class LPushCommand extends Command {
8 | constructor(cmd: [key: string, ...elements: TData[]], opts?: CommandOptions) {
9 | super(["lpush", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/lpushx.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/lpushx
6 | */
7 | export class LPushXCommand extends Command {
8 | constructor(cmd: [key: string, ...elements: TData[]], opts?: CommandOptions) {
9 | super(["lpushx", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/lrange.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 | import { LRangeCommand } from "./lrange";
5 | import { RPushCommand } from "./rpush";
6 | const client = newHttpClient();
7 |
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 |
11 | test("returns the correct range", async () => {
12 | const key = newKey();
13 | const value1 = randomID();
14 | const value2 = randomID();
15 | const value3 = randomID();
16 | await new RPushCommand([key, value1, value2, value3]).exec(client);
17 | const res = await new LRangeCommand([key, 1, 2]).exec(client);
18 | expect(res.length).toBe(2);
19 | expect(res[0]).toBe(value2);
20 | expect(res[1]).toBe(value3);
21 | });
22 |
--------------------------------------------------------------------------------
/pkg/commands/lrange.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | export class LRangeCommand extends Command {
5 | constructor(
6 | cmd: [key: string, start: number, end: number],
7 | opts?: CommandOptions
8 | ) {
9 | super(["lrange", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/lrem.test.ts:
--------------------------------------------------------------------------------
1 | import { afterAll, expect, test } from "bun:test";
2 | import { keygen, newHttpClient } from "../test-utils";
3 |
4 | import { LPushCommand } from "./lpush";
5 | import { LRemCommand } from "./lrem";
6 | const client = newHttpClient();
7 |
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 |
11 | test("returns the number of deleted elements", async () => {
12 | const key = newKey();
13 | await new LPushCommand([key, "element"]).exec(client);
14 | await new LPushCommand([key, "element"]).exec(client);
15 | await new LPushCommand([key, "something else"]).exec(client);
16 |
17 | const res = await new LRemCommand([key, 2, "element"]).exec(client);
18 | expect(res).toEqual(2);
19 | });
20 |
--------------------------------------------------------------------------------
/pkg/commands/lrem.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | export class LRemCommand extends Command {
4 | constructor(
5 | cmd: [key: string, count: number, value: TData],
6 | opts?: CommandOptions
7 | ) {
8 | super(["lrem", ...cmd], opts);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/pkg/commands/lset.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | export class LSetCommand extends Command<"OK", "OK"> {
5 | constructor(cmd: [key: string, index: number, data: TData], opts?: CommandOptions<"OK", "OK">) {
6 | super(["lset", ...cmd], opts);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/pkg/commands/ltrim.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | export class LTrimCommand extends Command<"OK", "OK"> {
5 | constructor(cmd: [key: string, start: number, end: number], opts?: CommandOptions<"OK", "OK">) {
6 | super(["ltrim", ...cmd], opts);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/pkg/commands/mget.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | /**
4 | * @see https://redis.io/commands/mget
5 | */
6 | export class MGetCommand extends Command<(string | null)[], TData> {
7 | constructor(cmd: [string[]] | [...string[]], opts?: CommandOptions<(string | null)[], TData>) {
8 | const keys = Array.isArray(cmd[0]) ? cmd[0] : (cmd as string[]);
9 | super(["mget", ...keys], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/mset.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 | import { MGetCommand } from "./mget";
5 | import { MSetCommand } from "./mset";
6 |
7 | const client = newHttpClient();
8 |
9 | const { newKey, cleanup } = keygen();
10 | afterAll(cleanup);
11 |
12 | test("gets exiting values", async () => {
13 | const key1 = newKey();
14 | const key2 = newKey();
15 | const kv = {
16 | [key1]: randomID(),
17 | [key2]: randomID(),
18 | };
19 | const res = await new MSetCommand([kv]).exec(client);
20 |
21 | expect(res).toEqual("OK");
22 | const res2 = await new MGetCommand([key1, key2]).exec(client);
23 | expect(res2).toEqual(Object.values(kv));
24 | });
25 |
--------------------------------------------------------------------------------
/pkg/commands/mset.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/mset
6 | */
7 | export class MSetCommand extends Command<"OK", "OK"> {
8 | constructor([kv]: [kv: Record], opts?: CommandOptions<"OK", "OK">) {
9 | super(["mset", ...Object.entries(kv).flatMap(([key, value]) => [key, value])], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/msetnx.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/msetnx
6 | */
7 | export class MSetNXCommand extends Command {
8 | constructor([kv]: [kv: Record], opts?: CommandOptions) {
9 | super(["msetnx", ...Object.entries(kv).flat()], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/persist.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/persist
6 | */
7 | export class PersistCommand extends Command<"0" | "1", 0 | 1> {
8 | constructor(cmd: [key: string], opts?: CommandOptions<"0" | "1", 0 | 1>) {
9 | super(["persist", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/pexpire.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | import type { ExpireOption } from "./expire";
4 |
5 | /**
6 | * @see https://redis.io/commands/pexpire
7 | */
8 | export class PExpireCommand extends Command<"0" | "1", 0 | 1> {
9 | constructor(
10 | cmd: [key: string, milliseconds: number, option?: ExpireOption],
11 | opts?: CommandOptions<"0" | "1", 0 | 1>
12 | ) {
13 | super(["pexpire", ...cmd], opts);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/pkg/commands/pexpireat.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | import type { ExpireOption } from "./expire";
4 |
5 | /**
6 | * @see https://redis.io/commands/pexpireat
7 | */
8 | export class PExpireAtCommand extends Command<"0" | "1", 0 | 1> {
9 | constructor(
10 | cmd: [key: string, unix: number, option?: ExpireOption],
11 | opts?: CommandOptions<"0" | "1", 0 | 1>
12 | ) {
13 | super(["pexpireat", ...cmd], opts);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/pkg/commands/pfadd.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command.ts";
2 | import { Command } from "./command.ts";
3 |
4 | /**
5 | * @see https://redis.io/commands/pfadd
6 | */
7 | export class PfAddCommand extends Command {
8 | constructor(cmd: [string, ...TData[]], opts?: CommandOptions) {
9 | super(["pfadd", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/pfcount.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command.ts";
2 | import { Command } from "./command.ts";
3 |
4 | /**
5 | * @see https://redis.io/commands/pfcount
6 | */
7 | export class PfCountCommand extends Command {
8 | constructor(cmd: [string, ...string[]], opts?: CommandOptions) {
9 | super(["pfcount", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/pfmerge.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command.ts";
2 | import { Command } from "./command.ts";
3 |
4 | /**
5 | * @see https://redis.io/commands/pfmerge
6 | */
7 | export class PfMergeCommand extends Command<"OK", "OK"> {
8 | constructor(cmd: [destination_key: string, ...string[]], opts?: CommandOptions<"OK", "OK">) {
9 | super(["pfmerge", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/ping.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, test } from "bun:test";
2 | import { newHttpClient, randomID } from "../test-utils";
3 | import { PingCommand } from "./ping";
4 |
5 | const client = newHttpClient();
6 |
7 | describe("with message", () => {
8 | test("returns the message", async () => {
9 | const message = randomID();
10 | const res = await new PingCommand([message]).exec(client);
11 | expect(res).toEqual(message);
12 | });
13 | });
14 | describe("without message", () => {
15 | test("returns pong", async () => {
16 | const res = await new PingCommand([]).exec(client);
17 | expect(res).toEqual("PONG");
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/pkg/commands/ping.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | /**
4 | * @see https://redis.io/commands/ping
5 | */
6 | export class PingCommand extends Command {
7 | constructor(cmd?: [message?: string], opts?: CommandOptions) {
8 | const command: string[] = ["ping"];
9 | if (cmd?.[0] !== undefined) {
10 | command.push(cmd[0]);
11 | }
12 | super(command, opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/psetex.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 |
5 | import { GetCommand } from "./get";
6 | import { PSetEXCommand } from "./psetex";
7 | const client = newHttpClient();
8 |
9 | const { newKey, cleanup } = keygen();
10 | afterAll(cleanup);
11 |
12 | test("sets value", async () => {
13 | const key = newKey();
14 | const value = randomID();
15 |
16 | const res = await new PSetEXCommand([key, 1000, value]).exec(client);
17 |
18 | expect(res).toEqual("OK");
19 | await new Promise((res) => setTimeout(res, 2000));
20 | const res2 = await new GetCommand([key]).exec(client);
21 |
22 | expect(res2).toEqual(null);
23 | });
24 |
--------------------------------------------------------------------------------
/pkg/commands/psetex.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/psetex
6 | */
7 | export class PSetEXCommand extends Command {
8 | constructor(
9 | cmd: [key: string, ttl: number, value: TData],
10 | opts?: CommandOptions
11 | ) {
12 | super(["psetex", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/psubscribe.ts:
--------------------------------------------------------------------------------
1 | import { Command, type CommandOptions } from "./command";
2 |
3 | /**
4 | * @see https://redis.io/commands/psubscribe
5 | */
6 | export class PSubscribeCommand extends Command {
7 | constructor(cmd: [...patterns: string[]], opts?: CommandOptions) {
8 | const sseHeaders = {
9 | Accept: "text/event-stream",
10 | "Cache-Control": "no-cache",
11 | Connection: "keep-alive",
12 | };
13 |
14 | super([], {
15 | ...opts,
16 | headers: sseHeaders,
17 | path: ["psubscribe", ...cmd],
18 | streamOptions: {
19 | isStreaming: true,
20 | onMessage: opts?.streamOptions?.onMessage,
21 | signal: opts?.streamOptions?.signal,
22 | },
23 | });
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/pkg/commands/pttl.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient } from "../test-utils";
2 | import { PTtlCommand } from "./pttl";
3 |
4 | import { afterAll, expect, test } from "bun:test";
5 | import { SetExCommand } from "./setex";
6 |
7 | const client = newHttpClient();
8 |
9 | const { newKey, cleanup } = keygen();
10 | afterAll(cleanup);
11 |
12 | test("returns the ttl on a key", async () => {
13 | const key = newKey();
14 | const ttl = 60;
15 | await new SetExCommand([key, ttl, "value"]).exec(client);
16 | const res = await new PTtlCommand([key]).exec(client);
17 | expect(res <= ttl * 1000).toBe(true);
18 | });
19 |
--------------------------------------------------------------------------------
/pkg/commands/pttl.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/pttl
6 | */
7 | export class PTtlCommand extends Command {
8 | constructor(cmd: [key: string], opts?: CommandOptions) {
9 | super(["pttl", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/publish.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from "bun:test";
2 | import { newHttpClient } from "../test-utils";
3 |
4 | import { PublishCommand } from "./publish";
5 |
6 | const client = newHttpClient();
7 |
8 | test("returns the number of clients that received the message", async () => {
9 | const res = await new PublishCommand(["channel", "hello"]).exec(client);
10 |
11 | expect(typeof res).toBe("number");
12 | });
13 |
--------------------------------------------------------------------------------
/pkg/commands/publish.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/publish
6 | */
7 | export class PublishCommand extends Command {
8 | constructor(cmd: [channel: string, message: TMessage], opts?: CommandOptions) {
9 | super(["publish", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/randomkey.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 | import { RandomKeyCommand } from "./randomkey";
5 | import { SetCommand } from "./set";
6 | const client = newHttpClient();
7 |
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 |
11 | test("returns a random key", async () => {
12 | const key = newKey();
13 | await new SetCommand([key, randomID()]).exec(client);
14 | const res = await new RandomKeyCommand().exec(client);
15 | expect(typeof res).toBe("string");
16 | });
17 |
--------------------------------------------------------------------------------
/pkg/commands/randomkey.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/randomkey
6 | */
7 | export class RandomKeyCommand extends Command {
8 | constructor(opts?: CommandOptions) {
9 | super(["randomkey"], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/rename.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 |
5 | import { RenameCommand } from "./rename";
6 | import { SetCommand } from "./set";
7 | const client = newHttpClient();
8 |
9 | const { newKey, cleanup } = keygen();
10 | afterAll(cleanup);
11 |
12 | test("renames the key", async () => {
13 | const source = newKey();
14 | const destination = newKey();
15 | const value = randomID();
16 | await new SetCommand([source, value]).exec(client);
17 | const res = await new RenameCommand([source, destination]).exec(client);
18 | expect(res).toEqual("OK");
19 | });
20 |
--------------------------------------------------------------------------------
/pkg/commands/rename.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/rename
6 | */
7 | export class RenameCommand extends Command<"OK", "OK"> {
8 | constructor(cmd: [source: string, destination: string], opts?: CommandOptions<"OK", "OK">) {
9 | super(["rename", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/renamenx.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/renamenx
6 | */
7 | export class RenameNXCommand extends Command<"0" | "1", 0 | 1> {
8 | constructor(cmd: [source: string, destination: string], opts?: CommandOptions<"0" | "1", 0 | 1>) {
9 | super(["renamenx", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/rpop.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/rpop
6 | */
7 | export class RPopCommand extends Command<
8 | unknown | null,
9 | TData | null
10 | > {
11 | constructor(
12 | cmd: [key: string, count?: number],
13 | opts?: CommandOptions
14 | ) {
15 | super(["rpop", ...cmd], opts);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/commands/rpush.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 | import { RPushCommand } from "./rpush";
5 | const client = newHttpClient();
6 |
7 | const { newKey, cleanup } = keygen();
8 | afterAll(cleanup);
9 |
10 | test("returns the length after command", async () => {
11 | const key = newKey();
12 | const res = await new RPushCommand([key, randomID()]).exec(client);
13 | expect(res).toEqual(1);
14 | const res2 = await new RPushCommand([key, randomID(), randomID()]).exec(client);
15 |
16 | expect(res2).toEqual(3);
17 | });
18 |
--------------------------------------------------------------------------------
/pkg/commands/rpush.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/rpush
6 | */
7 | export class RPushCommand extends Command {
8 | constructor(cmd: [key: string, ...elements: TData[]], opts?: CommandOptions) {
9 | super(["rpush", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/rpushx.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/rpushx
6 | */
7 | export class RPushXCommand extends Command {
8 | constructor(cmd: [key: string, ...elements: TData[]], opts?: CommandOptions) {
9 | super(["rpushx", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/sadd.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/sadd
6 | */
7 | export class SAddCommand extends Command {
8 | constructor(
9 | cmd: [key: string, member: TData, ...members: TData[]],
10 | opts?: CommandOptions
11 | ) {
12 | super(["sadd", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/scard.test.ts:
--------------------------------------------------------------------------------
1 | import { afterAll, expect, test } from "bun:test";
2 | import { keygen, newHttpClient } from "../test-utils";
3 | import { SAddCommand } from "./sadd";
4 |
5 | import { SCardCommand } from "./scard";
6 | const client = newHttpClient();
7 |
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 |
11 | test("returns the cardinality", async () => {
12 | const key = newKey();
13 | await new SAddCommand([key, "member1"]).exec(client);
14 | const res = await new SCardCommand([key]).exec(client);
15 | expect(res).toEqual(1);
16 | });
17 |
--------------------------------------------------------------------------------
/pkg/commands/scard.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | /**
4 | * @see https://redis.io/commands/scard
5 | */
6 | export class SCardCommand extends Command {
7 | constructor(cmd: [key: string], opts?: CommandOptions) {
8 | super(["scard", ...cmd], opts);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/pkg/commands/script_exists.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/script-exists
6 | */
7 | export class ScriptExistsCommand extends Command {
8 | constructor(hashes: T, opts?: CommandOptions) {
9 | super(["script", "exists", ...hashes], {
10 | deserialize: (result) => result as unknown as number[],
11 | ...opts,
12 | });
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/script_flush.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | export type ScriptFlushCommandOptions =
5 | | { sync: true; async?: never }
6 | | { sync?: never; async: true };
7 |
8 | /**
9 | * @see https://redis.io/commands/script-flush
10 | */
11 | export class ScriptFlushCommand extends Command<"OK", "OK"> {
12 | constructor([opts]: [opts?: ScriptFlushCommandOptions], cmdOpts?: CommandOptions<"OK", "OK">) {
13 | const cmd = ["script", "flush"];
14 | if (opts?.sync) {
15 | cmd.push("sync");
16 | } else if (opts?.async) {
17 | cmd.push("async");
18 | }
19 | super(cmd, cmdOpts);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/pkg/commands/script_load.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from "bun:test";
2 | import { newHttpClient } from "../test-utils";
3 | import { ScriptLoadCommand } from "./script_load";
4 |
5 | const client = newHttpClient();
6 |
7 | test("returns the hash", async () => {
8 | const script = "return ARGV[1]";
9 | const res = await new ScriptLoadCommand([script]).exec(client);
10 | expect(res).toEqual("098e0f0d1448c0a81dafe820f66d460eb09263da");
11 | });
12 |
--------------------------------------------------------------------------------
/pkg/commands/script_load.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/script-load
6 | */
7 | export class ScriptLoadCommand extends Command {
8 | constructor(args: [script: string], opts?: CommandOptions) {
9 | super(["script", "load", ...args], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/sdiff.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 | import { SAddCommand } from "./sadd";
5 | import { SDiffCommand } from "./sdiff";
6 | const client = newHttpClient();
7 |
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 |
11 | test("returns the diff", async () => {
12 | const key1 = newKey();
13 | const member1 = randomID();
14 | const key2 = newKey();
15 | const member2 = randomID();
16 | await new SAddCommand([key1, member1]).exec(client);
17 | await new SAddCommand([key2, member2]).exec(client);
18 | const res = await new SDiffCommand([key1, key2]).exec(client);
19 | expect(res).toEqual([member1]);
20 | });
21 |
--------------------------------------------------------------------------------
/pkg/commands/sdiff.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | /**
4 | * @see https://redis.io/commands/sdiff
5 | */
6 | export class SDiffCommand extends Command {
7 | constructor(cmd: [key: string, ...keys: string[]], opts?: CommandOptions) {
8 | super(["sdiff", ...cmd], opts);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/pkg/commands/sdiffstore.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | /**
4 | * @see https://redis.io/commands/sdiffstore
5 | */
6 | export class SDiffStoreCommand extends Command {
7 | constructor(
8 | cmd: [destination: string, ...keys: string[]],
9 | opts?: CommandOptions
10 | ) {
11 | super(["sdiffstore", ...cmd], opts);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/pkg/commands/setbit.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 | import { SetBitCommand } from "./setbit";
5 | const client = newHttpClient();
6 |
7 | const { newKey, cleanup } = keygen();
8 | afterAll(cleanup);
9 |
10 | test("returns the original bit", async () => {
11 | const key = newKey();
12 | const res = await new SetBitCommand([key, 0, 1]).exec(client);
13 | expect(res).toEqual(0);
14 | const res2 = await new SetBitCommand([key, 0, 1]).exec(client);
15 |
16 | expect(res2).toEqual(1);
17 | });
18 |
--------------------------------------------------------------------------------
/pkg/commands/setbit.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | /**
4 | * @see https://redis.io/commands/setbit
5 | */
6 |
7 | export class SetBitCommand extends Command<"0" | "1", 0 | 1> {
8 | constructor(
9 | cmd: [key: string, offset: number, value: 0 | 1],
10 | opts?: CommandOptions<"0" | "1", 0 | 1>
11 | ) {
12 | super(["setbit", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/setex.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 | import { GetCommand } from "./get";
5 | import { SetExCommand } from "./setex";
6 |
7 | const client = newHttpClient();
8 |
9 | const { newKey, cleanup } = keygen();
10 | afterAll(cleanup);
11 | test("sets value", async () => {
12 | const key = newKey();
13 | const value = randomID();
14 |
15 | const res = await new SetExCommand([key, 1, value]).exec(client);
16 |
17 | expect(res).toEqual("OK");
18 | await new Promise((res) => setTimeout(res, 2000));
19 | const res2 = await new GetCommand([key]).exec(client);
20 |
21 | expect(res2).toEqual(null);
22 | });
23 |
--------------------------------------------------------------------------------
/pkg/commands/setex.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/setex
6 | */
7 | export class SetExCommand extends Command<"OK", "OK"> {
8 | constructor(cmd: [key: string, ttl: number, value: TData], opts?: CommandOptions<"OK", "OK">) {
9 | super(["setex", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/setnx.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/setnx
6 | */
7 | export class SetNxCommand extends Command {
8 | constructor(cmd: [key: string, value: TData], opts?: CommandOptions) {
9 | super(["setnx", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/setrange.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/setrange
6 | */
7 | export class SetRangeCommand extends Command {
8 | constructor(
9 | cmd: [key: string, offset: number, value: string],
10 | opts?: CommandOptions
11 | ) {
12 | super(["setrange", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/sinter.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | /**
4 | * @see https://redis.io/commands/sinter
5 | */
6 | export class SInterCommand extends Command {
7 | constructor(cmd: [key: string, ...keys: string[]], opts?: CommandOptions) {
8 | super(["sinter", ...cmd], opts);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/pkg/commands/sinterstore.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | /**
4 | * @see https://redis.io/commands/sinterstore
5 | */
6 | export class SInterStoreCommand extends Command {
7 | constructor(
8 | cmd: [destination: string, key: string, ...keys: string[]],
9 | opts?: CommandOptions
10 | ) {
11 | super(["sinterstore", ...cmd], opts);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/pkg/commands/sismember.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | /**
4 | * @see https://redis.io/commands/sismember
5 | */
6 | export class SIsMemberCommand extends Command<"0" | "1", 0 | 1> {
7 | constructor(cmd: [key: string, member: TData], opts?: CommandOptions<"0" | "1", 0 | 1>) {
8 | super(["sismember", ...cmd], opts);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/pkg/commands/smembers.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/smembers
6 | */
7 | export class SMembersCommand extends Command {
8 | constructor(cmd: [key: string], opts?: CommandOptions) {
9 | super(["smembers", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/smismember.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | /**
4 | * @see https://redis.io/commands/smismember
5 | */
6 | export class SMIsMemberCommand extends Command<
7 | ("0" | "1")[],
8 | (0 | 1)[]
9 | > {
10 | constructor(
11 | cmd: [key: string, members: TMembers],
12 | opts?: CommandOptions<("0" | "1")[], (0 | 1)[]>
13 | ) {
14 | super(["smismember", cmd[0], ...cmd[1]], opts);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/pkg/commands/smove.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 | import { SAddCommand } from "./sadd";
5 | import { SMoveCommand } from "./smove";
6 | const client = newHttpClient();
7 |
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 |
11 | test("moves the member", async () => {
12 | const source = newKey();
13 | const destination = newKey();
14 | const member = randomID();
15 | await new SAddCommand([source, member]).exec(client);
16 | const res = await new SMoveCommand([source, destination, member]).exec(client);
17 | expect(res).toEqual(1);
18 | });
19 |
--------------------------------------------------------------------------------
/pkg/commands/smove.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | /**
4 | * @see https://redis.io/commands/smove
5 | */
6 | export class SMoveCommand extends Command<"0" | "1", 0 | 1> {
7 | constructor(
8 | cmd: [source: string, destination: string, member: TData],
9 | opts?: CommandOptions<"0" | "1", 0 | 1>
10 | ) {
11 | super(["smove", ...cmd], opts);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/pkg/commands/spop.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | /**
4 | * @see https://redis.io/commands/spop
5 | */
6 | export class SPopCommand extends Command {
7 | constructor(
8 | [key, count]: [key: string, count?: number],
9 | opts?: CommandOptions
10 | ) {
11 | const command: unknown[] = ["spop", key];
12 | if (typeof count === "number") {
13 | command.push(count);
14 | }
15 | super(command, opts);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/commands/srandmember.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | /**
4 | * @see https://redis.io/commands/srandmember
5 | */
6 | export class SRandMemberCommand extends Command {
7 | constructor(
8 | [key, count]: [key: string, count?: number],
9 | opts?: CommandOptions
10 | ) {
11 | const command: unknown[] = ["srandmember", key];
12 | if (typeof count === "number") {
13 | command.push(count);
14 | }
15 | super(command, opts);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/commands/srem.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 | import { SAddCommand } from "./sadd";
5 | import { SRemCommand } from "./srem";
6 |
7 | const client = newHttpClient();
8 |
9 | const { newKey, cleanup } = keygen();
10 | afterAll(cleanup);
11 |
12 | test("returns the number of removed members", async () => {
13 | const key = newKey();
14 | const value1 = randomID();
15 | const value2 = randomID();
16 | await new SAddCommand([key, value1, value2]).exec(client);
17 | const res = await new SRemCommand([key, value1]).exec(client);
18 | expect(res).toEqual(1);
19 | });
20 |
--------------------------------------------------------------------------------
/pkg/commands/srem.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | /**
4 | * @see https://redis.io/commands/srem
5 | */
6 | export class SRemCommand extends Command {
7 | constructor(cmd: [key: string, ...members: TData[]], opts?: CommandOptions) {
8 | super(["srem", ...cmd], opts);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/pkg/commands/strlen.test.ts:
--------------------------------------------------------------------------------
1 | import { afterAll, expect, test } from "bun:test";
2 | import { keygen, newHttpClient } from "../test-utils";
3 | import { SetCommand } from "./set";
4 |
5 | import { StrLenCommand } from "./strlen";
6 | const client = newHttpClient();
7 |
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 |
11 | test("returns the correct length", async () => {
12 | const key = newKey();
13 | const value = "abcd";
14 | await new SetCommand([key, value]).exec(client);
15 | const res = await new StrLenCommand([key]).exec(client);
16 | expect(res).toEqual(value.length);
17 | });
18 |
--------------------------------------------------------------------------------
/pkg/commands/strlen.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/strlen
6 | */
7 | export class StrLenCommand extends Command {
8 | constructor(cmd: [key: string], opts?: CommandOptions) {
9 | super(["strlen", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/sunion.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 | import { SAddCommand } from "./sadd";
5 | import { SUnionCommand } from "./sunion";
6 | const client = newHttpClient();
7 |
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 |
11 | test("returns the union", async () => {
12 | const key1 = newKey();
13 | const key2 = newKey();
14 |
15 | const member1 = randomID();
16 | const member2 = randomID();
17 |
18 | await new SAddCommand([key1, member1]).exec(client);
19 | await new SAddCommand([key2, member2]).exec(client);
20 | const res = await new SUnionCommand([key1, key2]).exec(client);
21 | expect(res.sort()).toEqual([member1, member2].sort());
22 | });
23 |
--------------------------------------------------------------------------------
/pkg/commands/sunion.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/sunion
6 | */
7 | export class SUnionCommand extends Command {
8 | constructor(cmd: [key: string, ...keys: string[]], opts?: CommandOptions) {
9 | super(["sunion", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/sunionstore.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/sunionstore
6 | */
7 | export class SUnionStoreCommand extends Command {
8 | constructor(
9 | cmd: [destination: string, key: string, ...keys: string[]],
10 | opts?: CommandOptions
11 | ) {
12 | super(["sunionstore", ...cmd], opts);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/commands/time.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from "bun:test";
2 | import { newHttpClient } from "../test-utils";
3 |
4 | import { TimeCommand } from "./time";
5 | const client = newHttpClient();
6 |
7 | test("returns the time", async () => {
8 | const res = await new TimeCommand().exec(client);
9 |
10 | expect(typeof res[0]).toBe("number");
11 | expect(typeof res[1]).toBe("number");
12 | });
13 |
--------------------------------------------------------------------------------
/pkg/commands/time.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 | /**
4 | * @see https://redis.io/commands/time
5 | */
6 | export class TimeCommand extends Command<[number, number], [number, number]> {
7 | constructor(opts?: CommandOptions<[number, number], [number, number]>) {
8 | super(["time"], opts);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/pkg/commands/touch.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 | import { MSetCommand } from "./mset";
5 | import { TouchCommand } from "./touch";
6 |
7 | const client = newHttpClient();
8 |
9 | const { newKey, cleanup } = keygen();
10 | afterAll(cleanup);
11 |
12 | test("returns the number of touched keys", async () => {
13 | const key1 = newKey();
14 | const key2 = newKey();
15 | const kv: Record = {};
16 | kv[key1] = randomID();
17 | kv[key2] = randomID();
18 | await new MSetCommand([kv]).exec(client);
19 | const res = await new TouchCommand([key1, key2]).exec(client);
20 | expect(res).toEqual(2);
21 | });
22 |
--------------------------------------------------------------------------------
/pkg/commands/touch.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/touch
6 | */
7 | export class TouchCommand extends Command {
8 | constructor(cmd: [...keys: string[]], opts?: CommandOptions) {
9 | super(["touch", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/ttl.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 | import { SetExCommand } from "./setex";
5 | import { TtlCommand } from "./ttl";
6 | const client = newHttpClient();
7 |
8 | const { newKey, cleanup } = keygen();
9 | afterAll(cleanup);
10 |
11 | test("returns the ttl on a key", async () => {
12 | const key = newKey();
13 | const ttl = 60;
14 | await new SetExCommand([key, ttl, "value"]).exec(client);
15 | const res = await new TtlCommand([key]).exec(client);
16 | expect(res).toEqual(ttl);
17 | });
18 |
--------------------------------------------------------------------------------
/pkg/commands/ttl.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/ttl
6 | */
7 | export class TtlCommand extends Command {
8 | constructor(cmd: [key: string], opts?: CommandOptions) {
9 | super(["ttl", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/type.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | export type Type = "string" | "list" | "set" | "zset" | "hash" | "none";
5 | /**
6 | * @see https://redis.io/commands/type
7 | */
8 | export class TypeCommand extends Command {
9 | constructor(cmd: [key: string], opts?: CommandOptions) {
10 | super(["type", ...cmd], opts);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/pkg/commands/unlink.test.ts:
--------------------------------------------------------------------------------
1 | import { keygen, newHttpClient, randomID } from "../test-utils";
2 |
3 | import { afterAll, expect, test } from "bun:test";
4 | import { MSetCommand } from "./mset";
5 | import { UnlinkCommand } from "./unlink";
6 |
7 | const client = newHttpClient();
8 |
9 | const { newKey, cleanup } = keygen();
10 | afterAll(cleanup);
11 |
12 | test("unlinks the keys", async () => {
13 | const key1 = newKey();
14 | const key2 = newKey();
15 | const key3 = newKey();
16 | const kv: Record = {};
17 | kv[key1] = randomID();
18 | kv[key2] = randomID();
19 | await new MSetCommand([kv]).exec(client);
20 | const res = await new UnlinkCommand([key1, key2, key3]).exec(client);
21 | expect(res).toEqual(2);
22 | });
23 |
--------------------------------------------------------------------------------
/pkg/commands/unlink.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/unlink
6 | */
7 | export class UnlinkCommand extends Command {
8 | constructor(cmd: [...keys: string[]], opts?: CommandOptions) {
9 | super(["unlink", ...cmd], opts);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/commands/xack.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/xack
6 | */
7 | export class XAckCommand extends Command {
8 | constructor(
9 | [key, group, id]: [key: string, group: string, id: string | string[]],
10 | opts?: CommandOptions
11 | ) {
12 | const ids = Array.isArray(id) ? [...id] : [id];
13 | super(["XACK", key, group, ...ids], opts);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/pkg/commands/xdel.ts:
--------------------------------------------------------------------------------
1 | import type { CommandOptions } from "./command";
2 | import { Command } from "./command";
3 |
4 | /**
5 | * @see https://redis.io/commands/xdel
6 | */
7 | export class XDelCommand extends Command