import { s as sortBy, m as mergeWith } from '../mergeWith-VagwCqUi.js';
import { importShared } from '../__federation_fn_import-BLFDCY-V.js';
import { A as isEqual, y as isPlainObject$1 } from '../isEqual-tnzoadW8.js';

/**
 * Asserts that an array has no duplicate values based on an optional key function.
 * Throws an error if duplicates are found, listing all duplicate keys.
 *
 * @template T The type of elements in the array.
 * @param items The array to check for duplicates.
 * @param context A string describing the context for the error message (e.g., "route loader fields").
 * @param keyFn A function that extracts the comparison key from each element. Defaults to identity.
 * @throws Error if duplicate keys are found.
 */
function assertNoDuplicates(items, context, keyFn) {
    const getKey = keyFn ?? ((item) => item);
    const seen = new Map();
    const duplicates = [];
    for (const item of items) {
        const key = getKey(item);
        const count = seen.get(key) ?? 0;
        seen.set(key, count + 1);
        if (count === 1) {
            // First time we see a duplicate
            duplicates.push(key);
        }
    }
    if (duplicates.length > 0) {
        const duplicateList = duplicates
            .map((key) => JSON.stringify(key))
            .join(', ');
        throw new Error(`Duplicate ${context} found: ${duplicateList}`);
    }
}

function normalizeContent(content) {
    if (typeof content === 'string') {
        const encoder = new TextEncoder();
        const { buffer } = encoder.encode(content);
        if (!(buffer instanceof ArrayBuffer)) {
            throw new TypeError('Buffer is not an ArrayBuffer');
        }
        return buffer;
    }
    return content;
}
async function hashWithSHA256(content) {
    const data = normalizeContent(content);
    const hashBuffer = await crypto.subtle.digest('SHA-256', data);
    const hashArray = [...new Uint8Array(hashBuffer)];
    return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
}

/**
 * Enhances an error with additional context while preserving the original stack trace
 * @param {Error} originalError - The original error that was caught
 * @param {string} contextMessage - Additional context to prepend to the error message
 * @param {Object} [additionalProps] - Optional object with additional properties to add to the error
 * @returns {Error} - Enhanced error with preserved stack trace
 */
function enhanceErrorWithContext(originalError, contextMessage) {
    // Create a new error with combined message
    const enhancedError = new Error(`${contextMessage}:\n${originalError instanceof Error
        ? originalError.message
        : String(originalError)}`);
    // Preserve the original stack if possible
    if (originalError instanceof Error) {
        enhancedError.stack = originalError.stack;
    }
    return enhancedError;
}

/**
 * Class providing typed event emitter functionality.
 *
 * @template T - Mapping of event names to payload types.
 */
class TypedEventEmitter {
    listenerMap = new Map();
    /**
     * Abort controller for the event emitter to terminate all async generator listeners.
     */
    eventEmitterAbortController = new AbortController();
    /**
     * Registers a listener for an event.
     *
     * @template K - The event name.
     * @param eventName - Name of the event.
     * @param listener - Callback invoked with the event payload.
     * @param options - Options for the listener.
     * @returns Function to unregister the listener.
     */
    on(eventName, listener, options) {
        const { signal } = options ?? {};
        const existingListeners = this.listenerMap.get(eventName) ?? [];
        this.listenerMap.set(eventName, [
            ...existingListeners,
            listener,
        ]);
        // Returns a function that unregisters this listener.
        const unregister = () => {
            const updatedListeners = (this.listenerMap.get(eventName) ?? []).filter((l) => l !== listener);
            if (updatedListeners.length > 0) {
                this.listenerMap.set(eventName, updatedListeners);
            }
            else {
                this.listenerMap.delete(eventName);
            }
        };
        signal?.addEventListener('abort', unregister, {
            once: true,
        });
        return unregister;
    }
    /**
     * Creates an async generator for an event.
     *
     * @template K - The event name.
     * @param eventName - Name of the event.
     * @param options - Additional options.
     * @returns An async generator yielding event payloads.
     */
    async *onAsync(eventName, options) {
        // Create a queue to store events when consumer isn't ready
        const queue = [];
        let pendingNext;
        let isDone = false;
        // Return early if the signal is aborted
        const eventEmitterAbortSignal = this.eventEmitterAbortController.signal;
        if (options.signal?.aborted || eventEmitterAbortSignal.aborted)
            return;
        function abortHandler() {
            isDone = true;
            // trigger dummy event to unblock the consumer
            pendingNext?.(undefined);
        }
        options.signal?.addEventListener('abort', abortHandler, { once: true });
        eventEmitterAbortSignal.addEventListener('abort', abortHandler, {
            once: true,
        });
        this.on(eventName, (payload) => {
            if (pendingNext) {
                pendingNext(payload);
                pendingNext = undefined;
            }
            else {
                queue.push(payload);
            }
        }, { signal: options.signal });
        // Keep yielding events until done
        while (!isDone) {
            const payload = queue.shift() ??
                (await new Promise((resolve) => {
                    pendingNext = resolve;
                }));
            // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- false positive since isDone is set to true in the abort handler
            if (isDone)
                break;
            yield payload;
        }
    }
    /**
     * Emits an event with the given payload.
     *
     * @template K - The event name.
     * @param eventName - Name of the event.
     * @param payload - Payload for the event.
     */
    emit(eventName, payload) {
        const listeners = this.listenerMap.get(eventName) ?? [];
        for (const listener of listeners) {
            listener(payload);
        }
    }
    /**
     * Removes all registered event listeners.
     *
     * Note: This is dangerous and should only be used in testing.
     */
    clearListeners() {
        this.listenerMap.clear();
        this.eventEmitterAbortController.abort();
    }
    /**
     * Aborts the event emitter to terminate all async generator listeners.
     */
    abortEventEmitter() {
        this.eventEmitterAbortController.abort();
    }
}
/**
 * Creates a typed event emitter that supports event listening and emitting.
 *
 * @template T - Mapping of event names to payload types.
 * @returns An emitter with `on`, `emit`, and `clear` methods.
 */
function createTypedEventEmitter() {
    return new TypedEventEmitter();
}

/**
 * Field container for a single value that cannot be overridden once set
 */
