/**
 * service-reloader.ts — 选择性 Service 实例重载（Phase 2B）
 *
 * Soft Reload 时只重新实例化 invalidation set 中包含的 service，
 * 其他 service 实例保持不变。正确处理嵌套目录结构。
 *
 * 核心流程：
 *
 *   1. 扫描 outDir/services/ 下所有 .js 文件
 *   2. 筛选出在 invalidation set 中的文件（需要重载的）
 *   3. 保存受影响 service 的旧引用（用于回退）
 *   4. 逐个调用旧实例的 dispose()（如果存在）
 *   5. require() 新编译产物，实例化并挂载到 app.services
 *   6. 如果任何步骤失败，回滚受影响的 service 到旧实例
 *
 * 安全保证：
 *
 *   | 场景                       | 行为                                           |
 *   |----------------------------|------------------------------------------------|
 *   | service 文件不在失效集合中  | 完全不触碰，实例保持不变                        |
 *   | dispose() 抛出异常          | 打印警告，继续重载（不中断流程）                |
 *   | require() 或 new 失败       | 回滚所有受影响 service 到旧实例，向上抛出错误    |
 *   | 嵌套目录（payment/stripe）  | 正确映射为 app.services.payment.stripe           |
 *
 * @module lib/dev/service-reloader
 * @see 11b-soft-reload.md §4（服务实例重载）
 * @see 11e-edge-cases.md §1（Reload 失败回退）
 * @see 11e-edge-cases.md §6（Service 副作用安全）
 * @see IMPLEMENTATION-PLAN.md 任务 2.2b
 */
/**
 * 最小化的 VextApp 接口（仅包含 service-reloader 需要的字段）
 *
 * 使用局部接口避免对完整 VextApp 类型的直接依赖，
 * 便于单元测试中构造 mock 对象。
 */
export interface ServiceReloaderApp {
    services: Record<string, unknown>;
    logger: {
        info(...args: unknown[]): void;
        warn(...args: unknown[]): void;
        debug(...args: unknown[]): void;
        error(...args: unknown[]): void;
    };
}
/**
 * Service 重载结果
 */
export interface ServiceReloadResult {
    /** 受影响（重载）的 service 数量 */
    reloaded: number;
    /** 未受影响（保持不变）的 service 数量 */
    unchanged: number;
    /** 重载的 service key 列表（点分格式，如 "payment.stripe"） */
    reloadedKeys: string[];
}
/**
 * filePathToServiceKeys — 将 service 文件路径转为嵌套 key 数组
 *
 * 复用 service-loader 的路径转换逻辑（详见 02-services.md）：
 *   - 取相对路径（相对于 servicesDir）
 *   - 去掉扩展名
 *   - 按 / 拆分为段
 *   - 每段做 kebab-case → camelCase 转换
 *
 * 示例：
 *   "user.js"             → ["user"]
 *   "user-profile.js"     → ["userProfile"]
 *   "payment/stripe.js"   → ["payment", "stripe"]
 *   "payment/ali-pay.js"  → ["payment", "aliPay"]
 *
 * v2.2 修复：v2.1 使用 path.basename() 只取文件名，
 * 无法处理嵌套目录结构，导致 payment/stripe.js 被错误地
 * 映射为 app.services["stripe"] 而非 app.services.payment.stripe。
 *
 * @param relativePath 相对于 servicesDir 的文件路径（如 "payment/stripe.js"）
 * @returns 嵌套 key 数组（如 ["payment", "stripe"]）
 */
export declare function filePathToServiceKeys(relativePath: string): string[];
/**
 * getNestedValue — 从嵌套对象中读取值
 *
 * getNestedValue(obj, ["payment", "stripe"]) → obj.payment.stripe
 *
 * @param obj  根对象
 * @param keys 嵌套 key 数组
 * @returns 目标值，路径不存在时返回 undefined
 */
export declare function getNestedValue(obj: Record<string, unknown>, keys: string[]): unknown;
/**
 * setNestedValue — 向嵌套对象中设置值
 *
 * setNestedValue(obj, ["payment", "stripe"], value)
 *   → obj.payment.stripe = value
 *
 * 自动创建中间层级对象（如果不存在）。
 *
 * @param obj   根对象
 * @param keys  嵌套 key 数组
 * @param value 要设置的值
 */
export declare function setNestedValue(obj: Record<string, unknown>, keys: string[], value: unknown): void;
/**
 * scanServiceDirectory — 递归扫描 services/ 目录下的所有 .js 文件
 *
 * 只扫描编译产物（.js），忽略 .map / .d.ts 等辅助文件。
 * 跳过以 _ 或 . 开头的文件/目录。
 *
 * @param dir 当前扫描的目录路径
 * @returns 所有 service .js 文件的绝对路径数组
 */
export declare function scanServiceDirectory(dir: string): Promise<string[]>;
/**
 * reloadServices — 选择性重载 service（v2.2）
 *
 * 只重新实例化变更的 service，其他 service 实例保持不变。
 * 正确处理嵌套目录结构（如 payment/stripe → app.services.payment.stripe）。
 *
 * 流程：
 *   1. 扫描 outDir/services/ 下所有 .js 文件
 *   2. 筛选出在 invalidation set 中的文件
 *   3. 保存受影响 service 的旧引用
 *   4. 逐个 dispose + 重新 require + 实例化
 *   5. 失败时回滚所有受影响 service
 *
 * @param app VextApp 实例（需要 app.services 和 app.logger）
 * @param outDir 编译产物目录（.vext/dev/ 的绝对路径）
 * @param invalidated require.cache 失效集合（绝对路径集合）
 * @returns 重载结果（重载数 / 未变更数 / 重载的 key 列表）
 * @throws 重载失败时抛出错误（已回滚受影响 service）
 */
export declare function reloadServices(app: ServiceReloaderApp, outDir: string, invalidated: Set<string>): Promise<ServiceReloadResult>;
