json-schema-compatibility-checker 1.1.13 → 1.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import type { JSONSchema7Definition } from "json-schema";
2
- import type { ConstraintValidatorRegistry, SchemaError } from "./types.js";
2
+ import type { ConstraintExecutionContext, ConstraintValidatorRegistry, SchemaError } from "./types.js";
3
3
  /**
4
4
  * Recursively validates runtime data against all `constraints` found
5
5
  * in a schema, using the provided validator registry.
@@ -18,4 +18,4 @@ import type { ConstraintValidatorRegistry, SchemaError } from "./types.js";
18
18
  * @param path - The current property path (for error reporting)
19
19
  * @returns Array of schema errors (empty if all constraints pass)
20
20
  */
21
- export declare function validateSchemaConstraints(schema: JSONSchema7Definition, data: unknown, registry: ConstraintValidatorRegistry, path?: string): Promise<SchemaError[]>;
21
+ export declare function validateSchemaConstraints(schema: JSONSchema7Definition, data: unknown, registry: ConstraintValidatorRegistry, context?: ConstraintExecutionContext, path?: string): Promise<SchemaError[]>;
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"validateSchemaConstraints",{enumerable:true,get:function(){return validateSchemaConstraints}});const _typests=require("./types.js");const _utilsts=require("./utils.js");async function validateValue(constraints,value,registry,path){const errors=[];for(const constraint of constraints){const name=typeof constraint==="string"?constraint:constraint.name;const params=typeof constraint==="string"?undefined:constraint.params;const validator=registry[name];if(!validator){errors.push({type:_typests.SchemaErrorType.CustomConstraint,key:path||"$root",expected:name,received:"unknown constraint (not registered)"});continue}try{const result=await validator(value,params);if(!result.valid){errors.push({type:_typests.SchemaErrorType.CustomConstraint,key:path||"$root",expected:name,received:result.message??"constraint validation failed"})}}catch(err){errors.push({type:_typests.SchemaErrorType.CustomConstraint,key:path||"$root",expected:name,received:err instanceof Error?err.message:"constraint validation error"})}}return errors}async function validateSchemaConstraints(schema,data,registry,path=""){if(typeof schema==="boolean")return[];const errors=[];const constraints=(0,_utilsts.toConstraintArray)(schema.constraints);if(constraints.length>0){errors.push(...await validateValue(constraints,data,registry,path))}if((0,_utilsts.isPlainObj)(schema.properties)&&(0,_utilsts.isPlainObj)(data)){const props=schema.properties;const dataObj=data;for(const key of Object.keys(props)){const propSchema=props[key];if(propSchema===undefined)continue;const propValue=dataObj[key];if(propValue===undefined&&!(0,_utilsts.hasOwn)(dataObj,key))continue;const propPath=path?`${path}.${key}`:key;errors.push(...await validateSchemaConstraints(propSchema,propValue,registry,propPath))}}if((0,_utilsts.isPlainObj)(schema.items)&&Array.isArray(data)){const itemSchema=schema.items;const itemPath=path?`${path}[]`:"[]";for(let i=0;i<data.length;i++){errors.push(...await validateSchemaConstraints(itemSchema,data[i],registry,itemPath))}}if(Array.isArray(schema.items)&&Array.isArray(data)){const tupleSchemas=schema.items;for(let i=0;i<tupleSchemas.length&&i<data.length;i++){const itemSchema=tupleSchemas[i];if(itemSchema===undefined)continue;const itemPath=path?`${path}[${i}]`:`[${i}]`;errors.push(...await validateSchemaConstraints(itemSchema,data[i],registry,itemPath))}}if((0,_utilsts.isPlainObj)(schema.patternProperties)&&(0,_utilsts.isPlainObj)(data)){const pp=schema.patternProperties;const dataObj=data;for(const pattern of Object.keys(pp)){const patternSchema=pp[pattern];if(patternSchema===undefined||typeof patternSchema==="boolean")continue;let regex;try{regex=new RegExp(pattern)}catch{continue}for(const dataKey of Object.keys(dataObj)){if(!regex.test(dataKey))continue;const dataValue=dataObj[dataKey];const ppPath=path?`${path}.${dataKey}`:dataKey;errors.push(...await validateSchemaConstraints(patternSchema,dataValue,registry,ppPath))}}}if((0,_utilsts.isPlainObj)(schema.additionalProperties)&&typeof schema.additionalProperties!=="boolean"&&(0,_utilsts.isPlainObj)(data)){const apSchema=schema.additionalProperties;const dataObj=data;const definedProps=(0,_utilsts.isPlainObj)(schema.properties)?new Set(Object.keys(schema.properties)):new Set;const ppPatterns=[];if((0,_utilsts.isPlainObj)(schema.patternProperties)){for(const pattern of Object.keys(schema.patternProperties)){try{ppPatterns.push(new RegExp(pattern))}catch{}}}for(const dataKey of Object.keys(dataObj)){if(definedProps.has(dataKey))continue;if(ppPatterns.some(re=>re.test(dataKey)))continue;const dataValue=dataObj[dataKey];const apPath=path?`${path}.${dataKey}`:dataKey;errors.push(...await validateSchemaConstraints(apSchema,dataValue,registry,apPath))}}if((0,_utilsts.isPlainObj)(schema.dependencies)&&(0,_utilsts.isPlainObj)(data)){const deps=schema.dependencies;const dataObj=data;for(const depKey of Object.keys(deps)){if(!(0,_utilsts.hasOwn)(dataObj,depKey))continue;const depValue=deps[depKey];if(depValue===undefined)continue;if(Array.isArray(depValue))continue;if(typeof depValue==="boolean")continue;errors.push(...await validateSchemaConstraints(depValue,data,registry,path))}}return errors}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"validateSchemaConstraints",{enumerable:true,get:function(){return validateSchemaConstraints}});const _typests=require("./types.js");const _utilsts=require("./utils.js");async function validateValue(constraints,value,registry,path,context){const errors=[];for(const constraint of constraints){const name=typeof constraint==="string"?constraint:constraint.name;const params=typeof constraint==="string"?undefined:constraint.params;const validator=registry[name];if(!validator){errors.push({type:_typests.SchemaErrorType.CustomConstraint,key:path||"$root",expected:name,received:"unknown constraint (not registered)"});continue}try{const result=await validator(value,params,context);if(!result.valid){errors.push({type:_typests.SchemaErrorType.CustomConstraint,key:path||"$root",expected:name,received:result.message??"constraint validation failed"})}}catch(err){errors.push({type:_typests.SchemaErrorType.CustomConstraint,key:path||"$root",expected:name,received:err instanceof Error?err.message:"constraint validation error"})}}return errors}async function validateSchemaConstraints(schema,data,registry,context,path=""){if(typeof schema==="boolean")return[];const errors=[];const constraints=(0,_utilsts.toConstraintArray)(schema.constraints);if(constraints.length>0){errors.push(...await validateValue(constraints,data,registry,path,context))}if((0,_utilsts.isPlainObj)(schema.properties)&&(0,_utilsts.isPlainObj)(data)){const props=schema.properties;const dataObj=data;for(const key of Object.keys(props)){const propSchema=props[key];if(propSchema===undefined)continue;const propValue=dataObj[key];if(propValue===undefined&&!(0,_utilsts.hasOwn)(dataObj,key))continue;const propPath=path?`${path}.${key}`:key;errors.push(...await validateSchemaConstraints(propSchema,propValue,registry,context,propPath))}}if((0,_utilsts.isPlainObj)(schema.items)&&Array.isArray(data)){const itemSchema=schema.items;const itemPath=path?`${path}[]`:"[]";for(let i=0;i<data.length;i++){errors.push(...await validateSchemaConstraints(itemSchema,data[i],registry,context,itemPath))}}if(Array.isArray(schema.items)&&Array.isArray(data)){const tupleSchemas=schema.items;for(let i=0;i<tupleSchemas.length&&i<data.length;i++){const itemSchema=tupleSchemas[i];if(itemSchema===undefined)continue;const itemPath=path?`${path}[${i}]`:`[${i}]`;errors.push(...await validateSchemaConstraints(itemSchema,data[i],registry,context,itemPath))}}if((0,_utilsts.isPlainObj)(schema.patternProperties)&&(0,_utilsts.isPlainObj)(data)){const pp=schema.patternProperties;const dataObj=data;for(const pattern of Object.keys(pp)){const patternSchema=pp[pattern];if(patternSchema===undefined||typeof patternSchema==="boolean")continue;let regex;try{regex=new RegExp(pattern)}catch{continue}for(const dataKey of Object.keys(dataObj)){if(!regex.test(dataKey))continue;const dataValue=dataObj[dataKey];const ppPath=path?`${path}.${dataKey}`:dataKey;errors.push(...await validateSchemaConstraints(patternSchema,dataValue,registry,context,ppPath))}}}if((0,_utilsts.isPlainObj)(schema.additionalProperties)&&typeof schema.additionalProperties!=="boolean"&&(0,_utilsts.isPlainObj)(data)){const apSchema=schema.additionalProperties;const dataObj=data;const definedProps=(0,_utilsts.isPlainObj)(schema.properties)?new Set(Object.keys(schema.properties)):new Set;const ppPatterns=[];if((0,_utilsts.isPlainObj)(schema.patternProperties)){for(const pattern of Object.keys(schema.patternProperties)){try{ppPatterns.push(new RegExp(pattern))}catch{}}}for(const dataKey of Object.keys(dataObj)){if(definedProps.has(dataKey))continue;if(ppPatterns.some(re=>re.test(dataKey)))continue;const dataValue=dataObj[dataKey];const apPath=path?`${path}.${dataKey}`:dataKey;errors.push(...await validateSchemaConstraints(apSchema,dataValue,registry,context,apPath))}}if((0,_utilsts.isPlainObj)(schema.dependencies)&&(0,_utilsts.isPlainObj)(data)){const deps=schema.dependencies;const dataObj=data;for(const depKey of Object.keys(deps)){if(!(0,_utilsts.hasOwn)(dataObj,depKey))continue;const depValue=deps[depKey];if(depValue===undefined)continue;if(Array.isArray(depValue))continue;if(typeof depValue==="boolean")continue;errors.push(...await validateSchemaConstraints(depValue,data,registry,context,path))}}return errors}
2
2
  //# sourceMappingURL=constraint-validator.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/constraint-validator.ts"],"sourcesContent":["import type { JSONSchema7Definition } from \"json-schema\";\nimport type {\n\tConstraint,\n\tConstraintValidatorRegistry,\n\tSchemaError,\n} from \"./types.ts\";\nimport { SchemaErrorType } from \"./types.ts\";\nimport { hasOwn, isPlainObj, toConstraintArray } from \"./utils.ts\";\n\n// ─── Constraint Validator ────────────────────────────────────────────────────\n//\n// Validates runtime data against custom `constraints` found in a schema,\n// using the provided validator registry.\n//\n// This module is separate from `runtime-validator.ts` (which wraps AJV)\n// and from `format-validator.ts` (which handles the `format` keyword).\n\n/**\n * Validates a single value against a list of constraints using the registry.\n *\n * @param constraints - The constraints to validate against\n * @param value - The runtime value\n * @param registry - The constraint validator registry\n * @param path - The property path for error reporting\n * @returns Array of errors (empty if all constraints pass)\n */\nasync function validateValue(\n\tconstraints: Constraint[],\n\tvalue: unknown,\n\tregistry: ConstraintValidatorRegistry,\n\tpath: string,\n): Promise<SchemaError[]> {\n\tconst errors: SchemaError[] = [];\n\n\tfor (const constraint of constraints) {\n\t\tconst name = typeof constraint === \"string\" ? constraint : constraint.name;\n\t\tconst params =\n\t\t\ttypeof constraint === \"string\" ? undefined : constraint.params;\n\n\t\tconst validator = registry[name];\n\n\t\tif (!validator) {\n\t\t\terrors.push({\n\t\t\t\ttype: SchemaErrorType.CustomConstraint,\n\t\t\t\tkey: path || \"$root\",\n\t\t\t\texpected: name,\n\t\t\t\treceived: \"unknown constraint (not registered)\",\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await validator(value, params);\n\t\t\tif (!result.valid) {\n\t\t\t\terrors.push({\n\t\t\t\t\ttype: SchemaErrorType.CustomConstraint,\n\t\t\t\t\tkey: path || \"$root\",\n\t\t\t\t\texpected: name,\n\t\t\t\t\treceived: result.message ?? \"constraint validation failed\",\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (err) {\n\t\t\terrors.push({\n\t\t\t\ttype: SchemaErrorType.CustomConstraint,\n\t\t\t\tkey: path || \"$root\",\n\t\t\t\texpected: name,\n\t\t\t\treceived:\n\t\t\t\t\terr instanceof Error ? err.message : \"constraint validation error\",\n\t\t\t});\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/**\n * Recursively validates runtime data against all `constraints` found\n * in a schema, using the provided validator registry.\n *\n * Walks into: root-level constraints, `properties`, `patternProperties`,\n * `items` (single schema and tuple form), `additionalProperties` (schema form),\n * `dependencies` (schema form).\n *\n * When a schema declares a constraint that is not present in the registry,\n * an \"unknown constraint (not registered)\" error is produced. This ensures\n * that unregistered constraints are never silently ignored at runtime.\n *\n * @param schema - The resolved/narrowed schema containing constraints\n * @param data - The runtime data to validate\n * @param registry - The constraint validator registry (may be empty)\n * @param path - The current property path (for error reporting)\n * @returns Array of schema errors (empty if all constraints pass)\n */\nexport async function validateSchemaConstraints(\n\tschema: JSONSchema7Definition,\n\tdata: unknown,\n\tregistry: ConstraintValidatorRegistry,\n\tpath = \"\",\n): Promise<SchemaError[]> {\n\t// Boolean schemas → nothing to validate\n\tif (typeof schema === \"boolean\") return [];\n\n\tconst errors: SchemaError[] = [];\n\n\t// ── Root-level constraints ──\n\tconst constraints = toConstraintArray(schema.constraints);\n\tif (constraints.length > 0) {\n\t\terrors.push(...(await validateValue(constraints, data, registry, path)));\n\t}\n\n\t// ── Recurse into properties ──\n\tif (isPlainObj(schema.properties) && isPlainObj(data)) {\n\t\tconst props = schema.properties as Record<string, JSONSchema7Definition>;\n\t\tconst dataObj = data as Record<string, unknown>;\n\n\t\tfor (const key of Object.keys(props)) {\n\t\t\tconst propSchema = props[key];\n\t\t\tif (propSchema === undefined) continue;\n\n\t\t\tconst propValue = dataObj[key];\n\t\t\t// Only validate if the property exists in the data\n\t\t\tif (propValue === undefined && !hasOwn(dataObj, key)) continue;\n\n\t\t\tconst propPath = path ? `${path}.${key}` : key;\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\tpropSchema,\n\t\t\t\t\tpropValue,\n\t\t\t\t\tregistry,\n\t\t\t\t\tpropPath,\n\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Recurse into items (single schema) ──\n\tif (isPlainObj(schema.items) && Array.isArray(data)) {\n\t\tconst itemSchema = schema.items as JSONSchema7Definition;\n\t\tconst itemPath = path ? `${path}[]` : \"[]\";\n\n\t\tfor (let i = 0; i < data.length; i++) {\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\titemSchema,\n\t\t\t\t\tdata[i],\n\t\t\t\t\tregistry,\n\t\t\t\t\titemPath,\n\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Recurse into tuple items ──\n\tif (Array.isArray(schema.items) && Array.isArray(data)) {\n\t\tconst tupleSchemas = schema.items as JSONSchema7Definition[];\n\t\tfor (let i = 0; i < tupleSchemas.length && i < data.length; i++) {\n\t\t\tconst itemSchema = tupleSchemas[i];\n\t\t\tif (itemSchema === undefined) continue;\n\t\t\tconst itemPath = path ? `${path}[${i}]` : `[${i}]`;\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\titemSchema,\n\t\t\t\t\tdata[i],\n\t\t\t\t\tregistry,\n\t\t\t\t\titemPath,\n\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Recurse into patternProperties ──\n\tif (isPlainObj(schema.patternProperties) && isPlainObj(data)) {\n\t\tconst pp = schema.patternProperties as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition\n\t\t>;\n\t\tconst dataObj = data as Record<string, unknown>;\n\n\t\tfor (const pattern of Object.keys(pp)) {\n\t\t\tconst patternSchema = pp[pattern];\n\t\t\tif (patternSchema === undefined || typeof patternSchema === \"boolean\")\n\t\t\t\tcontinue;\n\n\t\t\tlet regex: RegExp;\n\t\t\ttry {\n\t\t\t\tregex = new RegExp(pattern);\n\t\t\t} catch {\n\t\t\t\t// Invalid regex pattern — skip silently (same approach as AJV)\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const dataKey of Object.keys(dataObj)) {\n\t\t\t\tif (!regex.test(dataKey)) continue;\n\n\t\t\t\tconst dataValue = dataObj[dataKey];\n\t\t\t\tconst ppPath = path ? `${path}.${dataKey}` : dataKey;\n\t\t\t\terrors.push(\n\t\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\t\tpatternSchema,\n\t\t\t\t\t\tdataValue,\n\t\t\t\t\t\tregistry,\n\t\t\t\t\t\tppPath,\n\t\t\t\t\t)),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ── Recurse into additionalProperties (schema form) ──\n\tif (\n\t\tisPlainObj(schema.additionalProperties) &&\n\t\ttypeof schema.additionalProperties !== \"boolean\" &&\n\t\tisPlainObj(data)\n\t) {\n\t\tconst apSchema = schema.additionalProperties as JSONSchema7Definition;\n\t\tconst dataObj = data as Record<string, unknown>;\n\t\tconst definedProps = isPlainObj(schema.properties)\n\t\t\t? new Set(Object.keys(schema.properties as Record<string, unknown>))\n\t\t\t: new Set<string>();\n\n\t\t// Collect patternProperties regexes to exclude matching keys\n\t\tconst ppPatterns: RegExp[] = [];\n\t\tif (isPlainObj(schema.patternProperties)) {\n\t\t\tfor (const pattern of Object.keys(\n\t\t\t\tschema.patternProperties as Record<string, unknown>,\n\t\t\t)) {\n\t\t\t\ttry {\n\t\t\t\t\tppPatterns.push(new RegExp(pattern));\n\t\t\t\t} catch {\n\t\t\t\t\t// Invalid pattern — skip\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (const dataKey of Object.keys(dataObj)) {\n\t\t\t// Skip keys defined in properties\n\t\t\tif (definedProps.has(dataKey)) continue;\n\n\t\t\t// Skip keys matching any patternProperties pattern\n\t\t\tif (ppPatterns.some((re) => re.test(dataKey))) continue;\n\n\t\t\tconst dataValue = dataObj[dataKey];\n\t\t\tconst apPath = path ? `${path}.${dataKey}` : dataKey;\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\tapSchema,\n\t\t\t\t\tdataValue,\n\t\t\t\t\tregistry,\n\t\t\t\t\tapPath,\n\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Recurse into dependencies (schema form) ──\n\tif (isPlainObj(schema.dependencies) && isPlainObj(data)) {\n\t\tconst deps = schema.dependencies as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition | string[]\n\t\t>;\n\t\tconst dataObj = data as Record<string, unknown>;\n\n\t\tfor (const depKey of Object.keys(deps)) {\n\t\t\t// Dependency only applies if the trigger key is present in data\n\t\t\tif (!hasOwn(dataObj, depKey)) continue;\n\n\t\t\tconst depValue = deps[depKey];\n\t\t\tif (depValue === undefined) continue;\n\n\t\t\t// Skip array-form dependencies (property deps, not schema deps)\n\t\t\tif (Array.isArray(depValue)) continue;\n\n\t\t\t// Skip boolean schemas\n\t\t\tif (typeof depValue === \"boolean\") continue;\n\n\t\t\t// Schema-form dependency: validate the entire data object against it\n\t\t\t// The dependency schema applies to the whole object, not just the dep key\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(depValue, data, registry, path)),\n\t\t\t);\n\t\t}\n\t}\n\n\treturn errors;\n}\n"],"names":["validateSchemaConstraints","validateValue","constraints","value","registry","path","errors","constraint","name","params","undefined","validator","push","type","SchemaErrorType","CustomConstraint","key","expected","received","result","valid","message","err","Error","schema","data","toConstraintArray","length","isPlainObj","properties","props","dataObj","Object","keys","propSchema","propValue","hasOwn","propPath","items","Array","isArray","itemSchema","itemPath","i","tupleSchemas","patternProperties","pp","pattern","patternSchema","regex","RegExp","dataKey","test","dataValue","ppPath","additionalProperties","apSchema","definedProps","Set","ppPatterns","has","some","re","apPath","dependencies","deps","depKey","depValue"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/constraint-validator.ts"],"sourcesContent":["import type { JSONSchema7Definition } from \"json-schema\";\nimport type {\n\tConstraint,\n\tConstraintExecutionContext,\n\tConstraintValidatorRegistry,\n\tSchemaError,\n} from \"./types.ts\";\nimport { SchemaErrorType } from \"./types.ts\";\nimport { hasOwn, isPlainObj, toConstraintArray } from \"./utils.ts\";\n\n// ─── Constraint Validator ────────────────────────────────────────────────────\n//\n// Validates runtime data against custom `constraints` found in a schema,\n// using the provided validator registry.\n//\n// This module is separate from `runtime-validator.ts` (which wraps AJV)\n// and from `format-validator.ts` (which handles the `format` keyword).\n\n/**\n * Validates a single value against a list of constraints using the registry.\n *\n * @param constraints - The constraints to validate against\n * @param value - The runtime value\n * @param registry - The constraint validator registry\n * @param path - The property path for error reporting\n * @returns Array of errors (empty if all constraints pass)\n */\nasync function validateValue(\n\tconstraints: Constraint[],\n\tvalue: unknown,\n\tregistry: ConstraintValidatorRegistry,\n\tpath: string,\n\tcontext: ConstraintExecutionContext | undefined,\n): Promise<SchemaError[]> {\n\tconst errors: SchemaError[] = [];\n\n\tfor (const constraint of constraints) {\n\t\tconst name = typeof constraint === \"string\" ? constraint : constraint.name;\n\t\tconst params =\n\t\t\ttypeof constraint === \"string\" ? undefined : constraint.params;\n\n\t\tconst validator = registry[name];\n\n\t\tif (!validator) {\n\t\t\terrors.push({\n\t\t\t\ttype: SchemaErrorType.CustomConstraint,\n\t\t\t\tkey: path || \"$root\",\n\t\t\t\texpected: name,\n\t\t\t\treceived: \"unknown constraint (not registered)\",\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await validator(value, params, context);\n\t\t\tif (!result.valid) {\n\t\t\t\terrors.push({\n\t\t\t\t\ttype: SchemaErrorType.CustomConstraint,\n\t\t\t\t\tkey: path || \"$root\",\n\t\t\t\t\texpected: name,\n\t\t\t\t\treceived: result.message ?? \"constraint validation failed\",\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (err) {\n\t\t\terrors.push({\n\t\t\t\ttype: SchemaErrorType.CustomConstraint,\n\t\t\t\tkey: path || \"$root\",\n\t\t\t\texpected: name,\n\t\t\t\treceived:\n\t\t\t\t\terr instanceof Error ? err.message : \"constraint validation error\",\n\t\t\t});\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/**\n * Recursively validates runtime data against all `constraints` found\n * in a schema, using the provided validator registry.\n *\n * Walks into: root-level constraints, `properties`, `patternProperties`,\n * `items` (single schema and tuple form), `additionalProperties` (schema form),\n * `dependencies` (schema form).\n *\n * When a schema declares a constraint that is not present in the registry,\n * an \"unknown constraint (not registered)\" error is produced. This ensures\n * that unregistered constraints are never silently ignored at runtime.\n *\n * @param schema - The resolved/narrowed schema containing constraints\n * @param data - The runtime data to validate\n * @param registry - The constraint validator registry (may be empty)\n * @param path - The current property path (for error reporting)\n * @returns Array of schema errors (empty if all constraints pass)\n */\nexport async function validateSchemaConstraints(\n\tschema: JSONSchema7Definition,\n\tdata: unknown,\n\tregistry: ConstraintValidatorRegistry,\n\tcontext?: ConstraintExecutionContext,\n\tpath = \"\",\n): Promise<SchemaError[]> {\n\t// Boolean schemas → nothing to validate\n\tif (typeof schema === \"boolean\") return [];\n\n\tconst errors: SchemaError[] = [];\n\n\t// ── Root-level constraints ──\n\tconst constraints = toConstraintArray(schema.constraints);\n\tif (constraints.length > 0) {\n\t\terrors.push(\n\t\t\t...(await validateValue(constraints, data, registry, path, context)),\n\t\t);\n\t}\n\n\t// ── Recurse into properties ──\n\tif (isPlainObj(schema.properties) && isPlainObj(data)) {\n\t\tconst props = schema.properties as Record<string, JSONSchema7Definition>;\n\t\tconst dataObj = data as Record<string, unknown>;\n\n\t\tfor (const key of Object.keys(props)) {\n\t\t\tconst propSchema = props[key];\n\t\t\tif (propSchema === undefined) continue;\n\n\t\t\tconst propValue = dataObj[key];\n\t\t\t// Only validate if the property exists in the data\n\t\t\tif (propValue === undefined && !hasOwn(dataObj, key)) continue;\n\n\t\t\tconst propPath = path ? `${path}.${key}` : key;\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\tpropSchema,\n\t\t\t\t\tpropValue,\n\t\t\t\t\tregistry,\n\t\t\t\t\tcontext,\n\t\t\t\t\tpropPath,\n\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Recurse into items (single schema) ──\n\tif (isPlainObj(schema.items) && Array.isArray(data)) {\n\t\tconst itemSchema = schema.items as JSONSchema7Definition;\n\t\tconst itemPath = path ? `${path}[]` : \"[]\";\n\n\t\tfor (let i = 0; i < data.length; i++) {\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\titemSchema,\n\t\t\t\t\tdata[i],\n\t\t\t\t\tregistry,\n\t\t\t\t\tcontext,\n\t\t\t\t\titemPath,\n\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Recurse into tuple items ──\n\tif (Array.isArray(schema.items) && Array.isArray(data)) {\n\t\tconst tupleSchemas = schema.items as JSONSchema7Definition[];\n\t\tfor (let i = 0; i < tupleSchemas.length && i < data.length; i++) {\n\t\t\tconst itemSchema = tupleSchemas[i];\n\t\t\tif (itemSchema === undefined) continue;\n\t\t\tconst itemPath = path ? `${path}[${i}]` : `[${i}]`;\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\titemSchema,\n\t\t\t\t\tdata[i],\n\t\t\t\t\tregistry,\n\t\t\t\t\tcontext,\n\t\t\t\t\titemPath,\n\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Recurse into patternProperties ──\n\tif (isPlainObj(schema.patternProperties) && isPlainObj(data)) {\n\t\tconst pp = schema.patternProperties as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition\n\t\t>;\n\t\tconst dataObj = data as Record<string, unknown>;\n\n\t\tfor (const pattern of Object.keys(pp)) {\n\t\t\tconst patternSchema = pp[pattern];\n\t\t\tif (patternSchema === undefined || typeof patternSchema === \"boolean\")\n\t\t\t\tcontinue;\n\n\t\t\tlet regex: RegExp;\n\t\t\ttry {\n\t\t\t\tregex = new RegExp(pattern);\n\t\t\t} catch {\n\t\t\t\t// Invalid regex pattern — skip silently (same approach as AJV)\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const dataKey of Object.keys(dataObj)) {\n\t\t\t\tif (!regex.test(dataKey)) continue;\n\n\t\t\t\tconst dataValue = dataObj[dataKey];\n\t\t\t\tconst ppPath = path ? `${path}.${dataKey}` : dataKey;\n\t\t\t\terrors.push(\n\t\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\t\tpatternSchema,\n\t\t\t\t\t\tdataValue,\n\t\t\t\t\t\tregistry,\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\tppPath,\n\t\t\t\t\t)),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ── Recurse into additionalProperties (schema form) ──\n\tif (\n\t\tisPlainObj(schema.additionalProperties) &&\n\t\ttypeof schema.additionalProperties !== \"boolean\" &&\n\t\tisPlainObj(data)\n\t) {\n\t\tconst apSchema = schema.additionalProperties as JSONSchema7Definition;\n\t\tconst dataObj = data as Record<string, unknown>;\n\t\tconst definedProps = isPlainObj(schema.properties)\n\t\t\t? new Set(Object.keys(schema.properties as Record<string, unknown>))\n\t\t\t: new Set<string>();\n\n\t\t// Collect patternProperties regexes to exclude matching keys\n\t\tconst ppPatterns: RegExp[] = [];\n\t\tif (isPlainObj(schema.patternProperties)) {\n\t\t\tfor (const pattern of Object.keys(\n\t\t\t\tschema.patternProperties as Record<string, unknown>,\n\t\t\t)) {\n\t\t\t\ttry {\n\t\t\t\t\tppPatterns.push(new RegExp(pattern));\n\t\t\t\t} catch {\n\t\t\t\t\t// Invalid pattern — skip\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (const dataKey of Object.keys(dataObj)) {\n\t\t\t// Skip keys defined in properties\n\t\t\tif (definedProps.has(dataKey)) continue;\n\n\t\t\t// Skip keys matching any patternProperties pattern\n\t\t\tif (ppPatterns.some((re) => re.test(dataKey))) continue;\n\n\t\t\tconst dataValue = dataObj[dataKey];\n\t\t\tconst apPath = path ? `${path}.${dataKey}` : dataKey;\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\tapSchema,\n\t\t\t\t\tdataValue,\n\t\t\t\t\tregistry,\n\t\t\t\t\tcontext,\n\t\t\t\t\tapPath,\n\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Recurse into dependencies (schema form) ──\n\tif (isPlainObj(schema.dependencies) && isPlainObj(data)) {\n\t\tconst deps = schema.dependencies as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition | string[]\n\t\t>;\n\t\tconst dataObj = data as Record<string, unknown>;\n\n\t\tfor (const depKey of Object.keys(deps)) {\n\t\t\t// Dependency only applies if the trigger key is present in data\n\t\t\tif (!hasOwn(dataObj, depKey)) continue;\n\n\t\t\tconst depValue = deps[depKey];\n\t\t\tif (depValue === undefined) continue;\n\n\t\t\t// Skip array-form dependencies (property deps, not schema deps)\n\t\t\tif (Array.isArray(depValue)) continue;\n\n\t\t\t// Skip boolean schemas\n\t\t\tif (typeof depValue === \"boolean\") continue;\n\n\t\t\t// Schema-form dependency: validate the entire data object against it\n\t\t\t// The dependency schema applies to the whole object, not just the dep key\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\tdepValue,\n\t\t\t\t\tdata,\n\t\t\t\t\tregistry,\n\t\t\t\t\tcontext,\n\t\t\t\t\tpath,\n\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t}\n\n\treturn errors;\n}\n"],"names":["validateSchemaConstraints","validateValue","constraints","value","registry","path","context","errors","constraint","name","params","undefined","validator","push","type","SchemaErrorType","CustomConstraint","key","expected","received","result","valid","message","err","Error","schema","data","toConstraintArray","length","isPlainObj","properties","props","dataObj","Object","keys","propSchema","propValue","hasOwn","propPath","items","Array","isArray","itemSchema","itemPath","i","tupleSchemas","patternProperties","pp","pattern","patternSchema","regex","RegExp","dataKey","test","dataValue","ppPath","additionalProperties","apSchema","definedProps","Set","ppPatterns","has","some","re","apPath","dependencies","deps","depKey","depValue"],"mappings":"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"}