class ScalarContainer {
    _value;
    defaultValue;
    isSet = false;
    setBySource;
    getDynamicSource;
    constructor(defaultValue, options) {
        this.defaultValue = defaultValue;
        this.getDynamicSource = options?.getDynamicSource;
    }
    getValue() {
        return this._value === undefined ? this.defaultValue : this._value;
    }
    set(value, source) {
        const setBySource = source ?? this.getDynamicSource?.() ?? 'unknown';
        if (this.isSet) {
            throw new Error(`Value has already been set by ${this.setBySource} and cannot be overwritten by ${setBySource}`);
        }
        this._value = value;
        this.isSet = true;
        this.setBySource = setBySource;
    }
}
// Array field container
class ArrayContainer {
    _stripDuplicates;
    _value;
    constructor(initialValue, options) {
        this._stripDuplicates = options?.stripDuplicates ?? false;
        this._value = initialValue ?? [];
    }
    push(...items) {
        let currentValue = this._value;
        if (this._stripDuplicates) {
            // Add items without duplicates
            const set = new Set([...currentValue, ...items]);
            currentValue = [...set];
        }
        else {
            // Add all items
            currentValue = [...currentValue, ...items];
        }
        this._value = currentValue;
    }
    getValue() {
        return this._value;
    }
}
class ObjectContainer {
    map;
    getDynamicSource;
    constructor(initialValue, options) {
        this.getDynamicSource = options?.getDynamicSource;
        this.map = new Map(Object.entries(initialValue).map(([key, value]) => [
            key,
            { value, setBySource: this.getDynamicSource?.() },
        ]));
    }
    set(key, value, source) {
        const existingValue = this.map.get(key);
        const setBySource = source ?? this.getDynamicSource?.() ?? 'unknown';
        if (existingValue?.setBySource) {
            throw new Error(`Value for key ${key} has already been set by ${existingValue.setBySource} and cannot be overwritten by ${setBySource}`);
        }
        this.map.set(key, {
            value,
            setBySource,
        });
    }
    merge(value, source) {
        const mergeSource = source ?? this.getDynamicSource?.();
        for (const [key, val] of Object.entries(value)) {
            this.set(key, val, mergeSource);
        }
    }
    getValue() {
        return Object.fromEntries([...this.map.entries()].map(([key, value]) => [key, value.value]));
    }
}
// Map field container
class MapContainer {
    _value;
    getDynamicSource;
    constructor(initialValue, options) {
        this.getDynamicSource = options?.getDynamicSource;
        const initialMap = initialValue ?? new Map();
        this._value = new Map([...initialMap.entries()].map(([key, value]) => [
            key,
            { value, setBySource: this.getDynamicSource?.() },
        ]));
    }
    set(key, value, source) {
        const existingValue = this._value.get(key);
        const setBySource = source ?? this.getDynamicSource?.() ?? 'unknown';
        if (existingValue?.setBySource) {
            throw new Error(`Value for key ${key} has already been set by ${existingValue.setBySource} and cannot be overwritten by ${setBySource}`);
        }
        this._value.set(key, {
            value,
            setBySource,
        });
    }
    merge(value, source) {
        const mergeSource = source ?? this.getDynamicSource?.();
        for (const [key, val] of value.entries()) {
            this.set(key, val, mergeSource);
        }
    }
    mergeObj(value, source) {
        const mergeSource = source ?? this.getDynamicSource?.();
        for (const [key, val] of Object.entries(value)) {
            this.set(key, val, mergeSource);
        }
    }
    getValue() {
        return new Map([...this._value.entries()].map(([key, value]) => [key, value.value]));
    }
}
/**
 * Named field container
 *
 * This container stores objects that contains a name field that can be used for
 * detecting duplicate names.
 */
class NamedArrayFieldContainer {
    _value;
    getDynamicSource;
    constructor(initialValue, options) {
        this.getDynamicSource = options?.getDynamicSource;
        const value = initialValue ?? [];
        this._value = new Map(value.map((val) => [
            val.name,
            { value: val, setBySource: this.getDynamicSource?.() },
        ]));
    }
    add(value, source) {
        const existingValue = this._value.get(value.name);
        const setBySource = source ?? this.getDynamicSource?.() ?? 'unknown';
        if (existingValue?.setBySource) {
            throw new Error(`Value for name ${value.name} has already been set by ${existingValue.setBySource} and cannot be overwritten by ${setBySource}`);
        }
        this._value.set(value.name, {
            value,
            setBySource,
        });
    }
    addMany(values, source) {
        for (const value of values) {
            this.add(value, source);
        }
    }
    getValue() {
        return sortBy([...this._value.values()].map((value) => value.value), [(v) => v.name]);
    }
}
/**
 * Named array field container that returns a map of names to values
 */
class NamedArrayToMapFieldContainer {
    _namedFieldContainer;
    constructor(initialValue, options) {
        this._namedFieldContainer = new NamedArrayFieldContainer(initialValue, options);
    }
    add(value, source) {
        this._namedFieldContainer.add(value, source);
    }
    addMany(values, source) {
        this._namedFieldContainer.addMany(values, source);
    }
    getValue() {
        return new Map(this._namedFieldContainer.getValue().map((value) => [value.name, value]));
    }
}
// Map of maps field container
class MapOfMapsContainer {
    _value;
    getDynamicSource;
    constructor(initialValue, options) {
        this.getDynamicSource = options?.getDynamicSource;
        const initialMap = initialValue ?? new Map();
        this._value = new Map([...initialMap.entries()].map(([key1, innerMap]) => [
            key1,
            {
                value: new Map([...innerMap.entries()].map(([key2, value]) => [
                    key2,
                    { value, setBySource: this.getDynamicSource?.() },
                ])),
                setBySource: this.getDynamicSource?.(),
            },
        ]));
    }
    set(key1, key2, value, source) {
        const existingOuterValue = this._value.get(key1);
        const setBySource = source ?? this.getDynamicSource?.() ?? 'unknown';
        // If the outer key doesn't exist yet, create a new map for it
        if (!existingOuterValue) {
            const newInnerMap = new Map([[key2, { value, setBySource }]]);
            this._value.set(key1, {
                value: newInnerMap,
                setBySource,
            });
            return;
        }
        // Check if this specific key1+key2 combination has already been set
        const existingInnerValue = existingOuterValue.value.get(key2);
        if (existingInnerValue?.setBySource) {
            throw new Error(`Value for keys ${key1}+${key2} has already been set by ${existingInnerValue.setBySource} and cannot be overwritten by ${setBySource}`);
        }
        // Add the new value to the existing inner map
        existingOuterValue.value.set(key2, {
            value,
            setBySource,
        });
    }
    merge(key1, value, source) {
        const mergeSource = source ?? this.getDynamicSource?.() ?? 'unknown';
        for (const [key2, val] of value.entries()) {
            this.set(key1, key2, val, mergeSource);
        }
    }
    mergeObj(key1, value, source) {
        const mergeSource = source ?? this.getDynamicSource?.() ?? 'unknown';
        for (const [key2, val] of Object.entries(value)) {
            this.set(key1, key2, val, mergeSource);
        }
    }
    getValue() {
        return new Map([...this._value.entries()].map(([key1, outerValue]) => [
            key1,
            new Map([...outerValue.value.entries()].map(([key2, innerValue]) => [
                key2,
                innerValue.value,
            ])),
        ]));
    }
}
// Schema builder class
class FieldMapSchemaBuilder {
    options;
    constructor(options) {
        this.options = options;
    }
    scalar(defaultValue) {
        return new ScalarContainer(defaultValue, this.options);
    }
    string(defaultValue) {
        return new ScalarContainer(defaultValue, this.options);
    }
    number(defaultValue) {
        return new ScalarContainer(defaultValue, this.options);
    }
    boolean(defaultValue) {
        return new ScalarContainer(defaultValue, this.options);
    }
    array(initialValue, options) {
        return new ArrayContainer(initialValue, options);
    }
    object(defaultValue) {
        return new ObjectContainer(defaultValue, this.options);
    }
    map(initialValue) {
        return new MapContainer(initialValue ?? new Map(), this.options);
    }
    mapFromObj(initialValue) {
        return new MapContainer(new Map(Object.entries(initialValue ?? {})), this.options);
    }
    namedArray(initialValue) {
        return new NamedArrayFieldContainer(initialValue, this.options);
    }
    namedArrayToMap(initialValue) {
        return new NamedArrayToMapFieldContainer(initialValue, this.options);
    }
    mapOfMaps(initialValue) {
        return new MapOfMapsContainer(initialValue ?? new Map(), this.options);
    }
    mapOfMapsFromObj(initialValue) {
        const map = new Map();
        if (initialValue) {
            for (const [key1, innerObj] of Object.entries(initialValue)) {
                map.set(key1, new Map(Object.entries(innerObj)));
            }
        }
        return new MapOfMapsContainer(map, this.options);
    }
}
function createFieldMapSchemaBuilder(schemaBuilder) {
    return schemaBuilder;
}
/**
 * Creates a field map with type-safe field definitions
 */
