From 38ab80af8c485e89c029d57797f04db8ae9b3924 Mon Sep 17 00:00:00 2001 From: Diya Date: Fri, 15 May 2026 02:21:10 +0530 Subject: [PATCH 1/2] Make validator serialization a first-class feature --- lib/core.js | 10 ++++++++++ lib/index.d.ts | 7 ++++++- lib/index.js | 2 +- lib/restore-validator.spec.ts | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 lib/restore-validator.spec.ts diff --git a/lib/core.js b/lib/core.js index 51785a6a..c49c86d6 100644 --- a/lib/core.js +++ b/lib/core.js @@ -13,6 +13,7 @@ import { getKeywordName } from "./keywords.js"; import Validation from "./keywords/validation.js"; import { BasicOutputPlugin } from "./evaluation-plugins/basic-output.js"; import { DetailedOutputPlugin } from "./evaluation-plugins/detailed-output.js"; +import { serialize, deserialize } from "./compiled-schema-serialization.js"; export const FLAG = "FLAG", BASIC = "BASIC", DETAILED = "DETAILED"; @@ -22,10 +23,19 @@ export const validate = async (url, value = undefined, options = undefined) => { const schema = await getSchema(url); const compiled = await compile(schema); const interpretAst = (value, options) => interpret(compiled, Instance.fromJs(value), options); + interpretAst.serialize = () => serialize(compiled); return value === undefined ? interpretAst : interpretAst(value, options); }; +export const restoreValidator = (json) => { + const compiled = deserialize(json); + const interpretAst = (value, options) => interpret(compiled, Instance.fromJs(value), options); + interpretAst.serialize = () => serialize(compiled); + + return interpretAst; +}; + export const compile = async (schema) => { const ast = { metaData: {}, plugins: new Set() }; const schemaUri = await Validation.compile(schema, ast); diff --git a/lib/index.d.ts b/lib/index.d.ts index 0a9410d0..9e5761f5 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -28,7 +28,12 @@ export const validate: ( (url: string) => Promise ); -export type Validator = (value: Json, options?: OutputFormat | ValidationOptions) => Output; +export const restoreValidator: (json: string) => Validator; + +export type Validator = { + (value: Json, options?: OutputFormat | ValidationOptions): Output; + serialize(): string; +}; export type Output = { valid: true; diff --git a/lib/index.js b/lib/index.js index 6cf89c08..0cfc4c8a 100644 --- a/lib/index.js +++ b/lib/index.js @@ -129,7 +129,7 @@ addKeyword(unknown); addKeyword(vocabulary); addKeyword(writeOnly); -export { addSchema, unregisterSchema, validate, FLAG } from "./core.js"; +export { addSchema, unregisterSchema, validate, restoreValidator, FLAG } from "./core.js"; export { registerSchema, hasSchema, getAllRegisteredSchemaUris } from "./schema.js"; export { getMetaSchemaOutputFormat, diff --git a/lib/restore-validator.spec.ts b/lib/restore-validator.spec.ts new file mode 100644 index 00000000..c7e6531f --- /dev/null +++ b/lib/restore-validator.spec.ts @@ -0,0 +1,32 @@ +import { describe, expect, test } from "vitest"; +import { registerSchema, validate, restoreValidator } from "../v1/index.js"; + + +describe("Validator Serialization for high-level API", () => { + const schemaUri = "schema:high-level-serialization"; + const dialectUri = "https://json-schema.org/v1"; + + test("serializes and restores a validator successfully", async () => { + registerSchema({ + type: "object", + properties: { + foo: { type: "string", pattern: "^[a-z]+$" }, + bar: { type: "number", minimum: 10 } + }, + required: ["foo"] + }, schemaUri, dialectUri); + + const originalValidator = await validate(schemaUri); + + expect(originalValidator({ foo: "abc", bar: 42 }).valid).toBe(true); + expect(originalValidator({ foo: "123" }).valid).toBe(false); + + const json = originalValidator.serialize(); + expect(typeof json).toBe("string"); + + const restoredValidator = restoreValidator(json); + + expect(restoredValidator({ foo: "abc", bar: 42 }).valid).toBe(true); + expect(restoredValidator({ foo: "123" }).valid).toBe(false); + }); +}); From fa15c026d4c84ee04a9726a34beb30b7b057b039 Mon Sep 17 00:00:00 2001 From: Diya Date: Sun, 17 May 2026 00:22:37 +0530 Subject: [PATCH 2/2] update readme.md --- README.md | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c9212389..6433f1d2 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,16 @@ const output1 = isString("foo"); const output2 = isString(42); ``` +You can also serialize a compiled validation function and restore it later without needing to recompile the schema. + +```javascript +import { restoreValidator } from "@hyperjump/json-schema/draft-2020-12"; + +const serializedValidator = isString.serialize(); +const restoredIsString = restoreValidator(serializedValidator); +const output3 = restoredIsString("foo"); +``` + **File-based and web-based schemas** Schemas that are available on the web can be loaded automatically without @@ -240,21 +250,24 @@ Schema, such as `@hyperjump/json-schema/draft-2020-12`. Load a schema manually rather than fetching it from the filesystem or over the network. Any schema already registered with the same identifier will be replaced with no warning. -* **validate**: (schemaURI: string, instance: any, outputFormat: ValidationOptions | OutputFormat = FLAG) => Promise\ +* **validate**: (schemaURI: string, instance: any, outputFormat: ValidationOptions | OutputFormat = FLAG) => Promise\ Validate an instance against a schema. This function is curried to allow compiling the schema once and applying it to multiple instances. -* **validate**: (schemaURI: string) => Promise\<(instance: any, outputFormat: ValidationOptions | OutputFormat = FLAG) => OutputUnit> +* **validate**: (schemaURI: string) => Promise\ Compiling a schema to a validation function. +* **restoreValidator**: (serialized: string) => Validator + + Restore a validation function from a serialized string without needing to recompile. * **FLAG**: "FLAG" An identifier for the `FLAG` output format as defined by the 2019-09 and 2020-12 specifications. -* **InvalidSchemaError**: Error & { output: OutputUnit } +* **InvalidSchemaError**: Error & { output: Output & { valid: false } } This error is thrown if the schema being compiled is found to be invalid. - The `output` field contains an `OutputUnit` with information about the + The `output` field contains an `Output` object with information about the error. You can use the `setMetaSchemaOutputFormat` configuration to set the output format that is returned in `output`. * **setMetaSchemaOutputFormat**: (outputFormat: OutputFormat) => void @@ -278,11 +291,18 @@ The following types are used in the above definitions Only the `FLAG` output format is part of the Stable API. Additional [output formats](#output-formats) are included as part of the Experimental API. -* **OutputUnit**: { valid: boolean } +* **Validator**: (instance: any, outputFormat: ValidationOptions | OutputFormat = FLAG) => Output + * **serialize**: () => string + + A compiled validation function that can be executed to validate an instance, or serialized to a string so it can be restored at a later time without needing to recompile. +* **Output**: { valid: boolean } Output is an experimental feature of the JSON Schema specification. There - may be additional fields present in the OutputUnit, but only the `valid` + may be additional fields present in the Output, but only the `valid` property should be considered part of the Stable API. +* **OutputUnit**: + + A type used in detailed or hierarchical output formats. Contains information about an individual validation error or annotation. * **ValidationOptions**: * outputFormat?: OutputFormat @@ -760,7 +780,8 @@ These are available from the `@hyperjump/json-schema/experimental` export. Return a compiled schema. This is useful if you're creating tooling for something other than validation. -* **interpret**: (schema: CompiledSchema, instance: JsonNode, outputFormat: OutputFormat = BASIC) => OutputUnit +* **interpret**: (schema: CompiledSchema, instance: JsonNode, outputFormat: ValidationOptions | OutputFormat = BASIC) => Output +* **interpret**: (schema: CompiledSchema) => Validator A curried function for validating an instance against a compiled schema. This can be useful for creating custom output formats.