@@ -3,5 +3,5 @@ export { JsonSchemaCompatibilityChecker } from "./json-schema-compatibility-chec
3
3
  export { MergeEngine } from "./merge-engine.js";
4
4
  export { arePatternsEquivalent, isPatternSubset, isTrivialPattern, } from "./pattern-subset.js";
5
5
  export { formatSchemaType } from "./semantic-errors.js";
6
- export type { CheckerOptions, CheckRuntimeOptions, Constraint, Constraints, ConstraintValidationResult, ConstraintValidator, ConstraintValidatorRegistry, ResolvedConditionResult, ResolvedSubsetResult, SchemaError, SubsetResult, ValidateTargetOptions, ValidateTargets, } from "./types.js";
6
+ export type { CheckerOptions, CheckRuntimeOptions, Constraint, ConstraintExecutionContext, Constraints, ConstraintValidationResult, ConstraintValidator, ConstraintValidatorRegistry, ResolvedConditionResult, ResolvedSubsetResult, SchemaError, SubsetResult, ValidateTargetOptions, ValidateTargets, } from "./types.js";
7
7
  export { SchemaErrorType } from "./types.js";
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["export { resolveConditions } from \"./condition-resolver.ts\";\nexport { JsonSchemaCompatibilityChecker } from \"./json-schema-compatibility-checker.ts\";\nexport { MergeEngine } from \"./merge-engine.ts\";\nexport {\n\tarePatternsEquivalent,\n\tisPatternSubset,\n\tisTrivialPattern,\n} from \"./pattern-subset.ts\";\nexport { formatSchemaType } from \"./semantic-errors.ts\";\nexport type {\n\tCheckerOptions,\n\tCheckRuntimeOptions,\n\tConstraint,\n\tConstraints,\n\tConstraintValidationResult,\n\tConstraintValidator,\n\tConstraintValidatorRegistry,\n\tResolvedConditionResult,\n\tResolvedSubsetResult,\n\tSchemaError,\n\tSubsetResult,\n\tValidateTargetOptions,\n\tValidateTargets,\n} from \"./types.ts\";\nexport { SchemaErrorType } from \"./types.ts\";\n"],"names":["JsonSchemaCompatibilityChecker","MergeEngine","SchemaErrorType","arePatternsEquivalent","formatSchemaType","isPatternSubset","isTrivialPattern","resolveConditions"],"mappings":"mPACSA,wCAAAA,gEAA8B,MAC9BC,qBAAAA,0BAAW,MAsBXC,yBAAAA,wBAAe,MApBvBC,+BAAAA,sCAAqB,MAIbC,0BAAAA,kCAAgB,MAHxBC,yBAAAA,gCAAe,MACfC,0BAAAA,iCAAgB,MANRC,2BAAAA,sCAAiB,uCAAQ,2EACa,uEACnB,oDAKrB,uDAC0B,+CAgBD"}
1
+ {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["export { resolveConditions } from \"./condition-resolver.ts\";\nexport { JsonSchemaCompatibilityChecker } from \"./json-schema-compatibility-checker.ts\";\nexport { MergeEngine } from \"./merge-engine.ts\";\nexport {\n\tarePatternsEquivalent,\n\tisPatternSubset,\n\tisTrivialPattern,\n} from \"./pattern-subset.ts\";\nexport { formatSchemaType } from \"./semantic-errors.ts\";\nexport type {\n\tCheckerOptions,\n\tCheckRuntimeOptions,\n\tConstraint,\n\tConstraintExecutionContext,\n\tConstraints,\n\tConstraintValidationResult,\n\tConstraintValidator,\n\tConstraintValidatorRegistry,\n\tResolvedConditionResult,\n\tResolvedSubsetResult,\n\tSchemaError,\n\tSubsetResult,\n\tValidateTargetOptions,\n\tValidateTargets,\n} from \"./types.ts\";\nexport { SchemaErrorType } from \"./types.ts\";\n"],"names":["JsonSchemaCompatibilityChecker","MergeEngine","SchemaErrorType","arePatternsEquivalent","formatSchemaType","isPatternSubset","isTrivialPattern","resolveConditions"],"mappings":"mPACSA,wCAAAA,gEAA8B,MAC9BC,qBAAAA,0BAAW,MAuBXC,yBAAAA,wBAAe,MArBvBC,+BAAAA,sCAAqB,MAIbC,0BAAAA,kCAAgB,MAHxBC,yBAAAA,gCAAe,MACfC,0BAAAA,iCAAgB,MANRC,2BAAAA,sCAAiB,uCAAQ,2EACa,uEACnB,oDAKrB,uDAC0B,+CAiBD"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:Object.getOwnPropertyDescriptor(all,name).get})}_export(exports,{get JsonSchemaCompatibilityChecker(){return JsonSchemaCompatibilityChecker},get MergeEngine(){return _mergeenginets.MergeEngine},get SchemaErrorType(){return _typests.SchemaErrorType},get arePatternsEquivalent(){return _patternsubsetts.arePatternsEquivalent},get formatResult(){return _formatterts.formatResult},get isPatternSubset(){return _patternsubsetts.isPatternSubset},get isTrivialPattern(){return _patternsubsetts.isTrivialPattern},get normalize(){return _normalizerts.normalize},get resolveConditions(){return _conditionresolverts.resolveConditions}});const _conditionresolverts=require("./condition-resolver.js");const _constraintvalidatorts=require("./constraint-validator.js");const _datanarrowingts=require("./data-narrowing.js");const _formatterts=require("./formatter.js");const _mergeenginets=require("./merge-engine.js");const _normalizerts=require("./normalizer.js");const _patternsubsetts=require("./pattern-subset.js");const _runtimevalidatorts=require("./runtime-validator.js");const _subsetcheckerts=require("./subset-checker.js");const _typests=require("./types.js");const _utilsts=require("./utils.js");const _validatetargetsts=require("./validate-targets.js");function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}class JsonSchemaCompatibilityChecker{isSubset(sub,sup){if(sub===sup)return true;if((0,_utilsts.deepEqual)(sub,sup))return true;const nSub=(0,_normalizerts.normalize)(sub);const nSup=(0,_normalizerts.normalize)(sup);if(nSub!==sub&&nSup!==sup&&(0,_utilsts.deepEqual)(nSub,nSup))return true;if(nSub!==nSup&&(0,_utilsts.deepEqual)(nSub,nSup))return true;const{branches:subBranches}=(0,_subsetcheckerts.getBranchesTyped)(nSub);if(subBranches.length>1||subBranches[0]!==nSub){return subBranches.every(branch=>(0,_subsetcheckerts.isAtomicSubsetOf)(branch,nSup,this.engine))}return(0,_subsetcheckerts.isAtomicSubsetOf)(nSub,nSup,this.engine)}check(sub,sup,options){if(options){return this.checkWithOptions(sub,sup,options)}return this.checkInternal(sub,sup)}isEqual(a,b){return this.engine.isEqual((0,_normalizerts.normalize)(a),(0,_normalizerts.normalize)(b))}intersect(a,b){if(a===b||(0,_utilsts.deepEqual)(a,b))return(0,_normalizerts.normalize)(a);const nA=(0,_normalizerts.normalize)(a);const nB=(0,_normalizerts.normalize)(b);if((0,_utilsts.deepEqual)(nA,nB))return nA;const merged=this.engine.merge(nA,nB);if(merged===null)return null;if((0,_utilsts.deepEqual)(merged,nA)||(0,_utilsts.deepEqual)(merged,nB))return merged;return(0,_normalizerts.normalize)(merged)}normalize(def){return(0,_normalizerts.normalize)(def)}formatResult(label,result){return(0,_formatterts.formatResult)(label,result)}resolveConditions(schema,data){return(0,_conditionresolverts.resolveConditions)(schema,data,this.engine)}async checkWithOptions(sub,sup,options){const data=options.data;const{sub:validateSub,sup:validateSup,partialSub,partialSup}=(0,_validatetargetsts.resolveValidateTargets)(options.validate);const dataForConditions=(0,_utilsts.isPlainObj)(data)?data:{};const resolvedSub=(0,_conditionresolverts.resolveConditions)(sub,dataForConditions,this.engine);const resolvedSup=(0,_conditionresolverts.resolveConditions)(sup,dataForConditions,this.engine);const canNarrow=data!==undefined;const canNarrowSub=canNarrow&&(0,_utilsts.isPlainObj)(resolvedSub.resolved);const canNarrowSup=canNarrow&&(0,_utilsts.isPlainObj)(resolvedSup.resolved);const narrowedSubResolved=canNarrowSub?(0,_datanarrowingts.narrowSchemaWithData)(resolvedSub.resolved,data,resolvedSup.resolved):resolvedSub.resolved;const narrowedSupResolved=canNarrowSup?(0,_datanarrowingts.narrowSchemaWithData)(resolvedSup.resolved,data,resolvedSub.resolved):resolvedSup.resolved;const staticResult=this.checkInternal(narrowedSubResolved,narrowedSupResolved);if(!staticResult.isSubset){return{...staticResult,resolvedSub:{...resolvedSub,resolved:narrowedSubResolved},resolvedSup:{...resolvedSup,resolved:narrowedSupResolved}}}if((validateSub||validateSup)&&data!==undefined){const runtimeErrors=[];if(validateSub){const getErrors=partialSub?_runtimevalidatorts.getPartialRuntimeValidationErrors:_runtimevalidatorts.getRuntimeValidationErrors;runtimeErrors.push(...this.prefixRuntimeErrors(getErrors(narrowedSubResolved,data),"$sub"))}if(validateSup){const getErrors=partialSup?_runtimevalidatorts.getPartialRuntimeValidationErrors:_runtimevalidatorts.getRuntimeValidationErrors;runtimeErrors.push(...this.prefixRuntimeErrors(getErrors(narrowedSupResolved,data),"$sup"))}if(validateSub){runtimeErrors.push(...this.prefixRuntimeErrors(await (0,_constraintvalidatorts.validateSchemaConstraints)(narrowedSubResolved,data,this.constraintValidators),"$sub"))}if(validateSup){runtimeErrors.push(...this.prefixRuntimeErrors(await (0,_constraintvalidatorts.validateSchemaConstraints)(narrowedSupResolved,data,this.constraintValidators),"$sup"))}if(runtimeErrors.length>0){return{isSubset:false,merged:null,errors:runtimeErrors,resolvedSub:{...resolvedSub,resolved:narrowedSubResolved},resolvedSup:{...resolvedSup,resolved:narrowedSupResolved}}}}return{...staticResult,resolvedSub:{...resolvedSub,resolved:narrowedSubResolved},resolvedSup:{...resolvedSup,resolved:narrowedSupResolved}}}prefixRuntimeErrors(errors,rootKey){return errors.map(error=>({...error,key:error.key==="$root"?rootKey:`${rootKey}.${error.key}`}))}checkInternal(sub,sup){if(sub===sup){return{isSubset:true,merged:sub,errors:[]}}if((0,_utilsts.deepEqual)(sub,sup)){return{isSubset:true,merged:sub,errors:[]}}const nSub=(0,_normalizerts.normalize)(sub);const nSup=(0,_normalizerts.normalize)(sup);if((0,_utilsts.deepEqual)(nSub,nSup)){return{isSubset:true,merged:nSub,errors:[]}}const{branches:subBranches,type:subBranchType}=(0,_subsetcheckerts.getBranchesTyped)(nSub);const{branches:supBranches,type:supBranchType}=(0,_subsetcheckerts.getBranchesTyped)(nSup);if(subBranches.length>1||subBranches[0]!==nSub){return(0,_subsetcheckerts.checkBranchedSub)(subBranches,nSup,this.engine,subBranchType)}if(supBranches.length>1||supBranches[0]!==nSup){return(0,_subsetcheckerts.checkBranchedSup)(nSub,supBranches,this.engine,supBranchType)}return(0,_subsetcheckerts.checkAtomic)(nSub,nSup,this.engine)}static clearCache(){(0,_runtimevalidatorts.clearAllValidatorCaches)()}constructor(options){_define_property(this,"constraintValidators",void 0);_define_property(this,"engine",void 0);this.engine=new _mergeenginets.MergeEngine;this.constraintValidators=options?.constraints??{}}}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:Object.getOwnPropertyDescriptor(all,name).get})}_export(exports,{get JsonSchemaCompatibilityChecker(){return JsonSchemaCompatibilityChecker},get MergeEngine(){return _mergeenginets.MergeEngine},get SchemaErrorType(){return _typests.SchemaErrorType},get arePatternsEquivalent(){return _patternsubsetts.arePatternsEquivalent},get formatResult(){return _formatterts.formatResult},get isPatternSubset(){return _patternsubsetts.isPatternSubset},get isTrivialPattern(){return _patternsubsetts.isTrivialPattern},get normalize(){return _normalizerts.normalize},get resolveConditions(){return _conditionresolverts.resolveConditions}});const _conditionresolverts=require("./condition-resolver.js");const _constraintvalidatorts=require("./constraint-validator.js");const _datanarrowingts=require("./data-narrowing.js");const _formatterts=require("./formatter.js");const _mergeenginets=require("./merge-engine.js");const _normalizerts=require("./normalizer.js");const _patternsubsetts=require("./pattern-subset.js");const _runtimevalidatorts=require("./runtime-validator.js");const _subsetcheckerts=require("./subset-checker.js");const _typests=require("./types.js");const _utilsts=require("./utils.js");const _validatetargetsts=require("./validate-targets.js");function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}class JsonSchemaCompatibilityChecker{isSubset(sub,sup){if(sub===sup)return true;if((0,_utilsts.deepEqual)(sub,sup))return true;const nSub=(0,_normalizerts.normalize)(sub);const nSup=(0,_normalizerts.normalize)(sup);if(nSub!==sub&&nSup!==sup&&(0,_utilsts.deepEqual)(nSub,nSup))return true;if(nSub!==nSup&&(0,_utilsts.deepEqual)(nSub,nSup))return true;const{branches:subBranches}=(0,_subsetcheckerts.getBranchesTyped)(nSub);if(subBranches.length>1||subBranches[0]!==nSub){return subBranches.every(branch=>(0,_subsetcheckerts.isAtomicSubsetOf)(branch,nSup,this.engine))}return(0,_subsetcheckerts.isAtomicSubsetOf)(nSub,nSup,this.engine)}check(sub,sup,options){if(options){return this.checkWithOptions(sub,sup,options)}return this.checkInternal(sub,sup)}isEqual(a,b){return this.engine.isEqual((0,_normalizerts.normalize)(a),(0,_normalizerts.normalize)(b))}intersect(a,b){if(a===b||(0,_utilsts.deepEqual)(a,b))return(0,_normalizerts.normalize)(a);const nA=(0,_normalizerts.normalize)(a);const nB=(0,_normalizerts.normalize)(b);if((0,_utilsts.deepEqual)(nA,nB))return nA;const merged=this.engine.merge(nA,nB);if(merged===null)return null;if((0,_utilsts.deepEqual)(merged,nA)||(0,_utilsts.deepEqual)(merged,nB))return merged;return(0,_normalizerts.normalize)(merged)}normalize(def){return(0,_normalizerts.normalize)(def)}formatResult(label,result){return(0,_formatterts.formatResult)(label,result)}resolveConditions(schema,data){return(0,_conditionresolverts.resolveConditions)(schema,data,this.engine)}async checkWithOptions(sub,sup,options){const data=options.data;const constraintContext=options.constraintContext;const{sub:validateSub,sup:validateSup,partialSub,partialSup}=(0,_validatetargetsts.resolveValidateTargets)(options.validate);const dataForConditions=(0,_utilsts.isPlainObj)(data)?data:{};const resolvedSub=(0,_conditionresolverts.resolveConditions)(sub,dataForConditions,this.engine);const resolvedSup=(0,_conditionresolverts.resolveConditions)(sup,dataForConditions,this.engine);const canNarrow=data!==undefined;const canNarrowSub=canNarrow&&(0,_utilsts.isPlainObj)(resolvedSub.resolved);const canNarrowSup=canNarrow&&(0,_utilsts.isPlainObj)(resolvedSup.resolved);const narrowedSubResolved=canNarrowSub?(0,_datanarrowingts.narrowSchemaWithData)(resolvedSub.resolved,data,resolvedSup.resolved):resolvedSub.resolved;const narrowedSupResolved=canNarrowSup?(0,_datanarrowingts.narrowSchemaWithData)(resolvedSup.resolved,data,resolvedSub.resolved):resolvedSup.resolved;const staticResult=this.checkInternal(narrowedSubResolved,narrowedSupResolved);if(!staticResult.isSubset){return{...staticResult,resolvedSub:{...resolvedSub,resolved:narrowedSubResolved},resolvedSup:{...resolvedSup,resolved:narrowedSupResolved}}}if((validateSub||validateSup)&&data!==undefined){const runtimeErrors=[];if(validateSub){const getErrors=partialSub?_runtimevalidatorts.getPartialRuntimeValidationErrors:_runtimevalidatorts.getRuntimeValidationErrors;runtimeErrors.push(...this.prefixRuntimeErrors(getErrors(narrowedSubResolved,data),"$sub"))}if(validateSup){const getErrors=partialSup?_runtimevalidatorts.getPartialRuntimeValidationErrors:_runtimevalidatorts.getRuntimeValidationErrors;runtimeErrors.push(...this.prefixRuntimeErrors(getErrors(narrowedSupResolved,data),"$sup"))}if(validateSub){runtimeErrors.push(...this.prefixRuntimeErrors(await (0,_constraintvalidatorts.validateSchemaConstraints)(narrowedSubResolved,data,this.constraintValidators,constraintContext),"$sub"))}if(validateSup){runtimeErrors.push(...this.prefixRuntimeErrors(await (0,_constraintvalidatorts.validateSchemaConstraints)(narrowedSupResolved,data,this.constraintValidators,constraintContext),"$sup"))}if(runtimeErrors.length>0){return{isSubset:false,merged:null,errors:runtimeErrors,resolvedSub:{...resolvedSub,resolved:narrowedSubResolved},resolvedSup:{...resolvedSup,resolved:narrowedSupResolved}}}}return{...staticResult,resolvedSub:{...resolvedSub,resolved:narrowedSubResolved},resolvedSup:{...resolvedSup,resolved:narrowedSupResolved}}}prefixRuntimeErrors(errors,rootKey){return errors.map(error=>({...error,key:error.key==="$root"?rootKey:`${rootKey}.${error.key}`}))}checkInternal(sub,sup){if(sub===sup){return{isSubset:true,merged:sub,errors:[]}}if((0,_utilsts.deepEqual)(sub,sup)){return{isSubset:true,merged:sub,errors:[]}}const nSub=(0,_normalizerts.normalize)(sub);const nSup=(0,_normalizerts.normalize)(sup);if((0,_utilsts.deepEqual)(nSub,nSup)){return{isSubset:true,merged:nSub,errors:[]}}const{branches:subBranches,type:subBranchType}=(0,_subsetcheckerts.getBranchesTyped)(nSub);const{branches:supBranches,type:supBranchType}=(0,_subsetcheckerts.getBranchesTyped)(nSup);if(subBranches.length>1||subBranches[0]!==nSub){return(0,_subsetcheckerts.checkBranchedSub)(subBranches,nSup,this.engine,subBranchType)}if(supBranches.length>1||supBranches[0]!==nSup){return(0,_subsetcheckerts.checkBranchedSup)(nSub,supBranches,this.engine,supBranchType)}return(0,_subsetcheckerts.checkAtomic)(nSub,nSup,this.engine)}static clearCache(){(0,_runtimevalidatorts.clearAllValidatorCaches)()}constructor(options){_define_property(this,"constraintValidators",void 0);_define_property(this,"engine",void 0);this.engine=new _mergeenginets.MergeEngine;this.constraintValidators=options?.constraints??{}}}
2
2
  //# sourceMappingURL=json-schema-compatibility-checker.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/json-schema-compatibility-checker.ts"],"sourcesContent":["import type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\nimport { resolveConditions } from \"./condition-resolver.ts\";\nimport { validateSchemaConstraints } from \"./constraint-validator.ts\";\nimport { narrowSchemaWithData } from \"./data-narrowing.ts\";\nimport { formatResult } from \"./formatter.ts\";\nimport { MergeEngine } from \"./merge-engine.ts\";\nimport { normalize } from \"./normalizer.ts\";\nimport {\n\tarePatternsEquivalent,\n\tisPatternSubset,\n\tisTrivialPattern,\n} from \"./pattern-subset.ts\";\nimport {\n\tclearAllValidatorCaches,\n\tgetPartialRuntimeValidationErrors,\n\tgetRuntimeValidationErrors,\n} from \"./runtime-validator.ts\";\nimport type { BranchResult, BranchType } from \"./subset-checker.ts\";\nimport {\n\tcheckAtomic,\n\tcheckBranchedSub,\n\tcheckBranchedSup,\n\tgetBranchesTyped,\n\tisAtomicSubsetOf,\n} from \"./subset-checker.ts\";\nimport type {\n\tCheckerOptions,\n\tCheckRuntimeOptions,\n\tConstraintValidatorRegistry,\n\tResolvedConditionResult,\n\tResolvedSubsetResult,\n\tSchemaError,\n\tSubsetResult,\n\tValidateTargets,\n} from \"./types.ts\";\nimport { SchemaErrorType } from \"./types.ts\";\nimport { deepEqual, isPlainObj } from \"./utils.ts\";\nimport { resolveValidateTargets } from \"./validate-targets.ts\";\n\n// ─── Re-exports ──────────────────────────────────────────────────────────────\n\nexport type {\n\tSchemaError,\n\tSubsetResult,\n\tResolvedConditionResult,\n\tResolvedSubsetResult,\n\tCheckRuntimeOptions,\n\tValidateTargets,\n\tBranchType,\n\tBranchResult,\n};\n\nexport {\n\tSchemaErrorType,\n\tnormalize,\n\tresolveConditions,\n\tformatResult,\n\tMergeEngine,\n\tisPatternSubset,\n\tarePatternsEquivalent,\n\tisTrivialPattern,\n};\n\n// ─── Main Class ──────────────────────────────────────────────────────────────\n//\n// Lightweight facade that orchestrates sub-modules to verify compatibility\n// between JSON Schemas (Draft-07).\n//\n// Mathematical principle:\n// A ⊆ B ⟺ A ∩ B ≡ A\n//\n// In JSON Schema terms:\n// - A ∩ B = allOf([A, B]) resolved via merge\n// - ≡ = structural comparison\n//\n// @example\n// ```ts\n// const checker = new JsonSchemaCompatibilityChecker();\n//\n// checker.isSubset(strict, loose); // true\n// checker.check(loose, strict); // { isSubset: false, diffs: [...] }\n// checker.check(sub, sup, { data: {...} }); // resolves conditions then checks\n// ```\n\nexport class JsonSchemaCompatibilityChecker {\n\tprivate readonly constraintValidators: ConstraintValidatorRegistry;\n\tprivate readonly engine: MergeEngine;\n\n\tconstructor(options?: CheckerOptions) {\n\t\tthis.engine = new MergeEngine();\n\t\tthis.constraintValidators = options?.constraints ?? {};\n\t}\n\n\t// ── Subset check (boolean) ─────────────────────────────────────────────\n\n\t/**\n\t * Checks whether `sub ⊆ sup`.\n\t * Is every value valid for sub also valid for sup?\n\t *\n\t * Uses `getBranchesTyped` to distinguish `anyOf` from `oneOf`\n\t * internally, although the boolean result does not reflect the distinction.\n\t */\n\tisSubset(sub: JSONSchema7Definition, sup: JSONSchema7Definition): boolean {\n\t\t// ── Identity short-circuit ──\n\t\t// If sub and sup are the same reference, sub ⊆ sup is trivially true.\n\t\t// This avoids the entire normalize + merge + compare pipeline.\n\t\tif (sub === sup) return true;\n\n\t\t// ── Pre-normalize structural equality ──\n\t\t// If sub and sup are structurally identical before normalization,\n\t\t// they represent the same schema → sub ⊆ sup trivially.\n\t\t// This avoids the WeakMap overhead of normalize() for common cases\n\t\t// like {} ⊆ {} or identical schema objects with different references.\n\t\tif (deepEqual(sub, sup)) return true;\n\n\t\tconst nSub = normalize(sub);\n\t\tconst nSup = normalize(sup);\n\n\t\t// ── Post-normalize structural identity ──\n\t\t// After normalization, schemas that were syntactically different\n\t\t// but semantically equivalent become structurally equal\n\t\t// (e.g. {const:1} vs {const:1, type:\"integer\"}).\n\t\tif (nSub !== sub && nSup !== sup && deepEqual(nSub, nSup)) return true;\n\t\tif (nSub !== nSup && deepEqual(nSub, nSup)) return true;\n\n\t\tconst { branches: subBranches } = getBranchesTyped(nSub);\n\n\t\tif (subBranches.length > 1 || subBranches[0] !== nSub) {\n\t\t\treturn subBranches.every((branch) =>\n\t\t\t\tisAtomicSubsetOf(branch, nSup, this.engine),\n\t\t\t);\n\t\t}\n\n\t\treturn isAtomicSubsetOf(nSub, nSup, this.engine);\n\t}\n\n\t// ── Subset check (detailed) ────────────────────────────────────────────\n\n\t/**\n\t * Checks `sub ⊆ sup` and returns a detailed diagnostic\n\t * with human-readable semantic errors.\n\t *\n\t * When `options` is provided, both schemas go through runtime-aware\n\t * processing before the static check:\n\t * 1. Conditions (`if/then/else`) are resolved using `data`\n\t * (if `data` is `undefined`, conditions are resolved with `{}`)\n\t * 2. Schemas are narrowed using runtime values (enum materialization)\n\t * 3. The static subset check runs on the resolved/narrowed schemas\n\t *\n\t * When `validate` is enabled, additional runtime steps run **after** the\n\t * static check passes:\n\t * 4. `data` is validated against the targeted resolved schema(s) via AJV\n\t * 5. Custom constraints are validated against `data` for the targeted schema(s)\n\t *\n\t * `validate` accepts:\n\t * - `true` — validate against **both** sub and sup\n\t * - `{ sub: true }` — validate only against the sub schema\n\t * - `{ sup: true }` — validate only against the sup schema\n\t * - `{ sub: true, sup: true }` — equivalent to `true`\n\t *\n\t * @param sub - The source schema (subset candidate)\n\t * @param sup - The target schema (expected superset)\n\t * @param options - Runtime options with `data` and optional `validate` flag\n\t * @returns SubsetResult if no options, ResolvedSubsetResult if options provided\n\t *\n\t * @example\n\t * ```ts\n\t * // Static check (no runtime data)\n\t * checker.check(sub, sup);\n\t *\n\t * // Resolve conditions + narrowing + static check (no runtime validation)\n\t * checker.check(sub, sup, { data: { kind: \"text\", value: \"hello\" } });\n\t *\n\t * // Full pipeline including AJV + constraint runtime validation\n\t * checker.check(sub, sup, { data: { kind: \"text\", value: \"hello\" }, validate: true });\n\t * ```\n\t */\n\tcheck(\n\t\tsub: JSONSchema7Definition,\n\t\tsup: JSONSchema7Definition,\n\t\toptions: CheckRuntimeOptions,\n\t): Promise<ResolvedSubsetResult>;\n\tcheck(sub: JSONSchema7Definition, sup: JSONSchema7Definition): SubsetResult;\n\tcheck(\n\t\tsub: JSONSchema7Definition,\n\t\tsup: JSONSchema7Definition,\n\t\toptions?: CheckRuntimeOptions,\n\t): SubsetResult | Promise<ResolvedSubsetResult> {\n\t\t// ── Runtime-aware path ──\n\t\tif (options) {\n\t\t\treturn this.checkWithOptions(sub, sup, options);\n\t\t}\n\n\t\t// ── Standard path (no condition resolution) ──\n\t\treturn this.checkInternal(sub, sup);\n\t}\n\n\t// ── Equality ───────────────────────────────────────────────────────────\n\n\t/**\n\t * Checks structural equality between two schemas.\n\t */\n\tisEqual(a: JSONSchema7Definition, b: JSONSchema7Definition): boolean {\n\t\treturn this.engine.isEqual(normalize(a), normalize(b));\n\t}\n\n\t// ── Intersection ───────────────────────────────────────────────────────\n\n\t/**\n\t * Computes the intersection of two schemas (allOf merge).\n\t * Returns null if the schemas are incompatible.\n\t *\n\t * The result is normalized to eliminate structural artifacts\n\t * from the merge (e.g. redundant `enum` when `const` is present).\n\t */\n\tintersect(\n\t\ta: JSONSchema7Definition,\n\t\tb: JSONSchema7Definition,\n\t): JSONSchema7Definition | null {\n\t\t// ── Identity short-circuit ──\n\t\t// If a and b are the same reference or structurally equal,\n\t\t// intersection is just normalize(a) — skip the merge entirely.\n\t\tif (a === b || deepEqual(a, b)) return normalize(a);\n\n\t\tconst nA = normalize(a);\n\t\tconst nB = normalize(b);\n\n\t\t// ── Post-normalize identity ──\n\t\tif (deepEqual(nA, nB)) return nA;\n\n\t\tconst merged = this.engine.merge(nA, nB);\n\t\tif (merged === null) return null;\n\t\t// Fast path: if merge result equals one of the normalized inputs,\n\t\t// it's already normalized — skip redundant normalize call.\n\t\tif (deepEqual(merged, nA) || deepEqual(merged, nB)) return merged;\n\t\treturn normalize(merged);\n\t}\n\n\t// ── Normalization ──────────────────────────────────────────────────────\n\n\t/**\n\t * Normalizes a schema: infers `type` from `const`/`enum`,\n\t * and recursively normalizes all sub-schemas.\n\t */\n\tnormalize(def: JSONSchema7Definition): JSONSchema7Definition {\n\t\treturn normalize(def);\n\t}\n\n\t// ── Formatting ─────────────────────────────────────────────────────────\n\n\t/**\n\t * Formats a SubsetResult into a readable string (useful for logs/debug).\n\t */\n\tformatResult(label: string, result: SubsetResult): string {\n\t\treturn formatResult(label, result);\n\t}\n\n\t// ── Condition Resolution ────────────────────────────────────────────────\n\n\t/**\n\t * Resolves `if/then/else` conditions in a schema by evaluating the `if`\n\t * against runtime data.\n\t *\n\t * @param schema - The schema containing conditions to resolve\n\t * @param data - The runtime data used to evaluate conditions\n\t * @returns The resolved schema with branch info and discriminants\n\t */\n\tresolveConditions(\n\t\tschema: JSONSchema7,\n\t\tdata: Record<string, unknown>,\n\t): ResolvedConditionResult {\n\t\treturn resolveConditions(schema, data, this.engine);\n\t}\n\n\t// ── Private ────────────────────────────────────────────────────────────\n\n\t/**\n\t * Internal runtime-aware check logic. Extracted as an async method\n\t * so that `check()` without options stays synchronous while the\n\t * runtime path can `await` async constraint validators.\n\t */\n\tprivate async checkWithOptions(\n\t\tsub: JSONSchema7Definition,\n\t\tsup: JSONSchema7Definition,\n\t\toptions: CheckRuntimeOptions,\n\t): Promise<ResolvedSubsetResult> {\n\t\tconst data = options.data;\n\t\tconst {\n\t\t\tsub: validateSub,\n\t\t\tsup: validateSup,\n\t\t\tpartialSub,\n\t\t\tpartialSup,\n\t\t} = resolveValidateTargets(options.validate);\n\n\t\t// resolveConditions expects Record<string, unknown> for property access;\n\t\t// coerce non-object / undefined data to empty object so conditions\n\t\t// are always resolved (v1.0.11 compat: subData: undefined → {})\n\t\tconst dataForConditions: Record<string, unknown> = isPlainObj(data)\n\t\t\t? data\n\t\t\t: {};\n\n\t\tconst resolvedSub = resolveConditions(\n\t\t\tsub as JSONSchema7,\n\t\t\tdataForConditions,\n\t\t\tthis.engine,\n\t\t);\n\t\tconst resolvedSup = resolveConditions(\n\t\t\tsup as JSONSchema7,\n\t\t\tdataForConditions,\n\t\t\tthis.engine,\n\t\t);\n\n\t\t// ── Runtime-aware data narrowing ──\n\t\t// Apply narrowing only when concrete data is available.\n\t\t// When data is undefined there is nothing to narrow with.\n\t\t// Boolean schemas (true/false) cannot be narrowed — skip narrowing\n\t\t// to avoid passing a non-object to narrowSchemaWithData.\n\t\tconst canNarrow = data !== undefined;\n\t\tconst canNarrowSub = canNarrow && isPlainObj(resolvedSub.resolved);\n\t\tconst canNarrowSup = canNarrow && isPlainObj(resolvedSup.resolved);\n\n\t\tconst narrowedSubResolved = canNarrowSub\n\t\t\t? narrowSchemaWithData(resolvedSub.resolved, data, resolvedSup.resolved)\n\t\t\t: resolvedSub.resolved;\n\n\t\tconst narrowedSupResolved = canNarrowSup\n\t\t\t? narrowSchemaWithData(resolvedSup.resolved, data, resolvedSub.resolved)\n\t\t\t: resolvedSup.resolved;\n\n\t\t// ── Static subset check ──\n\t\t// Structural incompatibilities are schema-level problems — they are\n\t\t// permanent regardless of the concrete data. Run this before runtime\n\t\t// validation so that static errors always surface with higher priority.\n\t\tconst staticResult = this.checkInternal(\n\t\t\tnarrowedSubResolved,\n\t\t\tnarrowedSupResolved,\n\t\t);\n\n\t\tif (!staticResult.isSubset) {\n\t\t\treturn {\n\t\t\t\t...staticResult,\n\t\t\t\tresolvedSub: { ...resolvedSub, resolved: narrowedSubResolved },\n\t\t\t\tresolvedSup: { ...resolvedSup, resolved: narrowedSupResolved },\n\t\t\t};\n\t\t}\n\n\t\t// ── Runtime validation (opt-in) ──\n\t\t// Runs when `validate` is truthy (boolean or object with sub/sup flags).\n\t\t// Validates the concrete data against the targeted resolved/narrowed\n\t\t// schema(s) via AJV, then runs custom constraint validators if registered.\n\t\tif ((validateSub || validateSup) && data !== undefined) {\n\t\t\tconst runtimeErrors: SchemaError[] = [];\n\n\t\t\t// ── AJV validation ──\n\t\t\t// When partial mode is active for a target, use\n\t\t\t// getPartialRuntimeValidationErrors which strips `required` and\n\t\t\t// `additionalProperties` before AJV compilation so that only\n\t\t\t// the properties present in data are validated.\n\t\t\tif (validateSub) {\n\t\t\t\tconst getErrors = partialSub\n\t\t\t\t\t? getPartialRuntimeValidationErrors\n\t\t\t\t\t: getRuntimeValidationErrors;\n\t\t\t\truntimeErrors.push(\n\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\tgetErrors(narrowedSubResolved, data),\n\t\t\t\t\t\t\"$sub\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (validateSup) {\n\t\t\t\tconst getErrors = partialSup\n\t\t\t\t\t? getPartialRuntimeValidationErrors\n\t\t\t\t\t: getRuntimeValidationErrors;\n\t\t\t\truntimeErrors.push(\n\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\tgetErrors(narrowedSupResolved, data),\n\t\t\t\t\t\t\"$sup\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// ── Constraint validation ──\n\t\t\t// Validate runtime data against custom constraints in the targeted\n\t\t\t// schema(s). If a schema declares constraints that are not registered\n\t\t\t// in the registry, validateSchemaConstraints will report them as\n\t\t\t// \"unknown constraint (not registered)\" errors.\n\t\t\t// Constraint validators may be async, so we await the results.\n\t\t\tif (validateSub) {\n\t\t\t\truntimeErrors.push(\n\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\tawait validateSchemaConstraints(\n\t\t\t\t\t\t\tnarrowedSubResolved,\n\t\t\t\t\t\t\tdata,\n\t\t\t\t\t\t\tthis.constraintValidators,\n\t\t\t\t\t\t),\n\t\t\t\t\t\t\"$sub\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (validateSup) {\n\t\t\t\truntimeErrors.push(\n\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\tawait validateSchemaConstraints(\n\t\t\t\t\t\t\tnarrowedSupResolved,\n\t\t\t\t\t\t\tdata,\n\t\t\t\t\t\t\tthis.constraintValidators,\n\t\t\t\t\t\t),\n\t\t\t\t\t\t\"$sup\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (runtimeErrors.length > 0) {\n\t\t\t\treturn {\n\t\t\t\t\tisSubset: false,\n\t\t\t\t\tmerged: null,\n\t\t\t\t\terrors: runtimeErrors,\n\t\t\t\t\tresolvedSub: { ...resolvedSub, resolved: narrowedSubResolved },\n\t\t\t\t\tresolvedSup: { ...resolvedSup, resolved: narrowedSupResolved },\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\t...staticResult,\n\t\t\tresolvedSub: { ...resolvedSub, resolved: narrowedSubResolved },\n\t\t\tresolvedSup: { ...resolvedSup, resolved: narrowedSupResolved },\n\t\t};\n\t}\n\n\tprivate prefixRuntimeErrors(\n\t\terrors: SchemaError[],\n\t\trootKey: \"$sub\" | \"$sup\",\n\t): SchemaError[] {\n\t\treturn errors.map((error) => ({\n\t\t\t...error,\n\t\t\tkey: error.key === \"$root\" ? rootKey : `${rootKey}.${error.key}`,\n\t\t}));\n\t}\n\n\t/**\n\t * Internal check logic without condition resolution.\n\t * Factorizes the normalize → branch → atomic pipeline to avoid\n\t * duplication between the two paths of `check()`.\n\t */\n\tprivate checkInternal(\n\t\tsub: JSONSchema7Definition,\n\t\tsup: JSONSchema7Definition,\n\t): SubsetResult {\n\t\t// ── Identity short-circuit ──\n\t\t// Same reference → no errors, no merge needed.\n\t\tif (sub === sup) {\n\t\t\treturn { isSubset: true, merged: sub, errors: [] };\n\t\t}\n\n\t\t// ── Pre-normalize structural equality ──\n\t\t// Avoids WeakMap overhead for identical schemas ({} ⊆ {}, etc.).\n\t\tif (deepEqual(sub, sup)) {\n\t\t\treturn { isSubset: true, merged: sub, errors: [] };\n\t\t}\n\n\t\tconst nSub = normalize(sub);\n\t\tconst nSup = normalize(sup);\n\n\t\t// ── Post-normalize structural identity ──\n\t\t// Catches semantically equivalent schemas after normalization.\n\t\tif (deepEqual(nSub, nSup)) {\n\t\t\treturn { isSubset: true, merged: nSub, errors: [] };\n\t\t}\n\n\t\tconst { branches: subBranches, type: subBranchType } =\n\t\t\tgetBranchesTyped(nSub);\n\t\tconst { branches: supBranches, type: supBranchType } =\n\t\t\tgetBranchesTyped(nSup);\n\n\t\t// anyOf/oneOf in sub\n\t\tif (subBranches.length > 1 || subBranches[0] !== nSub) {\n\t\t\treturn checkBranchedSub(subBranches, nSup, this.engine, subBranchType);\n\t\t}\n\n\t\t// anyOf/oneOf in sup only\n\t\tif (supBranches.length > 1 || supBranches[0] !== nSup) {\n\t\t\treturn checkBranchedSup(nSub, supBranches, this.engine, supBranchType);\n\t\t}\n\n\t\t// Standard case\n\t\treturn checkAtomic(nSub, nSup, this.engine);\n\t}\n\n\t// ── Cache management ───────────────────────────────────────────────────\n\n\t/**\n\t * Clears all compiled AJV validator caches (WeakMap, LRU, and AJV internal).\n\t *\n\t * Useful for:\n\t * - Long-running processes where schemas evolve over time\n\t * - Test isolation (ensuring no cross-test cache pollution)\n\t * - Memory pressure situations where cached validators are no longer needed\n\t *\n\t * After calling this, the next validation call will recompile validators\n\t * from scratch — there is a one-time performance cost per unique schema.\n\t *\n\t * This is a static method because the AJV instance is a module-level\n\t * singleton shared across all `JsonSchemaCompatibilityChecker` instances.\n\t *\n\t * @example\n\t * ```ts\n\t * JsonSchemaCompatibilityChecker.clearCache();\n\t * ```\n\t */\n\tstatic clearCache(): void {\n\t\tclearAllValidatorCaches();\n\t}\n}\n"],"names":["JsonSchemaCompatibilityChecker","MergeEngine","SchemaErrorType","arePatternsEquivalent","formatResult","isPatternSubset","isTrivialPattern","normalize","resolveConditions","isSubset","sub","sup","deepEqual","nSub","nSup","branches","subBranches","getBranchesTyped","length","every","branch","isAtomicSubsetOf","engine","check","options","checkWithOptions","checkInternal","isEqual","a","b","intersect","nA","nB","merged","merge","def","label","result","schema","data","validateSub","validateSup","partialSub","partialSup","resolveValidateTargets","validate","dataForConditions","isPlainObj","resolvedSub","resolvedSup","canNarrow","undefined","canNarrowSub","resolved","canNarrowSup","narrowedSubResolved","narrowSchemaWithData","narrowedSupResolved","staticResult","runtimeErrors","getErrors","getPartialRuntimeValidationErrors","getRuntimeValidationErrors","push","prefixRuntimeErrors","validateSchemaConstraints","constraintValidators","errors","rootKey","map","error","key","type","subBranchType","supBranches","supBranchType","checkBranchedSub","checkBranchedSup","checkAtomic","clearCache","clearAllValidatorCaches","constraints"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/json-schema-compatibility-checker.ts"],"sourcesContent":["import type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\nimport { resolveConditions } from \"./condition-resolver.ts\";\nimport { validateSchemaConstraints } from \"./constraint-validator.ts\";\nimport { narrowSchemaWithData } from \"./data-narrowing.ts\";\nimport { formatResult } from \"./formatter.ts\";\nimport { MergeEngine } from \"./merge-engine.ts\";\nimport { normalize } from \"./normalizer.ts\";\nimport {\n\tarePatternsEquivalent,\n\tisPatternSubset,\n\tisTrivialPattern,\n} from \"./pattern-subset.ts\";\nimport {\n\tclearAllValidatorCaches,\n\tgetPartialRuntimeValidationErrors,\n\tgetRuntimeValidationErrors,\n} from \"./runtime-validator.ts\";\nimport type { BranchResult, BranchType } from \"./subset-checker.ts\";\nimport {\n\tcheckAtomic,\n\tcheckBranchedSub,\n\tcheckBranchedSup,\n\tgetBranchesTyped,\n\tisAtomicSubsetOf,\n} from \"./subset-checker.ts\";\nimport type {\n\tCheckerOptions,\n\tCheckRuntimeOptions,\n\tConstraintExecutionContext,\n\tConstraintValidatorRegistry,\n\tResolvedConditionResult,\n\tResolvedSubsetResult,\n\tSchemaError,\n\tSubsetResult,\n\tValidateTargets,\n} from \"./types.ts\";\nimport { SchemaErrorType } from \"./types.ts\";\nimport { deepEqual, isPlainObj } from \"./utils.ts\";\nimport { resolveValidateTargets } from \"./validate-targets.ts\";\n\n// ─── Re-exports ──────────────────────────────────────────────────────────────\n\nexport type {\n\tSchemaError,\n\tSubsetResult,\n\tResolvedConditionResult,\n\tResolvedSubsetResult,\n\tCheckRuntimeOptions,\n\tValidateTargets,\n\tBranchType,\n\tBranchResult,\n};\n\nexport {\n\tSchemaErrorType,\n\tnormalize,\n\tresolveConditions,\n\tformatResult,\n\tMergeEngine,\n\tisPatternSubset,\n\tarePatternsEquivalent,\n\tisTrivialPattern,\n};\n\n// ─── Main Class ──────────────────────────────────────────────────────────────\n//\n// Lightweight facade that orchestrates sub-modules to verify compatibility\n// between JSON Schemas (Draft-07).\n//\n// Mathematical principle:\n// A ⊆ B ⟺ A ∩ B ≡ A\n//\n// In JSON Schema terms:\n// - A ∩ B = allOf([A, B]) resolved via merge\n// - ≡ = structural comparison\n//\n// @example\n// ```ts\n// const checker = new JsonSchemaCompatibilityChecker();\n//\n// checker.isSubset(strict, loose); // true\n// checker.check(loose, strict); // { isSubset: false, diffs: [...] }\n// checker.check(sub, sup, { data: {...} }); // resolves conditions then checks\n// ```\n\nexport class JsonSchemaCompatibilityChecker {\n\tprivate readonly constraintValidators: ConstraintValidatorRegistry;\n\tprivate readonly engine: MergeEngine;\n\n\tconstructor(options?: CheckerOptions) {\n\t\tthis.engine = new MergeEngine();\n\t\tthis.constraintValidators = options?.constraints ?? {};\n\t}\n\n\t// ── Subset check (boolean) ─────────────────────────────────────────────\n\n\t/**\n\t * Checks whether `sub ⊆ sup`.\n\t * Is every value valid for sub also valid for sup?\n\t *\n\t * Uses `getBranchesTyped` to distinguish `anyOf` from `oneOf`\n\t * internally, although the boolean result does not reflect the distinction.\n\t */\n\tisSubset(sub: JSONSchema7Definition, sup: JSONSchema7Definition): boolean {\n\t\t// ── Identity short-circuit ──\n\t\t// If sub and sup are the same reference, sub ⊆ sup is trivially true.\n\t\t// This avoids the entire normalize + merge + compare pipeline.\n\t\tif (sub === sup) return true;\n\n\t\t// ── Pre-normalize structural equality ──\n\t\t// If sub and sup are structurally identical before normalization,\n\t\t// they represent the same schema → sub ⊆ sup trivially.\n\t\t// This avoids the WeakMap overhead of normalize() for common cases\n\t\t// like {} ⊆ {} or identical schema objects with different references.\n\t\tif (deepEqual(sub, sup)) return true;\n\n\t\tconst nSub = normalize(sub);\n\t\tconst nSup = normalize(sup);\n\n\t\t// ── Post-normalize structural identity ──\n\t\t// After normalization, schemas that were syntactically different\n\t\t// but semantically equivalent become structurally equal\n\t\t// (e.g. {const:1} vs {const:1, type:\"integer\"}).\n\t\tif (nSub !== sub && nSup !== sup && deepEqual(nSub, nSup)) return true;\n\t\tif (nSub !== nSup && deepEqual(nSub, nSup)) return true;\n\n\t\tconst { branches: subBranches } = getBranchesTyped(nSub);\n\n\t\tif (subBranches.length > 1 || subBranches[0] !== nSub) {\n\t\t\treturn subBranches.every((branch) =>\n\t\t\t\tisAtomicSubsetOf(branch, nSup, this.engine),\n\t\t\t);\n\t\t}\n\n\t\treturn isAtomicSubsetOf(nSub, nSup, this.engine);\n\t}\n\n\t// ── Subset check (detailed) ────────────────────────────────────────────\n\n\t/**\n\t * Checks `sub ⊆ sup` and returns a detailed diagnostic\n\t * with human-readable semantic errors.\n\t *\n\t * When `options` is provided, both schemas go through runtime-aware\n\t * processing before the static check:\n\t * 1. Conditions (`if/then/else`) are resolved using `data`\n\t * (if `data` is `undefined`, conditions are resolved with `{}`)\n\t * 2. Schemas are narrowed using runtime values (enum materialization)\n\t * 3. The static subset check runs on the resolved/narrowed schemas\n\t *\n\t * When `validate` is enabled, additional runtime steps run **after** the\n\t * static check passes:\n\t * 4. `data` is validated against the targeted resolved schema(s) via AJV\n\t * 5. Custom constraints are validated against `data` for the targeted schema(s)\n\t *\n\t * `validate` accepts:\n\t * - `true` — validate against **both** sub and sup\n\t * - `{ sub: true }` — validate only against the sub schema\n\t * - `{ sup: true }` — validate only against the sup schema\n\t * - `{ sub: true, sup: true }` — equivalent to `true`\n\t *\n\t * @param sub - The source schema (subset candidate)\n\t * @param sup - The target schema (expected superset)\n\t * @param options - Runtime options with `data` and optional `validate` flag\n\t * @returns SubsetResult if no options, ResolvedSubsetResult if options provided\n\t *\n\t * @example\n\t * ```ts\n\t * // Static check (no runtime data)\n\t * checker.check(sub, sup);\n\t *\n\t * // Resolve conditions + narrowing + static check (no runtime validation)\n\t * checker.check(sub, sup, { data: { kind: \"text\", value: \"hello\" } });\n\t *\n\t * // Full pipeline including AJV + constraint runtime validation\n\t * checker.check(sub, sup, { data: { kind: \"text\", value: \"hello\" }, validate: true });\n\t * ```\n\t */\n\tcheck(\n\t\tsub: JSONSchema7Definition,\n\t\tsup: JSONSchema7Definition,\n\t\toptions: CheckRuntimeOptions,\n\t): Promise<ResolvedSubsetResult>;\n\tcheck(sub: JSONSchema7Definition, sup: JSONSchema7Definition): SubsetResult;\n\tcheck(\n\t\tsub: JSONSchema7Definition,\n\t\tsup: JSONSchema7Definition,\n\t\toptions?: CheckRuntimeOptions,\n\t): SubsetResult | Promise<ResolvedSubsetResult> {\n\t\t// ── Runtime-aware path ──\n\t\tif (options) {\n\t\t\treturn this.checkWithOptions(sub, sup, options);\n\t\t}\n\n\t\t// ── Standard path (no condition resolution) ──\n\t\treturn this.checkInternal(sub, sup);\n\t}\n\n\t// ── Equality ───────────────────────────────────────────────────────────\n\n\t/**\n\t * Checks structural equality between two schemas.\n\t */\n\tisEqual(a: JSONSchema7Definition, b: JSONSchema7Definition): boolean {\n\t\treturn this.engine.isEqual(normalize(a), normalize(b));\n\t}\n\n\t// ── Intersection ───────────────────────────────────────────────────────\n\n\t/**\n\t * Computes the intersection of two schemas (allOf merge).\n\t * Returns null if the schemas are incompatible.\n\t *\n\t * The result is normalized to eliminate structural artifacts\n\t * from the merge (e.g. redundant `enum` when `const` is present).\n\t */\n\tintersect(\n\t\ta: JSONSchema7Definition,\n\t\tb: JSONSchema7Definition,\n\t): JSONSchema7Definition | null {\n\t\t// ── Identity short-circuit ──\n\t\t// If a and b are the same reference or structurally equal,\n\t\t// intersection is just normalize(a) — skip the merge entirely.\n\t\tif (a === b || deepEqual(a, b)) return normalize(a);\n\n\t\tconst nA = normalize(a);\n\t\tconst nB = normalize(b);\n\n\t\t// ── Post-normalize identity ──\n\t\tif (deepEqual(nA, nB)) return nA;\n\n\t\tconst merged = this.engine.merge(nA, nB);\n\t\tif (merged === null) return null;\n\t\t// Fast path: if merge result equals one of the normalized inputs,\n\t\t// it's already normalized — skip redundant normalize call.\n\t\tif (deepEqual(merged, nA) || deepEqual(merged, nB)) return merged;\n\t\treturn normalize(merged);\n\t}\n\n\t// ── Normalization ──────────────────────────────────────────────────────\n\n\t/**\n\t * Normalizes a schema: infers `type` from `const`/`enum`,\n\t * and recursively normalizes all sub-schemas.\n\t */\n\tnormalize(def: JSONSchema7Definition): JSONSchema7Definition {\n\t\treturn normalize(def);\n\t}\n\n\t// ── Formatting ─────────────────────────────────────────────────────────\n\n\t/**\n\t * Formats a SubsetResult into a readable string (useful for logs/debug).\n\t */\n\tformatResult(label: string, result: SubsetResult): string {\n\t\treturn formatResult(label, result);\n\t}\n\n\t// ── Condition Resolution ────────────────────────────────────────────────\n\n\t/**\n\t * Resolves `if/then/else` conditions in a schema by evaluating the `if`\n\t * against runtime data.\n\t *\n\t * @param schema - The schema containing conditions to resolve\n\t * @param data - The runtime data used to evaluate conditions\n\t * @returns The resolved schema with branch info and discriminants\n\t */\n\tresolveConditions(\n\t\tschema: JSONSchema7,\n\t\tdata: Record<string, unknown>,\n\t): ResolvedConditionResult {\n\t\treturn resolveConditions(schema, data, this.engine);\n\t}\n\n\t// ── Private ────────────────────────────────────────────────────────────\n\n\t/**\n\t * Internal runtime-aware check logic. Extracted as an async method\n\t * so that `check()` without options stays synchronous while the\n\t * runtime path can `await` async constraint validators.\n\t */\n\tprivate async checkWithOptions(\n\t\tsub: JSONSchema7Definition,\n\t\tsup: JSONSchema7Definition,\n\t\toptions: CheckRuntimeOptions,\n\t): Promise<ResolvedSubsetResult> {\n\t\tconst data = options.data;\n\t\tconst constraintContext: ConstraintExecutionContext | undefined =\n\t\t\toptions.constraintContext;\n\t\tconst {\n\t\t\tsub: validateSub,\n\t\t\tsup: validateSup,\n\t\t\tpartialSub,\n\t\t\tpartialSup,\n\t\t} = resolveValidateTargets(options.validate);\n\n\t\t// resolveConditions expects Record<string, unknown> for property access;\n\t\t// coerce non-object / undefined data to empty object so conditions\n\t\t// are always resolved (v1.0.11 compat: subData: undefined → {})\n\t\tconst dataForConditions: Record<string, unknown> = isPlainObj(data)\n\t\t\t? data\n\t\t\t: {};\n\n\t\tconst resolvedSub = resolveConditions(\n\t\t\tsub as JSONSchema7,\n\t\t\tdataForConditions,\n\t\t\tthis.engine,\n\t\t);\n\t\tconst resolvedSup = resolveConditions(\n\t\t\tsup as JSONSchema7,\n\t\t\tdataForConditions,\n\t\t\tthis.engine,\n\t\t);\n\n\t\t// ── Runtime-aware data narrowing ──\n\t\t// Apply narrowing only when concrete data is available.\n\t\t// When data is undefined there is nothing to narrow with.\n\t\t// Boolean schemas (true/false) cannot be narrowed — skip narrowing\n\t\t// to avoid passing a non-object to narrowSchemaWithData.\n\t\tconst canNarrow = data !== undefined;\n\t\tconst canNarrowSub = canNarrow && isPlainObj(resolvedSub.resolved);\n\t\tconst canNarrowSup = canNarrow && isPlainObj(resolvedSup.resolved);\n\n\t\tconst narrowedSubResolved = canNarrowSub\n\t\t\t? narrowSchemaWithData(resolvedSub.resolved, data, resolvedSup.resolved)\n\t\t\t: resolvedSub.resolved;\n\n\t\tconst narrowedSupResolved = canNarrowSup\n\t\t\t? narrowSchemaWithData(resolvedSup.resolved, data, resolvedSub.resolved)\n\t\t\t: resolvedSup.resolved;\n\n\t\t// ── Static subset check ──\n\t\t// Structural incompatibilities are schema-level problems — they are\n\t\t// permanent regardless of the concrete data. Run this before runtime\n\t\t// validation so that static errors always surface with higher priority.\n\t\tconst staticResult = this.checkInternal(\n\t\t\tnarrowedSubResolved,\n\t\t\tnarrowedSupResolved,\n\t\t);\n\n\t\tif (!staticResult.isSubset) {\n\t\t\treturn {\n\t\t\t\t...staticResult,\n\t\t\t\tresolvedSub: { ...resolvedSub, resolved: narrowedSubResolved },\n\t\t\t\tresolvedSup: { ...resolvedSup, resolved: narrowedSupResolved },\n\t\t\t};\n\t\t}\n\n\t\t// ── Runtime validation (opt-in) ──\n\t\t// Runs when `validate` is truthy (boolean or object with sub/sup flags).\n\t\t// Validates the concrete data against the targeted resolved/narrowed\n\t\t// schema(s) via AJV, then runs custom constraint validators if registered.\n\t\tif ((validateSub || validateSup) && data !== undefined) {\n\t\t\tconst runtimeErrors: SchemaError[] = [];\n\n\t\t\t// ── AJV validation ──\n\t\t\t// When partial mode is active for a target, use\n\t\t\t// getPartialRuntimeValidationErrors which strips `required` and\n\t\t\t// `additionalProperties` before AJV compilation so that only\n\t\t\t// the properties present in data are validated.\n\t\t\tif (validateSub) {\n\t\t\t\tconst getErrors = partialSub\n\t\t\t\t\t? getPartialRuntimeValidationErrors\n\t\t\t\t\t: getRuntimeValidationErrors;\n\t\t\t\truntimeErrors.push(\n\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\tgetErrors(narrowedSubResolved, data),\n\t\t\t\t\t\t\"$sub\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (validateSup) {\n\t\t\t\tconst getErrors = partialSup\n\t\t\t\t\t? getPartialRuntimeValidationErrors\n\t\t\t\t\t: getRuntimeValidationErrors;\n\t\t\t\truntimeErrors.push(\n\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\tgetErrors(narrowedSupResolved, data),\n\t\t\t\t\t\t\"$sup\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// ── Constraint validation ──\n\t\t\t// Validate runtime data against custom constraints in the targeted\n\t\t\t// schema(s). If a schema declares constraints that are not registered\n\t\t\t// in the registry, validateSchemaConstraints will report them as\n\t\t\t// \"unknown constraint (not registered)\" errors.\n\t\t\t// Constraint validators may be async, so we await the results.\n\t\t\tif (validateSub) {\n\t\t\t\truntimeErrors.push(\n\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\tawait validateSchemaConstraints(\n\t\t\t\t\t\t\tnarrowedSubResolved,\n\t\t\t\t\t\t\tdata,\n\t\t\t\t\t\t\tthis.constraintValidators,\n\t\t\t\t\t\t\tconstraintContext,\n\t\t\t\t\t\t),\n\t\t\t\t\t\t\"$sub\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (validateSup) {\n\t\t\t\truntimeErrors.push(\n\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\tawait validateSchemaConstraints(\n\t\t\t\t\t\t\tnarrowedSupResolved,\n\t\t\t\t\t\t\tdata,\n\t\t\t\t\t\t\tthis.constraintValidators,\n\t\t\t\t\t\t\tconstraintContext,\n\t\t\t\t\t\t),\n\t\t\t\t\t\t\"$sup\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (runtimeErrors.length > 0) {\n\t\t\t\treturn {\n\t\t\t\t\tisSubset: false,\n\t\t\t\t\tmerged: null,\n\t\t\t\t\terrors: runtimeErrors,\n\t\t\t\t\tresolvedSub: { ...resolvedSub, resolved: narrowedSubResolved },\n\t\t\t\t\tresolvedSup: { ...resolvedSup, resolved: narrowedSupResolved },\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\t...staticResult,\n\t\t\tresolvedSub: { ...resolvedSub, resolved: narrowedSubResolved },\n\t\t\tresolvedSup: { ...resolvedSup, resolved: narrowedSupResolved },\n\t\t};\n\t}\n\n\tprivate prefixRuntimeErrors(\n\t\terrors: SchemaError[],\n\t\trootKey: \"$sub\" | \"$sup\",\n\t): SchemaError[] {\n\t\treturn errors.map((error) => ({\n\t\t\t...error,\n\t\t\tkey: error.key === \"$root\" ? rootKey : `${rootKey}.${error.key}`,\n\t\t}));\n\t}\n\n\t/**\n\t * Internal check logic without condition resolution.\n\t * Factorizes the normalize → branch → atomic pipeline to avoid\n\t * duplication between the two paths of `check()`.\n\t */\n\tprivate checkInternal(\n\t\tsub: JSONSchema7Definition,\n\t\tsup: JSONSchema7Definition,\n\t): SubsetResult {\n\t\t// ── Identity short-circuit ──\n\t\t// Same reference → no errors, no merge needed.\n\t\tif (sub === sup) {\n\t\t\treturn { isSubset: true, merged: sub, errors: [] };\n\t\t}\n\n\t\t// ── Pre-normalize structural equality ──\n\t\t// Avoids WeakMap overhead for identical schemas ({} ⊆ {}, etc.).\n\t\tif (deepEqual(sub, sup)) {\n\t\t\treturn { isSubset: true, merged: sub, errors: [] };\n\t\t}\n\n\t\tconst nSub = normalize(sub);\n\t\tconst nSup = normalize(sup);\n\n\t\t// ── Post-normalize structural identity ──\n\t\t// Catches semantically equivalent schemas after normalization.\n\t\tif (deepEqual(nSub, nSup)) {\n\t\t\treturn { isSubset: true, merged: nSub, errors: [] };\n\t\t}\n\n\t\tconst { branches: subBranches, type: subBranchType } =\n\t\t\tgetBranchesTyped(nSub);\n\t\tconst { branches: supBranches, type: supBranchType } =\n\t\t\tgetBranchesTyped(nSup);\n\n\t\t// anyOf/oneOf in sub\n\t\tif (subBranches.length > 1 || subBranches[0] !== nSub) {\n\t\t\treturn checkBranchedSub(subBranches, nSup, this.engine, subBranchType);\n\t\t}\n\n\t\t// anyOf/oneOf in sup only\n\t\tif (supBranches.length > 1 || supBranches[0] !== nSup) {\n\t\t\treturn checkBranchedSup(nSub, supBranches, this.engine, supBranchType);\n\t\t}\n\n\t\t// Standard case\n\t\treturn checkAtomic(nSub, nSup, this.engine);\n\t}\n\n\t// ── Cache management ───────────────────────────────────────────────────\n\n\t/**\n\t * Clears all compiled AJV validator caches (WeakMap, LRU, and AJV internal).\n\t *\n\t * Useful for:\n\t * - Long-running processes where schemas evolve over time\n\t * - Test isolation (ensuring no cross-test cache pollution)\n\t * - Memory pressure situations where cached validators are no longer needed\n\t *\n\t * After calling this, the next validation call will recompile validators\n\t * from scratch — there is a one-time performance cost per unique schema.\n\t *\n\t * This is a static method because the AJV instance is a module-level\n\t * singleton shared across all `JsonSchemaCompatibilityChecker` instances.\n\t *\n\t * @example\n\t * ```ts\n\t * JsonSchemaCompatibilityChecker.clearCache();\n\t * ```\n\t */\n\tstatic clearCache(): void {\n\t\tclearAllValidatorCaches();\n\t}\n}\n"],"names":["JsonSchemaCompatibilityChecker","MergeEngine","SchemaErrorType","arePatternsEquivalent","formatResult","isPatternSubset","isTrivialPattern","normalize","resolveConditions","isSubset","sub","sup","deepEqual","nSub","nSup","branches","subBranches","getBranchesTyped","length","every","branch","isAtomicSubsetOf","engine","check","options","checkWithOptions","checkInternal","isEqual","a","b","intersect","nA","nB","merged","merge","def","label","result","schema","data","constraintContext","validateSub","validateSup","partialSub","partialSup","resolveValidateTargets","validate","dataForConditions","isPlainObj","resolvedSub","resolvedSup","canNarrow","undefined","canNarrowSub","resolved","canNarrowSup","narrowedSubResolved","narrowSchemaWithData","narrowedSupResolved","staticResult","runtimeErrors","getErrors","getPartialRuntimeValidationErrors","getRuntimeValidationErrors","push","prefixRuntimeErrors","validateSchemaConstraints","constraintValidators","errors","rootKey","map","error","key","type","subBranchType","supBranches","supBranchType","checkBranchedSub","checkBranchedSup","checkAtomic","clearCache","clearAllValidatorCaches","constraints"],"mappings":"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"}