function createFieldMap(schemaBuilder, options) {
    const schema = schemaBuilder(new FieldMapSchemaBuilder(options));
    // Add getValues method
    return {
        ...schema,
        getValues: () => {
            const values = {};
            for (const key of Object.keys(schema)) {
                const container = schema[key];
                values[key] = container.getValue();
            }
            return values;
        },
    };
}

/**
 * Recursively deep clones JSON-compatible values.
 * Throws if the value is not JSON-serializable.
 *
 * @param value - The value to clone
 * @returns A deep-cloned copy of the value
 * @throws Error if a non-JSON value is encountered
 */
function jsonDeepClone(value) {
    if (value === undefined ||
        value === null ||
        typeof value === 'boolean' ||
        typeof value === 'string') {
        return value;
    }
    if (typeof value === 'number') {
        return value;
    }
    if (typeof value === 'object') {
        if (value instanceof Date || value instanceof RegExp) {
            throw new TypeError(`Cannot clone value of unsupported type: ${value.constructor.name}`);
        }
        if (Array.isArray(value)) {
            // Clone array elements
            const copy = [];
            for (const [i, element] of value.entries()) {
                copy[i] = jsonDeepClone(element);
            }
            return copy;
        }
        // Clone plain object properties
        const copy = {};
        for (const key in value) {
            if (Object.prototype.hasOwnProperty.call(value, key)) {
                const prop = value[key];
                copy[key] = jsonDeepClone(prop);
            }
        }
        return copy;
    }
    // Reject functions, symbols, BigInt, etc.
    throw new Error(`Cannot clone value of unsupported type: ${typeof value}`);
}

// adapted from https://github.com/AitoDotAI/json-stringify-pretty-compact
// allows { } to be presented as {} to better mirror prettier formatting
function isObject(obj) {
    return typeof obj === 'object' && obj !== null;
}
function forEach(obj, cb) {
    if (Array.isArray(obj)) {
        // eslint-disable-next-line unicorn/no-array-for-each
        obj.forEach(cb);
    }
    else if (isObject(obj)) {
        for (const key of Object.keys(obj)) {
            const val = obj[key];
            cb(val, key);
        }
    }
}
function getTreeDepth(obj) {
    let depth = 0;
    if (Array.isArray(obj) || isObject(obj)) {
        forEach(obj, (val) => {
            if (Array.isArray(val) || isObject(val)) {
                const tmpDepth = getTreeDepth(val);
                if (tmpDepth > depth) {
                    depth = tmpDepth;
                }
            }
        });
        return depth + 1;
    }
    return depth;
}
function get(options, name, defaultValue) {
    return (name in options ? options[name] : defaultValue);
}
// Note: This regex matches even invalid JSON strings, but since we’re
// working on the output of `JSON.stringify` we know that only valid strings
// are present (unless the user supplied a weird `options.indent` but in
// that case we don’t care since the output would be invalid anyway).
// This regex has been adjusted to allow replacement of { } and [ ] with {} and []
// to better mirror prettier formatting.
const stringOrChar = /("(?:[^\\"]|\\.)*")|[:,]|\{(?!})|(?<!\{)}|\[(?!])|(?<!\[)]/g;
function prettify(str, options = {}) {
    const tokens = {
        '{': '{',
        '}': '}',
        '[': '[',
        ']': ']',
        ',': ', ',
        ':': ': ',
    };
    if (!!options.addMargin || !!options.addObjectMargin) {
        tokens['{'] = '{ ';
        tokens['}'] = ' }';
    }
    if (!!options.addMargin || !!options.addArrayMargin) {
        tokens['['] = '[ ';
        tokens[']'] = ' ]';
    }
    return str.replaceAll(stringOrChar, (match, string) => string ? match : tokens[match]);
}
function stringifyPrettyCompact(rootObj, options = {}) {
    const indent = JSON.stringify([1], null, get(options, 'indent', 2)).slice(2, -3);
    const addMargin = get(options, 'margins', false);
    const addArrayMargin = get(options, 'arrayMargins', false);
    const addObjectMargin = get(options, 'objectMargins', true);
    const maxLength = indent === '' ? Infinity : get(options, 'maxLength', 80);
    const maxNesting = get(options, 'maxNesting', Infinity);
    return (function stringifyRecursive(obj, currentIndent, reserved) {
        if (obj &&
            typeof obj === 'object' &&
            typeof obj.toJSON === 'function') {
            // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression
            obj = obj.toJSON();
        }
        const string = JSON.stringify(obj);
        // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
        if (string === undefined) {
            return string;
        }
        const length = maxLength - currentIndent.length - reserved;
        const treeDepth = getTreeDepth(obj);
        if (treeDepth <= maxNesting && string.length <= length) {
            const prettified = prettify(string, {
                addMargin,
                addArrayMargin,
                addObjectMargin,
            });
            if (prettified.length <= length) {
                return prettified;
            }
        }
        if (isObject(obj)) {
            const nextIndent = currentIndent + indent;
            const items = [];
            let delimiters;
            const atEndOfArray = (array, index) => index === array.length - 1 ? 0 : 1;
            if (Array.isArray(obj)) {
                for (let index = 0; index < obj.length; index += 1) {
                    items.push(stringifyRecursive(obj[index], nextIndent, atEndOfArray(obj, index)) || 'null');
                }
                delimiters = '[]';
            }
            else {
                // eslint-disable-next-line unicorn/no-array-for-each
                Object.keys(obj).forEach((key, index, array) => {
                    const keyPart = `${JSON.stringify(key)}: `;
                    const value = stringifyRecursive(obj[key], nextIndent, keyPart.length + atEndOfArray(array, index));
                    // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
                    if (value !== undefined) {
                        items.push(keyPart + value);
                    }
                });
                delimiters = '{}';
            }
            if (items.length > 0) {
                return [
                    delimiters[0],
                    indent + items.join(`,\n${nextIndent}`),
                    delimiters[1],
                ].join(`\n${currentIndent}`);
            }
        }
        return string;
    })(rootObj, '', 0);
}

function isPlainObject(value) {
	if (typeof value !== 'object' || value === null) {
		return false;
	}

	const prototype = Object.getPrototypeOf(value);
	return (prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in value) && !(Symbol.iterator in value);
}

