json-schema-compatibility-checker 1.1.13 → 1.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/subset-checker.ts"],"sourcesContent":["import type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\nimport { isFormatSubset } from \"./format-validator.ts\";\nimport type { MergeEngine } from \"./merge-engine.ts\";\nimport { normalize } from \"./normalizer.ts\";\nimport { isPatternSubset } from \"./pattern-subset.ts\";\nimport { computeSemanticErrors } from \"./semantic-errors.ts\";\nimport type { SchemaError, SubsetResult } from \"./types.ts\";\nimport { deepEqual, hasOwn, isPlainObj, omitKeys } from \"./utils.ts\";\n\n// ─── Subset Checker ──────────────────────────────────────────────────────────\n//\n// Subset verification logic via the approach:\n// A ⊆ B ⟺ A ∩ B ≡ A\n//\n// Handles:\n// - Atomic schemas (no anyOf/oneOf)\n// - anyOf/oneOf in sub → each branch must be accepted by sup\n// - anyOf/oneOf in sup → at least one branch must accept sub\n// - Point 6: Distinguish anyOf / oneOf in diff messages\n// - Point 7: Extended `not` reasoning (evaluateNot)\n// - not.type, not.const, not.enum (existing)\n// - not with properties+required (1.1)\n// - not with anyOf/oneOf (1.2)\n// - not in sub (1.3)\n// - not.format (format-vs-format)\n//\n// Uses shared native helpers from `./utils` for optimal performance\n// (deepEqual, hasOwn, isPlainObj, omitKeys).\n\n// ─── Branch type ─────────────────────────────────────────────────────────────\n\n/**\n * Branch type detected in a schema.\n *\n * Point 6 — Distinguishes `anyOf` from `oneOf` to produce more precise\n * diff messages. `\"none\"` indicates an atomic schema (no branches).\n *\n * Note: the exclusivity semantics of `oneOf` are not verified\n * (this would be an NP-hard problem in general). The checker treats `oneOf`\n * like `anyOf` for subset checking, which is correct for the `sub ⊆ sup`\n * case but may produce false positives if the sub's branches overlap.\n */\nexport type BranchType = \"anyOf\" | \"oneOf\" | \"none\";\n\nexport interface BranchResult {\n\t/** The branches extracted from the schema */\n\tbranches: JSONSchema7Definition[];\n\t/** The detected branch type */\n\ttype: BranchType;\n}\n\n// ─── Branch extraction ───────────────────────────────────────────────────────\n\n// Pre-allocated singleton results for boolean schemas to avoid per-call allocations.\n// These are safe because the branches arrays are never mutated after creation.\nconst BRANCH_TRUE: BranchResult = { branches: [true], type: \"none\" };\nconst BRANCH_FALSE: BranchResult = { branches: [false], type: \"none\" };\n\n/**\n * WeakMap cache for atomic (no anyOf/oneOf) schema branch results.\n * Avoids allocating `{ branches: [def], type: \"none\" }` on every call\n * for the same schema object. Since normalized schemas are cached and\n * return the same reference, this cache hits frequently.\n */\nconst atomicBranchCache = new WeakMap<object, BranchResult>();\n\n/**\n * Extracts branches from a schema and the branch type.\n *\n * Returns the elements of `anyOf`/`oneOf` if they exist, otherwise returns\n * the schema itself in an array with type `\"none\"`.\n *\n * Point 6 — Distinguishes `anyOf` from `oneOf` in diff paths.\n *\n * Optimization: reuses pre-allocated objects for boolean cases\n * (true/false) and a WeakMap cache for atomic schemas to\n * avoid allocations on these frequent paths.\n */\nexport function getBranchesTyped(def: JSONSchema7Definition): BranchResult {\n\tif (typeof def === \"boolean\") {\n\t\treturn def ? BRANCH_TRUE : BRANCH_FALSE;\n\t}\n\tif (hasOwn(def, \"anyOf\") && Array.isArray(def.anyOf)) {\n\t\treturn { branches: def.anyOf, type: \"anyOf\" };\n\t}\n\tif (hasOwn(def, \"oneOf\") && Array.isArray(def.oneOf)) {\n\t\treturn { branches: def.oneOf, type: \"oneOf\" };\n\t}\n\t// Cache atomic results per schema object to avoid repeated allocations.\n\tlet cached = atomicBranchCache.get(def);\n\tif (cached === undefined) {\n\t\tcached = { branches: [def], type: \"none\" };\n\t\tatomicBranchCache.set(def, cached);\n\t}\n\treturn cached;\n}\n\n// ─── `not` reasoning (Point 7 — extended) ────────────────────────────────────\n\n/**\n * Extended `not` reasoning for common cases.\n *\n * Point 7 — Checks compatibility when `sup` and/or `sub` contain `not`:\n *\n * **Existing cases (not in sup):**\n * - `not.type`: excluded type vs sub's type\n * - `not.const`: excluded const vs sub's const\n * - `not.enum`: excluded values vs sub's enum\n *\n * **Added cases:**\n * - 1.1 — `not` with `properties` + `required`: verify that sub's properties\n * are incompatible with the `not`'s properties (different const/enum)\n * - 1.2 — `not` with `anyOf`/`oneOf`: `not(anyOf([A,B]))` ≡ `allOf([not(A), not(B)])`,\n * so sub must be incompatible with EACH branch\n * - 1.3 — `not` in `sub` (not only in `sup`): a sub with `not`\n * accepts a set too wide to be a subset of a concrete sup\n * - `not.format`: format-vs-format via `isFormatSubset`\n *\n * Conservative ternary contract:\n * - `true` → compatible (certain)\n * - `false` → incompatible (certain)\n * - `null` → undetermined (let the merge engine decide)\n *\n * When in doubt → `null`. NEVER return `true` without certainty.\n */\nfunction evaluateNot(\n\tsub: JSONSchema7Definition,\n\tsup: JSONSchema7Definition,\n): boolean | null {\n\tif (typeof sub === \"boolean\" || typeof sup === \"boolean\") return null;\n\n\t// ── 1.3 — `not` in sub (not in sup) ──\n\t// A `not` in sub is an additional restriction: it excludes values from\n\t// the set accepted by sub, which makes it potentially smaller — thus\n\t// more likely to be ⊆ sup, not less.\n\t// We let the merge engine decide: allOf(sub, sup) will preserve the `not`\n\t// from sub, and the comparison merged ≡ sub will give the correct result.\n\t// Exception: if both have `not`, we handle the identity case below.\n\n\t// Check `not` in sup\n\tif (hasOwn(sup, \"not\") && isPlainObj(sup.not)) {\n\t\tconst notSchema = sup.not as JSONSchema7;\n\n\t\t// ── 1.1 — Case not with properties + required ──\n\t\t// IMPORTANT: this check is placed BEFORE the not.type check because when\n\t\t// the not has both `type` and `properties`, the not.type check alone\n\t\t// would produce a false negative (e.g. sub type=object and not type=object\n\t\t// would return false, but the properties could be incompatible\n\t\t// which would make sub compatible with the not).\n\t\t// If not contains properties with const/enum and required,\n\t\t// verify that sub's properties are incompatible with the not's properties.\n\t\tif (isPlainObj(notSchema.properties) && Array.isArray(notSchema.required)) {\n\t\t\tconst notProps = notSchema.properties as Record<\n\t\t\t\tstring,\n\t\t\t\tJSONSchema7Definition\n\t\t\t>;\n\t\t\tconst notRequired = notSchema.required as string[];\n\n\t\t\t// sub must have properties for us to compare\n\t\t\tif (isPlainObj(sub.properties)) {\n\t\t\t\tconst subProps = sub.properties as Record<\n\t\t\t\t\tstring,\n\t\t\t\t\tJSONSchema7Definition\n\t\t\t\t>;\n\t\t\t\tconst subRequired = Array.isArray(sub.required)\n\t\t\t\t\t? (sub.required as string[])\n\t\t\t\t\t: [];\n\t\t\t\tconst notPropKeys = Object.keys(notProps);\n\n\t\t\t\t// For sub to be compatible with not(schema),\n\t\t\t\t// it suffices that at least ONE property of the not is incompatible with sub.\n\t\t\t\t// This means that sub can never validate the schema inside not.\n\t\t\t\tconst hasIncompatibleProp = notPropKeys.some((key) => {\n\t\t\t\t\tconst notPropDef = notProps[key];\n\t\t\t\t\tif (typeof notPropDef === \"boolean\") return false;\n\t\t\t\t\tconst notProp = notPropDef as JSONSchema7;\n\n\t\t\t\t\t// If the property is required in not but NOT in sub.required\n\t\t\t\t\t// and it doesn't exist in sub.properties → sub may not\n\t\t\t\t\t// have this property → the not schema wouldn't match → compatible\n\t\t\t\t\tif (\n\t\t\t\t\t\tnotRequired.includes(key) &&\n\t\t\t\t\t\t!subRequired.includes(key) &&\n\t\t\t\t\t\t!hasOwn(subProps, key)\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn true; // Property absent from sub → not doesn't match\n\t\t\t\t\t}\n\n\t\t\t\t\t// Compare the const/enum of the property\n\t\t\t\t\tif (!hasOwn(subProps, key)) return false;\n\t\t\t\t\tconst subPropDef = subProps[key];\n\t\t\t\t\tif (typeof subPropDef === \"boolean\") return false;\n\t\t\t\t\tconst subProp = subPropDef as JSONSchema7;\n\n\t\t\t\t\t// not.prop has a const, sub.prop has a different const → incompatible for this prop\n\t\t\t\t\tif (hasOwn(notProp, \"const\") && hasOwn(subProp, \"const\")) {\n\t\t\t\t\t\tif (!deepEqual(notProp.const, subProp.const)) {\n\t\t\t\t\t\t\treturn true; // Different consts → sub doesn't match the not\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// not.prop has an enum, sub.prop has a const or enum with no value\n\t\t\t\t\t// in not.enum → incompatible for this prop\n\t\t\t\t\tif (hasOwn(notProp, \"enum\") && Array.isArray(notProp.enum)) {\n\t\t\t\t\t\tif (hasOwn(subProp, \"const\")) {\n\t\t\t\t\t\t\tconst inNotEnum = notProp.enum.some((v) =>\n\t\t\t\t\t\t\t\tdeepEqual(v, subProp.const),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tif (!inNotEnum) return true; // sub.const absent from not.enum\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (hasOwn(subProp, \"enum\") && Array.isArray(subProp.enum)) {\n\t\t\t\t\t\t\tconst noneInNotEnum = subProp.enum.every(\n\t\t\t\t\t\t\t\t(v) => !notProp.enum?.some((nv) => deepEqual(v, nv)),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tif (noneInNotEnum) return true; // No value from sub.enum in not.enum\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn false;\n\t\t\t\t});\n\n\t\t\t\tif (hasIncompatibleProp) return true;\n\n\t\t\t\t// Inverse check: if ALL properties of the not match sub\n\t\t\t\t// exactly (same const, sub has the not's required), then sub VIOLATES the not\n\t\t\t\tconst allPropsMatch = notPropKeys.every((key) => {\n\t\t\t\t\tconst notPropDef = notProps[key];\n\t\t\t\t\tif (typeof notPropDef === \"boolean\") return true;\n\t\t\t\t\tconst notProp = notPropDef as JSONSchema7;\n\n\t\t\t\t\t// The property must be in sub.required if it is in not.required\n\t\t\t\t\tif (notRequired.includes(key) && !subRequired.includes(key))\n\t\t\t\t\t\treturn false;\n\t\t\t\t\tif (!hasOwn(subProps, key)) return false;\n\t\t\t\t\tconst subPropDef = subProps[key];\n\t\t\t\t\tif (typeof subPropDef === \"boolean\") return true;\n\t\t\t\t\tconst subProp = subPropDef as JSONSchema7;\n\n\t\t\t\t\t// Check const match\n\t\t\t\t\tif (hasOwn(notProp, \"const\") && hasOwn(subProp, \"const\")) {\n\t\t\t\t\t\treturn deepEqual(notProp.const, subProp.const);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check enum inclusion\n\t\t\t\t\tif (hasOwn(notProp, \"enum\") && Array.isArray(notProp.enum)) {\n\t\t\t\t\t\tif (hasOwn(subProp, \"const\")) {\n\t\t\t\t\t\t\treturn notProp.enum.some((v) => deepEqual(v, subProp.const));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (hasOwn(subProp, \"enum\") && Array.isArray(subProp.enum)) {\n\t\t\t\t\t\t\t// All values of sub.enum are in not.enum\n\t\t\t\t\t\t\treturn subProp.enum.every((v) =>\n\t\t\t\t\t\t\t\tnotProp.enum?.some((nv) => deepEqual(v, nv)),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn false; // Undetermined for this property\n\t\t\t\t});\n\n\t\t\t\tif (allPropsMatch) return false; // sub matches the not exactly → incompatible\n\t\t\t}\n\t\t}\n\n\t\t// ── Case not.const ──\n\t\t// IMPORTANT: this check is placed BEFORE not.type because when the not has\n\t\t// both `type` and `const`, the not.type check alone would produce a\n\t\t// false negative (e.g. sub type=string const=\"active\" and not type=string\n\t\t// const=\"deleted\" → the type check would return false due to same type,\n\t\t// even though the consts are different → compatible).\n\t\tif (hasOwn(notSchema, \"const\") && hasOwn(sub, \"const\")) {\n\t\t\tconst notConst = notSchema.const;\n\t\t\tconst subConst = sub.const;\n\t\t\tif (deepEqual(subConst, notConst)) return false;\n\t\t\treturn true;\n\t\t}\n\n\t\t// ── Case not.const + sub.enum ──\n\t\t// When sub has enum and not has const, check that the forbidden const\n\t\t// is not in sub's enum values. If none match → compatible.\n\t\tif (hasOwn(notSchema, \"const\") && Array.isArray(sub.enum)) {\n\t\t\tconst notConst = notSchema.const;\n\t\t\tconst allDisjoint = sub.enum.every((v) => !deepEqual(v, notConst));\n\t\t\tif (allDisjoint) return true;\n\t\t\t// At least one enum value equals the forbidden const → incompatible\n\t\t\treturn false;\n\t\t}\n\n\t\t// ── Case not.enum ──\n\t\t// Also placed BEFORE not.type for the same reason.\n\t\tif (\n\t\t\thasOwn(notSchema, \"enum\") &&\n\t\t\tArray.isArray(notSchema.enum) &&\n\t\t\thasOwn(sub, \"enum\") &&\n\t\t\tArray.isArray(sub.enum)\n\t\t) {\n\t\t\t// All values of sub.enum must be absent from not.enum\n\t\t\tconst allExcluded = sub.enum.every(\n\t\t\t\t(val) => !notSchema.enum?.some((notVal) => deepEqual(val, notVal)),\n\t\t\t);\n\t\t\tif (allExcluded) return true;\n\t\t\t// Some values of sub are in not.enum → not automatically false,\n\t\t\t// the merge engine can still handle it\n\t\t}\n\n\t\t// ── Case not.enum + sub.const ──\n\t\t// When sub has const and not has enum, check that sub's const is not\n\t\t// in the forbidden enum values.\n\t\tif (\n\t\t\thasOwn(notSchema, \"enum\") &&\n\t\t\tArray.isArray(notSchema.enum) &&\n\t\t\thasOwn(sub, \"const\")\n\t\t) {\n\t\t\tconst inNotEnum = notSchema.enum.some((v) => deepEqual(v, sub.const));\n\t\t\tif (!inNotEnum) return true;\n\t\t\treturn false;\n\t\t}\n\n\t\t// ── Case not.type ──\n\t\t// Placed AFTER not.const, not.enum and properties+required to avoid\n\t\t// short-circuiting cases where the not has more specific constraints.\n\t\t// The type check alone is a fallback for simple not schemas\n\t\t// (e.g. { not: { type: \"string\" } }).\n\t\tif (hasOwn(notSchema, \"type\") && hasOwn(sub, \"type\")) {\n\t\t\tconst notType = notSchema.type;\n\t\t\tconst subType = sub.type;\n\n\t\t\t// If both are simple strings\n\t\t\tif (typeof notType === \"string\" && typeof subType === \"string\") {\n\t\t\t\t// Only return if the not does NOT have more specific constraints\n\t\t\t\t// (const, enum, properties) that should have been handled above\n\t\t\t\tif (\n\t\t\t\t\t!hasOwn(notSchema, \"const\") &&\n\t\t\t\t\t!hasOwn(notSchema, \"enum\") &&\n\t\t\t\t\t!isPlainObj(notSchema.properties)\n\t\t\t\t) {\n\t\t\t\t\tif (subType === notType) return false; // Incompatible: sub is exactly the excluded type\n\t\t\t\t\treturn true; // Compatible: sub is a different type from the excluded type\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If notType is an array, sub.type must not be in it\n\t\t\tif (Array.isArray(notType) && typeof subType === \"string\") {\n\t\t\t\tif (notType.includes(subType)) return false;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\t// ── 1.2 — Case not with anyOf / oneOf ──\n\t\t// not(anyOf([A, B])) ≡ allOf([not(A), not(B)])\n\t\t// For sub ⊆ not(anyOf(...)), sub must be incompatible with EACH branch.\n\t\tif (hasOwn(notSchema, \"anyOf\") && Array.isArray(notSchema.anyOf)) {\n\t\t\tconst branches = notSchema.anyOf as JSONSchema7Definition[];\n\t\t\t// For each branch of not.anyOf, verify that sub is incompatible\n\t\t\tconst allIncompatible = branches.every((branch) => {\n\t\t\t\tif (typeof branch === \"boolean\") return !branch; // not(true) = nothing, not(false) = everything\n\t\t\t\t// Create a virtual sup { not: branch } and verify recursively\n\t\t\t\tconst result = evaluateNot(sub, { not: branch });\n\t\t\t\t// result = true → sub is compatible with not(branch) → sub ⊄ branch → OK\n\t\t\t\t// result = false → sub is incompatible with not(branch) → sub ⊆ branch → not OK\n\t\t\t\t// result = null → undetermined\n\t\t\t\treturn result === true;\n\t\t\t});\n\t\t\tif (allIncompatible) return true;\n\n\t\t\t// Check if at least one branch accepts sub → incompatible with not(anyOf)\n\t\t\tconst anyBranchMatches = branches.some((branch) => {\n\t\t\t\tif (typeof branch === \"boolean\") return branch;\n\t\t\t\tconst result = evaluateNot(sub, { not: branch });\n\t\t\t\treturn result === false; // sub is incompatible with not(branch) → sub ⊆ branch\n\t\t\t});\n\t\t\tif (anyBranchMatches) return false;\n\t\t}\n\n\t\t// Same logic for oneOf (in the not context, treated like anyOf)\n\t\tif (hasOwn(notSchema, \"oneOf\") && Array.isArray(notSchema.oneOf)) {\n\t\t\tconst branches = notSchema.oneOf as JSONSchema7Definition[];\n\t\t\tconst allIncompatible = branches.every((branch) => {\n\t\t\t\tif (typeof branch === \"boolean\") return !branch;\n\t\t\t\tconst result = evaluateNot(sub, { not: branch });\n\t\t\t\treturn result === true;\n\t\t\t});\n\t\t\tif (allIncompatible) return true;\n\n\t\t\tconst anyBranchMatches = branches.some((branch) => {\n\t\t\t\tif (typeof branch === \"boolean\") return branch;\n\t\t\t\tconst result = evaluateNot(sub, { not: branch });\n\t\t\t\treturn result === false;\n\t\t\t});\n\t\t\tif (anyBranchMatches) return false;\n\t\t}\n\n\t\t// ── Case not.format (format-vs-format only) ──\n\t\t// If not has a format and sub also does, check compatibility\n\t\tif (hasOwn(notSchema, \"format\") && hasOwn(sub, \"format\")) {\n\t\t\tconst subFormat = sub.format as string;\n\t\t\tconst notFormat = notSchema.format as string;\n\t\t\tif (subFormat === notFormat) return false; // Incompatible: sub has exactly the excluded format\n\t\t\t// Different formats → compatible (conservative approximation)\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t// Check `not` in sub AND in sup (identity: { not: X } ⊆ { not: X })\n\tif (hasOwn(sub, \"not\") && hasOwn(sup, \"not\")) {\n\t\tif (deepEqual(sub.not, sup.not)) return true;\n\t}\n\n\treturn null; // No opinion → let the merge engine decide\n}\n\n// ─── Not stripping helper ────────────────────────────────────────────────────\n\n/**\n * Removes the `not` keyword from a schema to allow a clean merge\n * when `evaluateNot` has already confirmed compatibility.\n *\n * Also handles `not` nested in `properties`: if a property of `sup`\n * has a `not` that is compatible with the corresponding property of\n * `sub`, it is removed as well.\n *\n * Returns the cleaned schema, or `null` if the schema is empty after removal.\n */\nfunction stripNotFromSup(\n\tsub: JSONSchema7Definition,\n\tsup: JSONSchema7Definition,\n\tstripTopLevel: boolean = true,\n): JSONSchema7Definition {\n\tif (typeof sup === \"boolean\" || typeof sub === \"boolean\") return sup;\n\n\tlet result = sup as JSONSchema7;\n\n\t// ── Remove top-level `not` (only if confirmed) ──\n\tif (stripTopLevel && hasOwn(result, \"not\")) {\n\t\tresult = omitKeys(result as unknown as Record<string, unknown>, [\n\t\t\t\"not\",\n\t\t]) as JSONSchema7;\n\t}\n\n\t// ── Recursively remove `not` in common properties ──\n\t// For each shared property: if the sup property has a direct `not`,\n\t// evaluate and strip it. Then recurse deeper into nested objects so\n\t// that `not` constraints at any depth are handled.\n\tif (\n\t\tisPlainObj(result.properties) &&\n\t\tisPlainObj((sub as JSONSchema7).properties)\n\t) {\n\t\tconst subProps = (sub as JSONSchema7).properties as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition\n\t\t>;\n\t\tconst supProps = result.properties as Record<string, JSONSchema7Definition>;\n\t\tlet newProps: Record<string, JSONSchema7Definition> | undefined;\n\n\t\tfor (const key of Object.keys(supProps)) {\n\t\t\tconst supPropDef = supProps[key];\n\t\t\tconst subPropDef = subProps[key];\n\t\t\tif (\n\t\t\t\tsupPropDef === undefined ||\n\t\t\t\tsubPropDef === undefined ||\n\t\t\t\ttypeof supPropDef === \"boolean\" ||\n\t\t\t\ttypeof subPropDef === \"boolean\"\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet strippedProp: JSONSchema7Definition = supPropDef;\n\n\t\t\t// Direct `not` on this property — evaluate and strip if confirmed\n\t\t\tif (hasOwn(supPropDef, \"not\")) {\n\t\t\t\tconst propNotResult = evaluateNot(subPropDef, supPropDef);\n\t\t\t\tif (propNotResult === true) {\n\t\t\t\t\tstrippedProp = omitKeys(\n\t\t\t\t\t\tsupPropDef as unknown as Record<string, unknown>,\n\t\t\t\t\t\t[\"not\"],\n\t\t\t\t\t) as JSONSchema7Definition;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Recurse into nested object properties (handles `not` at any depth)\n\t\t\tconst recursed = stripNotFromSup(subPropDef, strippedProp, false);\n\t\t\tif (recursed !== supPropDef) {\n\t\t\t\tif (!newProps) newProps = { ...supProps };\n\t\t\t\tnewProps[key] = recursed;\n\t\t\t}\n\t\t}\n\n\t\tif (newProps) {\n\t\t\tresult = { ...result, properties: newProps };\n\t\t}\n\t}\n\n\treturn result;\n}\n\n// ─── Pattern stripping helper ────────────────────────────────────────────────\n\n/**\n * Removes the `pattern` from `sup` when `isPatternSubset` has confirmed that\n * sub.pattern ⊆ sup.pattern via sampling.\n *\n * Works like `stripNotFromSup`: we remove the sup constraint that is already\n * satisfied by sub, to prevent the merge engine from producing a combined\n * pattern (lookahead conjunction) structurally different from sub's pattern,\n * which would cause a false negative.\n *\n * Recurses into `properties` to handle nested patterns.\n *\n * @param sub The sub schema (used to extract patterns to compare)\n * @param sup The sup schema from which confirmed patterns are removed\n * @returns The cleaned sup schema\n */\nfunction stripPatternFromSup(\n\tsub: JSONSchema7Definition,\n\tsup: JSONSchema7Definition,\n): JSONSchema7Definition {\n\tif (typeof sub === \"boolean\" || typeof sup === \"boolean\") return sup;\n\n\tconst supObj: JSONSchema7 = sup;\n\n\t// Lazy copy-on-write: only create a copy when the first mutation is needed.\n\tlet result: JSONSchema7 = supObj;\n\tlet copied = false;\n\n\tfunction ensureCopy(): JSONSchema7 {\n\t\tif (!copied) {\n\t\t\tresult = { ...supObj };\n\t\t\tcopied = true;\n\t\t}\n\t\treturn result;\n\t}\n\n\t// ── Top-level pattern ──\n\tif (\n\t\thasOwn(result, \"pattern\") &&\n\t\thasOwn(sub, \"pattern\") &&\n\t\tresult.pattern !== (sub as JSONSchema7).pattern\n\t) {\n\t\tconst patResult = isPatternSubset(\n\t\t\t(sub as JSONSchema7).pattern as string,\n\t\t\tresult.pattern as string,\n\t\t);\n\t\tif (patResult === true) {\n\t\t\tresult = omitKeys(ensureCopy() as unknown as Record<string, unknown>, [\n\t\t\t\t\"pattern\",\n\t\t\t]) as JSONSchema7;\n\t\t\tcopied = true;\n\t\t}\n\t}\n\n\t// ── Patterns in common properties ──\n\tif (\n\t\tisPlainObj(result.properties) &&\n\t\tisPlainObj((sub as JSONSchema7).properties)\n\t) {\n\t\tconst subProps = (sub as JSONSchema7).properties as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition\n\t\t>;\n\t\tconst supProps = result.properties as Record<string, JSONSchema7Definition>;\n\t\tlet propsModified = false;\n\t\tlet newProps: Record<string, JSONSchema7Definition> | undefined;\n\n\t\tfor (const key of Object.keys(supProps)) {\n\t\t\tconst supPropDef = supProps[key];\n\t\t\tconst subPropDef = subProps[key];\n\t\t\tif (\n\t\t\t\tsupPropDef !== undefined &&\n\t\t\t\tsubPropDef !== undefined &&\n\t\t\t\ttypeof supPropDef !== \"boolean\" &&\n\t\t\t\ttypeof subPropDef !== \"boolean\" &&\n\t\t\t\thasOwn(supPropDef, \"pattern\") &&\n\t\t\t\thasOwn(subPropDef, \"pattern\") &&\n\t\t\t\tsupPropDef.pattern !== subPropDef.pattern\n\t\t\t) {\n\t\t\t\tconst propPatResult = isPatternSubset(\n\t\t\t\t\t(subPropDef as JSONSchema7).pattern as string,\n\t\t\t\t\t(supPropDef as JSONSchema7).pattern as string,\n\t\t\t\t);\n\t\t\t\tif (propPatResult === true) {\n\t\t\t\t\tif (!newProps) newProps = { ...supProps };\n\t\t\t\t\tnewProps[key] = omitKeys(\n\t\t\t\t\t\tsupPropDef as unknown as Record<string, unknown>,\n\t\t\t\t\t\t[\"pattern\"],\n\t\t\t\t\t) as JSONSchema7Definition;\n\t\t\t\t\tpropsModified = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (propsModified && newProps) {\n\t\t\tensureCopy().properties = newProps;\n\t\t}\n\t}\n\n\t// ── Pattern in items (single schema) ──\n\tif (\n\t\tisPlainObj(result.items) &&\n\t\ttypeof result.items !== \"boolean\" &&\n\t\tisPlainObj((sub as JSONSchema7).items) &&\n\t\ttypeof (sub as JSONSchema7).items !== \"boolean\"\n\t) {\n\t\tconst subItems = (sub as JSONSchema7).items as JSONSchema7;\n\t\tconst supItems = result.items as JSONSchema7;\n\t\tif (\n\t\t\thasOwn(supItems, \"pattern\") &&\n\t\t\thasOwn(subItems, \"pattern\") &&\n\t\t\tsupItems.pattern !== subItems.pattern\n\t\t) {\n\t\t\tconst itemsPatResult = isPatternSubset(\n\t\t\t\tsubItems.pattern as string,\n\t\t\t\tsupItems.pattern as string,\n\t\t\t);\n\t\t\tif (itemsPatResult === true) {\n\t\t\t\tensureCopy().items = omitKeys(\n\t\t\t\t\tsupItems as unknown as Record<string, unknown>,\n\t\t\t\t\t[\"pattern\"],\n\t\t\t\t) as JSONSchema7Definition;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Strips redundant numeric bounds from `sup` when `sub` already has a stricter\n * exclusive bound that implies the inclusive bound.\n *\n * For example, if sub has `exclusiveMinimum: 5` and sup has `minimum: 5`,\n * the sup's `minimum` is redundant because `x > 5` ⊂ `x ≥ 5`.\n */\nfunction stripRedundantBoundsFromSup(\n\tsub: JSONSchema7Definition,\n\tsup: JSONSchema7Definition,\n): JSONSchema7Definition {\n\tif (typeof sub === \"boolean\" || typeof sup === \"boolean\") return sup;\n\n\tconst keysToStrip: string[] = [];\n\n\t// sub.exclusiveMinimum >= sup.minimum → sup.minimum is redundant\n\tif (\n\t\tsup.minimum !== undefined &&\n\t\tsub.minimum === undefined &&\n\t\tsub.exclusiveMinimum !== undefined &&\n\t\tsub.exclusiveMinimum >= sup.minimum\n\t) {\n\t\tkeysToStrip.push(\"minimum\");\n\t}\n\n\t// sub.exclusiveMaximum <= sup.maximum → sup.maximum is redundant\n\tif (\n\t\tsup.maximum !== undefined &&\n\t\tsub.maximum === undefined &&\n\t\tsub.exclusiveMaximum !== undefined &&\n\t\tsub.exclusiveMaximum <= sup.maximum\n\t) {\n\t\tkeysToStrip.push(\"maximum\");\n\t}\n\n\t// sub.minimum > sup.exclusiveMinimum → sup.exclusiveMinimum is redundant\n\tif (\n\t\tsup.exclusiveMinimum !== undefined &&\n\t\tsub.exclusiveMinimum === undefined &&\n\t\tsub.minimum !== undefined &&\n\t\tsub.minimum > sup.exclusiveMinimum\n\t) {\n\t\tkeysToStrip.push(\"exclusiveMinimum\");\n\t}\n\n\t// sub.maximum < sup.exclusiveMaximum → sup.exclusiveMaximum is redundant\n\tif (\n\t\tsup.exclusiveMaximum !== undefined &&\n\t\tsub.exclusiveMaximum === undefined &&\n\t\tsub.maximum !== undefined &&\n\t\tsub.maximum < sup.exclusiveMaximum\n\t) {\n\t\tkeysToStrip.push(\"exclusiveMaximum\");\n\t}\n\n\t// ── Top-level stripping ────────────────────────────────────\n\tlet result: JSONSchema7Definition =\n\t\tkeysToStrip.length > 0\n\t\t\t? (omitKeys(\n\t\t\t\t\tsup as unknown as Record<string, unknown>,\n\t\t\t\t\tkeysToStrip,\n\t\t\t\t) as JSONSchema7)\n\t\t\t: sup;\n\n\t// ── Recurse into properties ────────────────────────────────\n\tconst resultObj = result as JSONSchema7;\n\tif (isPlainObj(resultObj.properties) && isPlainObj(sub.properties)) {\n\t\tconst subProps = sub.properties as Record<string, JSONSchema7Definition>;\n\t\tconst supProps = resultObj.properties as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition\n\t\t>;\n\t\tlet newProps: Record<string, JSONSchema7Definition> | undefined;\n\n\t\tfor (const key of Object.keys(supProps)) {\n\t\t\tconst supProp = supProps[key];\n\t\t\tconst subProp = subProps[key];\n\n\t\t\tif (\n\t\t\t\tsupProp !== undefined &&\n\t\t\t\tsubProp !== undefined &&\n\t\t\t\ttypeof supProp !== \"boolean\" &&\n\t\t\t\ttypeof subProp !== \"boolean\"\n\t\t\t) {\n\t\t\t\tconst stripped = stripRedundantBoundsFromSup(subProp, supProp);\n\t\t\t\tif (stripped !== supProp) {\n\t\t\t\t\tif (!newProps) newProps = { ...supProps };\n\t\t\t\t\tnewProps[key] = stripped;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (newProps) {\n\t\t\tresult = { ...resultObj, properties: newProps };\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Strips `dependencies` from `sup` when every dependency is semantically\n * satisfied by `sub`'s structure:\n * - Array-form deps: all dependent properties are in `sub.required`, OR the\n * trigger property is never produced by `sub` (not in properties + not required).\n * - Schema-form deps: only stripped when the trigger is never produced by `sub`.\n */\nfunction stripDependenciesFromSup(\n\tsub: JSONSchema7Definition,\n\tsup: JSONSchema7Definition,\n): JSONSchema7Definition {\n\tif (typeof sub === \"boolean\" || typeof sup === \"boolean\") return sup;\n\tif (!isPlainObj(sup.dependencies)) return sup;\n\n\tconst supDeps = sup.dependencies as Record<\n\t\tstring,\n\t\tJSONSchema7Definition | string[]\n\t>;\n\tconst subRequired = Array.isArray(sub.required) ? sub.required : [];\n\tconst subProps = isPlainObj(sub.properties)\n\t\t? (sub.properties as Record<string, JSONSchema7Definition>)\n\t\t: {};\n\tconst subHasAdditionalPropsFalse = sub.additionalProperties === false;\n\n\tfor (const key of Object.keys(supDeps)) {\n\t\tconst dep = supDeps[key];\n\n\t\tif (Array.isArray(dep)) {\n\t\t\t// Array-form: satisfied if all dependents are always required,\n\t\t\t// OR the trigger is never produced by sub\n\t\t\tconst triggerAlwaysPresent =\n\t\t\t\tsubRequired.includes(key) || hasOwn(subProps, key);\n\t\t\tconst triggerNeverProduced =\n\t\t\t\t!hasOwn(subProps, key) &&\n\t\t\t\t!subRequired.includes(key) &&\n\t\t\t\tsubHasAdditionalPropsFalse;\n\n\t\t\tif (triggerNeverProduced) continue;\n\n\t\t\tif (triggerAlwaysPresent) {\n\t\t\t\tconst allDepsRequired = dep.every((d) => subRequired.includes(d));\n\t\t\t\tif (allDepsRequired) continue;\n\t\t\t}\n\n\t\t\t// This dependency is not satisfied — cannot strip\n\t\t\treturn sup;\n\t\t}\n\n\t\t// Schema-form: strip if trigger is never produced OR if sub satisfies the dep schema\n\t\tif (isPlainObj(dep)) {\n\t\t\tconst triggerNeverProduced =\n\t\t\t\t!hasOwn(subProps, key) &&\n\t\t\t\t!subRequired.includes(key) &&\n\t\t\t\tsubHasAdditionalPropsFalse;\n\t\t\tif (triggerNeverProduced) continue;\n\n\t\t\t// The trigger exists in sub — check if sub structurally satisfies the dep schema.\n\t\t\t// A schema-form dependency { A: { required: ['B'], properties: { B: S } } }\n\t\t\t// means \"if A is present → the object must also match the dep schema\".\n\t\t\t// If sub already satisfies the dep schema (all dep.required are in sub.required,\n\t\t\t// and all dep.properties exist in sub.properties), the dependency is trivially met.\n\t\t\tconst depSchema = dep as JSONSchema7;\n\t\t\tconst depRequired = Array.isArray(depSchema.required)\n\t\t\t\t? (depSchema.required as string[])\n\t\t\t\t: [];\n\t\t\tconst depProps = isPlainObj(depSchema.properties)\n\t\t\t\t? (depSchema.properties as Record<string, JSONSchema7Definition>)\n\t\t\t\t: {};\n\n\t\t\tconst allDepRequiredSatisfied = depRequired.every((r) =>\n\t\t\t\tsubRequired.includes(r),\n\t\t\t);\n\t\t\tif (!allDepRequiredSatisfied) return sup;\n\n\t\t\tconst allDepPropsSatisfied = Object.keys(depProps).every((propKey) => {\n\t\t\t\tif (!hasOwn(subProps, propKey)) return false;\n\t\t\t\t// Check that sub's property is at least as narrow as the dep's property.\n\t\t\t\t// For simple cases (same type, sub has stricter constraints), deepEqual\n\t\t\t\t// or a structural check suffices. We compare the dep property against\n\t\t\t\t// the sub property: if sub.properties[k] has all constraints from\n\t\t\t\t// dep.properties[k], the dep is satisfied for that property.\n\t\t\t\tconst subPropDef = subProps[propKey];\n\t\t\t\tconst depPropDef = depProps[propKey];\n\t\t\t\tif (subPropDef === undefined || depPropDef === undefined) return false;\n\t\t\t\tif (\n\t\t\t\t\ttypeof subPropDef === \"boolean\" ||\n\t\t\t\t\ttypeof depPropDef === \"boolean\"\n\t\t\t\t) {\n\t\t\t\t\treturn subPropDef === depPropDef;\n\t\t\t\t}\n\t\t\t\t// If the sub property is structurally equal to or narrower than the dep property,\n\t\t\t\t// the dependency is satisfied. A simple heuristic: check that every keyword\n\t\t\t\t// in the dep property also exists in the sub property with an equal or stricter value.\n\t\t\t\tconst depPropKeys = Object.keys(depPropDef);\n\t\t\t\treturn depPropKeys.every((dk) => {\n\t\t\t\t\tconst depVal = (depPropDef as Record<string, unknown>)[dk];\n\t\t\t\t\tconst subVal = (subPropDef as Record<string, unknown>)[dk];\n\t\t\t\t\tif (subVal === undefined) return false;\n\t\t\t\t\t// For numeric constraints, sub must be at least as strict\n\t\t\t\t\tif (typeof depVal === \"number\" && typeof subVal === \"number\") {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tdk === \"minLength\" ||\n\t\t\t\t\t\t\tdk === \"minimum\" ||\n\t\t\t\t\t\t\tdk === \"exclusiveMinimum\" ||\n\t\t\t\t\t\t\tdk === \"minItems\" ||\n\t\t\t\t\t\t\tdk === \"minProperties\"\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn subVal >= depVal;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tdk === \"maxLength\" ||\n\t\t\t\t\t\t\tdk === \"maximum\" ||\n\t\t\t\t\t\t\tdk === \"exclusiveMaximum\" ||\n\t\t\t\t\t\t\tdk === \"maxItems\" ||\n\t\t\t\t\t\t\tdk === \"maxProperties\"\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn subVal <= depVal;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn deepEqual(depVal, subVal);\n\t\t\t\t});\n\t\t\t});\n\t\t\tif (!allDepPropsSatisfied) return sup;\n\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Unknown form — cannot strip safely\n\t\treturn sup;\n\t}\n\n\t// All dependencies satisfied → strip\n\treturn omitKeys(sup as unknown as Record<string, unknown>, [\n\t\t\"dependencies\",\n\t]) as JSONSchema7;\n}\n\n// ─── Vacuous false-property stripping ────────────────────────────────────────\n//\n// In JSON Schema, `properties: { x: false }` means \"if x is present, it must\n// validate against `false` (impossible)\". If x is **absent** from the instance,\n// the constraint is **trivially satisfied** (vacuous truth).\n//\n// After merge(sub, sup), the merge engine may add `false`-schema properties\n// from sup into the merged result even when sub doesn't define those properties.\n// This causes `deepEqual(merged, sub)` to fail — a false negative.\n//\n// `stripVacuousFalseProperties` removes those vacuously-satisfied `false`\n// properties from `merged` so that the structural comparison succeeds.\n\n/**\n * Strips `false`-schema properties from `merged` that are absent in `sub`.\n *\n * A property `{ key: false }` in merged that doesn't exist in sub's properties\n * is vacuously satisfied — sub never produces that key, so the \"impossible\"\n * constraint has no effect. Removing it lets `deepEqual(merged, sub)` succeed.\n *\n * @returns A new schema with vacuous `false` properties removed, or the\n * original `merged` if no stripping was needed.\n */\nfunction stripVacuousFalseProperties(\n\tmerged: JSONSchema7Definition,\n\tsub: JSONSchema7Definition,\n): JSONSchema7Definition {\n\tif (typeof merged === \"boolean\" || typeof sub === \"boolean\") return merged;\n\tif (!isPlainObj(merged.properties)) return merged;\n\n\tconst mergedProps = merged.properties as Record<\n\t\tstring,\n\t\tJSONSchema7Definition\n\t>;\n\tconst subProps = (isPlainObj(sub.properties) ? sub.properties : {}) as Record<\n\t\tstring,\n\t\tJSONSchema7Definition\n\t>;\n\n\tlet strippedProps: Record<string, JSONSchema7Definition> | null = null;\n\n\tfor (const key of Object.keys(mergedProps)) {\n\t\tif (mergedProps[key] === false && !hasOwn(subProps, key)) {\n\t\t\t// Lazily copy on first strip\n\t\t\tif (strippedProps === null) {\n\t\t\t\tstrippedProps = { ...mergedProps };\n\t\t\t}\n\t\t\tdelete strippedProps[key];\n\t\t}\n\t}\n\n\tif (strippedProps === null) return merged;\n\n\t// Return a new schema with the cleaned properties\n\tconst result = { ...merged, properties: strippedProps };\n\n\t// If properties is now empty and sub doesn't have properties, remove it\n\tif (Object.keys(strippedProps).length === 0 && !isPlainObj(sub.properties)) {\n\t\tdelete (result as Record<string, unknown>).properties;\n\t}\n\n\treturn result;\n}\n\n// ─── Nested branching fallback ───────────────────────────────────────────────\n//\n// The merge engine (`@x0k/json-schema-merge`) cannot resolve `allOf` over\n// `oneOf`/`anyOf` inside properties — it either throws or produces garbage\n// like `{ type: \"string\", oneOf: [...] }`.\n//\n// When the merge-based check fails (null or merged ≠ sub), and either schema\n// contains `oneOf`/`anyOf` inside its properties or items, we fall back to a\n// **property-by-property** comparison that uses the existing branching logic\n// (`getBranchesTyped` / `isAtomicSubsetOf`) on each sub-schema individually.\n//\n// Three helpers:\n// 1. `hasNestedBranching` — guard: does a schema contain oneOf/anyOf in\n// properties or items? Avoids triggering the fallback on normal schemas.\n// 2. `isPropertySubsetOf` — compares a single property sub-schema handling\n// branches on both sides (sub may have oneOf, sup may have oneOf).\n// 3. `isObjectSubsetByProperties` — the fallback itself: iterates over\n// object properties + items and delegates to `isPropertySubsetOf`.\n// 4. `tryNestedBranchingFallback` — the single entry point called from\n// `isAtomicSubsetOf` and `checkAtomic`. Encapsulates the guard check\n// and the call, returning `boolean | null` (null = not applicable).\n\n/**\n * Returns `true` if the schema contains `oneOf`/`anyOf` inside its\n * `properties` or `items`. Recurses into nested object schemas.\n *\n * This is a cheap guard so we only attempt the property-by-property\n * fallback when the merge failure is plausibly caused by nested branching.\n */\nfunction hasNestedBranching(schema: JSONSchema7Definition): boolean {\n\tif (typeof schema === \"boolean\") return false;\n\n\tif (isPlainObj(schema.properties)) {\n\t\tconst props = schema.properties as Record<string, JSONSchema7Definition>;\n\t\tfor (const key of Object.keys(props)) {\n\t\t\tconst prop = props[key];\n\t\t\tif (prop === undefined || typeof prop === \"boolean\") continue;\n\t\t\tif (hasOwn(prop, \"oneOf\") || hasOwn(prop, \"anyOf\")) return true;\n\t\t\tif (hasNestedBranching(prop)) return true;\n\t\t}\n\t}\n\n\tif (isPlainObj(schema.items) && typeof schema.items !== \"boolean\") {\n\t\tconst items = schema.items as JSONSchema7;\n\t\tif (hasOwn(items, \"oneOf\") || hasOwn(items, \"anyOf\")) return true;\n\t\tif (hasNestedBranching(items)) return true;\n\t}\n\n\treturn false;\n}\n\n/**\n * Checks `sub ⊆ sup` for a single property sub-schema, handling branches\n * on **both** sides.\n *\n * `isAtomicSubsetOf` only extracts branches from `sup`. When `sub` also\n * has `oneOf`/`anyOf`, we extract sub's branches and verify that **every**\n * branch is accepted by sup (same semantics as `checkBranchedSub`).\n */\nfunction isPropertySubsetOf(\n\tsub: JSONSchema7Definition,\n\tsup: JSONSchema7Definition,\n\tengine: MergeEngine,\n): boolean {\n\tconst { branches: subBranches } = getBranchesTyped(sub);\n\n\tif (subBranches.length > 1 || subBranches[0] !== sub) {\n\t\tfor (const branch of subBranches) {\n\t\t\tif (branch === undefined) continue;\n\t\t\tif (!isAtomicSubsetOf(branch, sup, engine)) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\treturn isAtomicSubsetOf(sub, sup, engine);\n}\n\n/**\n * Checks whether the array-level constraints of `sub` are compatible\n * with those of `sup` for subset semantics:\n * - `minItems`: sub.minItems must be >= sup.minItems\n * - `maxItems`: sub.maxItems must be <= sup.maxItems\n * - `uniqueItems`: if sup requires it, sub must too\n */\nfunction isArrayConstraintsSubset(sub: JSONSchema7, sup: JSONSchema7): boolean {\n\t// minItems: sub.minItems must be >= sup.minItems\n\tif (sup.minItems !== undefined) {\n\t\tif (sub.minItems === undefined || sub.minItems < sup.minItems) {\n\t\t\treturn false;\n\t\t}\n\t}\n\t// maxItems: sub.maxItems must be <= sup.maxItems\n\tif (sup.maxItems !== undefined) {\n\t\tif (sub.maxItems === undefined || sub.maxItems > sup.maxItems) {\n\t\t\treturn false;\n\t\t}\n\t}\n\t// uniqueItems: if sup requires it, sub must also require it\n\tif (sup.uniqueItems === true && sub.uniqueItems !== true) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n/**\n * Checks `sub ⊆ sup` by comparing object properties (and array items)\n * individually, using the full branching-aware logic.\n *\n * This is a **fallback** for when the merge-based check fails due to\n * `oneOf`/`anyOf` inside properties. It does NOT check object-level\n * keywords like `minProperties`/`maxProperties` — those are rare in\n * practice and are already handled correctly by the merge when the\n * branching isn't involved.\n *\n * @returns `true` if sub ⊆ sup, `false` otherwise.\n */\nfunction isObjectSubsetByProperties(\n\tsub: JSONSchema7,\n\tsup: JSONSchema7,\n\tengine: MergeEngine,\n): boolean {\n\tconst subIsObj = sub.type === \"object\" || isPlainObj(sub.properties);\n\tconst supIsObj = sup.type === \"object\" || isPlainObj(sup.properties);\n\n\t// ── Array path: both are arrays with items ──\n\tif (!subIsObj && !supIsObj) {\n\t\tif (\n\t\t\tsub.type === \"array\" &&\n\t\t\tsup.type === \"array\" &&\n\t\t\tisPlainObj(sub.items) &&\n\t\t\tisPlainObj(sup.items)\n\t\t) {\n\t\t\tif (\n\t\t\t\t!isPropertySubsetOf(\n\t\t\t\t\tsub.items as JSONSchema7Definition,\n\t\t\t\t\tsup.items as JSONSchema7Definition,\n\t\t\t\t\tengine,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn isArrayConstraintsSubset(sub, sup);\n\t\t}\n\t\treturn false;\n\t}\n\n\t// Both must look like objects\n\tif (!subIsObj || !supIsObj) return false;\n\n\t// ── Type compatibility ──\n\tif (hasOwn(sub, \"type\") && hasOwn(sup, \"type\") && sub.type !== sup.type) {\n\t\treturn false;\n\t}\n\n\tconst subProps = (isPlainObj(sub.properties) ? sub.properties : {}) as Record<\n\t\tstring,\n\t\tJSONSchema7Definition\n\t>;\n\tconst supProps = (isPlainObj(sup.properties) ? sup.properties : {}) as Record<\n\t\tstring,\n\t\tJSONSchema7Definition\n\t>;\n\tconst subRequired = Array.isArray(sub.required)\n\t\t? (sub.required as string[])\n\t\t: [];\n\tconst supRequired = Array.isArray(sup.required)\n\t\t? (sup.required as string[])\n\t\t: [];\n\n\t// ── Required: every key sup requires, sub must also require ──\n\tfor (const key of supRequired) {\n\t\tif (!subRequired.includes(key)) return false;\n\t}\n\n\t// ── additionalProperties: false on sup ──\n\tif (sup.additionalProperties === false) {\n\t\tfor (const key of Object.keys(subProps)) {\n\t\t\tif (!hasOwn(supProps, key)) return false;\n\t\t}\n\t}\n\n\t// ── Property-by-property check ──\n\tfor (const key of Object.keys(supProps)) {\n\t\tconst supProp = supProps[key];\n\t\tconst subProp = subProps[key];\n\t\tif (supProp === undefined || subProp === undefined) continue;\n\n\t\tif (!isPropertySubsetOf(subProp, supProp, engine)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// ── Sub's extra properties vs sup's additionalProperties schema ──\n\tif (\n\t\tisPlainObj(sup.additionalProperties) &&\n\t\ttypeof sup.additionalProperties !== \"boolean\"\n\t) {\n\t\tconst addPropSchema = sup.additionalProperties as JSONSchema7Definition;\n\t\tfor (const key of Object.keys(subProps)) {\n\t\t\tif (hasOwn(supProps, key)) continue;\n\t\t\tconst subProp = subProps[key];\n\t\t\tif (subProp === undefined) continue;\n\t\t\tif (!isPropertySubsetOf(subProp, addPropSchema, engine)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\t// ── Items (object schema that also has array items) ──\n\tif (isPlainObj(sub.items) && isPlainObj(sup.items)) {\n\t\tif (\n\t\t\t!isPropertySubsetOf(\n\t\t\t\tsub.items as JSONSchema7Definition,\n\t\t\t\tsup.items as JSONSchema7Definition,\n\t\t\t\tengine,\n\t\t\t)\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!isArrayConstraintsSubset(sub, sup)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/**\n * Attempts the property-by-property fallback when a merge-based check\n * fails and nested branching is detected.\n *\n * Encapsulates the guard (`hasNestedBranching`) and the call to\n * `isObjectSubsetByProperties` so callers don't repeat the pattern.\n *\n * @returns `true` if the fallback confirms sub ⊆ sup, `false` if it\n * confirms sub ⊄ sup, `null` if the fallback is not applicable\n * (neither schema has nested branching, or schemas are booleans).\n */\nfunction tryNestedBranchingFallback(\n\tsub: JSONSchema7Definition,\n\tsup: JSONSchema7Definition,\n\tengine: MergeEngine,\n): boolean | null {\n\tif (typeof sub === \"boolean\" || typeof sup === \"boolean\") return null;\n\tif (!hasNestedBranching(sub) && !hasNestedBranching(sup)) return null;\n\treturn isObjectSubsetByProperties(sub, sup, engine);\n}\n\n// ─── Atomic subset check ─────────────────────────────────────────────────────\n\n/**\n * Checks whether `sub ⊆ sup` for two schemas without anyOf/oneOf (or with\n * anyOf/oneOf only on the sup side).\n *\n * Point 7 — Integrates an extended `not` pre-check (`evaluateNot`) before the merge.\n *\n * When `evaluateNot` confirms compatibility (`true`), the `not` is removed\n * from `sup` before the merge to prevent the merge engine from adding a `not`\n * constraint that `sub` doesn't have (which would cause `isEqual(merged, sub)` to fail).\n *\n * Pattern pre-check — When both schemas have different patterns, checks\n * inclusion via sampling with `isPatternSubset`. If confirmed, removes the\n * pattern from sup before the merge (same strategy as for `not`).\n *\n * Principle: merge(sub, sup) ≡ sub → sub is a subset of sup.\n *\n * When the merge-based check fails and either sub or sup contains nested\n * `oneOf`/`anyOf` in properties or items, falls back to a property-by-property\n * comparison via `isObjectSubsetByProperties`.\n */\n\n// ─── allOf resolution ────────────────────────────────────────────────────────\n//\n// When `sup` contains `allOf`, the merge engine resolves it during\n// `merge(sub, sup)`, but the stripping pipeline (stripNotFromSup,\n// stripDependenciesFromSup, etc.) runs **before** the merge and operates\n// on the raw `sup` — it cannot see keywords nested inside `allOf` branches.\n//\n// `resolveSupAllOf` pre-resolves the `allOf` by merging all its branches\n// into a single schema so that the stripping pipeline can operate on\n// the flattened result. Any top-level keywords on `sup` alongside `allOf`\n// are preserved by including them in the merge.\n\n/**\n * If `sup` has an `allOf` array, resolve it by merging all branches\n * (plus any sibling keywords) into a single flattened schema.\n * Returns the original `sup` unchanged if there is no `allOf` or\n * if the merge fails.\n */\nfunction resolveSupAllOf(\n\tsup: JSONSchema7Definition,\n\tengine: MergeEngine,\n): JSONSchema7Definition {\n\tif (typeof sup === \"boolean\") return sup;\n\tif (!Array.isArray(sup.allOf) || sup.allOf.length === 0) return sup;\n\n\t// Collect sibling keywords (everything except `allOf`) into a base schema.\n\t// This handles cases like { allOf: [...], type: \"object\" }.\n\tconst { allOf: _allOf, ...sibling } = sup;\n\tconst branches = sup.allOf as JSONSchema7Definition[];\n\n\t// Start from the sibling keywords (or first branch if no siblings).\n\tlet resolved: JSONSchema7Definition | null =\n\t\tObject.keys(sibling).length > 0 ? (sibling as JSONSchema7) : null;\n\n\tfor (const branch of branches) {\n\t\tif (resolved === null) {\n\t\t\tresolved = branch;\n\t\t} else {\n\t\t\tresolved = engine.merge(resolved, branch);\n\t\t\tif (resolved === null) {\n\t\t\t\t// Merge failed — fall back to original sup so the existing\n\t\t\t\t// pipeline can still attempt the merge with allOf intact.\n\t\t\t\treturn sup;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn resolved ?? sup;\n}\n\nexport function isAtomicSubsetOf(\n\tsub: JSONSchema7Definition,\n\tsup: JSONSchema7Definition,\n\tengine: MergeEngine,\n): boolean {\n\t// ── Resolve allOf in sup ──\n\t// Pre-flatten allOf so that the stripping pipeline (stripNotFromSup,\n\t// stripDependenciesFromSup, etc.) can see keywords from all branches.\n\tsup = resolveSupAllOf(sup, engine);\n\n\tconst { branches: supBranches } = getBranchesTyped(sup);\n\n\t// Simple schema → direct merge\n\tif (supBranches.length === 1 && supBranches[0] === sup) {\n\t\t// Point 7: extended `not` pre-check\n\t\tconst notResult = evaluateNot(sub, sup);\n\t\tif (notResult === false) return false;\n\n\t\t// ── Format pre-check ──\n\t\t// If both schemas have a different `format`, check that\n\t\t// sub.format ⊆ sup.format. Otherwise, sub cannot be ⊆ sup.\n\t\t// This complements hasFormatConflict (which handles the merge) by handling\n\t\t// the subset check direction that the merge cannot resolve.\n\t\tif (\n\t\t\ttypeof sub !== \"boolean\" &&\n\t\t\ttypeof sup !== \"boolean\" &&\n\t\t\thasOwn(sub, \"format\") &&\n\t\t\thasOwn(sup, \"format\") &&\n\t\t\tsub.format !== sup.format\n\t\t) {\n\t\t\tconst fmtResult = isFormatSubset(\n\t\t\t\tsub.format as string,\n\t\t\t\tsup.format as string,\n\t\t\t);\n\t\t\tif (fmtResult !== true) return false;\n\t\t}\n\n\t\t// ── Pattern pre-check ──\n\t\t// If both schemas have different patterns, check inclusion\n\t\t// via sampling. If sub.pattern ⊄ sup.pattern (counter-example found),\n\t\t// we return false immediately. Otherwise, we can remove the pattern\n\t\t// from sup to avoid the structural false negative from the merge.\n\t\tif (\n\t\t\ttypeof sub !== \"boolean\" &&\n\t\t\ttypeof sup !== \"boolean\" &&\n\t\t\thasOwn(sub, \"pattern\") &&\n\t\t\thasOwn(sup, \"pattern\") &&\n\t\t\tsub.pattern !== sup.pattern\n\t\t) {\n\t\t\tconst patResult = isPatternSubset(\n\t\t\t\tsub.pattern as string,\n\t\t\t\tsup.pattern as string,\n\t\t\t);\n\t\t\tif (patResult === false) return false;\n\t\t}\n\n\t\t// Remove `not` from sup (top-level and/or in properties)\n\t\t// when evaluateNot confirms compatibility at the corresponding level.\n\t\t// This prevents the merge engine from adding a `not` constraint that sub doesn't have\n\t\t// (which would cause merged ≠ sub and produce a false negative).\n\t\tlet effectiveSup = sup;\n\t\tif (typeof sup !== \"boolean\") {\n\t\t\t// If top-level not is confirmed compatible → remove top-level not\n\t\t\tif (notResult === true) {\n\t\t\t\teffectiveSup = stripNotFromSup(sub, sup, true);\n\t\t\t\t// If sup only had `not` → sub is compatible (the not is resolved)\n\t\t\t\tif (\n\t\t\t\t\ttypeof effectiveSup !== \"boolean\" &&\n\t\t\t\t\tObject.keys(effectiveSup).length === 0\n\t\t\t\t) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Even if the top-level not is not confirmed (null), we attempt\n\t\t\t\t// to remove `not` in individual properties\n\t\t\t\t// without touching the top-level `not`\n\t\t\t\teffectiveSup = stripNotFromSup(sub, sup, false);\n\t\t\t}\n\n\t\t\t// Remove patterns from sup confirmed by sampling.\n\t\t\t// Same strategy as for `not`: we remove the constraint already\n\t\t\t// satisfied by sub to prevent the merge from producing a combined\n\t\t\t// pattern (lookahead conjunction) structurally ≠ sub.\n\t\t\teffectiveSup = stripPatternFromSup(sub, effectiveSup);\n\t\t\teffectiveSup = stripRedundantBoundsFromSup(sub, effectiveSup);\n\t\t\teffectiveSup = stripDependenciesFromSup(sub, effectiveSup);\n\t\t}\n\n\t\tconst merged = engine.merge(sub, effectiveSup);\n\t\tif (merged === null) {\n\t\t\t// ── Fallback: property-by-property for nested oneOf/anyOf ──\n\t\t\treturn tryNestedBranchingFallback(sub, effectiveSup, engine) ?? false;\n\t\t}\n\t\t// Fast path: if merged is already structurally equal to sub,\n\t\t// skip normalize entirely. This is the common case when sub ⊆ sup\n\t\t// (A ∩ B = A), saving O(n) normalize traversal on wide schemas.\n\t\tif (deepEqual(merged, sub)) return true;\n\n\t\t// Strip vacuously-satisfied `false` properties added by the merge\n\t\t// from sup but absent in sub (vacuous truth: absent ⊆ forbidden).\n\t\tconst strippedMerged = stripVacuousFalseProperties(merged, sub);\n\t\tif (strippedMerged !== merged && deepEqual(strippedMerged, sub)) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Slow path: normalize to eliminate merge artifacts (e.g. redundant\n\t\t// enum when const is present), then compare.\n\t\tconst normalizedMerged = normalize(strippedMerged);\n\t\tif (\n\t\t\tdeepEqual(normalizedMerged, sub) ||\n\t\t\tengine.isEqual(normalizedMerged, sub)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// ── Fallback: merged ≠ sub but nested branching may explain it ──\n\t\t// The merge engine preserves oneOf/anyOf as-is inside properties\n\t\t// (e.g. merge produces {type:\"string\", oneOf:[...]} ≠ sub).\n\t\treturn tryNestedBranchingFallback(sub, effectiveSup, engine) ?? false;\n\t}\n\n\t// anyOf/oneOf in sup → at least one branch must accept sub\n\treturn supBranches.some((branch) => {\n\t\t// Point 7: extended `not` pre-check per branch\n\t\tconst notResult = evaluateNot(sub, branch);\n\t\tif (notResult === false) return false;\n\n\t\t// ── Pattern pre-check par branche ──\n\t\tif (\n\t\t\ttypeof sub !== \"boolean\" &&\n\t\t\ttypeof branch !== \"boolean\" &&\n\t\t\thasOwn(sub, \"pattern\") &&\n\t\t\thasOwn(branch, \"pattern\") &&\n\t\t\tsub.pattern !== branch.pattern\n\t\t) {\n\t\t\tconst patResult = isPatternSubset(\n\t\t\t\tsub.pattern as string,\n\t\t\t\tbranch.pattern as string,\n\t\t\t);\n\t\t\tif (patResult === false) return false;\n\t\t}\n\n\t\t// Same strip logic for branches\n\t\tlet effectiveBranch = branch;\n\t\tif (typeof branch !== \"boolean\") {\n\t\t\tif (notResult === true) {\n\t\t\t\teffectiveBranch = stripNotFromSup(sub, branch, true);\n\t\t\t\tif (\n\t\t\t\t\ttypeof effectiveBranch !== \"boolean\" &&\n\t\t\t\t\tObject.keys(effectiveBranch).length === 0\n\t\t\t\t) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\teffectiveBranch = stripNotFromSup(sub, branch, false);\n\t\t\t}\n\n\t\t\t// Strip patterns confirmed by sampling\n\t\t\teffectiveBranch = stripPatternFromSup(sub, effectiveBranch);\n\t\t\teffectiveBranch = stripRedundantBoundsFromSup(sub, effectiveBranch);\n\t\t\teffectiveBranch = stripDependenciesFromSup(sub, effectiveBranch);\n\t\t}\n\n\t\tconst merged = engine.merge(sub, effectiveBranch);\n\t\tif (merged === null) {\n\t\t\t// Fallback for nested branching within branches\n\t\t\treturn tryNestedBranchingFallback(sub, effectiveBranch, engine) === true;\n\t\t}\n\t\t// Fast path: skip normalize if merged already equals sub\n\t\tif (deepEqual(merged, sub)) return true;\n\n\t\t// Strip vacuously-satisfied `false` properties (see comment above)\n\t\tconst strippedBranch = stripVacuousFalseProperties(merged, sub);\n\t\tif (strippedBranch !== merged && deepEqual(strippedBranch, sub)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst normalizedBranch = normalize(strippedBranch);\n\t\tif (\n\t\t\tdeepEqual(normalizedBranch, sub) ||\n\t\t\tengine.isEqual(normalizedBranch, sub)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Fallback: merged ≠ sub but nested branching may explain it\n\t\treturn tryNestedBranchingFallback(sub, effectiveBranch, engine) === true;\n\t});\n}\n\n// ─── Full subset check (with diffs) ─────────────────────────────────────────\n\n/**\n * Checks `sub ⊆ sup` for a sub that has branches (anyOf/oneOf).\n * Each branch of sub must be accepted by sup.\n *\n * Point 6 — Uses `getBranchesTyped` to distinguish `anyOf[i]` from\n * `oneOf[i]` in diff paths.\n */\nexport function checkBranchedSub(\n\tsubBranches: JSONSchema7Definition[],\n\tsup: JSONSchema7Definition,\n\tengine: MergeEngine,\n\tbranchType: BranchType = \"anyOf\",\n): SubsetResult {\n\tconst allErrors: SchemaError[] = [];\n\tlet allSubset = true;\n\n\tfor (let i = 0; i < subBranches.length; i++) {\n\t\tconst branch = subBranches[i];\n\t\tif (branch === undefined) continue;\n\t\tif (!isAtomicSubsetOf(branch, sup, engine)) {\n\t\t\tallSubset = false;\n\t\t\tconst branchErrors = computeSemanticErrors(branch, sup, \"\");\n\t\t\tallErrors.push(...branchErrors);\n\t\t}\n\t}\n\n\treturn {\n\t\tisSubset: allSubset,\n\t\tmerged: allSubset\n\t\t\t? branchType === \"oneOf\"\n\t\t\t\t? { oneOf: subBranches }\n\t\t\t\t: { anyOf: subBranches }\n\t\t\t: null,\n\t\terrors: allErrors,\n\t};\n}\n\n/**\n * Checks `sub ⊆ sup` for a sup that has branches (anyOf/oneOf).\n * At least one branch of sup must accept sub.\n *\n * Point 6 — Uses the sup's branch type for more precise messages.\n */\nexport function checkBranchedSup(\n\tsub: JSONSchema7Definition,\n\tsupBranches: JSONSchema7Definition[],\n\tengine: MergeEngine,\n\t_branchType: BranchType = \"anyOf\",\n): SubsetResult {\n\tfor (const branch of supBranches) {\n\t\t// Strip not + patterns confirmed by sampling before the merge\n\t\tlet effectiveBranch = branch;\n\t\tif (typeof sub !== \"boolean\" && typeof branch !== \"boolean\") {\n\t\t\tconst notResult = evaluateNot(sub, branch);\n\t\t\tif (notResult === false) continue; // This branch rejects sub\n\n\t\t\tif (notResult === true) {\n\t\t\t\teffectiveBranch = stripNotFromSup(sub, branch, true);\n\t\t\t\tif (\n\t\t\t\t\ttypeof effectiveBranch !== \"boolean\" &&\n\t\t\t\t\tObject.keys(effectiveBranch).length === 0\n\t\t\t\t) {\n\t\t\t\t\treturn { isSubset: true, merged: sub, errors: [] };\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\teffectiveBranch = stripNotFromSup(sub, branch, false);\n\t\t\t}\n\t\t\teffectiveBranch = stripPatternFromSup(sub, effectiveBranch);\n\t\t\teffectiveBranch = stripRedundantBoundsFromSup(sub, effectiveBranch);\n\t\t\teffectiveBranch = stripDependenciesFromSup(sub, effectiveBranch);\n\t\t}\n\t\tconst merged = engine.merge(sub, effectiveBranch);\n\t\tif (merged !== null) {\n\t\t\t// Fast path: skip normalize if merged already equals sub\n\t\t\tif (deepEqual(merged, sub)) {\n\t\t\t\treturn { isSubset: true, merged, errors: [] };\n\t\t\t}\n\t\t\tconst normalizedMerged = normalize(merged);\n\t\t\tif (\n\t\t\t\tdeepEqual(normalizedMerged, sub) ||\n\t\t\t\tengine.isEqual(normalizedMerged, sub)\n\t\t\t) {\n\t\t\t\treturn { isSubset: true, merged, errors: [] };\n\t\t\t}\n\t\t}\n\t}\n\n\t// Generate semantic errors by comparing sub with the original sup\n\tconst semanticErrors = computeSemanticErrors(\n\t\tsub,\n\t\t{ anyOf: supBranches } as JSONSchema7,\n\t\t\"\",\n\t);\n\n\treturn {\n\t\tisSubset: false,\n\t\tmerged: null,\n\t\terrors: semanticErrors,\n\t};\n}\n\n/**\n * Checks `sub ⊆ sup` for two atomic schemas (without anyOf/oneOf).\n * Uses `mergeOrThrow` to capture incompatibility errors.\n *\n * Uses `deepEqual` for structural comparison (with short-circuit\n * by reference and key counting).\n */\nexport function checkAtomic(\n\tsub: JSONSchema7Definition,\n\tsup: JSONSchema7Definition,\n\tengine: MergeEngine,\n): SubsetResult {\n\t// ── Resolve allOf in sup ──\n\t// Same as in isAtomicSubsetOf: pre-flatten allOf so that the stripping\n\t// pipeline can see keywords from all branches.\n\tsup = resolveSupAllOf(sup, engine);\n\n\t// ── evaluateNot pre-check (aligned with isAtomicSubsetOf) ──\n\tconst notResult =\n\t\ttypeof sub !== \"boolean\" && typeof sup !== \"boolean\"\n\t\t\t? evaluateNot(sub, sup)\n\t\t\t: null;\n\n\t// If evaluateNot confirms incompatibility → fail immediately\n\tif (notResult === false) {\n\t\tconst errors = computeSemanticErrors(sub, sup, \"\");\n\t\treturn { isSubset: false, merged: null, errors };\n\t}\n\n\t// Strip not + patterns confirmed by sampling before the merge,\n\t// same strategy as in isAtomicSubsetOf to avoid structural false negatives\n\t// caused by the conjunction of patterns as lookahead.\n\tlet effectiveSup = sup;\n\tif (typeof sub !== \"boolean\" && typeof sup !== \"boolean\") {\n\t\t// If the `not` is confirmed compatible → strip it before the merge\n\t\tif (notResult === true) {\n\t\t\teffectiveSup = stripNotFromSup(sub, sup, true);\n\t\t\tif (\n\t\t\t\ttypeof effectiveSup !== \"boolean\" &&\n\t\t\t\tObject.keys(effectiveSup).length === 0\n\t\t\t) {\n\t\t\t\treturn { isSubset: true, merged: sub, errors: [] };\n\t\t\t}\n\t\t} else {\n\t\t\teffectiveSup = stripNotFromSup(sub, sup, false);\n\t\t}\n\t\teffectiveSup = stripPatternFromSup(sub, effectiveSup);\n\t\teffectiveSup = stripRedundantBoundsFromSup(sub, effectiveSup);\n\t\teffectiveSup = stripDependenciesFromSup(sub, effectiveSup);\n\t}\n\n\ttry {\n\t\tconst merged = engine.mergeOrThrow(sub, effectiveSup);\n\n\t\t// Fast path: skip normalize if merged already equals sub\n\t\tif (deepEqual(merged, sub)) {\n\t\t\treturn { isSubset: true, merged, errors: [] };\n\t\t}\n\n\t\t// Strip vacuously-satisfied `false` properties added by the merge\n\t\t// from sup but absent in sub (vacuous truth: absent ⊆ forbidden).\n\t\tconst strippedMerged = stripVacuousFalseProperties(merged, sub);\n\t\tif (strippedMerged !== merged && deepEqual(strippedMerged, sub)) {\n\t\t\treturn { isSubset: true, merged: strippedMerged, errors: [] };\n\t\t}\n\n\t\tconst normalizedMerged = normalize(strippedMerged);\n\n\t\tif (\n\t\t\tdeepEqual(normalizedMerged, sub) ||\n\t\t\tengine.isEqual(normalizedMerged, sub)\n\t\t) {\n\t\t\treturn { isSubset: true, merged: normalizedMerged, errors: [] };\n\t\t}\n\n\t\t// ── Fallback: property-by-property for nested oneOf/anyOf ──\n\t\tif (tryNestedBranchingFallback(sub, effectiveSup, engine) === true) {\n\t\t\treturn { isSubset: true, merged: sub, errors: [] };\n\t\t}\n\n\t\tconst errors = computeSemanticErrors(sub, sup, \"\");\n\t\treturn { isSubset: false, merged: normalizedMerged, errors };\n\t} catch (_e) {\n\t\t// ── Fallback: property-by-property for nested oneOf/anyOf ──\n\t\tif (tryNestedBranchingFallback(sub, effectiveSup, engine) === true) {\n\t\t\treturn { isSubset: true, merged: sub, errors: [] };\n\t\t}\n\n\t\tconst errors = computeSemanticErrors(sub, sup, \"\");\n\t\treturn {\n\t\t\tisSubset: false,\n\t\t\tmerged: null,\n\t\t\terrors,\n\t\t};\n\t}\n}\n"],"names":["checkAtomic","checkBranchedSub","checkBranchedSup","getBranchesTyped","isAtomicSubsetOf","BRANCH_TRUE","branches","type","BRANCH_FALSE","atomicBranchCache","WeakMap","def","hasOwn","Array","isArray","anyOf","oneOf","cached","get","undefined","set","evaluateNot","sub","sup","isPlainObj","not","notSchema","properties","required","notProps","notRequired","subProps","subRequired","notPropKeys","Object","keys","hasIncompatibleProp","some","key","notPropDef","notProp","includes","subPropDef","subProp","deepEqual","const","enum","inNotEnum","v","noneInNotEnum","every","nv","allPropsMatch","notConst","subConst","allDisjoint","allExcluded","val","notVal","notType","subType","allIncompatible","branch","result","anyBranchMatches","subFormat","format","notFormat","stripNotFromSup","stripTopLevel","omitKeys","supProps","newProps","supPropDef","strippedProp","propNotResult","recursed","stripPatternFromSup","supObj","copied","ensureCopy","pattern","patResult","isPatternSubset","propsModified","propPatResult","items","subItems","supItems","itemsPatResult","stripRedundantBoundsFromSup","keysToStrip","minimum","exclusiveMinimum","push","maximum","exclusiveMaximum","length","resultObj","supProp","stripped","stripDependenciesFromSup","dependencies","supDeps","subHasAdditionalPropsFalse","additionalProperties","dep","triggerAlwaysPresent","triggerNeverProduced","allDepsRequired","d","depSchema","depRequired","depProps","allDepRequiredSatisfied","r","allDepPropsSatisfied","propKey","depPropDef","depPropKeys","dk","depVal","subVal","stripVacuousFalseProperties","merged","mergedProps","strippedProps","hasNestedBranching","schema","props","prop","isPropertySubsetOf","engine","subBranches","isArrayConstraintsSubset","minItems","maxItems","uniqueItems","isObjectSubsetByProperties","subIsObj","supIsObj","supRequired","addPropSchema","tryNestedBranchingFallback","resolveSupAllOf","allOf","_allOf","sibling","resolved","merge","supBranches","notResult","fmtResult","isFormatSubset","effectiveSup","strippedMerged","normalizedMerged","normalize","isEqual","effectiveBranch","strippedBranch","normalizedBranch","branchType","allErrors","allSubset","i","branchErrors","computeSemanticErrors","isSubset","errors","_branchType","semanticErrors","mergeOrThrow","_e"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/subset-checker.ts"],"sourcesContent":["import type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\nimport { isFormatSubset } from \"./format-validator.ts\";\nimport type { MergeEngine } from \"./merge-engine.ts\";\nimport { normalize } from \"./normalizer.ts\";\nimport { isPatternSubset } from \"./pattern-subset.ts\";\nimport { computeSemanticErrors } from \"./semantic-errors.ts\";\nimport type { SchemaError, SubsetResult } from \"./types.ts\";\nimport {\n\tdeepEqual,\n\thasOwn,\n\tisPlainObj,\n\tomitKeys,\n\tsemanticDeepEqual,\n} from \"./utils.ts\";\n\n// ─── Subset Checker ──────────────────────────────────────────────────────────\n//\n// Subset verification logic via the approach:\n// A ⊆ B ⟺ A ∩ B ≡ A\n//\n// Handles:\n// - Atomic schemas (no anyOf/oneOf)\n// - anyOf/oneOf in sub → each branch must be accepted by sup\n// - anyOf/oneOf in sup → at least one branch must accept sub\n// - Point 6: Distinguish anyOf / oneOf in diff messages\n// - Point 7: Extended `not` reasoning (evaluateNot)\n// - not.type, not.const, not.enum (existing)\n// - not with properties+required (1.1)\n// - not with anyOf/oneOf (1.2)\n// - not in sub (1.3)\n// - not.format (format-vs-format)\n//\n// Uses shared native helpers from `./utils` for optimal performance\n// (deepEqual, hasOwn, isPlainObj, omitKeys).\n\n// ─── Branch type ─────────────────────────────────────────────────────────────\n\n/**\n * Branch type detected in a schema.\n *\n * Point 6 — Distinguishes `anyOf` from `oneOf` to produce more precise\n * diff messages. `\"none\"` indicates an atomic schema (no branches).\n *\n * Note: the exclusivity semantics of `oneOf` are not verified\n * (this would be an NP-hard problem in general). The checker treats `oneOf`\n * like `anyOf` for subset checking, which is correct for the `sub ⊆ sup`\n * case but may produce false positives if the sub's branches overlap.\n */\nexport type BranchType = \"anyOf\" | \"oneOf\" | \"none\";\n\nexport interface BranchResult {\n\t/** The branches extracted from the schema */\n\tbranches: JSONSchema7Definition[];\n\t/** The detected branch type */\n\ttype: BranchType;\n}\n\n// ─── Branch extraction ───────────────────────────────────────────────────────\n\n// Pre-allocated singleton results for boolean schemas to avoid per-call allocations.\n// These are safe because the branches arrays are never mutated after creation.\nconst BRANCH_TRUE: BranchResult = { branches: [true], type: \"none\" };\nconst BRANCH_FALSE: BranchResult = { branches: [false], type: \"none\" };\n\n/**\n * WeakMap cache for atomic (no anyOf/oneOf) schema branch results.\n * Avoids allocating `{ branches: [def], type: \"none\" }` on every call\n * for the same schema object. Since normalized schemas are cached and\n * return the same reference, this cache hits frequently.\n */\nconst atomicBranchCache = new WeakMap<object, BranchResult>();\n\n/**\n * Extracts branches from a schema and the branch type.\n *\n * Returns the elements of `anyOf`/`oneOf` if they exist, otherwise returns\n * the schema itself in an array with type `\"none\"`.\n *\n * Point 6 — Distinguishes `anyOf` from `oneOf` in diff paths.\n *\n * Optimization: reuses pre-allocated objects for boolean cases\n * (true/false) and a WeakMap cache for atomic schemas to\n * avoid allocations on these frequent paths.\n */\nexport function getBranchesTyped(def: JSONSchema7Definition): BranchResult {\n\tif (typeof def === \"boolean\") {\n\t\treturn def ? BRANCH_TRUE : BRANCH_FALSE;\n\t}\n\tif (hasOwn(def, \"anyOf\") && Array.isArray(def.anyOf)) {\n\t\treturn { branches: def.anyOf, type: \"anyOf\" };\n\t}\n\tif (hasOwn(def, \"oneOf\") && Array.isArray(def.oneOf)) {\n\t\treturn { branches: def.oneOf, type: \"oneOf\" };\n\t}\n\t// Cache atomic results per schema object to avoid repeated allocations.\n\tlet cached = atomicBranchCache.get(def);\n\tif (cached === undefined) {\n\t\tcached = { branches: [def], type: \"none\" };\n\t\tatomicBranchCache.set(def, cached);\n\t}\n\treturn cached;\n}\n\n// ─── `not` reasoning (Point 7 — extended) ────────────────────────────────────\n\n/**\n * Extended `not` reasoning for common cases.\n *\n * Point 7 — Checks compatibility when `sup` and/or `sub` contain `not`:\n *\n * **Existing cases (not in sup):**\n * - `not.type`: excluded type vs sub's type\n * - `not.const`: excluded const vs sub's const\n * - `not.enum`: excluded values vs sub's enum\n *\n * **Added cases:**\n * - 1.1 — `not` with `properties` + `required`: verify that sub's properties\n * are incompatible with the `not`'s properties (different const/enum)\n * - 1.2 — `not` with `anyOf`/`oneOf`: `not(anyOf([A,B]))` ≡ `allOf([not(A), not(B)])`,\n * so sub must be incompatible with EACH branch\n * - 1.3 — `not` in `sub` (not only in `sup`): a sub with `not`\n * accepts a set too wide to be a subset of a concrete sup\n * - `not.format`: format-vs-format via `isFormatSubset`\n *\n * Conservative ternary contract:\n * - `true` → compatible (certain)\n * - `false` → incompatible (certain)\n * - `null` → undetermined (let the merge engine decide)\n *\n * When in doubt → `null`. NEVER return `true` without certainty.\n */\nfunction evaluateNot(\n\tsub: JSONSchema7Definition,\n\tsup: JSONSchema7Definition,\n): boolean | null {\n\tif (typeof sub === \"boolean\" || typeof sup === \"boolean\") return null;\n\n\t// ── 1.3 — `not` in sub (not in sup) ──\n\t// A `not` in sub is an additional restriction: it excludes values from\n\t// the set accepted by sub, which makes it potentially smaller — thus\n\t// more likely to be ⊆ sup, not less.\n\t// We let the merge engine decide: allOf(sub, sup) will preserve the `not`\n\t// from sub, and the comparison merged ≡ sub will give the correct result.\n\t// Exception: if both have `not`, we handle the identity case below.\n\n\t// Check `not` in sup\n\tif (hasOwn(sup, \"not\") && isPlainObj(sup.not)) {\n\t\tconst notSchema = sup.not as JSONSchema7;\n\n\t\t// ── 1.1 — Case not with properties + required ──\n\t\t// IMPORTANT: this check is placed BEFORE the not.type check because when\n\t\t// the not has both `type` and `properties`, the not.type check alone\n\t\t// would produce a false negative (e.g. sub type=object and not type=object\n\t\t// would return false, but the properties could be incompatible\n\t\t// which would make sub compatible with the not).\n\t\t// If not contains properties with const/enum and required,\n\t\t// verify that sub's properties are incompatible with the not's properties.\n\t\tif (isPlainObj(notSchema.properties) && Array.isArray(notSchema.required)) {\n\t\t\tconst notProps = notSchema.properties as Record<\n\t\t\t\tstring,\n\t\t\t\tJSONSchema7Definition\n\t\t\t>;\n\t\t\tconst notRequired = notSchema.required as string[];\n\n\t\t\t// sub must have properties for us to compare\n\t\t\tif (isPlainObj(sub.properties)) {\n\t\t\t\tconst subProps = sub.properties as Record<\n\t\t\t\t\tstring,\n\t\t\t\t\tJSONSchema7Definition\n\t\t\t\t>;\n\t\t\t\tconst subRequired = Array.isArray(sub.required)\n\t\t\t\t\t? (sub.required as string[])\n\t\t\t\t\t: [];\n\t\t\t\tconst notPropKeys = Object.keys(notProps);\n\n\t\t\t\t// For sub to be compatible with not(schema),\n\t\t\t\t// it suffices that at least ONE property of the not is incompatible with sub.\n\t\t\t\t// This means that sub can never validate the schema inside not.\n\t\t\t\tconst hasIncompatibleProp = notPropKeys.some((key) => {\n\t\t\t\t\tconst notPropDef = notProps[key];\n\t\t\t\t\tif (typeof notPropDef === \"boolean\") return false;\n\t\t\t\t\tconst notProp = notPropDef as JSONSchema7;\n\n\t\t\t\t\t// If the property is required in not but NOT in sub.required\n\t\t\t\t\t// and it doesn't exist in sub.properties → sub may not\n\t\t\t\t\t// have this property → the not schema wouldn't match → compatible\n\t\t\t\t\tif (\n\t\t\t\t\t\tnotRequired.includes(key) &&\n\t\t\t\t\t\t!subRequired.includes(key) &&\n\t\t\t\t\t\t!hasOwn(subProps, key)\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn true; // Property absent from sub → not doesn't match\n\t\t\t\t\t}\n\n\t\t\t\t\t// Compare the const/enum of the property\n\t\t\t\t\tif (!hasOwn(subProps, key)) return false;\n\t\t\t\t\tconst subPropDef = subProps[key];\n\t\t\t\t\tif (typeof subPropDef === \"boolean\") return false;\n\t\t\t\t\tconst subProp = subPropDef as JSONSchema7;\n\n\t\t\t\t\t// not.prop has a const, sub.prop has a different const → incompatible for this prop\n\t\t\t\t\tif (hasOwn(notProp, \"const\") && hasOwn(subProp, \"const\")) {\n\t\t\t\t\t\tif (!deepEqual(notProp.const, subProp.const)) {\n\t\t\t\t\t\t\treturn true; // Different consts → sub doesn't match the not\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// not.prop has an enum, sub.prop has a const or enum with no value\n\t\t\t\t\t// in not.enum → incompatible for this prop\n\t\t\t\t\tif (hasOwn(notProp, \"enum\") && Array.isArray(notProp.enum)) {\n\t\t\t\t\t\tif (hasOwn(subProp, \"const\")) {\n\t\t\t\t\t\t\tconst inNotEnum = notProp.enum.some((v) =>\n\t\t\t\t\t\t\t\tdeepEqual(v, subProp.const),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tif (!inNotEnum) return true; // sub.const absent from not.enum\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (hasOwn(subProp, \"enum\") && Array.isArray(subProp.enum)) {\n\t\t\t\t\t\t\tconst noneInNotEnum = subProp.enum.every(\n\t\t\t\t\t\t\t\t(v) => !notProp.enum?.some((nv) => deepEqual(v, nv)),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tif (noneInNotEnum) return true; // No value from sub.enum in not.enum\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn false;\n\t\t\t\t});\n\n\t\t\t\tif (hasIncompatibleProp) return true;\n\n\t\t\t\t// Inverse check: if ALL properties of the not match sub\n\t\t\t\t// exactly (same const, sub has the not's required), then sub VIOLATES the not\n\t\t\t\tconst allPropsMatch = notPropKeys.every((key) => {\n\t\t\t\t\tconst notPropDef = notProps[key];\n\t\t\t\t\tif (typeof notPropDef === \"boolean\") return true;\n\t\t\t\t\tconst notProp = notPropDef as JSONSchema7;\n\n\t\t\t\t\t// The property must be in sub.required if it is in not.required\n\t\t\t\t\tif (notRequired.includes(key) && !subRequired.includes(key))\n\t\t\t\t\t\treturn false;\n\t\t\t\t\tif (!hasOwn(subProps, key)) return false;\n\t\t\t\t\tconst subPropDef = subProps[key];\n\t\t\t\t\tif (typeof subPropDef === \"boolean\") return true;\n\t\t\t\t\tconst subProp = subPropDef as JSONSchema7;\n\n\t\t\t\t\t// Check const match\n\t\t\t\t\tif (hasOwn(notProp, \"const\") && hasOwn(subProp, \"const\")) {\n\t\t\t\t\t\treturn deepEqual(notProp.const, subProp.const);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check enum inclusion\n\t\t\t\t\tif (hasOwn(notProp, \"enum\") && Array.isArray(notProp.enum)) {\n\t\t\t\t\t\tif (hasOwn(subProp, \"const\")) {\n\t\t\t\t\t\t\treturn notProp.enum.some((v) => deepEqual(v, subProp.const));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (hasOwn(subProp, \"enum\") && Array.isArray(subProp.enum)) {\n\t\t\t\t\t\t\t// All values of sub.enum are in not.enum\n\t\t\t\t\t\t\treturn subProp.enum.every((v) =>\n\t\t\t\t\t\t\t\tnotProp.enum?.some((nv) => deepEqual(v, nv)),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn false; // Undetermined for this property\n\t\t\t\t});\n\n\t\t\t\tif (allPropsMatch) return false; // sub matches the not exactly → incompatible\n\t\t\t}\n\t\t}\n\n\t\t// ── Case not.const ──\n\t\t// IMPORTANT: this check is placed BEFORE not.type because when the not has\n\t\t// both `type` and `const`, the not.type check alone would produce a\n\t\t// false negative (e.g. sub type=string const=\"active\" and not type=string\n\t\t// const=\"deleted\" → the type check would return false due to same type,\n\t\t// even though the consts are different → compatible).\n\t\tif (hasOwn(notSchema, \"const\") && hasOwn(sub, \"const\")) {\n\t\t\tconst notConst = notSchema.const;\n\t\t\tconst subConst = sub.const;\n\t\t\tif (deepEqual(subConst, notConst)) return false;\n\t\t\treturn true;\n\t\t}\n\n\t\t// ── Case not.const + sub.enum ──\n\t\t// When sub has enum and not has const, check that the forbidden const\n\t\t// is not in sub's enum values. If none match → compatible.\n\t\tif (hasOwn(notSchema, \"const\") && Array.isArray(sub.enum)) {\n\t\t\tconst notConst = notSchema.const;\n\t\t\tconst allDisjoint = sub.enum.every((v) => !deepEqual(v, notConst));\n\t\t\tif (allDisjoint) return true;\n\t\t\t// At least one enum value equals the forbidden const → incompatible\n\t\t\treturn false;\n\t\t}\n\n\t\t// ── Case not.enum ──\n\t\t// Also placed BEFORE not.type for the same reason.\n\t\tif (\n\t\t\thasOwn(notSchema, \"enum\") &&\n\t\t\tArray.isArray(notSchema.enum) &&\n\t\t\thasOwn(sub, \"enum\") &&\n\t\t\tArray.isArray(sub.enum)\n\t\t) {\n\t\t\t// All values of sub.enum must be absent from not.enum\n\t\t\tconst allExcluded = sub.enum.every(\n\t\t\t\t(val) => !notSchema.enum?.some((notVal) => deepEqual(val, notVal)),\n\t\t\t);\n\t\t\tif (allExcluded) return true;\n\t\t\t// Some values of sub are in not.enum → not automatically false,\n\t\t\t// the merge engine can still handle it\n\t\t}\n\n\t\t// ── Case not.enum + sub.const ──\n\t\t// When sub has const and not has enum, check that sub's const is not\n\t\t// in the forbidden enum values.\n\t\tif (\n\t\t\thasOwn(notSchema, \"enum\") &&\n\t\t\tArray.isArray(notSchema.enum) &&\n\t\t\thasOwn(sub, \"const\")\n\t\t) {\n\t\t\tconst inNotEnum = notSchema.enum.some((v) => deepEqual(v, sub.const));\n\t\t\tif (!inNotEnum) return true;\n\t\t\treturn false;\n\t\t}\n\n\t\t// ── Case not.type ──\n\t\t// Placed AFTER not.const, not.enum and properties+required to avoid\n\t\t// short-circuiting cases where the not has more specific constraints.\n\t\t// The type check alone is a fallback for simple not schemas\n\t\t// (e.g. { not: { type: \"string\" } }).\n\t\tif (hasOwn(notSchema, \"type\") && hasOwn(sub, \"type\")) {\n\t\t\tconst notType = notSchema.type;\n\t\t\tconst subType = sub.type;\n\n\t\t\t// If both are simple strings\n\t\t\tif (typeof notType === \"string\" && typeof subType === \"string\") {\n\t\t\t\t// Only return if the not does NOT have more specific constraints\n\t\t\t\t// (const, enum, properties) that should have been handled above\n\t\t\t\tif (\n\t\t\t\t\t!hasOwn(notSchema, \"const\") &&\n\t\t\t\t\t!hasOwn(notSchema, \"enum\") &&\n\t\t\t\t\t!isPlainObj(notSchema.properties)\n\t\t\t\t) {\n\t\t\t\t\tif (subType === notType) return false; // Incompatible: sub is exactly the excluded type\n\t\t\t\t\treturn true; // Compatible: sub is a different type from the excluded type\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If notType is an array, sub.type must not be in it\n\t\t\tif (Array.isArray(notType) && typeof subType === \"string\") {\n\t\t\t\tif (notType.includes(subType)) return false;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\t// ── 1.2 — Case not with anyOf / oneOf ──\n\t\t// not(anyOf([A, B])) ≡ allOf([not(A), not(B)])\n\t\t// For sub ⊆ not(anyOf(...)), sub must be incompatible with EACH branch.\n\t\tif (hasOwn(notSchema, \"anyOf\") && Array.isArray(notSchema.anyOf)) {\n\t\t\tconst branches = notSchema.anyOf as JSONSchema7Definition[];\n\t\t\t// For each branch of not.anyOf, verify that sub is incompatible\n\t\t\tconst allIncompatible = branches.every((branch) => {\n\t\t\t\tif (typeof branch === \"boolean\") return !branch; // not(true) = nothing, not(false) = everything\n\t\t\t\t// Create a virtual sup { not: branch } and verify recursively\n\t\t\t\tconst result = evaluateNot(sub, { not: branch });\n\t\t\t\t// result = true → sub is compatible with not(branch) → sub ⊄ branch → OK\n\t\t\t\t// result = false → sub is incompatible with not(branch) → sub ⊆ branch → not OK\n\t\t\t\t// result = null → undetermined\n\t\t\t\treturn result === true;\n\t\t\t});\n\t\t\tif (allIncompatible) return true;\n\n\t\t\t// Check if at least one branch accepts sub → incompatible with not(anyOf)\n\t\t\tconst anyBranchMatches = branches.some((branch) => {\n\t\t\t\tif (typeof branch === \"boolean\") return branch;\n\t\t\t\tconst result = evaluateNot(sub, { not: branch });\n\t\t\t\treturn result === false; // sub is incompatible with not(branch) → sub ⊆ branch\n\t\t\t});\n\t\t\tif (anyBranchMatches) return false;\n\t\t}\n\n\t\t// Same logic for oneOf (in the not context, treated like anyOf)\n\t\tif (hasOwn(notSchema, \"oneOf\") && Array.isArray(notSchema.oneOf)) {\n\t\t\tconst branches = notSchema.oneOf as JSONSchema7Definition[];\n\t\t\tconst allIncompatible = branches.every((branch) => {\n\t\t\t\tif (typeof branch === \"boolean\") return !branch;\n\t\t\t\tconst result = evaluateNot(sub, { not: branch });\n\t\t\t\treturn result === true;\n\t\t\t});\n\t\t\tif (allIncompatible) return true;\n\n\t\t\tconst anyBranchMatches = branches.some((branch) => {\n\t\t\t\tif (typeof branch === \"boolean\") return branch;\n\t\t\t\tconst result = evaluateNot(sub, { not: branch });\n\t\t\t\treturn result === false;\n\t\t\t});\n\t\t\tif (anyBranchMatches) return false;\n\t\t}\n\n\t\t// ── Case not.format (format-vs-format only) ──\n\t\t// If not has a format and sub also does, check compatibility\n\t\tif (hasOwn(notSchema, \"format\") && hasOwn(sub, \"format\")) {\n\t\t\tconst subFormat = sub.format as string;\n\t\t\tconst notFormat = notSchema.format as string;\n\t\t\tif (subFormat === notFormat) return false; // Incompatible: sub has exactly the excluded format\n\t\t\t// Different formats → compatible (conservative approximation)\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t// Check `not` in sub AND in sup (identity: { not: X } ⊆ { not: X })\n\tif (hasOwn(sub, \"not\") && hasOwn(sup, \"not\")) {\n\t\tif (deepEqual(sub.not, sup.not)) return true;\n\t}\n\n\treturn null; // No opinion → let the merge engine decide\n}\n\n// ─── Not stripping helper ────────────────────────────────────────────────────\n\n/**\n * Removes the `not` keyword from a schema to allow a clean merge\n * when `evaluateNot` has already confirmed compatibility.\n *\n * Also handles `not` nested in `properties`: if a property of `sup`\n * has a `not` that is compatible with the corresponding property of\n * `sub`, it is removed as well.\n *\n * Returns the cleaned schema, or `null` if the schema is empty after removal.\n */\nfunction stripNotFromSup(\n\tsub: JSONSchema7Definition,\n\tsup: JSONSchema7Definition,\n\tstripTopLevel: boolean = true,\n): JSONSchema7Definition {\n\tif (typeof sup === \"boolean\" || typeof sub === \"boolean\") return sup;\n\n\tlet result = sup as JSONSchema7;\n\n\t// ── Remove top-level `not` (only if confirmed) ──\n\tif (stripTopLevel && hasOwn(result, \"not\")) {\n\t\tresult = omitKeys(result as unknown as Record<string, unknown>, [\n\t\t\t\"not\",\n\t\t]) as JSONSchema7;\n\t}\n\n\t// ── Recursively remove `not` in common properties ──\n\t// For each shared property: if the sup property has a direct `not`,\n\t// evaluate and strip it. Then recurse deeper into nested objects so\n\t// that `not` constraints at any depth are handled.\n\tif (\n\t\tisPlainObj(result.properties) &&\n\t\tisPlainObj((sub as JSONSchema7).properties)\n\t) {\n\t\tconst subProps = (sub as JSONSchema7).properties as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition\n\t\t>;\n\t\tconst supProps = result.properties as Record<string, JSONSchema7Definition>;\n\t\tlet newProps: Record<string, JSONSchema7Definition> | undefined;\n\n\t\tfor (const key of Object.keys(supProps)) {\n\t\t\tconst supPropDef = supProps[key];\n\t\t\tconst subPropDef = subProps[key];\n\t\t\tif (\n\t\t\t\tsupPropDef === undefined ||\n\t\t\t\tsubPropDef === undefined ||\n\t\t\t\ttypeof supPropDef === \"boolean\" ||\n\t\t\t\ttypeof subPropDef === \"boolean\"\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet strippedProp: JSONSchema7Definition = supPropDef;\n\n\t\t\t// Direct `not` on this property — evaluate and strip if confirmed\n\t\t\tif (hasOwn(supPropDef, \"not\")) {\n\t\t\t\tconst propNotResult = evaluateNot(subPropDef, supPropDef);\n\t\t\t\tif (propNotResult === true) {\n\t\t\t\t\tstrippedProp = omitKeys(\n\t\t\t\t\t\tsupPropDef as unknown as Record<string, unknown>,\n\t\t\t\t\t\t[\"not\"],\n\t\t\t\t\t) as JSONSchema7Definition;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Recurse into nested object properties (handles `not` at any depth)\n\t\t\tconst recursed = stripNotFromSup(subPropDef, strippedProp, false);\n\t\t\tif (recursed !== supPropDef) {\n\t\t\t\tif (!newProps) newProps = { ...supProps };\n\t\t\t\tnewProps[key] = recursed;\n\t\t\t}\n\t\t}\n\n\t\tif (newProps) {\n\t\t\tresult = { ...result, properties: newProps };\n\t\t}\n\t}\n\n\treturn result;\n}\n\n// ─── Pattern stripping helper ────────────────────────────────────────────────\n\n/**\n * Removes the `pattern` from `sup` when `isPatternSubset` has confirmed that\n * sub.pattern ⊆ sup.pattern via sampling.\n *\n * Works like `stripNotFromSup`: we remove the sup constraint that is already\n * satisfied by sub, to prevent the merge engine from producing a combined\n * pattern (lookahead conjunction) structurally different from sub's pattern,\n * which would cause a false negative.\n *\n * Recurses into `properties` to handle nested patterns.\n *\n * @param sub The sub schema (used to extract patterns to compare)\n * @param sup The sup schema from which confirmed patterns are removed\n * @returns The cleaned sup schema\n */\nfunction stripPatternFromSup(\n\tsub: JSONSchema7Definition,\n\tsup: JSONSchema7Definition,\n): JSONSchema7Definition {\n\tif (typeof sub === \"boolean\" || typeof sup === \"boolean\") return sup;\n\n\tconst supObj: JSONSchema7 = sup;\n\n\t// Lazy copy-on-write: only create a copy when the first mutation is needed.\n\tlet result: JSONSchema7 = supObj;\n\tlet copied = false;\n\n\tfunction ensureCopy(): JSONSchema7 {\n\t\tif (!copied) {\n\t\t\tresult = { ...supObj };\n\t\t\tcopied = true;\n\t\t}\n\t\treturn result;\n\t}\n\n\t// ── Top-level pattern ──\n\tif (\n\t\thasOwn(result, \"pattern\") &&\n\t\thasOwn(sub, \"pattern\") &&\n\t\tresult.pattern !== (sub as JSONSchema7).pattern\n\t) {\n\t\tconst patResult = isPatternSubset(\n\t\t\t(sub as JSONSchema7).pattern as string,\n\t\t\tresult.pattern as string,\n\t\t);\n\t\tif (patResult === true) {\n\t\t\tresult = omitKeys(ensureCopy() as unknown as Record<string, unknown>, [\n\t\t\t\t\"pattern\",\n\t\t\t]) as JSONSchema7;\n\t\t\tcopied = true;\n\t\t}\n\t}\n\n\t// ── Patterns in common properties ──\n\tif (\n\t\tisPlainObj(result.properties) &&\n\t\tisPlainObj((sub as JSONSchema7).properties)\n\t) {\n\t\tconst subProps = (sub as JSONSchema7).properties as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition\n\t\t>;\n\t\tconst supProps = result.properties as Record<string, JSONSchema7Definition>;\n\t\tlet propsModified = false;\n\t\tlet newProps: Record<string, JSONSchema7Definition> | undefined;\n\n\t\tfor (const key of Object.keys(supProps)) {\n\t\t\tconst supPropDef = supProps[key];\n\t\t\tconst subPropDef = subProps[key];\n\t\t\tif (\n\t\t\t\tsupPropDef !== undefined &&\n\t\t\t\tsubPropDef !== undefined &&\n\t\t\t\ttypeof supPropDef !== \"boolean\" &&\n\t\t\t\ttypeof subPropDef !== \"boolean\" &&\n\t\t\t\thasOwn(supPropDef, \"pattern\") &&\n\t\t\t\thasOwn(subPropDef, \"pattern\") &&\n\t\t\t\tsupPropDef.pattern !== subPropDef.pattern\n\t\t\t) {\n\t\t\t\tconst propPatResult = isPatternSubset(\n\t\t\t\t\t(subPropDef as JSONSchema7).pattern as string,\n\t\t\t\t\t(supPropDef as JSONSchema7).pattern as string,\n\t\t\t\t);\n\t\t\t\tif (propPatResult === true) {\n\t\t\t\t\tif (!newProps) newProps = { ...supProps };\n\t\t\t\t\tnewProps[key] = omitKeys(\n\t\t\t\t\t\tsupPropDef as unknown as Record<string, unknown>,\n\t\t\t\t\t\t[\"pattern\"],\n\t\t\t\t\t) as JSONSchema7Definition;\n\t\t\t\t\tpropsModified = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (propsModified && newProps) {\n\t\t\tensureCopy().properties = newProps;\n\t\t}\n\t}\n\n\t// ── Pattern in items (single schema) ──\n\tif (\n\t\tisPlainObj(result.items) &&\n\t\ttypeof result.items !== \"boolean\" &&\n\t\tisPlainObj((sub as JSONSchema7).items) &&\n\t\ttypeof (sub as JSONSchema7).items !== \"boolean\"\n\t) {\n\t\tconst subItems = (sub as JSONSchema7).items as JSONSchema7;\n\t\tconst supItems = result.items as JSONSchema7;\n\t\tif (\n\t\t\thasOwn(supItems, \"pattern\") &&\n\t\t\thasOwn(subItems, \"pattern\") &&\n\t\t\tsupItems.pattern !== subItems.pattern\n\t\t) {\n\t\t\tconst itemsPatResult = isPatternSubset(\n\t\t\t\tsubItems.pattern as string,\n\t\t\t\tsupItems.pattern as string,\n\t\t\t);\n\t\t\tif (itemsPatResult === true) {\n\t\t\t\tensureCopy().items = omitKeys(\n\t\t\t\t\tsupItems as unknown as Record<string, unknown>,\n\t\t\t\t\t[\"pattern\"],\n\t\t\t\t) as JSONSchema7Definition;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Strips redundant numeric bounds from `sup` when `sub` already has a stricter\n * exclusive bound that implies the inclusive bound.\n *\n * For example, if sub has `exclusiveMinimum: 5` and sup has `minimum: 5`,\n * the sup's `minimum` is redundant because `x > 5` ⊂ `x ≥ 5`.\n */\nfunction stripRedundantBoundsFromSup(\n\tsub: JSONSchema7Definition,\n\tsup: JSONSchema7Definition,\n): JSONSchema7Definition {\n\tif (typeof sub === \"boolean\" || typeof sup === \"boolean\") return sup;\n\n\tconst keysToStrip: string[] = [];\n\n\t// sub.exclusiveMinimum >= sup.minimum → sup.minimum is redundant\n\tif (\n\t\tsup.minimum !== undefined &&\n\t\tsub.minimum === undefined &&\n\t\tsub.exclusiveMinimum !== undefined &&\n\t\tsub.exclusiveMinimum >= sup.minimum\n\t) {\n\t\tkeysToStrip.push(\"minimum\");\n\t}\n\n\t// sub.exclusiveMaximum <= sup.maximum → sup.maximum is redundant\n\tif (\n\t\tsup.maximum !== undefined &&\n\t\tsub.maximum === undefined &&\n\t\tsub.exclusiveMaximum !== undefined &&\n\t\tsub.exclusiveMaximum <= sup.maximum\n\t) {\n\t\tkeysToStrip.push(\"maximum\");\n\t}\n\n\t// sub.minimum > sup.exclusiveMinimum → sup.exclusiveMinimum is redundant\n\tif (\n\t\tsup.exclusiveMinimum !== undefined &&\n\t\tsub.exclusiveMinimum === undefined &&\n\t\tsub.minimum !== undefined &&\n\t\tsub.minimum > sup.exclusiveMinimum\n\t) {\n\t\tkeysToStrip.push(\"exclusiveMinimum\");\n\t}\n\n\t// sub.maximum < sup.exclusiveMaximum → sup.exclusiveMaximum is redundant\n\tif (\n\t\tsup.exclusiveMaximum !== undefined &&\n\t\tsub.exclusiveMaximum === undefined &&\n\t\tsub.maximum !== undefined &&\n\t\tsub.maximum < sup.exclusiveMaximum\n\t) {\n\t\tkeysToStrip.push(\"exclusiveMaximum\");\n\t}\n\n\t// ── Top-level stripping ────────────────────────────────────\n\tlet result: JSONSchema7Definition =\n\t\tkeysToStrip.length > 0\n\t\t\t? (omitKeys(\n\t\t\t\t\tsup as unknown as Record<string, unknown>,\n\t\t\t\t\tkeysToStrip,\n\t\t\t\t) as JSONSchema7)\n\t\t\t: sup;\n\n\t// ── Recurse into properties ────────────────────────────────\n\tconst resultObj = result as JSONSchema7;\n\tif (isPlainObj(resultObj.properties) && isPlainObj(sub.properties)) {\n\t\tconst subProps = sub.properties as Record<string, JSONSchema7Definition>;\n\t\tconst supProps = resultObj.properties as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition\n\t\t>;\n\t\tlet newProps: Record<string, JSONSchema7Definition> | undefined;\n\n\t\tfor (const key of Object.keys(supProps)) {\n\t\t\tconst supProp = supProps[key];\n\t\t\tconst subProp = subProps[key];\n\n\t\t\tif (\n\t\t\t\tsupProp !== undefined &&\n\t\t\t\tsubProp !== undefined &&\n\t\t\t\ttypeof supProp !== \"boolean\" &&\n\t\t\t\ttypeof subProp !== \"boolean\"\n\t\t\t) {\n\t\t\t\tconst stripped = stripRedundantBoundsFromSup(subProp, supProp);\n\t\t\t\tif (stripped !== supProp) {\n\t\t\t\t\tif (!newProps) newProps = { ...supProps };\n\t\t\t\t\tnewProps[key] = stripped;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (newProps) {\n\t\t\tresult = { ...resultObj, properties: newProps };\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Strips `dependencies` from `sup` when every dependency is semantically\n * satisfied by `sub`'s structure:\n * - Array-form deps: all dependent properties are in `sub.required`, OR the\n * trigger property is never produced by `sub` (not in properties + not required).\n * - Schema-form deps: only stripped when the trigger is never produced by `sub`.\n */\nfunction stripDependenciesFromSup(\n\tsub: JSONSchema7Definition,\n\tsup: JSONSchema7Definition,\n): JSONSchema7Definition {\n\tif (typeof sub === \"boolean\" || typeof sup === \"boolean\") return sup;\n\tif (!isPlainObj(sup.dependencies)) return sup;\n\n\tconst supDeps = sup.dependencies as Record<\n\t\tstring,\n\t\tJSONSchema7Definition | string[]\n\t>;\n\tconst subRequired = Array.isArray(sub.required) ? sub.required : [];\n\tconst subProps = isPlainObj(sub.properties)\n\t\t? (sub.properties as Record<string, JSONSchema7Definition>)\n\t\t: {};\n\tconst subHasAdditionalPropsFalse = sub.additionalProperties === false;\n\n\tfor (const key of Object.keys(supDeps)) {\n\t\tconst dep = supDeps[key];\n\n\t\tif (Array.isArray(dep)) {\n\t\t\t// Array-form: satisfied if all dependents are always required,\n\t\t\t// OR the trigger is never produced by sub\n\t\t\tconst triggerAlwaysPresent =\n\t\t\t\tsubRequired.includes(key) || hasOwn(subProps, key);\n\t\t\tconst triggerNeverProduced =\n\t\t\t\t!hasOwn(subProps, key) &&\n\t\t\t\t!subRequired.includes(key) &&\n\t\t\t\tsubHasAdditionalPropsFalse;\n\n\t\t\tif (triggerNeverProduced) continue;\n\n\t\t\tif (triggerAlwaysPresent) {\n\t\t\t\tconst allDepsRequired = dep.every((d) => subRequired.includes(d));\n\t\t\t\tif (allDepsRequired) continue;\n\t\t\t}\n\n\t\t\t// This dependency is not satisfied — cannot strip\n\t\t\treturn sup;\n\t\t}\n\n\t\t// Schema-form: strip if trigger is never produced OR if sub satisfies the dep schema\n\t\tif (isPlainObj(dep)) {\n\t\t\tconst triggerNeverProduced =\n\t\t\t\t!hasOwn(subProps, key) &&\n\t\t\t\t!subRequired.includes(key) &&\n\t\t\t\tsubHasAdditionalPropsFalse;\n\t\t\tif (triggerNeverProduced) continue;\n\n\t\t\t// The trigger exists in sub — check if sub structurally satisfies the dep schema.\n\t\t\t// A schema-form dependency { A: { required: ['B'], properties: { B: S } } }\n\t\t\t// means \"if A is present → the object must also match the dep schema\".\n\t\t\t// If sub already satisfies the dep schema (all dep.required are in sub.required,\n\t\t\t// and all dep.properties exist in sub.properties), the dependency is trivially met.\n\t\t\tconst depSchema = dep as JSONSchema7;\n\t\t\tconst depRequired = Array.isArray(depSchema.required)\n\t\t\t\t? (depSchema.required as string[])\n\t\t\t\t: [];\n\t\t\tconst depProps = isPlainObj(depSchema.properties)\n\t\t\t\t? (depSchema.properties as Record<string, JSONSchema7Definition>)\n\t\t\t\t: {};\n\n\t\t\tconst allDepRequiredSatisfied = depRequired.every((r) =>\n\t\t\t\tsubRequired.includes(r),\n\t\t\t);\n\t\t\tif (!allDepRequiredSatisfied) return sup;\n\n\t\t\tconst allDepPropsSatisfied = Object.keys(depProps).every((propKey) => {\n\t\t\t\tif (!hasOwn(subProps, propKey)) return false;\n\t\t\t\t// Check that sub's property is at least as narrow as the dep's property.\n\t\t\t\t// For simple cases (same type, sub has stricter constraints), deepEqual\n\t\t\t\t// or a structural check suffices. We compare the dep property against\n\t\t\t\t// the sub property: if sub.properties[k] has all constraints from\n\t\t\t\t// dep.properties[k], the dep is satisfied for that property.\n\t\t\t\tconst subPropDef = subProps[propKey];\n\t\t\t\tconst depPropDef = depProps[propKey];\n\t\t\t\tif (subPropDef === undefined || depPropDef === undefined) return false;\n\t\t\t\tif (\n\t\t\t\t\ttypeof subPropDef === \"boolean\" ||\n\t\t\t\t\ttypeof depPropDef === \"boolean\"\n\t\t\t\t) {\n\t\t\t\t\treturn subPropDef === depPropDef;\n\t\t\t\t}\n\t\t\t\t// If the sub property is structurally equal to or narrower than the dep property,\n\t\t\t\t// the dependency is satisfied. A simple heuristic: check that every keyword\n\t\t\t\t// in the dep property also exists in the sub property with an equal or stricter value.\n\t\t\t\tconst depPropKeys = Object.keys(depPropDef);\n\t\t\t\treturn depPropKeys.every((dk) => {\n\t\t\t\t\tconst depVal = (depPropDef as Record<string, unknown>)[dk];\n\t\t\t\t\tconst subVal = (subPropDef as Record<string, unknown>)[dk];\n\t\t\t\t\tif (subVal === undefined) return false;\n\t\t\t\t\t// For numeric constraints, sub must be at least as strict\n\t\t\t\t\tif (typeof depVal === \"number\" && typeof subVal === \"number\") {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tdk === \"minLength\" ||\n\t\t\t\t\t\t\tdk === \"minimum\" ||\n\t\t\t\t\t\t\tdk === \"exclusiveMinimum\" ||\n\t\t\t\t\t\t\tdk === \"minItems\" ||\n\t\t\t\t\t\t\tdk === \"minProperties\"\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn subVal >= depVal;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tdk === \"maxLength\" ||\n\t\t\t\t\t\t\tdk === \"maximum\" ||\n\t\t\t\t\t\t\tdk === \"exclusiveMaximum\" ||\n\t\t\t\t\t\t\tdk === \"maxItems\" ||\n\t\t\t\t\t\t\tdk === \"maxProperties\"\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn subVal <= depVal;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn deepEqual(depVal, subVal);\n\t\t\t\t});\n\t\t\t});\n\t\t\tif (!allDepPropsSatisfied) return sup;\n\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Unknown form — cannot strip safely\n\t\treturn sup;\n\t}\n\n\t// All dependencies satisfied → strip\n\treturn omitKeys(sup as unknown as Record<string, unknown>, [\n\t\t\"dependencies\",\n\t]) as JSONSchema7;\n}\n\n// ─── Vacuous false-property stripping ────────────────────────────────────────\n//\n// In JSON Schema, `properties: { x: false }` means \"if x is present, it must\n// validate against `false` (impossible)\". If x is **absent** from the instance,\n// the constraint is **trivially satisfied** (vacuous truth).\n//\n// After merge(sub, sup), the merge engine may add `false`-schema properties\n// from sup into the merged result even when sub doesn't define those properties.\n// This causes `deepEqual(merged, sub)` to fail — a false negative.\n//\n// `stripVacuousFalseProperties` removes those vacuously-satisfied `false`\n// properties from `merged` so that the structural comparison succeeds.\n\n/**\n * Strips `false`-schema properties from `merged` that are absent in `sub`.\n *\n * A property `{ key: false }` in merged that doesn't exist in sub's properties\n * is vacuously satisfied — sub never produces that key, so the \"impossible\"\n * constraint has no effect. Removing it lets `deepEqual(merged, sub)` succeed.\n *\n * @returns A new schema with vacuous `false` properties removed, or the\n * original `merged` if no stripping was needed.\n */\nfunction stripVacuousFalseProperties(\n\tmerged: JSONSchema7Definition,\n\tsub: JSONSchema7Definition,\n): JSONSchema7Definition {\n\tif (typeof merged === \"boolean\" || typeof sub === \"boolean\") return merged;\n\tif (!isPlainObj(merged.properties)) return merged;\n\n\tconst mergedProps = merged.properties as Record<\n\t\tstring,\n\t\tJSONSchema7Definition\n\t>;\n\tconst subProps = (isPlainObj(sub.properties) ? sub.properties : {}) as Record<\n\t\tstring,\n\t\tJSONSchema7Definition\n\t>;\n\n\tlet strippedProps: Record<string, JSONSchema7Definition> | null = null;\n\n\tfor (const key of Object.keys(mergedProps)) {\n\t\tif (mergedProps[key] === false && !hasOwn(subProps, key)) {\n\t\t\t// Lazily copy on first strip\n\t\t\tif (strippedProps === null) {\n\t\t\t\tstrippedProps = { ...mergedProps };\n\t\t\t}\n\t\t\tdelete strippedProps[key];\n\t\t}\n\t}\n\n\tif (strippedProps === null) return merged;\n\n\t// Return a new schema with the cleaned properties\n\tconst result = { ...merged, properties: strippedProps };\n\n\t// If properties is now empty and sub doesn't have properties, remove it\n\tif (Object.keys(strippedProps).length === 0 && !isPlainObj(sub.properties)) {\n\t\tdelete (result as Record<string, unknown>).properties;\n\t}\n\n\treturn result;\n}\n\n// ─── Nested branching fallback ───────────────────────────────────────────────\n//\n// The merge engine (`@x0k/json-schema-merge`) cannot resolve `allOf` over\n// `oneOf`/`anyOf` inside properties — it either throws or produces garbage\n// like `{ type: \"string\", oneOf: [...] }`.\n//\n// When the merge-based check fails (null or merged ≠ sub), and either schema\n// contains `oneOf`/`anyOf` inside its properties or items, we fall back to a\n// **property-by-property** comparison that uses the existing branching logic\n// (`getBranchesTyped` / `isAtomicSubsetOf`) on each sub-schema individually.\n//\n// Three helpers:\n// 1. `hasNestedBranching` — guard: does a schema contain oneOf/anyOf in\n// properties or items? Avoids triggering the fallback on normal schemas.\n// 2. `isPropertySubsetOf` — compares a single property sub-schema handling\n// branches on both sides (sub may have oneOf, sup may have oneOf).\n// 3. `isObjectSubsetByProperties` — the fallback itself: iterates over\n// object properties + items and delegates to `isPropertySubsetOf`.\n// 4. `tryNestedBranchingFallback` — the single entry point called from\n// `isAtomicSubsetOf` and `checkAtomic`. Encapsulates the guard check\n// and the call, returning `boolean | null` (null = not applicable).\n\n/**\n * Returns `true` if the schema contains `oneOf`/`anyOf` inside its\n * `properties` or `items`. Recurses into nested object schemas.\n *\n * This is a cheap guard so we only attempt the property-by-property\n * fallback when the merge failure is plausibly caused by nested branching.\n */\nfunction hasNestedBranching(schema: JSONSchema7Definition): boolean {\n\tif (typeof schema === \"boolean\") return false;\n\n\tif (isPlainObj(schema.properties)) {\n\t\tconst props = schema.properties as Record<string, JSONSchema7Definition>;\n\t\tfor (const key of Object.keys(props)) {\n\t\t\tconst prop = props[key];\n\t\t\tif (prop === undefined || typeof prop === \"boolean\") continue;\n\t\t\tif (hasOwn(prop, \"oneOf\") || hasOwn(prop, \"anyOf\")) return true;\n\t\t\tif (hasNestedBranching(prop)) return true;\n\t\t}\n\t}\n\n\tif (isPlainObj(schema.items) && typeof schema.items !== \"boolean\") {\n\t\tconst items = schema.items as JSONSchema7;\n\t\tif (hasOwn(items, \"oneOf\") || hasOwn(items, \"anyOf\")) return true;\n\t\tif (hasNestedBranching(items)) return true;\n\t}\n\n\treturn false;\n}\n\n/**\n * Checks `sub ⊆ sup` for a single property sub-schema, handling branches\n * on **both** sides.\n *\n * `isAtomicSubsetOf` only extracts branches from `sup`. When `sub` also\n * has `oneOf`/`anyOf`, we extract sub's branches and verify that **every**\n * branch is accepted by sup (same semantics as `checkBranchedSub`).\n */\nfunction isPropertySubsetOf(\n\tsub: JSONSchema7Definition,\n\tsup: JSONSchema7Definition,\n\tengine: MergeEngine,\n): boolean {\n\tconst { branches: subBranches } = getBranchesTyped(sub);\n\n\tif (subBranches.length > 1 || subBranches[0] !== sub) {\n\t\tfor (const branch of subBranches) {\n\t\t\tif (branch === undefined) continue;\n\t\t\tif (!isAtomicSubsetOf(branch, sup, engine)) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\treturn isAtomicSubsetOf(sub, sup, engine);\n}\n\n/**\n * Checks whether the array-level constraints of `sub` are compatible\n * with those of `sup` for subset semantics:\n * - `minItems`: sub.minItems must be >= sup.minItems\n * - `maxItems`: sub.maxItems must be <= sup.maxItems\n * - `uniqueItems`: if sup requires it, sub must too\n */\nfunction isArrayConstraintsSubset(sub: JSONSchema7, sup: JSONSchema7): boolean {\n\t// minItems: sub.minItems must be >= sup.minItems\n\tif (sup.minItems !== undefined) {\n\t\tif (sub.minItems === undefined || sub.minItems < sup.minItems) {\n\t\t\treturn false;\n\t\t}\n\t}\n\t// maxItems: sub.maxItems must be <= sup.maxItems\n\tif (sup.maxItems !== undefined) {\n\t\tif (sub.maxItems === undefined || sub.maxItems > sup.maxItems) {\n\t\t\treturn false;\n\t\t}\n\t}\n\t// uniqueItems: if sup requires it, sub must also require it\n\tif (sup.uniqueItems === true && sub.uniqueItems !== true) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n/**\n * Checks `sub ⊆ sup` by comparing object properties (and array items)\n * individually, using the full branching-aware logic.\n *\n * This is a **fallback** for when the merge-based check fails due to\n * `oneOf`/`anyOf` inside properties. It does NOT check object-level\n * keywords like `minProperties`/`maxProperties` — those are rare in\n * practice and are already handled correctly by the merge when the\n * branching isn't involved.\n *\n * @returns `true` if sub ⊆ sup, `false` otherwise.\n */\nfunction isObjectSubsetByProperties(\n\tsub: JSONSchema7,\n\tsup: JSONSchema7,\n\tengine: MergeEngine,\n): boolean {\n\tconst subIsObj = sub.type === \"object\" || isPlainObj(sub.properties);\n\tconst supIsObj = sup.type === \"object\" || isPlainObj(sup.properties);\n\n\t// ── Array path: both are arrays with items ──\n\tif (!subIsObj && !supIsObj) {\n\t\tif (\n\t\t\tsub.type === \"array\" &&\n\t\t\tsup.type === \"array\" &&\n\t\t\tisPlainObj(sub.items) &&\n\t\t\tisPlainObj(sup.items)\n\t\t) {\n\t\t\tif (\n\t\t\t\t!isPropertySubsetOf(\n\t\t\t\t\tsub.items as JSONSchema7Definition,\n\t\t\t\t\tsup.items as JSONSchema7Definition,\n\t\t\t\t\tengine,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn isArrayConstraintsSubset(sub, sup);\n\t\t}\n\t\treturn false;\n\t}\n\n\t// Both must look like objects\n\tif (!subIsObj || !supIsObj) return false;\n\n\t// ── Type compatibility ──\n\tif (hasOwn(sub, \"type\") && hasOwn(sup, \"type\") && sub.type !== sup.type) {\n\t\treturn false;\n\t}\n\n\tconst subProps = (isPlainObj(sub.properties) ? sub.properties : {}) as Record<\n\t\tstring,\n\t\tJSONSchema7Definition\n\t>;\n\tconst supProps = (isPlainObj(sup.properties) ? sup.properties : {}) as Record<\n\t\tstring,\n\t\tJSONSchema7Definition\n\t>;\n\tconst subRequired = Array.isArray(sub.required)\n\t\t? (sub.required as string[])\n\t\t: [];\n\tconst supRequired = Array.isArray(sup.required)\n\t\t? (sup.required as string[])\n\t\t: [];\n\n\t// ── Required: every key sup requires, sub must also require ──\n\tfor (const key of supRequired) {\n\t\tif (!subRequired.includes(key)) return false;\n\t}\n\n\t// ── additionalProperties: false on sup ──\n\tif (sup.additionalProperties === false) {\n\t\tfor (const key of Object.keys(subProps)) {\n\t\t\tif (!hasOwn(supProps, key)) return false;\n\t\t}\n\t}\n\n\t// ── Property-by-property check ──\n\tfor (const key of Object.keys(supProps)) {\n\t\tconst supProp = supProps[key];\n\t\tconst subProp = subProps[key];\n\t\tif (supProp === undefined || subProp === undefined) continue;\n\n\t\tif (!isPropertySubsetOf(subProp, supProp, engine)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// ── Sub's extra properties vs sup's additionalProperties schema ──\n\tif (\n\t\tisPlainObj(sup.additionalProperties) &&\n\t\ttypeof sup.additionalProperties !== \"boolean\"\n\t) {\n\t\tconst addPropSchema = sup.additionalProperties as JSONSchema7Definition;\n\t\tfor (const key of Object.keys(subProps)) {\n\t\t\tif (hasOwn(supProps, key)) continue;\n\t\t\tconst subProp = subProps[key];\n\t\t\tif (subProp === undefined) continue;\n\t\t\tif (!isPropertySubsetOf(subProp, addPropSchema, engine)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\t// ── Items (object schema that also has array items) ──\n\tif (isPlainObj(sub.items) && isPlainObj(sup.items)) {\n\t\tif (\n\t\t\t!isPropertySubsetOf(\n\t\t\t\tsub.items as JSONSchema7Definition,\n\t\t\t\tsup.items as JSONSchema7Definition,\n\t\t\t\tengine,\n\t\t\t)\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!isArrayConstraintsSubset(sub, sup)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/**\n * Attempts the property-by-property fallback when a merge-based check\n * fails and nested branching is detected.\n *\n * Encapsulates the guard (`hasNestedBranching`) and the call to\n * `isObjectSubsetByProperties` so callers don't repeat the pattern.\n *\n * @returns `true` if the fallback confirms sub ⊆ sup, `false` if it\n * confirms sub ⊄ sup, `null` if the fallback is not applicable\n * (neither schema has nested branching, or schemas are booleans).\n */\nfunction tryNestedBranchingFallback(\n\tsub: JSONSchema7Definition,\n\tsup: JSONSchema7Definition,\n\tengine: MergeEngine,\n): boolean | null {\n\tif (typeof sub === \"boolean\" || typeof sup === \"boolean\") return null;\n\tif (!hasNestedBranching(sub) && !hasNestedBranching(sup)) return null;\n\treturn isObjectSubsetByProperties(sub, sup, engine);\n}\n\n// ─── Atomic subset check ─────────────────────────────────────────────────────\n\n/**\n * Checks whether `sub ⊆ sup` for two schemas without anyOf/oneOf (or with\n * anyOf/oneOf only on the sup side).\n *\n * Point 7 — Integrates an extended `not` pre-check (`evaluateNot`) before the merge.\n *\n * When `evaluateNot` confirms compatibility (`true`), the `not` is removed\n * from `sup` before the merge to prevent the merge engine from adding a `not`\n * constraint that `sub` doesn't have (which would cause `isEqual(merged, sub)` to fail).\n *\n * Pattern pre-check — When both schemas have different patterns, checks\n * inclusion via sampling with `isPatternSubset`. If confirmed, removes the\n * pattern from sup before the merge (same strategy as for `not`).\n *\n * Principle: merge(sub, sup) ≡ sub → sub is a subset of sup.\n *\n * When the merge-based check fails and either sub or sup contains nested\n * `oneOf`/`anyOf` in properties or items, falls back to a property-by-property\n * comparison via `isObjectSubsetByProperties`.\n */\n\n// ─── allOf resolution ────────────────────────────────────────────────────────\n//\n// When `sup` contains `allOf`, the merge engine resolves it during\n// `merge(sub, sup)`, but the stripping pipeline (stripNotFromSup,\n// stripDependenciesFromSup, etc.) runs **before** the merge and operates\n// on the raw `sup` — it cannot see keywords nested inside `allOf` branches.\n//\n// `resolveSupAllOf` pre-resolves the `allOf` by merging all its branches\n// into a single schema so that the stripping pipeline can operate on\n// the flattened result. Any top-level keywords on `sup` alongside `allOf`\n// are preserved by including them in the merge.\n\n/**\n * If `sup` has an `allOf` array, resolve it by merging all branches\n * (plus any sibling keywords) into a single flattened schema.\n * Returns the original `sup` unchanged if there is no `allOf` or\n * if the merge fails.\n */\nfunction resolveSupAllOf(\n\tsup: JSONSchema7Definition,\n\tengine: MergeEngine,\n): JSONSchema7Definition {\n\tif (typeof sup === \"boolean\") return sup;\n\tif (!Array.isArray(sup.allOf) || sup.allOf.length === 0) return sup;\n\n\t// Collect sibling keywords (everything except `allOf`) into a base schema.\n\t// This handles cases like { allOf: [...], type: \"object\" }.\n\tconst { allOf: _allOf, ...sibling } = sup;\n\tconst branches = sup.allOf as JSONSchema7Definition[];\n\n\t// Start from the sibling keywords (or first branch if no siblings).\n\tlet resolved: JSONSchema7Definition | null =\n\t\tObject.keys(sibling).length > 0 ? (sibling as JSONSchema7) : null;\n\n\tfor (const branch of branches) {\n\t\tif (resolved === null) {\n\t\t\tresolved = branch;\n\t\t} else {\n\t\t\tresolved = engine.merge(resolved, branch);\n\t\t\tif (resolved === null) {\n\t\t\t\t// Merge failed — fall back to original sup so the existing\n\t\t\t\t// pipeline can still attempt the merge with allOf intact.\n\t\t\t\treturn sup;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn resolved ?? sup;\n}\n\nexport function isAtomicSubsetOf(\n\tsub: JSONSchema7Definition,\n\tsup: JSONSchema7Definition,\n\tengine: MergeEngine,\n): boolean {\n\t// ── Resolve allOf in sup ──\n\t// Pre-flatten allOf so that the stripping pipeline (stripNotFromSup,\n\t// stripDependenciesFromSup, etc.) can see keywords from all branches.\n\tsup = resolveSupAllOf(sup, engine);\n\n\tconst { branches: supBranches } = getBranchesTyped(sup);\n\n\t// Simple schema → direct merge\n\tif (supBranches.length === 1 && supBranches[0] === sup) {\n\t\t// Point 7: extended `not` pre-check\n\t\tconst notResult = evaluateNot(sub, sup);\n\t\tif (notResult === false) return false;\n\n\t\t// ── Format pre-check ──\n\t\t// If both schemas have a different `format`, check that\n\t\t// sub.format ⊆ sup.format. Otherwise, sub cannot be ⊆ sup.\n\t\t// This complements hasFormatConflict (which handles the merge) by handling\n\t\t// the subset check direction that the merge cannot resolve.\n\t\tif (\n\t\t\ttypeof sub !== \"boolean\" &&\n\t\t\ttypeof sup !== \"boolean\" &&\n\t\t\thasOwn(sub, \"format\") &&\n\t\t\thasOwn(sup, \"format\") &&\n\t\t\tsub.format !== sup.format\n\t\t) {\n\t\t\tconst fmtResult = isFormatSubset(\n\t\t\t\tsub.format as string,\n\t\t\t\tsup.format as string,\n\t\t\t);\n\t\t\tif (fmtResult !== true) return false;\n\t\t}\n\n\t\t// ── Pattern pre-check ──\n\t\t// If both schemas have different patterns, check inclusion\n\t\t// via sampling. If sub.pattern ⊄ sup.pattern (counter-example found),\n\t\t// we return false immediately. Otherwise, we can remove the pattern\n\t\t// from sup to avoid the structural false negative from the merge.\n\t\tif (\n\t\t\ttypeof sub !== \"boolean\" &&\n\t\t\ttypeof sup !== \"boolean\" &&\n\t\t\thasOwn(sub, \"pattern\") &&\n\t\t\thasOwn(sup, \"pattern\") &&\n\t\t\tsub.pattern !== sup.pattern\n\t\t) {\n\t\t\tconst patResult = isPatternSubset(\n\t\t\t\tsub.pattern as string,\n\t\t\t\tsup.pattern as string,\n\t\t\t);\n\t\t\tif (patResult === false) return false;\n\t\t}\n\n\t\t// Remove `not` from sup (top-level and/or in properties)\n\t\t// when evaluateNot confirms compatibility at the corresponding level.\n\t\t// This prevents the merge engine from adding a `not` constraint that sub doesn't have\n\t\t// (which would cause merged ≠ sub and produce a false negative).\n\t\tlet effectiveSup = sup;\n\t\tif (typeof sup !== \"boolean\") {\n\t\t\t// If top-level not is confirmed compatible → remove top-level not\n\t\t\tif (notResult === true) {\n\t\t\t\teffectiveSup = stripNotFromSup(sub, sup, true);\n\t\t\t\t// If sup only had `not` → sub is compatible (the not is resolved)\n\t\t\t\tif (\n\t\t\t\t\ttypeof effectiveSup !== \"boolean\" &&\n\t\t\t\t\tObject.keys(effectiveSup).length === 0\n\t\t\t\t) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Even if the top-level not is not confirmed (null), we attempt\n\t\t\t\t// to remove `not` in individual properties\n\t\t\t\t// without touching the top-level `not`\n\t\t\t\teffectiveSup = stripNotFromSup(sub, sup, false);\n\t\t\t}\n\n\t\t\t// Remove patterns from sup confirmed by sampling.\n\t\t\t// Same strategy as for `not`: we remove the constraint already\n\t\t\t// satisfied by sub to prevent the merge from producing a combined\n\t\t\t// pattern (lookahead conjunction) structurally ≠ sub.\n\t\t\teffectiveSup = stripPatternFromSup(sub, effectiveSup);\n\t\t\teffectiveSup = stripRedundantBoundsFromSup(sub, effectiveSup);\n\t\t\teffectiveSup = stripDependenciesFromSup(sub, effectiveSup);\n\t\t}\n\n\t\tconst merged = engine.merge(sub, effectiveSup);\n\t\tif (merged === null) {\n\t\t\t// ── Fallback: property-by-property for nested oneOf/anyOf ──\n\t\t\treturn tryNestedBranchingFallback(sub, effectiveSup, engine) ?? false;\n\t\t}\n\t\t// Fast path: if merged is already structurally equal to sub,\n\t\t// skip normalize entirely. This is the common case when sub ⊆ sup\n\t\t// (A ∩ B = A), saving O(n) normalize traversal on wide schemas.\n\t\tif (semanticDeepEqual(merged, sub)) return true;\n\n\t\t// Strip vacuously-satisfied `false` properties added by the merge\n\t\t// from sup but absent in sub (vacuous truth: absent ⊆ forbidden).\n\t\tconst strippedMerged = stripVacuousFalseProperties(merged, sub);\n\t\tif (strippedMerged !== merged && semanticDeepEqual(strippedMerged, sub)) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Slow path: normalize to eliminate merge artifacts (e.g. redundant\n\t\t// enum when const is present), then compare.\n\t\tconst normalizedMerged = normalize(strippedMerged);\n\t\tif (\n\t\t\tsemanticDeepEqual(normalizedMerged, sub) ||\n\t\t\tengine.isEqual(normalizedMerged, sub)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// ── Fallback: merged ≠ sub but nested branching may explain it ──\n\t\t// The merge engine preserves oneOf/anyOf as-is inside properties\n\t\t// (e.g. merge produces {type:\"string\", oneOf:[...]} ≠ sub).\n\t\treturn tryNestedBranchingFallback(sub, effectiveSup, engine) ?? false;\n\t}\n\n\t// anyOf/oneOf in sup → at least one branch must accept sub\n\treturn supBranches.some((branch) => {\n\t\t// Point 7: extended `not` pre-check per branch\n\t\tconst notResult = evaluateNot(sub, branch);\n\t\tif (notResult === false) return false;\n\n\t\t// ── Pattern pre-check par branche ──\n\t\tif (\n\t\t\ttypeof sub !== \"boolean\" &&\n\t\t\ttypeof branch !== \"boolean\" &&\n\t\t\thasOwn(sub, \"pattern\") &&\n\t\t\thasOwn(branch, \"pattern\") &&\n\t\t\tsub.pattern !== branch.pattern\n\t\t) {\n\t\t\tconst patResult = isPatternSubset(\n\t\t\t\tsub.pattern as string,\n\t\t\t\tbranch.pattern as string,\n\t\t\t);\n\t\t\tif (patResult === false) return false;\n\t\t}\n\n\t\t// Same strip logic for branches\n\t\tlet effectiveBranch = branch;\n\t\tif (typeof branch !== \"boolean\") {\n\t\t\tif (notResult === true) {\n\t\t\t\teffectiveBranch = stripNotFromSup(sub, branch, true);\n\t\t\t\tif (\n\t\t\t\t\ttypeof effectiveBranch !== \"boolean\" &&\n\t\t\t\t\tObject.keys(effectiveBranch).length === 0\n\t\t\t\t) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\teffectiveBranch = stripNotFromSup(sub, branch, false);\n\t\t\t}\n\n\t\t\t// Strip patterns confirmed by sampling\n\t\t\teffectiveBranch = stripPatternFromSup(sub, effectiveBranch);\n\t\t\teffectiveBranch = stripRedundantBoundsFromSup(sub, effectiveBranch);\n\t\t\teffectiveBranch = stripDependenciesFromSup(sub, effectiveBranch);\n\t\t}\n\n\t\tconst merged = engine.merge(sub, effectiveBranch);\n\t\tif (merged === null) {\n\t\t\t// Fallback for nested branching within branches\n\t\t\treturn tryNestedBranchingFallback(sub, effectiveBranch, engine) === true;\n\t\t}\n\t\t// Fast path: skip normalize if merged already equals sub\n\t\tif (semanticDeepEqual(merged, sub)) return true;\n\n\t\t// Strip vacuously-satisfied `false` properties (see comment above)\n\t\tconst strippedBranch = stripVacuousFalseProperties(merged, sub);\n\t\tif (strippedBranch !== merged && semanticDeepEqual(strippedBranch, sub)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst normalizedBranch = normalize(strippedBranch);\n\t\tif (\n\t\t\tsemanticDeepEqual(normalizedBranch, sub) ||\n\t\t\tengine.isEqual(normalizedBranch, sub)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Fallback: merged ≠ sub but nested branching may explain it\n\t\treturn tryNestedBranchingFallback(sub, effectiveBranch, engine) === true;\n\t});\n}\n\n// ─── Full subset check (with diffs) ─────────────────────────────────────────\n\n/**\n * Checks `sub ⊆ sup` for a sub that has branches (anyOf/oneOf).\n * Each branch of sub must be accepted by sup.\n *\n * Point 6 — Uses `getBranchesTyped` to distinguish `anyOf[i]` from\n * `oneOf[i]` in diff paths.\n */\nexport function checkBranchedSub(\n\tsubBranches: JSONSchema7Definition[],\n\tsup: JSONSchema7Definition,\n\tengine: MergeEngine,\n\tbranchType: BranchType = \"anyOf\",\n): SubsetResult {\n\tconst allErrors: SchemaError[] = [];\n\tlet allSubset = true;\n\n\tfor (let i = 0; i < subBranches.length; i++) {\n\t\tconst branch = subBranches[i];\n\t\tif (branch === undefined) continue;\n\t\tif (!isAtomicSubsetOf(branch, sup, engine)) {\n\t\t\tallSubset = false;\n\t\t\tconst branchErrors = computeSemanticErrors(branch, sup, \"\");\n\t\t\tallErrors.push(...branchErrors);\n\t\t}\n\t}\n\n\treturn {\n\t\tisSubset: allSubset,\n\t\tmerged: allSubset\n\t\t\t? branchType === \"oneOf\"\n\t\t\t\t? { oneOf: subBranches }\n\t\t\t\t: { anyOf: subBranches }\n\t\t\t: null,\n\t\terrors: allErrors,\n\t};\n}\n\n/**\n * Checks `sub ⊆ sup` for a sup that has branches (anyOf/oneOf).\n * At least one branch of sup must accept sub.\n *\n * Point 6 — Uses the sup's branch type for more precise messages.\n */\nexport function checkBranchedSup(\n\tsub: JSONSchema7Definition,\n\tsupBranches: JSONSchema7Definition[],\n\tengine: MergeEngine,\n\t_branchType: BranchType = \"anyOf\",\n): SubsetResult {\n\tfor (const branch of supBranches) {\n\t\t// Strip not + patterns confirmed by sampling before the merge\n\t\tlet effectiveBranch = branch;\n\t\tif (typeof sub !== \"boolean\" && typeof branch !== \"boolean\") {\n\t\t\tconst notResult = evaluateNot(sub, branch);\n\t\t\tif (notResult === false) continue; // This branch rejects sub\n\n\t\t\tif (notResult === true) {\n\t\t\t\teffectiveBranch = stripNotFromSup(sub, branch, true);\n\t\t\t\tif (\n\t\t\t\t\ttypeof effectiveBranch !== \"boolean\" &&\n\t\t\t\t\tObject.keys(effectiveBranch).length === 0\n\t\t\t\t) {\n\t\t\t\t\treturn { isSubset: true, merged: sub, errors: [] };\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\teffectiveBranch = stripNotFromSup(sub, branch, false);\n\t\t\t}\n\t\t\teffectiveBranch = stripPatternFromSup(sub, effectiveBranch);\n\t\t\teffectiveBranch = stripRedundantBoundsFromSup(sub, effectiveBranch);\n\t\t\teffectiveBranch = stripDependenciesFromSup(sub, effectiveBranch);\n\t\t}\n\t\tconst merged = engine.merge(sub, effectiveBranch);\n\t\tif (merged !== null) {\n\t\t\t// Fast path: skip normalize if merged already equals sub\n\t\t\tif (deepEqual(merged, sub)) {\n\t\t\t\treturn { isSubset: true, merged, errors: [] };\n\t\t\t}\n\t\t\tconst normalizedMerged = normalize(merged);\n\t\t\tif (\n\t\t\t\tsemanticDeepEqual(normalizedMerged, sub) ||\n\t\t\t\tengine.isEqual(normalizedMerged, sub)\n\t\t\t) {\n\t\t\t\treturn { isSubset: true, merged, errors: [] };\n\t\t\t}\n\t\t}\n\t}\n\n\t// Generate semantic errors by comparing sub with the original sup\n\tconst semanticErrors = computeSemanticErrors(\n\t\tsub,\n\t\t{ anyOf: supBranches } as JSONSchema7,\n\t\t\"\",\n\t);\n\n\treturn {\n\t\tisSubset: false,\n\t\tmerged: null,\n\t\terrors: semanticErrors,\n\t};\n}\n\n/**\n * Checks `sub ⊆ sup` for two atomic schemas (without anyOf/oneOf).\n * Uses `mergeOrThrow` to capture incompatibility errors.\n *\n * Uses `deepEqual` for structural comparison (with short-circuit\n * by reference and key counting).\n */\nexport function checkAtomic(\n\tsub: JSONSchema7Definition,\n\tsup: JSONSchema7Definition,\n\tengine: MergeEngine,\n): SubsetResult {\n\t// ── Resolve allOf in sup ──\n\t// Same as in isAtomicSubsetOf: pre-flatten allOf so that the stripping\n\t// pipeline can see keywords from all branches.\n\tsup = resolveSupAllOf(sup, engine);\n\n\t// ── evaluateNot pre-check (aligned with isAtomicSubsetOf) ──\n\tconst notResult =\n\t\ttypeof sub !== \"boolean\" && typeof sup !== \"boolean\"\n\t\t\t? evaluateNot(sub, sup)\n\t\t\t: null;\n\n\t// If evaluateNot confirms incompatibility → fail immediately\n\tif (notResult === false) {\n\t\tconst errors = computeSemanticErrors(sub, sup, \"\");\n\t\treturn { isSubset: false, merged: null, errors };\n\t}\n\n\t// Strip not + patterns confirmed by sampling before the merge,\n\t// same strategy as in isAtomicSubsetOf to avoid structural false negatives\n\t// caused by the conjunction of patterns as lookahead.\n\tlet effectiveSup = sup;\n\tif (typeof sub !== \"boolean\" && typeof sup !== \"boolean\") {\n\t\t// If the `not` is confirmed compatible → strip it before the merge\n\t\tif (notResult === true) {\n\t\t\teffectiveSup = stripNotFromSup(sub, sup, true);\n\t\t\tif (\n\t\t\t\ttypeof effectiveSup !== \"boolean\" &&\n\t\t\t\tObject.keys(effectiveSup).length === 0\n\t\t\t) {\n\t\t\t\treturn { isSubset: true, merged: sub, errors: [] };\n\t\t\t}\n\t\t} else {\n\t\t\teffectiveSup = stripNotFromSup(sub, sup, false);\n\t\t}\n\t\teffectiveSup = stripPatternFromSup(sub, effectiveSup);\n\t\teffectiveSup = stripRedundantBoundsFromSup(sub, effectiveSup);\n\t\teffectiveSup = stripDependenciesFromSup(sub, effectiveSup);\n\t}\n\n\ttry {\n\t\tconst merged = engine.mergeOrThrow(sub, effectiveSup);\n\n\t\t// Fast path: skip normalize if merged already equals sub\n\t\tif (deepEqual(merged, sub)) {\n\t\t\treturn { isSubset: true, merged, errors: [] };\n\t\t}\n\n\t\t// Strip vacuously-satisfied `false` properties added by the merge\n\t\t// from sup but absent in sub (vacuous truth: absent ⊆ forbidden).\n\t\tconst strippedMerged = stripVacuousFalseProperties(merged, sub);\n\t\tif (strippedMerged !== merged && semanticDeepEqual(strippedMerged, sub)) {\n\t\t\treturn { isSubset: true, merged: strippedMerged, errors: [] };\n\t\t}\n\n\t\tconst normalizedMerged = normalize(strippedMerged);\n\n\t\tif (\n\t\t\tsemanticDeepEqual(normalizedMerged, sub) ||\n\t\t\tengine.isEqual(normalizedMerged, sub)\n\t\t) {\n\t\t\treturn { isSubset: true, merged: normalizedMerged, errors: [] };\n\t\t}\n\n\t\t// ── Fallback: property-by-property for nested oneOf/anyOf ──\n\t\tif (tryNestedBranchingFallback(sub, effectiveSup, engine) === true) {\n\t\t\treturn { isSubset: true, merged: sub, errors: [] };\n\t\t}\n\n\t\tconst errors = computeSemanticErrors(sub, sup, \"\");\n\t\treturn { isSubset: false, merged: normalizedMerged, errors };\n\t} catch (_e) {\n\t\t// ── Fallback: property-by-property for nested oneOf/anyOf ──\n\t\tif (tryNestedBranchingFallback(sub, effectiveSup, engine) === true) {\n\t\t\treturn { isSubset: true, merged: sub, errors: [] };\n\t\t}\n\n\t\tconst errors = computeSemanticErrors(sub, sup, \"\");\n\t\treturn {\n\t\t\tisSubset: false,\n\t\t\tmerged: null,\n\t\t\terrors,\n\t\t};\n\t}\n}\n"],"names":["checkAtomic","checkBranchedSub","checkBranchedSup","getBranchesTyped","isAtomicSubsetOf","BRANCH_TRUE","branches","type","BRANCH_FALSE","atomicBranchCache","WeakMap","def","hasOwn","Array","isArray","anyOf","oneOf","cached","get","undefined","set","evaluateNot","sub","sup","isPlainObj","not","notSchema","properties","required","notProps","notRequired","subProps","subRequired","notPropKeys","Object","keys","hasIncompatibleProp","some","key","notPropDef","notProp","includes","subPropDef","subProp","deepEqual","const","enum","inNotEnum","v","noneInNotEnum","every","nv","allPropsMatch","notConst","subConst","allDisjoint","allExcluded","val","notVal","notType","subType","allIncompatible","branch","result","anyBranchMatches","subFormat","format","notFormat","stripNotFromSup","stripTopLevel","omitKeys","supProps","newProps","supPropDef","strippedProp","propNotResult","recursed","stripPatternFromSup","supObj","copied","ensureCopy","pattern","patResult","isPatternSubset","propsModified","propPatResult","items","subItems","supItems","itemsPatResult","stripRedundantBoundsFromSup","keysToStrip","minimum","exclusiveMinimum","push","maximum","exclusiveMaximum","length","resultObj","supProp","stripped","stripDependenciesFromSup","dependencies","supDeps","subHasAdditionalPropsFalse","additionalProperties","dep","triggerAlwaysPresent","triggerNeverProduced","allDepsRequired","d","depSchema","depRequired","depProps","allDepRequiredSatisfied","r","allDepPropsSatisfied","propKey","depPropDef","depPropKeys","dk","depVal","subVal","stripVacuousFalseProperties","merged","mergedProps","strippedProps","hasNestedBranching","schema","props","prop","isPropertySubsetOf","engine","subBranches","isArrayConstraintsSubset","minItems","maxItems","uniqueItems","isObjectSubsetByProperties","subIsObj","supIsObj","supRequired","addPropSchema","tryNestedBranchingFallback","resolveSupAllOf","allOf","_allOf","sibling","resolved","merge","supBranches","notResult","fmtResult","isFormatSubset","effectiveSup","semanticDeepEqual","strippedMerged","normalizedMerged","normalize","isEqual","effectiveBranch","strippedBranch","normalizedBranch","branchType","allErrors","allSubset","i","branchErrors","computeSemanticErrors","isSubset","errors","_branchType","semanticErrors","mergeOrThrow","_e"],"mappings":"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"}
@@ -159,6 +159,23 @@ export interface CheckRuntimeOptions {
159
159
  * @default false
160
160
  */
161
161
  validate?: boolean | ValidateTargets;
162
+ /**
163
+ * Arbitrary context forwarded to every constraint validator during
164
+ * this `check()` call.
165
+ *
166
+ * Useful for passing per-request state (e.g. tenant ID, user scope)
167
+ * that is not encoded in the schema but is needed by validators at runtime.
168
+ *
169
+ * @example
170
+ * ```ts
171
+ * checker.check(sub, sup, {
172
+ * data: { accountId: '123' },
173
+ * validate: { sup: { partial: true } },
174
+ * constraintContext: { companyId: 42 },
175
+ * });
176
+ * ```
177
+ */
178
+ constraintContext?: ConstraintExecutionContext;
162
179
  }