@@ -159,6 +159,23 @@ export interface CheckRuntimeOptions {
159
159
  * @default false
160
160
  */
161
161
  validate?: boolean | ValidateTargets;
162
+ /**
163
+ * Arbitrary context forwarded to every constraint validator during
164
+ * this `check()` call.
165
+ *
166
+ * Useful for passing per-request state (e.g. tenant ID, user scope)
167
+ * that is not encoded in the schema but is needed by validators at runtime.
168
+ *
169
+ * @example
170
+ * ```ts
171
+ * checker.check(sub, sup, {
172
+ * data: { accountId: '123' },
173
+ * validate: { sup: { partial: true } },
174
+ * constraintContext: { companyId: 42 },
175
+ * });
176
+ * ```
177
+ */
178
+ constraintContext?: ConstraintExecutionContext;
162
179
  }
163
180
  /**
164
181
  * Extended result from `check()` when runtime options are provided.
@@ -181,6 +198,15 @@ export type Constraint = string | {
181
198
  params?: Record<string, unknown>;
182
199
  };
183
200
  export type Constraints = Constraint | Constraint[];
201
+ /**
202
+ * Arbitrary per-call context forwarded to every constraint validator
203
+ * during a `check()` invocation.
204
+ *
205
+ * Allows callers to pass request-scoped information (e.g. tenant ID,
206
+ * user ID, feature flags) that is not part of the schema definition
207
+ * but is required by constraint validators at runtime.
208
+ */
209
+ export type ConstraintExecutionContext = Record<string, unknown>;
184
210
  /**
185
211
  * Result of a constraint validation.
186
212
  */