function sortKeys(object, options = {}) {
	if (!isPlainObject(object) && !Array.isArray(object)) {
		throw new TypeError('Expected a plain object or array');
	}

	const {deep, compare} = options;
	const cache = new WeakMap();

	const deepSortArray = array => {
		const resultFromCache = cache.get(array);
		if (resultFromCache !== undefined) {
			return resultFromCache;
		}

		const result = [];
		cache.set(array, result);

		result.push(...array.map(item => {
			if (Array.isArray(item)) {
				return deepSortArray(item);
			}

			if (isPlainObject(item)) {
				return _sortKeys(item);
			}

			return item;
		}));

		return result;
	};

	const _sortKeys = object => {
		const resultFromCache = cache.get(object);
		if (resultFromCache !== undefined) {
			return resultFromCache;
		}

		const result = {};
		const keys = Object.keys(object).sort(compare);

		cache.set(object, result);

		for (const key of keys) {
			const value = object[key];
			let newValue;

			if (deep && Array.isArray(value)) {
				newValue = deepSortArray(value);
			} else {
				newValue = deep && isPlainObject(value) ? _sortKeys(value) : value;
			}

			Object.defineProperty(result, key, {
				...Object.getOwnPropertyDescriptor(object, key),
				value: newValue,
			});
		}

		return result;
	};

	if (Array.isArray(object)) {
		return deep ? deepSortArray(object) : [...object];
	}

	return _sortKeys(object);
}

/**
 * Stringifies a value with stable pretty printing.
 *
 * That means the keys are sorted and the value is pretty printed.
 *
 * @param value - The value to stringify.
 * @returns The stringified value.
 */
function stringifyPrettyStable(value) {
    const sortedValue = sortKeys(value, {
        deep: true,
    });
    return `${stringifyPrettyCompact(sortedValue)}\n`;
}

/**
 * Groups values of an iterable using a key-generating function and returns a Map.
 *
 * Mimics the behavior of ES2025 `Map.groupBy`.
 *
 * @template T The type of elements in the iterable.
 * @template K The key type returned by the key function.
 * @param iterable The iterable to group.
 * @param keyFn A function that returns a grouping key for each element.
 * @returns A Map where each key maps to an array of elements with that key.
 */
function mapGroupBy(iterable, keyFn) {
    const result = new Map();
    for (const item of iterable) {
        const key = keyFn(item);
        const group = result.get(key);
        if (group) {
            group.push(item);
        }
        else {
            result.set(key, [item]);
        }
    }
    return result;
}

/**
 * Creates a Map from an iterable, using a key-generating function.
 * If multiple elements generate the same key, later elements override earlier ones.
 *
 * Analogous to lodash's keyBy function, but returns a Map instead of an object.
 *
 * @template T The type of elements in the iterable.
 * @template K The key type returned by the key function.
 * @param iterable The iterable to transform.
 * @param keyFn A function that returns a unique key for each element.
 * @returns A Map where each key maps to a single element.
 */
function mapKeyBy(iterable, keyFn) {
    const result = new Map();
    for (const item of iterable) {
        const key = keyFn(item);
        result.set(key, item);
    }
    return result;
}

/**
 * Maps the values of a Map to a new Map with the same keys.
 *
 * @template K - Type of keys in the map.
 * @template V - Original value type.
 * @template R - Resulting value type.
 * @param map - The input Map to transform.
 * @param fn - A function to transform each value, receiving the value, key, and original map.
 * @returns A new Map with the same keys and transformed values.
 */
function mapValuesOfMap(map, fn) {
    const result = new Map();
    for (const [key, value] of map.entries()) {
        result.set(key, fn(value, key, map));
    }
    return result;
}

/**
 * Merges an array of maps, throwing an error if any keys overlap (unless the values are deeply equal and allowed).
 *
 * @param maps - The array of maps to merge.
 * @param options - Options to control merge behavior.
 * @returns The merged map.
 */
function safeMergeMapsWithOptions(maps, options = {}) {
    const result = new Map();
    for (const map of maps) {
        for (const [key, value] of map) {
            if (result.has(key)) {
                const existing = result.get(key);
                if (!options.allowEqualValues || !isEqual(existing, value)) {
                    throw new Error(`Cannot merge key ${String(key)} because it already exists.`);
                }
            }
            result.set(key, value);
        }
    }
    return result;
}
/**
 * Merges two maps, throwing an error if any keys overlap.
 *
 * @param mapOne - The first map to merge.
 * @param mapTwo - The second map to merge.
 * @param options - Options to control merge behavior
 * @returns The merged map.
 */
function safeMergeMap(mapOne, mapTwo, options = {}) {
    return safeMergeMapsWithOptions([mapOne, mapTwo], options);
}
/**
 * Merges an array of maps, throwing an error if any keys overlap (unless the values are deeply equal and allowed).
 * This function delegates to safeMergeMapsWithOptions.
 *
 * @param maps - The array of maps to merge.
 * @returns The merged map.
 */
function safeMergeMaps(...maps) {
    return safeMergeMapsWithOptions(maps);
}

/**
 * Sets a value at a given path in an object or array immutably.
 *
 * Mimics lodash set but returns a new object/array and throws on invalid paths.
 * Only operates on JSON-stringifyable data (objects and arrays).
 *
 * It navigates the path and recursively copies objects/arrays along the path.
 *
 * @param obj - The object or array to modify
 * @param path - The path to set the value at
 * @param value - The value to set
 * @returns A new object or array with the value set
 * @throws Error if the path is invalid (e.g., trying to set a property on a non-object)
 */
function immutableSet(obj, path, value) {
    if (path.length === 0) {
        return value;
    }
    const [head, ...tail] = path;
    if (Array.isArray(obj)) {
        if (typeof head !== 'number') {
            throw new TypeError(`Invalid path: expected number index for array, got "${head}"`);
        }
        const newArr = [...obj];
        newArr[head] =
            tail.length === 0
                ? value
                : immutableSet(obj[head], tail, value);
        return newArr;
    }
    if (isPlainObject$1(obj)) {
        const newObj = { ...obj };
        const key = String(head);
        newObj[key] =
            tail.length === 0
                ? value
                : immutableSet(obj[key], tail, value);
        return newObj;
    }
    throw new Error(`Invalid path: cannot set property "${head}" on non-object value`);
}

/**
 * Merges two objects, throwing an error if any keys overlap.
 *
 * @param itemOne - The first object to merge.
 * @param itemTwo - The second object to merge.
 * @param options - Options to control merge behavior
 * @returns The merged object.
 */
function safeMerge(itemOne, itemTwo, options = {}) {
    return mergeWith(itemOne, itemTwo, (targetValue, sourceValue, key) => {
        if (key in itemOne && key in itemTwo) {
            if (options.allowEqualValues && isEqual(targetValue, sourceValue)) {
                // eslint-disable-next-line @typescript-eslint/no-unsafe-return -- we're just using the sourceValue
                return sourceValue;
            }
            throw new Error(`Cannot merge key ${key} because it already exists.`);
        }
        // eslint-disable-next-line @typescript-eslint/no-unsafe-return -- we're just using the sourceValue
        return sourceValue;
    });
}
/**
 * Merges an array of objects, throwing an error if any keys overlap.
 *
 * @param items - The array of objects to merge.
 * @param options - Options to control merge behavior
 * @returns The merged object.
 */
function safeMergeAllWithOptions(items, options = {}) {
    const targetValue = {};
    for (const item of items.flat()) {
        safeMerge(targetValue, item, options);
    }
    return targetValue;
}
/**
 * Merges an array of objects, throwing an error if any keys overlap.
 *
 * @param items - The array of objects to merge.
 * @returns The merged object.
 */
