import { t as asNullableRecord } from "./record-coerce-DVQGe6I0.js";
import { o as normalizeOptionalString, t as mergeDeep } from "./deep-merge-B3LHo3pw.js";
import { isDeepStrictEqual } from "node:util";
//#region packages/normalization-core/src/string-normalization.ts
/** Coerces entries to strings, trims them, and drops empty results. */
function normalizeStringEntries(list) {
	return (list ?? []).map((entry) => normalizeOptionalString(String(entry)) ?? "").filter(Boolean);
}
/** Returns first-seen unique values while preserving insertion order. */
function uniqueValues(values) {
	return [...new Set(values)];
}
/** Returns first-seen unique strings while preserving insertion order. */
function uniqueStrings(values) {
	return uniqueValues(values);
}
//#endregion
//#region src/channels/plugins/dm-access.ts
/**
* Channel DM access helpers.
*
* Reads, writes, migrates, and normalizes direct-message policy and allowFrom fields.
*/
function cloneDm(entry) {
	const dm = asNullableRecord(entry.dm);
	return dm ? { ...dm } : null;
}
function allowFromListsMatch(left, right) {
	if (!Array.isArray(left) || !Array.isArray(right)) return false;
	const normalizedLeft = normalizeStringEntries(left);
	const normalizedRight = normalizeStringEntries(right);
	if (normalizedLeft.length !== normalizedRight.length) return false;
	return normalizedLeft.every((value, index) => value === normalizedRight[index]);
}
/**
* Migrates legacy `dm.*` aliases into the canonical DM access fields.
*/
function normalizeLegacyDmAliases(params) {
	let changed = false;
	let updated = params.entry;
	const rawDm = updated.dm;
	const dm = cloneDm(updated);
	let dmChanged = false;
	const topDmPolicy = updated.dmPolicy;
	const legacyDmPolicy = dm?.policy;
	if (topDmPolicy === void 0 && legacyDmPolicy !== void 0) {
		updated = {
			...updated,
			dmPolicy: legacyDmPolicy
		};
		changed = true;
		if (dm) {
			delete dm.policy;
			dmChanged = true;
		}
		params.changes.push(`Moved ${params.pathPrefix}.dm.policy → ${params.pathPrefix}.dmPolicy.`);
	} else if (topDmPolicy !== void 0 && legacyDmPolicy !== void 0 && topDmPolicy === legacyDmPolicy) {
		if (dm) {
			delete dm.policy;
			dmChanged = true;
			params.changes.push(`Removed ${params.pathPrefix}.dm.policy (dmPolicy already set).`);
		}
	}
	if (params.promoteAllowFrom !== false) {
		const topAllowFrom = updated.allowFrom;
		const legacyAllowFrom = dm?.allowFrom;
		if (topAllowFrom === void 0 && legacyAllowFrom !== void 0) {
			updated = {
				...updated,
				allowFrom: legacyAllowFrom
			};
			changed = true;
			if (dm) {
				delete dm.allowFrom;
				dmChanged = true;
			}
			params.changes.push(`Moved ${params.pathPrefix}.dm.allowFrom → ${params.pathPrefix}.allowFrom.`);
		} else if (topAllowFrom !== void 0 && legacyAllowFrom !== void 0 && allowFromListsMatch(topAllowFrom, legacyAllowFrom)) {
			if (dm) {
				delete dm.allowFrom;
				dmChanged = true;
				params.changes.push(`Removed ${params.pathPrefix}.dm.allowFrom (allowFrom already set).`);
			}
		}
	}
	if (dm && asNullableRecord(rawDm) && dmChanged) if (Object.keys(dm).length === 0) {
		if (updated.dm !== void 0) {
			const { dm: _ignored, ...rest } = updated;
			updated = rest;
			changed = true;
			params.changes.push(`Removed empty ${params.pathPrefix}.dm after migration.`);
		}
	} else {
		updated = {
			...updated,
			dm
		};
		changed = true;
	}
	return {
		entry: updated,
		changed
	};
}
//#endregion
//#region src/config/channel-compat-normalization.ts
function parseAliasStreamingMode(value) {
	if (typeof value !== "string") return null;
	const normalized = value.trim().toLowerCase();
	return normalized === "off" || normalized === "partial" || normalized === "block" || normalized === "progress" ? normalized : null;
}
/**
* Doctor-only stream mode resolution across nested and legacy alias keys.
*
* Runtime helpers no longer read `streamMode`, so doctor contracts use this to
* preserve legacy intent (nested mode > scalar string > streamMode > scalar
* boolean) while migrating flat aliases into `streaming.mode`.
*/
function resolveLegacyAliasStreamingMode(entry, defaultMode) {
	const nestedMode = asNullableRecord(entry.streaming)?.mode;
	const parsed = parseAliasStreamingMode(nestedMode ?? entry.streaming) ?? parseAliasStreamingMode(entry.streamMode);
	if (parsed) return parsed;
	if (typeof entry.streaming === "boolean") return entry.streaming ? "partial" : "off";
	return defaultMode;
}
/** Checks whether any account entry still carries a channel-specific legacy alias. */
function hasLegacyAccountStreamingAliases(value, match) {
	const accounts = asNullableRecord(value);
	if (!accounts) return false;
	return Object.values(accounts).some((account) => match(account));
}
function ensureNestedRecord(owner, key) {
	const existing = asNullableRecord(owner[key]);
	if (existing) return { ...existing };
	return {};
}
/**
* Moves legacy flat streaming aliases into the nested `streaming` config shape.
*
* Existing nested values win over legacy aliases, matching doctor migration rules
* that preserve explicit modern config while removing stale compatibility keys.
*/
function normalizeLegacyStreamingAliases(params) {
	const beforeStreaming = params.entry.streaming;
	const hadLegacyStreamMode = params.entry.streamMode !== void 0;
	const hasLegacyFlatFields = params.entry.chunkMode !== void 0 || params.entry.blockStreaming !== void 0 || params.entry.blockStreamingCoalesce !== void 0 || params.includePreviewChunk === true && params.entry.draftChunk !== void 0 || params.entry.nativeStreaming !== void 0;
	if (!(hadLegacyStreamMode || typeof beforeStreaming === "boolean" || typeof beforeStreaming === "string" || hasLegacyFlatFields)) return {
		entry: params.entry,
		changed: false
	};
	const updated = { ...params.entry };
	let changed = false;
	const streaming = ensureNestedRecord(updated, "streaming");
	const block = ensureNestedRecord(streaming, "block");
	const preview = ensureNestedRecord(streaming, "preview");
	let movedStreamMode = false;
	if ((hadLegacyStreamMode || typeof beforeStreaming === "boolean" || typeof beforeStreaming === "string") && streaming.mode === void 0) {
		streaming.mode = params.resolvedMode;
		if (hadLegacyStreamMode) {
			movedStreamMode = true;
			params.changes.push(`Moved ${params.pathPrefix}.streamMode → ${params.pathPrefix}.streaming.mode (${params.resolvedMode}).`);
		} else if (typeof beforeStreaming === "boolean") params.changes.push(`Moved ${params.pathPrefix}.streaming (boolean) → ${params.pathPrefix}.streaming.mode (${params.resolvedMode}).`);
		else if (typeof beforeStreaming === "string") params.changes.push(`Moved ${params.pathPrefix}.streaming (scalar) → ${params.pathPrefix}.streaming.mode (${params.resolvedMode}).`);
		changed = true;
	}
	if (hadLegacyStreamMode) {
		if (!movedStreamMode) params.changes.push(`Removed ${params.pathPrefix}.streamMode (${params.pathPrefix}.streaming.mode already set).`);
		delete updated.streamMode;
		changed = true;
	}
	const moveOrRemoveAlias = (flatKey, target, slot, nestedPath) => {
		if (updated[flatKey] === void 0) return;
		const nested = `${params.pathPrefix}.streaming.${nestedPath}`;
		if (target[slot] === void 0) {
			target[slot] = updated[flatKey];
			params.changes.push(`Moved ${params.pathPrefix}.${flatKey} → ${nested}.`);
		} else params.changes.push(`Removed ${params.pathPrefix}.${flatKey} (${nested} already set).`);
		delete updated[flatKey];
		changed = true;
	};
	moveOrRemoveAlias("chunkMode", streaming, "chunkMode", "chunkMode");
	moveOrRemoveAlias("blockStreaming", block, "enabled", "block.enabled");
	if (params.includePreviewChunk === true) moveOrRemoveAlias("draftChunk", preview, "chunk", "preview.chunk");
	moveOrRemoveAlias("blockStreamingCoalesce", block, "coalesce", "block.coalesce");
	if (updated.nativeStreaming !== void 0 && params.resolvedNativeTransport !== void 0) {
		if (streaming.nativeTransport === void 0) {
			streaming.nativeTransport = params.resolvedNativeTransport;
			params.changes.push(`Moved ${params.pathPrefix}.nativeStreaming → ${params.pathPrefix}.streaming.nativeTransport.`);
		} else params.changes.push(`Removed ${params.pathPrefix}.nativeStreaming (${params.pathPrefix}.streaming.nativeTransport already set).`);
		delete updated.nativeStreaming;
		changed = true;
	} else if (typeof beforeStreaming === "boolean" && streaming.nativeTransport === void 0 && params.resolvedNativeTransport !== void 0) {
		streaming.nativeTransport = params.resolvedNativeTransport;
		params.changes.push(`Moved ${params.pathPrefix}.streaming (boolean) → ${params.pathPrefix}.streaming.nativeTransport.`);
		changed = true;
	}
	if (changed && beforeStreaming === void 0 && streaming.mode === void 0 && params.aliasOnlyMode !== void 0) {
		streaming.mode = params.aliasOnlyMode;
		params.changes.push(`Set ${params.pathPrefix}.streaming.mode (${params.aliasOnlyMode}) to keep the previous default while migrating flat streaming keys.`);
		changed = true;
	}
	if (Object.keys(preview).length > 0) streaming.preview = preview;
	if (Object.keys(block).length > 0) streaming.block = block;
	updated.streaming = streaming;
	return {
		entry: updated,
		changed
	};
}
/**
* Root flat delivery aliases resolved per-key for every account (nested-first,
* flat-fallback), even when the account carried its own `streaming` value that
* replaces the root object wholesale at merge time. Capture them before root
* migration so replace-semantics channels can seed existing account streaming
* objects with the delivery settings those accounts previously inherited.
*/
function buildRootFlatDeliverySeed(entry, includePreviewChunk) {
	const seed = {};
	if (entry.chunkMode !== void 0) seed.chunkMode = entry.chunkMode;
	const block = {};
	if (entry.blockStreaming !== void 0) block.enabled = entry.blockStreaming;
	if (entry.blockStreamingCoalesce !== void 0) block.coalesce = entry.blockStreamingCoalesce;
	if (Object.keys(block).length > 0) seed.block = block;
	if (includePreviewChunk === true && entry.draftChunk !== void 0) seed.preview = { chunk: entry.draftChunk };
	return Object.keys(seed).length > 0 ? seed : null;
}
/**
* Rebuilds a materialized account streaming object with the per-slot
* precedence the runtime resolvers applied pre-migration. The slots disagree:
* - mode, block.enabled, preview.chunk resolve on the MERGED entry
*   (src/channels/streaming.ts nested-first), so the root nested object
*   outranked account flat aliases and preview.chunk picks atomically.
* - chunkMode resolves the raw account entry before the root entry
*   (resolveChunkModeForProvider in src/auto-reply/chunk.ts), so an account
*   flat chunkMode outranked every root spelling.
* - block.coalesce merges the account pick over the root pick per field
*   (resolveProviderBlockStreamingCoalesce in
*   src/auto-reply/reply/block-streaming.ts).
* One generic deep-fill cannot express that ladder, so seed slot by slot.
* Copying root values freezes inheritance at fix time by design (the change
* message records it); merged-entry channels (mattermost-style resolved
* accounts) would otherwise lose the root values entirely once the account
* owns a streaming object.
*/
function seedMaterializedAccountStreaming(params) {
	const { created } = params;
	const rootNested = params.rootNestedBefore ?? {};
	const rootFlat = params.rootFlat ?? {};
	let seeded = fillMissingRecordFields(structuredClone(rootNested), created).value;
	seeded = fillMissingRecordFields(seeded, rootFlat).value;
	seeded = fillMissingRecordFields(seeded, params.rootAfter).value;
	if (created.chunkMode !== void 0) seeded = {
		...seeded,
		chunkMode: created.chunkMode
	};
	const createdCoalesce = asNullableRecord(asNullableRecord(created.block)?.coalesce);
	if (createdCoalesce) {
		const rootCoalesce = asNullableRecord(asNullableRecord(rootNested.block)?.coalesce) ?? asNullableRecord(asNullableRecord(rootFlat.block)?.coalesce);
		seeded = {
			...seeded,
			block: {
				...asNullableRecord(seeded.block),
				coalesce: {
					...structuredClone(rootCoalesce ?? {}),
					...structuredClone(createdCoalesce)
				}
			}
		};
	}
	const rootNestedPreviewChunk = asNullableRecord(rootNested.preview)?.chunk;
	if (rootNestedPreviewChunk !== void 0 && asNullableRecord(created.preview)?.chunk !== void 0) seeded = {
		...seeded,
		preview: {
			...asNullableRecord(seeded.preview),
			chunk: structuredClone(rootNestedPreviewChunk)
		}
	};
	return seeded;
}
/** Deep-fills record fields missing from target with copies of source values. */
function fillMissingRecordFields(target, source) {
	let filled = false;
	const value = { ...target };
	for (const [key, sourceValue] of Object.entries(source)) {
		if (sourceValue === void 0) continue;
		const existing = value[key];
		if (existing === void 0) {
			value[key] = structuredClone(sourceValue);
			filled = true;
			continue;
		}
		const existingRecord = asNullableRecord(existing);
		const sourceRecord = asNullableRecord(sourceValue);
		if (!existingRecord || !sourceRecord) continue;
		const merged = fillMissingRecordFields(existingRecord, sourceRecord);
		if (merged.filled) {
			value[key] = merged.value;
			filled = true;
		}
	}
	return {
		value,
		filled
	};
}
/**
* Runs generic channel doctor alias migration for the root entry and accounts.
*
* Channel plugins provide streaming resolution and optional account-specific
* migrations so core can keep one compatibility path for all channel shapes.
*/
function normalizeLegacyChannelAliases(params) {
	let updated = params.entry;
	let changed = false;
	const rootFlatDeliverySeed = params.seedAccountStreamingFromRoot === true ? buildRootFlatDeliverySeed(params.entry, params.resolveStreamingOptions(params.entry).includePreviewChunk) : null;
	const rootNestedStreamingBefore = params.seedAccountStreamingFromRoot === true ? asNullableRecord(params.entry.streaming) : null;
	if (params.normalizeDm === true) {
		const dm = normalizeLegacyDmAliases({
			entry: updated,
			pathPrefix: params.pathPrefix,
			changes: params.changes,
			promoteAllowFrom: params.rootDmPromoteAllowFrom
		});
		updated = dm.entry;
		changed = dm.changed;
	}
	const streaming = normalizeLegacyStreamingAliases({
		entry: updated,
		pathPrefix: params.pathPrefix,
		changes: params.changes,
		...params.resolveStreamingOptions(updated)
	});
	updated = streaming.entry;
	changed = changed || streaming.changed;
	const rawAccounts = asNullableRecord(updated.accounts);
	if (!rawAccounts) return {
		entry: updated,
		changed
	};
	const rootStreaming = asNullableRecord(updated.streaming);
	let accountsChanged = false;
	const accounts = { ...rawAccounts };
	for (const [accountId, rawAccount] of Object.entries(rawAccounts)) {
		const account = asNullableRecord(rawAccount);
		if (!account) continue;
		let accountEntry = account;
		let accountChanged = false;
		const accountPathPrefix = `${params.pathPrefix}.accounts.${accountId}`;
		if (params.normalizeAccountDm === true) {
			const accountDm = normalizeLegacyDmAliases({
				entry: accountEntry,
				pathPrefix: accountPathPrefix,
				changes: params.changes
			});
			accountEntry = accountDm.entry;
			accountChanged = accountDm.changed;
		}
		const accountStreamingOptions = { ...params.resolveStreamingOptions(accountEntry) };
		if (rootStreaming) delete accountStreamingOptions.aliasOnlyMode;
		const beforeAccountStreaming = accountEntry.streaming;
		const accountStreaming = normalizeLegacyStreamingAliases({
			entry: accountEntry,
			pathPrefix: accountPathPrefix,
			changes: params.changes,
			...accountStreamingOptions
		});
		accountEntry = accountStreaming.entry;
		accountChanged = accountChanged || accountStreaming.changed;
		if (params.seedAccountStreamingFromRoot === true && accountStreaming.changed && beforeAccountStreaming === void 0 && rootStreaming) {
			const created = asNullableRecord(accountEntry.streaming);
			if (created) {
				const seeded = seedMaterializedAccountStreaming({
					created,
					rootNestedBefore: rootNestedStreamingBefore,
					rootFlat: rootFlatDeliverySeed,
					rootAfter: rootStreaming
				});
				if (JSON.stringify(seeded) !== JSON.stringify(created)) {
					accountEntry = {
						...accountEntry,
						streaming: seeded
					};
					params.changes.push(`Copied ${params.pathPrefix}.streaming into ${accountPathPrefix}.streaming to keep inherited settings while migrating flat streaming keys.`);
				}
			}
		} else if (rootFlatDeliverySeed && beforeAccountStreaming !== void 0) {
			const accountStreamingObject = asNullableRecord(accountEntry.streaming);
			if (accountStreamingObject) {
				let seededAccount = accountStreamingObject;
				if (rootFlatDeliverySeed.chunkMode !== void 0 && seededAccount.chunkMode === void 0) seededAccount = {
					...seededAccount,
					chunkMode: rootFlatDeliverySeed.chunkMode
				};
				const rootFlatBlock = asNullableRecord(rootFlatDeliverySeed.block);
				const rootFlatBlockEnabled = rootFlatBlock?.enabled;
				if (rootFlatBlockEnabled !== void 0 && asNullableRecord(seededAccount.block)?.enabled === void 0) seededAccount = {
					...seededAccount,
					block: {
						...asNullableRecord(seededAccount.block),
						enabled: rootFlatBlockEnabled
					}
				};
				const rootFlatCoalesce = asNullableRecord(rootFlatBlock?.coalesce);
				if (rootFlatCoalesce) {
					const accountCoalesce = asNullableRecord(asNullableRecord(seededAccount.block)?.coalesce);
					const mergedCoalesce = {
						...structuredClone(rootFlatCoalesce),
						...structuredClone(accountCoalesce ?? {})
					};
					if (JSON.stringify(mergedCoalesce) !== JSON.stringify(accountCoalesce ?? {})) seededAccount = {
						...seededAccount,
						block: {
							...asNullableRecord(seededAccount.block),
							coalesce: mergedCoalesce
						}
					};
				}
				const rootFlatPreviewChunk = asNullableRecord(rootFlatDeliverySeed.preview)?.chunk;
				if (rootFlatPreviewChunk !== void 0 && asNullableRecord(seededAccount.preview)?.chunk === void 0) seededAccount = {
					...seededAccount,
					preview: {
						...asNullableRecord(seededAccount.preview),
						chunk: structuredClone(rootFlatPreviewChunk)
					}
				};
				if (seededAccount !== accountStreamingObject) {
					accountEntry = {
						...accountEntry,
						streaming: seededAccount
					};
					accountChanged = true;
					params.changes.push(`Copied flat ${params.pathPrefix} delivery keys into ${accountPathPrefix}.streaming to keep inherited settings while migrating flat streaming keys.`);
				}
			}
		}
		const accountExtra = params.normalizeAccountExtra?.({
			account: accountEntry,
			accountId,
			pathPrefix: accountPathPrefix,
			changes: params.changes
		});
		if (accountExtra) {
			accountEntry = accountExtra.entry;
			accountChanged = accountChanged || accountExtra.changed;
		}
		if (accountChanged) {
			accounts[accountId] = accountEntry;
			accountsChanged = true;
		}
	}
	if (accountsChanged) {
		updated = {
			...updated,
			accounts
		};
		changed = true;
	}
	return {
		entry: updated,
		changed
	};
}
/** Detects legacy streaming aliases on one channel or account config entry. */
function hasLegacyStreamingAliases(value, options) {
	const entry = asNullableRecord(value);
	if (!entry) return false;
	return entry.streamMode !== void 0 || typeof entry.streaming === "boolean" || typeof entry.streaming === "string" || entry.chunkMode !== void 0 || entry.blockStreaming !== void 0 || entry.blockStreamingCoalesce !== void 0 || options?.includePreviewChunk === true && entry.draftChunk !== void 0 || options?.includeNativeTransport === true && entry.nativeStreaming !== void 0;
}
//#endregion
//#region src/config/channel-doctor-helpers.ts
/** Applies one channel-specific doctor migration to every object-shaped account. */
function normalizeChannelAccounts(params) {
	const rawAccounts = asNullableRecord(params.entry.accounts);
	if (!rawAccounts) return {
		entry: params.entry,
		changed: false
	};
	let changed = false;
	const accounts = { ...rawAccounts };
	for (const [accountId, value] of Object.entries(rawAccounts)) {
		const account = asNullableRecord(value);
		if (!account) continue;
		const normalized = params.normalizeAccount({
			account,
			accountId,
			pathPrefix: `${params.pathPrefix}.accounts.${accountId}`,
			changes: params.changes
		});
		if (normalized.changed) {
			accounts[accountId] = normalized.entry;
			changed = true;
		}
	}
	return changed ? {
		entry: {
			...params.entry,
			accounts
		},
		changed: true
	} : {
		entry: params.entry,
		changed: false
	};
}
/** Applies the same channel-specific doctor migration at root and account scope. */
function normalizeChannelConfigEntries(params) {
	const changes = params.changes ?? [];
	const channels = params.cfg.channels;
	const entry = asNullableRecord(channels?.[params.channelId]);
	if (!entry) return {
		config: params.cfg,
		changes
	};
	const channelPath = `channels.${params.channelId}`;
	const root = params.normalizeEntry({
		entry,
		pathPrefix: channelPath,
		changes
	});
	const accounts = normalizeChannelAccounts({
		entry: root.entry,
		pathPrefix: channelPath,
		changes,
		normalizeAccount: (accountParams) => params.normalizeEntry({
			entry: accountParams.account,
			accountId: accountParams.accountId,
			pathPrefix: accountParams.pathPrefix,
			changes: accountParams.changes
		})
	});
	if (!root.changed && !accounts.changed) return {
		config: params.cfg,
		changes
	};
	return {
		config: {
			...params.cfg,
			channels: {
				...channels,
				[params.channelId]: accounts.entry
			}
		},
		changes
	};
}
function stripRetiredKeys(params) {
	if (params.recursive && Array.isArray(params.value)) {
		let changed = false;
		const value = params.value.map((item, index) => {
			const stripped = stripRetiredKeys({
				...params,
				value: item,
				pathPrefix: `${params.pathPrefix}[${index}]`
			});
			changed = changed || stripped.changed;
			return stripped.value;
		});
		return {
			value: changed ? value : params.value,
			changed
		};
	}
	const record = asNullableRecord(params.value);
	if (!record) return {
		value: params.value,
		changed: false
	};
	let changed = false;
	const value = {};
	for (const [key, child] of Object.entries(record)) {
		if (params.keys.has(key)) {
			params.onRemove?.({
				key,
				pathPrefix: params.pathPrefix
			});
			changed = true;
			continue;
		}
		if (!params.recursive) {
			value[key] = child;
			continue;
		}
		const stripped = stripRetiredKeys({
			...params,
			value: child,
			pathPrefix: `${params.pathPrefix}.${key}`
		});
		changed = changed || stripped.changed;
		value[key] = stripped.value;
	}
	return {
		value: changed ? value : params.value,
		changed
	};
}
/** Removes retired keys recursively or from a channel root and its accounts. */
function stripRetiredChannelKeys(params) {
	const channels = params.cfg.channels;
	const entry = asNullableRecord(channels?.[params.channelId]);
	if (!entry) return {
		config: params.cfg,
		changed: false
	};
	const channelPath = `channels.${params.channelId}`;
	if (params.scope === "recursive") {
		const stripped = stripRetiredKeys({
			value: entry,
			keys: params.keys,
			pathPrefix: channelPath,
			recursive: true,
			onRemove: params.onRemove
		});
		return stripped.changed ? {
			config: {
				...params.cfg,
				channels: {
					...channels,
					[params.channelId]: stripped.value
				}
			},
			changed: true
		} : {
			config: params.cfg,
			changed: false
		};
	}
	const normalized = normalizeChannelConfigEntries({
		cfg: params.cfg,
		channelId: params.channelId,
		normalizeEntry: (entryParams) => {
			const stripped = stripRetiredKeys({
				value: entryParams.entry,
				keys: params.keys,
				pathPrefix: entryParams.pathPrefix,
				recursive: false,
				onRemove: params.onRemove
			});
			return {
				entry: stripped.value,
				changed: stripped.changed
			};
		}
	});
	return {
		config: normalized.config,
		changed: normalized.config !== params.cfg
	};
}
/** Materializes root/default-account inheritance after aliases create streaming. */
function materializeInheritedAccountStreaming(params) {
	const channels = params.cfg.channels;
	const entry = asNullableRecord(channels?.[params.channelId]);
	const accounts = asNullableRecord(entry?.accounts);
	if (!entry || !accounts) return params.cfg;
	const rootStreaming = asNullableRecord(entry.streaming);
	const defaultKey = Object.hasOwn(accounts, "default") ? "default" : Object.keys(accounts).find((key) => key.trim().toLowerCase() === "default");
	let changed = false;
	const nextAccounts = { ...accounts };
	const accountIds = Object.keys(accounts).toSorted((left, right) => left === defaultKey ? -1 : right === defaultKey ? 1 : left.localeCompare(right));
	for (const accountId of accountIds) {
		if (asNullableRecord(params.accountsBefore?.[accountId])?.streaming !== void 0) continue;
		const account = asNullableRecord(nextAccounts[accountId]);
		const created = asNullableRecord(account?.streaming);
		if (!account || !created) continue;
		const defaultStreaming = defaultKey ? asNullableRecord(asNullableRecord(nextAccounts[defaultKey])?.streaming) : null;
		const inherited = accountId === defaultKey ? rootStreaming : defaultStreaming ?? rootStreaming;
		if (!inherited) continue;
		const materialized = asNullableRecord(mergeDeep(inherited, created));
		if (!materialized || isDeepStrictEqual(materialized, created)) continue;
		nextAccounts[accountId] = {
			...account,
			streaming: materialized
		};
		changed = true;
		const sourcePath = accountId !== defaultKey && defaultKey && defaultStreaming ? `channels.${params.channelId}.accounts.${defaultKey}.streaming` : `channels.${params.channelId}.streaming`;
		params.changes.push(`Copied ${sourcePath} into channels.${params.channelId}.accounts.${accountId}.streaming to keep inherited settings while migrating flat streaming keys.`);
	}
	return changed ? {
		...params.cfg,
		channels: {
			...channels,
			[params.channelId]: {
				...entry,
				accounts: nextAccounts
			}
		}
	} : params.cfg;
}
//#endregion
export { hasLegacyAccountStreamingAliases as a, resolveLegacyAliasStreamingMode as c, stripRetiredChannelKeys as i, uniqueStrings as l, normalizeChannelAccounts as n, hasLegacyStreamingAliases as o, normalizeChannelConfigEntries as r, normalizeLegacyChannelAliases as s, materializeInheritedAccountStreaming as t };