@@ -193,14 +219,15 @@ export interface ConstraintValidationResult {
193
219
  /**
194
220
  * A constraint validator function.
195
221
  *
196
- * Receives the value to validate and optional params defined
197
- * in the schema's constraint definition.
222
+ * Receives the value to validate, optional params defined in the schema's
223
+ * constraint definition, and an optional per-call execution context.
198
224
  *
199
225
  * Can be synchronous or asynchronous. When async validators are used,
200
226
  * `check()` with runtime options returns a `Promise`.
201
227
  *
202
228
  * @param value - The runtime value to validate
203
229
  * @param params - The `params` object from the constraint definition, if any
230
+ * @param context - The per-call execution context passed via `constraintContext`, if any
204
231
  * @returns The validation result, or a Promise resolving to it
205
232
  *
206
233
  * @example
@@ -221,9 +248,15 @@ export interface ConstraintValidationResult {
221
248
  * valid: typeof value === "number" && value >= (params?.min ?? 0),
222
249
  * message: `Value must be at least ${params?.min}`,
223
250
  * });
251
+ *
252
+ * // Validator using execution context (e.g. tenant-scoped DB lookup)
253
+ * const isUniqueInTenant: ConstraintValidator = async (value, params, context) => ({
254
+ * valid: await checkUniqueness(value as string, context?.companyId as number),
255
+ * message: "Value must be unique within the tenant",
256
+ * });
224
257
  * ```
225
258
  */
226
- export type ConstraintValidator = (value: unknown, params?: Record<string, unknown>) => ConstraintValidationResult | Promise<ConstraintValidationResult>;
259
+ export type ConstraintValidator = (value: unknown, params?: Record<string, unknown>, context?: ConstraintExecutionContext) => ConstraintValidationResult | Promise<ConstraintValidationResult>;
227
260
  /**
228
261
  * Registry mapping constraint names to their validator functions.
229
262
  *
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/types.ts"],"sourcesContent":["import type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\n\n// ─── Module augmentation ─────────────────────────────────────────────────────\n// Extends JSONSchema7 with the custom `constraints` keyword so that consumers\n// of this package see the property on every JSONSchema7 without needing a\n// separate ambient file or `/// <reference>` directive.\n\ndeclare module \"json-schema\" {\n\tinterface JSONSchema7 {\n\t\tconstraints?: Constraints;\n\t}\n}\n\n// ─── Public types ────────────────────────────────────────────────────────────\n\n/**\n * Discriminant for `SchemaError` — indicates the nature of the incompatibility.\n *\n * | Member | Description | Source module(s) |\n * |------------------------|--------------------------------------------------------------------|---------------------------|\n * | `TypeMismatch` | Incompatible types (e.g. `string` vs `number`, boolean schemas) | `semantic-errors.ts` |\n * | `MissingProperty` | Required property absent from the source schema | `semantic-errors.ts` |\n * | `Optionality` | Property required in target but optional in source | `semantic-errors.ts` |\n * | `EnumMismatch` | `enum` / `const` values are incompatible | `semantic-errors.ts` |\n * | `NumericConstraint` | `minimum`, `maximum`, `exclusiveMin/Max`, `multipleOf` | `semantic-errors.ts` |\n * | `StringConstraint` | `minLength`, `maxLength`, `pattern`, `format` | `semantic-errors.ts` |\n * | `ObjectConstraint` | `additionalProperties`, `min/maxProperties`, `propertyNames`, etc. | `semantic-errors.ts` |\n * | `ArrayConstraint` | `minItems`, `maxItems`, `uniqueItems`, `contains` | `semantic-errors.ts` |\n * | `NotSchema` | Incompatibility on the `not` keyword | `semantic-errors.ts` |\n * | `BranchMismatch` | No `anyOf` / `oneOf` branch matches | `semantic-errors.ts` |\n * | `RuntimeValidation` | Runtime data invalid against a standard JSON Schema keyword (AJV) | `runtime-validator.ts` |\n * | `CustomConstraint` | Custom constraint failed, unknown, or threw | `constraint-validator.ts` |\n */\nexport enum SchemaErrorType {\n\tTypeMismatch = \"type_mismatch\",\n\tMissingProperty = \"missing_property\",\n\tOptionality = \"optionality\",\n\tEnumMismatch = \"enum_mismatch\",\n\tNumericConstraint = \"numeric_constraint\",\n\tStringConstraint = \"string_constraint\",\n\tObjectConstraint = \"object_constraint\",\n\tArrayConstraint = \"array_constraint\",\n\tNotSchema = \"not_schema\",\n\tBranchMismatch = \"branch_mismatch\",\n\tRuntimeValidation = \"runtime_validation\",\n\tCustomConstraint = \"custom_constraint\",\n}\n\nexport interface SchemaError {\n\t/** Discriminant indicating the nature of the error */\n\ttype: SchemaErrorType;\n\t/** Normalized path to the concerned property (e.g. \"user.name\", \"users[].name\", \"accountId\") */\n\tkey: string;\n\t/** Type or value expected by the target schema (sup) */\n\texpected: string;\n\t/** Type or value received from the source schema (sub) */\n\treceived: string;\n}\n\nexport interface SubsetResult {\n\t/** true if sub ⊆ sup (every value valid for sub is also valid for sup) */\n\tisSubset: boolean;\n\t/** The schema resulting from the intersection allOf(sub, sup), or null if incompatible */\n\tmerged: JSONSchema7Definition | null;\n\t/** Semantic errors describing incompatibilities between the two schemas */\n\terrors: SchemaError[];\n}\n\n/**\n * Per-target validation options.\n *\n * When `partial` is `true`, runtime validation strips `required` and\n * `additionalProperties` constraints at every level of the schema before\n * passing it to AJV. This allows validating **only the properties present\n * in `data`** without false negatives on missing required properties or\n * extra properties not defined in the schema.\n *\n * Partial mode applies recursively: nested object schemas also have their\n * `required` and `additionalProperties` stripped.\n *\n * @example\n * ```ts\n * // Validate sup in partial mode — only check properties present in data\n * checker.check(sub, sup, {\n * data: { accountId: 'salut' },\n * validate: { sup: { partial: true } },\n * });\n * ```\n */\nexport interface ValidateTargetOptions {\n\t/**\n\t * When `true`, strip `required` and `additionalProperties` from the\n\t * schema before AJV validation so that only properties present in\n\t * `data` are validated.\n\t *\n\t * @default false\n\t */\n\tpartial?: boolean;\n}\n\n/**\n * Granular control over which schema(s) runtime validation applies to.\n *\n * When provided as an object, each key independently controls whether\n * runtime validation (AJV + custom constraints) runs against that schema:\n * - `sub`: validate `data` against the resolved/narrowed sub schema\n * - `sup`: validate `data` against the resolved/narrowed sup schema\n *\n * Each target accepts either a boolean or a `ValidateTargetOptions` object.\n * When `true`, validation runs with default options. When an object is\n * provided, its flags (e.g. `partial`) customize the validation behavior.\n *\n * Omitted keys default to `false`.\n *\n * @example\n * ```ts\n * // Validate only the sup schema (e.g. target input with constraints)\n * checker.check(sub, sup, { data: {}, validate: { sup: true } });\n *\n * // Validate only the sub schema\n * checker.check(sub, sup, { data: {}, validate: { sub: true } });\n *\n * // Validate both (equivalent to `validate: true`)\n * checker.check(sub, sup, { data: {}, validate: { sub: true, sup: true } });\n *\n * // Validate sup in partial mode (skip required / additionalProperties)\n * checker.check(sub, sup, {\n * data: { accountId: 'salut' },\n * validate: { sup: { partial: true } },\n * });\n * ```\n */\nexport interface ValidateTargets {\n\t/** When `true` or an options object, validate `data` against the resolved sub schema */\n\tsub?: boolean | ValidateTargetOptions;\n\t/** When `true` or an options object, validate `data` against the resolved sup schema */\n\tsup?: boolean | ValidateTargetOptions;\n}\n\n/**\n * Options for runtime-aware subset checking.\n *\n * When `data` is provided, the checker:\n * 1. Resolves `if/then/else` conditions in both `sub` and `sup` using `data`\n * (if `data` is `undefined`, conditions are resolved with `{}`)\n * 2. Narrows schemas using runtime values (e.g. enum materialization)\n * 3. Performs the static subset check on the resolved/narrowed schemas\n *\n * When `validate` is `true` (or an object with `sub`/`sup` flags), additional\n * runtime steps run **after** the static check passes:\n * 4. `data` is validated against the targeted resolved schema(s) via AJV\n * 5. Custom constraints are validated against `data` for the targeted schema(s)\n *\n * `data` can be a partial discriminant (e.g. `{ kind: \"text\" }`) used solely\n * for condition resolution and narrowing. It does **not** need to be a complete\n * instance of the schemas unless runtime validation is enabled.\n */\nexport interface CheckRuntimeOptions {\n\t/** Runtime data used for condition resolution, narrowing, and optionally runtime validation */\n\tdata: unknown;\n\n\t/**\n\t * Controls runtime validation of `data` against resolved schemas.\n\t *\n\t * - `true` — validate against **both** sub and sup schemas (AJV + constraints)\n\t * - `false` / omitted — no runtime validation (data used only for condition\n\t * resolution and narrowing)\n\t * - `{ sub: true }` — validate only against the sub schema\n\t * - `{ sup: true }` — validate only against the sup schema\n\t * - `{ sub: true, sup: true }` — equivalent to `true`\n\t * - `{ sup: { partial: true } }` — validate sup in partial mode\n\t * (skip `required` / `additionalProperties` enforcement)\n\t *\n\t * @default false\n\t */\n\tvalidate?: boolean | ValidateTargets;\n}\n\n/**\n * Extended result from `check()` when runtime options are provided.\n * Includes resolution results for sub and sup in addition to the SubsetResult.\n */\nexport interface ResolvedSubsetResult extends SubsetResult {\n\tresolvedSub: ResolvedConditionResult;\n\tresolvedSup: ResolvedConditionResult;\n}\n\nexport interface ResolvedConditionResult {\n\t/** The schema with if/then/else resolved (flattened) */\n\tresolved: JSONSchema7;\n\t/** The branch that was applied (\"then\" | \"else\" | null if no condition) */\n\tbranch: \"then\" | \"else\" | null;\n\t/** The discriminant used for resolution */\n\tdiscriminant: Record<string, unknown>;\n}\n\nexport type Constraint =\n\t| string\n\t| {\n\t\t\tname: string;\n\t\t\tparams?: Record<string, unknown>;\n\t };\n\nexport type Constraints = Constraint | Constraint[];\n\n// ─── Constraint Validator types ──────────────────────────────────────────────\n\n/**\n * Result of a constraint validation.\n */\nexport interface ConstraintValidationResult {\n\t/** Whether the value satisfies the constraint */\n\tvalid: boolean;\n\t/** Human-readable message when `valid` is `false` */\n\tmessage?: string;\n}\n\n/**\n * A constraint validator function.\n *\n * Receives the value to validate and optional params defined\n * in the schema's constraint definition.\n *\n * Can be synchronous or asynchronous. When async validators are used,\n * `check()` with runtime options returns a `Promise`.\n *\n * @param value - The runtime value to validate\n * @param params - The `params` object from the constraint definition, if any\n * @returns The validation result, or a Promise resolving to it\n *\n * @example\n * ```ts\n * // Synchronous validator\n * const isUuid: ConstraintValidator = (value) => ({\n * valid: typeof value === \"string\" && /^[0-9a-f]{8}-/.test(value),\n * message: \"Value must be a valid UUID\",\n * });\n *\n * // Async validator\n * const isUniqueEmail: ConstraintValidator = async (value) => ({\n * valid: await checkEmailUniqueness(value as string),\n * message: \"Email must be unique\",\n * });\n *\n * const minAge: ConstraintValidator = (value, params) => ({\n * valid: typeof value === \"number\" && value >= (params?.min ?? 0),\n * message: `Value must be at least ${params?.min}`,\n * });\n * ```\n */\nexport type ConstraintValidator = (\n\tvalue: unknown,\n\tparams?: Record<string, unknown>,\n) => ConstraintValidationResult | Promise<ConstraintValidationResult>;\n\n/**\n * Registry mapping constraint names to their validator functions.\n *\n * Keys are constraint names as they appear in schema definitions\n * (e.g. `\"IsUuid\"`, `\"MinAge\"`).\n */\nexport type ConstraintValidatorRegistry = Record<string, ConstraintValidator>;\n\n/**\n * Options for the JsonSchemaCompatibilityChecker constructor.\n */\nexport interface CheckerOptions {\n\t/**\n\t * Registry of custom constraint validators.\n\t *\n\t * When provided, the checker can validate runtime data against\n\t * custom constraints defined in schemas via the `constraints` keyword.\n\t *\n\t * Constraint names must match those used in schema definitions.\n\t * Unknown constraints (present in a schema but absent from the registry)\n\t * will be reported as errors during runtime validation.\n\t */\n\tconstraints?: ConstraintValidatorRegistry;\n}\n"],"names":["SchemaErrorType"],"mappings":"oGAiCYA,yDAAAA,mBAAL,IAAA,AAAKA,sCAAAA,2oBAAAA"}
1
+ {"version":3,"sources":["../../src/types.ts"],"sourcesContent":["import type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\n\n// ─── Module augmentation ─────────────────────────────────────────────────────\n// Extends JSONSchema7 with the custom `constraints` keyword so that consumers\n// of this package see the property on every JSONSchema7 without needing a\n// separate ambient file or `/// <reference>` directive.\n\ndeclare module \"json-schema\" {\n\tinterface JSONSchema7 {\n\t\tconstraints?: Constraints;\n\t}\n}\n\n// ─── Public types ────────────────────────────────────────────────────────────\n\n/**\n * Discriminant for `SchemaError` — indicates the nature of the incompatibility.\n *\n * | Member | Description | Source module(s) |\n * |------------------------|--------------------------------------------------------------------|---------------------------|\n * | `TypeMismatch` | Incompatible types (e.g. `string` vs `number`, boolean schemas) | `semantic-errors.ts` |\n * | `MissingProperty` | Required property absent from the source schema | `semantic-errors.ts` |\n * | `Optionality` | Property required in target but optional in source | `semantic-errors.ts` |\n * | `EnumMismatch` | `enum` / `const` values are incompatible | `semantic-errors.ts` |\n * | `NumericConstraint` | `minimum`, `maximum`, `exclusiveMin/Max`, `multipleOf` | `semantic-errors.ts` |\n * | `StringConstraint` | `minLength`, `maxLength`, `pattern`, `format` | `semantic-errors.ts` |\n * | `ObjectConstraint` | `additionalProperties`, `min/maxProperties`, `propertyNames`, etc. | `semantic-errors.ts` |\n * | `ArrayConstraint` | `minItems`, `maxItems`, `uniqueItems`, `contains` | `semantic-errors.ts` |\n * | `NotSchema` | Incompatibility on the `not` keyword | `semantic-errors.ts` |\n * | `BranchMismatch` | No `anyOf` / `oneOf` branch matches | `semantic-errors.ts` |\n * | `RuntimeValidation` | Runtime data invalid against a standard JSON Schema keyword (AJV) | `runtime-validator.ts` |\n * | `CustomConstraint` | Custom constraint failed, unknown, or threw | `constraint-validator.ts` |\n */\nexport enum SchemaErrorType {\n\tTypeMismatch = \"type_mismatch\",\n\tMissingProperty = \"missing_property\",\n\tOptionality = \"optionality\",\n\tEnumMismatch = \"enum_mismatch\",\n\tNumericConstraint = \"numeric_constraint\",\n\tStringConstraint = \"string_constraint\",\n\tObjectConstraint = \"object_constraint\",\n\tArrayConstraint = \"array_constraint\",\n\tNotSchema = \"not_schema\",\n\tBranchMismatch = \"branch_mismatch\",\n\tRuntimeValidation = \"runtime_validation\",\n\tCustomConstraint = \"custom_constraint\",\n}\n\nexport interface SchemaError {\n\t/** Discriminant indicating the nature of the error */\n\ttype: SchemaErrorType;\n\t/** Normalized path to the concerned property (e.g. \"user.name\", \"users[].name\", \"accountId\") */\n\tkey: string;\n\t/** Type or value expected by the target schema (sup) */\n\texpected: string;\n\t/** Type or value received from the source schema (sub) */\n\treceived: string;\n}\n\nexport interface SubsetResult {\n\t/** true if sub ⊆ sup (every value valid for sub is also valid for sup) */\n\tisSubset: boolean;\n\t/** The schema resulting from the intersection allOf(sub, sup), or null if incompatible */\n\tmerged: JSONSchema7Definition | null;\n\t/** Semantic errors describing incompatibilities between the two schemas */\n\terrors: SchemaError[];\n}\n\n/**\n * Per-target validation options.\n *\n * When `partial` is `true`, runtime validation strips `required` and\n * `additionalProperties` constraints at every level of the schema before\n * passing it to AJV. This allows validating **only the properties present\n * in `data`** without false negatives on missing required properties or\n * extra properties not defined in the schema.\n *\n * Partial mode applies recursively: nested object schemas also have their\n * `required` and `additionalProperties` stripped.\n *\n * @example\n * ```ts\n * // Validate sup in partial mode — only check properties present in data\n * checker.check(sub, sup, {\n * data: { accountId: 'salut' },\n * validate: { sup: { partial: true } },\n * });\n * ```\n */\nexport interface ValidateTargetOptions {\n\t/**\n\t * When `true`, strip `required` and `additionalProperties` from the\n\t * schema before AJV validation so that only properties present in\n\t * `data` are validated.\n\t *\n\t * @default false\n\t */\n\tpartial?: boolean;\n}\n\n/**\n * Granular control over which schema(s) runtime validation applies to.\n *\n * When provided as an object, each key independently controls whether\n * runtime validation (AJV + custom constraints) runs against that schema:\n * - `sub`: validate `data` against the resolved/narrowed sub schema\n * - `sup`: validate `data` against the resolved/narrowed sup schema\n *\n * Each target accepts either a boolean or a `ValidateTargetOptions` object.\n * When `true`, validation runs with default options. When an object is\n * provided, its flags (e.g. `partial`) customize the validation behavior.\n *\n * Omitted keys default to `false`.\n *\n * @example\n * ```ts\n * // Validate only the sup schema (e.g. target input with constraints)\n * checker.check(sub, sup, { data: {}, validate: { sup: true } });\n *\n * // Validate only the sub schema\n * checker.check(sub, sup, { data: {}, validate: { sub: true } });\n *\n * // Validate both (equivalent to `validate: true`)\n * checker.check(sub, sup, { data: {}, validate: { sub: true, sup: true } });\n *\n * // Validate sup in partial mode (skip required / additionalProperties)\n * checker.check(sub, sup, {\n * data: { accountId: 'salut' },\n * validate: { sup: { partial: true } },\n * });\n * ```\n */\nexport interface ValidateTargets {\n\t/** When `true` or an options object, validate `data` against the resolved sub schema */\n\tsub?: boolean | ValidateTargetOptions;\n\t/** When `true` or an options object, validate `data` against the resolved sup schema */\n\tsup?: boolean | ValidateTargetOptions;\n}\n\n/**\n * Options for runtime-aware subset checking.\n *\n * When `data` is provided, the checker:\n * 1. Resolves `if/then/else` conditions in both `sub` and `sup` using `data`\n * (if `data` is `undefined`, conditions are resolved with `{}`)\n * 2. Narrows schemas using runtime values (e.g. enum materialization)\n * 3. Performs the static subset check on the resolved/narrowed schemas\n *\n * When `validate` is `true` (or an object with `sub`/`sup` flags), additional\n * runtime steps run **after** the static check passes:\n * 4. `data` is validated against the targeted resolved schema(s) via AJV\n * 5. Custom constraints are validated against `data` for the targeted schema(s)\n *\n * `data` can be a partial discriminant (e.g. `{ kind: \"text\" }`) used solely\n * for condition resolution and narrowing. It does **not** need to be a complete\n * instance of the schemas unless runtime validation is enabled.\n */\nexport interface CheckRuntimeOptions {\n\t/** Runtime data used for condition resolution, narrowing, and optionally runtime validation */\n\tdata: unknown;\n\n\t/**\n\t * Controls runtime validation of `data` against resolved schemas.\n\t *\n\t * - `true` — validate against **both** sub and sup schemas (AJV + constraints)\n\t * - `false` / omitted — no runtime validation (data used only for condition\n\t * resolution and narrowing)\n\t * - `{ sub: true }` — validate only against the sub schema\n\t * - `{ sup: true }` — validate only against the sup schema\n\t * - `{ sub: true, sup: true }` — equivalent to `true`\n\t * - `{ sup: { partial: true } }` — validate sup in partial mode\n\t * (skip `required` / `additionalProperties` enforcement)\n\t *\n\t * @default false\n\t */\n\tvalidate?: boolean | ValidateTargets;\n\n\t/**\n\t * Arbitrary context forwarded to every constraint validator during\n\t * this `check()` call.\n\t *\n\t * Useful for passing per-request state (e.g. tenant ID, user scope)\n\t * that is not encoded in the schema but is needed by validators at runtime.\n\t *\n\t * @example\n\t * ```ts\n\t * checker.check(sub, sup, {\n\t * data: { accountId: '123' },\n\t * validate: { sup: { partial: true } },\n\t * constraintContext: { companyId: 42 },\n\t * });\n\t * ```\n\t */\n\tconstraintContext?: ConstraintExecutionContext;\n}\n\n/**\n * Extended result from `check()` when runtime options are provided.\n * Includes resolution results for sub and sup in addition to the SubsetResult.\n */\nexport interface ResolvedSubsetResult extends SubsetResult {\n\tresolvedSub: ResolvedConditionResult;\n\tresolvedSup: ResolvedConditionResult;\n}\n\nexport interface ResolvedConditionResult {\n\t/** The schema with if/then/else resolved (flattened) */\n\tresolved: JSONSchema7;\n\t/** The branch that was applied (\"then\" | \"else\" | null if no condition) */\n\tbranch: \"then\" | \"else\" | null;\n\t/** The discriminant used for resolution */\n\tdiscriminant: Record<string, unknown>;\n}\n\nexport type Constraint =\n\t| string\n\t| {\n\t\t\tname: string;\n\t\t\tparams?: Record<string, unknown>;\n\t };\n\nexport type Constraints = Constraint | Constraint[];\n\n// ─── Constraint Validator types ──────────────────────────────────────────────\n\n/**\n * Arbitrary per-call context forwarded to every constraint validator\n * during a `check()` invocation.\n *\n * Allows callers to pass request-scoped information (e.g. tenant ID,\n * user ID, feature flags) that is not part of the schema definition\n * but is required by constraint validators at runtime.\n */\nexport type ConstraintExecutionContext = Record<string, unknown>;\n\n/**\n * Result of a constraint validation.\n */\nexport interface ConstraintValidationResult {\n\t/** Whether the value satisfies the constraint */\n\tvalid: boolean;\n\t/** Human-readable message when `valid` is `false` */\n\tmessage?: string;\n}\n\n/**\n * A constraint validator function.\n *\n * Receives the value to validate, optional params defined in the schema's\n * constraint definition, and an optional per-call execution context.\n *\n * Can be synchronous or asynchronous. When async validators are used,\n * `check()` with runtime options returns a `Promise`.\n *\n * @param value - The runtime value to validate\n * @param params - The `params` object from the constraint definition, if any\n * @param context - The per-call execution context passed via `constraintContext`, if any\n * @returns The validation result, or a Promise resolving to it\n *\n * @example\n * ```ts\n * // Synchronous validator\n * const isUuid: ConstraintValidator = (value) => ({\n * valid: typeof value === \"string\" && /^[0-9a-f]{8}-/.test(value),\n * message: \"Value must be a valid UUID\",\n * });\n *\n * // Async validator\n * const isUniqueEmail: ConstraintValidator = async (value) => ({\n * valid: await checkEmailUniqueness(value as string),\n * message: \"Email must be unique\",\n * });\n *\n * const minAge: ConstraintValidator = (value, params) => ({\n * valid: typeof value === \"number\" && value >= (params?.min ?? 0),\n * message: `Value must be at least ${params?.min}`,\n * });\n *\n * // Validator using execution context (e.g. tenant-scoped DB lookup)\n * const isUniqueInTenant: ConstraintValidator = async (value, params, context) => ({\n * valid: await checkUniqueness(value as string, context?.companyId as number),\n * message: \"Value must be unique within the tenant\",\n * });\n * ```\n */\nexport type ConstraintValidator = (\n\tvalue: unknown,\n\tparams?: Record<string, unknown>,\n\tcontext?: ConstraintExecutionContext,\n) => ConstraintValidationResult | Promise<ConstraintValidationResult>;\n\n/**\n * Registry mapping constraint names to their validator functions.\n *\n * Keys are constraint names as they appear in schema definitions\n * (e.g. `\"IsUuid\"`, `\"MinAge\"`).\n */\nexport type ConstraintValidatorRegistry = Record<string, ConstraintValidator>;\n\n/**\n * Options for the JsonSchemaCompatibilityChecker constructor.\n */\nexport interface CheckerOptions {\n\t/**\n\t * Registry of custom constraint validators.\n\t *\n\t * When provided, the checker can validate runtime data against\n\t * custom constraints defined in schemas via the `constraints` keyword.\n\t *\n\t * Constraint names must match those used in schema definitions.\n\t * Unknown constraints (present in a schema but absent from the registry)\n\t * will be reported as errors during runtime validation.\n\t */\n\tconstraints?: ConstraintValidatorRegistry;\n}\n"],"names":["SchemaErrorType"],"mappings":"oGAiCYA,yDAAAA,mBAAL,IAAA,AAAKA,sCAAAA,2oBAAAA"}
@@ -1,5 +1,5 @@
1
1
  import type { JSONSchema7Definition } from "json-schema";
2
- import type { ConstraintValidatorRegistry, SchemaError } from "./types.js";
2
+ import type { ConstraintExecutionContext, ConstraintValidatorRegistry, SchemaError } from "./types.js";
3
3
  /**
4
4
  * Recursively validates runtime data against all `constraints` found
5
5
  * in a schema, using the provided validator registry.
@@ -18,4 +18,4 @@ import type { ConstraintValidatorRegistry, SchemaError } from "./types.js";
18
18
  * @param path - The current property path (for error reporting)
19
19
  * @returns Array of schema errors (empty if all constraints pass)
20
20
  */