function safeMergeAll(...items) {
    return safeMergeAllWithOptions(items);
}

/**
 * Stable string comparison function that uses case-insensitive lexicographic ordering
 * with a case-sensitive tiebreaker for determinism.
 *
 * This matches the default behavior of `localeCompare` (case-insensitive) while
 * providing consistent results across different operating systems and locales.
 * Use this for deterministic sorting where locale-aware comparison is not required.
 *
 * @param a - First string to compare
 * @param b - Second string to compare
 * @returns Negative if a < b, positive if a > b, zero if equal
 *
 * @example
 * ```ts
 * const items = ['Banana', 'apple', 'Cherry', 'cherry'];
 * items.sort(compareStrings); // ['apple', 'Banana', 'Cherry', 'cherry']
 * ```
 */
function compareStrings(a, b) {
    const aLower = a.toLowerCase();
    const bLower = b.toLowerCase();
    if (aLower < bLower)
        return -1;
    if (aLower > bLower)
        return 1;
    // Tiebreaker: case-sensitive comparison for determinism
    if (a < b)
        return -1;
    if (a > b)
        return 1;
    return 0;
}

/**
 * Recursively sorts all keys in an object, including nested objects and arrays.
 *
 * @param obj - The object to sort recursively.
 * @returns A new object with all keys sorted recursively.
 */
function sortKeysRecursive(obj) {
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }
    if (Array.isArray(obj)) {
        return obj.map(sortKeysRecursive);
    }
    return Object.fromEntries(Object.entries(obj)
        .toSorted(([a], [b]) => compareStrings(a, b))
        .map(([key, value]) => [key, sortKeysRecursive(value)]));
}

/**
 * Sorts the keys of an object.
 *
 * @param obj - The object to sort.
 * @returns The sorted object.
 */
function sortObjectKeys(obj) {
    return Object.fromEntries(Object.entries(obj).toSorted(([a], [b]) => compareStrings(a, b)));
}

/**
 * Returns a new Set containing values in `setA` that are not in `setB`.
 *
 * @param setA - The original set
 * @param setB - The set of values to exclude
 * @returns A new Set with the difference
 */
function differenceSet(setA, setB) {
    const result = new Set();
    for (const value of setA) {
        if (!setB.has(value)) {
            result.add(value);
        }
    }
    return result;
}

/**
 * Lowercase the first character of a string, leaving the remainder unchanged.
 *
 * - Returns the input unchanged when it's an empty string
 * - Non-alphabetic first characters are returned as-is
 *
 * @param str - The input string
 * @returns The string with the first character lowercased
 * @example
 * lowercaseFirstChar('Hello') // 'hello'
 * lowercaseFirstChar('hello') // 'hello'
 * lowercaseFirstChar('1World') // '1World'
 */
function lowercaseFirstChar(str) {
    if (str.length === 0) {
        return str;
    }
    return str.charAt(0).toLowerCase() + str.slice(1);
}
/**
 * Uppercase the first character of a string, leaving the remainder unchanged.
 *
 * - Returns the input unchanged when it's an empty string
 * - Non-alphabetic first characters are returned as-is
 *
 * @param str - The input string
 * @returns The string with the first character uppercased
 * @example
 * uppercaseFirstChar('hello') // 'Hello'
 * uppercaseFirstChar('Hello') // 'Hello'
 * uppercaseFirstChar('#tag') // '#tag'
 */
function uppercaseFirstChar(str) {
    if (str.length === 0) {
        return str;
    }
    return str.charAt(0).toUpperCase() + str.slice(1);
}

/**
 * Converts a string to the specified case while preserving non-alphanumeric prefixes and suffixes.
 *
 * @param input - The string to convert
 * @param caseConverter - The case conversion function to apply
 * @returns The converted string with preserved prefix and suffix
 *
 * @example
 * ```typescript
 * convertCaseWithPrefix('_layoutTest', kebabCase)
 * // Returns: '_layout-test'
 *
 * convertCaseWithPrefix('__privateHelper', kebabCase)
 * // Returns: '__private-helper'
 *
 * convertCaseWithPrefix('[id]', kebabCase)
 * // Returns: '[id]'
 * ```
 */
function convertCaseWithPrefix(input, caseConverter) {
    // Handle special pattern where the entire string is non-alphanumeric (like "[id]")
    if (!/[a-zA-Z0-9]/.test(input)) {
        return input;
    }
    // Extract non-alphanumeric prefix
    const prefixMatch = /^([^a-zA-Z0-9]*)/.exec(input);
    const prefix = prefixMatch?.[1] ?? '';
    // Extract non-alphanumeric suffix
    const suffixMatch = /([^a-zA-Z0-9]*)$/.exec(input);
    const suffix = suffixMatch?.[1] ?? '';
    // Get the middle part (everything between prefix and suffix)
    const startIndex = prefix.length;
    const endIndex = input.length - suffix.length;
    const middlePart = input.slice(startIndex, endIndex);
    // Only apply case conversion to the middle part if it contains alphanumeric characters
    const convertedMiddlePart = middlePart && /[a-zA-Z0-9]/.test(middlePart)
        ? caseConverter(middlePart)
        : middlePart;
    return prefix + convertedMiddlePart + suffix;
}

