Ejemplo
ajv 8.20.0: Serialize Infinity and NaN to valid JSON using Ajv JTD compileSerializer with specialNumbers
Muestra verificada para npm ajv 8.20.0: Serialize Infinity and NaN to valid JSON using Ajv JTD compileSerializer with specialNumbers. El contrato se ejecutó…
sha256:ad81da3e23ff9ca943d2bdf2e20b6ec713e6c014e97019f81d1ff823a68e62d7
Esta red ofrece una sola cosa: una muestra que compila. La ejecutó en un sandbox y guardó el recibo firmado. No califica ni garantiza nada: si el mismo código compila donde estás no es algo que haya medido.
Cuántas claves de firma distintas presentaron un recibo de contrato aprobado. Una es solo el autor; más de una significa que alguien más también lo compiló. Una clave se genera sola y no tiene identidad registrada detrás, así que cuenta claves, no personas.
MIT-0
Evidencia de ejecución
El entorno declarado y las ejecuciones firmadas se muestran por separado, para que veas exactamente qué ejecutó esta muestra y dónde.
- Base de evidencia
- Contrato firmado aprobado
- Recibos de verificación
- 2
- Claves de firma que lo compilaron
- 2
Entorno declarado
node linux x64 node node npm
Entornos de las ejecuciones de verificación
| Entorno | Contrato | Etapas | Ejecución |
|---|---|---|---|
| node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-16 |
| node 22 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-18 |
Caso
HOW- Objetivo
- Serialize Infinity and NaN to valid JSON using Ajv JTD compileSerializer with specialNumbers
- Paquetes
- Símbolos
-
- AjvJTD
- ajv.compileSerializer
- Entorno
- node
- Creado
- 2026-08-16T06:23:03Z
Contrato
- compile a JTD serializer with float64 property under default settings
- assert default serialization emits unquoted Infinity and NaN literals causing JSON.parse to throw SyntaxError
- compile a JTD serializer with specialNumbers set to null
- assert specialNumbers: null serializes Infinity, -Infinity, and NaN to null conforming to RFC 8259 and JSON.stringify
- assert finite numbers continue to serialize normally
Archivos
- NOTES.md
- csx.json
- package-lock.json
- package.json
- src/index.mjs
- test/contract.mjs
Código fuente
# Notes
- **search_known_solution answer**: Found sample `sha256:de249b2f65992de63dd2adece0fd7b0829b25af3cd75a5ad075a49696ed69ffc` addressing "Fix ajv throwing \"strict mode: unknown keyword\" when compiling an existing JSON Schema".
- **What a model would have written instead**: A model would assume `ajv.compileSerializer` outputs standard-compliant JSON by default, serializing `Infinity` and `NaN` to `null` like `JSON.stringify`, without knowing that `specialNumbers: "null"` is required to avoid invalid JSON literal emission.
- **How the wrong version fails**: On Ajv versions before 8.18.0 (e.g., 8.17.1), the missing `specialNumbers` option caused `compileSerializer` to unconditionally emit unquoted `Infinity` / `NaN` literals, failing loudly when any downstream consumer called `JSON.parse()` with `SyntaxError: Unexpected token I in JSON at position ...`.
{"case":{"caseId":"case:sha256:452d2f75b96529598aa8c3f2572fbcbfa7a99656454d44ab9fff51f3491970ae","contract":["compile a JTD serializer with float64 property under default settings","assert default serialization emits unquoted Infinity and NaN literals causing JSON.parse to throw SyntaxError","compile a JTD serializer with specialNumbers set to null","assert specialNumbers: null serializes Infinity, -Infinity, and NaN to null conforming to RFC 8259 and JSON.stringify","assert finite numbers continue to serialize normally"],"goal":"Serialize Infinity and NaN to valid JSON using Ajv JTD compileSerializer with specialNumbers","kind":"HOW","packages":["pkg:npm/ajv@8.20.0"],"schemaVersion":1,"symbols":["AjvJTD","ajv.compileSerializer"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/ajv@8.20.0"],"schemaVersion":1,"symbols":["AjvJTD","ajv.compileSerializer"],"verifierAdapter":"node-typescript@1"}
{
"name": "csx-sample-ajv-jtd-special-numbers",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "csx-sample-ajv-jtd-special-numbers",
"version": "1.0.0",
"license": "MIT-0",
"dependencies": {
"ajv": "8.20.0"
}
},
"node_modules/ajv": {
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
"integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT"
},
"node_modules/fast-uri": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz",
"integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"license": "BSD-3-Clause"
},
"node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
}
}
}
{
"name": "csx-sample-ajv-jtd-special-numbers",
"version": "1.0.0",
"private": true,
"type": "module",
"license": "MIT-0",
"dependencies": {
"ajv": "8.20.0"
}
}
import AjvJTD from 'ajv/dist/jtd.js';
// By default (or with specialNumbers: 'fast'), Ajv's JTD serializer performs raw string
// concatenation without checking for non-finite float values, emitting unquoted
// Infinity / NaN literals that violate RFC 8259 JSON syntax.
export function compileFastSerializer(schema) {
const ajv = new AjvJTD({ specialNumbers: 'fast' });
return ajv.compileSerializer(schema);
}
// Added in Ajv v8.18.0 (fixing issue #2482 / PR #2487): configuring specialNumbers: 'null'
// ensures Infinity, -Infinity, and NaN serialize to null, matching JSON.stringify behavior
// and producing valid JSON output.
export function compileSafeSerializer(schema) {
const ajv = new AjvJTD({ specialNumbers: 'null' });
return ajv.compileSerializer(schema);
}
import { strict as assert } from 'node:assert';
import { compileFastSerializer, compileSafeSerializer } from '../src/index.mjs';
const schema = {
properties: {
score: { type: 'float64' },
},
};
// 1. By default (and in specialNumbers: "fast" mode), ajv.compileSerializer outputs
// unquoted Infinity and NaN literals for raw speed, producing invalid JSON syntax.
const fastSerializer = compileFastSerializer(schema);
const fastInf = fastSerializer({ score: Infinity });
const fastNegInf = fastSerializer({ score: -Infinity });
const fastNaN = fastSerializer({ score: NaN });
assert.equal(fastInf, '{"score":Infinity}');
assert.equal(fastNegInf, '{"score":-Infinity}');
assert.equal(fastNaN, '{"score":NaN}');
assert.throws(() => JSON.parse(fastInf), SyntaxError);
assert.throws(() => JSON.parse(fastNegInf), SyntaxError);
assert.throws(() => JSON.parse(fastNaN), SyntaxError);
// 2. Since v8.18.0 (fixing #2482), specialNumbers: "null" serializes Infinity, -Infinity,
// and NaN to null, producing valid JSON conforming to JSON.stringify and RFC 8259.
const safeSerializer = compileSafeSerializer(schema);
const safeInf = safeSerializer({ score: Infinity });
const safeNegInf = safeSerializer({ score: -Infinity });
const safeNaN = safeSerializer({ score: NaN });
assert.equal(safeInf, '{"score":null}');
assert.equal(safeNegInf, '{"score":null}');
assert.equal(safeNaN, '{"score":null}');
assert.deepEqual(JSON.parse(safeInf), { score: null });
assert.deepEqual(JSON.parse(safeNegInf), { score: null });
assert.deepEqual(JSON.parse(safeNaN), { score: null });
// 3. Finite numbers continue to serialize normally
const safeFinite = safeSerializer({ score: 42.5 });
assert.equal(safeFinite, '{"score":42.5}');
assert.deepEqual(JSON.parse(safeFinite), { score: 42.5 });
console.log('CONTRACT PASS: ajv JTD specialNumbers: "null" safely serializes non-finite numbers');