Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

feat: use a UUID for nonce #19

Merged
merged 7 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,12 @@ import {
} from "tupleson";

const tson = createTson({
// This nonce function is used to generate a nonce for the serialized value
// This is used to identify the value as a serialized value
nonce: () => "__tson",
/**
* The nonce function every time we start serializing a new object
* Should return a unique value every time it's called
* @default `${crypto.randomUUID} if available, otherwise a random string generated by Math.random`
*/
// nonce: () => "__tson",
types: [
// Pick which types you want to support
tsonSet,
Expand Down
1 change: 1 addition & 0 deletions src/extend/decimal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const decimalJs: TsonType<Decimal, string> = {
};

const tson = createTson({
nonce: () => "__tson",
types: [decimalJs],
});

Expand Down
1 change: 1 addition & 0 deletions src/handlers/tsonRegExp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { tsonRegExp } from "./index.js";

test("regex", () => {
const t = createTson({
nonce: () => "__tson",
types: [tsonRegExp],
});

Expand Down
1 change: 1 addition & 0 deletions src/handlers/tsonURL.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { tsonURL } from "./index.js";

test("URL", () => {
const ctx = createTson({
nonce: () => "__tson",
types: [tsonURL],
});

Expand Down
16 changes: 16 additions & 0 deletions src/internals/getNonce.crypto.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// eslint-disable-next-line eslint-comments/disable-enable-pair
/* eslint-disable @typescript-eslint/no-explicit-any */
import { expect, test } from "vitest";

test("with crypto", async () => {
const before = global.crypto;

global.crypto = {
randomUUID: () => "test",
} as any;
const { getNonce } = await import("./getNonce.js");

expect(getNonce()).toBe("test");

global.crypto = before;
});
15 changes: 15 additions & 0 deletions src/internals/getNonce.nocrypto.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// eslint-disable-next-line eslint-comments/disable-enable-pair
/* eslint-disable @typescript-eslint/no-explicit-any */
import { expect, test } from "vitest";

test("without crypto", async () => {
const before = global.crypto;

global.crypto = undefined as any;

const { getNonce } = await import("./getNonce.js");

expect(getNonce().length).toBeGreaterThan(20);

global.crypto = before;
});
11 changes: 11 additions & 0 deletions src/internals/getNonce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { TsonNonce } from "../types.js";

const randomString = () => Math.random().toString(36).slice(2);

export type GetNonce = () => TsonNonce;

export const getNonce: GetNonce =
typeof crypto === "object" && typeof crypto.randomUUID === "function"
? () => crypto.randomUUID() as TsonNonce
: () =>
[randomString(), randomString(), randomString()].join("") as TsonNonce;
9 changes: 4 additions & 5 deletions src/serialize.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any, eslint-comments/disable-enable-pair */
import { CircularReferenceError } from "./errors.js";
import { GetNonce, getNonce } from "./internals/getNonce.js";
import { mapOrReturn } from "./internals/mapOrReturn.js";
import {
TsonAllTypes,
Expand Down Expand Up @@ -69,7 +70,6 @@ export function createTsonSerialize(opts: TsonOptions): TsonSerializeFn {

return [nonPrimitive, byPrimitive] as const;
})();
const maybeNonce = opts.nonce;

const [nonPrimitive, byPrimitive] = handlers;

Expand Down Expand Up @@ -122,11 +122,10 @@ export function createTsonSerialize(opts: TsonOptions): TsonSerializeFn {
return walk;
};

const nonceFn: GetNonce = opts.nonce ? (opts.nonce as GetNonce) : getNonce;

return ((obj): TsonSerialized => {
const nonce: TsonNonce =
typeof maybeNonce === "function"
? (maybeNonce() as TsonNonce)
: ("__tson" as TsonNonce);
const nonce = nonceFn();

const json = walker(nonce)(obj);

Expand Down
1 change: 1 addition & 0 deletions src/stringify.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { tsonUndefined } from "./handlers/tsonUndefined.js";

test("lets have a look at the stringified output", () => {
const t = createTson({
nonce: () => "__tson",
types: [tsonMap, tsonSet, tsonBigint, tsonUndefined],
});

Expand Down
8 changes: 8 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,15 @@ export type TsonType<
> = TsonTypeTester & TsonTransformer<TValue, TSerializedType>;

export interface TsonOptions {
/**
* The nonce function every time we start serializing a new object
* Should return a unique value every time it's called
* @default `${crypto.randomUUID} if available, otherwise a random string generated by Math.random`
*/
nonce?: () => number | string;
/**
* The list of types to use
*/
types: (TsonType<any, any> | TsonType<any, never>)[];
}

Expand Down
Loading