21
- export declare function validateSchemaConstraints(schema: JSONSchema7Definition, data: unknown, registry: ConstraintValidatorRegistry, path?: string): Promise<SchemaError[]>;
21
+ export declare function validateSchemaConstraints(schema: JSONSchema7Definition, data: unknown, registry: ConstraintValidatorRegistry, context?: ConstraintExecutionContext, path?: string): Promise<SchemaError[]>;
@@ -1,2 +1,2 @@
1
- import{SchemaErrorType}from"./types.js";import{hasOwn,isPlainObj,toConstraintArray}from"./utils.js";async function validateValue(constraints,value,registry,path){const errors=[];for(const constraint of constraints){const name=typeof constraint==="string"?constraint:constraint.name;const params=typeof constraint==="string"?undefined:constraint.params;const validator=registry[name];if(!validator){errors.push({type:SchemaErrorType.CustomConstraint,key:path||"$root",expected:name,received:"unknown constraint (not registered)"});continue}try{const result=await validator(value,params);if(!result.valid){errors.push({type:SchemaErrorType.CustomConstraint,key:path||"$root",expected:name,received:result.message??"constraint validation failed"})}}catch(err){errors.push({type:SchemaErrorType.CustomConstraint,key:path||"$root",expected:name,received:err instanceof Error?err.message:"constraint validation error"})}}return errors}export async function validateSchemaConstraints(schema,data,registry,path=""){if(typeof schema==="boolean")return[];const errors=[];const constraints=toConstraintArray(schema.constraints);if(constraints.length>0){errors.push(...await validateValue(constraints,data,registry,path))}if(isPlainObj(schema.properties)&&isPlainObj(data)){const props=schema.properties;const dataObj=data;for(const key of Object.keys(props)){const propSchema=props[key];if(propSchema===undefined)continue;const propValue=dataObj[key];if(propValue===undefined&&!hasOwn(dataObj,key))continue;const propPath=path?`${path}.${key}`:key;errors.push(...await validateSchemaConstraints(propSchema,propValue,registry,propPath))}}if(isPlainObj(schema.items)&&Array.isArray(data)){const itemSchema=schema.items;const itemPath=path?`${path}[]`:"[]";for(let i=0;i<data.length;i++){errors.push(...await validateSchemaConstraints(itemSchema,data[i],registry,itemPath))}}if(Array.isArray(schema.items)&&Array.isArray(data)){const tupleSchemas=schema.items;for(let i=0;i<tupleSchemas.length&&i<data.length;i++){const itemSchema=tupleSchemas[i];if(itemSchema===undefined)continue;const itemPath=path?`${path}[${i}]`:`[${i}]`;errors.push(...await validateSchemaConstraints(itemSchema,data[i],registry,itemPath))}}if(isPlainObj(schema.patternProperties)&&isPlainObj(data)){const pp=schema.patternProperties;const dataObj=data;for(const pattern of Object.keys(pp)){const patternSchema=pp[pattern];if(patternSchema===undefined||typeof patternSchema==="boolean")continue;let regex;try{regex=new RegExp(pattern)}catch{continue}for(const dataKey of Object.keys(dataObj)){if(!regex.test(dataKey))continue;const dataValue=dataObj[dataKey];const ppPath=path?`${path}.${dataKey}`:dataKey;errors.push(...await validateSchemaConstraints(patternSchema,dataValue,registry,ppPath))}}}if(isPlainObj(schema.additionalProperties)&&typeof schema.additionalProperties!=="boolean"&&isPlainObj(data)){const apSchema=schema.additionalProperties;const dataObj=data;const definedProps=isPlainObj(schema.properties)?new Set(Object.keys(schema.properties)):new Set;const ppPatterns=[];if(isPlainObj(schema.patternProperties)){for(const pattern of Object.keys(schema.patternProperties)){try{ppPatterns.push(new RegExp(pattern))}catch{}}}for(const dataKey of Object.keys(dataObj)){if(definedProps.has(dataKey))continue;if(ppPatterns.some(re=>re.test(dataKey)))continue;const dataValue=dataObj[dataKey];const apPath=path?`${path}.${dataKey}`:dataKey;errors.push(...await validateSchemaConstraints(apSchema,dataValue,registry,apPath))}}if(isPlainObj(schema.dependencies)&&isPlainObj(data)){const deps=schema.dependencies;const dataObj=data;for(const depKey of Object.keys(deps)){if(!hasOwn(dataObj,depKey))continue;const depValue=deps[depKey];if(depValue===undefined)continue;if(Array.isArray(depValue))continue;if(typeof depValue==="boolean")continue;errors.push(...await validateSchemaConstraints(depValue,data,registry,path))}}return errors}
1
+ import{SchemaErrorType}from"./types.js";import{hasOwn,isPlainObj,toConstraintArray}from"./utils.js";async function validateValue(constraints,value,registry,path,context){const errors=[];for(const constraint of constraints){const name=typeof constraint==="string"?constraint:constraint.name;const params=typeof constraint==="string"?undefined:constraint.params;const validator=registry[name];if(!validator){errors.push({type:SchemaErrorType.CustomConstraint,key:path||"$root",expected:name,received:"unknown constraint (not registered)"});continue}try{const result=await validator(value,params,context);if(!result.valid){errors.push({type:SchemaErrorType.CustomConstraint,key:path||"$root",expected:name,received:result.message??"constraint validation failed"})}}catch(err){errors.push({type:SchemaErrorType.CustomConstraint,key:path||"$root",expected:name,received:err instanceof Error?err.message:"constraint validation error"})}}return errors}export async function validateSchemaConstraints(schema,data,registry,context,path=""){if(typeof schema==="boolean")return[];const errors=[];const constraints=toConstraintArray(schema.constraints);if(constraints.length>0){errors.push(...await validateValue(constraints,data,registry,path,context))}if(isPlainObj(schema.properties)&&isPlainObj(data)){const props=schema.properties;const dataObj=data;for(const key of Object.keys(props)){const propSchema=props[key];if(propSchema===undefined)continue;const propValue=dataObj[key];if(propValue===undefined&&!hasOwn(dataObj,key))continue;const propPath=path?`${path}.${key}`:key;errors.push(...await validateSchemaConstraints(propSchema,propValue,registry,context,propPath))}}if(isPlainObj(schema.items)&&Array.isArray(data)){const itemSchema=schema.items;const itemPath=path?`${path}[]`:"[]";for(let i=0;i<data.length;i++){errors.push(...await validateSchemaConstraints(itemSchema,data[i],registry,context,itemPath))}}if(Array.isArray(schema.items)&&Array.isArray(data)){const tupleSchemas=schema.items;for(let i=0;i<tupleSchemas.length&&i<data.length;i++){const itemSchema=tupleSchemas[i];if(itemSchema===undefined)continue;const itemPath=path?`${path}[${i}]`:`[${i}]`;errors.push(...await validateSchemaConstraints(itemSchema,data[i],registry,context,itemPath))}}if(isPlainObj(schema.patternProperties)&&isPlainObj(data)){const pp=schema.patternProperties;const dataObj=data;for(const pattern of Object.keys(pp)){const patternSchema=pp[pattern];if(patternSchema===undefined||typeof patternSchema==="boolean")continue;let regex;try{regex=new RegExp(pattern)}catch{continue}for(const dataKey of Object.keys(dataObj)){if(!regex.test(dataKey))continue;const dataValue=dataObj[dataKey];const ppPath=path?`${path}.${dataKey}`:dataKey;errors.push(...await validateSchemaConstraints(patternSchema,dataValue,registry,context,ppPath))}}}if(isPlainObj(schema.additionalProperties)&&typeof schema.additionalProperties!=="boolean"&&isPlainObj(data)){const apSchema=schema.additionalProperties;const dataObj=data;const definedProps=isPlainObj(schema.properties)?new Set(Object.keys(schema.properties)):new Set;const ppPatterns=[];if(isPlainObj(schema.patternProperties)){for(const pattern of Object.keys(schema.patternProperties)){try{ppPatterns.push(new RegExp(pattern))}catch{}}}for(const dataKey of Object.keys(dataObj)){if(definedProps.has(dataKey))continue;if(ppPatterns.some(re=>re.test(dataKey)))continue;const dataValue=dataObj[dataKey];const apPath=path?`${path}.${dataKey}`:dataKey;errors.push(...await validateSchemaConstraints(apSchema,dataValue,registry,context,apPath))}}if(isPlainObj(schema.dependencies)&&isPlainObj(data)){const deps=schema.dependencies;const dataObj=data;for(const depKey of Object.keys(deps)){if(!hasOwn(dataObj,depKey))continue;const depValue=deps[depKey];if(depValue===undefined)continue;if(Array.isArray(depValue))continue;if(typeof depValue==="boolean")continue;errors.push(...await validateSchemaConstraints(depValue,data,registry,context,path))}}return errors}
2
2
  //# sourceMappingURL=constraint-validator.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/constraint-validator.ts"],"sourcesContent":["import type { JSONSchema7Definition } from \"json-schema\";\nimport type {\n\tConstraint,\n\tConstraintValidatorRegistry,\n\tSchemaError,\n} from \"./types.ts\";\nimport { SchemaErrorType } from \"./types.ts\";\nimport { hasOwn, isPlainObj, toConstraintArray } from \"./utils.ts\";\n\n// ─── Constraint Validator ────────────────────────────────────────────────────\n//\n// Validates runtime data against custom `constraints` found in a schema,\n// using the provided validator registry.\n//\n// This module is separate from `runtime-validator.ts` (which wraps AJV)\n// and from `format-validator.ts` (which handles the `format` keyword).\n\n/**\n * Validates a single value against a list of constraints using the registry.\n *\n * @param constraints - The constraints to validate against\n * @param value - The runtime value\n * @param registry - The constraint validator registry\n * @param path - The property path for error reporting\n * @returns Array of errors (empty if all constraints pass)\n */\nasync function validateValue(\n\tconstraints: Constraint[],\n\tvalue: unknown,\n\tregistry: ConstraintValidatorRegistry,\n\tpath: string,\n): Promise<SchemaError[]> {\n\tconst errors: SchemaError[] = [];\n\n\tfor (const constraint of constraints) {\n\t\tconst name = typeof constraint === \"string\" ? constraint : constraint.name;\n\t\tconst params =\n\t\t\ttypeof constraint === \"string\" ? undefined : constraint.params;\n\n\t\tconst validator = registry[name];\n\n\t\tif (!validator) {\n\t\t\terrors.push({\n\t\t\t\ttype: SchemaErrorType.CustomConstraint,\n\t\t\t\tkey: path || \"$root\",\n\t\t\t\texpected: name,\n\t\t\t\treceived: \"unknown constraint (not registered)\",\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await validator(value, params);\n\t\t\tif (!result.valid) {\n\t\t\t\terrors.push({\n\t\t\t\t\ttype: SchemaErrorType.CustomConstraint,\n\t\t\t\t\tkey: path || \"$root\",\n\t\t\t\t\texpected: name,\n\t\t\t\t\treceived: result.message ?? \"constraint validation failed\",\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (err) {\n\t\t\terrors.push({\n\t\t\t\ttype: SchemaErrorType.CustomConstraint,\n\t\t\t\tkey: path || \"$root\",\n\t\t\t\texpected: name,\n\t\t\t\treceived:\n\t\t\t\t\terr instanceof Error ? err.message : \"constraint validation error\",\n\t\t\t});\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/**\n * Recursively validates runtime data against all `constraints` found\n * in a schema, using the provided validator registry.\n *\n * Walks into: root-level constraints, `properties`, `patternProperties`,\n * `items` (single schema and tuple form), `additionalProperties` (schema form),\n * `dependencies` (schema form).\n *\n * When a schema declares a constraint that is not present in the registry,\n * an \"unknown constraint (not registered)\" error is produced. This ensures\n * that unregistered constraints are never silently ignored at runtime.\n *\n * @param schema - The resolved/narrowed schema containing constraints\n * @param data - The runtime data to validate\n * @param registry - The constraint validator registry (may be empty)\n * @param path - The current property path (for error reporting)\n * @returns Array of schema errors (empty if all constraints pass)\n */\nexport async function validateSchemaConstraints(\n\tschema: JSONSchema7Definition,\n\tdata: unknown,\n\tregistry: ConstraintValidatorRegistry,\n\tpath = \"\",\n): Promise<SchemaError[]> {\n\t// Boolean schemas → nothing to validate\n\tif (typeof schema === \"boolean\") return [];\n\n\tconst errors: SchemaError[] = [];\n\n\t// ── Root-level constraints ──\n\tconst constraints = toConstraintArray(schema.constraints);\n\tif (constraints.length > 0) {\n\t\terrors.push(...(await validateValue(constraints, data, registry, path)));\n\t}\n\n\t// ── Recurse into properties ──\n\tif (isPlainObj(schema.properties) && isPlainObj(data)) {\n\t\tconst props = schema.properties as Record<string, JSONSchema7Definition>;\n\t\tconst dataObj = data as Record<string, unknown>;\n\n\t\tfor (const key of Object.keys(props)) {\n\t\t\tconst propSchema = props[key];\n\t\t\tif (propSchema === undefined) continue;\n\n\t\t\tconst propValue = dataObj[key];\n\t\t\t// Only validate if the property exists in the data\n\t\t\tif (propValue === undefined && !hasOwn(dataObj, key)) continue;\n\n\t\t\tconst propPath = path ? `${path}.${key}` : key;\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\tpropSchema,\n\t\t\t\t\tpropValue,\n\t\t\t\t\tregistry,\n\t\t\t\t\tpropPath,\n\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Recurse into items (single schema) ──\n\tif (isPlainObj(schema.items) && Array.isArray(data)) {\n\t\tconst itemSchema = schema.items as JSONSchema7Definition;\n\t\tconst itemPath = path ? `${path}[]` : \"[]\";\n\n\t\tfor (let i = 0; i < data.length; i++) {\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\titemSchema,\n\t\t\t\t\tdata[i],\n\t\t\t\t\tregistry,\n\t\t\t\t\titemPath,\n\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Recurse into tuple items ──\n\tif (Array.isArray(schema.items) && Array.isArray(data)) {\n\t\tconst tupleSchemas = schema.items as JSONSchema7Definition[];\n\t\tfor (let i = 0; i < tupleSchemas.length && i < data.length; i++) {\n\t\t\tconst itemSchema = tupleSchemas[i];\n\t\t\tif (itemSchema === undefined) continue;\n\t\t\tconst itemPath = path ? `${path}[${i}]` : `[${i}]`;\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\titemSchema,\n\t\t\t\t\tdata[i],\n\t\t\t\t\tregistry,\n\t\t\t\t\titemPath,\n\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Recurse into patternProperties ──\n\tif (isPlainObj(schema.patternProperties) && isPlainObj(data)) {\n\t\tconst pp = schema.patternProperties as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition\n\t\t>;\n\t\tconst dataObj = data as Record<string, unknown>;\n\n\t\tfor (const pattern of Object.keys(pp)) {\n\t\t\tconst patternSchema = pp[pattern];\n\t\t\tif (patternSchema === undefined || typeof patternSchema === \"boolean\")\n\t\t\t\tcontinue;\n\n\t\t\tlet regex: RegExp;\n\t\t\ttry {\n\t\t\t\tregex = new RegExp(pattern);\n\t\t\t} catch {\n\t\t\t\t// Invalid regex pattern — skip silently (same approach as AJV)\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const dataKey of Object.keys(dataObj)) {\n\t\t\t\tif (!regex.test(dataKey)) continue;\n\n\t\t\t\tconst dataValue = dataObj[dataKey];\n\t\t\t\tconst ppPath = path ? `${path}.${dataKey}` : dataKey;\n\t\t\t\terrors.push(\n\t\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\t\tpatternSchema,\n\t\t\t\t\t\tdataValue,\n\t\t\t\t\t\tregistry,\n\t\t\t\t\t\tppPath,\n\t\t\t\t\t)),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ── Recurse into additionalProperties (schema form) ──\n\tif (\n\t\tisPlainObj(schema.additionalProperties) &&\n\t\ttypeof schema.additionalProperties !== \"boolean\" &&\n\t\tisPlainObj(data)\n\t) {\n\t\tconst apSchema = schema.additionalProperties as JSONSchema7Definition;\n\t\tconst dataObj = data as Record<string, unknown>;\n\t\tconst definedProps = isPlainObj(schema.properties)\n\t\t\t? new Set(Object.keys(schema.properties as Record<string, unknown>))\n\t\t\t: new Set<string>();\n\n\t\t// Collect patternProperties regexes to exclude matching keys\n\t\tconst ppPatterns: RegExp[] = [];\n\t\tif (isPlainObj(schema.patternProperties)) {\n\t\t\tfor (const pattern of Object.keys(\n\t\t\t\tschema.patternProperties as Record<string, unknown>,\n\t\t\t)) {\n\t\t\t\ttry {\n\t\t\t\t\tppPatterns.push(new RegExp(pattern));\n\t\t\t\t} catch {\n\t\t\t\t\t// Invalid pattern — skip\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (const dataKey of Object.keys(dataObj)) {\n\t\t\t// Skip keys defined in properties\n\t\t\tif (definedProps.has(dataKey)) continue;\n\n\t\t\t// Skip keys matching any patternProperties pattern\n\t\t\tif (ppPatterns.some((re) => re.test(dataKey))) continue;\n\n\t\t\tconst dataValue = dataObj[dataKey];\n\t\t\tconst apPath = path ? `${path}.${dataKey}` : dataKey;\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\tapSchema,\n\t\t\t\t\tdataValue,\n\t\t\t\t\tregistry,\n\t\t\t\t\tapPath,\n\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Recurse into dependencies (schema form) ──\n\tif (isPlainObj(schema.dependencies) && isPlainObj(data)) {\n\t\tconst deps = schema.dependencies as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition | string[]\n\t\t>;\n\t\tconst dataObj = data as Record<string, unknown>;\n\n\t\tfor (const depKey of Object.keys(deps)) {\n\t\t\t// Dependency only applies if the trigger key is present in data\n\t\t\tif (!hasOwn(dataObj, depKey)) continue;\n\n\t\t\tconst depValue = deps[depKey];\n\t\t\tif (depValue === undefined) continue;\n\n\t\t\t// Skip array-form dependencies (property deps, not schema deps)\n\t\t\tif (Array.isArray(depValue)) continue;\n\n\t\t\t// Skip boolean schemas\n\t\t\tif (typeof depValue === \"boolean\") continue;\n\n\t\t\t// Schema-form dependency: validate the entire data object against it\n\t\t\t// The dependency schema applies to the whole object, not just the dep key\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(depValue, data, registry, path)),\n\t\t\t);\n\t\t}\n\t}\n\n\treturn errors;\n}\n"],"names":["SchemaErrorType","hasOwn","isPlainObj","toConstraintArray","validateValue","constraints","value","registry","path","errors","constraint","name","params","undefined","validator","push","type","CustomConstraint","key","expected","received","result","valid","message","err","Error","validateSchemaConstraints","schema","data","length","properties","props","dataObj","Object","keys","propSchema","propValue","propPath","items","Array","isArray","itemSchema","itemPath","i","tupleSchemas","patternProperties","pp","pattern","patternSchema","regex","RegExp","dataKey","test","dataValue","ppPath","additionalProperties","apSchema","definedProps","Set","ppPatterns","has","some","re","apPath","dependencies","deps","depKey","depValue"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/constraint-validator.ts"],"sourcesContent":["import type { JSONSchema7Definition } from \"json-schema\";\nimport type {\n\tConstraint,\n\tConstraintExecutionContext,\n\tConstraintValidatorRegistry,\n\tSchemaError,\n} from \"./types.ts\";\nimport { SchemaErrorType } from \"./types.ts\";\nimport { hasOwn, isPlainObj, toConstraintArray } from \"./utils.ts\";\n\n// ─── Constraint Validator ────────────────────────────────────────────────────\n//\n// Validates runtime data against custom `constraints` found in a schema,\n// using the provided validator registry.\n//\n// This module is separate from `runtime-validator.ts` (which wraps AJV)\n// and from `format-validator.ts` (which handles the `format` keyword).\n\n/**\n * Validates a single value against a list of constraints using the registry.\n *\n * @param constraints - The constraints to validate against\n * @param value - The runtime value\n * @param registry - The constraint validator registry\n * @param path - The property path for error reporting\n * @returns Array of errors (empty if all constraints pass)\n */\nasync function validateValue(\n\tconstraints: Constraint[],\n\tvalue: unknown,\n\tregistry: ConstraintValidatorRegistry,\n\tpath: string,\n\tcontext: ConstraintExecutionContext | undefined,\n): Promise<SchemaError[]> {\n\tconst errors: SchemaError[] = [];\n\n\tfor (const constraint of constraints) {\n\t\tconst name = typeof constraint === \"string\" ? constraint : constraint.name;\n\t\tconst params =\n\t\t\ttypeof constraint === \"string\" ? undefined : constraint.params;\n\n\t\tconst validator = registry[name];\n\n\t\tif (!validator) {\n\t\t\terrors.push({\n\t\t\t\ttype: SchemaErrorType.CustomConstraint,\n\t\t\t\tkey: path || \"$root\",\n\t\t\t\texpected: name,\n\t\t\t\treceived: \"unknown constraint (not registered)\",\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await validator(value, params, context);\n\t\t\tif (!result.valid) {\n\t\t\t\terrors.push({\n\t\t\t\t\ttype: SchemaErrorType.CustomConstraint,\n\t\t\t\t\tkey: path || \"$root\",\n\t\t\t\t\texpected: name,\n\t\t\t\t\treceived: result.message ?? \"constraint validation failed\",\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (err) {\n\t\t\terrors.push({\n\t\t\t\ttype: SchemaErrorType.CustomConstraint,\n\t\t\t\tkey: path || \"$root\",\n\t\t\t\texpected: name,\n\t\t\t\treceived:\n\t\t\t\t\terr instanceof Error ? err.message : \"constraint validation error\",\n\t\t\t});\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/**\n * Recursively validates runtime data against all `constraints` found\n * in a schema, using the provided validator registry.\n *\n * Walks into: root-level constraints, `properties`, `patternProperties`,\n * `items` (single schema and tuple form), `additionalProperties` (schema form),\n * `dependencies` (schema form).\n *\n * When a schema declares a constraint that is not present in the registry,\n * an \"unknown constraint (not registered)\" error is produced. This ensures\n * that unregistered constraints are never silently ignored at runtime.\n *\n * @param schema - The resolved/narrowed schema containing constraints\n * @param data - The runtime data to validate\n * @param registry - The constraint validator registry (may be empty)\n * @param path - The current property path (for error reporting)\n * @returns Array of schema errors (empty if all constraints pass)\n */\nexport async function validateSchemaConstraints(\n\tschema: JSONSchema7Definition,\n\tdata: unknown,\n\tregistry: ConstraintValidatorRegistry,\n\tcontext?: ConstraintExecutionContext,\n\tpath = \"\",\n): Promise<SchemaError[]> {\n\t// Boolean schemas → nothing to validate\n\tif (typeof schema === \"boolean\") return [];\n\n\tconst errors: SchemaError[] = [];\n\n\t// ── Root-level constraints ──\n\tconst constraints = toConstraintArray(schema.constraints);\n\tif (constraints.length > 0) {\n\t\terrors.push(\n\t\t\t...(await validateValue(constraints, data, registry, path, context)),\n\t\t);\n\t}\n\n\t// ── Recurse into properties ──\n\tif (isPlainObj(schema.properties) && isPlainObj(data)) {\n\t\tconst props = schema.properties as Record<string, JSONSchema7Definition>;\n\t\tconst dataObj = data as Record<string, unknown>;\n\n\t\tfor (const key of Object.keys(props)) {\n\t\t\tconst propSchema = props[key];\n\t\t\tif (propSchema === undefined) continue;\n\n\t\t\tconst propValue = dataObj[key];\n\t\t\t// Only validate if the property exists in the data\n\t\t\tif (propValue === undefined && !hasOwn(dataObj, key)) continue;\n\n\t\t\tconst propPath = path ? `${path}.${key}` : key;\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\tpropSchema,\n\t\t\t\t\tpropValue,\n\t\t\t\t\tregistry,\n\t\t\t\t\tcontext,\n\t\t\t\t\tpropPath,\n\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Recurse into items (single schema) ──\n\tif (isPlainObj(schema.items) && Array.isArray(data)) {\n\t\tconst itemSchema = schema.items as JSONSchema7Definition;\n\t\tconst itemPath = path ? `${path}[]` : \"[]\";\n\n\t\tfor (let i = 0; i < data.length; i++) {\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\titemSchema,\n\t\t\t\t\tdata[i],\n\t\t\t\t\tregistry,\n\t\t\t\t\tcontext,\n\t\t\t\t\titemPath,\n\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Recurse into tuple items ──\n\tif (Array.isArray(schema.items) && Array.isArray(data)) {\n\t\tconst tupleSchemas = schema.items as JSONSchema7Definition[];\n\t\tfor (let i = 0; i < tupleSchemas.length && i < data.length; i++) {\n\t\t\tconst itemSchema = tupleSchemas[i];\n\t\t\tif (itemSchema === undefined) continue;\n\t\t\tconst itemPath = path ? `${path}[${i}]` : `[${i}]`;\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\titemSchema,\n\t\t\t\t\tdata[i],\n\t\t\t\t\tregistry,\n\t\t\t\t\tcontext,\n\t\t\t\t\titemPath,\n\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Recurse into patternProperties ──\n\tif (isPlainObj(schema.patternProperties) && isPlainObj(data)) {\n\t\tconst pp = schema.patternProperties as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition\n\t\t>;\n\t\tconst dataObj = data as Record<string, unknown>;\n\n\t\tfor (const pattern of Object.keys(pp)) {\n\t\t\tconst patternSchema = pp[pattern];\n\t\t\tif (patternSchema === undefined || typeof patternSchema === \"boolean\")\n\t\t\t\tcontinue;\n\n\t\t\tlet regex: RegExp;\n\t\t\ttry {\n\t\t\t\tregex = new RegExp(pattern);\n\t\t\t} catch {\n\t\t\t\t// Invalid regex pattern — skip silently (same approach as AJV)\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const dataKey of Object.keys(dataObj)) {\n\t\t\t\tif (!regex.test(dataKey)) continue;\n\n\t\t\t\tconst dataValue = dataObj[dataKey];\n\t\t\t\tconst ppPath = path ? `${path}.${dataKey}` : dataKey;\n\t\t\t\terrors.push(\n\t\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\t\tpatternSchema,\n\t\t\t\t\t\tdataValue,\n\t\t\t\t\t\tregistry,\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\tppPath,\n\t\t\t\t\t)),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ── Recurse into additionalProperties (schema form) ──\n\tif (\n\t\tisPlainObj(schema.additionalProperties) &&\n\t\ttypeof schema.additionalProperties !== \"boolean\" &&\n\t\tisPlainObj(data)\n\t) {\n\t\tconst apSchema = schema.additionalProperties as JSONSchema7Definition;\n\t\tconst dataObj = data as Record<string, unknown>;\n\t\tconst definedProps = isPlainObj(schema.properties)\n\t\t\t? new Set(Object.keys(schema.properties as Record<string, unknown>))\n\t\t\t: new Set<string>();\n\n\t\t// Collect patternProperties regexes to exclude matching keys\n\t\tconst ppPatterns: RegExp[] = [];\n\t\tif (isPlainObj(schema.patternProperties)) {\n\t\t\tfor (const pattern of Object.keys(\n\t\t\t\tschema.patternProperties as Record<string, unknown>,\n\t\t\t)) {\n\t\t\t\ttry {\n\t\t\t\t\tppPatterns.push(new RegExp(pattern));\n\t\t\t\t} catch {\n\t\t\t\t\t// Invalid pattern — skip\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (const dataKey of Object.keys(dataObj)) {\n\t\t\t// Skip keys defined in properties\n\t\t\tif (definedProps.has(dataKey)) continue;\n\n\t\t\t// Skip keys matching any patternProperties pattern\n\t\t\tif (ppPatterns.some((re) => re.test(dataKey))) continue;\n\n\t\t\tconst dataValue = dataObj[dataKey];\n\t\t\tconst apPath = path ? `${path}.${dataKey}` : dataKey;\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\tapSchema,\n\t\t\t\t\tdataValue,\n\t\t\t\t\tregistry,\n\t\t\t\t\tcontext,\n\t\t\t\t\tapPath,\n\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Recurse into dependencies (schema form) ──\n\tif (isPlainObj(schema.dependencies) && isPlainObj(data)) {\n\t\tconst deps = schema.dependencies as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition | string[]\n\t\t>;\n\t\tconst dataObj = data as Record<string, unknown>;\n\n\t\tfor (const depKey of Object.keys(deps)) {\n\t\t\t// Dependency only applies if the trigger key is present in data\n\t\t\tif (!hasOwn(dataObj, depKey)) continue;\n\n\t\t\tconst depValue = deps[depKey];\n\t\t\tif (depValue === undefined) continue;\n\n\t\t\t// Skip array-form dependencies (property deps, not schema deps)\n\t\t\tif (Array.isArray(depValue)) continue;\n\n\t\t\t// Skip boolean schemas\n\t\t\tif (typeof depValue === \"boolean\") continue;\n\n\t\t\t// Schema-form dependency: validate the entire data object against it\n\t\t\t// The dependency schema applies to the whole object, not just the dep key\n\t\t\terrors.push(\n\t\t\t\t...(await validateSchemaConstraints(\n\t\t\t\t\tdepValue,\n\t\t\t\t\tdata,\n\t\t\t\t\tregistry,\n\t\t\t\t\tcontext,\n\t\t\t\t\tpath,\n\t\t\t\t)),\n\t\t\t);\n\t\t}\n\t}\n\n\treturn errors;\n}\n"],"names":["SchemaErrorType","hasOwn","isPlainObj","toConstraintArray","validateValue","constraints","value","registry","path","context","errors","constraint","name","params","undefined","validator","push","type","CustomConstraint","key","expected","received","result","valid","message","err","Error","validateSchemaConstraints","schema","data","length","properties","props","dataObj","Object","keys","propSchema","propValue","propPath","items","Array","isArray","itemSchema","itemPath","i","tupleSchemas","patternProperties","pp","pattern","patternSchema","regex","RegExp","dataKey","test","dataValue","ppPath","additionalProperties","apSchema","definedProps","Set","ppPatterns","has","some","re","apPath","dependencies","deps","depKey","depValue"],"mappings":"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"}
@@ -3,5 +3,5 @@ export { JsonSchemaCompatibilityChecker } from "./json-schema-compatibility-chec
3
3
  export { MergeEngine } from "./merge-engine.js";
4
4
  export { arePatternsEquivalent, isPatternSubset, isTrivialPattern, } from "./pattern-subset.js";
5
5
  export { formatSchemaType } from "./semantic-errors.js";
