mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
@earendil-works/pi-schema compiles JSON Schema (drafts 2020-12 and 7) once into plain closures: no eval or new Function, so it runs under a strict CSP. It passes 2,222 of the 2,223 core tests of the official JSON-Schema-Test-Suite, checks Chord-style argument schemas within 1.2x of TypeBox's JIT, and keeps 3,200 compiled schemas in 7.9 MB (TypeBox: 54.7 MB). - compile(schema, options) returns a Validator: check() is the boolean check (a type guard for builder schemas), errors() reports each failure with a JSON Pointer path, missing and unknown fields at the field. - Builders (str, int, obj, opt, union, ...) return plain JSON Schema typed with the values it accepts (Static<typeof schema>). - Formats are an optional entry point (./formats), adapted from TypeBox (MIT). - Tests run the vendored suite (drafts 2020-12 and 7, with optional and format tests); npm run bench compares conformance, speed, setup cost, memory and bundle size with TypeBox, @cfworker/json-schema and omptype.
77 lines
2.6 KiB
TypeScript
77 lines
2.6 KiB
TypeScript
// Loads the vendored JSON-Schema-Test-Suite: its test files, the remote documents and the official meta-schemas.
|
|
|
|
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
import { join, relative } from "node:path";
|
|
import type { JsonSchema } from "../src/index.ts";
|
|
|
|
/** One test file: its path below `tests/` and its groups. */
|
|
export interface SuiteFile {
|
|
readonly name: string;
|
|
readonly draft: "2020-12" | "7";
|
|
readonly groups: readonly SuiteGroup[];
|
|
}
|
|
|
|
/** A schema with values and whether each is valid. */
|
|
export interface SuiteGroup {
|
|
readonly description: string;
|
|
readonly schema: JsonSchema;
|
|
readonly tests: readonly { readonly description: string; readonly data: unknown; readonly valid: boolean }[];
|
|
}
|
|
|
|
const fixtures = join(import.meta.dirname, "fixtures");
|
|
const root = join(fixtures, "json-schema-test-suite");
|
|
|
|
/** Every test file of drafts 2020-12 and 7. */
|
|
export function suiteFiles(): SuiteFile[] {
|
|
const files: SuiteFile[] = [];
|
|
for (const [dir, draft] of [
|
|
["draft2020-12", "2020-12"],
|
|
["draft7", "7"],
|
|
] as const) {
|
|
for (const path of jsonFiles(join(root, "tests", dir))) {
|
|
const name = relative(join(root, "tests"), path).replace(/\.json$/, "");
|
|
files.push({ name, draft, groups: JSON.parse(readFileSync(path, "utf8")) });
|
|
}
|
|
}
|
|
return files;
|
|
}
|
|
|
|
/** The remote documents and meta-schemas the tests reference, by URI (fresh copies). */
|
|
export function suiteSchemas(): Record<string, JsonSchema> {
|
|
const schemas: Record<string, JsonSchema> = {};
|
|
const remotes = join(root, "remotes");
|
|
for (const path of jsonFiles(remotes)) {
|
|
schemas[`http://localhost:1234/${relative(remotes, path).replaceAll("\\", "/")}`] = readJson(path);
|
|
}
|
|
const meta = join(fixtures, "meta-schemas");
|
|
schemas["http://json-schema.org/draft-07/schema"] = readJson(join(meta, "draft-07-schema.json"));
|
|
schemas["https://json-schema.org/draft/2020-12/schema"] = readJson(join(meta, "draft2020-12-schema.json"));
|
|
for (const vocabulary of [
|
|
"core",
|
|
"applicator",
|
|
"unevaluated",
|
|
"validation",
|
|
"meta-data",
|
|
"format-annotation",
|
|
"content",
|
|
]) {
|
|
schemas[`https://json-schema.org/draft/2020-12/meta/${vocabulary}`] = readJson(
|
|
join(meta, `draft2020-12-meta-${vocabulary}.json`),
|
|
);
|
|
}
|
|
return schemas;
|
|
}
|
|
|
|
function readJson(path: string): JsonSchema {
|
|
return JSON.parse(readFileSync(path, "utf8"));
|
|
}
|
|
|
|
function jsonFiles(dir: string): string[] {
|
|
const out: string[] = [];
|
|
for (const entry of readdirSync(dir).sort()) {
|
|
const path = join(dir, entry);
|
|
if (statSync(path).isDirectory()) out.push(...jsonFiles(path));
|
|
else if (entry.endsWith(".json")) out.push(path);
|
|
}
|
|
return out;
|
|
}
|