const peq = new Uint32Array(0x10000);
const myers_32 = (a, b) => {
    const n = a.length;
    const m = b.length;
    const lst = 1 << (n - 1);
    let pv = -1;
    let mv = 0;
    let sc = n;
    let i = n;
    while (i--) {
        peq[a.charCodeAt(i)] |= 1 << i;
    }
    for (i = 0; i < m; i++) {
        let eq = peq[b.charCodeAt(i)];
        const xv = eq | mv;
        eq |= ((eq & pv) + pv) ^ pv;
        mv |= ~(eq | pv);
        pv &= eq;
        if (mv & lst) {
            sc++;
        }
        if (pv & lst) {
            sc--;
        }
        mv = (mv << 1) | 1;
        pv = (pv << 1) | ~(xv | mv);
        mv &= xv;
    }
    i = n;
    while (i--) {
        peq[a.charCodeAt(i)] = 0;
    }
    return sc;
};
const myers_x = (b, a) => {
    const n = a.length;
    const m = b.length;
    const mhc = [];
    const phc = [];
    const hsize = Math.ceil(n / 32);
    const vsize = Math.ceil(m / 32);
    for (let i = 0; i < hsize; i++) {
        phc[i] = -1;
        mhc[i] = 0;
    }
    let j = 0;
    for (; j < vsize - 1; j++) {
        let mv = 0;
        let pv = -1;
        const start = j * 32;
        const vlen = Math.min(32, m) + start;
        for (let k = start; k < vlen; k++) {
            peq[b.charCodeAt(k)] |= 1 << k;
        }
        for (let i = 0; i < n; i++) {
            const eq = peq[a.charCodeAt(i)];
            const pb = (phc[(i / 32) | 0] >>> i) & 1;
            const mb = (mhc[(i / 32) | 0] >>> i) & 1;
            const xv = eq | mv;
            const xh = ((((eq | mb) & pv) + pv) ^ pv) | eq | mb;
            let ph = mv | ~(xh | pv);
            let mh = pv & xh;
            if ((ph >>> 31) ^ pb) {
                phc[(i / 32) | 0] ^= 1 << i;
            }
            if ((mh >>> 31) ^ mb) {
                mhc[(i / 32) | 0] ^= 1 << i;
            }
            ph = (ph << 1) | pb;
            mh = (mh << 1) | mb;
            pv = mh | ~(xv | ph);
            mv = ph & xv;
        }
        for (let k = start; k < vlen; k++) {
            peq[b.charCodeAt(k)] = 0;
        }
    }
    let mv = 0;
    let pv = -1;
    const start = j * 32;
    const vlen = Math.min(32, m - start) + start;
    for (let k = start; k < vlen; k++) {
        peq[b.charCodeAt(k)] |= 1 << k;
    }
    let score = m;
    for (let i = 0; i < n; i++) {
        const eq = peq[a.charCodeAt(i)];
        const pb = (phc[(i / 32) | 0] >>> i) & 1;
        const mb = (mhc[(i / 32) | 0] >>> i) & 1;
        const xv = eq | mv;
        const xh = ((((eq | mb) & pv) + pv) ^ pv) | eq | mb;
        let ph = mv | ~(xh | pv);
        let mh = pv & xh;
        score += (ph >>> (m - 1)) & 1;
        score -= (mh >>> (m - 1)) & 1;
        if ((ph >>> 31) ^ pb) {
            phc[(i / 32) | 0] ^= 1 << i;
        }
        if ((mh >>> 31) ^ mb) {
            mhc[(i / 32) | 0] ^= 1 << i;
        }
        ph = (ph << 1) | pb;
        mh = (mh << 1) | mb;
        pv = mh | ~(xv | ph);
        mv = ph & xv;
    }
    for (let k = start; k < vlen; k++) {
        peq[b.charCodeAt(k)] = 0;
    }
    return score;
};
const distance = (a, b) => {
    if (a.length < b.length) {
        const tmp = b;
        b = a;
        a = tmp;
    }
    if (b.length === 0) {
        return a.length;
    }
    if (a.length <= 32) {
        return myers_32(a, b);
    }
    return myers_x(a, b);
};

/**
 * Finds the n closest matches to a target string from an array of strings using Levenshtein distance.
 *
 * @param target - The string to find matches for
 * @param candidates - Array of candidate strings to search through
 * @param n - Number of closest matches to return (defaults to 1)
 * @returns Array of the n closest matches, sorted by distance (closest first)
 *
 * @example
 * ```typescript
 * const candidates = ['apple', 'banana', 'orange', 'grape'];
 * const matches = findClosestMatch('aple', candidates, 2);
 * // Returns: ['apple', 'grape'] (assuming these are the 2 closest matches)
 * ```
 */
function findClosestMatch(target, candidates, n = 1) {
    if (candidates.length === 0) {
        return [];
    }
    if (n <= 0) {
        return [];
    }
    // Calculate distances for all candidates
    const distances = candidates.map((candidate, index) => ({
        candidate,
        distance: distance(target, candidate),
        index,
    }));
    // Sort by distance (ascending) and then by original index for stable sorting
    distances.sort((a, b) => {
        if (a.distance !== b.distance) {
            return a.distance - b.distance;
        }
        return a.index - b.index;
    });
    // Return the n closest matches
    return distances.slice(0, n).map((item) => item.candidate);
}

/**
 * Quotes a string with single quotes and escapes single quotes.
 * @param value - The string to quote.
 * @returns The quoted string.
 */
function quot(value) {
    return `'${value.replaceAll("'", String.raw `\'`)}'`;
}
/**
 * Quotes a string with double quotes and escapes double quotes.
 * @param value - The string to quote.
 * @returns The quoted string.
 */
function doubleQuot(value) {
    return `"${value.replaceAll('"', String.raw `\"`)}"`;
}

let random = bytes => crypto.getRandomValues(new Uint8Array(bytes));
let customRandom = (alphabet, defaultSize, getRandom) => {
  let mask = (2 << Math.log2(alphabet.length - 1)) - 1;
  let step = -~((1.6 * mask * defaultSize) / alphabet.length);
  return (size = defaultSize) => {
    let id = '';
    while (true) {
      let bytes = getRandom(step);
      let j = step | 0;
      while (j--) {
        id += alphabet[bytes[j] & mask] || '';
        if (id.length >= size) return id
      }
    }
  }
};
let customAlphabet = (alphabet, size = 21) =>
  customRandom(alphabet, size | 0, random);

const NUMBERS = '0123456789';
const LOWERCASE = 'abcdefghijklmnopqrstuvwxyz';
const UPPERCASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
/**
 * Use custom ID generator for characters that be easily selected
 */
const customNanoid = customAlphabet(`${NUMBERS}${LOWERCASE}${UPPERCASE}_`, 12);
/**
 * Generate a random key string made up of numbers, lowercase, and uppercase letters
 * @returns A random key string
 */
function randomKey(length = 12) {
    return customNanoid(length);
}

class ToposortCyclicalDependencyError extends Error {
    cyclePath;
    constructor(nodes) {
        super(`Cyclical dependency detected: ${nodes.map((n) => JSON.stringify(n)).join(' -> ')}`);
        this.name = 'ToposortCyclicalDependencyError';
        this.cyclePath = nodes; // Store the path for potential inspection
    }
}
class ToposortUnknownNodeError extends Error {
    unknownNode;
    constructor(node) {
        super(`Unknown node referenced in edges: ${JSON.stringify(node)}`);
        this.name = 'ToposortUnknownNodeError';
        this.unknownNode = node; // Store the node for potential inspection
    }
}

/**
 * Creates a map of inbound edges from node indices to their source indices
 */
function makeInboundEdges(nodes, edgeArr) {
    const inboundEdgesMap = new Map();
    for (const edge of edgeArr) {
        const [source, target] = edge;
        const sourceIndex = nodes.get(source);
        const targetIndex = nodes.get(target);
        // Check both source and target exist in the provided nodes set
        if (sourceIndex === undefined)
            throw new ToposortUnknownNodeError(source);
        if (targetIndex === undefined)
            throw new ToposortUnknownNodeError(target);
        const targetEdges = inboundEdgesMap.get(targetIndex);
        if (targetEdges) {
            targetEdges.add(sourceIndex);
        }
        else {
            inboundEdgesMap.set(targetIndex, new Set([sourceIndex]));
        }
    }
    return inboundEdgesMap;
}
/**
 * Creates a map of node indices to their out-degree
 */
function makeNodeOutDegrees(inboundEdgesMap, nodeLength) {
    const nodeOutDegrees = Array.from({ length: nodeLength }, () => 0);
    for (const [, sources] of inboundEdgesMap.entries()) {
        for (const source of sources) {
            nodeOutDegrees[source]++;
        }
    }
    return nodeOutDegrees;
}
/**
 * Detects a cycle in a graph and returns the path of the first cycle found.
 */