6
- export type { CheckerOptions, CheckRuntimeOptions, Constraint, Constraints, ConstraintValidationResult, ConstraintValidator, ConstraintValidatorRegistry, ResolvedConditionResult, ResolvedSubsetResult, SchemaError, SubsetResult, ValidateTargetOptions, ValidateTargets, } from "./types.js";
6
+ export type { CheckerOptions, CheckRuntimeOptions, Constraint, ConstraintExecutionContext, Constraints, ConstraintValidationResult, ConstraintValidator, ConstraintValidatorRegistry, ResolvedConditionResult, ResolvedSubsetResult, SchemaError, SubsetResult, ValidateTargetOptions, ValidateTargets, } from "./types.js";
7
7
  export { SchemaErrorType } from "./types.js";
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["export { resolveConditions } from \"./condition-resolver.ts\";\nexport { JsonSchemaCompatibilityChecker } from \"./json-schema-compatibility-checker.ts\";\nexport { MergeEngine } from \"./merge-engine.ts\";\nexport {\n\tarePatternsEquivalent,\n\tisPatternSubset,\n\tisTrivialPattern,\n} from \"./pattern-subset.ts\";\nexport { formatSchemaType } from \"./semantic-errors.ts\";\nexport type {\n\tCheckerOptions,\n\tCheckRuntimeOptions,\n\tConstraint,\n\tConstraints,\n\tConstraintValidationResult,\n\tConstraintValidator,\n\tConstraintValidatorRegistry,\n\tResolvedConditionResult,\n\tResolvedSubsetResult,\n\tSchemaError,\n\tSubsetResult,\n\tValidateTargetOptions,\n\tValidateTargets,\n} from \"./types.ts\";\nexport { SchemaErrorType } from \"./types.ts\";\n"],"names":["resolveConditions","JsonSchemaCompatibilityChecker","MergeEngine","arePatternsEquivalent","isPatternSubset","isTrivialPattern","formatSchemaType","SchemaErrorType"],"mappings":"AAAA,OAASA,iBAAiB,KAAQ,yBAA0B,AAC5D,QAASC,8BAA8B,KAAQ,wCAAyC,AACxF,QAASC,WAAW,KAAQ,mBAAoB,AAChD,QACCC,qBAAqB,CACrBC,eAAe,CACfC,gBAAgB,KACV,qBAAsB,AAC7B,QAASC,gBAAgB,KAAQ,sBAAuB,AAgBxD,QAASC,eAAe,KAAQ,YAAa"}
1
+ {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["export { resolveConditions } from \"./condition-resolver.ts\";\nexport { JsonSchemaCompatibilityChecker } from \"./json-schema-compatibility-checker.ts\";\nexport { MergeEngine } from \"./merge-engine.ts\";\nexport {\n\tarePatternsEquivalent,\n\tisPatternSubset,\n\tisTrivialPattern,\n} from \"./pattern-subset.ts\";\nexport { formatSchemaType } from \"./semantic-errors.ts\";\nexport type {\n\tCheckerOptions,\n\tCheckRuntimeOptions,\n\tConstraint,\n\tConstraintExecutionContext,\n\tConstraints,\n\tConstraintValidationResult,\n\tConstraintValidator,\n\tConstraintValidatorRegistry,\n\tResolvedConditionResult,\n\tResolvedSubsetResult,\n\tSchemaError,\n\tSubsetResult,\n\tValidateTargetOptions,\n\tValidateTargets,\n} from \"./types.ts\";\nexport { SchemaErrorType } from \"./types.ts\";\n"],"names":["resolveConditions","JsonSchemaCompatibilityChecker","MergeEngine","arePatternsEquivalent","isPatternSubset","isTrivialPattern","formatSchemaType","SchemaErrorType"],"mappings":"AAAA,OAASA,iBAAiB,KAAQ,yBAA0B,AAC5D,QAASC,8BAA8B,KAAQ,wCAAyC,AACxF,QAASC,WAAW,KAAQ,mBAAoB,AAChD,QACCC,qBAAqB,CACrBC,eAAe,CACfC,gBAAgB,KACV,qBAAsB,AAC7B,QAASC,gBAAgB,KAAQ,sBAAuB,AAiBxD,QAASC,eAAe,KAAQ,YAAa"}
@@ -1,2 +1,2 @@
1
- function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}import{resolveConditions}from"./condition-resolver.js";import{validateSchemaConstraints}from"./constraint-validator.js";import{narrowSchemaWithData}from"./data-narrowing.js";import{formatResult}from"./formatter.js";import{MergeEngine}from"./merge-engine.js";import{normalize}from"./normalizer.js";import{arePatternsEquivalent,isPatternSubset,isTrivialPattern}from"./pattern-subset.js";import{clearAllValidatorCaches,getPartialRuntimeValidationErrors,getRuntimeValidationErrors}from"./runtime-validator.js";import{checkAtomic,checkBranchedSub,checkBranchedSup,getBranchesTyped,isAtomicSubsetOf}from"./subset-checker.js";import{SchemaErrorType}from"./types.js";import{deepEqual,isPlainObj}from"./utils.js";import{resolveValidateTargets}from"./validate-targets.js";export{SchemaErrorType,normalize,resolveConditions,formatResult,MergeEngine,isPatternSubset,arePatternsEquivalent,isTrivialPattern};export class JsonSchemaCompatibilityChecker{isSubset(sub,sup){if(sub===sup)return true;if(deepEqual(sub,sup))return true;const nSub=normalize(sub);const nSup=normalize(sup);if(nSub!==sub&&nSup!==sup&&deepEqual(nSub,nSup))return true;if(nSub!==nSup&&deepEqual(nSub,nSup))return true;const{branches:subBranches}=getBranchesTyped(nSub);if(subBranches.length>1||subBranches[0]!==nSub){return subBranches.every(branch=>isAtomicSubsetOf(branch,nSup,this.engine))}return isAtomicSubsetOf(nSub,nSup,this.engine)}check(sub,sup,options){if(options){return this.checkWithOptions(sub,sup,options)}return this.checkInternal(sub,sup)}isEqual(a,b){return this.engine.isEqual(normalize(a),normalize(b))}intersect(a,b){if(a===b||deepEqual(a,b))return normalize(a);const nA=normalize(a);const nB=normalize(b);if(deepEqual(nA,nB))return nA;const merged=this.engine.merge(nA,nB);if(merged===null)return null;if(deepEqual(merged,nA)||deepEqual(merged,nB))return merged;return normalize(merged)}normalize(def){return normalize(def)}formatResult(label,result){return formatResult(label,result)}resolveConditions(schema,data){return resolveConditions(schema,data,this.engine)}async checkWithOptions(sub,sup,options){const data=options.data;const{sub:validateSub,sup:validateSup,partialSub,partialSup}=resolveValidateTargets(options.validate);const dataForConditions=isPlainObj(data)?data:{};const resolvedSub=resolveConditions(sub,dataForConditions,this.engine);const resolvedSup=resolveConditions(sup,dataForConditions,this.engine);const canNarrow=data!==undefined;const canNarrowSub=canNarrow&&isPlainObj(resolvedSub.resolved);const canNarrowSup=canNarrow&&isPlainObj(resolvedSup.resolved);const narrowedSubResolved=canNarrowSub?narrowSchemaWithData(resolvedSub.resolved,data,resolvedSup.resolved):resolvedSub.resolved;const narrowedSupResolved=canNarrowSup?narrowSchemaWithData(resolvedSup.resolved,data,resolvedSub.resolved):resolvedSup.resolved;const staticResult=this.checkInternal(narrowedSubResolved,narrowedSupResolved);if(!staticResult.isSubset){return{...staticResult,resolvedSub:{...resolvedSub,resolved:narrowedSubResolved},resolvedSup:{...resolvedSup,resolved:narrowedSupResolved}}}if((validateSub||validateSup)&&data!==undefined){const runtimeErrors=[];if(validateSub){const getErrors=partialSub?getPartialRuntimeValidationErrors:getRuntimeValidationErrors;runtimeErrors.push(...this.prefixRuntimeErrors(getErrors(narrowedSubResolved,data),"$sub"))}if(validateSup){const getErrors=partialSup?getPartialRuntimeValidationErrors:getRuntimeValidationErrors;runtimeErrors.push(...this.prefixRuntimeErrors(getErrors(narrowedSupResolved,data),"$sup"))}if(validateSub){runtimeErrors.push(...this.prefixRuntimeErrors(await validateSchemaConstraints(narrowedSubResolved,data,this.constraintValidators),"$sub"))}if(validateSup){runtimeErrors.push(...this.prefixRuntimeErrors(await validateSchemaConstraints(narrowedSupResolved,data,this.constraintValidators),"$sup"))}if(runtimeErrors.length>0){return{isSubset:false,merged:null,errors:runtimeErrors,resolvedSub:{...resolvedSub,resolved:narrowedSubResolved},resolvedSup:{...resolvedSup,resolved:narrowedSupResolved}}}}return{...staticResult,resolvedSub:{...resolvedSub,resolved:narrowedSubResolved},resolvedSup:{...resolvedSup,resolved:narrowedSupResolved}}}prefixRuntimeErrors(errors,rootKey){return errors.map(error=>({...error,key:error.key==="$root"?rootKey:`${rootKey}.${error.key}`}))}checkInternal(sub,sup){if(sub===sup){return{isSubset:true,merged:sub,errors:[]}}if(deepEqual(sub,sup)){return{isSubset:true,merged:sub,errors:[]}}const nSub=normalize(sub);const nSup=normalize(sup);if(deepEqual(nSub,nSup)){return{isSubset:true,merged:nSub,errors:[]}}const{branches:subBranches,type:subBranchType}=getBranchesTyped(nSub);const{branches:supBranches,type:supBranchType}=getBranchesTyped(nSup);if(subBranches.length>1||subBranches[0]!==nSub){return checkBranchedSub(subBranches,nSup,this.engine,subBranchType)}if(supBranches.length>1||supBranches[0]!==nSup){return checkBranchedSup(nSub,supBranches,this.engine,supBranchType)}return checkAtomic(nSub,nSup,this.engine)}static clearCache(){clearAllValidatorCaches()}constructor(options){_define_property(this,"constraintValidators",void 0);_define_property(this,"engine",void 0);this.engine=new MergeEngine;this.constraintValidators=options?.constraints??{}}}
1
+ function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}import{resolveConditions}from"./condition-resolver.js";import{validateSchemaConstraints}from"./constraint-validator.js";import{narrowSchemaWithData}from"./data-narrowing.js";import{formatResult}from"./formatter.js";import{MergeEngine}from"./merge-engine.js";import{normalize}from"./normalizer.js";import{arePatternsEquivalent,isPatternSubset,isTrivialPattern}from"./pattern-subset.js";import{clearAllValidatorCaches,getPartialRuntimeValidationErrors,getRuntimeValidationErrors}from"./runtime-validator.js";import{checkAtomic,checkBranchedSub,checkBranchedSup,getBranchesTyped,isAtomicSubsetOf}from"./subset-checker.js";import{SchemaErrorType}from"./types.js";import{deepEqual,isPlainObj}from"./utils.js";import{resolveValidateTargets}from"./validate-targets.js";export{SchemaErrorType,normalize,resolveConditions,formatResult,MergeEngine,isPatternSubset,arePatternsEquivalent,isTrivialPattern};export class JsonSchemaCompatibilityChecker{isSubset(sub,sup){if(sub===sup)return true;if(deepEqual(sub,sup))return true;const nSub=normalize(sub);const nSup=normalize(sup);if(nSub!==sub&&nSup!==sup&&deepEqual(nSub,nSup))return true;if(nSub!==nSup&&deepEqual(nSub,nSup))return true;const{branches:subBranches}=getBranchesTyped(nSub);if(subBranches.length>1||subBranches[0]!==nSub){return subBranches.every(branch=>isAtomicSubsetOf(branch,nSup,this.engine))}return isAtomicSubsetOf(nSub,nSup,this.engine)}check(sub,sup,options){if(options){return this.checkWithOptions(sub,sup,options)}return this.checkInternal(sub,sup)}isEqual(a,b){return this.engine.isEqual(normalize(a),normalize(b))}intersect(a,b){if(a===b||deepEqual(a,b))return normalize(a);const nA=normalize(a);const nB=normalize(b);if(deepEqual(nA,nB))return nA;const merged=this.engine.merge(nA,nB);if(merged===null)return null;if(deepEqual(merged,nA)||deepEqual(merged,nB))return merged;return normalize(merged)}normalize(def){return normalize(def)}formatResult(label,result){return formatResult(label,result)}resolveConditions(schema,data){return resolveConditions(schema,data,this.engine)}async checkWithOptions(sub,sup,options){const data=options.data;const constraintContext=options.constraintContext;const{sub:validateSub,sup:validateSup,partialSub,partialSup}=resolveValidateTargets(options.validate);const dataForConditions=isPlainObj(data)?data:{};const resolvedSub=resolveConditions(sub,dataForConditions,this.engine);const resolvedSup=resolveConditions(sup,dataForConditions,this.engine);const canNarrow=data!==undefined;const canNarrowSub=canNarrow&&isPlainObj(resolvedSub.resolved);const canNarrowSup=canNarrow&&isPlainObj(resolvedSup.resolved);const narrowedSubResolved=canNarrowSub?narrowSchemaWithData(resolvedSub.resolved,data,resolvedSup.resolved):resolvedSub.resolved;const narrowedSupResolved=canNarrowSup?narrowSchemaWithData(resolvedSup.resolved,data,resolvedSub.resolved):resolvedSup.resolved;const staticResult=this.checkInternal(narrowedSubResolved,narrowedSupResolved);if(!staticResult.isSubset){return{...staticResult,resolvedSub:{...resolvedSub,resolved:narrowedSubResolved},resolvedSup:{...resolvedSup,resolved:narrowedSupResolved}}}if((validateSub||validateSup)&&data!==undefined){const runtimeErrors=[];if(validateSub){const getErrors=partialSub?getPartialRuntimeValidationErrors:getRuntimeValidationErrors;runtimeErrors.push(...this.prefixRuntimeErrors(getErrors(narrowedSubResolved,data),"$sub"))}if(validateSup){const getErrors=partialSup?getPartialRuntimeValidationErrors:getRuntimeValidationErrors;runtimeErrors.push(...this.prefixRuntimeErrors(getErrors(narrowedSupResolved,data),"$sup"))}if(validateSub){runtimeErrors.push(...this.prefixRuntimeErrors(await validateSchemaConstraints(narrowedSubResolved,data,this.constraintValidators,constraintContext),"$sub"))}if(validateSup){runtimeErrors.push(...this.prefixRuntimeErrors(await validateSchemaConstraints(narrowedSupResolved,data,this.constraintValidators,constraintContext),"$sup"))}if(runtimeErrors.length>0){return{isSubset:false,merged:null,errors:runtimeErrors,resolvedSub:{...resolvedSub,resolved:narrowedSubResolved},resolvedSup:{...resolvedSup,resolved:narrowedSupResolved}}}}return{...staticResult,resolvedSub:{...resolvedSub,resolved:narrowedSubResolved},resolvedSup:{...resolvedSup,resolved:narrowedSupResolved}}}prefixRuntimeErrors(errors,rootKey){return errors.map(error=>({...error,key:error.key==="$root"?rootKey:`${rootKey}.${error.key}`}))}checkInternal(sub,sup){if(sub===sup){return{isSubset:true,merged:sub,errors:[]}}if(deepEqual(sub,sup)){return{isSubset:true,merged:sub,errors:[]}}const nSub=normalize(sub);const nSup=normalize(sup);if(deepEqual(nSub,nSup)){return{isSubset:true,merged:nSub,errors:[]}}const{branches:subBranches,type:subBranchType}=getBranchesTyped(nSub);const{branches:supBranches,type:supBranchType}=getBranchesTyped(nSup);if(subBranches.length>1||subBranches[0]!==nSub){return checkBranchedSub(subBranches,nSup,this.engine,subBranchType)}if(supBranches.length>1||supBranches[0]!==nSup){return checkBranchedSup(nSub,supBranches,this.engine,supBranchType)}return checkAtomic(nSub,nSup,this.engine)}static clearCache(){clearAllValidatorCaches()}constructor(options){_define_property(this,"constraintValidators",void 0);_define_property(this,"engine",void 0);this.engine=new MergeEngine;this.constraintValidators=options?.constraints??{}}}
2
2
  //# sourceMappingURL=json-schema-compatibility-checker.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/json-schema-compatibility-checker.ts"],"sourcesContent":["import type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\nimport { resolveConditions } from \"./condition-resolver.ts\";\nimport { validateSchemaConstraints } from \"./constraint-validator.ts\";\nimport { narrowSchemaWithData } from \"./data-narrowing.ts\";\nimport { formatResult } from \"./formatter.ts\";\nimport { MergeEngine } from \"./merge-engine.ts\";\nimport { normalize } from \"./normalizer.ts\";\nimport {\n\tarePatternsEquivalent,\n\tisPatternSubset,\n\tisTrivialPattern,\n} from \"./pattern-subset.ts\";\nimport {\n\tclearAllValidatorCaches,\n\tgetPartialRuntimeValidationErrors,\n\tgetRuntimeValidationErrors,\n} from \"./runtime-validator.ts\";\nimport type { BranchResult, BranchType } from \"./subset-checker.ts\";\nimport {\n\tcheckAtomic,\n\tcheckBranchedSub,\n\tcheckBranchedSup,\n\tgetBranchesTyped,\n\tisAtomicSubsetOf,\n} from \"./subset-checker.ts\";\nimport type {\n\tCheckerOptions,\n\tCheckRuntimeOptions,\n\tConstraintValidatorRegistry,\n\tResolvedConditionResult,\n\tResolvedSubsetResult,\n\tSchemaError,\n\tSubsetResult,\n\tValidateTargets,\n} from \"./types.ts\";\nimport { SchemaErrorType } from \"./types.ts\";\nimport { deepEqual, isPlainObj } from \"./utils.ts\";\nimport { resolveValidateTargets } from \"./validate-targets.ts\";\n\n// ─── Re-exports ──────────────────────────────────────────────────────────────\n\nexport type {\n\tSchemaError,\n\tSubsetResult,\n\tResolvedConditionResult,\n\tResolvedSubsetResult,\n\tCheckRuntimeOptions,\n\tValidateTargets,\n\tBranchType,\n\tBranchResult,\n};\n\nexport {\n\tSchemaErrorType,\n\tnormalize,\n\tresolveConditions,\n\tformatResult,\n\tMergeEngine,\n\tisPatternSubset,\n\tarePatternsEquivalent,\n\tisTrivialPattern,\n};\n\n// ─── Main Class ──────────────────────────────────────────────────────────────\n//\n// Lightweight facade that orchestrates sub-modules to verify compatibility\n// between JSON Schemas (Draft-07).\n//\n// Mathematical principle:\n// A ⊆ B ⟺ A ∩ B ≡ A\n//\n// In JSON Schema terms:\n// - A ∩ B = allOf([A, B]) resolved via merge\n// - ≡ = structural comparison\n//\n// @example\n// ```ts\n// const checker = new JsonSchemaCompatibilityChecker();\n//\n// checker.isSubset(strict, loose); // true\n// checker.check(loose, strict); // { isSubset: false, diffs: [...] }\n// checker.check(sub, sup, { data: {...} }); // resolves conditions then checks\n// ```\n\nexport class JsonSchemaCompatibilityChecker {\n\tprivate readonly constraintValidators: ConstraintValidatorRegistry;\n\tprivate readonly engine: MergeEngine;\n\n\tconstructor(options?: CheckerOptions) {\n\t\tthis.engine = new MergeEngine();\n\t\tthis.constraintValidators = options?.constraints ?? {};\n\t}\n\n\t// ── Subset check (boolean) ─────────────────────────────────────────────\n\n\t/**\n\t * Checks whether `sub ⊆ sup`.\n\t * Is every value valid for sub also valid for sup?\n\t *\n\t * Uses `getBranchesTyped` to distinguish `anyOf` from `oneOf`\n\t * internally, although the boolean result does not reflect the distinction.\n\t */\n\tisSubset(sub: JSONSchema7Definition, sup: JSONSchema7Definition): boolean {\n\t\t// ── Identity short-circuit ──\n\t\t// If sub and sup are the same reference, sub ⊆ sup is trivially true.\n\t\t// This avoids the entire normalize + merge + compare pipeline.\n\t\tif (sub === sup) return true;\n\n\t\t// ── Pre-normalize structural equality ──\n\t\t// If sub and sup are structurally identical before normalization,\n\t\t// they represent the same schema → sub ⊆ sup trivially.\n\t\t// This avoids the WeakMap overhead of normalize() for common cases\n\t\t// like {} ⊆ {} or identical schema objects with different references.\n\t\tif (deepEqual(sub, sup)) return true;\n\n\t\tconst nSub = normalize(sub);\n\t\tconst nSup = normalize(sup);\n\n\t\t// ── Post-normalize structural identity ──\n\t\t// After normalization, schemas that were syntactically different\n\t\t// but semantically equivalent become structurally equal\n\t\t// (e.g. {const:1} vs {const:1, type:\"integer\"}).\n\t\tif (nSub !== sub && nSup !== sup && deepEqual(nSub, nSup)) return true;\n\t\tif (nSub !== nSup && deepEqual(nSub, nSup)) return true;\n\n\t\tconst { branches: subBranches } = getBranchesTyped(nSub);\n\n\t\tif (subBranches.length > 1 || subBranches[0] !== nSub) {\n\t\t\treturn subBranches.every((branch) =>\n\t\t\t\tisAtomicSubsetOf(branch, nSup, this.engine),\n\t\t\t);\n\t\t}\n\n\t\treturn isAtomicSubsetOf(nSub, nSup, this.engine);\n\t}\n\n\t// ── Subset check (detailed) ────────────────────────────────────────────\n\n\t/**\n\t * Checks `sub ⊆ sup` and returns a detailed diagnostic\n\t * with human-readable semantic errors.\n\t *\n\t * When `options` is provided, both schemas go through runtime-aware\n\t * processing before the static check:\n\t * 1. Conditions (`if/then/else`) are resolved using `data`\n\t * (if `data` is `undefined`, conditions are resolved with `{}`)\n\t * 2. Schemas are narrowed using runtime values (enum materialization)\n\t * 3. The static subset check runs on the resolved/narrowed schemas\n\t *\n\t * When `validate` is enabled, additional runtime steps run **after** the\n\t * static check passes:\n\t * 4. `data` is validated against the targeted resolved schema(s) via AJV\n\t * 5. Custom constraints are validated against `data` for the targeted schema(s)\n\t *\n\t * `validate` accepts:\n\t * - `true` — validate against **both** sub and sup\n\t * - `{ sub: true }` — validate only against the sub schema\n\t * - `{ sup: true }` — validate only against the sup schema\n\t * - `{ sub: true, sup: true }` — equivalent to `true`\n\t *\n\t * @param sub - The source schema (subset candidate)\n\t * @param sup - The target schema (expected superset)\n\t * @param options - Runtime options with `data` and optional `validate` flag\n\t * @returns SubsetResult if no options, ResolvedSubsetResult if options provided\n\t *\n\t * @example\n\t * ```ts\n\t * // Static check (no runtime data)\n\t * checker.check(sub, sup);\n\t *\n\t * // Resolve conditions + narrowing + static check (no runtime validation)\n\t * checker.check(sub, sup, { data: { kind: \"text\", value: \"hello\" } });\n\t *\n\t * // Full pipeline including AJV + constraint runtime validation\n\t * checker.check(sub, sup, { data: { kind: \"text\", value: \"hello\" }, validate: true });\n\t * ```\n\t */\n\tcheck(\n\t\tsub: JSONSchema7Definition,\n\t\tsup: JSONSchema7Definition,\n\t\toptions: CheckRuntimeOptions,\n\t): Promise<ResolvedSubsetResult>;\n\tcheck(sub: JSONSchema7Definition, sup: JSONSchema7Definition): SubsetResult;\n\tcheck(\n\t\tsub: JSONSchema7Definition,\n\t\tsup: JSONSchema7Definition,\n\t\toptions?: CheckRuntimeOptions,\n\t): SubsetResult | Promise<ResolvedSubsetResult> {\n\t\t// ── Runtime-aware path ──\n\t\tif (options) {\n\t\t\treturn this.checkWithOptions(sub, sup, options);\n\t\t}\n\n\t\t// ── Standard path (no condition resolution) ──\n\t\treturn this.checkInternal(sub, sup);\n\t}\n\n\t// ── Equality ───────────────────────────────────────────────────────────\n\n\t/**\n\t * Checks structural equality between two schemas.\n\t */\n\tisEqual(a: JSONSchema7Definition, b: JSONSchema7Definition): boolean {\n\t\treturn this.engine.isEqual(normalize(a), normalize(b));\n\t}\n\n\t// ── Intersection ───────────────────────────────────────────────────────\n\n\t/**\n\t * Computes the intersection of two schemas (allOf merge).\n\t * Returns null if the schemas are incompatible.\n\t *\n\t * The result is normalized to eliminate structural artifacts\n\t * from the merge (e.g. redundant `enum` when `const` is present).\n\t */\n\tintersect(\n\t\ta: JSONSchema7Definition,\n\t\tb: JSONSchema7Definition,\n\t): JSONSchema7Definition | null {\n\t\t// ── Identity short-circuit ──\n\t\t// If a and b are the same reference or structurally equal,\n\t\t// intersection is just normalize(a) — skip the merge entirely.\n\t\tif (a === b || deepEqual(a, b)) return normalize(a);\n\n\t\tconst nA = normalize(a);\n\t\tconst nB = normalize(b);\n\n\t\t// ── Post-normalize identity ──\n\t\tif (deepEqual(nA, nB)) return nA;\n\n\t\tconst merged = this.engine.merge(nA, nB);\n\t\tif (merged === null) return null;\n\t\t// Fast path: if merge result equals one of the normalized inputs,\n\t\t// it's already normalized — skip redundant normalize call.\n\t\tif (deepEqual(merged, nA) || deepEqual(merged, nB)) return merged;\n\t\treturn normalize(merged);\n\t}\n\n\t// ── Normalization ──────────────────────────────────────────────────────\n\n\t/**\n\t * Normalizes a schema: infers `type` from `const`/`enum`,\n\t * and recursively normalizes all sub-schemas.\n\t */\n\tnormalize(def: JSONSchema7Definition): JSONSchema7Definition {\n\t\treturn normalize(def);\n\t}\n\n\t// ── Formatting ─────────────────────────────────────────────────────────\n\n\t/**\n\t * Formats a SubsetResult into a readable string (useful for logs/debug).\n\t */\n\tformatResult(label: string, result: SubsetResult): string {\n\t\treturn formatResult(label, result);\n\t}\n\n\t// ── Condition Resolution ────────────────────────────────────────────────\n\n\t/**\n\t * Resolves `if/then/else` conditions in a schema by evaluating the `if`\n\t * against runtime data.\n\t *\n\t * @param schema - The schema containing conditions to resolve\n\t * @param data - The runtime data used to evaluate conditions\n\t * @returns The resolved schema with branch info and discriminants\n\t */\n\tresolveConditions(\n\t\tschema: JSONSchema7,\n\t\tdata: Record<string, unknown>,\n\t): ResolvedConditionResult {\n\t\treturn resolveConditions(schema, data, this.engine);\n\t}\n\n\t// ── Private ────────────────────────────────────────────────────────────\n\n\t/**\n\t * Internal runtime-aware check logic. Extracted as an async method\n\t * so that `check()` without options stays synchronous while the\n\t * runtime path can `await` async constraint validators.\n\t */\n\tprivate async checkWithOptions(\n\t\tsub: JSONSchema7Definition,\n\t\tsup: JSONSchema7Definition,\n\t\toptions: CheckRuntimeOptions,\n\t): Promise<ResolvedSubsetResult> {\n\t\tconst data = options.data;\n\t\tconst {\n\t\t\tsub: validateSub,\n\t\t\tsup: validateSup,\n\t\t\tpartialSub,\n\t\t\tpartialSup,\n\t\t} = resolveValidateTargets(options.validate);\n\n\t\t// resolveConditions expects Record<string, unknown> for property access;\n\t\t// coerce non-object / undefined data to empty object so conditions\n\t\t// are always resolved (v1.0.11 compat: subData: undefined → {})\n\t\tconst dataForConditions: Record<string, unknown> = isPlainObj(data)\n\t\t\t? data\n\t\t\t: {};\n\n\t\tconst resolvedSub = resolveConditions(\n\t\t\tsub as JSONSchema7,\n\t\t\tdataForConditions,\n\t\t\tthis.engine,\n\t\t);\n\t\tconst resolvedSup = resolveConditions(\n\t\t\tsup as JSONSchema7,\n\t\t\tdataForConditions,\n\t\t\tthis.engine,\n\t\t);\n\n\t\t// ── Runtime-aware data narrowing ──\n\t\t// Apply narrowing only when concrete data is available.\n\t\t// When data is undefined there is nothing to narrow with.\n\t\t// Boolean schemas (true/false) cannot be narrowed — skip narrowing\n\t\t// to avoid passing a non-object to narrowSchemaWithData.\n\t\tconst canNarrow = data !== undefined;\n\t\tconst canNarrowSub = canNarrow && isPlainObj(resolvedSub.resolved);\n\t\tconst canNarrowSup = canNarrow && isPlainObj(resolvedSup.resolved);\n\n\t\tconst narrowedSubResolved = canNarrowSub\n\t\t\t? narrowSchemaWithData(resolvedSub.resolved, data, resolvedSup.resolved)\n\t\t\t: resolvedSub.resolved;\n\n\t\tconst narrowedSupResolved = canNarrowSup\n\t\t\t? narrowSchemaWithData(resolvedSup.resolved, data, resolvedSub.resolved)\n\t\t\t: resolvedSup.resolved;\n\n\t\t// ── Static subset check ──\n\t\t// Structural incompatibilities are schema-level problems — they are\n\t\t// permanent regardless of the concrete data. Run this before runtime\n\t\t// validation so that static errors always surface with higher priority.\n\t\tconst staticResult = this.checkInternal(\n\t\t\tnarrowedSubResolved,\n\t\t\tnarrowedSupResolved,\n\t\t);\n\n\t\tif (!staticResult.isSubset) {\n\t\t\treturn {\n\t\t\t\t...staticResult,\n\t\t\t\tresolvedSub: { ...resolvedSub, resolved: narrowedSubResolved },\n\t\t\t\tresolvedSup: { ...resolvedSup, resolved: narrowedSupResolved },\n\t\t\t};\n\t\t}\n\n\t\t// ── Runtime validation (opt-in) ──\n\t\t// Runs when `validate` is truthy (boolean or object with sub/sup flags).\n\t\t// Validates the concrete data against the targeted resolved/narrowed\n\t\t// schema(s) via AJV, then runs custom constraint validators if registered.\n\t\tif ((validateSub || validateSup) && data !== undefined) {\n\t\t\tconst runtimeErrors: SchemaError[] = [];\n\n\t\t\t// ── AJV validation ──\n\t\t\t// When partial mode is active for a target, use\n\t\t\t// getPartialRuntimeValidationErrors which strips `required` and\n\t\t\t// `additionalProperties` before AJV compilation so that only\n\t\t\t// the properties present in data are validated.\n\t\t\tif (validateSub) {\n\t\t\t\tconst getErrors = partialSub\n\t\t\t\t\t? getPartialRuntimeValidationErrors\n\t\t\t\t\t: getRuntimeValidationErrors;\n\t\t\t\truntimeErrors.push(\n\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\tgetErrors(narrowedSubResolved, data),\n\t\t\t\t\t\t\"$sub\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (validateSup) {\n\t\t\t\tconst getErrors = partialSup\n\t\t\t\t\t? getPartialRuntimeValidationErrors\n\t\t\t\t\t: getRuntimeValidationErrors;\n\t\t\t\truntimeErrors.push(\n\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\tgetErrors(narrowedSupResolved, data),\n\t\t\t\t\t\t\"$sup\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// ── Constraint validation ──\n\t\t\t// Validate runtime data against custom constraints in the targeted\n\t\t\t// schema(s). If a schema declares constraints that are not registered\n\t\t\t// in the registry, validateSchemaConstraints will report them as\n\t\t\t// \"unknown constraint (not registered)\" errors.\n\t\t\t// Constraint validators may be async, so we await the results.\n\t\t\tif (validateSub) {\n\t\t\t\truntimeErrors.push(\n\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\tawait validateSchemaConstraints(\n\t\t\t\t\t\t\tnarrowedSubResolved,\n\t\t\t\t\t\t\tdata,\n\t\t\t\t\t\t\tthis.constraintValidators,\n\t\t\t\t\t\t),\n\t\t\t\t\t\t\"$sub\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (validateSup) {\n\t\t\t\truntimeErrors.push(\n\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\tawait validateSchemaConstraints(\n\t\t\t\t\t\t\tnarrowedSupResolved,\n\t\t\t\t\t\t\tdata,\n\t\t\t\t\t\t\tthis.constraintValidators,\n\t\t\t\t\t\t),\n\t\t\t\t\t\t\"$sup\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (runtimeErrors.length > 0) {\n\t\t\t\treturn {\n\t\t\t\t\tisSubset: false,\n\t\t\t\t\tmerged: null,\n\t\t\t\t\terrors: runtimeErrors,\n\t\t\t\t\tresolvedSub: { ...resolvedSub, resolved: narrowedSubResolved },\n\t\t\t\t\tresolvedSup: { ...resolvedSup, resolved: narrowedSupResolved },\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\t...staticResult,\n\t\t\tresolvedSub: { ...resolvedSub, resolved: narrowedSubResolved },\n\t\t\tresolvedSup: { ...resolvedSup, resolved: narrowedSupResolved },\n\t\t};\n\t}\n\n\tprivate prefixRuntimeErrors(\n\t\terrors: SchemaError[],\n\t\trootKey: \"$sub\" | \"$sup\",\n\t): SchemaError[] {\n\t\treturn errors.map((error) => ({\n\t\t\t...error,\n\t\t\tkey: error.key === \"$root\" ? rootKey : `${rootKey}.${error.key}`,\n\t\t}));\n\t}\n\n\t/**\n\t * Internal check logic without condition resolution.\n\t * Factorizes the normalize → branch → atomic pipeline to avoid\n\t * duplication between the two paths of `check()`.\n\t */\n\tprivate checkInternal(\n\t\tsub: JSONSchema7Definition,\n\t\tsup: JSONSchema7Definition,\n\t): SubsetResult {\n\t\t// ── Identity short-circuit ──\n\t\t// Same reference → no errors, no merge needed.\n\t\tif (sub === sup) {\n\t\t\treturn { isSubset: true, merged: sub, errors: [] };\n\t\t}\n\n\t\t// ── Pre-normalize structural equality ──\n\t\t// Avoids WeakMap overhead for identical schemas ({} ⊆ {}, etc.).\n\t\tif (deepEqual(sub, sup)) {\n\t\t\treturn { isSubset: true, merged: sub, errors: [] };\n\t\t}\n\n\t\tconst nSub = normalize(sub);\n\t\tconst nSup = normalize(sup);\n\n\t\t// ── Post-normalize structural identity ──\n\t\t// Catches semantically equivalent schemas after normalization.\n\t\tif (deepEqual(nSub, nSup)) {\n\t\t\treturn { isSubset: true, merged: nSub, errors: [] };\n\t\t}\n\n\t\tconst { branches: subBranches, type: subBranchType } =\n\t\t\tgetBranchesTyped(nSub);\n\t\tconst { branches: supBranches, type: supBranchType } =\n\t\t\tgetBranchesTyped(nSup);\n\n\t\t// anyOf/oneOf in sub\n\t\tif (subBranches.length > 1 || subBranches[0] !== nSub) {\n\t\t\treturn checkBranchedSub(subBranches, nSup, this.engine, subBranchType);\n\t\t}\n\n\t\t// anyOf/oneOf in sup only\n\t\tif (supBranches.length > 1 || supBranches[0] !== nSup) {\n\t\t\treturn checkBranchedSup(nSub, supBranches, this.engine, supBranchType);\n\t\t}\n\n\t\t// Standard case\n\t\treturn checkAtomic(nSub, nSup, this.engine);\n\t}\n\n\t// ── Cache management ───────────────────────────────────────────────────\n\n\t/**\n\t * Clears all compiled AJV validator caches (WeakMap, LRU, and AJV internal).\n\t *\n\t * Useful for:\n\t * - Long-running processes where schemas evolve over time\n\t * - Test isolation (ensuring no cross-test cache pollution)\n\t * - Memory pressure situations where cached validators are no longer needed\n\t *\n\t * After calling this, the next validation call will recompile validators\n\t * from scratch — there is a one-time performance cost per unique schema.\n\t *\n\t * This is a static method because the AJV instance is a module-level\n\t * singleton shared across all `JsonSchemaCompatibilityChecker` instances.\n\t *\n\t * @example\n\t * ```ts\n\t * JsonSchemaCompatibilityChecker.clearCache();\n\t * ```\n\t */\n\tstatic clearCache(): void {\n\t\tclearAllValidatorCaches();\n\t}\n}\n"],"names":["resolveConditions","validateSchemaConstraints","narrowSchemaWithData","formatResult","MergeEngine","normalize","arePatternsEquivalent","isPatternSubset","isTrivialPattern","clearAllValidatorCaches","getPartialRuntimeValidationErrors","getRuntimeValidationErrors","checkAtomic","checkBranchedSub","checkBranchedSup","getBranchesTyped","isAtomicSubsetOf","SchemaErrorType","deepEqual","isPlainObj","resolveValidateTargets","JsonSchemaCompatibilityChecker","isSubset","sub","sup","nSub","nSup","branches","subBranches","length","every","branch","engine","check","options","checkWithOptions","checkInternal","isEqual","a","b","intersect","nA","nB","merged","merge","def","label","result","schema","data","validateSub","validateSup","partialSub","partialSup","validate","dataForConditions","resolvedSub","resolvedSup","canNarrow","undefined","canNarrowSub","resolved","canNarrowSup","narrowedSubResolved","narrowedSupResolved","staticResult","runtimeErrors","getErrors","push","prefixRuntimeErrors","constraintValidators","errors","rootKey","map","error","key","type","subBranchType","supBranches","supBranchType","clearCache","constraints"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/json-schema-compatibility-checker.ts"],"sourcesContent":["import type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\nimport { resolveConditions } from \"./condition-resolver.ts\";\nimport { validateSchemaConstraints } from \"./constraint-validator.ts\";\nimport { narrowSchemaWithData } from \"./data-narrowing.ts\";\nimport { formatResult } from \"./formatter.ts\";\nimport { MergeEngine } from \"./merge-engine.ts\";\nimport { normalize } from \"./normalizer.ts\";\nimport {\n\tarePatternsEquivalent,\n\tisPatternSubset,\n\tisTrivialPattern,\n} from \"./pattern-subset.ts\";\nimport {\n\tclearAllValidatorCaches,\n\tgetPartialRuntimeValidationErrors,\n\tgetRuntimeValidationErrors,\n} from \"./runtime-validator.ts\";\nimport type { BranchResult, BranchType } from \"./subset-checker.ts\";\nimport {\n\tcheckAtomic,\n\tcheckBranchedSub,\n\tcheckBranchedSup,\n\tgetBranchesTyped,\n\tisAtomicSubsetOf,\n} from \"./subset-checker.ts\";\nimport type {\n\tCheckerOptions,\n\tCheckRuntimeOptions,\n\tConstraintExecutionContext,\n\tConstraintValidatorRegistry,\n\tResolvedConditionResult,\n\tResolvedSubsetResult,\n\tSchemaError,\n\tSubsetResult,\n\tValidateTargets,\n} from \"./types.ts\";\nimport { SchemaErrorType } from \"./types.ts\";\nimport { deepEqual, isPlainObj } from \"./utils.ts\";\nimport { resolveValidateTargets } from \"./validate-targets.ts\";\n\n// ─── Re-exports ──────────────────────────────────────────────────────────────\n\nexport type {\n\tSchemaError,\n\tSubsetResult,\n\tResolvedConditionResult,\n\tResolvedSubsetResult,\n\tCheckRuntimeOptions,\n\tValidateTargets,\n\tBranchType,\n\tBranchResult,\n};\n\nexport {\n\tSchemaErrorType,\n\tnormalize,\n\tresolveConditions,\n\tformatResult,\n\tMergeEngine,\n\tisPatternSubset,\n\tarePatternsEquivalent,\n\tisTrivialPattern,\n};\n\n// ─── Main Class ──────────────────────────────────────────────────────────────\n//\n// Lightweight facade that orchestrates sub-modules to verify compatibility\n// between JSON Schemas (Draft-07).\n//\n// Mathematical principle:\n// A ⊆ B ⟺ A ∩ B ≡ A\n//\n// In JSON Schema terms:\n// - A ∩ B = allOf([A, B]) resolved via merge\n// - ≡ = structural comparison\n//\n// @example\n// ```ts\n// const checker = new JsonSchemaCompatibilityChecker();\n//\n// checker.isSubset(strict, loose); // true\n// checker.check(loose, strict); // { isSubset: false, diffs: [...] }\n// checker.check(sub, sup, { data: {...} }); // resolves conditions then checks\n// ```\n\nexport class JsonSchemaCompatibilityChecker {\n\tprivate readonly constraintValidators: ConstraintValidatorRegistry;\n\tprivate readonly engine: MergeEngine;\n\n\tconstructor(options?: CheckerOptions) {\n\t\tthis.engine = new MergeEngine();\n\t\tthis.constraintValidators = options?.constraints ?? {};\n\t}\n\n\t// ── Subset check (boolean) ─────────────────────────────────────────────\n\n\t/**\n\t * Checks whether `sub ⊆ sup`.\n\t * Is every value valid for sub also valid for sup?\n\t *\n\t * Uses `getBranchesTyped` to distinguish `anyOf` from `oneOf`\n\t * internally, although the boolean result does not reflect the distinction.\n\t */\n\tisSubset(sub: JSONSchema7Definition, sup: JSONSchema7Definition): boolean {\n\t\t// ── Identity short-circuit ──\n\t\t// If sub and sup are the same reference, sub ⊆ sup is trivially true.\n\t\t// This avoids the entire normalize + merge + compare pipeline.\n\t\tif (sub === sup) return true;\n\n\t\t// ── Pre-normalize structural equality ──\n\t\t// If sub and sup are structurally identical before normalization,\n\t\t// they represent the same schema → sub ⊆ sup trivially.\n\t\t// This avoids the WeakMap overhead of normalize() for common cases\n\t\t// like {} ⊆ {} or identical schema objects with different references.\n\t\tif (deepEqual(sub, sup)) return true;\n\n\t\tconst nSub = normalize(sub);\n\t\tconst nSup = normalize(sup);\n\n\t\t// ── Post-normalize structural identity ──\n\t\t// After normalization, schemas that were syntactically different\n\t\t// but semantically equivalent become structurally equal\n\t\t// (e.g. {const:1} vs {const:1, type:\"integer\"}).\n\t\tif (nSub !== sub && nSup !== sup && deepEqual(nSub, nSup)) return true;\n\t\tif (nSub !== nSup && deepEqual(nSub, nSup)) return true;\n\n\t\tconst { branches: subBranches } = getBranchesTyped(nSub);\n\n\t\tif (subBranches.length > 1 || subBranches[0] !== nSub) {\n\t\t\treturn subBranches.every((branch) =>\n\t\t\t\tisAtomicSubsetOf(branch, nSup, this.engine),\n\t\t\t);\n\t\t}\n\n\t\treturn isAtomicSubsetOf(nSub, nSup, this.engine);\n\t}\n\n\t// ── Subset check (detailed) ────────────────────────────────────────────\n\n\t/**\n\t * Checks `sub ⊆ sup` and returns a detailed diagnostic\n\t * with human-readable semantic errors.\n\t *\n\t * When `options` is provided, both schemas go through runtime-aware\n\t * processing before the static check:\n\t * 1. Conditions (`if/then/else`) are resolved using `data`\n\t * (if `data` is `undefined`, conditions are resolved with `{}`)\n\t * 2. Schemas are narrowed using runtime values (enum materialization)\n\t * 3. The static subset check runs on the resolved/narrowed schemas\n\t *\n\t * When `validate` is enabled, additional runtime steps run **after** the\n\t * static check passes:\n\t * 4. `data` is validated against the targeted resolved schema(s) via AJV\n\t * 5. Custom constraints are validated against `data` for the targeted schema(s)\n\t *\n\t * `validate` accepts:\n\t * - `true` — validate against **both** sub and sup\n\t * - `{ sub: true }` — validate only against the sub schema\n\t * - `{ sup: true }` — validate only against the sup schema\n\t * - `{ sub: true, sup: true }` — equivalent to `true`\n\t *\n\t * @param sub - The source schema (subset candidate)\n\t * @param sup - The target schema (expected superset)\n\t * @param options - Runtime options with `data` and optional `validate` flag\n\t * @returns SubsetResult if no options, ResolvedSubsetResult if options provided\n\t *\n\t * @example\n\t * ```ts\n\t * // Static check (no runtime data)\n\t * checker.check(sub, sup);\n\t *\n\t * // Resolve conditions + narrowing + static check (no runtime validation)\n\t * checker.check(sub, sup, { data: { kind: \"text\", value: \"hello\" } });\n\t *\n\t * // Full pipeline including AJV + constraint runtime validation\n\t * checker.check(sub, sup, { data: { kind: \"text\", value: \"hello\" }, validate: true });\n\t * ```\n\t */\n\tcheck(\n\t\tsub: JSONSchema7Definition,\n\t\tsup: JSONSchema7Definition,\n\t\toptions: CheckRuntimeOptions,\n\t): Promise<ResolvedSubsetResult>;\n\tcheck(sub: JSONSchema7Definition, sup: JSONSchema7Definition): SubsetResult;\n\tcheck(\n\t\tsub: JSONSchema7Definition,\n\t\tsup: JSONSchema7Definition,\n\t\toptions?: CheckRuntimeOptions,\n\t): SubsetResult | Promise<ResolvedSubsetResult> {\n\t\t// ── Runtime-aware path ──\n\t\tif (options) {\n\t\t\treturn this.checkWithOptions(sub, sup, options);\n\t\t}\n\n\t\t// ── Standard path (no condition resolution) ──\n\t\treturn this.checkInternal(sub, sup);\n\t}\n\n\t// ── Equality ───────────────────────────────────────────────────────────\n\n\t/**\n\t * Checks structural equality between two schemas.\n\t */\n\tisEqual(a: JSONSchema7Definition, b: JSONSchema7Definition): boolean {\n\t\treturn this.engine.isEqual(normalize(a), normalize(b));\n\t}\n\n\t// ── Intersection ───────────────────────────────────────────────────────\n\n\t/**\n\t * Computes the intersection of two schemas (allOf merge).\n\t * Returns null if the schemas are incompatible.\n\t *\n\t * The result is normalized to eliminate structural artifacts\n\t * from the merge (e.g. redundant `enum` when `const` is present).\n\t */\n\tintersect(\n\t\ta: JSONSchema7Definition,\n\t\tb: JSONSchema7Definition,\n\t): JSONSchema7Definition | null {\n\t\t// ── Identity short-circuit ──\n\t\t// If a and b are the same reference or structurally equal,\n\t\t// intersection is just normalize(a) — skip the merge entirely.\n\t\tif (a === b || deepEqual(a, b)) return normalize(a);\n\n\t\tconst nA = normalize(a);\n\t\tconst nB = normalize(b);\n\n\t\t// ── Post-normalize identity ──\n\t\tif (deepEqual(nA, nB)) return nA;\n\n\t\tconst merged = this.engine.merge(nA, nB);\n\t\tif (merged === null) return null;\n\t\t// Fast path: if merge result equals one of the normalized inputs,\n\t\t// it's already normalized — skip redundant normalize call.\n\t\tif (deepEqual(merged, nA) || deepEqual(merged, nB)) return merged;\n\t\treturn normalize(merged);\n\t}\n\n\t// ── Normalization ──────────────────────────────────────────────────────\n\n\t/**\n\t * Normalizes a schema: infers `type` from `const`/`enum`,\n\t * and recursively normalizes all sub-schemas.\n\t */\n\tnormalize(def: JSONSchema7Definition): JSONSchema7Definition {\n\t\treturn normalize(def);\n\t}\n\n\t// ── Formatting ─────────────────────────────────────────────────────────\n\n\t/**\n\t * Formats a SubsetResult into a readable string (useful for logs/debug).\n\t */\n\tformatResult(label: string, result: SubsetResult): string {\n\t\treturn formatResult(label, result);\n\t}\n\n\t// ── Condition Resolution ────────────────────────────────────────────────\n\n\t/**\n\t * Resolves `if/then/else` conditions in a schema by evaluating the `if`\n\t * against runtime data.\n\t *\n\t * @param schema - The schema containing conditions to resolve\n\t * @param data - The runtime data used to evaluate conditions\n\t * @returns The resolved schema with branch info and discriminants\n\t */\n\tresolveConditions(\n\t\tschema: JSONSchema7,\n\t\tdata: Record<string, unknown>,\n\t): ResolvedConditionResult {\n\t\treturn resolveConditions(schema, data, this.engine);\n\t}\n\n\t// ── Private ────────────────────────────────────────────────────────────\n\n\t/**\n\t * Internal runtime-aware check logic. Extracted as an async method\n\t * so that `check()` without options stays synchronous while the\n\t * runtime path can `await` async constraint validators.\n\t */\n\tprivate async checkWithOptions(\n\t\tsub: JSONSchema7Definition,\n\t\tsup: JSONSchema7Definition,\n\t\toptions: CheckRuntimeOptions,\n\t): Promise<ResolvedSubsetResult> {\n\t\tconst data = options.data;\n\t\tconst constraintContext: ConstraintExecutionContext | undefined =\n\t\t\toptions.constraintContext;\n\t\tconst {\n\t\t\tsub: validateSub,\n\t\t\tsup: validateSup,\n\t\t\tpartialSub,\n\t\t\tpartialSup,\n\t\t} = resolveValidateTargets(options.validate);\n\n\t\t// resolveConditions expects Record<string, unknown> for property access;\n\t\t// coerce non-object / undefined data to empty object so conditions\n\t\t// are always resolved (v1.0.11 compat: subData: undefined → {})\n\t\tconst dataForConditions: Record<string, unknown> = isPlainObj(data)\n\t\t\t? data\n\t\t\t: {};\n\n\t\tconst resolvedSub = resolveConditions(\n\t\t\tsub as JSONSchema7,\n\t\t\tdataForConditions,\n\t\t\tthis.engine,\n\t\t);\n\t\tconst resolvedSup = resolveConditions(\n\t\t\tsup as JSONSchema7,\n\t\t\tdataForConditions,\n\t\t\tthis.engine,\n\t\t);\n\n\t\t// ── Runtime-aware data narrowing ──\n\t\t// Apply narrowing only when concrete data is available.\n\t\t// When data is undefined there is nothing to narrow with.\n\t\t// Boolean schemas (true/false) cannot be narrowed — skip narrowing\n\t\t// to avoid passing a non-object to narrowSchemaWithData.\n\t\tconst canNarrow = data !== undefined;\n\t\tconst canNarrowSub = canNarrow && isPlainObj(resolvedSub.resolved);\n\t\tconst canNarrowSup = canNarrow && isPlainObj(resolvedSup.resolved);\n\n\t\tconst narrowedSubResolved = canNarrowSub\n\t\t\t? narrowSchemaWithData(resolvedSub.resolved, data, resolvedSup.resolved)\n\t\t\t: resolvedSub.resolved;\n\n\t\tconst narrowedSupResolved = canNarrowSup\n\t\t\t? narrowSchemaWithData(resolvedSup.resolved, data, resolvedSub.resolved)\n\t\t\t: resolvedSup.resolved;\n\n\t\t// ── Static subset check ──\n\t\t// Structural incompatibilities are schema-level problems — they are\n\t\t// permanent regardless of the concrete data. Run this before runtime\n\t\t// validation so that static errors always surface with higher priority.\n\t\tconst staticResult = this.checkInternal(\n\t\t\tnarrowedSubResolved,\n\t\t\tnarrowedSupResolved,\n\t\t);\n\n\t\tif (!staticResult.isSubset) {\n\t\t\treturn {\n\t\t\t\t...staticResult,\n\t\t\t\tresolvedSub: { ...resolvedSub, resolved: narrowedSubResolved },\n\t\t\t\tresolvedSup: { ...resolvedSup, resolved: narrowedSupResolved },\n\t\t\t};\n\t\t}\n\n\t\t// ── Runtime validation (opt-in) ──\n\t\t// Runs when `validate` is truthy (boolean or object with sub/sup flags).\n\t\t// Validates the concrete data against the targeted resolved/narrowed\n\t\t// schema(s) via AJV, then runs custom constraint validators if registered.\n\t\tif ((validateSub || validateSup) && data !== undefined) {\n\t\t\tconst runtimeErrors: SchemaError[] = [];\n\n\t\t\t// ── AJV validation ──\n\t\t\t// When partial mode is active for a target, use\n\t\t\t// getPartialRuntimeValidationErrors which strips `required` and\n\t\t\t// `additionalProperties` before AJV compilation so that only\n\t\t\t// the properties present in data are validated.\n\t\t\tif (validateSub) {\n\t\t\t\tconst getErrors = partialSub\n\t\t\t\t\t? getPartialRuntimeValidationErrors\n\t\t\t\t\t: getRuntimeValidationErrors;\n\t\t\t\truntimeErrors.push(\n\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\tgetErrors(narrowedSubResolved, data),\n\t\t\t\t\t\t\"$sub\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (validateSup) {\n\t\t\t\tconst getErrors = partialSup\n\t\t\t\t\t? getPartialRuntimeValidationErrors\n\t\t\t\t\t: getRuntimeValidationErrors;\n\t\t\t\truntimeErrors.push(\n\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\tgetErrors(narrowedSupResolved, data),\n\t\t\t\t\t\t\"$sup\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// ── Constraint validation ──\n\t\t\t// Validate runtime data against custom constraints in the targeted\n\t\t\t// schema(s). If a schema declares constraints that are not registered\n\t\t\t// in the registry, validateSchemaConstraints will report them as\n\t\t\t// \"unknown constraint (not registered)\" errors.\n\t\t\t// Constraint validators may be async, so we await the results.\n\t\t\tif (validateSub) {\n\t\t\t\truntimeErrors.push(\n\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\tawait validateSchemaConstraints(\n\t\t\t\t\t\t\tnarrowedSubResolved,\n\t\t\t\t\t\t\tdata,\n\t\t\t\t\t\t\tthis.constraintValidators,\n\t\t\t\t\t\t\tconstraintContext,\n\t\t\t\t\t\t),\n\t\t\t\t\t\t\"$sub\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (validateSup) {\n\t\t\t\truntimeErrors.push(\n\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\tawait validateSchemaConstraints(\n\t\t\t\t\t\t\tnarrowedSupResolved,\n\t\t\t\t\t\t\tdata,\n\t\t\t\t\t\t\tthis.constraintValidators,\n\t\t\t\t\t\t\tconstraintContext,\n\t\t\t\t\t\t),\n\t\t\t\t\t\t\"$sup\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (runtimeErrors.length > 0) {\n\t\t\t\treturn {\n\t\t\t\t\tisSubset: false,\n\t\t\t\t\tmerged: null,\n\t\t\t\t\terrors: runtimeErrors,\n\t\t\t\t\tresolvedSub: { ...resolvedSub, resolved: narrowedSubResolved },\n\t\t\t\t\tresolvedSup: { ...resolvedSup, resolved: narrowedSupResolved },\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\t...staticResult,\n\t\t\tresolvedSub: { ...resolvedSub, resolved: narrowedSubResolved },\n\t\t\tresolvedSup: { ...resolvedSup, resolved: narrowedSupResolved },\n\t\t};\n\t}\n\n\tprivate prefixRuntimeErrors(\n\t\terrors: SchemaError[],\n\t\trootKey: \"$sub\" | \"$sup\",\n\t): SchemaError[] {\n\t\treturn errors.map((error) => ({\n\t\t\t...error,\n\t\t\tkey: error.key === \"$root\" ? rootKey : `${rootKey}.${error.key}`,\n\t\t}));\n\t}\n\n\t/**\n\t * Internal check logic without condition resolution.\n\t * Factorizes the normalize → branch → atomic pipeline to avoid\n\t * duplication between the two paths of `check()`.\n\t */\n\tprivate checkInternal(\n\t\tsub: JSONSchema7Definition,\n\t\tsup: JSONSchema7Definition,\n\t): SubsetResult {\n\t\t// ── Identity short-circuit ──\n\t\t// Same reference → no errors, no merge needed.\n\t\tif (sub === sup) {\n\t\t\treturn { isSubset: true, merged: sub, errors: [] };\n\t\t}\n\n\t\t// ── Pre-normalize structural equality ──\n\t\t// Avoids WeakMap overhead for identical schemas ({} ⊆ {}, etc.).\n\t\tif (deepEqual(sub, sup)) {\n\t\t\treturn { isSubset: true, merged: sub, errors: [] };\n\t\t}\n\n\t\tconst nSub = normalize(sub);\n\t\tconst nSup = normalize(sup);\n\n\t\t// ── Post-normalize structural identity ──\n\t\t// Catches semantically equivalent schemas after normalization.\n\t\tif (deepEqual(nSub, nSup)) {\n\t\t\treturn { isSubset: true, merged: nSub, errors: [] };\n\t\t}\n\n\t\tconst { branches: subBranches, type: subBranchType } =\n\t\t\tgetBranchesTyped(nSub);\n\t\tconst { branches: supBranches, type: supBranchType } =\n\t\t\tgetBranchesTyped(nSup);\n\n\t\t// anyOf/oneOf in sub\n\t\tif (subBranches.length > 1 || subBranches[0] !== nSub) {\n\t\t\treturn checkBranchedSub(subBranches, nSup, this.engine, subBranchType);\n\t\t}\n\n\t\t// anyOf/oneOf in sup only\n\t\tif (supBranches.length > 1 || supBranches[0] !== nSup) {\n\t\t\treturn checkBranchedSup(nSub, supBranches, this.engine, supBranchType);\n\t\t}\n\n\t\t// Standard case\n\t\treturn checkAtomic(nSub, nSup, this.engine);\n\t}\n\n\t// ── Cache management ───────────────────────────────────────────────────\n\n\t/**\n\t * Clears all compiled AJV validator caches (WeakMap, LRU, and AJV internal).\n\t *\n\t * Useful for:\n\t * - Long-running processes where schemas evolve over time\n\t * - Test isolation (ensuring no cross-test cache pollution)\n\t * - Memory pressure situations where cached validators are no longer needed\n\t *\n\t * After calling this, the next validation call will recompile validators\n\t * from scratch — there is a one-time performance cost per unique schema.\n\t *\n\t * This is a static method because the AJV instance is a module-level\n\t * singleton shared across all `JsonSchemaCompatibilityChecker` instances.\n\t *\n\t * @example\n\t * ```ts\n\t * JsonSchemaCompatibilityChecker.clearCache();\n\t * ```\n\t */\n\tstatic clearCache(): void {\n\t\tclearAllValidatorCaches();\n\t}\n}\n"],"names":["resolveConditions","validateSchemaConstraints","narrowSchemaWithData","formatResult","MergeEngine","normalize","arePatternsEquivalent","isPatternSubset","isTrivialPattern","clearAllValidatorCaches","getPartialRuntimeValidationErrors","getRuntimeValidationErrors","checkAtomic","checkBranchedSub","checkBranchedSup","getBranchesTyped","isAtomicSubsetOf","SchemaErrorType","deepEqual","isPlainObj","resolveValidateTargets","JsonSchemaCompatibilityChecker","isSubset","sub","sup","nSub","nSup","branches","subBranches","length","every","branch","engine","check","options","checkWithOptions","checkInternal","isEqual","a","b","intersect","nA","nB","merged","merge","def","label","result","schema","data","constraintContext","validateSub","validateSup","partialSub","partialSup","validate","dataForConditions","resolvedSub","resolvedSup","canNarrow","undefined","canNarrowSub","resolved","canNarrowSup","narrowedSubResolved","narrowedSupResolved","staticResult","runtimeErrors","getErrors","push","prefixRuntimeErrors","constraintValidators","errors","rootKey","map","error","key","type","subBranchType","supBranches","supBranchType","clearCache","constraints"],"mappings":"oLACA,OAASA,iBAAiB,KAAQ,yBAA0B,AAC5D,QAASC,yBAAyB,KAAQ,2BAA4B,AACtE,QAASC,oBAAoB,KAAQ,qBAAsB,AAC3D,QAASC,YAAY,KAAQ,gBAAiB,AAC9C,QAASC,WAAW,KAAQ,mBAAoB,AAChD,QAASC,SAAS,KAAQ,iBAAkB,AAC5C,QACCC,qBAAqB,CACrBC,eAAe,CACfC,gBAAgB,KACV,qBAAsB,AAC7B,QACCC,uBAAuB,CACvBC,iCAAiC,CACjCC,0BAA0B,KACpB,wBAAyB,AAEhC,QACCC,WAAW,CACXC,gBAAgB,CAChBC,gBAAgB,CAChBC,gBAAgB,CAChBC,gBAAgB,KACV,qBAAsB,AAY7B,QAASC,eAAe,KAAQ,YAAa,AAC7C,QAASC,SAAS,CAAEC,UAAU,KAAQ,YAAa,AACnD,QAASC,sBAAsB,KAAQ,uBAAwB,AAe/D,QACCH,eAAe,CACfZ,SAAS,CACTL,iBAAiB,CACjBG,YAAY,CACZC,WAAW,CACXG,eAAe,CACfD,qBAAqB,CACrBE,gBAAgB,CACf,AAuBF,QAAO,MAAMa,+BAkBZC,SAASC,GAA0B,CAAEC,GAA0B,CAAW,CAIzE,GAAID,MAAQC,IAAK,OAAO,KAOxB,GAAIN,UAAUK,IAAKC,KAAM,OAAO,KAEhC,MAAMC,KAAOpB,UAAUkB,KACvB,MAAMG,KAAOrB,UAAUmB,KAMvB,GAAIC,OAASF,KAAOG,OAASF,KAAON,UAAUO,KAAMC,MAAO,OAAO,KAClE,GAAID,OAASC,MAAQR,UAAUO,KAAMC,MAAO,OAAO,KAEnD,KAAM,CAAEC,SAAUC,WAAW,CAAE,CAAGb,iBAAiBU,MAEnD,GAAIG,YAAYC,MAAM,CAAG,GAAKD,WAAW,CAAC,EAAE,GAAKH,KAAM,CACtD,OAAOG,YAAYE,KAAK,CAAC,AAACC,QACzBf,iBAAiBe,OAAQL,KAAM,IAAI,CAACM,MAAM,EAE5C,CAEA,OAAOhB,iBAAiBS,KAAMC,KAAM,IAAI,CAACM,MAAM,CAChD,CAiDAC,MACCV,GAA0B,CAC1BC,GAA0B,CAC1BU,OAA6B,CACkB,CAE/C,GAAIA,QAAS,CACZ,OAAO,IAAI,CAACC,gBAAgB,CAACZ,IAAKC,IAAKU,QACxC,CAGA,OAAO,IAAI,CAACE,aAAa,CAACb,IAAKC,IAChC,CAOAa,QAAQC,CAAwB,CAAEC,CAAwB,CAAW,CACpE,OAAO,IAAI,CAACP,MAAM,CAACK,OAAO,CAAChC,UAAUiC,GAAIjC,UAAUkC,GACpD,CAWAC,UACCF,CAAwB,CACxBC,CAAwB,CACO,CAI/B,GAAID,IAAMC,GAAKrB,UAAUoB,EAAGC,GAAI,OAAOlC,UAAUiC,GAEjD,MAAMG,GAAKpC,UAAUiC,GACrB,MAAMI,GAAKrC,UAAUkC,GAGrB,GAAIrB,UAAUuB,GAAIC,IAAK,OAAOD,GAE9B,MAAME,OAAS,IAAI,CAACX,MAAM,CAACY,KAAK,CAACH,GAAIC,IACrC,GAAIC,SAAW,KAAM,OAAO,KAG5B,GAAIzB,UAAUyB,OAAQF,KAAOvB,UAAUyB,OAAQD,IAAK,OAAOC,OAC3D,OAAOtC,UAAUsC,OAClB,CAQAtC,UAAUwC,GAA0B,CAAyB,CAC5D,OAAOxC,UAAUwC,IAClB,CAOA1C,aAAa2C,KAAa,CAAEC,MAAoB,CAAU,CACzD,OAAO5C,aAAa2C,MAAOC,OAC5B,CAYA/C,kBACCgD,MAAmB,CACnBC,IAA6B,CACH,CAC1B,OAAOjD,kBAAkBgD,OAAQC,KAAM,IAAI,CAACjB,MAAM,CACnD,CASA,MAAcG,iBACbZ,GAA0B,CAC1BC,GAA0B,CAC1BU,OAA4B,CACI,CAChC,MAAMe,KAAOf,QAAQe,IAAI,CACzB,MAAMC,kBACLhB,QAAQgB,iBAAiB,CAC1B,KAAM,CACL3B,IAAK4B,WAAW,CAChB3B,IAAK4B,WAAW,CAChBC,UAAU,CACVC,UAAU,CACV,CAAGlC,uBAAuBc,QAAQqB,QAAQ,EAK3C,MAAMC,kBAA6CrC,WAAW8B,MAC3DA,KACA,CAAC,EAEJ,MAAMQ,YAAczD,kBACnBuB,IACAiC,kBACA,IAAI,CAACxB,MAAM,EAEZ,MAAM0B,YAAc1D,kBACnBwB,IACAgC,kBACA,IAAI,CAACxB,MAAM,EAQZ,MAAM2B,UAAYV,OAASW,UAC3B,MAAMC,aAAeF,WAAaxC,WAAWsC,YAAYK,QAAQ,EACjE,MAAMC,aAAeJ,WAAaxC,WAAWuC,YAAYI,QAAQ,EAEjE,MAAME,oBAAsBH,aACzB3D,qBAAqBuD,YAAYK,QAAQ,CAAEb,KAAMS,YAAYI,QAAQ,EACrEL,YAAYK,QAAQ,CAEvB,MAAMG,oBAAsBF,aACzB7D,qBAAqBwD,YAAYI,QAAQ,CAAEb,KAAMQ,YAAYK,QAAQ,EACrEJ,YAAYI,QAAQ,CAMvB,MAAMI,aAAe,IAAI,CAAC9B,aAAa,CACtC4B,oBACAC,qBAGD,GAAI,CAACC,aAAa5C,QAAQ,CAAE,CAC3B,MAAO,CACN,GAAG4C,YAAY,CACfT,YAAa,CAAE,GAAGA,WAAW,CAAEK,SAAUE,mBAAoB,EAC7DN,YAAa,CAAE,GAAGA,WAAW,CAAEI,SAAUG,mBAAoB,CAC9D,CACD,CAMA,GAAI,AAACd,CAAAA,aAAeC,WAAU,GAAMH,OAASW,UAAW,CACvD,MAAMO,cAA+B,EAAE,CAOvC,GAAIhB,YAAa,CAChB,MAAMiB,UAAYf,WACf3C,kCACAC,2BACHwD,cAAcE,IAAI,IACd,IAAI,CAACC,mBAAmB,CAC1BF,UAAUJ,oBAAqBf,MAC/B,QAGH,CAEA,GAAIG,YAAa,CAChB,MAAMgB,UAAYd,WACf5C,kCACAC,2BACHwD,cAAcE,IAAI,IACd,IAAI,CAACC,mBAAmB,CAC1BF,UAAUH,oBAAqBhB,MAC/B,QAGH,CAQA,GAAIE,YAAa,CAChBgB,cAAcE,IAAI,IACd,IAAI,CAACC,mBAAmB,CAC1B,MAAMrE,0BACL+D,oBACAf,KACA,IAAI,CAACsB,oBAAoB,CACzBrB,mBAED,QAGH,CAEA,GAAIE,YAAa,CAChBe,cAAcE,IAAI,IACd,IAAI,CAACC,mBAAmB,CAC1B,MAAMrE,0BACLgE,oBACAhB,KACA,IAAI,CAACsB,oBAAoB,CACzBrB,mBAED,QAGH,CAEA,GAAIiB,cAActC,MAAM,CAAG,EAAG,CAC7B,MAAO,CACNP,SAAU,MACVqB,OAAQ,KACR6B,OAAQL,cACRV,YAAa,CAAE,GAAGA,WAAW,CAAEK,SAAUE,mBAAoB,EAC7DN,YAAa,CAAE,GAAGA,WAAW,CAAEI,SAAUG,mBAAoB,CAC9D,CACD,CACD,CAEA,MAAO,CACN,GAAGC,YAAY,CACfT,YAAa,CAAE,GAAGA,WAAW,CAAEK,SAAUE,mBAAoB,EAC7DN,YAAa,CAAE,GAAGA,WAAW,CAAEI,SAAUG,mBAAoB,CAC9D,CACD,CAEA,AAAQK,oBACPE,MAAqB,CACrBC,OAAwB,CACR,CAChB,OAAOD,OAAOE,GAAG,CAAC,AAACC,OAAW,CAAA,CAC7B,GAAGA,KAAK,CACRC,IAAKD,MAAMC,GAAG,GAAK,QAAUH,QAAU,CAAC,EAAEA,QAAQ,CAAC,EAAEE,MAAMC,GAAG,CAAC,CAAC,AACjE,CAAA,EACD,CAOA,AAAQxC,cACPb,GAA0B,CAC1BC,GAA0B,CACX,CAGf,GAAID,MAAQC,IAAK,CAChB,MAAO,CAAEF,SAAU,KAAMqB,OAAQpB,IAAKiD,OAAQ,EAAE,AAAC,CAClD,CAIA,GAAItD,UAAUK,IAAKC,KAAM,CACxB,MAAO,CAAEF,SAAU,KAAMqB,OAAQpB,IAAKiD,OAAQ,EAAE,AAAC,CAClD,CAEA,MAAM/C,KAAOpB,UAAUkB,KACvB,MAAMG,KAAOrB,UAAUmB,KAIvB,GAAIN,UAAUO,KAAMC,MAAO,CAC1B,MAAO,CAAEJ,SAAU,KAAMqB,OAAQlB,KAAM+C,OAAQ,EAAE,AAAC,CACnD,CAEA,KAAM,CAAE7C,SAAUC,WAAW,CAAEiD,KAAMC,aAAa,CAAE,CACnD/D,iBAAiBU,MAClB,KAAM,CAAEE,SAAUoD,WAAW,CAAEF,KAAMG,aAAa,CAAE,CACnDjE,iBAAiBW,MAGlB,GAAIE,YAAYC,MAAM,CAAG,GAAKD,WAAW,CAAC,EAAE,GAAKH,KAAM,CACtD,OAAOZ,iBAAiBe,YAAaF,KAAM,IAAI,CAACM,MAAM,CAAE8C,cACzD,CAGA,GAAIC,YAAYlD,MAAM,CAAG,GAAKkD,WAAW,CAAC,EAAE,GAAKrD,KAAM,CACtD,OAAOZ,iBAAiBW,KAAMsD,YAAa,IAAI,CAAC/C,MAAM,CAAEgD,cACzD,CAGA,OAAOpE,YAAYa,KAAMC,KAAM,IAAI,CAACM,MAAM,CAC3C,CAuBA,OAAOiD,YAAmB,CACzBxE,yBACD,CA9aA,YAAYyB,OAAwB,CAAE,CAHtC,sBAAiBqC,uBAAjB,KAAA,GACA,sBAAiBvC,SAAjB,KAAA,EAGC,CAAA,IAAI,CAACA,MAAM,CAAG,IAAI5B,WAClB,CAAA,IAAI,CAACmE,oBAAoB,CAAGrC,SAASgD,aAAe,CAAC,CACtD,CA4aD"}
