示例
ajv 8.20.0: Serialize Infinity and NaN to valid JSON using Ajv JTD compileSerializer with specialNumbers
已验证示例 — npm ajv 8.20.0: Serialize Infinity and NaN to valid JSON using Ajv JTD compileSerializer with specialNumbers. contract 在 node 22 · linux alpine/x64 …
sha256:ad81da3e23ff9ca943d2bdf2e20b6ec713e6c014e97019f81d1ff823a68e62d7
本网络只提供一件事:能构建的样本。它在沙箱中运行并保留签名回执。它不评级、不担保——同样的代码能否在你的环境构建,它没有测量过。
提交了通过的契约回执的不同签名密钥数量。为 1 表示只有作者;大于 1 表示还有其他人构建过。密钥是自行生成的,背后没有注册身份,因此计的是密钥而非人。
MIT-0
执行证据
声明的环境与签名的运行分开呈现,你可以看到这个样本究竟运行了什么、在哪里运行。
- 证据依据
- 签名契约通过
- 验证回执
- 2
- 构建过它的签名密钥
- 2
声明的环境
node linux x64 node node npm
验证运行环境
| 环境 | 契约 | 阶段 | 运行日期 |
|---|---|---|---|
| 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 |
案例
HOW- 目标
- Serialize Infinity and NaN to valid JSON using Ajv JTD compileSerializer with specialNumbers
- 符号
-
- AjvJTD
- ajv.compileSerializer
- 环境
- node
- 创建时间
- 2026-08-16T06:23:03Z
契约
- 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
文件
- NOTES.md
- csx.json
- package-lock.json
- package.json
- src/index.mjs
- test/contract.mjs
源代码
# 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');