function detectCycle$1(nodes, 
// Set of nodes already processed by a prior algorithm (e.g., a topological sort).
// This function will only search for cycles among the remaining nodes.
initialVisited, edges) {
    // Tracks nodes visited by ANY DFS traversal within this function.
    const cycleCheckVisited = new Set();
    for (let i = 0; i < nodes.length; i++) {
        if (!initialVisited.has(i) && !cycleCheckVisited.has(i)) {
            const path = [];
            const recursionStack = new Set();
            const cyclePath = dfs(i, path, recursionStack);
            if (cyclePath) {
                // A cycle was found, convert indices to nodes and return immediately.
                return cyclePath.map((idx) => nodes[idx]);
            }
        }
    }
    // No cycles were found in any of the graph's components.
    return [];
    /**
     * Performs a DFS from a starting node to find a cycle.
     * @returns The cycle path as an array of indices if found, otherwise null.
     */
    function dfs(node, path, recursionStack) {
        path.push(node);
        recursionStack.add(node);
        cycleCheckVisited.add(node);
        const neighbors = edges.get(node) ?? new Set();
        for (const neighbor of neighbors) {
            if (recursionStack.has(neighbor)) {
                const cycleStartIndex = path.indexOf(neighbor);
                const cycle = path.slice(cycleStartIndex);
                cycle.push(neighbor);
                return cycle;
            }
            // If the neighbor hasn't been visited by any DFS run yet, recurse.
            if (!cycleCheckVisited.has(neighbor)) {
                const result = dfs(neighbor, path, recursionStack);
                if (result) {
                    // A cycle was found deeper in the traversal; propagate the result up.
                    return result;
                }
            }
        }
        // Backtrack: No cycle found from this node.
        recursionStack.delete(node);
        path.pop();
        return null;
    }
}
/**
 * Default comparison function for stable topological sort
 */
function defaultCompareFunc$1(a, b) {
    if (typeof a === 'string' && typeof b === 'string') {
        return compareStrings(a, b);
    }
    if (a === b)
        return 0;
    return a < b ? -1 : 1;
}
/**
 * Performs a locality-based topological sort on a directed acyclic graph.
 *
 * This is a variant of Kahn's algorithm that starts from nodes with no outbound edges first,
 * and then works its way back.
 *
 * This is useful for tasks where we want to process nodes in a way that is consistent with their locality in the graph
 * such as sorting fragments of a generated code file that depend on each other.
 *
 * @param nodes - The nodes to sort
 * @param edges - The edges of the graph
 * @param options - Optional options for the topological sort
 * @returns The sorted nodes
 */
function toposortLocal(nodes, edges, options = {}) {
    const { compareFunc = defaultCompareFunc$1 } = options;
    const compareIdx = (a, b) => compareFunc(nodes[a], nodes[b]);
    // Map each node to its index
    const nodeIndexMap = new Map(nodes.map((node, index) => [node, index]));
    // Create a map of outgoing edges from each node
    const inboundEdgesMap = makeInboundEdges(nodeIndexMap, edges);
    const nodeOutDegrees = makeNodeOutDegrees(inboundEdgesMap, nodes.length);
    // Create a stack of nodes with no incoming edges (in-degree == 0)
    // We use a stack so that the most recently added nodes are processed first
    const zeroOutDegreeStack = [];
    for (const [i, nodeOutDegree] of nodeOutDegrees.entries()) {
        if (nodeOutDegree === 0) {
            zeroOutDegreeStack.push(i);
        }
    }
    zeroOutDegreeStack.sort(compareIdx);
    const reverseResult = [];
    const visited = new Set();
    // Process nodes in BFS order
    while (zeroOutDegreeStack.length > 0) {
        const current = zeroOutDegreeStack.pop();
        if (current === undefined)
            break;
        visited.add(current);
        reverseResult.push(nodes[current]);
        // Process all outgoing edges from the current node
        const incomingEdges = inboundEdgesMap.get(current);
        const newZeroOutDegreeNodes = [];
        if (incomingEdges) {
            for (const source of incomingEdges) {
                nodeOutDegrees[source]--;
                // If the target node now has no incoming edges, add it to the queue
                if (nodeOutDegrees[source] === 0) {
                    newZeroOutDegreeNodes.push(source);
                }
            }
        }
        newZeroOutDegreeNodes.sort(compareIdx);
        zeroOutDegreeStack.push(...newZeroOutDegreeNodes);
    }
    // Check for cycles
    if (reverseResult.length !== nodes.length) {
        const cyclePath = detectCycle$1(nodes, visited, inboundEdgesMap);
        throw new ToposortCyclicalDependencyError(cyclePath.toReversed());
    }
    return reverseResult.toReversed();
}

class TinyQueue {
    constructor(data = [], compare = (a, b) => (a < b ? -1 : a > b ? 1 : 0)) {
        this.data = data;
        this.length = this.data.length;
        this.compare = compare;

        if (this.length > 0) {
            for (let i = (this.length >> 1) - 1; i >= 0; i--) this._down(i);
        }
    }

    push(item) {
        this.data.push(item);
        this._up(this.length++);
    }

    pop() {
        if (this.length === 0) return undefined;

        const top = this.data[0];
        const bottom = this.data.pop();

        if (--this.length > 0) {
            this.data[0] = bottom;
            this._down(0);
        }

        return top;
    }

    peek() {
        return this.data[0];
    }

    _up(pos) {
        const {data, compare} = this;
        const item = data[pos];

        while (pos > 0) {
            const parent = (pos - 1) >> 1;
            const current = data[parent];
            if (compare(item, current) >= 0) break;
            data[pos] = current;
            pos = parent;
        }

        data[pos] = item;
    }

    _down(pos) {
        const {data, compare} = this;
        const halfLength = this.length >> 1;
        const item = data[pos];

        while (pos < halfLength) {
            let bestChild = (pos << 1) + 1; // initially it is the left child
            const right = bestChild + 1;

            if (right < this.length && compare(data[right], data[bestChild]) < 0) {
                bestChild = right;
            }
            if (compare(data[bestChild], item) >= 0) break;

            data[pos] = data[bestChild];
            pos = bestChild;
        }

        data[pos] = item;
    }
}

/**
 * Creates a map of outgoing edges from node indices to their target indices
 */
function makeOutgoingEdges(nodes, edgeArr) {
    const outgoingEdgesMap = new Map();
    for (const edge of edgeArr) {
        const [source, target] = edge;
        const sourceIndex = nodes.get(source);
        const targetIndex = nodes.get(target);
        // Check both source and target exist in the provided nodes set
        if (sourceIndex === undefined)
            throw new ToposortUnknownNodeError(source);
        if (targetIndex === undefined)
            throw new ToposortUnknownNodeError(target);
        const sourceEdges = outgoingEdgesMap.get(sourceIndex);
        if (sourceEdges) {
            sourceEdges.add(targetIndex);
        }
        else {
            outgoingEdgesMap.set(sourceIndex, new Set([targetIndex]));
        }
    }
    return outgoingEdgesMap;
}
/**
 * Creates a map of node indices to their in-degree
 */
function makeNodeInDegrees(outgoingEdgesMap, nodeLength) {
    const nodeInDegrees = Array.from({ length: nodeLength }, () => 0);
    for (const [, targets] of outgoingEdgesMap.entries()) {
        for (const target of targets) {
            nodeInDegrees[target]++;
        }
    }
    return nodeInDegrees;
}
/**
 * Detects cycles in a graph by checking if all nodes are included in the topological sort
 */
