/**
 * traversalHelpers — module-level boundary validators and normalizers
 * used by LogicalTraversal.
 *
 * @module domain/services/query/traversalHelpers
 */
import TraversalError from "../../errors/TraversalError.js";
import ORSet from "../../crdt/ORSet.js";
// ── Helpers ────────────────────────────────────────────────────────────────
/**
 * Returns true when a value is a materialized adjacency record.
 */
export function isAdjacencyMaps(adjacency) {
    return (adjacency !== null &&
        typeof adjacency === 'object' &&
        adjacency['outgoing'] instanceof Map && // nosemgrep: ts-no-record-string-unknown-outside-adapters -- 0025B; nosemgrep: ts-no-unknown-outside-adapters -- 0025B
        adjacency['incoming'] instanceof Map // nosemgrep: ts-no-record-string-unknown-outside-adapters -- 0025B; nosemgrep: ts-no-unknown-outside-adapters -- 0025B
    );
}
/**
 * Validates the materialized adjacency boundary before traversal.
 *
 * @throws {TraversalError} code 'E_TRAVERSAL_ADJACENCY' if invalid
 */
export function requireAdjacencyMaps(adjacency) {
    if (!isAdjacencyMaps(adjacency)) {
        throw new TraversalError('materialized traversal adjacency is invalid', {
            code: 'E_TRAVERSAL_ADJACENCY',
        });
    }
    return adjacency;
}
/**
 * Validates the materialized traversal state boundary before building the provider.
 *
 * @throws {TraversalError} code 'E_TRAVERSAL_STATE' if invalid
 */
export function requireTraversalState(state) {
    if (state === null ||
        typeof state !== 'object' ||
        !(state['nodeAlive'] instanceof ORSet) // nosemgrep: ts-no-record-string-unknown-outside-adapters -- 0025B; nosemgrep: ts-no-unknown-outside-adapters -- 0025B
    ) {
        throw new TraversalError('materialized traversal state is missing nodeAlive', {
            code: 'E_TRAVERSAL_STATE',
        });
    }
    return state;
}
/**
 * Strips keys whose value is `undefined` from an object so that
 * `exactOptionalPropertyTypes` doesn't complain about explicit `undefined`
 * being assigned to optional-but-not-undefined-typed properties.
 *
 * The return type is `WithoutUndefined<T>` so that properties whose values
 * were `T | undefined` become `T`, making the object assignable to params
 * typed with strict optional properties.
 */
export function stripUndefined(// nosemgrep: ts-no-record-string-unknown-outside-adapters -- 0025B; nosemgrep: ts-no-unknown-outside-adapters -- 0025B
obj) {
    const out = {}; // nosemgrep: ts-no-record-string-unknown-outside-adapters -- 0025B; nosemgrep: ts-no-unknown-outside-adapters -- 0025B
    for (const key of Object.keys(obj)) {
        if (obj[key] !== undefined) {
            out[key] = obj[key];
        }
    }
    return out;
}
/**
 * Validates and normalizes an edge direction parameter.
 *
 * @throws {TraversalError} code 'INVALID_DIRECTION' if not a valid direction
 */
export function assertDirection(direction) {
    if (direction === undefined) {
        return 'out';
    }
    if (direction === 'out' || direction === 'in' || direction === 'both') {
        return direction;
    }
    throw new TraversalError(`Invalid direction: ${direction}`, {
        code: 'INVALID_DIRECTION',
        context: { direction },
    });
}
/**
 * Normalizes a label filter into a Set for efficient lookup.
 *
 * Accepts a single label string, an array of labels, or undefined. Returns
 * a Set containing the label(s) or undefined if no filter is specified.
 *
 * @throws {TraversalError} code 'INVALID_LABEL_FILTER' if type is not string, array, or undefined
 */
export function normalizeLabelFilter(labelFilter) {
    if (labelFilter === undefined) {
        return undefined;
    }
    if (Array.isArray(labelFilter)) {
        return new Set(labelFilter);
    }
    if (typeof labelFilter === 'string') {
        return new Set([labelFilter]);
    }
    throw new TraversalError('labelFilter must be a string or array', {
        code: 'INVALID_LABEL_FILTER',
        context: { receivedType: typeof labelFilter },
    });
}
