diff --git a/.changeset/brown-glasses-thank.md b/.changeset/brown-glasses-thank.md new file mode 100644 index 00000000000..269c6616347 --- /dev/null +++ b/.changeset/brown-glasses-thank.md @@ -0,0 +1,5 @@ +--- +"effect": patch +--- + +Handle BigInt values safely when formatting JSON diagnostics. diff --git a/packages/effect/src/Formatter.ts b/packages/effect/src/Formatter.ts index 426131f10ba..d9a6a0a8029 100644 --- a/packages/effect/src/Formatter.ts +++ b/packages/effect/src/Formatter.ts @@ -253,9 +253,10 @@ function safeToString(input: any): string { * Uses `JSON.stringify` internally with a replacer that tracks the current * object ancestry. Circular references are replaced with `undefined`, which * omits them from object output. `Redactable` values are automatically redacted - * before serialization. Values not supported by JSON, such as `BigInt`, - * `Symbol`, `undefined`, and functions, follow standard `JSON.stringify` - * behavior. The `space` parameter controls indentation and defaults to `0`. + * before serialization. `BigInt` values are stringified with an `n` suffix. + * Other values not supported by JSON, such as `Symbol`, `undefined`, and + * functions, follow standard `JSON.stringify` behavior. The `space` parameter + * controls indentation and defaults to `0`. * * **Example** (Formatting compact JSON) * @@ -302,6 +303,9 @@ export function formatJson(input: unknown, options?: { input, function(this: unknown, _key: string, value: unknown) { const redacted = redact(value) + if (typeof redacted === "bigint") { + return `${redacted}n` + } if (typeof redacted !== "object" || redacted === null) { return redacted } diff --git a/packages/effect/test/Formatter.test.ts b/packages/effect/test/Formatter.test.ts index 782dcedb2dc..aea0f4d2e83 100644 --- a/packages/effect/test/Formatter.test.ts +++ b/packages/effect/test/Formatter.test.ts @@ -1,4 +1,4 @@ -import { Context, Option, Redactable, Redacted, Schema } from "effect" +import { Context, Inspectable, Option, Redactable, Redacted, Schema } from "effect" import { format, formatJson } from "effect/Formatter" import { describe, it } from "vitest" import { strictEqual } from "./utils/assert.ts" @@ -239,9 +239,25 @@ describe("Formatter", () => { strictEqual(formatJson({ left: shared, right: shared }), `{"left":{"a":1},"right":{"a":1}}`) }) + it("should stringify BigInt values", () => { + strictEqual(formatJson({ value: 123n }), `{"value":"123n"}`) + strictEqual(formatJson([1n, 2n]), `["1n","2n"]`) + }) + it("should redact sensitive data", () => { strictEqual(formatJson(data), `{"secret":"[REDACTED]"}`) strictEqual(formatJson({ a: data }), `{"a":{"secret":"[REDACTED]"}}`) }) }) + + describe("Inspectable.toStringUnknown", () => { + it("should stringify BigInt values", () => { + strictEqual( + Inspectable.toStringUnknown({ value: 123n }), + `{ + "value": "123n" +}` + ) + }) + }) })