json-schema-compatibility-checker 1.1.2 → 1.1.4
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.
- package/dist/cjs/condition-resolver.js +1 -1
- package/dist/cjs/condition-resolver.js.map +1 -1
- package/dist/cjs/constraint-validator.d.ts +5 -5
- package/dist/cjs/constraint-validator.js +1 -1
- package/dist/cjs/constraint-validator.js.map +1 -1
- package/dist/cjs/json-schema-compatibility-checker.d.ts +7 -1
- package/dist/cjs/json-schema-compatibility-checker.js +1 -1
- package/dist/cjs/json-schema-compatibility-checker.js.map +1 -1
- package/dist/cjs/merge-engine.js +1 -1
- package/dist/cjs/merge-engine.js.map +1 -1
- package/dist/cjs/normalizer.js +1 -1
- package/dist/cjs/normalizer.js.map +1 -1
- package/dist/cjs/semantic-errors.js +1 -1
- package/dist/cjs/semantic-errors.js.map +1 -1
- package/dist/cjs/types.d.ts +11 -4
- package/dist/esm/condition-resolver.js +1 -1
- package/dist/esm/condition-resolver.js.map +1 -1
- package/dist/esm/constraint-validator.d.ts +5 -5
- package/dist/esm/constraint-validator.js +1 -1
- package/dist/esm/constraint-validator.js.map +1 -1
- package/dist/esm/json-schema-compatibility-checker.d.ts +7 -1
- package/dist/esm/json-schema-compatibility-checker.js +1 -1
- package/dist/esm/json-schema-compatibility-checker.js.map +1 -1
- package/dist/esm/merge-engine.js +1 -1
- package/dist/esm/merge-engine.js.map +1 -1
- package/dist/esm/normalizer.js +1 -1
- package/dist/esm/normalizer.js.map +1 -1
- package/dist/esm/semantic-errors.js +1 -1
- package/dist/esm/semantic-errors.js.map +1 -1
- package/dist/esm/types.d.ts +11 -4
- package/dist/esm/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"resolveConditions",{enumerable:true,get:function(){return resolveConditions}});const _runtimevalidatorts=require("./runtime-validator.js");const _utilsts=require("./utils.js");const SPECIAL_MERGE_KEYS=new Set(["required","properties","dependencies"]);const SUB_SCHEMA_KEYS=new Set(["additionalProperties","items","contains","propertyNames","not"]);const MIN_KEYS=new Set(["minimum","exclusiveMinimum","minLength","minItems","minProperties"]);const MAX_KEYS=new Set(["maximum","exclusiveMaximum","maxLength","maxItems","maxProperties"]);function evaluateCondition(ifSchema,data){return(0,_runtimevalidatorts.isDataValidForSchema)(ifSchema,data)}const DISCRIMINANT_INDICATORS=["const","enum","minimum","maximum","exclusiveMinimum","exclusiveMaximum","pattern","minLength","maxLength","multipleOf","minItems","maxItems","format"];function extractDiscriminants(ifSchema,data,out){if(!(0,_utilsts.isPlainObj)(ifSchema.properties))return;const props=ifSchema.properties;for(const key of Object.keys(props)){const propDef=props[key];if(typeof propDef==="boolean")continue;const prop=propDef;const hasIndicator=DISCRIMINANT_INDICATORS.some(indicator=>(0,_utilsts.hasOwn)(prop,indicator));if(hasIndicator&&(0,_utilsts.hasOwn)(data,key)){out[key]=data[key]}}}function mergeBranchInto(resolved,branchDef,engine){if(typeof branchDef==="boolean")return;const branchSchema=branchDef;if(Array.isArray(branchSchema.required)){resolved.required=(0,_utilsts.unionStrings)(resolved.required??[],branchSchema.required)}if((0,_utilsts.isPlainObj)(branchSchema.properties)){const branchProps=branchSchema.properties;const mergedProps={...resolved.properties??{}};for(const key of Object.keys(branchProps)){const branchProp=branchProps[key];if(branchProp===undefined)continue;const existing=resolved.properties?.[key];if(existing!==undefined&&typeof existing!=="boolean"&&typeof branchProp!=="boolean"){const merged=engine.merge(existing,branchProp);mergedProps[key]=
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"resolveConditions",{enumerable:true,get:function(){return resolveConditions}});const _runtimevalidatorts=require("./runtime-validator.js");const _utilsts=require("./utils.js");const SPECIAL_MERGE_KEYS=new Set(["required","properties","dependencies"]);const SUB_SCHEMA_KEYS=new Set(["additionalProperties","items","contains","propertyNames","not"]);const MIN_KEYS=new Set(["minimum","exclusiveMinimum","minLength","minItems","minProperties"]);const MAX_KEYS=new Set(["maximum","exclusiveMaximum","maxLength","maxItems","maxProperties"]);function evaluateCondition(ifSchema,data){return(0,_runtimevalidatorts.isDataValidForSchema)(ifSchema,data)}const DISCRIMINANT_INDICATORS=["const","enum","minimum","maximum","exclusiveMinimum","exclusiveMaximum","pattern","minLength","maxLength","multipleOf","minItems","maxItems","format"];function extractDiscriminants(ifSchema,data,out){if(!(0,_utilsts.isPlainObj)(ifSchema.properties))return;const props=ifSchema.properties;for(const key of Object.keys(props)){const propDef=props[key];if(typeof propDef==="boolean")continue;const prop=propDef;const hasIndicator=DISCRIMINANT_INDICATORS.some(indicator=>(0,_utilsts.hasOwn)(prop,indicator));if(hasIndicator&&(0,_utilsts.hasOwn)(data,key)){out[key]=data[key]}}}function mergeBranchInto(resolved,branchDef,engine){if(typeof branchDef==="boolean")return;const branchSchema=branchDef;if(Array.isArray(branchSchema.required)){resolved.required=(0,_utilsts.unionStrings)(resolved.required??[],branchSchema.required)}if((0,_utilsts.isPlainObj)(branchSchema.properties)){const branchProps=branchSchema.properties;const mergedProps={...resolved.properties??{}};for(const key of Object.keys(branchProps)){const branchProp=branchProps[key];if(branchProp===undefined)continue;const existing=resolved.properties?.[key];if(existing!==undefined&&typeof existing!=="boolean"&&typeof branchProp!=="boolean"){const merged=engine.merge(existing,branchProp);let mergedProp=merged??branchProp;if(typeof mergedProp!=="boolean"){const existingObj=existing;const branchObj=branchProp;const unionedConstraints=(0,_utilsts.mergeConstraints)(existingObj.constraints,branchObj.constraints);if(unionedConstraints!==undefined){mergedProp={...mergedProp,constraints:unionedConstraints}}}mergedProps[key]=mergedProp}else{mergedProps[key]=branchProp}}resolved.properties=mergedProps}if((0,_utilsts.isPlainObj)(branchSchema.dependencies)){const resolvedDeps=resolved.dependencies??{};const branchDeps=branchSchema.dependencies;const acc={...resolvedDeps};for(const depKey of Object.keys(branchDeps)){const branchVal=branchDeps[depKey];if(branchVal===undefined)continue;const existingVal=acc[depKey];if(existingVal===undefined){acc[depKey]=branchVal}else if(Array.isArray(existingVal)&&Array.isArray(branchVal)){acc[depKey]=(0,_utilsts.unionStrings)(existingVal,branchVal)}else if((0,_utilsts.isPlainObj)(existingVal)&&(0,_utilsts.isPlainObj)(branchVal)){const merged=engine.merge(existingVal,branchVal);acc[depKey]=merged??branchVal}else{acc[depKey]=branchVal}}resolved.dependencies=acc}for(const key of Object.keys(branchSchema)){if(SPECIAL_MERGE_KEYS.has(key))continue;const branchVal=branchSchema[key];const resolvedVal=resolved[key];if(resolvedVal===undefined){resolved[key]=branchVal;continue}if((0,_utilsts.deepEqual)(resolvedVal,branchVal))continue;if(SUB_SCHEMA_KEYS.has(key)){const merged=engine.merge(resolvedVal,branchVal);if(merged!==null){resolved[key]=merged}else{resolved[key]=branchVal}continue}if(MIN_KEYS.has(key)){if(typeof resolvedVal==="number"&&typeof branchVal==="number"){resolved[key]=Math.max(resolvedVal,branchVal)}else{resolved[key]=branchVal}continue}if(MAX_KEYS.has(key)){if(typeof resolvedVal==="number"&&typeof branchVal==="number"){resolved[key]=Math.min(resolvedVal,branchVal)}else{resolved[key]=branchVal}continue}if(key==="uniqueItems"){resolved[key]=resolvedVal===true||branchVal===true;continue}if(key==="pattern"||key==="format"){resolved[key]=branchVal;continue}if(key==="constraints"){const merged=(0,_utilsts.mergeConstraints)(resolvedVal,branchVal);if(merged!==undefined){resolved[key]=merged}continue}const base={[key]:resolvedVal};const branch={[key]:branchVal};const merged=engine.merge(base,branch);if(merged&&typeof merged!=="boolean"&&(0,_utilsts.hasOwn)(merged,key)){resolved[key]=merged[key]}else{resolved[key]=branchVal}}}function resolveConditions(schema,data,engine){let branch=null;const discriminant={};const hasTopLevelIf=schema.if!==undefined;const hasAllOfConditions=Array.isArray(schema.allOf)&&schema.allOf.some(e=>typeof e!=="boolean"&&(0,_utilsts.hasOwn)(e,"if"));if(!hasTopLevelIf&&!hasAllOfConditions){const resolved=resolveNestedProperties(schema,data,engine,discriminant);return{resolved,branch,discriminant}}let resolved={...schema};if(hasAllOfConditions){resolved=resolveAllOfConditions(resolved,data,engine,discriminant)}if(resolved.if!==undefined){const ifSchema=resolved.if;const matches=evaluateCondition(ifSchema,data);extractDiscriminants(ifSchema,data,discriminant);const applicableBranch=matches?resolved.then:resolved.else;branch=matches?"then":"else";if(applicableBranch){mergeBranchInto(resolved,applicableBranch,engine)}delete resolved.if;delete resolved.then;delete resolved.else}resolved=resolveNestedProperties(resolved,data,engine,discriminant);return{resolved,branch,discriminant}}function resolveAllOfConditions(resolved,data,engine,discriminant){if(!Array.isArray(resolved.allOf))return resolved;const remainingAllOf=[];for(const entry of resolved.allOf){if(typeof entry==="boolean"){remainingAllOf.push(entry);continue}const subSchema=entry;if(subSchema.if===undefined){remainingAllOf.push(entry);continue}const ifSchema=subSchema.if;const matches=evaluateCondition(ifSchema,data);extractDiscriminants(ifSchema,data,discriminant);const applicableBranch=matches?subSchema.then:subSchema.else;if(applicableBranch){mergeBranchInto(resolved,applicableBranch,engine)}const remaining=(0,_utilsts.omitKeys)(subSchema,["if","then","else"]);if(Object.keys(remaining).length>0){remainingAllOf.push(remaining)}}resolved={...resolved};if(remainingAllOf.length===0){delete resolved.allOf}else{resolved.allOf=remainingAllOf}return resolved}function resolveNestedProperties(resolved,data,engine,discriminant){if(!(0,_utilsts.isPlainObj)(resolved.properties))return resolved;const props=resolved.properties;const propKeys=Object.keys(props);let changed=false;const resolvedProps={};for(const key of propKeys){const propDef=props[key];if(propDef===undefined)continue;if(typeof propDef==="boolean"){resolvedProps[key]=propDef;continue}const propSchema=propDef;const hasConditions=propSchema.if!==undefined||Array.isArray(propSchema.allOf)&&propSchema.allOf.some(e=>typeof e!=="boolean"&&(0,_utilsts.hasOwn)(e,"if"));if(!hasConditions){resolvedProps[key]=propDef;continue}const nestedData=(0,_utilsts.isPlainObj)(data[key])?data[key]:{};const nested=resolveConditions(propSchema,nestedData,engine);for(const dk of Object.keys(nested.discriminant)){discriminant[`${key}.${dk}`]=nested.discriminant[dk]}resolvedProps[key]=nested.resolved;changed=true}return changed?{...resolved,properties:resolvedProps}:resolved}
|
|
2
2
|
//# sourceMappingURL=condition-resolver.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/condition-resolver.ts"],"sourcesContent":["import type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\nimport type { MergeEngine } from \"./merge-engine.ts\";\nimport { isDataValidForSchema } from \"./runtime-validator.ts\";\nimport type { ResolvedConditionResult } from \"./types.ts\";\nimport {\n\tdeepEqual,\n\thasOwn,\n\tisPlainObj,\n\tmergeConstraints,\n\tomitKeys,\n\tunionStrings,\n} from \"./utils.ts\";\n\n// ─── Condition Resolver ──────────────────────────────────────────────────────\n//\n// Resolves `if/then/else` in a schema by evaluating the `if` against\n// partial data (discriminants).\n//\n// Strategy:\n// 1. Evaluate whether partial data satisfies the `if`\n// 2. Merge the applicable branch (`then` or `else`) into the base schema\n// 3. Remove the `if/then/else` keywords from the result\n// 4. Recurse into `properties` to resolve nested conditions\n//\n// The `if` evaluation relies on a shared runtime validator\n// to avoid duplicating partial validation logic.\n// The resolver continues to orchestrate:\n// - evaluation of `if/then/else` branches\n// - merging the applicable branch\n// - recursive resolution of nested properties\n//\n// Uses custom utilities from `utils.ts`:\n// - `hasOwn` / `isPlainObj` for safe property access and type checks\n// - `unionStrings` for merging string arrays (required, deps)\n// - `omitKeys` for excluding keys from objects\n\n// ─── Keywords classification ─────────────────────────────────────────────────\n\n/** Keywords that must not be processed by the generic loop in mergeBranchInto */\nconst SPECIAL_MERGE_KEYS = new Set([\"required\", \"properties\", \"dependencies\"]);\n\n/** Keywords containing a single sub-schema (mergeable via engine.merge) */\nconst SUB_SCHEMA_KEYS = new Set([\n\t\"additionalProperties\",\n\t\"items\",\n\t\"contains\",\n\t\"propertyNames\",\n\t\"not\",\n]);\n\n/** Numeric keywords of \"minimum\" type (take the max to be more restrictive) */\nconst MIN_KEYS = new Set([\n\t\"minimum\",\n\t\"exclusiveMinimum\",\n\t\"minLength\",\n\t\"minItems\",\n\t\"minProperties\",\n]);\n\n/** Numeric keywords of \"maximum\" type (take the min to be more restrictive) */\nconst MAX_KEYS = new Set([\n\t\"maximum\",\n\t\"exclusiveMaximum\",\n\t\"maxLength\",\n\t\"maxItems\",\n\t\"maxProperties\",\n]);\n\n// ─── Condition evaluation (internal) ─────────────────────────────────────────\n\n/**\n * Evaluates whether partial data satisfies an `if` schema.\n *\n * This version delegates runtime validation to the shared validator.\n * Only the resolver semantics are kept here:\n * if the data matches the `if`, apply `then`, otherwise `else`.\n */\nfunction evaluateCondition(\n\tifSchema: JSONSchema7,\n\tdata: Record<string, unknown>,\n): boolean {\n\treturn isDataValidForSchema(ifSchema, data);\n}\n\n// ─── Discriminant extraction ─────────────────────────────────────────────────\n\n/**\n * Keywords that indicate a property is a discriminant\n * (its value in the data is used for resolution).\n *\n * Point 5 — Extended with numeric/string/pattern constraints.\n */\nconst DISCRIMINANT_INDICATORS = [\n\t\"const\",\n\t\"enum\",\n\t\"minimum\",\n\t\"maximum\",\n\t\"exclusiveMinimum\",\n\t\"exclusiveMaximum\",\n\t\"pattern\",\n\t\"minLength\",\n\t\"maxLength\",\n\t\"multipleOf\",\n\t\"minItems\",\n\t\"maxItems\",\n\t\"format\",\n] as const;\n\n/**\n * Extracts discriminant values used in an `if` schema from partial data.\n *\n * Point 5 — Also collects discriminants for new constraints\n * (minimum, maximum, pattern, etc.).\n */\nfunction extractDiscriminants(\n\tifSchema: JSONSchema7,\n\tdata: Record<string, unknown>,\n\tout: Record<string, unknown>,\n): void {\n\tif (!isPlainObj(ifSchema.properties)) return;\n\n\tconst props = ifSchema.properties as Record<string, JSONSchema7Definition>;\n\tfor (const key of Object.keys(props)) {\n\t\tconst propDef = props[key];\n\t\tif (typeof propDef === \"boolean\") continue;\n\t\tconst prop = propDef as JSONSchema7;\n\n\t\t// Collect if at least one discriminant indicator is present\n\t\tconst hasIndicator = DISCRIMINANT_INDICATORS.some((indicator) =>\n\t\t\thasOwn(prop, indicator),\n\t\t);\n\n\t\tif (hasIndicator && hasOwn(data, key)) {\n\t\t\tout[key] = data[key];\n\t\t}\n\t}\n}\n\n// ─── Branch merging (deduplicated) ───────────────────────────────────────────\n\n/**\n * Merges a conditional branch (`then` or `else`) into the resolved schema.\n *\n * Point 4 — Fix first-writer-wins:\n * Instead of ignoring keywords already present in `resolved`,\n * attempts a smart merge depending on the keyword type:\n *\n * - `properties` → individual merge via engine.merge\n * - `dependencies` → Point 3: union of arrays (form 1),\n * merge of schemas (form 2)\n * - Sub-schema keys → merge via engine.merge\n * - Min keys → `Math.max` (more restrictive)\n * - Max keys → `Math.min` (more restrictive)\n * - `uniqueItems` → `true` wins over `false`\n * - `pattern` / `format` → branch wins (more context-specific)\n * - Others → attempt merge via engine, otherwise branch wins\n *\n * Uses custom utilities from `utils.ts` for each merge operation.\n */\nfunction mergeBranchInto(\n\tresolved: JSONSchema7,\n\tbranchDef: JSONSchema7Definition,\n\tengine: MergeEngine,\n): void {\n\tif (typeof branchDef === \"boolean\") return;\n\n\tconst branchSchema = branchDef as JSONSchema7;\n\n\t// ── Merge required via unionStrings (deduplicated automatically) ──\n\tif (Array.isArray(branchSchema.required)) {\n\t\tresolved.required = unionStrings(\n\t\t\tresolved.required ?? [],\n\t\t\tbranchSchema.required,\n\t\t);\n\t}\n\n\t// ── Merge properties ──\n\tif (isPlainObj(branchSchema.properties)) {\n\t\tconst branchProps = branchSchema.properties as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition\n\t\t>;\n\t\tconst mergedProps: Record<string, JSONSchema7Definition> = {\n\t\t\t...(resolved.properties ?? {}),\n\t\t};\n\t\tfor (const key of Object.keys(branchProps)) {\n\t\t\tconst branchProp = branchProps[key];\n\t\t\tif (branchProp === undefined) continue;\n\t\t\tconst existing = resolved.properties?.[key];\n\t\t\tif (\n\t\t\t\texisting !== undefined &&\n\t\t\t\ttypeof existing !== \"boolean\" &&\n\t\t\t\ttypeof branchProp !== \"boolean\"\n\t\t\t) {\n\t\t\t\tconst merged = engine.merge(\n\t\t\t\t\texisting as JSONSchema7Definition,\n\t\t\t\t\tbranchProp as JSONSchema7Definition,\n\t\t\t\t);\n\t\t\t\tmergedProps[key] = (merged ?? branchProp) as JSONSchema7Definition;\n\t\t\t} else {\n\t\t\t\tmergedProps[key] = branchProp;\n\t\t\t}\n\t\t}\n\t\tresolved.properties = mergedProps;\n\t}\n\n\t// ── Merge dependencies (Point 3) ──\n\tif (isPlainObj(branchSchema.dependencies)) {\n\t\tconst resolvedDeps = (resolved.dependencies ?? {}) as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition | string[]\n\t\t>;\n\t\tconst branchDeps = branchSchema.dependencies as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition | string[]\n\t\t>;\n\n\t\tconst acc = { ...resolvedDeps };\n\t\tfor (const depKey of Object.keys(branchDeps)) {\n\t\t\tconst branchVal = branchDeps[depKey] as\n\t\t\t\t| JSONSchema7Definition\n\t\t\t\t| string[]\n\t\t\t\t| undefined;\n\t\t\tif (branchVal === undefined) continue;\n\t\t\tconst existingVal = acc[depKey] as\n\t\t\t\t| JSONSchema7Definition\n\t\t\t\t| string[]\n\t\t\t\t| undefined;\n\n\t\t\tif (existingVal === undefined) {\n\t\t\t\t// No existing value → copy directly\n\t\t\t\tacc[depKey] = branchVal;\n\t\t\t} else if (Array.isArray(existingVal) && Array.isArray(branchVal)) {\n\t\t\t\t// Form 1: deduplicated union of string arrays\n\t\t\t\tacc[depKey] = unionStrings(\n\t\t\t\t\texistingVal as string[],\n\t\t\t\t\tbranchVal as string[],\n\t\t\t\t);\n\t\t\t} else if (isPlainObj(existingVal) && isPlainObj(branchVal)) {\n\t\t\t\t// Form 2: merge sub-schemas\n\t\t\t\tconst merged = engine.merge(\n\t\t\t\t\texistingVal as JSONSchema7Definition,\n\t\t\t\t\tbranchVal as JSONSchema7Definition,\n\t\t\t\t);\n\t\t\t\tacc[depKey] = (merged ?? branchVal) as JSONSchema7Definition;\n\t\t\t} else {\n\t\t\t\t// Incompatible types (array vs schema) → branch wins\n\t\t\t\tacc[depKey] = branchVal;\n\t\t\t}\n\t\t}\n\t\tresolved.dependencies = acc as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition | string[]\n\t\t>;\n\t}\n\n\t// ── Merge remaining keywords (Point 4 — fix first-writer-wins) ──\n\tfor (const key of Object.keys(branchSchema) as (keyof JSONSchema7)[]) {\n\t\t// Skip keys already handled above\n\t\tif (SPECIAL_MERGE_KEYS.has(key)) continue;\n\n\t\tconst branchVal = branchSchema[key];\n\t\tconst resolvedVal = resolved[key];\n\n\t\t// If resolved doesn't have this key → copy directly\n\t\tif (resolvedVal === undefined) {\n\t\t\t(resolved as Record<string, unknown>)[key] = branchVal;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// If both have the same value → nothing to do\n\t\tif (deepEqual(resolvedVal, branchVal)) continue;\n\n\t\t// ── Sub-schema keys → merge via engine ──\n\t\tif (SUB_SCHEMA_KEYS.has(key)) {\n\t\t\tconst merged = engine.merge(\n\t\t\t\tresolvedVal as JSONSchema7Definition,\n\t\t\t\tbranchVal as JSONSchema7Definition,\n\t\t\t);\n\t\t\tif (merged !== null) {\n\t\t\t\t(resolved as Record<string, unknown>)[key] = merged;\n\t\t\t} else {\n\t\t\t\t// Merge impossible → branch wins (conditional context)\n\t\t\t\t(resolved as Record<string, unknown>)[key] = branchVal;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// ── Min keys → Math.max (more restrictive) ──\n\t\tif (MIN_KEYS.has(key)) {\n\t\t\tif (typeof resolvedVal === \"number\" && typeof branchVal === \"number\") {\n\t\t\t\t(resolved as Record<string, unknown>)[key] = Math.max(\n\t\t\t\t\tresolvedVal,\n\t\t\t\t\tbranchVal,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t(resolved as Record<string, unknown>)[key] = branchVal;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// ── Max keys → Math.min (more restrictive) ──\n\t\tif (MAX_KEYS.has(key)) {\n\t\t\tif (typeof resolvedVal === \"number\" && typeof branchVal === \"number\") {\n\t\t\t\t(resolved as Record<string, unknown>)[key] = Math.min(\n\t\t\t\t\tresolvedVal,\n\t\t\t\t\tbranchVal,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t(resolved as Record<string, unknown>)[key] = branchVal;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// ── uniqueItems → true wins over false ──\n\t\tif (key === \"uniqueItems\") {\n\t\t\t(resolved as Record<string, unknown>)[key] =\n\t\t\t\tresolvedVal === true || branchVal === true;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// ── pattern / format → branch wins (more context-specific) ──\n\t\tif (key === \"pattern\" || key === \"format\") {\n\t\t\t(resolved as Record<string, unknown>)[key] = branchVal;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// ── constraints → union + dedup (intersection semantics) ──\n\t\t// Constraints follow allOf semantics: both the base and the branch\n\t\t// constraints must be satisfied. This is the same logic used by\n\t\t// the merge engine's applyConstraintsMerge post-processor.\n\t\tif (key === \"constraints\") {\n\t\t\tconst merged = mergeConstraints(resolvedVal, branchVal);\n\t\t\tif (merged !== undefined) {\n\t\t\t\t(resolved as Record<string, unknown>)[key] = merged;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// ── Fallback: attempt merge via engine for remaining cases ──\n\t\tconst base = { [key]: resolvedVal } as JSONSchema7Definition;\n\t\tconst branch = { [key]: branchVal } as JSONSchema7Definition;\n\t\tconst merged = engine.merge(base, branch);\n\t\tif (\n\t\t\tmerged &&\n\t\t\ttypeof merged !== \"boolean\" &&\n\t\t\thasOwn(merged as object, key)\n\t\t) {\n\t\t\t(resolved as Record<string, unknown>)[key] = (\n\t\t\t\tmerged as unknown as Record<string, unknown>\n\t\t\t)[key];\n\t\t} else {\n\t\t\t// Merge failed → branch wins (applicable conditional context)\n\t\t\t(resolved as Record<string, unknown>)[key] = branchVal;\n\t\t}\n\t}\n}\n\n// ─── Public API ──────────────────────────────────────────────────────────────\n\n/**\n * Resolves `if/then/else` in a schema by evaluating the `if` against\n * partial data (discriminants).\n *\n * @param schema The schema potentially containing if/then/else\n * @param data Partial data used to evaluate the conditions\n * @param engine The MergeEngine for merging branches\n *\n * @example\n * ```ts\n * const form = {\n * type: \"object\",\n * properties: { accountType: { type: \"string\" }, ... },\n * if: { properties: { accountType: { const: \"business\" } } },\n * then: { required: [\"companyName\"] },\n * else: { required: [\"firstName\"] },\n * };\n *\n * const { resolved } = resolveConditions(form, { accountType: \"business\" }, engine);\n * // → resolved no longer has if/then/else, but has required: [\"companyName\"]\n * ```\n */\nexport function resolveConditions(\n\tschema: JSONSchema7,\n\tdata: Record<string, unknown>,\n\tengine: MergeEngine,\n): ResolvedConditionResult {\n\tlet branch: \"then\" | \"else\" | null = null;\n\tconst discriminant: Record<string, unknown> = {};\n\n\t// ── Fast path: no conditions at all ──\n\t// If there's no `if` and no `allOf` with conditions, skip the copy entirely.\n\tconst hasTopLevelIf = schema.if !== undefined;\n\tconst hasAllOfConditions =\n\t\tArray.isArray(schema.allOf) &&\n\t\tschema.allOf.some(\n\t\t\t(e) => typeof e !== \"boolean\" && hasOwn(e as object, \"if\"),\n\t\t);\n\n\tif (!hasTopLevelIf && !hasAllOfConditions) {\n\t\t// Phase 3 only: check nested properties (resolveNestedProperties\n\t\t// already returns the original if nothing changes)\n\t\tconst resolved = resolveNestedProperties(\n\t\t\tschema,\n\t\t\tdata,\n\t\t\tengine,\n\t\t\tdiscriminant,\n\t\t);\n\t\treturn { resolved, branch, discriminant };\n\t}\n\n\t// ── Copy-on-write: only copy when mutations are needed ──\n\tlet resolved = { ...schema };\n\n\t// ── Phase 1: Resolve if/then/else in allOf ──\n\tif (hasAllOfConditions) {\n\t\tresolved = resolveAllOfConditions(resolved, data, engine, discriminant);\n\t}\n\n\t// ── Phase 2: Resolve if/then/else at this level ──\n\tif (resolved.if !== undefined) {\n\t\tconst ifSchema = resolved.if as JSONSchema7;\n\t\tconst matches = evaluateCondition(ifSchema, data);\n\n\t\textractDiscriminants(ifSchema, data, discriminant);\n\n\t\tconst applicableBranch = matches ? resolved.then : resolved.else;\n\t\tbranch = matches ? \"then\" : \"else\";\n\n\t\tif (applicableBranch) {\n\t\t\tmergeBranchInto(\n\t\t\t\tresolved,\n\t\t\t\tapplicableBranch as JSONSchema7Definition,\n\t\t\t\tengine,\n\t\t\t);\n\t\t}\n\n\t\tdelete resolved.if;\n\t\tdelete resolved.then;\n\t\tdelete resolved.else;\n\t}\n\n\t// ── Phase 3: Recurse into properties ──\n\tresolved = resolveNestedProperties(resolved, data, engine, discriminant);\n\n\treturn { resolved, branch, discriminant };\n}\n\n// ─── Internal phases ─────────────────────────────────────────────────────────\n\n/**\n * Phase 1: Iterates over `allOf` entries and resolves those containing\n * an `if/then/else`. Non-conditional entries are preserved.\n *\n\n */\nfunction resolveAllOfConditions(\n\tresolved: JSONSchema7,\n\tdata: Record<string, unknown>,\n\tengine: MergeEngine,\n\tdiscriminant: Record<string, unknown>,\n): JSONSchema7 {\n\tif (!Array.isArray(resolved.allOf)) return resolved;\n\n\tconst remainingAllOf: JSONSchema7Definition[] = [];\n\n\tfor (const entry of resolved.allOf) {\n\t\tif (typeof entry === \"boolean\") {\n\t\t\tremainingAllOf.push(entry);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst subSchema = entry as JSONSchema7;\n\n\t\tif (subSchema.if === undefined) {\n\t\t\tremainingAllOf.push(entry);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Resolve the condition of this allOf entry\n\t\tconst ifSchema = subSchema.if as JSONSchema7;\n\t\tconst matches = evaluateCondition(ifSchema, data);\n\n\t\textractDiscriminants(ifSchema, data, discriminant);\n\n\t\tconst applicableBranch = matches ? subSchema.then : subSchema.else;\n\n\t\tif (applicableBranch) {\n\t\t\tmergeBranchInto(\n\t\t\t\tresolved,\n\t\t\t\tapplicableBranch as JSONSchema7Definition,\n\t\t\t\tengine,\n\t\t\t);\n\t\t}\n\n\t\t// Keep non-conditional parts of the allOf entry\n\t\tconst remaining = omitKeys(\n\t\t\tsubSchema as unknown as Record<string, unknown>,\n\t\t\t[\"if\", \"then\", \"else\"],\n\t\t);\n\t\tif (Object.keys(remaining).length > 0) {\n\t\t\tremainingAllOf.push(remaining as JSONSchema7);\n\t\t}\n\t}\n\n\tresolved = { ...resolved };\n\tif (remainingAllOf.length === 0) {\n\t\tdelete resolved.allOf;\n\t} else {\n\t\tresolved.allOf = remainingAllOf;\n\t}\n\n\treturn resolved;\n}\n\n/**\n * Phase 3: Recurses into the `properties` of the resolved schema to resolve\n * nested conditions (e.g. an object whose property has an if/then/else).\n *\n\n */\nfunction resolveNestedProperties(\n\tresolved: JSONSchema7,\n\tdata: Record<string, unknown>,\n\tengine: MergeEngine,\n\tdiscriminant: Record<string, unknown>,\n): JSONSchema7 {\n\tif (!isPlainObj(resolved.properties)) return resolved;\n\n\tconst props = resolved.properties as Record<string, JSONSchema7Definition>;\n\tconst propKeys = Object.keys(props);\n\tlet changed = false;\n\tconst resolvedProps: Record<string, JSONSchema7Definition> = {};\n\n\tfor (const key of propKeys) {\n\t\tconst propDef = props[key];\n\t\tif (propDef === undefined) continue;\n\t\tif (typeof propDef === \"boolean\") {\n\t\t\tresolvedProps[key] = propDef;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst propSchema = propDef as JSONSchema7;\n\t\tconst hasConditions =\n\t\t\tpropSchema.if !== undefined ||\n\t\t\t(Array.isArray(propSchema.allOf) &&\n\t\t\t\tpropSchema.allOf.some(\n\t\t\t\t\t(e) => typeof e !== \"boolean\" && hasOwn(e as object, \"if\"),\n\t\t\t\t));\n\n\t\tif (!hasConditions) {\n\t\t\tresolvedProps[key] = propDef;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Nested data available → resolve recursively\n\t\tconst nestedData = isPlainObj(data[key])\n\t\t\t? (data[key] as Record<string, unknown>)\n\t\t\t: {};\n\n\t\tconst nested = resolveConditions(propSchema, nestedData, engine);\n\n\t\t// Propagate nested discriminants with prefix\n\t\tfor (const dk of Object.keys(nested.discriminant)) {\n\t\t\tdiscriminant[`${key}.${dk}`] = nested.discriminant[dk];\n\t\t}\n\n\t\tresolvedProps[key] = nested.resolved;\n\t\tchanged = true;\n\t}\n\n\treturn changed ? { ...resolved, properties: resolvedProps } : resolved;\n}\n"],"names":["resolveConditions","SPECIAL_MERGE_KEYS","Set","SUB_SCHEMA_KEYS","MIN_KEYS","MAX_KEYS","evaluateCondition","ifSchema","data","isDataValidForSchema","DISCRIMINANT_INDICATORS","extractDiscriminants","out","isPlainObj","properties","props","key","Object","keys","propDef","prop","hasIndicator","some","indicator","hasOwn","mergeBranchInto","resolved","branchDef","engine","branchSchema","Array","isArray","required","unionStrings","branchProps","mergedProps","branchProp","undefined","existing","merged","merge","dependencies","resolvedDeps","branchDeps","acc","depKey","branchVal","existingVal","has","resolvedVal","deepEqual","Math","max","min","mergeConstraints","base","branch","schema","discriminant","hasTopLevelIf","if","hasAllOfConditions","allOf","e","resolveNestedProperties","resolveAllOfConditions","matches","applicableBranch","then","else","remainingAllOf","entry","push","subSchema","remaining","omitKeys","length","propKeys","changed","resolvedProps","propSchema","hasConditions","nestedData","nested","dk"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/condition-resolver.ts"],"sourcesContent":["import type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\nimport type { MergeEngine } from \"./merge-engine.ts\";\nimport { isDataValidForSchema } from \"./runtime-validator.ts\";\nimport type { ResolvedConditionResult } from \"./types.ts\";\nimport {\n\tdeepEqual,\n\thasOwn,\n\tisPlainObj,\n\tmergeConstraints,\n\tomitKeys,\n\tunionStrings,\n} from \"./utils.ts\";\n\n// ─── Condition Resolver ──────────────────────────────────────────────────────\n//\n// Resolves `if/then/else` in a schema by evaluating the `if` against\n// partial data (discriminants).\n//\n// Strategy:\n// 1. Evaluate whether partial data satisfies the `if`\n// 2. Merge the applicable branch (`then` or `else`) into the base schema\n// 3. Remove the `if/then/else` keywords from the result\n// 4. Recurse into `properties` to resolve nested conditions\n//\n// The `if` evaluation relies on a shared runtime validator\n// to avoid duplicating partial validation logic.\n// The resolver continues to orchestrate:\n// - evaluation of `if/then/else` branches\n// - merging the applicable branch\n// - recursive resolution of nested properties\n//\n// Uses custom utilities from `utils.ts`:\n// - `hasOwn` / `isPlainObj` for safe property access and type checks\n// - `unionStrings` for merging string arrays (required, deps)\n// - `omitKeys` for excluding keys from objects\n\n// ─── Keywords classification ─────────────────────────────────────────────────\n\n/** Keywords that must not be processed by the generic loop in mergeBranchInto */\nconst SPECIAL_MERGE_KEYS = new Set([\"required\", \"properties\", \"dependencies\"]);\n\n/** Keywords containing a single sub-schema (mergeable via engine.merge) */\nconst SUB_SCHEMA_KEYS = new Set([\n\t\"additionalProperties\",\n\t\"items\",\n\t\"contains\",\n\t\"propertyNames\",\n\t\"not\",\n]);\n\n/** Numeric keywords of \"minimum\" type (take the max to be more restrictive) */\nconst MIN_KEYS = new Set([\n\t\"minimum\",\n\t\"exclusiveMinimum\",\n\t\"minLength\",\n\t\"minItems\",\n\t\"minProperties\",\n]);\n\n/** Numeric keywords of \"maximum\" type (take the min to be more restrictive) */\nconst MAX_KEYS = new Set([\n\t\"maximum\",\n\t\"exclusiveMaximum\",\n\t\"maxLength\",\n\t\"maxItems\",\n\t\"maxProperties\",\n]);\n\n// ─── Condition evaluation (internal) ─────────────────────────────────────────\n\n/**\n * Evaluates whether partial data satisfies an `if` schema.\n *\n * This version delegates runtime validation to the shared validator.\n * Only the resolver semantics are kept here:\n * if the data matches the `if`, apply `then`, otherwise `else`.\n */\nfunction evaluateCondition(\n\tifSchema: JSONSchema7,\n\tdata: Record<string, unknown>,\n): boolean {\n\treturn isDataValidForSchema(ifSchema, data);\n}\n\n// ─── Discriminant extraction ─────────────────────────────────────────────────\n\n/**\n * Keywords that indicate a property is a discriminant\n * (its value in the data is used for resolution).\n *\n * Point 5 — Extended with numeric/string/pattern constraints.\n */\nconst DISCRIMINANT_INDICATORS = [\n\t\"const\",\n\t\"enum\",\n\t\"minimum\",\n\t\"maximum\",\n\t\"exclusiveMinimum\",\n\t\"exclusiveMaximum\",\n\t\"pattern\",\n\t\"minLength\",\n\t\"maxLength\",\n\t\"multipleOf\",\n\t\"minItems\",\n\t\"maxItems\",\n\t\"format\",\n] as const;\n\n/**\n * Extracts discriminant values used in an `if` schema from partial data.\n *\n * Point 5 — Also collects discriminants for new constraints\n * (minimum, maximum, pattern, etc.).\n */\nfunction extractDiscriminants(\n\tifSchema: JSONSchema7,\n\tdata: Record<string, unknown>,\n\tout: Record<string, unknown>,\n): void {\n\tif (!isPlainObj(ifSchema.properties)) return;\n\n\tconst props = ifSchema.properties as Record<string, JSONSchema7Definition>;\n\tfor (const key of Object.keys(props)) {\n\t\tconst propDef = props[key];\n\t\tif (typeof propDef === \"boolean\") continue;\n\t\tconst prop = propDef as JSONSchema7;\n\n\t\t// Collect if at least one discriminant indicator is present\n\t\tconst hasIndicator = DISCRIMINANT_INDICATORS.some((indicator) =>\n\t\t\thasOwn(prop, indicator),\n\t\t);\n\n\t\tif (hasIndicator && hasOwn(data, key)) {\n\t\t\tout[key] = data[key];\n\t\t}\n\t}\n}\n\n// ─── Branch merging (deduplicated) ───────────────────────────────────────────\n\n/**\n * Merges a conditional branch (`then` or `else`) into the resolved schema.\n *\n * Point 4 — Fix first-writer-wins:\n * Instead of ignoring keywords already present in `resolved`,\n * attempts a smart merge depending on the keyword type:\n *\n * - `properties` → individual merge via engine.merge\n * - `dependencies` → Point 3: union of arrays (form 1),\n * merge of schemas (form 2)\n * - Sub-schema keys → merge via engine.merge\n * - Min keys → `Math.max` (more restrictive)\n * - Max keys → `Math.min` (more restrictive)\n * - `uniqueItems` → `true` wins over `false`\n * - `pattern` / `format` → branch wins (more context-specific)\n * - Others → attempt merge via engine, otherwise branch wins\n *\n * Uses custom utilities from `utils.ts` for each merge operation.\n */\nfunction mergeBranchInto(\n\tresolved: JSONSchema7,\n\tbranchDef: JSONSchema7Definition,\n\tengine: MergeEngine,\n): void {\n\tif (typeof branchDef === \"boolean\") return;\n\n\tconst branchSchema = branchDef as JSONSchema7;\n\n\t// ── Merge required via unionStrings (deduplicated automatically) ──\n\tif (Array.isArray(branchSchema.required)) {\n\t\tresolved.required = unionStrings(\n\t\t\tresolved.required ?? [],\n\t\t\tbranchSchema.required,\n\t\t);\n\t}\n\n\t// ── Merge properties ──\n\tif (isPlainObj(branchSchema.properties)) {\n\t\tconst branchProps = branchSchema.properties as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition\n\t\t>;\n\t\tconst mergedProps: Record<string, JSONSchema7Definition> = {\n\t\t\t...(resolved.properties ?? {}),\n\t\t};\n\t\tfor (const key of Object.keys(branchProps)) {\n\t\t\tconst branchProp = branchProps[key];\n\t\t\tif (branchProp === undefined) continue;\n\t\t\tconst existing = resolved.properties?.[key];\n\t\t\tif (\n\t\t\t\texisting !== undefined &&\n\t\t\t\ttypeof existing !== \"boolean\" &&\n\t\t\t\ttypeof branchProp !== \"boolean\"\n\t\t\t) {\n\t\t\t\tconst merged = engine.merge(\n\t\t\t\t\texisting as JSONSchema7Definition,\n\t\t\t\t\tbranchProp as JSONSchema7Definition,\n\t\t\t\t);\n\t\t\t\tlet mergedProp = (merged ?? branchProp) as JSONSchema7Definition;\n\n\t\t\t\t// The merge engine does not handle the custom `constraints`\n\t\t\t\t// keyword — it uses identity/first-wins for unknown keywords.\n\t\t\t\t// We need to manually union + dedup constraints from both\n\t\t\t\t// the existing property and the branch property so that all\n\t\t\t\t// runtime constraints are preserved in the resolved schema.\n\t\t\t\tif (typeof mergedProp !== \"boolean\") {\n\t\t\t\t\tconst existingObj = existing as JSONSchema7;\n\t\t\t\t\tconst branchObj = branchProp as JSONSchema7;\n\t\t\t\t\tconst unionedConstraints = mergeConstraints(\n\t\t\t\t\t\texistingObj.constraints,\n\t\t\t\t\t\tbranchObj.constraints,\n\t\t\t\t\t);\n\t\t\t\t\tif (unionedConstraints !== undefined) {\n\t\t\t\t\t\tmergedProp = { ...mergedProp, constraints: unionedConstraints };\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tmergedProps[key] = mergedProp;\n\t\t\t} else {\n\t\t\t\tmergedProps[key] = branchProp;\n\t\t\t}\n\t\t}\n\t\tresolved.properties = mergedProps;\n\t}\n\n\t// ── Merge dependencies (Point 3) ──\n\tif (isPlainObj(branchSchema.dependencies)) {\n\t\tconst resolvedDeps = (resolved.dependencies ?? {}) as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition | string[]\n\t\t>;\n\t\tconst branchDeps = branchSchema.dependencies as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition | string[]\n\t\t>;\n\n\t\tconst acc = { ...resolvedDeps };\n\t\tfor (const depKey of Object.keys(branchDeps)) {\n\t\t\tconst branchVal = branchDeps[depKey] as\n\t\t\t\t| JSONSchema7Definition\n\t\t\t\t| string[]\n\t\t\t\t| undefined;\n\t\t\tif (branchVal === undefined) continue;\n\t\t\tconst existingVal = acc[depKey] as\n\t\t\t\t| JSONSchema7Definition\n\t\t\t\t| string[]\n\t\t\t\t| undefined;\n\n\t\t\tif (existingVal === undefined) {\n\t\t\t\t// No existing value → copy directly\n\t\t\t\tacc[depKey] = branchVal;\n\t\t\t} else if (Array.isArray(existingVal) && Array.isArray(branchVal)) {\n\t\t\t\t// Form 1: deduplicated union of string arrays\n\t\t\t\tacc[depKey] = unionStrings(\n\t\t\t\t\texistingVal as string[],\n\t\t\t\t\tbranchVal as string[],\n\t\t\t\t);\n\t\t\t} else if (isPlainObj(existingVal) && isPlainObj(branchVal)) {\n\t\t\t\t// Form 2: merge sub-schemas\n\t\t\t\tconst merged = engine.merge(\n\t\t\t\t\texistingVal as JSONSchema7Definition,\n\t\t\t\t\tbranchVal as JSONSchema7Definition,\n\t\t\t\t);\n\t\t\t\tacc[depKey] = (merged ?? branchVal) as JSONSchema7Definition;\n\t\t\t} else {\n\t\t\t\t// Incompatible types (array vs schema) → branch wins\n\t\t\t\tacc[depKey] = branchVal;\n\t\t\t}\n\t\t}\n\t\tresolved.dependencies = acc as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition | string[]\n\t\t>;\n\t}\n\n\t// ── Merge remaining keywords (Point 4 — fix first-writer-wins) ──\n\tfor (const key of Object.keys(branchSchema) as (keyof JSONSchema7)[]) {\n\t\t// Skip keys already handled above\n\t\tif (SPECIAL_MERGE_KEYS.has(key)) continue;\n\n\t\tconst branchVal = branchSchema[key];\n\t\tconst resolvedVal = resolved[key];\n\n\t\t// If resolved doesn't have this key → copy directly\n\t\tif (resolvedVal === undefined) {\n\t\t\t(resolved as Record<string, unknown>)[key] = branchVal;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// If both have the same value → nothing to do\n\t\tif (deepEqual(resolvedVal, branchVal)) continue;\n\n\t\t// ── Sub-schema keys → merge via engine ──\n\t\tif (SUB_SCHEMA_KEYS.has(key)) {\n\t\t\tconst merged = engine.merge(\n\t\t\t\tresolvedVal as JSONSchema7Definition,\n\t\t\t\tbranchVal as JSONSchema7Definition,\n\t\t\t);\n\t\t\tif (merged !== null) {\n\t\t\t\t(resolved as Record<string, unknown>)[key] = merged;\n\t\t\t} else {\n\t\t\t\t// Merge impossible → branch wins (conditional context)\n\t\t\t\t(resolved as Record<string, unknown>)[key] = branchVal;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// ── Min keys → Math.max (more restrictive) ──\n\t\tif (MIN_KEYS.has(key)) {\n\t\t\tif (typeof resolvedVal === \"number\" && typeof branchVal === \"number\") {\n\t\t\t\t(resolved as Record<string, unknown>)[key] = Math.max(\n\t\t\t\t\tresolvedVal,\n\t\t\t\t\tbranchVal,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t(resolved as Record<string, unknown>)[key] = branchVal;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// ── Max keys → Math.min (more restrictive) ──\n\t\tif (MAX_KEYS.has(key)) {\n\t\t\tif (typeof resolvedVal === \"number\" && typeof branchVal === \"number\") {\n\t\t\t\t(resolved as Record<string, unknown>)[key] = Math.min(\n\t\t\t\t\tresolvedVal,\n\t\t\t\t\tbranchVal,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t(resolved as Record<string, unknown>)[key] = branchVal;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// ── uniqueItems → true wins over false ──\n\t\tif (key === \"uniqueItems\") {\n\t\t\t(resolved as Record<string, unknown>)[key] =\n\t\t\t\tresolvedVal === true || branchVal === true;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// ── pattern / format → branch wins (more context-specific) ──\n\t\tif (key === \"pattern\" || key === \"format\") {\n\t\t\t(resolved as Record<string, unknown>)[key] = branchVal;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// ── constraints → union + dedup (intersection semantics) ──\n\t\t// Constraints follow allOf semantics: both the base and the branch\n\t\t// constraints must be satisfied. This is the same logic used by\n\t\t// the merge engine's applyConstraintsMerge post-processor.\n\t\tif (key === \"constraints\") {\n\t\t\tconst merged = mergeConstraints(resolvedVal, branchVal);\n\t\t\tif (merged !== undefined) {\n\t\t\t\t(resolved as Record<string, unknown>)[key] = merged;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// ── Fallback: attempt merge via engine for remaining cases ──\n\t\tconst base = { [key]: resolvedVal } as JSONSchema7Definition;\n\t\tconst branch = { [key]: branchVal } as JSONSchema7Definition;\n\t\tconst merged = engine.merge(base, branch);\n\t\tif (\n\t\t\tmerged &&\n\t\t\ttypeof merged !== \"boolean\" &&\n\t\t\thasOwn(merged as object, key)\n\t\t) {\n\t\t\t(resolved as Record<string, unknown>)[key] = (\n\t\t\t\tmerged as unknown as Record<string, unknown>\n\t\t\t)[key];\n\t\t} else {\n\t\t\t// Merge failed → branch wins (applicable conditional context)\n\t\t\t(resolved as Record<string, unknown>)[key] = branchVal;\n\t\t}\n\t}\n}\n\n// ─── Public API ──────────────────────────────────────────────────────────────\n\n/**\n * Resolves `if/then/else` in a schema by evaluating the `if` against\n * partial data (discriminants).\n *\n * @param schema The schema potentially containing if/then/else\n * @param data Partial data used to evaluate the conditions\n * @param engine The MergeEngine for merging branches\n *\n * @example\n * ```ts\n * const form = {\n * type: \"object\",\n * properties: { accountType: { type: \"string\" }, ... },\n * if: { properties: { accountType: { const: \"business\" } } },\n * then: { required: [\"companyName\"] },\n * else: { required: [\"firstName\"] },\n * };\n *\n * const { resolved } = resolveConditions(form, { accountType: \"business\" }, engine);\n * // → resolved no longer has if/then/else, but has required: [\"companyName\"]\n * ```\n */\nexport function resolveConditions(\n\tschema: JSONSchema7,\n\tdata: Record<string, unknown>,\n\tengine: MergeEngine,\n): ResolvedConditionResult {\n\tlet branch: \"then\" | \"else\" | null = null;\n\tconst discriminant: Record<string, unknown> = {};\n\n\t// ── Fast path: no conditions at all ──\n\t// If there's no `if` and no `allOf` with conditions, skip the copy entirely.\n\tconst hasTopLevelIf = schema.if !== undefined;\n\tconst hasAllOfConditions =\n\t\tArray.isArray(schema.allOf) &&\n\t\tschema.allOf.some(\n\t\t\t(e) => typeof e !== \"boolean\" && hasOwn(e as object, \"if\"),\n\t\t);\n\n\tif (!hasTopLevelIf && !hasAllOfConditions) {\n\t\t// Phase 3 only: check nested properties (resolveNestedProperties\n\t\t// already returns the original if nothing changes)\n\t\tconst resolved = resolveNestedProperties(\n\t\t\tschema,\n\t\t\tdata,\n\t\t\tengine,\n\t\t\tdiscriminant,\n\t\t);\n\t\treturn { resolved, branch, discriminant };\n\t}\n\n\t// ── Copy-on-write: only copy when mutations are needed ──\n\tlet resolved = { ...schema };\n\n\t// ── Phase 1: Resolve if/then/else in allOf ──\n\tif (hasAllOfConditions) {\n\t\tresolved = resolveAllOfConditions(resolved, data, engine, discriminant);\n\t}\n\n\t// ── Phase 2: Resolve if/then/else at this level ──\n\tif (resolved.if !== undefined) {\n\t\tconst ifSchema = resolved.if as JSONSchema7;\n\t\tconst matches = evaluateCondition(ifSchema, data);\n\n\t\textractDiscriminants(ifSchema, data, discriminant);\n\n\t\tconst applicableBranch = matches ? resolved.then : resolved.else;\n\t\tbranch = matches ? \"then\" : \"else\";\n\n\t\tif (applicableBranch) {\n\t\t\tmergeBranchInto(\n\t\t\t\tresolved,\n\t\t\t\tapplicableBranch as JSONSchema7Definition,\n\t\t\t\tengine,\n\t\t\t);\n\t\t}\n\n\t\tdelete resolved.if;\n\t\tdelete resolved.then;\n\t\tdelete resolved.else;\n\t}\n\n\t// ── Phase 3: Recurse into properties ──\n\tresolved = resolveNestedProperties(resolved, data, engine, discriminant);\n\n\treturn { resolved, branch, discriminant };\n}\n\n// ─── Internal phases ─────────────────────────────────────────────────────────\n\n/**\n * Phase 1: Iterates over `allOf` entries and resolves those containing\n * an `if/then/else`. Non-conditional entries are preserved.\n *\n\n */\nfunction resolveAllOfConditions(\n\tresolved: JSONSchema7,\n\tdata: Record<string, unknown>,\n\tengine: MergeEngine,\n\tdiscriminant: Record<string, unknown>,\n): JSONSchema7 {\n\tif (!Array.isArray(resolved.allOf)) return resolved;\n\n\tconst remainingAllOf: JSONSchema7Definition[] = [];\n\n\tfor (const entry of resolved.allOf) {\n\t\tif (typeof entry === \"boolean\") {\n\t\t\tremainingAllOf.push(entry);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst subSchema = entry as JSONSchema7;\n\n\t\tif (subSchema.if === undefined) {\n\t\t\tremainingAllOf.push(entry);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Resolve the condition of this allOf entry\n\t\tconst ifSchema = subSchema.if as JSONSchema7;\n\t\tconst matches = evaluateCondition(ifSchema, data);\n\n\t\textractDiscriminants(ifSchema, data, discriminant);\n\n\t\tconst applicableBranch = matches ? subSchema.then : subSchema.else;\n\n\t\tif (applicableBranch) {\n\t\t\tmergeBranchInto(\n\t\t\t\tresolved,\n\t\t\t\tapplicableBranch as JSONSchema7Definition,\n\t\t\t\tengine,\n\t\t\t);\n\t\t}\n\n\t\t// Keep non-conditional parts of the allOf entry\n\t\tconst remaining = omitKeys(\n\t\t\tsubSchema as unknown as Record<string, unknown>,\n\t\t\t[\"if\", \"then\", \"else\"],\n\t\t);\n\t\tif (Object.keys(remaining).length > 0) {\n\t\t\tremainingAllOf.push(remaining as JSONSchema7);\n\t\t}\n\t}\n\n\tresolved = { ...resolved };\n\tif (remainingAllOf.length === 0) {\n\t\tdelete resolved.allOf;\n\t} else {\n\t\tresolved.allOf = remainingAllOf;\n\t}\n\n\treturn resolved;\n}\n\n/**\n * Phase 3: Recurses into the `properties` of the resolved schema to resolve\n * nested conditions (e.g. an object whose property has an if/then/else).\n *\n\n */\nfunction resolveNestedProperties(\n\tresolved: JSONSchema7,\n\tdata: Record<string, unknown>,\n\tengine: MergeEngine,\n\tdiscriminant: Record<string, unknown>,\n): JSONSchema7 {\n\tif (!isPlainObj(resolved.properties)) return resolved;\n\n\tconst props = resolved.properties as Record<string, JSONSchema7Definition>;\n\tconst propKeys = Object.keys(props);\n\tlet changed = false;\n\tconst resolvedProps: Record<string, JSONSchema7Definition> = {};\n\n\tfor (const key of propKeys) {\n\t\tconst propDef = props[key];\n\t\tif (propDef === undefined) continue;\n\t\tif (typeof propDef === \"boolean\") {\n\t\t\tresolvedProps[key] = propDef;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst propSchema = propDef as JSONSchema7;\n\t\tconst hasConditions =\n\t\t\tpropSchema.if !== undefined ||\n\t\t\t(Array.isArray(propSchema.allOf) &&\n\t\t\t\tpropSchema.allOf.some(\n\t\t\t\t\t(e) => typeof e !== \"boolean\" && hasOwn(e as object, \"if\"),\n\t\t\t\t));\n\n\t\tif (!hasConditions) {\n\t\t\tresolvedProps[key] = propDef;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Nested data available → resolve recursively\n\t\tconst nestedData = isPlainObj(data[key])\n\t\t\t? (data[key] as Record<string, unknown>)\n\t\t\t: {};\n\n\t\tconst nested = resolveConditions(propSchema, nestedData, engine);\n\n\t\t// Propagate nested discriminants with prefix\n\t\tfor (const dk of Object.keys(nested.discriminant)) {\n\t\t\tdiscriminant[`${key}.${dk}`] = nested.discriminant[dk];\n\t\t}\n\n\t\tresolvedProps[key] = nested.resolved;\n\t\tchanged = true;\n\t}\n\n\treturn changed ? { ...resolved, properties: resolvedProps } : resolved;\n}\n"],"names":["resolveConditions","SPECIAL_MERGE_KEYS","Set","SUB_SCHEMA_KEYS","MIN_KEYS","MAX_KEYS","evaluateCondition","ifSchema","data","isDataValidForSchema","DISCRIMINANT_INDICATORS","extractDiscriminants","out","isPlainObj","properties","props","key","Object","keys","propDef","prop","hasIndicator","some","indicator","hasOwn","mergeBranchInto","resolved","branchDef","engine","branchSchema","Array","isArray","required","unionStrings","branchProps","mergedProps","branchProp","undefined","existing","merged","merge","mergedProp","existingObj","branchObj","unionedConstraints","mergeConstraints","constraints","dependencies","resolvedDeps","branchDeps","acc","depKey","branchVal","existingVal","has","resolvedVal","deepEqual","Math","max","min","base","branch","schema","discriminant","hasTopLevelIf","if","hasAllOfConditions","allOf","e","resolveNestedProperties","resolveAllOfConditions","matches","applicableBranch","then","else","remainingAllOf","entry","push","subSchema","remaining","omitKeys","length","propKeys","changed","resolvedProps","propSchema","hasConditions","nestedData","nested","dk"],"mappings":"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"}
|
|
@@ -8,14 +8,14 @@ import type { ConstraintValidatorRegistry, SchemaError } from "./types.js";
|
|
|
8
8
|
* `items` (single schema and tuple form), `additionalProperties` (schema form),
|
|
9
9
|
* `dependencies` (schema form).
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* When a schema declares a constraint that is not present in the registry,
|
|
12
|
+
* an "unknown constraint (not registered)" error is produced. This ensures
|
|
13
|
+
* that unregistered constraints are never silently ignored at runtime.
|
|
14
14
|
*
|
|
15
15
|
* @param schema - The resolved/narrowed schema containing constraints
|
|
16
16
|
* @param data - The runtime data to validate
|
|
17
|
-
* @param registry - The constraint validator registry (
|
|
17
|
+
* @param registry - The constraint validator registry (may be empty)
|
|
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): SchemaError[]
|
|
21
|
+
export declare function validateSchemaConstraints(schema: JSONSchema7Definition, data: unknown, registry: ConstraintValidatorRegistry, 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 _utilsts=require("./utils.js");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({key:path||"$root",expected:`constraint: ${name}`,received:"unknown constraint (not registered)"});continue}try{const result=validator(value,params);if(!result.valid){errors.push({key:path||"$root",expected:`constraint: ${name}`,received:result.message??"constraint validation failed"})}}catch(err){errors.push({key:path||"$root",expected:`constraint: ${name}`,received:err instanceof Error?err.message:"constraint validation error"})}}return errors}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(...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(...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(...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(...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(...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(...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(...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 _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({key:path||"$root",expected:`constraint: ${name}`,received:"unknown constraint (not registered)"});continue}try{const result=await validator(value,params);if(!result.valid){errors.push({key:path||"$root",expected:`constraint: ${name}`,received:result.message??"constraint validation failed"})}}catch(err){errors.push({key:path||"$root",expected:`constraint: ${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}
|
|
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 { 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 */\nfunction validateValue(\n\tconstraints: Constraint[],\n\tvalue: unknown,\n\tregistry: ConstraintValidatorRegistry,\n\tpath: string,\n): 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\tkey: path || \"$root\",\n\t\t\t\texpected: `constraint: ${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 = validator(value, params);\n\t\t\tif (!result.valid) {\n\t\t\t\terrors.push({\n\t\t\t\t\tkey: path || \"$root\",\n\t\t\t\t\texpected: `constraint: ${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\tkey: path || \"$root\",\n\t\t\t\texpected: `constraint: ${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 * @precondition `registry` must be non-empty. Callers should check\n * `Object.keys(registry).length > 0` before calling this function\n * to avoid unnecessary recursion into schemas that have no validators.\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 (must be non-empty)\n * @param path - The current property path (for error reporting)\n * @returns Array of schema errors (empty if all constraints pass)\n */\nexport function validateSchemaConstraints(\n\tschema: JSONSchema7Definition,\n\tdata: unknown,\n\tregistry: ConstraintValidatorRegistry,\n\tpath = \"\",\n): 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(...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...validateSchemaConstraints(propSchema, propValue, registry, propPath),\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...validateSchemaConstraints(itemSchema, data[i], registry, itemPath),\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...validateSchemaConstraints(itemSchema, data[i], registry, itemPath),\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...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...validateSchemaConstraints(apSchema, dataValue, registry, apPath),\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(...validateSchemaConstraints(depValue, data, registry, path));\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","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\tConstraintValidatorRegistry,\n\tSchemaError,\n} 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\tkey: path || \"$root\",\n\t\t\t\texpected: `constraint: ${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\tkey: path || \"$root\",\n\t\t\t\t\texpected: `constraint: ${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\tkey: path || \"$root\",\n\t\t\t\texpected: `constraint: ${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","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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"}
|
|
@@ -53,7 +53,7 @@ export declare class JsonSchemaCompatibilityChecker {
|
|
|
53
53
|
* checker.check(sub, sup, { data: { kind: "text", value: "hello" }, validate: true });
|
|
54
54
|
* ```
|
|
55
55
|
*/
|
|
56
|
-
check(sub: JSONSchema7Definition, sup: JSONSchema7Definition, options: CheckRuntimeOptions): ResolvedSubsetResult
|
|
56
|
+
check(sub: JSONSchema7Definition, sup: JSONSchema7Definition, options: CheckRuntimeOptions): Promise<ResolvedSubsetResult>;
|
|
57
57
|
check(sub: JSONSchema7Definition, sup: JSONSchema7Definition): SubsetResult;
|
|
58
58
|
/**
|
|
59
59
|
* Checks structural equality between two schemas.
|
|
@@ -85,6 +85,12 @@ export declare class JsonSchemaCompatibilityChecker {
|
|
|
85
85
|
* @returns The resolved schema with branch info and discriminants
|
|
86
86
|
*/
|
|
87
87
|
resolveConditions(schema: JSONSchema7, data: Record<string, unknown>): ResolvedConditionResult;
|
|
88
|
+
/**
|
|
89
|
+
* Internal runtime-aware check logic. Extracted as an async method
|
|
90
|
+
* so that `check()` without options stays synchronous while the
|
|
91
|
+
* runtime path can `await` async constraint validators.
|
|
92
|
+
*/
|
|
93
|
+
private checkWithOptions;
|
|
88
94
|
private prefixRuntimeErrors;
|
|
89
95
|
/**
|
|
90
96
|
* Internal check logic without condition resolution.
|
|
@@ -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 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 _utilsts=require("./utils.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){const data=options.data;const shouldValidate=options.validate===true;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(shouldValidate&&data!==undefined){const runtimeErrors=[];runtimeErrors.push(...this.prefixRuntimeErrors((0,_runtimevalidatorts.getRuntimeValidationErrors)(narrowedSubResolved,data),"$sub"));runtimeErrors.push(...this.prefixRuntimeErrors((0,_runtimevalidatorts.getRuntimeValidationErrors)(narrowedSupResolved,data),"$sup"));
|
|
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 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 _utilsts=require("./utils.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 shouldValidate=options.validate===true;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(shouldValidate&&data!==undefined){const runtimeErrors=[];runtimeErrors.push(...this.prefixRuntimeErrors((0,_runtimevalidatorts.getRuntimeValidationErrors)(narrowedSubResolved,data),"$sub"));runtimeErrors.push(...this.prefixRuntimeErrors((0,_runtimevalidatorts.getRuntimeValidationErrors)(narrowedSupResolved,data),"$sup"));runtimeErrors.push(...this.prefixRuntimeErrors(await (0,_constraintvalidatorts.validateSchemaConstraints)(narrowedSubResolved,data,this.constraintValidators),"$sub"));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??{}}}
|
|
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\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} from \"./types.ts\";\nimport { deepEqual, isPlainObj } from \"./utils.ts\";\n\n// ─── Re-exports ──────────────────────────────────────────────────────────────\n\nexport type {\n\tSchemaError,\n\tSubsetResult,\n\tResolvedConditionResult,\n\tResolvedSubsetResult,\n\tCheckRuntimeOptions,\n\tBranchType,\n\tBranchResult,\n};\n\nexport {\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: true` is set in options, two additional runtime steps\n\t * run **after** the static check passes:\n\t * 4. `data` is validated against both resolved schemas via AJV\n\t * 5. Custom constraints are validated against `data`\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): 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 | ResolvedSubsetResult {\n\t\t// ── Runtime-aware path ──\n\t\tif (options) {\n\t\t\tconst data = options.data;\n\t\t\tconst shouldValidate = options.validate === true;\n\n\t\t\t// resolveConditions expects Record<string, unknown> for property access;\n\t\t\t// coerce non-object / undefined data to empty object so conditions\n\t\t\t// are always resolved (v1.0.11 compat: subData: undefined → {})\n\t\t\tconst dataForConditions: Record<string, unknown> = isPlainObj(data)\n\t\t\t\t? data\n\t\t\t\t: {};\n\n\t\t\tconst resolvedSub = resolveConditions(\n\t\t\t\tsub as JSONSchema7,\n\t\t\t\tdataForConditions,\n\t\t\t\tthis.engine,\n\t\t\t);\n\t\t\tconst resolvedSup = resolveConditions(\n\t\t\t\tsup as JSONSchema7,\n\t\t\t\tdataForConditions,\n\t\t\t\tthis.engine,\n\t\t\t);\n\n\t\t\t// ── Runtime-aware data narrowing ──\n\t\t\t// Apply narrowing only when concrete data is available.\n\t\t\t// When data is undefined there is nothing to narrow with.\n\t\t\t// Boolean schemas (true/false) cannot be narrowed — skip narrowing\n\t\t\t// to avoid passing a non-object to narrowSchemaWithData.\n\t\t\tconst canNarrow = data !== undefined;\n\t\t\tconst canNarrowSub = canNarrow && isPlainObj(resolvedSub.resolved);\n\t\t\tconst canNarrowSup = canNarrow && isPlainObj(resolvedSup.resolved);\n\n\t\t\tconst narrowedSubResolved = canNarrowSub\n\t\t\t\t? narrowSchemaWithData(resolvedSub.resolved, data, resolvedSup.resolved)\n\t\t\t\t: resolvedSub.resolved;\n\n\t\t\tconst narrowedSupResolved = canNarrowSup\n\t\t\t\t? narrowSchemaWithData(resolvedSup.resolved, data, resolvedSub.resolved)\n\t\t\t\t: resolvedSup.resolved;\n\n\t\t\t// ── Static subset check ──\n\t\t\t// Structural incompatibilities are schema-level problems — they are\n\t\t\t// permanent regardless of the concrete data. Run this before runtime\n\t\t\t// validation so that static errors always surface with higher priority.\n\t\t\tconst staticResult = this.checkInternal(\n\t\t\t\tnarrowedSubResolved,\n\t\t\t\tnarrowedSupResolved,\n\t\t\t);\n\n\t\t\tif (!staticResult.isSubset) {\n\t\t\t\treturn {\n\t\t\t\t\t...staticResult,\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\n\t\t\t// ── Runtime validation (opt-in) ──\n\t\t\t// Only runs when `validate: true` is explicitly set.\n\t\t\t// Validates the concrete data against both resolved/narrowed schemas\n\t\t\t// via AJV, then runs custom constraint validators if registered.\n\t\t\tif (shouldValidate && data !== undefined) {\n\t\t\t\tconst runtimeErrors: SchemaError[] = [];\n\n\t\t\t\truntimeErrors.push(\n\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\tgetRuntimeValidationErrors(narrowedSubResolved, data),\n\t\t\t\t\t\t\"$sub\",\n\t\t\t\t\t),\n\t\t\t\t);\n\n\t\t\t\truntimeErrors.push(\n\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\tgetRuntimeValidationErrors(narrowedSupResolved, data),\n\t\t\t\t\t\t\"$sup\",\n\t\t\t\t\t),\n\t\t\t\t);\n\n\t\t\t\t// ── Constraint validation ──\n\t\t\t\t// Validate runtime data against custom constraints in both schemas.\n\t\t\t\t// Only runs if constraint validators were registered in the constructor.\n\t\t\t\tif (Object.keys(this.constraintValidators).length > 0) {\n\t\t\t\t\truntimeErrors.push(\n\t\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\t\tvalidateSchemaConstraints(\n\t\t\t\t\t\t\t\tnarrowedSubResolved,\n\t\t\t\t\t\t\t\tdata,\n\t\t\t\t\t\t\t\tthis.constraintValidators,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\"$sub\",\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\n\t\t\t\t\truntimeErrors.push(\n\t\t\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\t\t\tvalidateSchemaConstraints(\n\t\t\t\t\t\t\t\tnarrowedSupResolved,\n\t\t\t\t\t\t\t\tdata,\n\t\t\t\t\t\t\t\tthis.constraintValidators,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\"$sup\",\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tif (runtimeErrors.length > 0) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tisSubset: false,\n\t\t\t\t\t\tmerged: null,\n\t\t\t\t\t\terrors: runtimeErrors,\n\t\t\t\t\t\tresolvedSub: { ...resolvedSub, resolved: narrowedSubResolved },\n\t\t\t\t\t\tresolvedSup: { ...resolvedSup, resolved: narrowedSupResolved },\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\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// ── 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\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","arePatternsEquivalent","formatResult","isPatternSubset","isTrivialPattern","normalize","resolveConditions","isSubset","sub","sup","deepEqual","nSub","nSup","branches","subBranches","getBranchesTyped","length","every","branch","isAtomicSubsetOf","engine","check","options","data","shouldValidate","validate","dataForConditions","isPlainObj","resolvedSub","resolvedSup","canNarrow","undefined","canNarrowSub","resolved","canNarrowSup","narrowedSubResolved","narrowSchemaWithData","narrowedSupResolved","staticResult","checkInternal","runtimeErrors","push","prefixRuntimeErrors","getRuntimeValidationErrors","Object","keys","constraintValidators","validateSchemaConstraints","merged","errors","isEqual","a","b","intersect","nA","nB","merge","def","label","result","schema","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\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} from \"./types.ts\";\nimport { deepEqual, isPlainObj } from \"./utils.ts\";\n\n// ─── Re-exports ──────────────────────────────────────────────────────────────\n\nexport type {\n\tSchemaError,\n\tSubsetResult,\n\tResolvedConditionResult,\n\tResolvedSubsetResult,\n\tCheckRuntimeOptions,\n\tBranchType,\n\tBranchResult,\n};\n\nexport {\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: true` is set in options, two additional runtime steps\n\t * run **after** the static check passes:\n\t * 4. `data` is validated against both resolved schemas via AJV\n\t * 5. Custom constraints are validated against `data`\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 shouldValidate = options.validate === true;\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// Only runs when `validate: true` is explicitly set.\n\t\t// Validates the concrete data against both resolved/narrowed schemas\n\t\t// via AJV, then runs custom constraint validators if registered.\n\t\tif (shouldValidate && data !== undefined) {\n\t\t\tconst runtimeErrors: SchemaError[] = [];\n\n\t\t\truntimeErrors.push(\n\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\tgetRuntimeValidationErrors(narrowedSubResolved, data),\n\t\t\t\t\t\"$sub\",\n\t\t\t\t),\n\t\t\t);\n\n\t\t\truntimeErrors.push(\n\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\tgetRuntimeValidationErrors(narrowedSupResolved, data),\n\t\t\t\t\t\"$sup\",\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 both schemas.\n\t\t\t// Always runs when validate: true — if a schema declares constraints\n\t\t\t// that are not registered in the registry, validateSchemaConstraints\n\t\t\t// will report them as \"unknown constraint (not registered)\" errors.\n\t\t\t// Constraint validators may be async, so we await the results.\n\t\t\truntimeErrors.push(\n\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\tawait validateSchemaConstraints(\n\t\t\t\t\t\tnarrowedSubResolved,\n\t\t\t\t\t\tdata,\n\t\t\t\t\t\tthis.constraintValidators,\n\t\t\t\t\t),\n\t\t\t\t\t\"$sub\",\n\t\t\t\t),\n\t\t\t);\n\n\t\t\truntimeErrors.push(\n\t\t\t\t...this.prefixRuntimeErrors(\n\t\t\t\t\tawait validateSchemaConstraints(\n\t\t\t\t\t\tnarrowedSupResolved,\n\t\t\t\t\t\tdata,\n\t\t\t\t\t\tthis.constraintValidators,\n\t\t\t\t\t),\n\t\t\t\t\t\"$sup\",\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","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","shouldValidate","validate","dataForConditions","isPlainObj","resolvedSub","resolvedSup","canNarrow","undefined","canNarrowSub","resolved","canNarrowSup","narrowedSubResolved","narrowSchemaWithData","narrowedSupResolved","staticResult","runtimeErrors","push","prefixRuntimeErrors","getRuntimeValidationErrors","validateSchemaConstraints","constraintValidators","errors","rootKey","map","error","key","type","subBranchType","supBranches","supBranchType","checkBranchedSub","checkBranchedSup","checkAtomic","clearCache","clearAllValidatorCaches","constraints"],"mappings":"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"}
|
package/dist/cjs/merge-engine.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"MergeEngine",{enumerable:true,get:function(){return MergeEngine}});const _jsonschemamerge=require("@x0k/json-schema-merge");const _array=require("@x0k/json-schema-merge/lib/array");const _formatvalidatorts=require("./format-validator.js");const _utilsts=require("./utils.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}function hasConstConflict(a,b){if(typeof a==="boolean"||typeof b==="boolean")return false;const aHasConst=(0,_utilsts.hasOwn)(a,"const");const bHasConst=(0,_utilsts.hasOwn)(b,"const");const aConst=a.const;const bConst=b.const;const aEnum=a.enum;const bEnum=b.enum;if(aHasConst&&bHasConst){return!(0,_utilsts.deepEqual)(aConst,bConst)}if(aHasConst&&Array.isArray(bEnum)){return!bEnum.some(v=>(0,_utilsts.deepEqual)(v,aConst))}if(bHasConst&&Array.isArray(aEnum)){return!aEnum.some(v=>(0,_utilsts.deepEqual)(v,bConst))}return false}const SINGLE_SCHEMA_CONFLICT_KEYS=["items","additionalProperties","contains","propertyNames","not"];const PROPERTIES_MAP_CONFLICT_KEYS=["properties","patternProperties"];function hasDeepConstConflict(a,b){if(hasConstConflict(a,b))return true;if(typeof a==="boolean"||typeof b==="boolean")return false;for(const key of SINGLE_SCHEMA_CONFLICT_KEYS){const aVal=a[key];const bVal=b[key];if((0,_utilsts.isPlainObj)(aVal)&&(0,_utilsts.isPlainObj)(bVal)&&hasDeepConstConflict(aVal,bVal)){return true}}for(const key of PROPERTIES_MAP_CONFLICT_KEYS){const aMap=a[key];const bMap=b[key];if(!(0,_utilsts.isPlainObj)(aMap)||!(0,_utilsts.isPlainObj)(bMap))continue;const aMapSafe=aMap;const bMapSafe=bMap;for(const propKey of Object.keys(aMapSafe)){const aVal=aMapSafe[propKey];const bVal=bMapSafe[propKey];if(aVal!==undefined&&bVal!==undefined&&(0,_utilsts.hasOwn)(bMapSafe,propKey)&&hasDeepConstConflict(aVal,bVal)){return true}}}if(Array.isArray(a.items)&&Array.isArray(b.items)){const aItems=a.items;const bItems=b.items;const len=Math.min(aItems.length,bItems.length);for(let i=0;i<len;i++){const aItem=aItems[i];const bItem=bItems[i];if(aItem===undefined||bItem===undefined)continue;if(hasDeepConstConflict(aItem,bItem)){return true}}}return false}function hasAdditionalPropertiesConflict(a,b){if(typeof a==="boolean"||typeof b==="boolean")return false;const aProps=(0,_utilsts.isPlainObj)(a.properties)?a.properties:undefined;const bProps=(0,_utilsts.isPlainObj)(b.properties)?b.properties:undefined;if(!aProps&&!bProps)return false;const aKeys=aProps?Object.keys(aProps):[];const bKeys=bProps?Object.keys(bProps):[];const aRequired=Array.isArray(a.required)?a.required:[];const bRequired=Array.isArray(b.required)?b.required:[];if(a.additionalProperties===false&&aProps&&bProps){const hasRequiredExtra=bRequired.some(k=>!(0,_utilsts.hasOwn)(aProps,k)&&(0,_utilsts.hasOwn)(bProps,k));if(hasRequiredExtra&&aKeys.length>0)return true}if((0,_utilsts.isPlainObj)(a.additionalProperties)&&typeof a.additionalProperties!=="boolean"&&aProps&&bProps){const addPropsSchema=a.additionalProperties;if((0,_utilsts.hasOwn)(addPropsSchema,"type")){const addPropsType=addPropsSchema.type;const hasTypeConflict=bRequired.some(k=>{if((0,_utilsts.hasOwn)(aProps,k))return false;if(!(0,_utilsts.hasOwn)(bProps,k))return false;const bPropDef=bProps[k];if(typeof bPropDef==="boolean")return false;const bProp=bPropDef;if(!(0,_utilsts.hasOwn)(bProp,"type"))return false;if(typeof addPropsType==="string"&&typeof bProp.type==="string"){return addPropsType!==bProp.type&&!(addPropsType==="number"&&bProp.type==="integer")&&!(addPropsType==="integer"&&bProp.type==="number")}return false});if(hasTypeConflict)return true}}if(b.additionalProperties===false&&bProps&&aProps){const hasRequiredExtra=aRequired.some(k=>!(0,_utilsts.hasOwn)(bProps,k)&&(0,_utilsts.hasOwn)(aProps,k));if(hasRequiredExtra&&bKeys.length>0)return true}if((0,_utilsts.isPlainObj)(b.additionalProperties)&&typeof b.additionalProperties!=="boolean"&&bProps&&aProps){const addPropsSchema=b.additionalProperties;if((0,_utilsts.hasOwn)(addPropsSchema,"type")){const addPropsType=addPropsSchema.type;const hasTypeConflict=aRequired.some(k=>{if((0,_utilsts.hasOwn)(bProps,k))return false;if(!(0,_utilsts.hasOwn)(aProps,k))return false;const aPropDef=aProps[k];if(typeof aPropDef==="boolean")return false;const aProp=aPropDef;if(!(0,_utilsts.hasOwn)(aProp,"type"))return false;if(typeof addPropsType==="string"&&typeof aProp.type==="string"){return addPropsType!==aProp.type&&!(addPropsType==="number"&&aProp.type==="integer")&&!(addPropsType==="integer"&&aProp.type==="number")}return false});if(hasTypeConflict)return true}}if(aProps&&bProps){for(const k of aKeys){if(!(0,_utilsts.hasOwn)(bProps,k))continue;const aPropDef=aProps[k];const bPropDef=bProps[k];if(typeof aPropDef==="boolean"||typeof bPropDef==="boolean")continue;if(hasAdditionalPropertiesConflict(aPropDef,bPropDef)){return true}}}return false}function hasFormatConflict(a,b){if(typeof a==="boolean"||typeof b==="boolean")return false;if((0,_utilsts.hasOwn)(a,"format")&&(0,_utilsts.hasOwn)(b,"format")){const aFormat=a.format;const bFormat=b.format;if(aFormat!==bFormat){const subsetCheck=(0,_formatvalidatorts.isFormatSubset)(aFormat,bFormat);if(subsetCheck!==true){const reverseCheck=(0,_formatvalidatorts.isFormatSubset)(bFormat,aFormat);if(reverseCheck!==true){return true}}}}if((0,_utilsts.isPlainObj)(a.properties)&&(0,_utilsts.isPlainObj)(b.properties)){const aMap=a.properties;const bMap=b.properties;for(const k of Object.keys(aMap)){const aVal=aMap[k];const bVal=bMap[k];if(aVal!==undefined&&bVal!==undefined&&(0,_utilsts.hasOwn)(bMap,k)&&hasFormatConflict(aVal,bVal)){return true}}}if((0,_utilsts.isPlainObj)(a.items)&&(0,_utilsts.isPlainObj)(b.items)){if(hasFormatConflict(a.items,b.items))return true}if((0,_utilsts.isPlainObj)(a.additionalProperties)&&(0,_utilsts.isPlainObj)(b.additionalProperties)){if(hasFormatConflict(a.additionalProperties,b.additionalProperties))return true}return false}function hasConstraintsAnywhere(schema){if(typeof schema==="boolean")return false;if((0,_utilsts.hasOwn)(schema,"constraints")&&schema.constraints!==undefined){return true}if((0,_utilsts.isPlainObj)(schema.properties)){const props=schema.properties;for(const key of Object.keys(props)){const prop=props[key];if(prop!==undefined&&hasConstraintsAnywhere(prop))return true}}if((0,_utilsts.isPlainObj)(schema.patternProperties)){const pp=schema.patternProperties;for(const key of Object.keys(pp)){const val=pp[key];if(val!==undefined&&hasConstraintsAnywhere(val))return true}}if(Array.isArray(schema.items)){for(const item of schema.items){if(item!==undefined&&hasConstraintsAnywhere(item))return true}}else if((0,_utilsts.isPlainObj)(schema.items)){if(hasConstraintsAnywhere(schema.items))return true}if((0,_utilsts.isPlainObj)(schema.additionalProperties)){if(hasConstraintsAnywhere(schema.additionalProperties))return true}if((0,_utilsts.isPlainObj)(schema.dependencies)){const deps=schema.dependencies;for(const key of Object.keys(deps)){const val=deps[key];if(val!==undefined&&!Array.isArray(val)&&hasConstraintsAnywhere(val))return true}}return false}function applyConstraintsMerge(merged,a,b){if(typeof merged==="boolean")return merged;if(typeof a==="boolean"||typeof b==="boolean")return merged;if(!hasConstraintsAnywhere(a)&&!hasConstraintsAnywhere(b)){return merged}let changed=false;const result={...merged};const mergedConstraints=(0,_utilsts.mergeConstraints)(a.constraints,b.constraints);const currentConstraints=result.constraints;if(mergedConstraints!==undefined){if(!(0,_utilsts.deepEqual)(currentConstraints,mergedConstraints)){result.constraints=mergedConstraints;changed=true}}else if(currentConstraints!==undefined){delete result.constraints;changed=true}if((0,_utilsts.isPlainObj)(result.properties)&&((0,_utilsts.isPlainObj)(a.properties)||(0,_utilsts.isPlainObj)(b.properties))){const mergedProps=result.properties;const aProps=a.properties??{};const bProps=b.properties??{};let propsChanged=false;const newProps={};for(const key of Object.keys(mergedProps)){const mProp=mergedProps[key];const aProp=aProps[key];const bProp=bProps[key];if(mProp===undefined)continue;if(aProp!==undefined&&bProp!==undefined&&typeof mProp!=="boolean"&&typeof aProp!=="boolean"&&typeof bProp!=="boolean"){const patched=applyConstraintsMerge(mProp,aProp,bProp);newProps[key]=patched;if(patched!==mProp)propsChanged=true}else{newProps[key]=mProp}}if(propsChanged){result.properties=newProps;changed=true}}if((0,_utilsts.isPlainObj)(result.items)&&(0,_utilsts.isPlainObj)(a.items)&&(0,_utilsts.isPlainObj)(b.items)){const patched=applyConstraintsMerge(result.items,a.items,b.items);if(patched!==result.items){result.items=patched;changed=true}}if((0,_utilsts.isPlainObj)(result.patternProperties)&&((0,_utilsts.isPlainObj)(a.patternProperties)||(0,_utilsts.isPlainObj)(b.patternProperties))){const mergedPP=result.patternProperties;const aPP=a.patternProperties??{};const bPP=b.patternProperties??{};let ppChanged=false;const newPP={};for(const key of Object.keys(mergedPP)){const mVal=mergedPP[key];const aVal=aPP[key];const bVal=bPP[key];if(mVal===undefined)continue;if(aVal!==undefined&&bVal!==undefined&&typeof mVal!=="boolean"&&typeof aVal!=="boolean"&&typeof bVal!=="boolean"){const patched=applyConstraintsMerge(mVal,aVal,bVal);newPP[key]=patched;if(patched!==mVal)ppChanged=true}else{newPP[key]=mVal}}if(ppChanged){result.patternProperties=newPP;changed=true}}if(Array.isArray(result.items)&&Array.isArray(a.items)&&Array.isArray(b.items)){const mergedItems=result.items;const aItems=a.items;const bItems=b.items;const len=mergedItems.length;let tupleChanged=false;const newItems=new Array(len);for(let i=0;i<len;i++){const mItem=mergedItems[i];const aItem=aItems[i];const bItem=bItems[i];if(mItem!==undefined&&aItem!==undefined&&bItem!==undefined&&typeof mItem!=="boolean"&&typeof aItem!=="boolean"&&typeof bItem!=="boolean"){const patched=applyConstraintsMerge(mItem,aItem,bItem);newItems[i]=patched;if(patched!==mItem)tupleChanged=true}else{newItems[i]=mItem}}if(tupleChanged){result.items=newItems;changed=true}}if((0,_utilsts.isPlainObj)(result.additionalProperties)&&(0,_utilsts.isPlainObj)(a.additionalProperties)&&(0,_utilsts.isPlainObj)(b.additionalProperties)){const patched=applyConstraintsMerge(result.additionalProperties,a.additionalProperties,b.additionalProperties);if(patched!==result.additionalProperties){result.additionalProperties=patched;changed=true}}if((0,_utilsts.isPlainObj)(result.dependencies)&&((0,_utilsts.isPlainObj)(a.dependencies)||(0,_utilsts.isPlainObj)(b.dependencies))){const mergedDeps=result.dependencies;const aDeps=a.dependencies??{};const bDeps=b.dependencies??{};let depsChanged=false;const newDeps={};for(const key of Object.keys(mergedDeps)){const mVal=mergedDeps[key];const aVal=aDeps[key];const bVal=bDeps[key];if(mVal===undefined)continue;if(aVal!==undefined&&bVal!==undefined&&!Array.isArray(mVal)&&!Array.isArray(aVal)&&!Array.isArray(bVal)&&typeof mVal!=="boolean"&&typeof aVal!=="boolean"&&typeof bVal!=="boolean"){const patched=applyConstraintsMerge(mVal,aVal,bVal);newDeps[key]=patched;if(patched!==mVal)depsChanged=true}else{newDeps[key]=mVal}}if(depsChanged){result.dependencies=newDeps;changed=true}}return changed?result:merged}class MergeEngine{merge(a,b){if(a===b)return a;if(a===false||b===false)return false;if(a===true)return b;if(b===true)return a;if(hasDeepConstConflict(a,b)){return null}if(hasFormatConflict(a,b)){return null}if(hasAdditionalPropertiesConflict(a,b)){return null}try{const result=this.shallowAllOfMergeFn({allOf:[a,b]});return applyConstraintsMerge(result,a,b)}catch{return null}}mergeOrThrow(a,b){if(a===b)return a;if(a===false||b===false)return false;if(a===true)return b;if(b===true)return a;if(hasDeepConstConflict(a,b)){throw new Error("Incompatible const values: schemas have conflicting const constraints")}if(hasFormatConflict(a,b)){throw new Error("Incompatible format values: schemas have conflicting format constraints")}if(hasAdditionalPropertiesConflict(a,b)){throw new Error("Incompatible additionalProperties: required properties conflict with additionalProperties constraint")}const result=this.shallowAllOfMergeFn({allOf:[a,b]});return applyConstraintsMerge(result,a,b)}compare(a,b){return this.compareFn(a,b)}isEqual(a,b){return this.compareFn(a,b)===0}overlay(base,override){if(override===false)return false;if(override===true)return true;if(typeof base==="boolean")return override;const baseObj=base;const overrideObj=override;if(!isObjectLike(baseObj)&&!isObjectLike(overrideObj)){return override}if(!isObjectLike(baseObj)){return override}if(!isObjectLike(overrideObj)){return override}const baseProps=baseObj.properties??{};const overrideProps=overrideObj.properties??{};const mergedProps={};for(const key of Object.keys(baseProps)){const baseProp=baseProps[key];if(baseProp===undefined)continue;mergedProps[key]=baseProp}for(const key of Object.keys(overrideProps)){const overrideProp=overrideProps[key];if(overrideProp===undefined)continue;const baseProp=baseProps[key];if(baseProp!==undefined&&typeof baseProp!=="boolean"&&typeof overrideProp!=="boolean"&&isObjectLike(baseProp)&&isObjectLike(overrideProp)){mergedProps[key]=this.overlay(baseProp,overrideProp)}else{mergedProps[key]=overrideProp}}const baseRequired=Array.isArray(baseObj.required)?baseObj.required:[];const overrideRequired=Array.isArray(overrideObj.required)?overrideObj.required:[];const mergedRequired=(0,_utilsts.unionStrings)(baseRequired,overrideRequired);const result={...baseObj};result.properties=mergedProps;if(mergedRequired.length>0){result.required=mergedRequired}else{delete result.required}if((0,_utilsts.hasOwn)(overrideObj,"additionalProperties")){result.additionalProperties=overrideObj.additionalProperties}if((0,_utilsts.hasOwn)(overrideObj,"minProperties")){result.minProperties=overrideObj.minProperties}if((0,_utilsts.hasOwn)(overrideObj,"maxProperties")){result.maxProperties=overrideObj.maxProperties}if((0,_utilsts.hasOwn)(overrideObj,"propertyNames")){result.propertyNames=overrideObj.propertyNames}if((0,_utilsts.hasOwn)(overrideObj,"patternProperties")){result.patternProperties=overrideObj.patternProperties}if((0,_utilsts.hasOwn)(overrideObj,"dependencies")){result.dependencies=overrideObj.dependencies}if((0,_utilsts.hasOwn)(overrideObj,"type")){result.type=overrideObj.type}return result}constructor(){_define_property(this,"compareFn",void 0);_define_property(this,"shallowAllOfMergeFn",void 0);const{compareSchemaDefinitions,compareSchemaValues}=(0,_jsonschemamerge.createComparator)();const safeCompareSchemaValues=(a,b)=>{if(a===null&&b===null)return 0;return compareSchemaValues(a,b)};const{mergeArrayOfSchemaDefinitions}=(0,_jsonschemamerge.createMerger)({intersectJson:(0,_array.createIntersector)(safeCompareSchemaValues),deduplicateJsonSchemaDef:(0,_array.createDeduplicator)(compareSchemaDefinitions)});this.compareFn=compareSchemaDefinitions;this.shallowAllOfMergeFn=(0,_jsonschemamerge.createShallowAllOfMerge)(mergeArrayOfSchemaDefinitions)}}const OBJECT_SHAPE_KEYWORDS=["properties","patternProperties","additionalProperties","required","dependencies","propertyNames","minProperties","maxProperties"];function isObjectLike(schema){if(schema.type==="object")return true;for(const kw of OBJECT_SHAPE_KEYWORDS){if((0,_utilsts.hasOwn)(schema,kw))return true}return false}
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"MergeEngine",{enumerable:true,get:function(){return MergeEngine}});const _jsonschemamerge=require("@x0k/json-schema-merge");const _array=require("@x0k/json-schema-merge/lib/array");const _formatvalidatorts=require("./format-validator.js");const _utilsts=require("./utils.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}function hasConstConflict(a,b){if(typeof a==="boolean"||typeof b==="boolean")return false;const aHasConst=(0,_utilsts.hasOwn)(a,"const");const bHasConst=(0,_utilsts.hasOwn)(b,"const");const aConst=a.const;const bConst=b.const;const aEnum=a.enum;const bEnum=b.enum;if(aHasConst&&bHasConst){return!(0,_utilsts.deepEqual)(aConst,bConst)}if(aHasConst&&Array.isArray(bEnum)){return!bEnum.some(v=>(0,_utilsts.deepEqual)(v,aConst))}if(bHasConst&&Array.isArray(aEnum)){return!aEnum.some(v=>(0,_utilsts.deepEqual)(v,bConst))}return false}const SINGLE_SCHEMA_CONFLICT_KEYS=["items","additionalProperties","contains","propertyNames","not"];const PROPERTIES_MAP_CONFLICT_KEYS=["properties","patternProperties"];function hasDeepConstConflict(a,b){if(hasConstConflict(a,b))return true;if(typeof a==="boolean"||typeof b==="boolean")return false;for(const key of SINGLE_SCHEMA_CONFLICT_KEYS){const aVal=a[key];const bVal=b[key];if((0,_utilsts.isPlainObj)(aVal)&&(0,_utilsts.isPlainObj)(bVal)&&hasDeepConstConflict(aVal,bVal)){return true}}for(const key of PROPERTIES_MAP_CONFLICT_KEYS){const aMap=a[key];const bMap=b[key];if(!(0,_utilsts.isPlainObj)(aMap)||!(0,_utilsts.isPlainObj)(bMap))continue;const aMapSafe=aMap;const bMapSafe=bMap;for(const propKey of Object.keys(aMapSafe)){const aVal=aMapSafe[propKey];const bVal=bMapSafe[propKey];if(aVal!==undefined&&bVal!==undefined&&(0,_utilsts.hasOwn)(bMapSafe,propKey)&&hasDeepConstConflict(aVal,bVal)){return true}}}if(Array.isArray(a.items)&&Array.isArray(b.items)){const aItems=a.items;const bItems=b.items;const len=Math.min(aItems.length,bItems.length);for(let i=0;i<len;i++){const aItem=aItems[i];const bItem=bItems[i];if(aItem===undefined||bItem===undefined)continue;if(hasDeepConstConflict(aItem,bItem)){return true}}}return false}function hasAdditionalPropertiesConflict(a,b){if(typeof a==="boolean"||typeof b==="boolean")return false;const aProps=(0,_utilsts.isPlainObj)(a.properties)?a.properties:undefined;const bProps=(0,_utilsts.isPlainObj)(b.properties)?b.properties:undefined;if(!aProps&&!bProps)return false;const aKeys=aProps?Object.keys(aProps):[];const bKeys=bProps?Object.keys(bProps):[];const aRequired=Array.isArray(a.required)?a.required:[];const bRequired=Array.isArray(b.required)?b.required:[];if(a.additionalProperties===false&&aProps&&bProps){const hasRequiredExtra=bRequired.some(k=>!(0,_utilsts.hasOwn)(aProps,k)&&(0,_utilsts.hasOwn)(bProps,k));if(hasRequiredExtra&&aKeys.length>0)return true}if((0,_utilsts.isPlainObj)(a.additionalProperties)&&typeof a.additionalProperties!=="boolean"&&aProps&&bProps){const addPropsSchema=a.additionalProperties;if((0,_utilsts.hasOwn)(addPropsSchema,"type")){const addPropsType=addPropsSchema.type;const hasTypeConflict=bRequired.some(k=>{if((0,_utilsts.hasOwn)(aProps,k))return false;if(!(0,_utilsts.hasOwn)(bProps,k))return false;const bPropDef=bProps[k];if(typeof bPropDef==="boolean")return false;const bProp=bPropDef;if(!(0,_utilsts.hasOwn)(bProp,"type"))return false;if(typeof addPropsType==="string"&&typeof bProp.type==="string"){return addPropsType!==bProp.type&&!(addPropsType==="number"&&bProp.type==="integer")&&!(addPropsType==="integer"&&bProp.type==="number")}return false});if(hasTypeConflict)return true}}if(b.additionalProperties===false&&bProps&&aProps){const hasRequiredExtra=aRequired.some(k=>!(0,_utilsts.hasOwn)(bProps,k)&&(0,_utilsts.hasOwn)(aProps,k));if(hasRequiredExtra&&bKeys.length>0)return true}if((0,_utilsts.isPlainObj)(b.additionalProperties)&&typeof b.additionalProperties!=="boolean"&&bProps&&aProps){const addPropsSchema=b.additionalProperties;if((0,_utilsts.hasOwn)(addPropsSchema,"type")){const addPropsType=addPropsSchema.type;const hasTypeConflict=aRequired.some(k=>{if((0,_utilsts.hasOwn)(bProps,k))return false;if(!(0,_utilsts.hasOwn)(aProps,k))return false;const aPropDef=aProps[k];if(typeof aPropDef==="boolean")return false;const aProp=aPropDef;if(!(0,_utilsts.hasOwn)(aProp,"type"))return false;if(typeof addPropsType==="string"&&typeof aProp.type==="string"){return addPropsType!==aProp.type&&!(addPropsType==="number"&&aProp.type==="integer")&&!(addPropsType==="integer"&&aProp.type==="number")}return false});if(hasTypeConflict)return true}}if(aProps&&bProps){for(const k of aKeys){if(!(0,_utilsts.hasOwn)(bProps,k))continue;const aPropDef=aProps[k];const bPropDef=bProps[k];if(typeof aPropDef==="boolean"||typeof bPropDef==="boolean")continue;if(hasAdditionalPropertiesConflict(aPropDef,bPropDef)){return true}}}return false}function hasFormatConflict(a,b){if(typeof a==="boolean"||typeof b==="boolean")return false;if((0,_utilsts.hasOwn)(a,"format")&&(0,_utilsts.hasOwn)(b,"format")){const aFormat=a.format;const bFormat=b.format;if(aFormat!==bFormat){const subsetCheck=(0,_formatvalidatorts.isFormatSubset)(aFormat,bFormat);if(subsetCheck!==true){const reverseCheck=(0,_formatvalidatorts.isFormatSubset)(bFormat,aFormat);if(reverseCheck!==true){return true}}}}if((0,_utilsts.isPlainObj)(a.properties)&&(0,_utilsts.isPlainObj)(b.properties)){const aMap=a.properties;const bMap=b.properties;for(const k of Object.keys(aMap)){const aVal=aMap[k];const bVal=bMap[k];if(aVal!==undefined&&bVal!==undefined&&(0,_utilsts.hasOwn)(bMap,k)&&hasFormatConflict(aVal,bVal)){return true}}}if((0,_utilsts.isPlainObj)(a.items)&&(0,_utilsts.isPlainObj)(b.items)){if(hasFormatConflict(a.items,b.items))return true}if((0,_utilsts.isPlainObj)(a.additionalProperties)&&(0,_utilsts.isPlainObj)(b.additionalProperties)){if(hasFormatConflict(a.additionalProperties,b.additionalProperties))return true}return false}class MergeEngine{merge(a,b){if(a===b)return a;if(a===false||b===false)return false;if(a===true)return b;if(b===true)return a;if(hasDeepConstConflict(a,b)){return null}if(hasFormatConflict(a,b)){return null}if(hasAdditionalPropertiesConflict(a,b)){return null}try{return this.shallowAllOfMergeFn({allOf:[a,b]})}catch{return null}}mergeOrThrow(a,b){if(a===b)return a;if(a===false||b===false)return false;if(a===true)return b;if(b===true)return a;if(hasDeepConstConflict(a,b)){throw new Error("Incompatible const values: schemas have conflicting const constraints")}if(hasFormatConflict(a,b)){throw new Error("Incompatible format values: schemas have conflicting format constraints")}if(hasAdditionalPropertiesConflict(a,b)){throw new Error("Incompatible additionalProperties: required properties conflict with additionalProperties constraint")}return this.shallowAllOfMergeFn({allOf:[a,b]})}compare(a,b){return this.compareFn(a,b)}isEqual(a,b){return this.compareFn(a,b)===0}overlay(base,override){if(override===false)return false;if(override===true)return true;if(typeof base==="boolean")return override;const baseObj=base;const overrideObj=override;if(!isObjectLike(baseObj)&&!isObjectLike(overrideObj)){return override}if(!isObjectLike(baseObj)){return override}if(!isObjectLike(overrideObj)){return override}const baseProps=baseObj.properties??{};const overrideProps=overrideObj.properties??{};const mergedProps={};for(const key of Object.keys(baseProps)){const baseProp=baseProps[key];if(baseProp===undefined)continue;mergedProps[key]=baseProp}for(const key of Object.keys(overrideProps)){const overrideProp=overrideProps[key];if(overrideProp===undefined)continue;const baseProp=baseProps[key];if(baseProp!==undefined&&typeof baseProp!=="boolean"&&typeof overrideProp!=="boolean"&&isObjectLike(baseProp)&&isObjectLike(overrideProp)){mergedProps[key]=this.overlay(baseProp,overrideProp)}else{mergedProps[key]=overrideProp}}const baseRequired=Array.isArray(baseObj.required)?baseObj.required:[];const overrideRequired=Array.isArray(overrideObj.required)?overrideObj.required:[];const mergedRequired=(0,_utilsts.unionStrings)(baseRequired,overrideRequired);const result={...baseObj};result.properties=mergedProps;if(mergedRequired.length>0){result.required=mergedRequired}else{delete result.required}if((0,_utilsts.hasOwn)(overrideObj,"additionalProperties")){result.additionalProperties=overrideObj.additionalProperties}if((0,_utilsts.hasOwn)(overrideObj,"minProperties")){result.minProperties=overrideObj.minProperties}if((0,_utilsts.hasOwn)(overrideObj,"maxProperties")){result.maxProperties=overrideObj.maxProperties}if((0,_utilsts.hasOwn)(overrideObj,"propertyNames")){result.propertyNames=overrideObj.propertyNames}if((0,_utilsts.hasOwn)(overrideObj,"patternProperties")){result.patternProperties=overrideObj.patternProperties}if((0,_utilsts.hasOwn)(overrideObj,"dependencies")){result.dependencies=overrideObj.dependencies}if((0,_utilsts.hasOwn)(overrideObj,"type")){result.type=overrideObj.type}return result}constructor(){_define_property(this,"compareFn",void 0);_define_property(this,"shallowAllOfMergeFn",void 0);const{compareSchemaDefinitions,compareSchemaValues}=(0,_jsonschemamerge.createComparator)();const safeCompareSchemaValues=(a,b)=>{if(a===null&&b===null)return 0;return compareSchemaValues(a,b)};const{mergeArrayOfSchemaDefinitions}=(0,_jsonschemamerge.createMerger)({intersectJson:(0,_array.createIntersector)(safeCompareSchemaValues),deduplicateJsonSchemaDef:(0,_array.createDeduplicator)(compareSchemaDefinitions)});this.compareFn=compareSchemaDefinitions;this.shallowAllOfMergeFn=(0,_jsonschemamerge.createShallowAllOfMerge)(mergeArrayOfSchemaDefinitions)}}const OBJECT_SHAPE_KEYWORDS=["properties","patternProperties","additionalProperties","required","dependencies","propertyNames","minProperties","maxProperties"];function isObjectLike(schema){if(schema.type==="object")return true;for(const kw of OBJECT_SHAPE_KEYWORDS){if((0,_utilsts.hasOwn)(schema,kw))return true}return false}
|
|
2
2
|
//# sourceMappingURL=merge-engine.js.map
|