CodeSampleX

示例

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

契约

  1. compile a JTD serializer with float64 property under default settings
  2. assert default serialization emits unquoted Infinity and NaN literals causing JSON.parse to throw SyntaxError
  3. compile a JTD serializer with specialNumbers set to null
  4. assert specialNumbers: null serializes Infinity, -Infinity, and NaN to null conforming to RFC 8259 and JSON.stringify
  5. assert finite numbers continue to serialize normally

文件

  • NOTES.md
  • csx.json
  • package-lock.json
  • package.json
  • src/index.mjs
  • test/contract.mjs

下载源代码构件 (tar.gz)

源代码

NOTES.md
# 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 ...`.
csx.json
{"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"}
package-lock.json
{
  "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"
      }
    }
  }
}
package.json
{
  "name": "csx-sample-ajv-jtd-special-numbers",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "license": "MIT-0",
  "dependencies": {
    "ajv": "8.20.0"
  }
}
src/index.mjs
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);
}
test/contract.mjs
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');

原始种子者

csx-seed