From 548a10a8bc3845ff399fcb3d1dde5a603146545b Mon Sep 17 00:00:00 2001 From: KATT Date: Mon, 2 Oct 2023 18:37:00 +0200 Subject: [PATCH 1/7] use a uuid for nonce --- src/extend/decimal.test.ts | 1 + src/handlers/tsonRegExp.test.ts | 1 + src/handlers/tsonURL.test.ts | 1 + src/internals/getNonce.ts | 12 ++++++++++++ src/serialize.ts | 8 ++++---- src/types.ts | 5 +++++ 6 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 src/internals/getNonce.ts diff --git a/src/extend/decimal.test.ts b/src/extend/decimal.test.ts index c0c34eb2..52cb96bd 100644 --- a/src/extend/decimal.test.ts +++ b/src/extend/decimal.test.ts @@ -11,6 +11,7 @@ const decimalJs: TsonType = { }; const tson = createTson({ + nonce: () => "__tson", types: [decimalJs], }); diff --git a/src/handlers/tsonRegExp.test.ts b/src/handlers/tsonRegExp.test.ts index 00d5190a..8f2e55b2 100644 --- a/src/handlers/tsonRegExp.test.ts +++ b/src/handlers/tsonRegExp.test.ts @@ -5,6 +5,7 @@ import { tsonRegExp } from "./index.js"; test("regex", () => { const t = createTson({ + nonce: () => "__tson", types: [tsonRegExp], }); diff --git a/src/handlers/tsonURL.test.ts b/src/handlers/tsonURL.test.ts index b50a54b3..1ca3b08b 100644 --- a/src/handlers/tsonURL.test.ts +++ b/src/handlers/tsonURL.test.ts @@ -5,6 +5,7 @@ import { tsonURL } from "./index.js"; test("URL", () => { const ctx = createTson({ + nonce: () => "__tson", types: [tsonURL], }); diff --git a/src/internals/getNonce.ts b/src/internals/getNonce.ts new file mode 100644 index 00000000..65b056ad --- /dev/null +++ b/src/internals/getNonce.ts @@ -0,0 +1,12 @@ +import { TsonNonce } from "../types.js"; + +const randomString = () => Math.random().toString(36).slice(2); + +type GetNonce = () => TsonNonce; + +// istanbul ignore next +export const getNonce: GetNonce = + typeof crypto === "object" && typeof crypto.randomUUID === "function" + ? () => crypto.randomUUID() as TsonNonce + : () => + [randomString(), randomString(), randomString()].join("") as TsonNonce; diff --git a/src/serialize.ts b/src/serialize.ts index 3684fe85..a4cbc662 100644 --- a/src/serialize.ts +++ b/src/serialize.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any, eslint-comments/disable-enable-pair */ import { CircularReferenceError } from "./errors.js"; +import { getNonce } from "./internals/getNonce.js"; import { mapOrReturn } from "./internals/mapOrReturn.js"; import { TsonAllTypes, @@ -123,10 +124,9 @@ export function createTsonSerialize(opts: TsonOptions): TsonSerializeFn { }; return ((obj): TsonSerialized => { - const nonce: TsonNonce = - typeof maybeNonce === "function" - ? (maybeNonce() as TsonNonce) - : ("__tson" as TsonNonce); + const nonce: TsonNonce = maybeNonce + ? (maybeNonce() as TsonNonce) + : getNonce(); const json = walker(nonce)(obj); diff --git a/src/types.ts b/src/types.ts index 99584f45..ccc17036 100644 --- a/src/types.ts +++ b/src/types.ts @@ -95,6 +95,11 @@ export type TsonType< > = TsonTypeTester & TsonTransformer; export interface TsonOptions { + /** + * + * @returns A nonce to use for this serialization + * @default () => uuid + */ nonce?: () => number | string; types: (TsonType | TsonType)[]; } From 5f190b24b83bb9479d7576c111430e4c3463319c Mon Sep 17 00:00:00 2001 From: KATT Date: Mon, 2 Oct 2023 18:37:38 +0200 Subject: [PATCH 2/7] more --- src/stringify.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stringify.test.ts b/src/stringify.test.ts index 438ca0c3..3735ac27 100644 --- a/src/stringify.test.ts +++ b/src/stringify.test.ts @@ -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], }); From 3fb05a26ba80534cb415f9ad51db6986035925d7 Mon Sep 17 00:00:00 2001 From: KATT Date: Mon, 2 Oct 2023 18:41:10 +0200 Subject: [PATCH 3/7] nonce --- src/types.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/types.ts b/src/types.ts index ccc17036..94f9691c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -96,9 +96,9 @@ export type TsonType< export interface TsonOptions { /** - * - * @returns A nonce to use for this serialization - * @default () => uuid + * The nonce function every time we start serializing a new object + * Should return a unique value every time it's called + * @default crypto.randomUUID */ nonce?: () => number | string; types: (TsonType | TsonType)[]; From 0e6b9f0839659f158b7414ec12f59a508bc6e761 Mon Sep 17 00:00:00 2001 From: KATT Date: Mon, 2 Oct 2023 18:43:34 +0200 Subject: [PATCH 4/7] nonce --- README.md | 9 ++++++--- src/types.ts | 5 ++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index be64a194..f0da24d6 100644 --- a/README.md +++ b/README.md @@ -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, diff --git a/src/types.ts b/src/types.ts index 94f9691c..a6d04749 100644 --- a/src/types.ts +++ b/src/types.ts @@ -98,9 +98,12 @@ 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 + * @default `${crypto.randomUUID} if available, otherwise a random string generated by Math.random` */ nonce?: () => number | string; + /** + * The list of types to use + */ types: (TsonType | TsonType)[]; } From 86d3459d11b260539f306e7bfa0d585e39d89175 Mon Sep 17 00:00:00 2001 From: KATT Date: Mon, 2 Oct 2023 18:46:03 +0200 Subject: [PATCH 5/7] get nonce --- src/internals/getNonce.ts | 2 +- src/serialize.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/internals/getNonce.ts b/src/internals/getNonce.ts index 65b056ad..c0d88914 100644 --- a/src/internals/getNonce.ts +++ b/src/internals/getNonce.ts @@ -2,7 +2,7 @@ import { TsonNonce } from "../types.js"; const randomString = () => Math.random().toString(36).slice(2); -type GetNonce = () => TsonNonce; +export type GetNonce = () => TsonNonce; // istanbul ignore next export const getNonce: GetNonce = diff --git a/src/serialize.ts b/src/serialize.ts index a4cbc662..4d72f3ee 100644 --- a/src/serialize.ts +++ b/src/serialize.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any, eslint-comments/disable-enable-pair */ import { CircularReferenceError } from "./errors.js"; -import { getNonce } from "./internals/getNonce.js"; +import { GetNonce, getNonce } from "./internals/getNonce.js"; import { mapOrReturn } from "./internals/mapOrReturn.js"; import { TsonAllTypes, @@ -123,10 +123,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 = maybeNonce - ? (maybeNonce() as TsonNonce) - : getNonce(); + const nonce = nonceFn(); const json = walker(nonce)(obj); From f9312e808fa606b4e82b553af78850ca1be3a718 Mon Sep 17 00:00:00 2001 From: KATT Date: Mon, 2 Oct 2023 18:47:04 +0200 Subject: [PATCH 6/7] lint --- src/serialize.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/serialize.ts b/src/serialize.ts index 4d72f3ee..81cdc913 100644 --- a/src/serialize.ts +++ b/src/serialize.ts @@ -70,7 +70,6 @@ export function createTsonSerialize(opts: TsonOptions): TsonSerializeFn { return [nonPrimitive, byPrimitive] as const; })(); - const maybeNonce = opts.nonce; const [nonPrimitive, byPrimitive] = handlers; From 2ae86a77fc62e7040587ce37a12311d53d625c15 Mon Sep 17 00:00:00 2001 From: KATT Date: Mon, 2 Oct 2023 19:01:41 +0200 Subject: [PATCH 7/7] add tests --- src/internals/getNonce.crypto.test.ts | 16 ++++++++++++++++ src/internals/getNonce.nocrypto.test.ts | 15 +++++++++++++++ src/internals/getNonce.ts | 1 - 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/internals/getNonce.crypto.test.ts create mode 100644 src/internals/getNonce.nocrypto.test.ts diff --git a/src/internals/getNonce.crypto.test.ts b/src/internals/getNonce.crypto.test.ts new file mode 100644 index 00000000..d003a465 --- /dev/null +++ b/src/internals/getNonce.crypto.test.ts @@ -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; +}); diff --git a/src/internals/getNonce.nocrypto.test.ts b/src/internals/getNonce.nocrypto.test.ts new file mode 100644 index 00000000..6a2c68ef --- /dev/null +++ b/src/internals/getNonce.nocrypto.test.ts @@ -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; +}); diff --git a/src/internals/getNonce.ts b/src/internals/getNonce.ts index c0d88914..abe97f60 100644 --- a/src/internals/getNonce.ts +++ b/src/internals/getNonce.ts @@ -4,7 +4,6 @@ const randomString = () => Math.random().toString(36).slice(2); export type GetNonce = () => TsonNonce; -// istanbul ignore next export const getNonce: GetNonce = typeof crypto === "object" && typeof crypto.randomUUID === "function" ? () => crypto.randomUUID() as TsonNonce