function detectCycle(nodes, visited, edges) {
    // If all nodes were visited, no cycle exists
    if (visited.size === nodes.length) {
        return [];
    }
    // Run DFS from any unvisited node to find a cycle
    const path = [];
    const visitSet = new Set();
    function dfs(node) {
        if (visitSet.has(node)) {
            path.push(node);
            return true;
        }
        if (visited.has(node)) {
            return false;
        }
        visitSet.add(node);
        path.push(node);
        const neighbors = edges.get(node) ?? new Set();
        for (const neighbor of neighbors) {
            if (dfs(neighbor)) {
                return true;
            }
        }
        path.pop();
        visitSet.delete(node);
        return false;
    }
    // For cycle detection, we need to try all unvisited nodes
    for (let i = 0; i < nodes.length; i++) {
        if (!visited.has(i)) {
            // Reset path and visitSet for each starting node
            path.length = 0;
            visitSet.clear();
            const cycleFound = dfs(i);
            if (cycleFound) {
                // Convert path indices to actual nodes
                return path.map((idx) => nodes[idx]);
            }
        }
    }
    return [];
}
/**
 * Default comparison function for stable topological sort
 */
function defaultCompareFunc(a, b) {
    if (typeof a === 'string' && typeof b === 'string') {
        return compareStrings(a, b);
    }
    if (a === b)
        return 0;
    return a < b ? -1 : 1;
}
/**
 * Performs a topological sort on a directed acyclic graph.
 *
 * @param nodes - The nodes to sort
 * @param edges - The edges of the graph
 * @param options - Optional options for the topological sort
 * @returns The sorted nodes
 */
function toposort(nodes, edges, options = {}) {
    const { compareFunc } = options;
    // Map each node to its index
    const nodeIndexMap = new Map(nodes.map((node, index) => [node, index]));
    // Create a map of outgoing edges from each node
    const outgoingEdgesMap = makeOutgoingEdges(nodeIndexMap, edges);
    const nodeInDegrees = makeNodeInDegrees(outgoingEdgesMap, nodes.length);
    // Create a queue of nodes with no incoming edges (in-degree == 0)
    const zeroInDegreeQueue = compareFunc
        ? new TinyQueue([], (a, b) => compareFunc(nodes[a], nodes[b]))
        : [];
    for (const [i, nodeInDegree] of nodeInDegrees.entries()) {
        if (nodeInDegree === 0) {
            zeroInDegreeQueue.push(i);
        }
    }
    const result = [];
    const visited = new Set();
    // Process nodes in BFS order
    while (zeroInDegreeQueue.length > 0) {
        const current = zeroInDegreeQueue.pop();
        if (current === undefined)
            break;
        visited.add(current);
        result.push(nodes[current]);
        // Process all outgoing edges from the current node
        const outgoingEdges = outgoingEdgesMap.get(current);
        if (outgoingEdges) {
            for (const target of outgoingEdges) {
                nodeInDegrees[target]--;
                // If the target node now has no incoming edges, add it to the queue
                if (nodeInDegrees[target] === 0) {
                    zeroInDegreeQueue.push(target);
                }
            }
        }
    }
    // Check for cycles
    if (result.length !== nodes.length) {
        const cyclePath = detectCycle(nodes, visited, outgoingEdgesMap);
        throw new ToposortCyclicalDependencyError(cyclePath);
    }
    return result;
}
/**
 * Performs a topological sort on a directed acyclic graph, always selecting
 * the smallest available node according to the provided comparison function,
 * yielding the lexicographically minimal ordering.
 *
 * @param nodes - The nodes to sort
 * @param edges - The edges of the graph
 * @param compareFunc - Optional custom comparison function to break ties between nodes with the same topological level (default is string comparison)
 * @returns The sorted nodes
 */
function toposortOrdered(nodes, edges, compareFunc = defaultCompareFunc) {
    return toposort(nodes, edges, { compareFunc });
}

/**
 * Type guard to check if a value is not null or undefined.
 */
function notEmpty(value) {
    return value !== null && value !== undefined;
}

const {z: z$1} = await importShared('zod');

/**
 * Regex for validating kebab case, e.g. "my-project".
 */
const KEBAB_CASE_REGEX = /^[a-z0-9-]+$/;
/**
 * Regex for validating pascal case, e.g. "MyProject".
 */
const PASCAL_CASE_REGEX = /^[A-Z][a-zA-Z0-9]*$/;
/**
 * Regex for validating camel case, e.g. "myProject".
 */
const CAMEL_CASE_REGEX = /^[a-z][a-zA-Z0-9]*$/;
/**
 * Regex for validating constant case, e.g. "MY_PROJECT".
 */
const CONSTANT_CASE_REGEX = /^[A-Z][A-Z0-9_]*$/;
const CASE_VALIDATORS = {
    /**
     * Zod validator for validating kebab case, e.g. "my-project".
     */
    KEBAB_CASE: z$1.string().regex(KEBAB_CASE_REGEX, {
        message: 'Must be kebab case (e.g. "my-project")',
    }),
    /**
     * Zod validator for validating pascal case, e.g. "MyProject".
     */
    PASCAL_CASE: z$1.string().regex(PASCAL_CASE_REGEX, {
        message: 'Must be pascal case (e.g. "MyProject")',
    }),
    /**
     * Zod validator for validating camel case, e.g. "myProject".
     */
    CAMEL_CASE: z$1.string().regex(CAMEL_CASE_REGEX, {
        message: 'Must be camel case (e.g. "myProject")',
    }),
    /**
     * Zod validator for validating constant case, e.g. "MY_PROJECT".
     */
    CONSTANT_CASE: z$1.string().regex(CONSTANT_CASE_REGEX, {
        message: 'Must be constant case (e.g. "MY_PROJECT")',
    }),
};

const {z} = await importShared('zod');

/**
 * Zod validator for a positive integer.
 */
const POSITIVE_INT = z.int().positive();
/**
 * Zod number validators.
 */
const NUMBER_VALIDATORS = {
    POSITIVE_INT,
};

export { ArrayContainer, CAMEL_CASE_REGEX, CASE_VALIDATORS, CONSTANT_CASE_REGEX, FieldMapSchemaBuilder, KEBAB_CASE_REGEX, MapContainer, MapOfMapsContainer, NUMBER_VALIDATORS, NamedArrayFieldContainer, NamedArrayToMapFieldContainer, ObjectContainer, PASCAL_CASE_REGEX, ScalarContainer, ToposortCyclicalDependencyError, ToposortUnknownNodeError, TypedEventEmitter, assertNoDuplicates, compareStrings, convertCaseWithPrefix, createFieldMap, createFieldMapSchemaBuilder, createTypedEventEmitter, differenceSet, doubleQuot, enhanceErrorWithContext, findClosestMatch, hashWithSHA256, immutableSet, jsonDeepClone, lowercaseFirstChar, mapGroupBy, mapKeyBy, mapValuesOfMap, notEmpty, quot, randomKey, safeMerge, safeMergeAll, safeMergeAllWithOptions, safeMergeMap, safeMergeMaps, safeMergeMapsWithOptions, sortKeysRecursive, sortObjectKeys, stringifyPrettyCompact, stringifyPrettyStable, toposort, toposortLocal, toposortOrdered, uppercaseFirstChar };
//# sourceMappingURL=utils-9w-0yxVZ.js.map