@@ -159,6 +159,23 @@ export interface CheckRuntimeOptions {
159
159
  * @default false
160
160
  */
161
161
  validate?: boolean | ValidateTargets;
162
+ /**
163
+ * Arbitrary context forwarded to every constraint validator during
164
+ * this `check()` call.
165
+ *
166
+ * Useful for passing per-request state (e.g. tenant ID, user scope)
167
+ * that is not encoded in the schema but is needed by validators at runtime.
168
+ *
169
+ * @example
170
+ * ```ts
171
+ * checker.check(sub, sup, {
172
+ * data: { accountId: '123' },
173
+ * validate: { sup: { partial: true } },
174
+ * constraintContext: { companyId: 42 },
175
+ * });
176
+ * ```
177
+ */
178
+ constraintContext?: ConstraintExecutionContext;
162
179
  }
163
180
  /**
164
181
  * Extended result from `check()` when runtime options are provided.
@@ -181,6 +198,15 @@ export type Constraint = string | {
181
198
  params?: Record<string, unknown>;
182
199
  };
183
200
  export type Constraints = Constraint | Constraint[];
201
+ /**
202
+ * Arbitrary per-call context forwarded to every constraint validator
203
+ * during a `check()` invocation.
204
+ *
205
+ * Allows callers to pass request-scoped information (e.g. tenant ID,
206
+ * user ID, feature flags) that is not part of the schema definition
207
+ * but is required by constraint validators at runtime.
208
+ */
209
+ export type ConstraintExecutionContext = Record<string, unknown>;
184
210
  /**
185
211
  * Result of a constraint validation.
186
212
  */