163
180
  /**
164
181
  * Extended result from `check()` when runtime options are provided.
@@ -181,6 +198,15 @@ export type Constraint = string | {
181
198
  params?: Record<string, unknown>;
182
199
  };
183
200
  export type Constraints = Constraint | Constraint[];
201
+ /**
202
+ * Arbitrary per-call context forwarded to every constraint validator
203
+ * during a `check()` invocation.
204
+ *
205
+ * Allows callers to pass request-scoped information (e.g. tenant ID,
206
+ * user ID, feature flags) that is not part of the schema definition
207
+ * but is required by constraint validators at runtime.
208
+ */
209
+ export type ConstraintExecutionContext = Record<string, unknown>;
184
210
  /**
185
211
  * Result of a constraint validation.
186
212
  */
@@ -193,14 +219,15 @@ export interface ConstraintValidationResult {
193
219
  /**
194
220
  * A constraint validator function.
195
221
  *
196
- * Receives the value to validate and optional params defined
197
- * in the schema's constraint definition.
222
+ * Receives the value to validate, optional params defined in the schema's
223
+ * constraint definition, and an optional per-call execution context.
198
224
  *
199
225
  * Can be synchronous or asynchronous. When async validators are used,
200
226
  * `check()` with runtime options returns a `Promise`.
201
227
  *
202
228
  * @param value - The runtime value to validate
203
229
  * @param params - The `params` object from the constraint definition, if any
230
+ * @param context - The per-call execution context passed via `constraintContext`, if any
204
231
  * @returns The validation result, or a Promise resolving to it
205
232
  *
206
233
  * @example
@@ -221,9 +248,15 @@ export interface ConstraintValidationResult {
221
248
  * valid: typeof value === "number" && value >= (params?.min ?? 0),
222
249
  * message: `Value must be at least ${params?.min}`,
223
250
  * });
251
+ *
252
+ * // Validator using execution context (e.g. tenant-scoped DB lookup)
253
+ * const isUniqueInTenant: ConstraintValidator = async (value, params, context) => ({
254
+ * valid: await checkUniqueness(value as string, context?.companyId as number),
255
+ * message: "Value must be unique within the tenant",
256
+ * });
224
257
  * ```
225
258
  */
226
- export type ConstraintValidator = (value: unknown, params?: Record<string, unknown>) => ConstraintValidationResult | Promise<ConstraintValidationResult>;
259
+ export type ConstraintValidator = (value: unknown, params?: Record<string, unknown>, context?: ConstraintExecutionContext) => ConstraintValidationResult | Promise<ConstraintValidationResult>;
227
260
  /**
228
261
  * Registry mapping constraint names to their validator functions.
229
262
  *
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/types.ts"],"sourcesContent":["import type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\n\n// ─── Module augmentation ─────────────────────────────────────────────────────\n// Extends JSONSchema7 with the custom `constraints` keyword so that consumers\n// of this package see the property on every JSONSchema7 without needing a\n// separate ambient file or `/// <reference>` directive.\n\ndeclare module \"json-schema\" {\n\tinterface JSONSchema7 {\n\t\tconstraints?: Constraints;\n\t}\n}\n\n// ─── Public types ────────────────────────────────────────────────────────────\n\n/**\n * Discriminant for `SchemaError` — indicates the nature of the incompatibility.\n *\n * | Member | Description | Source module(s) |\n * |------------------------|--------------------------------------------------------------------|---------------------------|\n * | `TypeMismatch` | Incompatible types (e.g. `string` vs `number`, boolean schemas) | `semantic-errors.ts` |\n * | `MissingProperty` | Required property absent from the source schema | `semantic-errors.ts` |\n * | `Optionality` | Property required in target but optional in source | `semantic-errors.ts` |\n * | `EnumMismatch` | `enum` / `const` values are incompatible | `semantic-errors.ts` |\n * | `NumericConstraint` | `minimum`, `maximum`, `exclusiveMin/Max`, `multipleOf` | `semantic-errors.ts` |\n * | `StringConstraint` | `minLength`, `maxLength`, `pattern`, `format` | `semantic-errors.ts` |\n * | `ObjectConstraint` | `additionalProperties`, `min/maxProperties`, `propertyNames`, etc. | `semantic-errors.ts` |\n * | `ArrayConstraint` | `minItems`, `maxItems`, `uniqueItems`, `contains` | `semantic-errors.ts` |\n * | `NotSchema` | Incompatibility on the `not` keyword | `semantic-errors.ts` |\n * | `BranchMismatch` | No `anyOf` / `oneOf` branch matches | `semantic-errors.ts` |\n * | `RuntimeValidation` | Runtime data invalid against a standard JSON Schema keyword (AJV) | `runtime-validator.ts` |\n * | `CustomConstraint` | Custom constraint failed, unknown, or threw | `constraint-validator.ts` |\n */\nexport enum SchemaErrorType {\n\tTypeMismatch = \"type_mismatch\",\n\tMissingProperty = \"missing_property\",\n\tOptionality = \"optionality\",\n\tEnumMismatch = \"enum_mismatch\",\n\tNumericConstraint = \"numeric_constraint\",\n\tStringConstraint = \"string_constraint\",\n\tObjectConstraint = \"object_constraint\",\n\tArrayConstraint = \"array_constraint\",\n\tNotSchema = \"not_schema\",\n\tBranchMismatch = \"branch_mismatch\",\n\tRuntimeValidation = \"runtime_validation\",\n\tCustomConstraint = \"custom_constraint\",\n}\n\nexport interface SchemaError {\n\t/** Discriminant indicating the nature of the error */\n\ttype: SchemaErrorType;\n\t/** Normalized path to the concerned property (e.g. \"user.name\", \"users[].name\", \"accountId\") */\n\tkey: string;\n\t/** Type or value expected by the target schema (sup) */\n\texpected: string;\n\t/** Type or value received from the source schema (sub) */\n\treceived: string;\n}\n\nexport interface SubsetResult {\n\t/** true if sub ⊆ sup (every value valid for sub is also valid for sup) */\n\tisSubset: boolean;\n\t/** The schema resulting from the intersection allOf(sub, sup), or null if incompatible */\n\tmerged: JSONSchema7Definition | null;\n\t/** Semantic errors describing incompatibilities between the two schemas */\n\terrors: SchemaError[];\n}\n\n/**\n * Per-target validation options.\n *\n * When `partial` is `true`, runtime validation strips `required` and\n * `additionalProperties` constraints at every level of the schema before\n * passing it to AJV. This allows validating **only the properties present\n * in `data`** without false negatives on missing required properties or\n * extra properties not defined in the schema.\n *\n * Partial mode applies recursively: nested object schemas also have their\n * `required` and `additionalProperties` stripped.\n *\n * @example\n * ```ts\n * // Validate sup in partial mode — only check properties present in data\n * checker.check(sub, sup, {\n * data: { accountId: 'salut' },\n * validate: { sup: { partial: true } },\n * });\n * ```\n */\nexport interface ValidateTargetOptions {\n\t/**\n\t * When `true`, strip `required` and `additionalProperties` from the\n\t * schema before AJV validation so that only properties present in\n\t * `data` are validated.\n\t *\n\t * @default false\n\t */\n\tpartial?: boolean;\n}\n\n/**\n * Granular control over which schema(s) runtime validation applies to.\n *\n * When provided as an object, each key independently controls whether\n * runtime validation (AJV + custom constraints) runs against that schema:\n * - `sub`: validate `data` against the resolved/narrowed sub schema\n * - `sup`: validate `data` against the resolved/narrowed sup schema\n *\n * Each target accepts either a boolean or a `ValidateTargetOptions` object.\n * When `true`, validation runs with default options. When an object is\n * provided, its flags (e.g. `partial`) customize the validation behavior.\n *\n * Omitted keys default to `false`.\n *\n * @example\n * ```ts\n * // Validate only the sup schema (e.g. target input with constraints)\n * checker.check(sub, sup, { data: {}, validate: { sup: true } });\n *\n * // Validate only the sub schema\n * checker.check(sub, sup, { data: {}, validate: { sub: true } });\n *\n * // Validate both (equivalent to `validate: true`)\n * checker.check(sub, sup, { data: {}, validate: { sub: true, sup: true } });\n *\n * // Validate sup in partial mode (skip required / additionalProperties)\n * checker.check(sub, sup, {\n * data: { accountId: 'salut' },\n * validate: { sup: { partial: true } },\n * });\n * ```\n */\nexport interface ValidateTargets {\n\t/** When `true` or an options object, validate `data` against the resolved sub schema */\n\tsub?: boolean | ValidateTargetOptions;\n\t/** When `true` or an options object, validate `data` against the resolved sup schema */\n\tsup?: boolean | ValidateTargetOptions;\n}\n\n/**\n * Options for runtime-aware subset checking.\n *\n * When `data` is provided, the checker:\n * 1. Resolves `if/then/else` conditions in both `sub` and `sup` using `data`\n * (if `data` is `undefined`, conditions are resolved with `{}`)\n * 2. Narrows schemas using runtime values (e.g. enum materialization)\n * 3. Performs the static subset check on the resolved/narrowed schemas\n *\n * When `validate` is `true` (or an object with `sub`/`sup` flags), additional\n * runtime steps run **after** the static check passes:\n * 4. `data` is validated against the targeted resolved schema(s) via AJV\n * 5. Custom constraints are validated against `data` for the targeted schema(s)\n *\n * `data` can be a partial discriminant (e.g. `{ kind: \"text\" }`) used solely\n * for condition resolution and narrowing. It does **not** need to be a complete\n * instance of the schemas unless runtime validation is enabled.\n */\nexport interface CheckRuntimeOptions {\n\t/** Runtime data used for condition resolution, narrowing, and optionally runtime validation */\n\tdata: unknown;\n\n\t/**\n\t * Controls runtime validation of `data` against resolved schemas.\n\t *\n\t * - `true` — validate against **both** sub and sup schemas (AJV + constraints)\n\t * - `false` / omitted — no runtime validation (data used only for condition\n\t * resolution and narrowing)\n\t * - `{ sub: true }` — validate only against the sub schema\n\t * - `{ sup: true }` — validate only against the sup schema\n\t * - `{ sub: true, sup: true }` — equivalent to `true`\n\t * - `{ sup: { partial: true } }` — validate sup in partial mode\n\t * (skip `required` / `additionalProperties` enforcement)\n\t *\n\t * @default false\n\t */\n\tvalidate?: boolean | ValidateTargets;\n}\n\n/**\n * Extended result from `check()` when runtime options are provided.\n * Includes resolution results for sub and sup in addition to the SubsetResult.\n */\nexport interface ResolvedSubsetResult extends SubsetResult {\n\tresolvedSub: ResolvedConditionResult;\n\tresolvedSup: ResolvedConditionResult;\n}\n\nexport interface ResolvedConditionResult {\n\t/** The schema with if/then/else resolved (flattened) */\n\tresolved: JSONSchema7;\n\t/** The branch that was applied (\"then\" | \"else\" | null if no condition) */\n\tbranch: \"then\" | \"else\" | null;\n\t/** The discriminant used for resolution */\n\tdiscriminant: Record<string, unknown>;\n}\n\nexport type Constraint =\n\t| string\n\t| {\n\t\t\tname: string;\n\t\t\tparams?: Record<string, unknown>;\n\t };\n\nexport type Constraints = Constraint | Constraint[];\n\n// ─── Constraint Validator types ──────────────────────────────────────────────\n\n/**\n * Result of a constraint validation.\n */\nexport interface ConstraintValidationResult {\n\t/** Whether the value satisfies the constraint */\n\tvalid: boolean;\n\t/** Human-readable message when `valid` is `false` */\n\tmessage?: string;\n}\n\n/**\n * A constraint validator function.\n *\n * Receives the value to validate and optional params defined\n * in the schema's constraint definition.\n *\n * Can be synchronous or asynchronous. When async validators are used,\n * `check()` with runtime options returns a `Promise`.\n *\n * @param value - The runtime value to validate\n * @param params - The `params` object from the constraint definition, if any\n * @returns The validation result, or a Promise resolving to it\n *\n * @example\n * ```ts\n * // Synchronous validator\n * const isUuid: ConstraintValidator = (value) => ({\n * valid: typeof value === \"string\" && /^[0-9a-f]{8}-/.test(value),\n * message: \"Value must be a valid UUID\",\n * });\n *\n * // Async validator\n * const isUniqueEmail: ConstraintValidator = async (value) => ({\n * valid: await checkEmailUniqueness(value as string),\n * message: \"Email must be unique\",\n * });\n *\n * const minAge: ConstraintValidator = (value, params) => ({\n * valid: typeof value === \"number\" && value >= (params?.min ?? 0),\n * message: `Value must be at least ${params?.min}`,\n * });\n * ```\n */\nexport type ConstraintValidator = (\n\tvalue: unknown,\n\tparams?: Record<string, unknown>,\n) => ConstraintValidationResult | Promise<ConstraintValidationResult>;\n\n/**\n * Registry mapping constraint names to their validator functions.\n *\n * Keys are constraint names as they appear in schema definitions\n * (e.g. `\"IsUuid\"`, `\"MinAge\"`).\n */\nexport type ConstraintValidatorRegistry = Record<string, ConstraintValidator>;\n\n/**\n * Options for the JsonSchemaCompatibilityChecker constructor.\n */\nexport interface CheckerOptions {\n\t/**\n\t * Registry of custom constraint validators.\n\t *\n\t * When provided, the checker can validate runtime data against\n\t * custom constraints defined in schemas via the `constraints` keyword.\n\t *\n\t * Constraint names must match those used in schema definitions.\n\t * Unknown constraints (present in a schema but absent from the registry)\n\t * will be reported as errors during runtime validation.\n\t */\n\tconstraints?: ConstraintValidatorRegistry;\n}\n"],"names":["SchemaErrorType"],"mappings":"oGAiCYA,yDAAAA,mBAAL,IAAA,AAAKA,sCAAAA,2oBAAAA"}
1
+ {"version":3,"sources":["../../src/types.ts"],"sourcesContent":["import type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\n\n// ─── Module augmentation ─────────────────────────────────────────────────────\n// Extends JSONSchema7 with the custom `constraints` keyword so that consumers\n// of this package see the property on every JSONSchema7 without needing a\n// separate ambient file or `/// <reference>` directive.\n\ndeclare module \"json-schema\" {\n\tinterface JSONSchema7 {\n\t\tconstraints?: Constraints;\n\t}\n}\n\n// ─── Public types ────────────────────────────────────────────────────────────\n\n/**\n * Discriminant for `SchemaError` — indicates the nature of the incompatibility.\n *\n * | Member | Description | Source module(s) |\n * |------------------------|--------------------------------------------------------------------|---------------------------|\n * | `TypeMismatch` | Incompatible types (e.g. `string` vs `number`, boolean schemas) | `semantic-errors.ts` |\n * | `MissingProperty` | Required property absent from the source schema | `semantic-errors.ts` |\n * | `Optionality` | Property required in target but optional in source | `semantic-errors.ts` |\n * | `EnumMismatch` | `enum` / `const` values are incompatible | `semantic-errors.ts` |\n * | `NumericConstraint` | `minimum`, `maximum`, `exclusiveMin/Max`, `multipleOf` | `semantic-errors.ts` |\n * | `StringConstraint` | `minLength`, `maxLength`, `pattern`, `format` | `semantic-errors.ts` |\n * | `ObjectConstraint` | `additionalProperties`, `min/maxProperties`, `propertyNames`, etc. | `semantic-errors.ts` |\n * | `ArrayConstraint` | `minItems`, `maxItems`, `uniqueItems`, `contains` | `semantic-errors.ts` |\n * | `NotSchema` | Incompatibility on the `not` keyword | `semantic-errors.ts` |\n * | `BranchMismatch` | No `anyOf` / `oneOf` branch matches | `semantic-errors.ts` |\n * | `RuntimeValidation` | Runtime data invalid against a standard JSON Schema keyword (AJV) | `runtime-validator.ts` |\n * | `CustomConstraint` | Custom constraint failed, unknown, or threw | `constraint-validator.ts` |\n */\nexport enum SchemaErrorType {\n\tTypeMismatch = \"type_mismatch\",\n\tMissingProperty = \"missing_property\",\n\tOptionality = \"optionality\",\n\tEnumMismatch = \"enum_mismatch\",\n\tNumericConstraint = \"numeric_constraint\",\n\tStringConstraint = \"string_constraint\",\n\tObjectConstraint = \"object_constraint\",\n\tArrayConstraint = \"array_constraint\",\n\tNotSchema = \"not_schema\",\n\tBranchMismatch = \"branch_mismatch\",\n\tRuntimeValidation = \"runtime_validation\",\n\tCustomConstraint = \"custom_constraint\",\n}\n\nexport interface SchemaError {\n\t/** Discriminant indicating the nature of the error */\n\ttype: SchemaErrorType;\n\t/** Normalized path to the concerned property (e.g. \"user.name\", \"users[].name\", \"accountId\") */\n\tkey: string;\n\t/** Type or value expected by the target schema (sup) */\n\texpected: string;\n\t/** Type or value received from the source schema (sub) */\n\treceived: string;\n}\n\nexport interface SubsetResult {\n\t/** true if sub ⊆ sup (every value valid for sub is also valid for sup) */\n\tisSubset: boolean;\n\t/** The schema resulting from the intersection allOf(sub, sup), or null if incompatible */\n\tmerged: JSONSchema7Definition | null;\n\t/** Semantic errors describing incompatibilities between the two schemas */\n\terrors: SchemaError[];\n}\n\n/**\n * Per-target validation options.\n *\n * When `partial` is `true`, runtime validation strips `required` and\n * `additionalProperties` constraints at every level of the schema before\n * passing it to AJV. This allows validating **only the properties present\n * in `data`** without false negatives on missing required properties or\n * extra properties not defined in the schema.\n *\n * Partial mode applies recursively: nested object schemas also have their\n * `required` and `additionalProperties` stripped.\n *\n * @example\n * ```ts\n * // Validate sup in partial mode — only check properties present in data\n * checker.check(sub, sup, {\n * data: { accountId: 'salut' },\n * validate: { sup: { partial: true } },\n * });\n * ```\n */\nexport interface ValidateTargetOptions {\n\t/**\n\t * When `true`, strip `required` and `additionalProperties` from the\n\t * schema before AJV validation so that only properties present in\n\t * `data` are validated.\n\t *\n\t * @default false\n\t */\n\tpartial?: boolean;\n}\n\n/**\n * Granular control over which schema(s) runtime validation applies to.\n *\n * When provided as an object, each key independently controls whether\n * runtime validation (AJV + custom constraints) runs against that schema:\n * - `sub`: validate `data` against the resolved/narrowed sub schema\n * - `sup`: validate `data` against the resolved/narrowed sup schema\n *\n * Each target accepts either a boolean or a `ValidateTargetOptions` object.\n * When `true`, validation runs with default options. When an object is\n * provided, its flags (e.g. `partial`) customize the validation behavior.\n *\n * Omitted keys default to `false`.\n *\n * @example\n * ```ts\n * // Validate only the sup schema (e.g. target input with constraints)\n * checker.check(sub, sup, { data: {}, validate: { sup: true } });\n *\n * // Validate only the sub schema\n * checker.check(sub, sup, { data: {}, validate: { sub: true } });\n *\n * // Validate both (equivalent to `validate: true`)\n * checker.check(sub, sup, { data: {}, validate: { sub: true, sup: true } });\n *\n * // Validate sup in partial mode (skip required / additionalProperties)\n * checker.check(sub, sup, {\n * data: { accountId: 'salut' },\n * validate: { sup: { partial: true } },\n * });\n * ```\n */\nexport interface ValidateTargets {\n\t/** When `true` or an options object, validate `data` against the resolved sub schema */\n\tsub?: boolean | ValidateTargetOptions;\n\t/** When `true` or an options object, validate `data` against the resolved sup schema */\n\tsup?: boolean | ValidateTargetOptions;\n}\n\n/**\n * Options for runtime-aware subset checking.\n *\n * When `data` is provided, the checker:\n * 1. Resolves `if/then/else` conditions in both `sub` and `sup` using `data`\n * (if `data` is `undefined`, conditions are resolved with `{}`)\n * 2. Narrows schemas using runtime values (e.g. enum materialization)\n * 3. Performs the static subset check on the resolved/narrowed schemas\n *\n * When `validate` is `true` (or an object with `sub`/`sup` flags), additional\n * runtime steps run **after** the static check passes:\n * 4. `data` is validated against the targeted resolved schema(s) via AJV\n * 5. Custom constraints are validated against `data` for the targeted schema(s)\n *\n * `data` can be a partial discriminant (e.g. `{ kind: \"text\" }`) used solely\n * for condition resolution and narrowing. It does **not** need to be a complete\n * instance of the schemas unless runtime validation is enabled.\n */\nexport interface CheckRuntimeOptions {\n\t/** Runtime data used for condition resolution, narrowing, and optionally runtime validation */\n\tdata: unknown;\n\n\t/**\n\t * Controls runtime validation of `data` against resolved schemas.\n\t *\n\t * - `true` — validate against **both** sub and sup schemas (AJV + constraints)\n\t * - `false` / omitted — no runtime validation (data used only for condition\n\t * resolution and narrowing)\n\t * - `{ sub: true }` — validate only against the sub schema\n\t * - `{ sup: true }` — validate only against the sup schema\n\t * - `{ sub: true, sup: true }` — equivalent to `true`\n\t * - `{ sup: { partial: true } }` — validate sup in partial mode\n\t * (skip `required` / `additionalProperties` enforcement)\n\t *\n\t * @default false\n\t */\n\tvalidate?: boolean | ValidateTargets;\n\n\t/**\n\t * Arbitrary context forwarded to every constraint validator during\n\t * this `check()` call.\n\t *\n\t * Useful for passing per-request state (e.g. tenant ID, user scope)\n\t * that is not encoded in the schema but is needed by validators at runtime.\n\t *\n\t * @example\n\t * ```ts\n\t * checker.check(sub, sup, {\n\t * data: { accountId: '123' },\n\t * validate: { sup: { partial: true } },\n\t * constraintContext: { companyId: 42 },\n\t * });\n\t * ```\n\t */\n\tconstraintContext?: ConstraintExecutionContext;\n}\n\n/**\n * Extended result from `check()` when runtime options are provided.\n * Includes resolution results for sub and sup in addition to the SubsetResult.\n */\nexport interface ResolvedSubsetResult extends SubsetResult {\n\tresolvedSub: ResolvedConditionResult;\n\tresolvedSup: ResolvedConditionResult;\n}\n\nexport interface ResolvedConditionResult {\n\t/** The schema with if/then/else resolved (flattened) */\n\tresolved: JSONSchema7;\n\t/** The branch that was applied (\"then\" | \"else\" | null if no condition) */\n\tbranch: \"then\" | \"else\" | null;\n\t/** The discriminant used for resolution */\n\tdiscriminant: Record<string, unknown>;\n}\n\nexport type Constraint =\n\t| string\n\t| {\n\t\t\tname: string;\n\t\t\tparams?: Record<string, unknown>;\n\t };\n\nexport type Constraints = Constraint | Constraint[];\n\n// ─── Constraint Validator types ──────────────────────────────────────────────\n\n/**\n * Arbitrary per-call context forwarded to every constraint validator\n * during a `check()` invocation.\n *\n * Allows callers to pass request-scoped information (e.g. tenant ID,\n * user ID, feature flags) that is not part of the schema definition\n * but is required by constraint validators at runtime.\n */\nexport type ConstraintExecutionContext = Record<string, unknown>;\n\n/**\n * Result of a constraint validation.\n */\nexport interface ConstraintValidationResult {\n\t/** Whether the value satisfies the constraint */\n\tvalid: boolean;\n\t/** Human-readable message when `valid` is `false` */\n\tmessage?: string;\n}\n\n/**\n * A constraint validator function.\n *\n * Receives the value to validate, optional params defined in the schema's\n * constraint definition, and an optional per-call execution context.\n *\n * Can be synchronous or asynchronous. When async validators are used,\n * `check()` with runtime options returns a `Promise`.\n *\n * @param value - The runtime value to validate\n * @param params - The `params` object from the constraint definition, if any\n * @param context - The per-call execution context passed via `constraintContext`, if any\n * @returns The validation result, or a Promise resolving to it\n *\n * @example\n * ```ts\n * // Synchronous validator\n * const isUuid: ConstraintValidator = (value) => ({\n * valid: typeof value === \"string\" && /^[0-9a-f]{8}-/.test(value),\n * message: \"Value must be a valid UUID\",\n * });\n *\n * // Async validator\n * const isUniqueEmail: ConstraintValidator = async (value) => ({\n * valid: await checkEmailUniqueness(value as string),\n * message: \"Email must be unique\",\n * });\n *\n * const minAge: ConstraintValidator = (value, params) => ({\n * valid: typeof value === \"number\" && value >= (params?.min ?? 0),\n * message: `Value must be at least ${params?.min}`,\n * });\n *\n * // Validator using execution context (e.g. tenant-scoped DB lookup)\n * const isUniqueInTenant: ConstraintValidator = async (value, params, context) => ({\n * valid: await checkUniqueness(value as string, context?.companyId as number),\n * message: \"Value must be unique within the tenant\",\n * });\n * ```\n */\nexport type ConstraintValidator = (\n\tvalue: unknown,\n\tparams?: Record<string, unknown>,\n\tcontext?: ConstraintExecutionContext,\n) => ConstraintValidationResult | Promise<ConstraintValidationResult>;\n\n/**\n * Registry mapping constraint names to their validator functions.\n *\n * Keys are constraint names as they appear in schema definitions\n * (e.g. `\"IsUuid\"`, `\"MinAge\"`).\n */\nexport type ConstraintValidatorRegistry = Record<string, ConstraintValidator>;\n\n/**\n * Options for the JsonSchemaCompatibilityChecker constructor.\n */\nexport interface CheckerOptions {\n\t/**\n\t * Registry of custom constraint validators.\n\t *\n\t * When provided, the checker can validate runtime data against\n\t * custom constraints defined in schemas via the `constraints` keyword.\n\t *\n\t * Constraint names must match those used in schema definitions.\n\t * Unknown constraints (present in a schema but absent from the registry)\n\t * will be reported as errors during runtime validation.\n\t */\n\tconstraints?: ConstraintValidatorRegistry;\n}\n"],"names":["SchemaErrorType"],"mappings":"oGAiCYA,yDAAAA,mBAAL,IAAA,AAAKA,sCAAAA,2oBAAAA"}
@@ -42,6 +42,17 @@ export declare function unionStrings(a: string[], b: string[]): string[];
42
42
  * Typed wrapper around deepEqual for schemas.
43
43
  */
44
44
  export declare function schemaDeepEqual(a: JSONSchema7Definition, b: JSONSchema7Definition): boolean;
45
+ /**
46
+ * Schema-aware deep equality that ignores non-semantic keywords
47
+ * (metadata like `title`, `description`, `default`, `examples`,
48
+ * and extension keywords like `x-tags`, `tags`, etc.).
49
+ *
50
+ * Unlike a naive recursive strip, this function understands JSON Schema
51
+ * structure: it only filters keys at schema level, and correctly recurses
52
+ * into property maps (where keys are user-defined property names, not
53
+ * schema keywords) and sub-schema keywords.
54
+ */
55
+ export declare function semanticDeepEqual(a: unknown, b: unknown): boolean;
45
56
  /**
46
57
  * Normalizes a `Constraints` value (single or array) into an array.
47
58
  * Returns an empty array if the value is `undefined`.