Пример
See exactly what JSON.stringify destroys about a Date, Map, Set, BigInt, undefined, NaN, Infinity and RegExp, and which of them superjson puts back
sha256:97bbd6c4d7adf8fafd6021c2aa3a67ab2229cb30314cdd2e7106ba36b77a7316
CROSS_PASS
L4_CROSS_PASS
MIT-0
Кейс
- Цель
- See exactly what JSON.stringify destroys about a Date, Map, Set, BigInt, undefined, NaN, Infinity and RegExp, and which of them superjson puts back HOW
- Пакеты
- superjson 2.2.6
- Окружение
- node 22
- Создан
- 2026-08-14T13:20:21Z
Контракт
- assert JSON.stringify throws a TypeError reading exactly "Do not know how to serialize a BigInt", naming no key, so one BigInt takes the whole document down
- assert every other loss is silent: JSON.stringify succeeds and turns the Date into a string that is no longer a Date
- assert Map, Set and RegExp all come back as {} from JSON.stringify, because their contents live in internal slots and only enumerable own properties are walked
- assert NaN and Infinity both become null, arriving indistinguishable from each other and from a real null
- assert the asymmetry: the same undefined is deleted from an object but coerced to null inside an array, so a record and a list do not survive the same round trip
- assert JSON.stringify(undefined) returns the value undefined rather than a string, so sending it directly sends invalid JSON
- assert superjson round-trips the whole fixture back to its own types: Date, Map, Set, bigint and RegExp, with NaN, Infinity and the exact 2^53+1 bigint value intact
- assert Number() on that bigint silently returns 2^53, which is why the throw exists and why the obvious workaround is wrong
- assert a Map keeps non-string keys, while the hand-rolled Object.fromEntries fix stringifies a number key and collapses an object key onto "[object Object]"
- assert both halves of the undefined asymmetry are repaired: the object key exists again and the array slot holds a real undefined rather than a hole
- assert restoration rebuilds rather than aliases, so the returned Date is a new object with the same instant
- assert nesting is not special-cased: a Date inside an array inside an object, and a Date held as a Map value, both come back
- assert negative zero survives superjson but not JSON.stringify, by the same route as NaN and Infinity: tagged "number" and carried as the string "-0"
- assert serialize returns exactly {json, meta}, with json being ordinary JSON that JSON.stringify accepts, and a rebuilt tree that does not alias the input
- assert meta.values is one dotted-path entry per rebuilt value with the tags Date, map, set, bigint, undefined, number and regexp, that the casing of a tag is not derivable, and that meta.v is 1
- assert URL is a second capitalised tag that superjson restores, and that plain JSON.stringify flattens a URL to its href because URL has a toJSON of its own, so toJSON is not Date-specific
- assert a path segment containing a dot is escaped in the meta key and that such a key round-trips intact
- assert meta is absent entirely rather than empty when nothing needed annotating, so reading result.meta.values on plain data throws
- assert reaching one object by two paths brings meta back on its own with referentialEqualities and no values, keyed by identity rather than by value, and that the annotation re-links the two copies so x === y survives the round trip
- assert deserialize takes the split back as an object, so the two pieces can travel separately
- assert stringify is exactly JSON.stringify of the split, and that a bare undefined becomes valid JSON here unlike JSON.stringify
- assert an unregistered class instance keeps its data, loses its prototype and methods, and is not annotated at all
- assert a function is left in json by serialize as a live reference and is only dropped by the JSON.stringify inside stringify, so json is typed JSONValue but is not always a JSON value
- assert an unregistered symbol value takes that same two-step path, and that plain JSON.stringify drops a symbol the same asymmetric way it drops undefined
- assert an Invalid Date is NOT restored: it is unannotated, passed through into json, then turned into null by Date.prototype.toJSON at the stringify step
- assert a TypeError comes back as a plain Error with the stack dropped, while JSON.stringify reports any Error as {} because message and stack are own but non-enumerable and name is not an own property
- assert the subclass survives only as data: the tag is the capitalised "Error" for every Error and the name string is reassigned onto a base Error, so err.name still reads TypeError while instanceof TypeError is false
- assert the dropped stack is opt-in rather than policy: allowErrorProps('stack') on a private instance carries it through stringify and parse, while the default instance measured above does not
- assert registerClass restores instanceof and methods and annotates the value as ["class", name]
- assert deserializing a class name this end never registered throws rather than degrading to a plain object
- assert registerClass mutates the singleton shared by every importer of the default export, while a private SuperJSON instance keeps its own registry
Файлы
- csx.json
- package-lock.json
- package.json
- src/roundtrip.mjs
- test/contract.mjs
Исходный сидер
Квитанции проверки
- node 22 · CONTAINER_RUN · compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS · node-typescript@1 · 2026-08-14 · ed25519:a2ec939a4c60e243
- · CONTAINER_RUN · compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS · node-typescript@1 · 2026-08-14 · ed25519:7da3f610facce8bf
- · CONTAINER_RUN · compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS · node-typescript@1 · 2026-08-14 · ed25519:7da3f610facce8bf
- · CONTAINER_RUN · compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS · node-typescript@1 · 2026-08-14 · ed25519:7da3f610facce8bf
- · CONTAINER_RUN · compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS · node-typescript@1 · 2026-08-14 · ed25519:7da3f610facce8bf