import { loadConfig } from "c12";
import path from "node:path";
import colors from "ansi-colors";
import colorSupport from "color-support";
//#region src/brands.ts
const fileBrand = "heyapi.file";
const nodeBrand = "heyapi.node";
const symbolBrand = "heyapi.symbol";
//#endregion
//#region src/config/interactive.ts
/**
* Detect if the current session is interactive based on TTY status and environment variables.
* This is used as a fallback when the user doesn't explicitly set the interactive option.
* @internal
*/
function detectInteractiveSession() {
	return Boolean(process.stdin.isTTY && process.stdout.isTTY && !process.env.CI && !process.env.NO_INTERACTIVE && !process.env.NO_INTERACTION);
}
//#endregion
//#region src/config/merge.ts
function isPlainObject(value) {
	return typeof value === "object" && value !== null && !Array.isArray(value);
}
function mergeConfigs(configA, configB) {
	const a = configA || {};
	const b = configB || {};
	const result = { ...a };
	for (const key of Object.keys(b)) {
		const valueA = a[key];
		const valueB = b[key];
		if (isPlainObject(valueA) && isPlainObject(valueB)) result[key] = mergeConfigs(valueA, valueB);
		else result[key] = valueB;
	}
	return result;
}
//#endregion
//#region src/config/load.ts
async function loadConfigFile({ configFile, logger, name, userConfig }) {
	const eventC12 = logger.timeEvent("c12");
	const { config: fileConfig, configFile: loadedConfigFile } = await loadConfig({
		configFile,
		name
	});
	eventC12.timeEnd();
	const fileConfigs = fileConfig instanceof Array ? fileConfig : [fileConfig];
	return {
		configFile: loadedConfigFile,
		configs: fileConfigs.map((config) => mergeConfigs(config, userConfig)),
		foundConfig: fileConfigs.some((config) => Object.keys(config).length)
	};
}
//#endregion
//#region src/log.ts
colors.enabled = colorSupport().hasBasic;
const DEBUG_NAMESPACE = "heyapi";
const NO_WARNINGS = /^(1|true|yes|on)$/i.test(process.env.HEYAPI_DISABLE_WARNINGS ?? "");
const DebugGroups = {
	analyzer: colors.greenBright,
	dsl: colors.cyanBright,
	file: colors.yellowBright,
	registry: colors.blueBright,
	symbol: colors.magentaBright
};
const WarnGroups = { deprecated: colors.magentaBright };
let cachedDebugGroups;
function getDebugGroups() {
	if (cachedDebugGroups) return cachedDebugGroups;
	const value = process.env.DEBUG;
	cachedDebugGroups = new Set(value ? value.split(",").map((x) => x.trim().toLowerCase()) : []);
	return cachedDebugGroups;
}
/**
* Tracks which deprecations have been shown to avoid spam.
*/
const shownDeprecations = /* @__PURE__ */ new Set();
function debug(message, group) {
	const groups = getDebugGroups();
	if (!(groups.has("*") || groups.has(`${DEBUG_NAMESPACE}:*`) || groups.has(`${DEBUG_NAMESPACE}:${group}`) || groups.has(group))) return;
	const prefix = (DebugGroups[group] ?? colors.whiteBright)(`${DEBUG_NAMESPACE}:${group}`);
	console.debug(`${prefix} ${message}`);
}
function warn(message, group) {
	if (NO_WARNINGS) return;
	const color = group ? WarnGroups[group] : colors.yellowBright;
	console.warn(color(`${message}`));
}
function warnDeprecated({ context, field, replacement }) {
	const key = context ? `${context}:${field}:${JSON.stringify(replacement)}` : `${field}:${JSON.stringify(replacement)}`;
	if (shownDeprecations.has(key)) return;
	shownDeprecations.add(key);
	let message = `\`${field}\` is deprecated.`;
	if (replacement) {
		const reps = typeof replacement === "function" ? replacement(field) : replacement;
		const repString = (reps instanceof Array ? reps : [reps]).map((r) => `\`${r}\``).join(" or ");
		message += ` Use ${repString} instead.`;
	}
	warn(`${context ? `[${context}] ` : ""}${message}`, "deprecated");
}
const log = {
	debug,
	warn,
	warnDeprecated
};
//#endregion
//#region src/files/file.ts
var File = class {
	/**
	* Exports from this file.
	*/
	_exports = [];
	/**
	* File extension (e.g., `.ts`).
	*/
	_extension;
	/**
	* Actual emitted file path, including extension and directories.
	*/
	_finalPath;
	/**
	* Imports to this file.
	*/
	_imports = [];
	/**
	* Language of the file.
	*/
	_language;
	/**
	* Logical, extension-free path used for planning and routing.
	*/
	_logicalFilePath;
	/**
	* Base name of the file (without extension).
	*/
	_name;
	/**
	* Syntax nodes contained in this file.
	*/
	_nodes = [];
	/**
	* Renderer assigned to this file.
	*/
	_renderer;
	/** Brand used for identifying files. */
	"~brand" = fileBrand;
	/** All names defined in this file, including local scopes. */
	allNames = /* @__PURE__ */ new Map();
	/** Whether this file is external to the project. */
	external;
	/** Unique identifier for the file. */
	id;
	/** The project this file belongs to. */
	project;
	/** Names declared at the top level of the file. */
	topLevelNames = /* @__PURE__ */ new Map();
	constructor(input, id, project) {
		this.external = input.external ?? false;
		this.id = id;
		if (input.language !== void 0) this._language = input.language;
		this._logicalFilePath = input.logicalFilePath.split(path.sep).join("/");
		if (input.name !== void 0) this._name = input.name;
		this.project = project;
	}
	/**
	* Exports from this file.
	*/
	get exports() {
		return [...this._exports];
	}
	/**
	* Read-only accessor for the file extension.
	*/
	get extension() {
		if (this.external) return;
		if (this._extension) return this._extension;
		const language = this.language;
		const extension = language ? this.project.extensions[language] : void 0;
		if (extension && extension[0]) return extension[0];
	}
	/**
	* Read-only accessor for the final emitted path.
	*
	* If undefined, the file has not yet been assigned a final path
	* or is external to the project and should not be emitted.
	*/
	get finalPath() {
		if (this._finalPath) return this._finalPath;
		return [...this._logicalFilePath ? this._logicalFilePath.split("/").slice(0, -1) : [], `${this.name}${this.extension ?? ""}`].join("/");
	}
	/**
	* Imports to this file.
	*/
	get imports() {
		return [...this._imports];
	}
	/**
	* Language of the file; inferred from nodes or fallback if not set explicitly.
	*/
	get language() {
		if (this._language) return this._language;
		if (this._nodes[0]) return this._nodes[0].language;
	}
	/**
	* Logical, extension-free path used for planning and routing.
	*/
	get logicalFilePath() {
		return this._logicalFilePath;
	}
	/**
	* Base name of the file (without extension).
	*
	* If no name was set explicitly, it is inferred from the logical file path.
	*/
	get name() {
		if (this._name) return this._name;
		const name = this._logicalFilePath.split("/").pop();
		if (name) return name;
		const message = `File ${this.toString()} has no name`;
		log.debug(message, "file");
		throw new Error(message);
	}
	/**
	* Syntax nodes contained in this file.
	*/
	get nodes() {
		return [...this._nodes];
	}
	/**
	* Renderer assigned to this file.
	*/
	get renderer() {
		return this._renderer;
	}
	/**
	* Add an export group to the file.
	*/
	addExport(group) {
		this._exports.push(group);
	}
	/**
	* Add an import group to the file.
	*/
	addImport(group) {
		this._imports.push(group);
	}
	/**
	* Add a syntax node to the file.
	*/
	addNode(node) {
		this._nodes.push(node);
		node.file = this;
	}
	/**
	* Sets the file extension.
	*/
	setExtension(extension) {
		this._extension = extension;
	}
	/**
	* Sets the final emitted path of the file.
	*/
	setFinalPath(path) {
		this._finalPath = path;
	}
	/**
	* Sets the language of the file.
	*/
	setLanguage(lang) {
		this._language = lang;
	}
	/**
	* Sets the name of the file.
	*/
	setName(name) {
		this._name = name;
	}
	/**
	* Sets the renderer assigned to this file.
	*/
	setRenderer(renderer) {
		this._renderer = renderer;
	}
	/**
	* Returns a debug‑friendly string representation identifying the file.
	*/
	toString() {
		return `[File ${this._logicalFilePath}#${this.id}]`;
	}
};
//#endregion
//#region src/guards.ts
function isBrand(value, brand) {
	if (!value || typeof value !== "object") return false;
	return value["~brand"] === brand;
}
function isNode(value) {
	if (!value || typeof value !== "object") return false;
	return isBrand(value, nodeBrand);
}
function isNodeRef(value) {
	return isBrand(value["~ref"], nodeBrand);
}
function isSymbol(value) {
	return isBrand(value, symbolBrand);
}
function isSymbolRef(value) {
	return isBrand(value["~ref"], symbolBrand);
}
//#endregion
//#region src/languages/extensions.ts
const defaultExtensions = {
	c: [".c"],
	"c#": [".cs"],
	"c++": [".cpp", ".hpp"],
	css: [".css"],
	dart: [".dart"],
	go: [".go"],
	haskell: [".hs"],
	html: [".html"],
	java: [".java"],
	javascript: [".js", ".jsx"],
	json: [".json"],
	kotlin: [".kt"],
	lua: [".lua"],
	markdown: [".md"],
	matlab: [".m"],
	perl: [".pl"],
	php: [".php"],
	python: [".py"],
	r: [".r"],
	ruby: [".rb"],
	rust: [".rs"],
	scala: [".scala"],
	shell: [".sh"],
	sql: [".sql"],
	swift: [".swift"],
	typescript: [".ts", ".tsx"],
	yaml: [".yaml", ".yml"]
};
//#endregion
//#region src/languages/modules.ts
const defaultModuleEntryNames = {
	javascript: "index",
	python: "__init__",
	typescript: "index"
};
//#endregion
//#region src/planner/resolvers.ts
const pythonNameConflictResolver = ({ attempt, baseName }) => attempt === 1 ? `${baseName}_` : `${baseName}_${attempt}`;
const simpleNameConflictResolver = ({ attempt, baseName }) => `${baseName}${attempt + 1}`;
const underscoreNameConflictResolver = ({ attempt, baseName }) => `${baseName}_${attempt + 1}`;
//#endregion
//#region src/languages/resolvers.ts
const defaultNameConflictResolvers = {
	php: underscoreNameConflictResolver,
	python: pythonNameConflictResolver,
	ruby: underscoreNameConflictResolver
};
//#endregion
//#region src/logger.ts
let loggerCounter = 0;
const nameToId = (name) => `${name}-${loggerCounter++}`;
const idEnd = (id) => `${id}-end`;
const idLength = (id) => `${id}-length`;
const idStart = (id) => `${id}-start`;
const getSeverity = (duration, percentage) => {
	if (duration > 200) return {
		color: colors.red,
		type: "duration"
	};
	if (percentage > 30) return {
		color: colors.red,
		type: "percentage"
	};
	if (duration > 50) return {
		color: colors.yellow,
		type: "duration"
	};
	if (percentage > 10) return {
		color: colors.yellow,
		type: "percentage"
	};
};
var Logger = class {
	events = [];
	end(result) {
		let event;
		let events = this.events;
		for (const index of result.position) {
			event = events[index];
			if (event?.events) events = event.events;
		}
		if (event && !event.end) event.end = performance.mark(idEnd(event.id));
	}
	/**
	* Recursively end all unended events in the event tree.
	* This ensures all events have end marks before measuring.
	*/
	endAllEvents(events) {
		for (const event of events) {
			if (!event.end) event.end = performance.mark(idEnd(event.id));
			if (event.events.length) this.endAllEvents(event.events);
		}
	}
	report(print = true) {
		const firstEvent = this.events[0];
		if (!firstEvent) return;
		this.endAllEvents(this.events);
		const lastEvent = this.events[this.events.length - 1];
		const name = "root";
		const id = nameToId(name);
		try {
			const measure = performance.measure(idLength(id), idStart(firstEvent.id), idEnd(lastEvent.id));
			if (print) this.reportEvent({
				end: lastEvent.end,
				events: this.events,
				id,
				indent: 0,
				measure,
				name,
				start: firstEvent.start
			});
			return measure;
		} catch {
			return;
		}
	}
	reportEvent({ indent, ...parent }) {
		const color = !indent ? colors.cyan : colors.gray;
		const lastIndex = parent.events.length - 1;
		parent.events.forEach((event, index) => {
			try {
				const measure = performance.measure(idLength(event.id), idStart(event.id), idEnd(event.id));
				const duration = Math.ceil(measure.duration * 100) / 100;
				const percentage = Math.ceil(measure.duration / parent.measure.duration * 100 * 100) / 100;
				const severity = indent ? getSeverity(duration, percentage) : void 0;
				let durationLabel = `${duration.toFixed(2).padStart(8)}ms`;
				if (severity?.type === "duration") durationLabel = severity.color(durationLabel);
				const branch = index === lastIndex ? "└─ " : "├─ ";
				const prefix = !indent ? "" : "│  ".repeat(indent - 1) + branch;
				const maxLength = 38 - prefix.length;
				const percentageBranch = !indent ? "" : "↳ ";
				let percentageLabel = `${indent ? " ".repeat(indent - 1) + percentageBranch : ""}${percentage.toFixed(2)}%`;
				if (severity?.type === "percentage") percentageLabel = severity.color(percentageLabel);
				const jobPrefix = colors.gray("[root] ");
				console.log(`${jobPrefix}${colors.gray(prefix)}${color(`${event.name.padEnd(maxLength)} ${durationLabel} (${percentageLabel})`)}`);
				this.reportEvent({
					...event,
					indent: indent + 1,
					measure
				});
			} catch {}
		});
	}
	start(id) {
		return performance.mark(idStart(id));
	}
	storeEvent({ result, ...event }) {
		const lastEventIndex = event.events.length - 1;
		const lastEvent = event.events[lastEventIndex];
		if (lastEvent && !lastEvent.end) {
			result.position = [...result.position, lastEventIndex];
			this.storeEvent({
				...event,
				events: lastEvent.events,
				result
			});
			return;
		}
		const length = event.events.push({
			...event,
			events: []
		});
		result.position = [...result.position, length - 1];
	}
	timeEvent(name) {
		const id = nameToId(name);
		const start = this.start(id);
		const event = {
			events: this.events,
			id,
			name,
			start
		};
		const result = { position: [] };
		this.storeEvent({
			...event,
			result
		});
		return {
			mark: start,
			timeEnd: () => this.end(result)
		};
	}
};
//#endregion
//#region src/files/registry.ts
var FileRegistry = class {
	_id = 0;
	_values = /* @__PURE__ */ new Map();
	project;
	constructor(project) {
		this.project = project;
	}
	get(args) {
		return this._values.get(this.createFileKey(args));
	}
	isRegistered(args) {
		return this._values.has(this.createFileKey(args));
	}
	get nextId() {
		return this._id++;
	}
	register(file) {
		const key = this.createFileKey(file);
		let result = this._values.get(key);
		if (result) {
			if (file.name) result.setName(file.name);
		} else result = new File(file, this.nextId, this.project);
		this._values.set(key, result);
		return result;
	}
	*registered() {
		for (const file of this._values.values()) yield file;
	}
	createFileKey(args) {
		const logicalPath = args.logicalFilePath.split(path.sep).join("/");
		return `${args.external ? "ext:" : ""}${logicalPath}${args.language ? `:${args.language}` : ""}`;
	}
};
//#endregion
//#region src/refs/refs.ts
/**
* Wraps a single value in a Ref object.
*
* If the value is already a Ref, returns it as-is (idempotent).
*
* @example
* ```ts
* const r = ref(123); // { '~ref': 123 }
* console.log(r['~ref']); // 123
*
* const r2 = ref(r); // { '~ref': 123 } (not double-wrapped)
* ```
*/
const ref = (value) => {
	if (isRef(value)) return value;
	return { "~ref": value };
};
/**
* Converts a plain object to an object of Refs (deep, per property).
*
* @example
* ```ts
* const obj = { a: 1, b: "x" };
* const refs = refs(obj); // { a: { '~ref': 1 }, b: { '~ref': "x" } }
* ```
*/
const refs = (obj) => {
	const result = {};
	for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key)) result[key] = ref(obj[key]);
	return result;
};
/**
* Unwraps a single Ref object to its value.
*
* @example
* ```ts
* const r = { '~ref': 42 };
* const n = fromRef(r); // 42
* console.log(n); // 42
* ```
*/
const fromRef = (ref) => ref?.["~ref"];
/**
* Converts an object of Refs back to a plain object (unwraps all refs).
*
* @example
* ```ts
* const refs = { a: { '~ref': 1 }, b: { '~ref': "x" } };
* const plain = fromRefs(refs); // { a: 1, b: "x" }
* ```
*/
const fromRefs = (obj) => {
	const result = {};
	for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key)) result[key] = fromRef(obj[key]);
	return result;
};
/**
* Checks whether a value is a Ref object.
*
* @param value Value to check
* @returns True if the value is a Ref object.
*/
const isRef = (value) => typeof value === "object" && value !== null && "~ref" in value;
//#endregion
//#region src/nodes/registry.ts
var NodeRegistry = class {
	list = [];
	add(node) {
		return this.list.push(ref(node)) - 1;
	}
	*all() {
		for (const r of this.list) {
			const node = fromRef(r);
			if (node) yield node;
		}
	}
	remove(index) {
		this.list[index] = ref(null);
	}
	update(index, node) {
		this.list[index] = ref(node);
	}
};
//#endregion
//#region src/project/namespace.ts
const typescriptMergeKindRank = {
	class: 3,
	enum: 4,
	function: 5,
	interface: 1,
	namespace: 0,
	type: 2,
	var: 6
};
/**
* Returns true if two declarations of given kinds
* are allowed to share the same identifier in TypeScript.
*/
function canTypeScriptDeclarationsShareIdentifier(a, b) {
	if (typescriptMergeKindRank[a] > typescriptMergeKindRank[b]) [a, b] = [b, a];
	switch (a) {
		case "interface": return b === "class" || b === "interface";
		case "namespace": return b === "class" || b === "enum" || b === "function" || b === "namespace";
		case "type": return b === "function" || b === "var";
		default: return false;
	}
}
function canDeclarationsShareIdentifier(language, a, b) {
	if (language === "typescript") return canTypeScriptDeclarationsShareIdentifier(a, b);
	return false;
}
//#endregion
//#region src/planner/scope.ts
function createScope(args = {}) {
	return {
		childNames: args.childNames || /* @__PURE__ */ new Map(),
		children: [],
		localNames: args.localNames || /* @__PURE__ */ new Map(),
		parent: args.parent,
		symbols: []
	};
}
function registerName(scope, name, kind) {
	const kinds = scope.localNames.get(name) ?? /* @__PURE__ */ new Set();
	kinds.add(kind);
	scope.localNames.set(name, kinds);
}
function registerChildName(scope, name, kind) {
	const kinds = scope.childNames.get(name) ?? /* @__PURE__ */ new Set();
	kinds.add(kind);
	scope.childNames.set(name, kinds);
}
//#endregion
//#region src/planner/analyzer.ts
var AnalysisContext = class {
	/**
	* Stack of parent nodes during analysis.
	*
	* The top of the stack is the current semantic container.
	*/
	_parentStack = [];
	scope;
	scopes = createScope();
	symbol;
	constructor(node) {
		this._parentStack.push(node);
		this.scope = this.scopes;
		this.symbol = node.symbol;
	}
	/**
	* Get the current semantic parent (top of stack).
	*/
	get currentParent() {
		return this._parentStack[this._parentStack.length - 1];
	}
	/**
	* Register a child node under the current parent.
	*/
	addChild(child, relationship = "container") {
		const parent = this.currentParent;
		if (!parent) return;
		if (!parent.structuralChildren) parent.structuralChildren = /* @__PURE__ */ new Map();
		parent.structuralChildren.set(child, relationship);
		if (!child.structuralParents) child.structuralParents = /* @__PURE__ */ new Map();
		child.structuralParents.set(parent, relationship);
	}
	addDependency(symbol) {
		if (this.symbol !== fromRef(symbol)) this.scope.symbols.push(symbol);
	}
	analyze(input) {
		const value = isRef(input) ? input : ref(input);
		if (isSymbolRef(value)) {
			const symbol = fromRef(value);
			if (symbol.node && this.currentParent !== symbol.node) this.addChild(symbol.node, "reference");
			this.addDependency(value);
		} else if (isNodeRef(value)) {
			const node = fromRef(value);
			this.addChild(node, "container");
			this.pushParent(node);
			node.analyze(this);
			this.popParent();
		}
	}
	injectChildren(input) {
		const value = isRef(input) ? input : ref(input);
		const symbol = isSymbolRef(value) ? fromRef(value) : void 0;
		if (!symbol) return;
		for (const child of symbol.children) if (!child.overridable) registerChildName(this.scope, child.name, child.kind);
	}
	localNames(scope) {
		const names = /* @__PURE__ */ new Map();
		for (const [name, kinds] of scope.localNames) names.set(name, new Set(kinds));
		if (scope.parent) {
			const parentNames = this.localNames(scope.parent);
			for (const [name, kinds] of parentNames) if (!names.has(name)) names.set(name, kinds);
			else {
				const existingKinds = names.get(name);
				for (const kind of kinds) existingKinds.add(kind);
			}
		}
		return names;
	}
	/**
	* Pop the current semantic parent.
	* Call this when exiting a container node.
	*/
	popParent() {
		this._parentStack.pop();
	}
	popScope() {
		this.scope = this.scope.parent ?? this.scope;
	}
	/**
	* Push a node as the current semantic parent.
	*/
	pushParent(node) {
		this._parentStack.push(node);
	}
	pushScope() {
		const scope = createScope({ parent: this.scope });
		this.scope.children.push(scope);
		this.scope = scope;
	}
	walkScopes(callback, scope = this.scopes) {
		this.scope = scope;
		for (const symbol of scope.symbols) callback(symbol, scope);
		for (const child of scope.children) {
			scope = child;
			this.walkScopes(callback, scope);
		}
		this.scope = this.scopes;
	}
};
var Analyzer = class {
	nodeCache = /* @__PURE__ */ new WeakMap();
	analyzeNode(node) {
		const cached = this.nodeCache.get(node);
		if (cached) return cached;
		node.root = true;
		const ctx = new AnalysisContext(node);
		node.analyze(ctx);
		this.nodeCache.set(node, ctx);
		return ctx;
	}
	analyze(nodes, callback) {
		for (const node of nodes) {
			const ctx = this.analyzeNode(node);
			callback?.(ctx, node);
		}
	}
};
//#endregion
//#region src/planner/planner.ts
const isTypeOnlyKind = (kind) => kind === "type" || kind === "interface";
var Planner = class {
	analyzer = new Analyzer();
	cacheResolvedNames = /* @__PURE__ */ new Set();
	project;
	constructor(project) {
		this.project = project;
	}
	/**
	* Executes the planning phase for the project.
	*/
	plan(meta) {
		this.cacheResolvedNames.clear();
		this.allocateFiles();
		this.assignLocalNames();
		this.resolveFilePaths(meta);
		this.planExports();
		this.planImports();
	}
	/**
	* Creates and assigns a file to every node, re-export,
	* and external dependency.
	*/
	allocateFiles() {
		this.analyzer.analyze(this.project.nodes.all(), (ctx, node) => {
			const symbol = node.symbol;
			if (!symbol) return;
			const file = this.project.files.register({
				external: false,
				language: node.language,
				logicalFilePath: symbol.getFilePath?.(symbol) || this.project.defaultFileName
			});
			file.addNode(node);
			symbol.setFile(file);
			for (const logicalFilePath of symbol.getExportFromFilePath?.(symbol) ?? []) this.project.files.register({
				external: false,
				language: file.language,
				logicalFilePath
			});
			ctx.walkScopes((dependency) => {
				const dep = fromRef(dependency);
				if (dep.external && dep.isCanonical && !dep.file) {
					const file = this.project.files.register({
						external: true,
						language: dep.node?.language,
						logicalFilePath: dep.external
					});
					dep.setFile(file);
				}
			});
		});
	}
	/**
	* Assigns final names to all symbols.
	*
	* First assigns top-level (file-scoped) symbol names, then local symbols.
	*/
	assignLocalNames() {
		this.analyzer.analyze(this.project.nodes.all(), (ctx, node) => {
			const symbol = node.symbol;
			if (!symbol) return;
			this.assignTopLevelName({
				ctx,
				node,
				symbol
			});
		});
		this.analyzer.analyze(this.project.nodes.all(), (ctx, node) => {
			const file = node.file;
			if (!file) return;
			ctx.walkScopes((dependency) => {
				const dep = fromRef(dependency);
				if (dep.file) return;
				this.assignLocalName({
					ctx,
					file,
					scopesToUpdate: [createScope({ localNames: file.allNames })],
					symbol: dep
				});
			});
		});
	}
	/**
	* Resolves and sets final file paths for all non-external files. Attaches renderers.
	*
	* Uses the project's fileName function if provided, otherwise uses the file's current name.
	*
	* Resolves final paths relative to the project's root directory.
	*/
	resolveFilePaths(meta) {
		for (const file of this.project.files.registered()) {
			if (file.external) {
				file.setFinalPath(file.logicalFilePath);
				continue;
			}
			const finalName = this.project.fileName?.(file.name) || file.name;
			file.setName(finalName);
			const finalPath = file.finalPath;
			if (finalPath) file.setFinalPath(path.resolve(this.project.root, finalPath));
			const ctx = {
				file,
				meta,
				project: this.project
			};
			const renderer = this.project.renderers.find((r) => r.supports(ctx));
			if (renderer) file.setRenderer(renderer);
		}
	}
	/**
	* Plans exports by analyzing all exported symbols.
	*
	* Registers re-export targets as files and creates new exported symbols for them.
	*
	* Assigns names to re-exported symbols and collects re-export metadata,
	* distinguishing type-only exports based on symbol kinds.
	*/
	planExports() {
		const seenByFile = /* @__PURE__ */ new Map();
		const sourceFile = /* @__PURE__ */ new Map();
		this.analyzer.analyze(this.project.nodes.all(), (ctx, node) => {
			if (!node.exported) return;
			const symbol = node.symbol;
			if (!symbol) return;
			const file = node.file;
			if (!file) return;
			for (const logicalFilePath of symbol.getExportFromFilePath?.(symbol) ?? []) {
				const target = this.project.files.register({
					external: false,
					language: node.language,
					logicalFilePath
				});
				if (target.id === file.id) continue;
				let fileMap = seenByFile.get(target);
				if (!fileMap) {
					fileMap = /* @__PURE__ */ new Map();
					seenByFile.set(target, fileMap);
				}
				const exp = this.project.symbols.register({
					exported: true,
					external: symbol.external,
					importKind: symbol.importKind,
					kind: symbol.kind,
					name: symbol.finalName
				});
				exp.setFile(target);
				sourceFile.set(exp.id, file);
				this.assignTopLevelName({
					ctx,
					symbol: exp
				});
				let entry = fileMap.get(exp.finalName);
				if (!entry) {
					entry = {
						kinds: /* @__PURE__ */ new Set(),
						symbol: exp
					};
					fileMap.set(exp.finalName, entry);
				}
				entry.kinds.add(exp.kind);
			}
		});
		for (const [file, fileMap] of seenByFile) {
			const exports = /* @__PURE__ */ new Map();
			for (const [, entry] of fileMap) {
				const source = sourceFile.get(entry.symbol.id);
				let exp = exports.get(source);
				if (!exp) exp = {
					canExportAll: true,
					exports: [],
					from: source,
					isTypeOnly: true
				};
				const isTypeOnly = [...entry.kinds].every((kind) => isTypeOnlyKind(kind));
				const exportedName = entry.symbol.finalName;
				exp.exports.push({
					exportedName,
					isTypeOnly,
					kind: entry.symbol.importKind,
					sourceName: entry.symbol.name
				});
				if (entry.symbol.name !== entry.symbol.finalName) exp.canExportAll = false;
				if (!isTypeOnly) exp.isTypeOnly = false;
				exports.set(source, exp);
			}
			for (const [, exp] of exports) file.addExport(exp);
		}
	}
	/**
	* Plans imports by analyzing symbol dependencies across files.
	*
	* For external dependencies, assigns top-level names.
	*
	* Creates or reuses import symbols for dependencies from other files,
	* assigning names and updating import metadata including type-only flags.
	*/
	planImports() {
		const seenByFile = /* @__PURE__ */ new Map();
		this.analyzer.analyze(this.project.nodes.all(), (ctx) => {
			const symbol = ctx.symbol;
			if (!symbol) return;
			const file = symbol.file;
			if (!file) return;
			let fileMap = seenByFile.get(file);
			if (!fileMap) {
				fileMap = /* @__PURE__ */ new Map();
				seenByFile.set(file, fileMap);
			}
			ctx.walkScopes((dependency) => {
				const dep = fromRef(dependency);
				if (!dep.file || dep.file.id === file.id) return;
				if (dep.external) this.assignTopLevelName({
					ctx,
					symbol: dep
				});
				const key = `${dep.file.id}|${dep.finalName}|${dep.importKind}`;
				let entry = fileMap.get(key);
				if (!entry) {
					const imp = this.project.symbols.register({
						exported: dep.exported,
						external: dep.external,
						importKind: dep.importKind,
						kind: dep.kind,
						name: dep.finalName
					});
					imp.setFile(file);
					this.assignTopLevelName({
						ctx,
						scope: createScope({ localNames: imp.file.allNames }),
						symbol: imp
					});
					entry = {
						dep,
						kinds: /* @__PURE__ */ new Set(),
						symbol: imp
					};
					fileMap.set(key, entry);
				}
				entry.kinds.add(dep.kind);
				dependency["~ref"] = entry.symbol;
			});
		});
		for (const [file, fileMap] of seenByFile) {
			const imports = /* @__PURE__ */ new Map();
			for (const [, entry] of fileMap) {
				const source = entry.dep.file;
				let imp = imports.get(source);
				if (!imp) imp = {
					from: source,
					imports: [],
					isTypeOnly: true,
					kind: "named"
				};
				const isTypeOnly = [...entry.kinds].every((kind) => isTypeOnlyKind(kind));
				if (entry.symbol.importKind === "namespace") {
					imp.imports = [];
					imp.kind = "namespace";
					imp.localName = entry.symbol.finalName;
				} else if (entry.symbol.importKind === "default") {
					imp.kind = "default";
					imp.localName = entry.symbol.finalName;
				} else imp.imports.push({
					isTypeOnly,
					localName: entry.symbol.finalName,
					sourceName: entry.dep.finalName
				});
				if (!isTypeOnly) imp.isTypeOnly = false;
				imports.set(source, imp);
			}
			for (const [, imp] of imports) file.addImport(imp);
		}
	}
	/**
	* Assigns the final name to a top-level (file-scoped) symbol.
	*
	* Uses the symbol's file top-level names as the default scope,
	* and updates all relevant name scopes including the file's allNames and local scopes.
	*
	* Supports optional overrides for the naming scope and scopes to update.
	*/
	assignTopLevelName(args) {
		if (!args.symbol.file) return;
		this.assignSymbolName({
			...args,
			file: args.symbol.file,
			scope: args?.scope ?? createScope({ localNames: args.symbol.file.topLevelNames }),
			scopesToUpdate: [
				createScope({ localNames: args.symbol.file.allNames }),
				args.ctx.scopes,
				...args?.scopesToUpdate ?? []
			]
		});
	}
	/**
	* Assigns the final name to a non-top-level (local) symbol.
	*
	* Uses the provided scope or derives it from the current analysis context's local names.
	*
	* Updates all provided name scopes accordingly.
	*/
	assignLocalName(args) {
		this.assignSymbolName({
			...args,
			scope: args.scope ?? args.ctx.scope
		});
	}
	/**
	* Assigns the final name to a symbol within the provided name scope.
	*
	* Resolves name conflicts until a unique name is found.
	*
	* Updates all specified name scopes with the assigned final name.
	*/
	assignSymbolName(args) {
		const { file, node, scope, scopesToUpdate, symbol } = args;
		if (this.cacheResolvedNames.has(symbol.id)) return;
		const baseName = symbol.name;
		let finalName = node?.nameSanitizer?.(baseName) ?? symbol.node?.nameSanitizer?.(baseName) ?? baseName;
		let attempt = 1;
		while (true) {
			const language = node?.language || symbol.node?.language || file.language;
			if (this.nameIsAvailable({
				kind: symbol.kind,
				language,
				name: finalName,
				override: symbol.override,
				scope
			})) break;
			const resolvedName = ((language ? this.project.nameConflictResolvers[language] : void 0) ?? this.project.defaultNameConflictResolver)({
				attempt,
				baseName
			});
			if (!resolvedName) throw new Error(`Unresolvable name conflict: ${symbol.toString()}`);
			finalName = node?.nameSanitizer?.(resolvedName) ?? symbol.node?.nameSanitizer?.(resolvedName) ?? resolvedName;
			attempt = attempt + 1;
		}
		symbol.setFinalName(finalName);
		this.cacheResolvedNames.add(symbol.id);
		const updateScopes = [scope, ...scopesToUpdate];
		for (const scope of updateScopes) registerName(scope, symbol.finalName, symbol.kind);
	}
	/**
	* Checks whether `name` can be used for a new symbol of `kind` in `scope`.
	*
	* Walks up the scope chain and verifies that every existing declaration with
	* that name is compatible (i.e., can share the same identifier) with `kind`.
	* This avoids copying the entire accumulated name map on every call.
	*/
	nameIsAvailable({ kind, language, name, override, scope }) {
		function conflicts(kinds) {
			if (!kinds) return false;
			for (const existingKind of kinds) if (!canDeclarationsShareIdentifier(language, kind, existingKind)) return true;
			return false;
		}
		let current = scope;
		while (current) {
			if (!override && conflicts(current.childNames.get(name))) return false;
			if (conflicts(current.localNames.get(name))) return false;
			current = current.parent;
		}
		return true;
	}
};
//#endregion
//#region src/symbols/symbol.ts
var Symbol = class {
	/**
	* Canonical symbol this stub resolves to, if any.
	*
	* Stubs created during DSL construction may later be associated
	* with a fully registered symbol. Once set, all property lookups
	* should defer to the canonical symbol.
	*/
	_canonical;
	/**
	* Child symbol bindings scoped under this symbol.
	*
	* @default []
	*/
	_children;
	/**
	* True if this symbol is exported from its defining file.
	*
	* @default false
	*/
	_exported;
	/**
	* External module name if this symbol is imported from a module not managed
	* by the project (e.g., "zod", "lodash").
	*
	* @default undefined
	*/
	_external;
	/**
	* The file this symbol is ultimately emitted into.
	*
	* Only top-level symbols have an assigned file.
	*/
	_file;
	/**
	* The alias-resolved, conflict-free emitted name.
	*/
	_finalName;
	/**
	* Custom strategy to determine from which file path(s) this symbol is re-exported.
	*
	* @returns The file path(s) that re-export this symbol, or undefined if none.
	*/
	_getExportFromFilePath;
	/**
	* Custom strategy to determine file output path.
	*
	* @returns The file path to output the symbol to, or undefined to fallback to default behavior.
	*/
	_getFilePath;
	/**
	* How this symbol should be imported (namespace/default/named).
	*
	* @default 'named'
	*/
	_importKind;
	/**
	* Kind of symbol (class, type, alias, etc.).
	*
	* @default 'var'
	*/
	_kind;
	/**
	* Arbitrary user metadata.
	*
	* @default undefined
	*/
	_meta;
	/**
	* Intended user-facing name before conflict resolution.
	*
	* @example "UserModel"
	*/
	_name;
	/**
	* Node that defines this symbol.
	*/
	_node;
	/**
	* Indicates whether this symbol overrides another declaration.
	*
	* @default false
	*/
	_override;
	/** Brand used for identifying symbols. */
	"~brand" = symbolBrand;
	/** Globally unique, stable symbol ID. */
	id;
	constructor(input, id) {
		this._children = input.children ?? [];
		this._exported = input.exported ?? false;
		this._external = input.external;
		this._getExportFromFilePath = input.getExportFromFilePath;
		this._getFilePath = input.getFilePath;
		this.id = id;
		this._importKind = input.importKind ?? "named";
		this._kind = input.kind ?? "var";
		this._meta = input.meta;
		this._name = input.name;
		this._override = input.override ?? false;
	}
	/**
	* Returns the canonical symbol for this instance.
	*
	* If this symbol was created as a stub, this getter returns
	* the fully registered canonical symbol. Otherwise, it returns
	* the symbol itself.
	*/
	get canonical() {
		return this._canonical ?? this;
	}
	/**
	* Read-only accessor for child symbol bindings.
	*/
	get children() {
		return this.canonical._children;
	}
	/**
	* Indicates whether this symbol is exported from its defining file.
	*/
	get exported() {
		return this.canonical._exported;
	}
	/**
	* External module from which this symbol originates, if any.
	*/
	get external() {
		return this.canonical._external;
	}
	/**
	* Read‑only accessor for the assigned output file.
	*
	* Only top-level symbols have an assigned file.
	*/
	get file() {
		return this.canonical._file;
	}
	/**
	* Read‑only accessor for the resolved final emitted name.
	*/
	get finalName() {
		if (!this.canonical._finalName) {
			const message = `Symbol finalName has not been resolved yet for ${this.canonical.toString()}`;
			log.debug(message, "symbol");
			throw new Error(message);
		}
		return this.canonical._finalName;
	}
	/**
	* Custom re-export file path resolver, if provided.
	*/
	get getExportFromFilePath() {
		return this.canonical._getExportFromFilePath;
	}
	/**
	* Custom file path resolver, if provided.
	*/
	get getFilePath() {
		return this.canonical._getFilePath;
	}
	/**
	* How this symbol should be imported (named/default/namespace).
	*/
	get importKind() {
		return this.canonical._importKind;
	}
	/**
	* Indicates whether this is a canonical symbol (not a stub).
	*/
	get isCanonical() {
		return !this._canonical || this._canonical === this;
	}
	/**
	* The symbol's kind (class, type, alias, variable, etc.).
	*/
	get kind() {
		return this.canonical._kind;
	}
	/**
	* Arbitrary user‑provided metadata associated with this symbol.
	*/
	get meta() {
		return this.canonical._meta;
	}
	/**
	* User-intended name before aliasing or conflict resolution.
	*/
	get name() {
		return this.canonical._name;
	}
	/**
	* Read‑only accessor for the defining node.
	*/
	get node() {
		return this.canonical._node;
	}
	/**
	* Indicates whether this symbol is marked as an override.
	*/
	get override() {
		return this.canonical._override;
	}
	/**
	* Marks this symbol as a stub and assigns its canonical symbol.
	*
	* After calling this, all semantic queries (name, kind, file,
	* meta, etc.) should reflect the canonical symbol's values.
	*
	* @param symbol — The canonical symbol this stub should resolve to.
	*/
	setCanonical(symbol) {
		this._canonical = symbol;
	}
	/**
	* Assigns the child symbol bindings associated with this symbol.
	*
	* @param children — Child bindings to associate with the symbol.
	*/
	setChildren(children) {
		this.assertCanonical();
		this._children = children;
	}
	/**
	* Marks the symbol as exported from its file.
	*
	* @param exported — Whether the symbol is exported.
	*/
	setExported(exported) {
		this.assertCanonical();
		this._exported = exported;
	}
	/**
	* Assigns the output file this symbol will be emitted into.
	*
	* This may only be set once.
	*/
	setFile(file) {
		this.assertCanonical();
		if (this._file && this._file !== file) {
			const message = `Symbol ${this.canonical.toString()} is already assigned to a different file.`;
			log.debug(message, "symbol");
			throw new Error(message);
		}
		this._file = file;
	}
	/**
	* Assigns the conflict‑resolved final local name for this symbol.
	*
	* This may only be set once.
	*/
	setFinalName(name) {
		this.assertCanonical();
		if (this._finalName && this._finalName !== name) {
			const message = `Symbol finalName has already been resolved for ${this.canonical.toString()}.`;
			log.debug(message, "symbol");
			throw new Error(message);
		}
		this._finalName = name;
	}
	/**
	* Sets how this symbol should be imported.
	*
	* @param kind — The import strategy (named/default/namespace).
	*/
	setImportKind(kind) {
		this.assertCanonical();
		this._importKind = kind;
	}
	/**
	* Sets the symbol's kind (class, type, alias, variable, etc.).
	*
	* @param kind — The new symbol kind.
	*/
	setKind(kind) {
		this.assertCanonical();
		this._kind = kind;
	}
	/**
	* Updates the intended user‑facing name for this symbol.
	*
	* @param name — The new name.
	*/
	setName(name) {
		this.assertCanonical();
		this._name = name;
	}
	/**
	* Binds the node that defines this symbol.
	*
	* This may only be set once.
	*/
	setNode(node) {
		this.assertCanonical();
		if (this._node && this._node !== node) {
			const message = `Symbol ${this.canonical.toString()} is already bound to a different node.`;
			log.debug(message, "symbol");
		}
		this._node = node;
		node.symbol = this;
	}
	/**
	* Marks whether this symbol should be treated as an override.
	*
	* @param override — Override marker value.
	*/
	setOverride(override) {
		this.assertCanonical();
		this._override = override;
	}
	/**
	* Returns a debug‑friendly string representation identifying the symbol.
	*/
	toString() {
		const canonical = this.canonical;
		if (canonical._finalName) return `[Symbol ${canonical._name} → ${canonical._finalName}#${canonical.id}]`;
		return `[Symbol ${canonical._name}#${canonical.id}] ${JSON.stringify(canonical._meta ?? {})}`;
	}
	/**
	* Ensures this symbol is canonical before allowing mutation.
	*
	* A symbol that has been marked as a stub (i.e., its `_canonical` points
	* to a different symbol) may not be mutated. This guard throws an error
	* if any setter attempts to modify a stub, preventing accidental writes
	* to non‑canonical instances.
	*
	* @throws {Error} If the symbol is a stub and is being mutated.
	*/
	assertCanonical() {
		if (this._canonical && this._canonical !== this) {
			const message = `Illegal mutation of stub symbol ${this.toString()} → canonical: ${this._canonical.toString()}`;
			log.debug(message, "symbol");
			throw new Error(message);
		}
	}
};
//#endregion
//#region src/symbols/registry.ts
var SymbolRegistry = class {
	/** Forward index: cache key → index key space it was registered with. */
	_cacheDependencies = /* @__PURE__ */ new Map();
	/** Reverse index: serialized index entry → set of cache keys that depend on it. */
	_dependencyToCache = /* @__PURE__ */ new Map();
	_id = 0;
	_indices = /* @__PURE__ */ new Map();
	_queryCache = /* @__PURE__ */ new Map();
	_registered = /* @__PURE__ */ new Set();
	_stubs = /* @__PURE__ */ new Set();
	_stubCache = /* @__PURE__ */ new Map();
	_values = /* @__PURE__ */ new Map();
	get(identifier) {
		return typeof identifier === "number" ? this._values.get(identifier) : this.query(identifier)[0];
	}
	isRegistered(identifier) {
		const symbol = this.get(identifier);
		return symbol ? this._registered.has(symbol.id) : false;
	}
	get nextId() {
		return this._id++;
	}
	query(filter) {
		const indexKeySpace = this.buildIndexKeySpace(filter);
		const cacheKey = this.cacheKeyFromKeySpace(indexKeySpace);
		return this.queryByKeySpace(indexKeySpace, cacheKey);
	}
	reference(meta) {
		const indexKeySpace = this.buildIndexKeySpace(meta);
		const cacheKey = this.cacheKeyFromKeySpace(indexKeySpace);
		const [registered] = this.queryByKeySpace(indexKeySpace, cacheKey);
		if (registered) return registered;
		const cachedId = this._stubCache.get(cacheKey);
		if (cachedId !== void 0) return this._values.get(cachedId);
		const stub = new Symbol({
			meta,
			name: ""
		}, this.nextId);
		this._values.set(stub.id, stub);
		this._stubs.add(stub.id);
		this._stubCache.set(cacheKey, stub.id);
		return stub;
	}
	register(symbol) {
		const result = new Symbol(symbol, this.nextId);
		this._values.set(result.id, result);
		this._registered.add(result.id);
		if (result.meta) {
			const indexKeySpace = this.buildIndexKeySpace(result.meta);
			this.indexSymbol(result.id, indexKeySpace);
			this.invalidateCache(indexKeySpace);
			this.replaceStubs(result, indexKeySpace);
		}
		return result;
	}
	*registered() {
		for (const id of this._registered.values()) yield this._values.get(id);
	}
	buildIndexKeySpace(meta, prefix = "", entries = []) {
		for (const [key, value] of Object.entries(meta)) {
			const path = prefix ? `${prefix}.${key}` : key;
			if (value && typeof value === "object" && !Array.isArray(value)) this.buildIndexKeySpace(value, path, entries);
			else entries.push([
				path,
				value,
				`${path}:${JSON.stringify(value)}`
			]);
		}
		return entries;
	}
	/**
	* Derives a stable, order-insensitive cache key from a pre-built key space.
	* Avoids rebuilding the key space when it's already available.
	*/
	cacheKeyFromKeySpace(indexKeySpace) {
		return indexKeySpace.map((indexEntry) => indexEntry[2]).sort().join("|");
	}
	indexSymbol(symbolId, indexKeySpace) {
		for (const [key, value] of indexKeySpace) {
			if (!this._indices.has(key)) this._indices.set(key, /* @__PURE__ */ new Map());
			const values = this._indices.get(key);
			const set = values.get(value) ?? /* @__PURE__ */ new Set();
			set.add(symbolId);
			values.set(value, set);
		}
	}
	invalidateCache(indexKeySpace) {
		for (const indexEntry of indexKeySpace) {
			const serialized = indexEntry[2];
			const cacheKeys = this._dependencyToCache.get(serialized);
			if (!cacheKeys) continue;
			for (const cacheKey of cacheKeys) {
				this._queryCache.delete(cacheKey);
				const deps = this._cacheDependencies.get(cacheKey);
				if (deps) {
					for (const dep of deps) if (dep[2] !== serialized) this._dependencyToCache.get(dep[2])?.delete(cacheKey);
					this._cacheDependencies.delete(cacheKey);
				}
			}
			this._dependencyToCache.delete(serialized);
		}
	}
	isSubset(sub, sup) {
		const supMap = new Map(sup.map((e) => [e[0], e[1]]));
		for (const [key, value] of sub) if (!supMap.has(key) || supMap.get(key) !== value) return false;
		return true;
	}
	queryByKeySpace(indexKeySpace, cacheKey) {
		const cached = this._queryCache.get(cacheKey);
		if (cached) return cached;
		const sets = [];
		let missed = false;
		for (const indexEntry of indexKeySpace) {
			const serialized = indexEntry[2];
			let cacheKeys;
			if (this._dependencyToCache.has(serialized)) cacheKeys = this._dependencyToCache.get(serialized);
			else {
				cacheKeys = /* @__PURE__ */ new Set();
				this._dependencyToCache.set(serialized, cacheKeys);
			}
			cacheKeys.add(cacheKey);
			if (!missed) {
				const values = this._indices.get(indexEntry[0]);
				if (!values) {
					missed = true;
					continue;
				}
				const set = values.get(indexEntry[1]);
				if (!set) {
					missed = true;
					continue;
				}
				sets.push(set);
			}
		}
		this._cacheDependencies.set(cacheKey, indexKeySpace);
		if (missed || !sets.length) {
			this._queryCache.set(cacheKey, []);
			return [];
		}
		sets.sort((a, b) => a.size - b.size);
		const result = new Set(sets[0]);
		for (let i = 1; i < sets.length; i++) {
			const set = sets[i];
			for (const symbolId of result) if (!set.has(symbolId)) result.delete(symbolId);
		}
		const symbols = Array.from(result, (symbolId) => this._values.get(symbolId));
		this._queryCache.set(cacheKey, symbols);
		return symbols;
	}
	replaceStubs(symbol, indexKeySpace) {
		for (const stubId of this._stubs.values()) {
			const stub = this._values.get(stubId);
			if (stub?.meta) {
				const stubKeySpace = this.buildIndexKeySpace(stub.meta);
				if (this.isSubset(stubKeySpace, indexKeySpace)) {
					const cacheKey = this.cacheKeyFromKeySpace(stubKeySpace);
					this._stubCache.delete(cacheKey);
					this._stubs.delete(stubId);
					stub.setCanonical(symbol);
				}
			}
		}
	}
};
//#endregion
//#region src/project/project.ts
var Project = class {
	_isPlanned = false;
	files;
	nodes = new NodeRegistry();
	symbols = new SymbolRegistry();
	defaultFileName;
	defaultNameConflictResolver;
	extensions;
	fileName;
	moduleEntryNames;
	nameConflictResolvers;
	renderers;
	root;
	constructor(args) {
		const fileName = args.fileName;
		this.defaultFileName = args.defaultFileName ?? "main";
		this.defaultNameConflictResolver = args.defaultNameConflictResolver ?? simpleNameConflictResolver;
		this.extensions = {
			...defaultExtensions,
			...args.extensions
		};
		this.fileName = typeof fileName === "string" ? () => fileName : fileName;
		this.files = new FileRegistry(this);
		this.moduleEntryNames = {
			...defaultModuleEntryNames,
			...args.moduleEntryNames
		};
		this.nameConflictResolvers = {
			...defaultNameConflictResolvers,
			...args.nameConflictResolvers
		};
		this.renderers = args.renderers ?? [];
		this.root = path.resolve(args.root).replace(/[/\\]+$/, "");
	}
	plan(meta) {
		if (this._isPlanned) return;
		new Planner(this).plan(meta);
		this._isPlanned = true;
	}
	render(meta) {
		if (!this._isPlanned) this.plan(meta);
		const files = [];
		for (const file of this.files.registered()) if (!file.external && file.finalPath && file.renderer) {
			const content = file.renderer.render({
				file,
				meta,
				project: this
			});
			files.push({
				content,
				path: file.finalPath
			});
		}
		return files;
	}
};
//#endregion
//#region src/structure/node.ts
var StructureNode = class StructureNode {
	/** Nested nodes within this node. */
	children = /* @__PURE__ */ new Map();
	/** Items contained in this node. */
	items = [];
	/** The name of this node (e.g., "Users", "Accounts"). */
	name;
	/** Parent node in the hierarchy. Undefined if this is the root node. */
	parent;
	/** Shell claimed for this node. */
	shell;
	/** Source of the claimed shell. */
	shellSource;
	/** True if this is a virtual root. */
	virtual;
	constructor(name, parent, options) {
		this.name = name;
		this.parent = parent;
		this.virtual = options?.virtual ?? false;
	}
	get isRoot() {
		return !this.parent;
	}
	/**
	* Gets or creates a child node.
	*
	* If the child doesn't exist, it's created automatically.
	*
	* @param name - The name of the child node
	* @returns The child node instance
	*/
	child(name) {
		if (!this.children.has(name)) this.children.set(name, new StructureNode(name, this));
		return this.children.get(name);
	}
	/**
	* Gets the full path of this node in the hierarchy.
	*
	* @returns An array of node names from the root to this node
	*/
	getPath() {
		const path = [];
		let cursor = this;
		while (cursor) {
			path.unshift(cursor.name);
			cursor = cursor.parent;
		}
		return path;
	}
	/**
	* Yields items from a specific source with typed data.
	*
	* @param source - The source symbol to filter by
	* @returns Generator of items from that source
	*/
	*itemsFrom(source) {
		for (const item of this.items) if (item.source === source) yield item;
	}
	/**
	* Walk all nodes in the structure (depth-first, post-order).
	*
	* @returns Generator of all structure nodes
	*/
	*walk() {
		for (const node of this.children.values()) yield* node.walk();
		yield this;
	}
};
//#endregion
//#region src/structure/model.ts
var StructureModel = class {
	/** Root nodes mapped by their names. */
	_roots = /* @__PURE__ */ new Map();
	/** Node for data without a specific root. */
	_virtualRoot;
	/**
	* Get all root nodes.
	*/
	get roots() {
		const roots = Array.from(this._roots.values());
		if (this._virtualRoot) roots.unshift(this._virtualRoot);
		return roots;
	}
	/**
	* Insert data into the structure.
	*/
	insert(args) {
		const { data, locations, source } = args;
		for (const location of locations) {
			const { path, shell } = location;
			const fullPath = path.filter((s) => Boolean(s));
			const segments = fullPath.slice(0, -1);
			if (!fullPath[fullPath.length - 1]) throw new Error("Cannot insert data without path.");
			let cursor = null;
			for (const segment of segments) {
				if (!cursor) cursor = this.root(segment);
				else cursor = cursor.child(segment);
				if (shell && !cursor.shell) {
					cursor.shell = shell;
					cursor.shellSource = source;
				}
			}
			if (!cursor) cursor = this.root(null);
			cursor.items.push({
				data,
				location: fullPath,
				source
			});
		}
	}
	/**
	* Gets or creates a root by name.
	*
	* If the root doesn't exist, it's created automatically.
	*
	* @param name - The name of the root
	* @returns The root instance
	*/
	root(name) {
		if (!name) return this._virtualRoot ??= new StructureNode("", void 0, { virtual: true });
		if (!this._roots.has(name)) this._roots.set(name, new StructureNode(name));
		return this._roots.get(name);
	}
	/**
	* Walk all nodes in the structure (depth-first, post-order).
	*
	* @returns Generator of all structure nodes
	*/
	*walk() {
		if (this._virtualRoot) yield* this._virtualRoot.walk();
		for (const root of this._roots.values()) yield* root.walk();
	}
};
//#endregion
export { File, Logger, Project, StructureModel, StructureNode, Symbol, defaultExtensions, defaultModuleEntryNames, defaultNameConflictResolvers, detectInteractiveSession, fromRef, fromRefs, isNode, isNodeRef, isRef, isSymbol, isSymbolRef, loadConfigFile, log, mergeConfigs, nodeBrand, pythonNameConflictResolver, ref, refs, simpleNameConflictResolver, symbolBrand, underscoreNameConflictResolver };

//# sourceMappingURL=index.mjs.map