Skip to content

Commit

Permalink
fix: parse numbers larger than MAX_SAFE_INTEGER as string (#39)
Browse files Browse the repository at this point in the history
* fix: parse numbers larger than MAX_SAFE_INTEGER as string

* feat: custom deserializer and docs
  • Loading branch information
chronark authored Mar 12, 2022
1 parent 5fd9088 commit 456e60b
Show file tree
Hide file tree
Showing 113 changed files with 175 additions and 118 deletions.
36 changes: 31 additions & 5 deletions .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,26 @@ For more information about pipelines using REST see [here](https://blog.upstash.

### Advanced

Low level `Command` classes can be imported from `@upstash/redis/commands`.
In case you need more control about types and or (de)serialization, please use a `Command`-class directly.
A low level `Command` class can be imported from `@upstash/redis/commands` inn case you need more control about types and or (de)serialization.

By default all objects you are storing in redis are serialized using `JSON.stringify` and recursively deserialized as well. Here's an example how you could customize that behaviour:

```ts
import { Command } from "@upstash/redis/commands"
import { HttpClient} from "@upstash/redis/http"
import { GetCommand} from "@upstash/redis/commands"

/**
* TData represents what the user will enter or receive,
* TResult is the raw data returned from upstash, which may need to be
* transformed or parsed.
*/
const deserialize: (raw: TResult) => TData = ...

class CustomGetCommand<TData, TResult> extends Command<TData | null, TResult | null> {
constructor(key: string, ) {
super(["get", key], { deserialize })
}
}

const client = new HttpClient({
baseUrl: <UPSTASH_REDIS_REST_URL>,
Expand All @@ -220,11 +234,23 @@ const client = new HttpClient({
},
})

const get = new GetCommand<OptionalCustomType>("key")
const res = new CustomGetCommand("key").exec(client)

const data = await get.exec(client)
```

#### Javascript MAX_SAFE_INTEGER

Unfortunately javascript can not handle numbers larger than `2^53 -1` safely and would return wrong results.
In these cases the default deserializer will return them as string instead. This might cause a mismatch with your custom types.

```ts
await redis.set("key", "101600000000150081467")
const res = await redis<number>("get")
```

In this example `res` will still be a string despite the type annotation.
Please keep that in mind and adjust accordingly.

## Docs

See [the documentation](https://docs.upstash.com/features/javascriptsdk) for details.
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/append.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/append
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/bitcount.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/bitcount
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/bitop.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/bitop
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/bitpos.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/bitpos
Expand Down
21 changes: 21 additions & 0 deletions pkg/commands/command.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Command } from "./command"
import { keygen, newHttpClient } from "../test-utils"
import { randomUUID } from "crypto"
import { describe, it, expect, afterAll } from "@jest/globals"
const client = newHttpClient()

const { newKey, cleanup } = keygen()
afterAll(cleanup)

describe("deserialize large numbers", () => {
it("returns the correct number", async () => {
const key = newKey()
const field = randomUUID()
const value = "101600000000150081467"

await new Command(["hset", key, field, value]).exec(client)

const res = await new Command(["hget", key, field]).exec(client)
expect(res).toEqual(value)
})
})
8 changes: 4 additions & 4 deletions pkg/command.ts → pkg/commands/command.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { UpstashError } from "./error"
import { HttpClient, UpstashResponse } from "./http"
import { parseResponse } from "./util"
import { UpstashError } from "../error"
import { HttpClient, UpstashResponse } from "../http"
import { parseResponse } from "../util"

/**
* Command offers default (de)serialization and the exec method to all commands.
*
* TData represents what the user will enter or receive,
* TResult is the raw data returned from upstash, which may need to be transformed or parsed.
*/
export abstract class Command<TData, TResult> {
export class Command<TData, TResult> {
public readonly command: string[]
public deserialize: (result: TResult) => TData
/**
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/dbsize.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/dbsize
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/decr.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/decr
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/decrby.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/decrby
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/del.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NonEmptyArray } from "../types"
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/del
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/echo.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/echo
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/exists.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NonEmptyArray } from "../types"
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/exists
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/expire.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/expire
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/expireat.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/expireat
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/flushall.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"
/**
* @see https://redis.io/commands/flushall
*/
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/flushdb.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"
/**
* @see https://redis.io/commands/flushdb
*/
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/get.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/get
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/getbit.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/getbit
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/getrange.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/getrange
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/getset.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/getset
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/hdel.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/hdel
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/hexists.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/hexists
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/hget.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/hget
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/hgetall.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
*
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/hincrby.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/hincrby
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/hincrbyfloat.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/hincrbyfloat
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/hkeys.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/hkeys
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/hlen.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/hlen
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/hmget.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

function deserialize<TData extends Record<string, unknown>>(
fields: string[],
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/hmset.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/hmset
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/hscan.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ScanCommandOptions } from "./scan"
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/hscan
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/hset.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/hset
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/hsetnx.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/hsetnx
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/hstrlen.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/hstrlen
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/hvals.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/hvals
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/incr.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/incr
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/incrby.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/incrby
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/incrbyfloat.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/incrbyfloat
Expand Down
1 change: 1 addition & 0 deletions pkg/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from "./append"
export * from "./bitcount"
export * from "./bitop"
export * from "./bitpos"
export * from "./command"
export * from "./dbsize"
export * from "./decr"
export * from "./decrby"
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/keys.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/keys
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/lindex.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

export class LIndexCommand<TData = string> extends Command<TData | null, unknown | null> {
constructor(key: string, index: number) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/linsert.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"
export class LInsertCommand<TData = string> extends Command<number, number> {
constructor(key: string, direction: "before" | "after", pivot: TData, value: TData) {
super(["linsert", key, direction, pivot, value])
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/llen.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/llen
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/lpop.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/lpop
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/lpush.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NonEmptyArray } from "../types"
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/lpush
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/lpushx.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NonEmptyArray } from "../types"
import { Command } from "../command"
import { Command } from "./command"

/**
* @see https://redis.io/commands/lpushx
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/lrange.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

export class LRangeCommand<TData = string> extends Command<TData[], unknown[]> {
constructor(key: string, start: number, end: number) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/lrem.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"
export class LRemCommand<TData> extends Command<number, number> {
constructor(key: string, count: number, value: TData) {
super(["lrem", key, count, value])
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/lset.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

export class LSetCommand<TData = string> extends Command<"OK", "OK"> {
constructor(key: string, value: TData, index: number) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/ltrim.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"

export class LTrimCommand extends Command<"OK", "OK"> {
constructor(key: string, start: number, end: number) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/mget.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from "../command"
import { Command } from "./command"
/**
* @see https://redis.io/commands/mget
*/
Expand Down
Loading

0 comments on commit 456e60b

Please sign in to comment.