@@ -193,14 +219,15 @@ export interface ConstraintValidationResult {
193
219
  /**
194
220
  * A constraint validator function.
195
221
  *
196
- * Receives the value to validate and optional params defined
197
- * in the schema's constraint definition.
222
+ * Receives the value to validate, optional params defined in the schema's
223
+ * constraint definition, and an optional per-call execution context.
198
224
  *
199
225
  * Can be synchronous or asynchronous. When async validators are used,
200
226
  * `check()` with runtime options returns a `Promise`.
201
227
  *
202
228
  * @param value - The runtime value to validate
203
229
  * @param params - The `params` object from the constraint definition, if any
230
+ * @param context - The per-call execution context passed via `constraintContext`, if any
204
231
  * @returns The validation result, or a Promise resolving to it
205
232
  *
206
233
  * @example
@@ -221,9 +248,15 @@ export interface ConstraintValidationResult {
221
248
  * valid: typeof value === "number" && value >= (params?.min ?? 0),
222
249
  * message: `Value must be at least ${params?.min}`,
223
250
  * });
251
+ *
252
+ * // Validator using execution context (e.g. tenant-scoped DB lookup)
253
+ * const isUniqueInTenant: ConstraintValidator = async (value, params, context) => ({
254
+ * valid: await checkUniqueness(value as string, context?.companyId as number),
255
+ * message: "Value must be unique within the tenant",
256
+ * });
224
257
  * ```
225
258
  */
226
- export type ConstraintValidator = (value: unknown, params?: Record<string, unknown>) => ConstraintValidationResult | Promise<ConstraintValidationResult>;
259
+ export type ConstraintValidator = (value: unknown, params?: Record<string, unknown>, context?: ConstraintExecutionContext) => ConstraintValidationResult | Promise<ConstraintValidationResult>;
227
260
  /**
228
261
  * Registry mapping constraint names to their validator functions.
229
262
  *
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/types.ts"],"sourcesContent":["import type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\n\n// ─── Module augmentation ─────────────────────────────────────────────────────\n// Extends JSONSchema7 with the custom `constraints` keyword so that consumers\n// of this package see the property on every JSONSchema7 without needing a\n// separate ambient file or `/// <reference>` directive.\n\ndeclare module \"json-schema\" {\n\tinterface JSONSchema7 {\n\t\tconstraints?: Constraints;\n\t}\n}\n\n// ─── Public types ────────────────────────────────────────────────────────────\n\n/**\n * Discriminant for `SchemaError` — indicates the nature of the incompatibility.\n *\n * | Member | Description | Source module(s) |\n * |------------------------|--------------------------------------------------------------------|---------------------------|\n * | `TypeMismatch` | Incompatible types (e.g. `string` vs `number`, boolean schemas) | `semantic-errors.ts` |\n * | `MissingProperty` | Required property absent from the source schema | `semantic-errors.ts` |\n * | `Optionality` | Property required in target but optional in source | `semantic-errors.ts` |\n * | `EnumMismatch` | `enum` / `const` values are incompatible | `semantic-errors.ts` |\n * | `NumericConstraint` | `minimum`, `maximum`, `exclusiveMin/Max`, `multipleOf` | `semantic-errors.ts` |\n * | `StringConstraint` | `minLength`, `maxLength`, `pattern`, `format` | `semantic-errors.ts` |\n * | `ObjectConstraint` | `additionalProperties`, `min/maxProperties`, `propertyNames`, etc. | `semantic-errors.ts` |\n * | `ArrayConstraint` | `minItems`, `maxItems`, `uniqueItems`, `contains` | `semantic-errors.ts` |\n * | `NotSchema` | Incompatibility on the `not` keyword | `semantic-errors.ts` |\n * | `BranchMismatch` | No `anyOf` / `oneOf` branch matches | `semantic-errors.ts` |\n * | `RuntimeValidation` | Runtime data invalid against a standard JSON Schema keyword (AJV) | `runtime-validator.ts` |\n * | `CustomConstraint` | Custom constraint failed, unknown, or threw | `constraint-validator.ts` |\n */\nexport enum SchemaErrorType {\n\tTypeMismatch = \"type_mismatch\",\n\tMissingProperty = \"missing_property\",\n\tOptionality = \"optionality\",\n\tEnumMismatch = \"enum_mismatch\",\n\tNumericConstraint = \"numeric_constraint\",\n\tStringConstraint = \"string_constraint\",\n\tObjectConstraint = \"object_constraint\",\n\tArrayConstraint = \"array_constraint\",\n\tNotSchema = \"not_schema\",\n\tBranchMismatch = \"branch_mismatch\",\n\tRuntimeValidation = \"runtime_validation\",\n\tCustomConstraint = \"custom_constraint\",\n}\n\nexport interface SchemaError {\n\t/** Discriminant indicating the nature of the error */\n\ttype: SchemaErrorType;\n\t/** Normalized path to the concerned property (e.g. \"user.name\", \"users[].name\", \"accountId\") */\n\tkey: string;\n\t/** Type or value expected by the target schema (sup) */\n\texpected: string;\n\t/** Type or value received from the source schema (sub) */\n\treceived: string;\n}\n\nexport interface SubsetResult {\n\t/** true if sub ⊆ sup (every value valid for sub is also valid for sup) */\n\tisSubset: boolean;\n\t/** The schema resulting from the intersection allOf(sub, sup), or null if incompatible */\n\tmerged: JSONSchema7Definition | null;\n\t/** Semantic errors describing incompatibilities between the two schemas */\n\terrors: SchemaError[];\n}\n\n/**\n * Per-target validation options.\n *\n * When `partial` is `true`, runtime validation strips `required` and\n * `additionalProperties` constraints at every level of the schema before\n * passing it to AJV. This allows validating **only the properties present\n * in `data`** without false negatives on missing required properties or\n * extra properties not defined in the schema.\n *\n * Partial mode applies recursively: nested object schemas also have their\n * `required` and `additionalProperties` stripped.\n *\n * @example\n * ```ts\n * // Validate sup in partial mode — only check properties present in data\n * checker.check(sub, sup, {\n * data: { accountId: 'salut' },\n * validate: { sup: { partial: true } },\n * });\n * ```\n */\nexport interface ValidateTargetOptions {\n\t/**\n\t * When `true`, strip `required` and `additionalProperties` from the\n\t * schema before AJV validation so that only properties present in\n\t * `data` are validated.\n\t *\n\t * @default false\n\t */\n\tpartial?: boolean;\n}\n\n/**\n * Granular control over which schema(s) runtime validation applies to.\n *\n * When provided as an object, each key independently controls whether\n * runtime validation (AJV + custom constraints) runs against that schema:\n * - `sub`: validate `data` against the resolved/narrowed sub schema\n * - `sup`: validate `data` against the resolved/narrowed sup schema\n *\n * Each target accepts either a boolean or a `ValidateTargetOptions` object.\n * When `true`, validation runs with default options. When an object is\n * provided, its flags (e.g. `partial`) customize the validation behavior.\n *\n * Omitted keys default to `false`.\n *\n * @example\n * ```ts\n * // Validate only the sup schema (e.g. target input with constraints)\n * checker.check(sub, sup, { data: {}, validate: { sup: true } });\n *\n * // Validate only the sub schema\n * checker.check(sub, sup, { data: {}, validate: { sub: true } });\n *\n * // Validate both (equivalent to `validate: true`)\n * checker.check(sub, sup, { data: {}, validate: { sub: true, sup: true } });\n *\n * // Validate sup in partial mode (skip required / additionalProperties)\n * checker.check(sub, sup, {\n * data: { accountId: 'salut' },\n * validate: { sup: { partial: true } },\n * });\n * ```\n */\nexport interface ValidateTargets {\n\t/** When `true` or an options object, validate `data` against the resolved sub schema */\n\tsub?: boolean | ValidateTargetOptions;\n\t/** When `true` or an options object, validate `data` against the resolved sup schema */\n\tsup?: boolean | ValidateTargetOptions;\n}\n\n/**\n * Options for runtime-aware subset checking.\n *\n * When `data` is provided, the checker:\n * 1. Resolves `if/then/else` conditions in both `sub` and `sup` using `data`\n * (if `data` is `undefined`, conditions are resolved with `{}`)\n * 2. Narrows schemas using runtime values (e.g. enum materialization)\n * 3. Performs the static subset check on the resolved/narrowed schemas\n *\n * When `validate` is `true` (or an object with `sub`/`sup` flags), additional\n * runtime steps run **after** the static check passes:\n * 4. `data` is validated against the targeted resolved schema(s) via AJV\n * 5. Custom constraints are validated against `data` for the targeted schema(s)\n *\n * `data` can be a partial discriminant (e.g. `{ kind: \"text\" }`) used solely\n * for condition resolution and narrowing. It does **not** need to be a complete\n * instance of the schemas unless runtime validation is enabled.\n */\nexport interface CheckRuntimeOptions {\n\t/** Runtime data used for condition resolution, narrowing, and optionally runtime validation */\n\tdata: unknown;\n\n\t/**\n\t * Controls runtime validation of `data` against resolved schemas.\n\t *\n\t * - `true` — validate against **both** sub and sup schemas (AJV + constraints)\n\t * - `false` / omitted — no runtime validation (data used only for condition\n\t * resolution and narrowing)\n\t * - `{ sub: true }` — validate only against the sub schema\n\t * - `{ sup: true }` — validate only against the sup schema\n\t * - `{ sub: true, sup: true }` — equivalent to `true`\n\t * - `{ sup: { partial: true } }` — validate sup in partial mode\n\t * (skip `required` / `additionalProperties` enforcement)\n\t *\n\t * @default false\n\t */\n\tvalidate?: boolean | ValidateTargets;\n}\n\n/**\n * Extended result from `check()` when runtime options are provided.\n * Includes resolution results for sub and sup in addition to the SubsetResult.\n */\nexport interface ResolvedSubsetResult extends SubsetResult {\n\tresolvedSub: ResolvedConditionResult;\n\tresolvedSup: ResolvedConditionResult;\n}\n\nexport interface ResolvedConditionResult {\n\t/** The schema with if/then/else resolved (flattened) */\n\tresolved: JSONSchema7;\n\t/** The branch that was applied (\"then\" | \"else\" | null if no condition) */\n\tbranch: \"then\" | \"else\" | null;\n\t/** The discriminant used for resolution */\n\tdiscriminant: Record<string, unknown>;\n}\n\nexport type Constraint =\n\t| string\n\t| {\n\t\t\tname: string;\n\t\t\tparams?: Record<string, unknown>;\n\t };\n\nexport type Constraints = Constraint | Constraint[];\n\n// ─── Constraint Validator types ──────────────────────────────────────────────\n\n/**\n * Result of a constraint validation.\n */\nexport interface ConstraintValidationResult {\n\t/** Whether the value satisfies the constraint */\n\tvalid: boolean;\n\t/** Human-readable message when `valid` is `false` */\n\tmessage?: string;\n}\n\n/**\n * A constraint validator function.\n *\n * Receives the value to validate and optional params defined\n * in the schema's constraint definition.\n *\n * Can be synchronous or asynchronous. When async validators are used,\n * `check()` with runtime options returns a `Promise`.\n *\n * @param value - The runtime value to validate\n * @param params - The `params` object from the constraint definition, if any\n * @returns The validation result, or a Promise resolving to it\n *\n * @example\n * ```ts\n * // Synchronous validator\n * const isUuid: ConstraintValidator = (value) => ({\n * valid: typeof value === \"string\" && /^[0-9a-f]{8}-/.test(value),\n * message: \"Value must be a valid UUID\",\n * });\n *\n * // Async validator\n * const isUniqueEmail: ConstraintValidator = async (value) => ({\n * valid: await checkEmailUniqueness(value as string),\n * message: \"Email must be unique\",\n * });\n *\n * const minAge: ConstraintValidator = (value, params) => ({\n * valid: typeof value === \"number\" && value >= (params?.min ?? 0),\n * message: `Value must be at least ${params?.min}`,\n * });\n * ```\n */\nexport type ConstraintValidator = (\n\tvalue: unknown,\n\tparams?: Record<string, unknown>,\n) => ConstraintValidationResult | Promise<ConstraintValidationResult>;\n\n/**\n * Registry mapping constraint names to their validator functions.\n *\n * Keys are constraint names as they appear in schema definitions\n * (e.g. `\"IsUuid\"`, `\"MinAge\"`).\n */\nexport type ConstraintValidatorRegistry = Record<string, ConstraintValidator>;\n\n/**\n * Options for the JsonSchemaCompatibilityChecker constructor.\n */\nexport interface CheckerOptions {\n\t/**\n\t * Registry of custom constraint validators.\n\t *\n\t * When provided, the checker can validate runtime data against\n\t * custom constraints defined in schemas via the `constraints` keyword.\n\t *\n\t * Constraint names must match those used in schema definitions.\n\t * Unknown constraints (present in a schema but absent from the registry)\n\t * will be reported as errors during runtime validation.\n\t */\n\tconstraints?: ConstraintValidatorRegistry;\n}\n"],"names":["SchemaErrorType"],"mappings":"AAiCA,OAAO,IAAA,AAAKA,sCAAAA,2oBAAAA,oBAaX"}
1
+ {"version":3,"sources":["../../src/types.ts"],"sourcesContent":["import type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\n\n// ─── Module augmentation ─────────────────────────────────────────────────────\n// Extends JSONSchema7 with the custom `constraints` keyword so that consumers\n// of this package see the property on every JSONSchema7 without needing a\n// separate ambient file or `/// <reference>` directive.\n\ndeclare module \"json-schema\" {\n\tinterface JSONSchema7 {\n\t\tconstraints?: Constraints;\n\t}\n}\n\n// ─── Public types ────────────────────────────────────────────────────────────\n\n/**\n * Discriminant for `SchemaError` — indicates the nature of the incompatibility.\n *\n * | Member | Description | Source module(s) |\n * |------------------------|--------------------------------------------------------------------|---------------------------|\n * | `TypeMismatch` | Incompatible types (e.g. `string` vs `number`, boolean schemas) | `semantic-errors.ts` |\n * | `MissingProperty` | Required property absent from the source schema | `semantic-errors.ts` |\n * | `Optionality` | Property required in target but optional in source | `semantic-errors.ts` |\n * | `EnumMismatch` | `enum` / `const` values are incompatible | `semantic-errors.ts` |\n * | `NumericConstraint` | `minimum`, `maximum`, `exclusiveMin/Max`, `multipleOf` | `semantic-errors.ts` |\n * | `StringConstraint` | `minLength`, `maxLength`, `pattern`, `format` | `semantic-errors.ts` |\n * | `ObjectConstraint` | `additionalProperties`, `min/maxProperties`, `propertyNames`, etc. | `semantic-errors.ts` |\n * | `ArrayConstraint` | `minItems`, `maxItems`, `uniqueItems`, `contains` | `semantic-errors.ts` |\n * | `NotSchema` | Incompatibility on the `not` keyword | `semantic-errors.ts` |\n * | `BranchMismatch` | No `anyOf` / `oneOf` branch matches | `semantic-errors.ts` |\n * | `RuntimeValidation` | Runtime data invalid against a standard JSON Schema keyword (AJV) | `runtime-validator.ts` |\n * | `CustomConstraint` | Custom constraint failed, unknown, or threw | `constraint-validator.ts` |\n */\nexport enum SchemaErrorType {\n\tTypeMismatch = \"type_mismatch\",\n\tMissingProperty = \"missing_property\",\n\tOptionality = \"optionality\",\n\tEnumMismatch = \"enum_mismatch\",\n\tNumericConstraint = \"numeric_constraint\",\n\tStringConstraint = \"string_constraint\",\n\tObjectConstraint = \"object_constraint\",\n\tArrayConstraint = \"array_constraint\",\n\tNotSchema = \"not_schema\",\n\tBranchMismatch = \"branch_mismatch\",\n\tRuntimeValidation = \"runtime_validation\",\n\tCustomConstraint = \"custom_constraint\",\n}\n\nexport interface SchemaError {\n\t/** Discriminant indicating the nature of the error */\n\ttype: SchemaErrorType;\n\t/** Normalized path to the concerned property (e.g. \"user.name\", \"users[].name\", \"accountId\") */\n\tkey: string;\n\t/** Type or value expected by the target schema (sup) */\n\texpected: string;\n\t/** Type or value received from the source schema (sub) */\n\treceived: string;\n}\n\nexport interface SubsetResult {\n\t/** true if sub ⊆ sup (every value valid for sub is also valid for sup) */\n\tisSubset: boolean;\n\t/** The schema resulting from the intersection allOf(sub, sup), or null if incompatible */\n\tmerged: JSONSchema7Definition | null;\n\t/** Semantic errors describing incompatibilities between the two schemas */\n\terrors: SchemaError[];\n}\n\n/**\n * Per-target validation options.\n *\n * When `partial` is `true`, runtime validation strips `required` and\n * `additionalProperties` constraints at every level of the schema before\n * passing it to AJV. This allows validating **only the properties present\n * in `data`** without false negatives on missing required properties or\n * extra properties not defined in the schema.\n *\n * Partial mode applies recursively: nested object schemas also have their\n * `required` and `additionalProperties` stripped.\n *\n * @example\n * ```ts\n * // Validate sup in partial mode — only check properties present in data\n * checker.check(sub, sup, {\n * data: { accountId: 'salut' },\n * validate: { sup: { partial: true } },\n * });\n * ```\n */\nexport interface ValidateTargetOptions {\n\t/**\n\t * When `true`, strip `required` and `additionalProperties` from the\n\t * schema before AJV validation so that only properties present in\n\t * `data` are validated.\n\t *\n\t * @default false\n\t */\n\tpartial?: boolean;\n}\n\n/**\n * Granular control over which schema(s) runtime validation applies to.\n *\n * When provided as an object, each key independently controls whether\n * runtime validation (AJV + custom constraints) runs against that schema:\n * - `sub`: validate `data` against the resolved/narrowed sub schema\n * - `sup`: validate `data` against the resolved/narrowed sup schema\n *\n * Each target accepts either a boolean or a `ValidateTargetOptions` object.\n * When `true`, validation runs with default options. When an object is\n * provided, its flags (e.g. `partial`) customize the validation behavior.\n *\n * Omitted keys default to `false`.\n *\n * @example\n * ```ts\n * // Validate only the sup schema (e.g. target input with constraints)\n * checker.check(sub, sup, { data: {}, validate: { sup: true } });\n *\n * // Validate only the sub schema\n * checker.check(sub, sup, { data: {}, validate: { sub: true } });\n *\n * // Validate both (equivalent to `validate: true`)\n * checker.check(sub, sup, { data: {}, validate: { sub: true, sup: true } });\n *\n * // Validate sup in partial mode (skip required / additionalProperties)\n * checker.check(sub, sup, {\n * data: { accountId: 'salut' },\n * validate: { sup: { partial: true } },\n * });\n * ```\n */\nexport interface ValidateTargets {\n\t/** When `true` or an options object, validate `data` against the resolved sub schema */\n\tsub?: boolean | ValidateTargetOptions;\n\t/** When `true` or an options object, validate `data` against the resolved sup schema */\n\tsup?: boolean | ValidateTargetOptions;\n}\n\n/**\n * Options for runtime-aware subset checking.\n *\n * When `data` is provided, the checker:\n * 1. Resolves `if/then/else` conditions in both `sub` and `sup` using `data`\n * (if `data` is `undefined`, conditions are resolved with `{}`)\n * 2. Narrows schemas using runtime values (e.g. enum materialization)\n * 3. Performs the static subset check on the resolved/narrowed schemas\n *\n * When `validate` is `true` (or an object with `sub`/`sup` flags), additional\n * runtime steps run **after** the static check passes:\n * 4. `data` is validated against the targeted resolved schema(s) via AJV\n * 5. Custom constraints are validated against `data` for the targeted schema(s)\n *\n * `data` can be a partial discriminant (e.g. `{ kind: \"text\" }`) used solely\n * for condition resolution and narrowing. It does **not** need to be a complete\n * instance of the schemas unless runtime validation is enabled.\n */\nexport interface CheckRuntimeOptions {\n\t/** Runtime data used for condition resolution, narrowing, and optionally runtime validation */\n\tdata: unknown;\n\n\t/**\n\t * Controls runtime validation of `data` against resolved schemas.\n\t *\n\t * - `true` — validate against **both** sub and sup schemas (AJV + constraints)\n\t * - `false` / omitted — no runtime validation (data used only for condition\n\t * resolution and narrowing)\n\t * - `{ sub: true }` — validate only against the sub schema\n\t * - `{ sup: true }` — validate only against the sup schema\n\t * - `{ sub: true, sup: true }` — equivalent to `true`\n\t * - `{ sup: { partial: true } }` — validate sup in partial mode\n\t * (skip `required` / `additionalProperties` enforcement)\n\t *\n\t * @default false\n\t */\n\tvalidate?: boolean | ValidateTargets;\n\n\t/**\n\t * Arbitrary context forwarded to every constraint validator during\n\t * this `check()` call.\n\t *\n\t * Useful for passing per-request state (e.g. tenant ID, user scope)\n\t * that is not encoded in the schema but is needed by validators at runtime.\n\t *\n\t * @example\n\t * ```ts\n\t * checker.check(sub, sup, {\n\t * data: { accountId: '123' },\n\t * validate: { sup: { partial: true } },\n\t * constraintContext: { companyId: 42 },\n\t * });\n\t * ```\n\t */\n\tconstraintContext?: ConstraintExecutionContext;\n}\n\n/**\n * Extended result from `check()` when runtime options are provided.\n * Includes resolution results for sub and sup in addition to the SubsetResult.\n */\nexport interface ResolvedSubsetResult extends SubsetResult {\n\tresolvedSub: ResolvedConditionResult;\n\tresolvedSup: ResolvedConditionResult;\n}\n\nexport interface ResolvedConditionResult {\n\t/** The schema with if/then/else resolved (flattened) */\n\tresolved: JSONSchema7;\n\t/** The branch that was applied (\"then\" | \"else\" | null if no condition) */\n\tbranch: \"then\" | \"else\" | null;\n\t/** The discriminant used for resolution */\n\tdiscriminant: Record<string, unknown>;\n}\n\nexport type Constraint =\n\t| string\n\t| {\n\t\t\tname: string;\n\t\t\tparams?: Record<string, unknown>;\n\t };\n\nexport type Constraints = Constraint | Constraint[];\n\n// ─── Constraint Validator types ──────────────────────────────────────────────\n\n/**\n * Arbitrary per-call context forwarded to every constraint validator\n * during a `check()` invocation.\n *\n * Allows callers to pass request-scoped information (e.g. tenant ID,\n * user ID, feature flags) that is not part of the schema definition\n * but is required by constraint validators at runtime.\n */\nexport type ConstraintExecutionContext = Record<string, unknown>;\n\n/**\n * Result of a constraint validation.\n */\nexport interface ConstraintValidationResult {\n\t/** Whether the value satisfies the constraint */\n\tvalid: boolean;\n\t/** Human-readable message when `valid` is `false` */\n\tmessage?: string;\n}\n\n/**\n * A constraint validator function.\n *\n * Receives the value to validate, optional params defined in the schema's\n * constraint definition, and an optional per-call execution context.\n *\n * Can be synchronous or asynchronous. When async validators are used,\n * `check()` with runtime options returns a `Promise`.\n *\n * @param value - The runtime value to validate\n * @param params - The `params` object from the constraint definition, if any\n * @param context - The per-call execution context passed via `constraintContext`, if any\n * @returns The validation result, or a Promise resolving to it\n *\n * @example\n * ```ts\n * // Synchronous validator\n * const isUuid: ConstraintValidator = (value) => ({\n * valid: typeof value === \"string\" && /^[0-9a-f]{8}-/.test(value),\n * message: \"Value must be a valid UUID\",\n * });\n *\n * // Async validator\n * const isUniqueEmail: ConstraintValidator = async (value) => ({\n * valid: await checkEmailUniqueness(value as string),\n * message: \"Email must be unique\",\n * });\n *\n * const minAge: ConstraintValidator = (value, params) => ({\n * valid: typeof value === \"number\" && value >= (params?.min ?? 0),\n * message: `Value must be at least ${params?.min}`,\n * });\n *\n * // Validator using execution context (e.g. tenant-scoped DB lookup)\n * const isUniqueInTenant: ConstraintValidator = async (value, params, context) => ({\n * valid: await checkUniqueness(value as string, context?.companyId as number),\n * message: \"Value must be unique within the tenant\",\n * });\n * ```\n */\nexport type ConstraintValidator = (\n\tvalue: unknown,\n\tparams?: Record<string, unknown>,\n\tcontext?: ConstraintExecutionContext,\n) => ConstraintValidationResult | Promise<ConstraintValidationResult>;\n\n/**\n * Registry mapping constraint names to their validator functions.\n *\n * Keys are constraint names as they appear in schema definitions\n * (e.g. `\"IsUuid\"`, `\"MinAge\"`).\n */\nexport type ConstraintValidatorRegistry = Record<string, ConstraintValidator>;\n\n/**\n * Options for the JsonSchemaCompatibilityChecker constructor.\n */\nexport interface CheckerOptions {\n\t/**\n\t * Registry of custom constraint validators.\n\t *\n\t * When provided, the checker can validate runtime data against\n\t * custom constraints defined in schemas via the `constraints` keyword.\n\t *\n\t * Constraint names must match those used in schema definitions.\n\t * Unknown constraints (present in a schema but absent from the registry)\n\t * will be reported as errors during runtime validation.\n\t */\n\tconstraints?: ConstraintValidatorRegistry;\n}\n"],"names":["SchemaErrorType"],"mappings":"AAiCA,OAAO,IAAA,AAAKA,sCAAAA,2oBAAAA,oBAaX"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-schema-compatibility-checker",
3
- "version": "1.1.13",
3
+ "version": "1.1.14",
4
4
  "license": "MIT",
5
5
  "description": "A tool to check compatibility between two JSON Schemas.",
6
6
  "author": {