UNPKG

narrow-minded

Version:

Easy typeof validations with sophisticated TypeScript inference.

1 lines 26.4 kB
{"version":3,"file":"index.module.mjs","sources":["../src/schema.ts","../src/narrow.ts","../src/guard.ts","../src/traverse.ts","../src/diff.ts"],"sourcesContent":["/**\n * Includes all values that can be returned by a `typeof` expression.\n */\nexport type Primitive =\n\t| 'string'\n\t| 'number'\n\t| 'bigint'\n\t| 'boolean'\n\t| 'symbol'\n\t| 'undefined'\n\t| 'object'\n\t| 'function'\n\n/**\n * Reverse mapping `Primitive` type to the string that represents each primitive.\n */\n// prettier-ignore\nexport type UnPrimitive<N> =\n\tN extends 'string'\n\t? string\n\t: N extends 'number'\n\t? number\n\t: N extends 'bigint'\n\t? bigint\n\t: N extends 'boolean'\n\t? boolean\n\t: N extends 'symbol'\n\t? symbol\n\t: N extends 'undefined'\n\t? undefined\n\t: N extends 'object'\n\t? object\n\t: N extends 'function'\n\t? Function\n\t: unknown\n\n/**\n * Recursive type of an array Narow schema.\n */\nexport type NarrowerArr = Array<\n\tPrimitive | NarrowerObj | NarrowerArr | NarrowerSome\n>\n\n/**\n * Recursive type of an object Narow schema\n */\nexport interface NarrowerObj {\n\t[k: string]: Primitive | NarrowerArr | NarrowerObj | NarrowerSome\n}\n\n/**\n * This is the type that specifies a narrowed structure. The simplest form is a Primitive string,\n * which will validate using a `typeof` comparison. Deeper structures can be defined using objects\n * and arrays that will be validated recursively.\n *\n * @example\n * // An array of mixed strings and numbers:\n * ['string', 'number']\n *\n * // A deep object:\n * {\n * \tn: 'number',\n * \tchild: {\n * \t\tword: 'string'\n * \t},\n * \tthings: [\n * \t\t['number'],\n * \t\t'boolean'\n * \t],\n * }\n */\nexport type Narrower = Primitive | NarrowerArr | NarrowerObj | NarrowerSome\n\n/* eslint-disable @typescript-eslint/array-type */\n/**\n * This attempts to infer a narrowed type based on a Narrow schema, which results in nice types\n * within conditional blocks. If inference is not possible, the type remains `unknown`.\n *\n * An empty array as a schema is a special case: TypeScript wants to assume the contained type is\n * `never` (the array is empty, so the contents have no type) but this is not useful in practice, so\n * the content type is also replaced with `unknown`.\n */\n// prettier-ignore\nexport type UnNarrow<N> =\n\tN extends Primitive\n\t? UnPrimitive<N>\n\t: N extends Array<never>\n\t? Array<unknown>\n\t: N extends Array<infer N2>\n\t? N extends NarrowerSome\n\t\t? UnNarrow<N2>\n\t\t: Array<UnNarrow<N2>>\n\t: N extends Record<keyof N, infer _N2>\n\t? { [k in keyof N]: UnNarrow<N[k]> }\n\t: unknown\n/* eslint-enable @typescript-eslint/array-type */\n\n/**\n * Unique symbol that is used to decorate an array of Narrower schemas.\n */\nexport const SOME = Symbol('SOME')\n\n/**\n * Supplemental type for `SOME` decorated arrays. Note that this _does not_ intersect with an\n * `Array` type of any kind, because the array type must be kept generic in order for inference and\n * un-narrowing to work.\n */\nexport type NarrowerSome = {\n\t[SOME]: true\n}\n\n/**\n * Decorates a narrower array to indicate narrowing should use the array as a\n * set of options instead of asserting the value is an actual array.\n *\n * @example\n * narrow(some('number'), 1) //=> true\n * narrow({ optional: some('string', 'undefined') }), { optional: 'yep' }) //=> true\n * narrow({ optional: some('string', 'undefined') }), {}) //=> true\n *\n * @param narrowers The Narrower sub-schemas that the value must be one of.\n * @returns An array with the SOME symbol set to true.\n */\nexport const some = <NA extends NarrowerArr>(\n\t...narrowers: NA\n): NA & NarrowerSome => {\n\treturn Object.assign(narrowers, {\n\t\t[SOME]: true,\n\t} as const)\n}\n\n/**\n * Type guard for `NarrowerArr` type.\n * @param n Narrower schema\n * @returns true if `n` is an array and is _not_ `SOME` decorated\n */\nexport const isNarrowerArr = (\n\tn: Narrower,\n): n is NarrowerArr & { [SOME]: never } => Array.isArray(n) && !(SOME in n)\n\n/**\n * Type guard for `NarrowerSome` type.\n * @param n Narrower schema\n * @returns true if `n` is a `SOME` decorated array.\n */\nexport const isNarrowerSome = (n: Narrower): n is NarrowerArr & NarrowerSome =>\n\tArray.isArray(n) && SOME in n\n\n/**\n * Type guard for `NarrowerObj` type\n * @param n Narrower schema\n * @returns true if `n` is an indexable object.\n */\nexport const isNarrowerObj = (\n\tn: Narrower,\n): n is NarrowerObj & { [SOME]: never } => isRecordObj(n)\n\n/**\n * Type guard for an indexable object\n * @param u Any value\n * @returns true if `u` is a non-array, non-null object.\n */\nexport const isRecordObj = (u: unknown): u is Record<string, unknown> =>\n\ttypeof u === 'object' && u !== null && !Array.isArray(u)\n","import {\n\tNarrower,\n\tNarrowerArr,\n\tNarrowerObj,\n\tNarrowerSome,\n\tPrimitive,\n\tSOME,\n\tUnNarrow,\n} from './schema'\n\n/**\n * This function validates any value with `typeof` checks. Arrays and objects are traversed\n * according to the Narrower structure. The boolean return value is also a TypeScript type\n * predicate.\n *\n * **Objects** -\n * All keys of `n` are checked against `u` and their narrow is validated if the key exists.\n * Keys that are missing from `u` are treated as having the value `undefined`. This means\n * you can use `{ key: some('undefined', ...)}` to allow for missing/optional keys.\n *\n * **Arrays** -\n * Including multiple types in a Narrower array allows for mixed types. Each item in `u` must\n * satisfy at least one of the types.\n *\n * **Null** -\n * `typeof null` is `'object'` but null cannot have any keys. Use `{}` to match an object\n * that is not null.\n *\n * @example\n * // An array of mixed strings and numbers:\n * narrow(['string', 'number'], [1, 'two']) //=> true\n * narrow(['string', 'number'], [{}]) //=> false\n *\n * // Null:\n * narrow('object', null) //=> true\n * narrow({}, null) //=> false\n *\n * // A deep object:\n * narrow({\n * \tn: 'number',\n * \tchild: {\n * \t\tword: 'string'\n * \t},\n * \tthings: [\n * \t\t['number'],\n * \t\t'boolean'\n * \t],\n * }, {\n * \tn: 3.14,\n * \tchild: {\n * \t\tword: 'Yes'\n * \t},\n * \tthings: [\n * \t\tfalse,\n * \t\t[1, 2, 3],\n * \t\ttrue\n * \t]\n * }) //=> true\n *\n * @param n The Narrower schema.\n * @param u The value of unknown type to validate.\n * @returns A type predicate that `u` satisfies `n`.\n */\nexport const narrow = <\n\tN extends Primitive | NarrowerArr | NarrowerObj | NarrowerSome,\n>(\n\tn: N,\n\tu: unknown,\n): u is UnNarrow<N> => {\n\treturn _narrow(n, u)\n}\n\n/**\n * This does the actual value comparison based on the Narrower schema.\n * It leaves out the fancy type inference.\n * @private\n * @param n The schema.\n * @param u The value to validate.\n * @returns Whether u matches n.\n */\nconst _narrow = <N extends Narrower>(n: N, u: unknown): boolean => {\n\tif (typeof n === 'string') {\n\t\tif (n === typeof u) {\n\t\t\treturn true\n\t\t} else {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tif (Array.isArray(n)) {\n\t\tif (SOME in n) {\n\t\t\treturn n.some(t => _narrow(t, u))\n\t\t} else {\n\t\t\tif (Array.isArray(u)) {\n\t\t\t\tif (n.length === 0) {\n\t\t\t\t\t// An empty schema array represents an array with unknown contents.\n\t\t\t\t\treturn true\n\t\t\t\t}\n\n\t\t\t\treturn u.every(v => n.some(t => _narrow(t, v)))\n\t\t\t} else {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\n\tif (typeof u !== 'object' || u === null) {\n\t\treturn false\n\t}\n\n\tconst o = u as NarrowerObj\n\n\treturn Object.entries(n).every(([k, t]) => _narrow(t, o[k]))\n}\n","import { Narrower, UnNarrow } from './schema'\nimport { narrow } from './narrow'\n\n/**\n * Creates a function from a narrower schema that can be reused to narrow objects.\n * This simple closure can be used when a whole Guard instance would be too much.\n *\n * @example\n * import { satisfier } from 'narrow-minded'\n * const satisfies = satisfier(['string', 'number'])\n * satisfies(['horse', 42]) // => true\n */\nexport const satisfier =\n\t<N extends Narrower>(n: N) =>\n\t(u: unknown): u is UnNarrow<N> =>\n\t\tnarrow(n, u)\n\nexport type NarrowingFunction<P> = (u: unknown) => u is P\nexport type Payload<G> = G extends Guard<infer P> ? P : unknown\n\nexport class Guard<P> {\n\t/**\n\t * Creates a new guard that uses a `narrow` function.\n\t * A little shortcut for `new Guard(narrow(...))`.\n\t * @example\n\t *\n\t * import { Guard } from 'narrow-minded'\n\t * const myGuard = Guard.narrow(['string', 'number'])\n\t * myGuard.satisfied(['horse', 42]) // => true\n\t *\n\t * @param n Narrower\n\t * @returns Guard\n\t */\n\tstatic narrow<N extends Narrower>(n: N) {\n\t\treturn new Guard((u: unknown): u is UnNarrow<N> => narrow(n, u))\n\t}\n\n\treadonly NF: NarrowingFunction<P>\n\n\tconstructor(NF: NarrowingFunction<P>) {\n\t\tthis.NF = NF\n\t}\n\n\t/**\n\t * Runs the guard's narrowing function to validate the unknown value's type.\n\t * Operates as a type predicate so conditional blocks infer this structure.\n\t * @example\n\t *\n\t * const myGuard = Guard.narrow({\n\t * \tname: 'string',\n\t * \tvalues: ['number'],\n\t * })\n\t *\n\t * const good: unknown = { name: 'Horse', values: [1, 2] }\n\t * if (myGuard.satisfied(good)) {\n\t * \tconsole.log('Good ' + good.name)\n\t * \t// => 'Good Horse'\n\t * }\n\t *\n\t * const bad: unknown = { name: 42, values: 'Nope' }\n\t * if (!myGuard.satisfied(bad)) {\n\t * \tconsole.log('Bad ')\n\t * \t// => 'Bad'\n\t * }\n\t *\n\t * @param u The unknown value.\n\t * @returns A type predicate that `u` satisfies this guard.\n\t */\n\tsatisfied(u: unknown): u is P {\n\t\treturn this.NF(u)\n\t}\n\n\t/**\n\t * An identity function that returns the value passed to it. Useful for\n\t * defining objects that satisfy this guard using type inference.\n\t * @param p\n\t * @returns p\n\t */\n\tbuild(p: P): P {\n\t\treturn p\n\t}\n\n\t/**\n\t * Creates a new guard that will satisfy the constraints of `this` AND `other`.\n\t * Useful for combining primitive narrows with more complex type checking.\n\t * @example\n\t * const myGuard = Guard.narrow({ type: 'string' }).and(\n\t * \t(u: unknown): u is { type: 'this' | 'that' } =>\n\t * \t\t['this', 'that'].includes((u as any).type),\n\t * )\n\t *\n\t * @param other - Another Guard or a Narrower/NarrowerFunction which will\n\t * be wrapped into a Guard automatically.\n\t * @return Guard\n\t */\n\tand<N extends Narrower>(other: N): Guard<P & UnNarrow<N>>\n\tand<P2>(other: Guard<P2> | NarrowingFunction<P2>): Guard<P & P2>\n\tand<P2>(other: any) {\n\t\tconst left = this.NF\n\t\tconst right =\n\t\t\tother instanceof Guard\n\t\t\t\t? other.NF\n\t\t\t\t: other instanceof Function\n\t\t\t\t\t? other\n\t\t\t\t\t: (u: unknown): u is P2 => narrow(other, u)\n\t\treturn new Guard((u: unknown): u is P & P2 => left(u) && right(u))\n\t}\n}\n\n/**\n * A singleton that can be used to build `and` chains.\n * @example\n * if (unknown.and('string').satisfied('Great')) {\n * \tconsole.log('Great')\n * }\n */\nexport const unknown = new Guard<unknown>((_): _ is unknown => true)\n","export type TraversalNode<V = unknown> = {\n\tproperty: string\n\tvalue: V\n\tlevel: number\n\tparent: TraversalNode<V> | undefined\n}\n\nexport type TraversalVisit<V = unknown, R = unknown> = (\n\tnode: TraversalNode<V>,\n) => R\n\nexport type TraversalDequeue<V = unknown> = (\n\tq: Array<TraversalNode<V>>,\n) => TraversalNode<V>\n\nexport type TraversalEnqueue<V = unknown, R = unknown> = (\n\tnode: TraversalNode<V>,\n\tq: Array<TraversalNode<V>>,\n\tvisitResult: R,\n) => void\n\nexport type TraversalOptions<V = unknown, R = unknown> = {\n\tvisit: TraversalVisit<V, R>\n\tdequeue: TraversalDequeue<V>\n\tenqueue: TraversalEnqueue<V, R>\n}\n\nexport const makeSubnodes = (node: TraversalNode): TraversalNode[] => {\n\tconst { value, level } = node\n\n\tif (typeof value !== 'object' || value === null) {\n\t\treturn []\n\t}\n\n\treturn Object.entries(value).map(([childProperty, childValue]) => ({\n\t\tproperty: childProperty,\n\t\tvalue: childValue,\n\t\tlevel: level + 1,\n\t\tparent: node,\n\t}))\n}\n\nexport const shouldContinue = (visitResult: unknown) =>\n\ttypeof visitResult === 'undefined' || Boolean(visitResult)\n\nexport type TraversalOrder = 'breadth' | 'depth'\n\nexport const DEFAULT_TRAVERSALS: Record<\n\tTraversalOrder,\n\t{\n\t\tdequeue: TraversalDequeue\n\t\tenqueue: TraversalEnqueue\n\t}\n> = {\n\tdepth: {\n\t\tdequeue: q => q.pop()!,\n\t\tenqueue: (node, q, visitResult) => {\n\t\t\tif (!shouldContinue(visitResult)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst subnodes = makeSubnodes(node)\n\t\t\tsubnodes.reverse()\n\t\t\tq.push(...subnodes)\n\t\t},\n\t},\n\tbreadth: {\n\t\tdequeue: q => q.shift()!,\n\t\tenqueue: (node, q, visitResult) => {\n\t\t\tif (!shouldContinue(visitResult)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst subnodes = makeSubnodes(node)\n\t\t\tq.push(...subnodes)\n\t\t},\n\t},\n}\n\n/**\n * Generic tree traversal.\n */\nexport const traverse = <V = unknown, R = unknown>(\n\troot: V,\n\t{ visit, dequeue, enqueue }: TraversalOptions<V, R>,\n) => {\n\tconst q: Array<TraversalNode<V>> = [\n\t\t{\n\t\t\tproperty: '',\n\t\t\tvalue: root,\n\t\t\tlevel: 0,\n\t\t\tparent: undefined,\n\t\t},\n\t]\n\n\twhile (q.length > 0) {\n\t\tconst node = dequeue(q)\n\t\tconst visitResult = visit(node)\n\t\tenqueue(node, q, visitResult)\n\t}\n}\n\nexport const traverseObjectDepthFirst = (\n\troot: unknown,\n\tvisit: TraversalVisit,\n) =>\n\ttraverse(root, {\n\t\tvisit,\n\t\tdequeue: DEFAULT_TRAVERSALS.depth.dequeue,\n\t\tenqueue: DEFAULT_TRAVERSALS.depth.enqueue,\n\t})\n\nexport const traverseObjectBreadthFirst = (\n\troot: unknown,\n\tvisit: TraversalVisit,\n) =>\n\ttraverse(root, {\n\t\tvisit,\n\t\tdequeue: DEFAULT_TRAVERSALS.breadth.dequeue,\n\t\tenqueue: DEFAULT_TRAVERSALS.breadth.enqueue,\n\t})\n","import {\n\tisNarrowerArr,\n\tisNarrowerObj,\n\tisNarrowerSome,\n\tisRecordObj,\n\tNarrower,\n\tsome,\n} from './schema'\nimport { TraversalNode, traverse } from './traverse'\n\ntype Diff = {\n\texpected: Narrower\n\treceived: unknown\n}\ntype DiffNode = TraversalNode<Diff>\n\n/**\n *\n */\nexport type DiffResult = {\n\tlevel: number\n\tproperty: string\n\texpected: Narrower\n\treceived: unknown\n}\n\n/**\n *\n * @example\n * // Given this failed narrow:\n * narrow(\n * \t{\n * \t\ttitle: 'string',\n * \t\tcount: 'number',\n * \t},\n * \t{\n * \t\ttitle: 10,\n * \t\tcount: 'bad boy',\n * \t},\n * )\n * //=> false\n *\n * // The diff would be:\n * diffNarrow(\n * \t{\n * \t\ttitle: 'string',\n * \t\tcount: 'number',\n * \t},\n * \t{\n * \t\ttitle: 10,\n * \t\tcount: 'bad boy',\n * \t},\n * )\n * //=>\n * [\n * \t{\n * \t\tlevel: 1,\n * \t\tproperty: 'title',\n * \t\texpected: 'string',\n * \t\treceived: 10,\n * \t},\n * \t{\n * \t\tlevel: 1,\n * \t\tproperty: 'count',\n * \t\texpected: 'number',\n * \t\treceived: 'bad boy',\n * \t},\n * ]\n * @param n\n * @param u\n * @returns Array of `DiffResult` objects that indicate where `u` failed to satisfy `n`. An empty\n * array is the same as `narrow(n, u)` returning true.\n */\nexport const diffNarrow = <N extends Narrower>(n: N, u: unknown) => {\n\tconst diffResults: DiffResult[] = []\n\n\tconst root: Diff = {\n\t\texpected: n,\n\t\treceived: u,\n\t}\n\n\ttraverse(root, {\n\t\tvisit: node => {\n\t\t\tconst { value } = node\n\t\t\tconst { expected, received } = value\n\n\t\t\tif (isShallowMatch(expected, received)) {\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\t// If there is an ancestor some-arr that hasn't been traversed yet, then don't record a diff\n\t\t\t// yet. Once we get to only 1 some-arr entry remaining, `findAncestorSome` will return\n\t\t\t// nothing.\n\t\t\t//\n\t\t\t// This is called a second time within `enqueue` which is a performance hit. This could be\n\t\t\t// restructured to only do the tree climb once.\n\t\t\tif (findAncestorSome(node)) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tdiffResults.push({\n\t\t\t\tlevel: node.level,\n\t\t\t\tproperty: node.property,\n\t\t\t\texpected,\n\t\t\t\treceived,\n\t\t\t})\n\n\t\t\treturn false\n\t\t},\n\t\tdequeue: q => q.pop()!,\n\t\tenqueue: (node, q, visitResult: boolean) => {\n\t\t\tif (visitResult) {\n\t\t\t\tconst subnodes = makeDiffNodes(node)\n\t\t\t\tsubnodes.reverse()\n\t\t\t\tq.push(...subnodes)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst altAncestor = makeAltSomeAncestor(node)\n\t\t\tif (altAncestor) {\n\t\t\t\tq.push(altAncestor)\n\t\t\t}\n\t\t},\n\t})\n\n\treturn diffResults\n}\n\n/**\n * Checks if `node` needs to be traversed and builds the required sub-nodes.\n */\nconst makeDiffNodes = (node: DiffNode): DiffNode[] => {\n\tconst { expected, received } = node.value\n\n\tif (isNarrowerObj(expected) && isRecordObj(received)) {\n\t\treturn Object.entries(expected).map(([keySub, expectedSub]): DiffNode => {\n\t\t\tconst receivedSub = received[keySub]\n\n\t\t\treturn {\n\t\t\t\tparent: node,\n\t\t\t\tlevel: node.level + 1,\n\t\t\t\tproperty: keySub,\n\t\t\t\tvalue: {\n\t\t\t\t\texpected: expectedSub,\n\t\t\t\t\treceived: receivedSub,\n\t\t\t\t},\n\t\t\t}\n\t\t})\n\t}\n\n\tif (isNarrowerArr(expected) && Array.isArray(received)) {\n\t\treturn received.map((receivedSub, receivedIdx): DiffNode => {\n\t\t\tconst expectArrAsSome = some(...expected)\n\n\t\t\treturn {\n\t\t\t\tparent: node,\n\t\t\t\tlevel: node.level + 1,\n\t\t\t\tproperty: receivedIdx.toString(),\n\t\t\t\tvalue: {\n\t\t\t\t\texpected: expectArrAsSome,\n\t\t\t\t\treceived: receivedSub,\n\t\t\t\t},\n\t\t\t}\n\t\t})\n\t}\n\n\tif (isNarrowerSome(expected)) {\n\t\t// Every some-arr is shallow-matched against the first entry, so only enqueue a node for the\n\t\t// first entry. If `visit` results in a mismatch down the this branch, `makeAltSomeAncestor`\n\t\t// creates a node with that first branch removed.\n\t\treturn [\n\t\t\t{\n\t\t\t\tparent: node,\n\t\t\t\tlevel: node.level,\n\t\t\t\tproperty: node.property,\n\t\t\t\tvalue: {\n\t\t\t\t\texpected: expected[0]!,\n\t\t\t\t\treceived,\n\t\t\t\t},\n\t\t\t},\n\t\t]\n\t}\n\n\treturn []\n}\n\n/**\n * First element is the input leaf. Last element is the highest ancestor (root).\n */\nconst listAncestors = (leaf: DiffNode): DiffNode[] => {\n\tconst ancestors: DiffNode[] = [leaf]\n\twhile (true) {\n\t\tconst child = ancestors[0]\n\t\tconst parent = child?.parent\n\t\tif (!parent) {\n\t\t\tbreak\n\t\t}\n\t\tancestors.unshift(parent)\n\t}\n\tancestors.reverse()\n\treturn ancestors\n}\n\n/**\n * Traverses the ancestors to find the lowest one that is a some-arr and has >1 entry. A some-arr with 1\n * entry would be the ancestor that led to the current (unmatched) node. Includes the input leaf as\n * the first element (if it has remaining entries).\n */\nconst findAncestorSome = (leaf: DiffNode): DiffNode | undefined => {\n\tconst ancestors = listAncestors(leaf)\n\treturn ancestors.find(parent => {\n\t\tconst {\n\t\t\tvalue: { expected },\n\t\t} = parent\n\t\treturn isNarrowerSome(expected) && expected.length > 1\n\t})\n}\n\n/**\n * Searches the node's ancestors for the deepest some-node that has remaining alternative schemas.\n * If found, the first some-entry is popped and a copy of the node with that (already checked) entry\n * removed.\n */\nconst makeAltSomeAncestor = (\n\tnode: TraversalNode<Diff>,\n): DiffNode | undefined => {\n\tconst ancestorSomeNode = findAncestorSome(node)\n\n\tconst ancestorExpected =\n\t\tancestorSomeNode?.value?.expected &&\n\t\tisNarrowerSome(ancestorSomeNode?.value?.expected)\n\t\t\t? ancestorSomeNode.value.expected\n\t\t\t: undefined\n\n\t// This is the common case when there are no some-arr ancestors at all. It also comes up when all\n\t// ancestor some-arrs have been exhausted (reduced to only 1 option).\n\tif (!(ancestorSomeNode && ancestorExpected)) {\n\t\treturn undefined\n\t}\n\n\t// Create a copy of the ancestor node with the first some-entry removed.\n\treturn {\n\t\tparent: ancestorSomeNode.parent,\n\t\tlevel: ancestorSomeNode.level,\n\t\tproperty: ancestorSomeNode.property,\n\t\tvalue: {\n\t\t\texpected: some(...ancestorExpected.slice(1)),\n\t\t\treceived: ancestorSomeNode.value.received,\n\t\t},\n\t}\n}\n\nconst isShallowMatch = (n: Narrower, u: unknown): boolean => {\n\tif (typeof n === 'string') {\n\t\treturn n === typeof u\n\t}\n\n\tif (isNarrowerObj(n)) {\n\t\treturn isRecordObj(u)\n\t}\n\n\tif (isNarrowerArr(n)) {\n\t\treturn Array.isArray(u)\n\t}\n\n\tif (isNarrowerSome(n)) {\n\t\t// An empty some-arr matches nothing.\n\t\tif (n.length === 0) {\n\t\t\treturn false\n\t\t}\n\n\t\t// A shallow match only checks the first entry.\n\t\tconst firstNSub = n[0]!\n\t\treturn isShallowMatch(firstNSub, u)\n\t}\n\n\treturn false\n}\n"],"names":["SOME","Symbol","some","_Object$assign","Object","assign","slice","call","arguments","isNarrowerArr","n","Array","isArray","isNarrowerSome","isNarrowerObj","isRecordObj","u","narrow","_narrow","t","length","every","v","o","entries","_ref","satisfier","Guard","NF","this","_proto","prototype","satisfied","build","p","and","other","left","right","Function","unknown","_","makeSubnodes","node","value","level","map","property","childValue","parent","shouldContinue","visitResult","Boolean","DEFAULT_TRAVERSALS","depth","dequeue","q","pop","enqueue","subnodes","reverse","push","apply","breadth","shift","traverse","root","_ref2","visit","undefined","traverseObjectDepthFirst","traverseObjectBreadthFirst","diffNarrow","diffResults","expected","received","isShallowMatch","findAncestorSome","makeDiffNodes","altAncestor","makeAltSomeAncestor","_node$value","keySub","expectedSub","receivedSub","receivedIdx","expectArrAsSome","toString","leaf","ancestors","child","unshift","listAncestors","find","_ancestorSomeNode$val","_ancestorSomeNode$val2","ancestorSomeNode","ancestorExpected"],"mappings":"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"}