Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions lib/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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);
Expand Down
9 changes: 8 additions & 1 deletion lib/experimental.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ export type CompiledSchema = {
ast: AST;
};

export const serialize: (compiledSchema: CompiledSchema) => string;
export const deserialize: (serialized: string) => CompiledSchema;

type AST = {
metaData: Record<string, MetaData>;
plugins: EvaluationPlugin[];
plugins: Set<EvaluationPlugin>;
} & Record<string, Node<unknown>[] | boolean>;

type Node<A> = [keywordId: string, schemaUri: string, keywordValue: A];
Expand Down Expand Up @@ -88,13 +91,15 @@ export type ValidationContext = {

// Evaluation Plugins
export type EvaluationPlugin<Context extends ValidationContext = ValidationContext> = {
id?: string;
beforeSchema?(url: string, instance: JsonNode, context: Context): void;
beforeKeyword?(keywordNode: Node<unknown>, instance: JsonNode, context: Context, schemaContext: Context, keyword: Keyword<unknown>): void;
afterKeyword?(keywordNode: Node<unknown>, instance: JsonNode, context: Context, valid: boolean, schemaContext: Context, keyword: Keyword<unknown>): void;
afterSchema?(url: string, instance: JsonNode, context: Context, valid: boolean): void;
};

export class BasicOutputPlugin implements EvaluationPlugin<ErrorsContext> {
id: string;
errors: OutputUnit[];

beforeSchema(url: string, instance: JsonNode, context: ErrorsContext): void;
Expand All @@ -104,6 +109,7 @@ export class BasicOutputPlugin implements EvaluationPlugin<ErrorsContext> {
}

export class DetailedOutputPlugin implements EvaluationPlugin<ErrorsContext> {
id: string;
errors: OutputUnit[];

beforeSchema(url: string, instance: JsonNode, context: ErrorsContext): void;
Expand All @@ -117,6 +123,7 @@ export type ErrorsContext = ValidationContext & {
};

export class AnnotationsPlugin implements EvaluationPlugin<AnnotationsContext> {
id: string;
annotations: OutputUnit[];

beforeSchema(url: string, instance: JsonNode, context: AnnotationsContext): void;
Expand Down
7 changes: 6 additions & 1 deletion lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ export const validate: (
(url: string) => Promise<Validator>
);

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;
Expand Down
32 changes: 32 additions & 0 deletions lib/restore-validator.spec.ts
Original file line number Diff line number Diff line change
@@ -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);

Check failure on line 27 in lib/restore-validator.spec.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

lib/restore-validator.spec.ts > Validator Serialization for high-level API > serializes and restores a validator successfully

TypeError: restoreValidator is not a function ❯ lib/restore-validator.spec.ts:27:31

Check failure on line 27 in lib/restore-validator.spec.ts

View workflow job for this annotation

GitHub Actions / build (22.x)

lib/restore-validator.spec.ts > Validator Serialization for high-level API > serializes and restores a validator successfully

TypeError: restoreValidator is not a function ❯ lib/restore-validator.spec.ts:27:31

expect(restoredValidator({ foo: "abc", bar: 42 }).valid).toBe(true);
expect(restoredValidator({ foo: "123" }).valid).toBe(false);
});
});
Loading