import type { HostContext } from './types.js'; /** 最近一次卸载逐出的结果,作为运行态证据。 */ export interface UnloadEvictionRecord { loaderFound: boolean; cleared: number; at: string; } /** 最近一次卸载逐出结果(首次卸载前为 undefined)。 */ export declare function unloadEvictionRecord(): UnloadEvictionRecord | undefined; /** * Fiber dispose 时逐出本包模块缓存条目。 * * 注意:只靠这一路**不足以**让「行重建即换血」成立。dispose 发生时新 Fiber 的 * import 往往已经在排队,逐出赶不上同一轮重建(2026-10-17 实测:cleared=29 * 但本次挂载仍跑旧模块,要再往返一次才生效)。真正有效的是 * {@link installPreMountCacheEviction},本函数保留为兜底(例如进程退出路径)。 */ export declare function installUnloadCacheEviction(ctx: HostContext): void; /** * **挂载前**逐出本包模块缓存——让行重建(set_plugin 往返、patch reload)真正换血。 * * 为什么必须是「挂载前」:Loader 的时序是 * 1. `new Entry(...)` → 构造函数里同步 `emit('loader/entry-init', entry)` * 2. 随后 `entry.init()` → `await this.parent.tree.import(name, ...)` * 所以监听 `loader/entry-init` 并**同步**完成逐出,就能保证本包的 import 发生 * 在缓存清空之后,从而从磁盘求值当前构建。 * * 为什么不能只靠 dispose 时逐出:dispose 与新 Fiber 的 import 之间没有顺序保证, * 实测逐出「成功」但本轮仍是旧模块。 * * 只对本包的行触发(按行 name 精确匹配),不惊动其它插件。 */ export declare function installPreMountCacheEviction(ctx: HostContext): void; /** 最近一次挂载前逐出的结果(诊断用)。 */ export interface PreMountEvictionRecord { loaderFound: boolean; cleared: number; keys?: number; matched?: number; at: string; } /** 最近一次挂载前逐出结果 + 累计次数(首次发生前为 undefined)。 */ export declare function preMountEvictionRecord(): (PreMountEvictionRecord & { count: number; }) | undefined; /** * 一次自建换血(复刻 `vendor/hmr/src/index.ts` 的 `partialReload`): * * 1. 清本包模块缓存(ESM loadCache + CJS require.cache),保留备份; * 2. `loader.import(入口文件 URL)` **重新求值**本包模块; * 3. 用新模块的 exports 替换旧 plugin 注册(`registry.delete` + `registry.plugin`), * 并把新 fiber 挂回原 entry。 * * 为什么必须重新 import:`set_plugin` 的 disable→enable 只走 * `Entry.update()` → `_patchContext()`,复用旧 `moduleNamespace`, * **从不重新 import**。清缓存而不重新 import 等于白清。 * * 任何一步失败都回滚缓存并保留旧 fiber 继续服务。 */ export interface ReloadAttempt { /** 触发时间(ISO-8601)。 */ at: string; /** 结果。 */ outcome: 'ok' | 'failed' | 'unavailable'; /** 逐出的 ESM 条目数。 */ cleared?: number; /** 逐出的 CJS 条目数。 */ cjsCleared?: number; /** 失败/不可用原因(已截断)。 */ error?: string; /** 换血经历的总次数。 */ count?: number; } /** 最近一次换血尝试(诊断用)。 */ export declare function reloadRecord(): (ReloadAttempt) | undefined; /** * 执行一次真正的换血。私有面缺失时返回 unavailable,绝不抛错。 * @param ctx 插件上下文。 */ export declare function reloadSelf(ctx: HostContext): Promise; /** 换血过程中「停旧 watcher」这一步的观察(诊断用)。 */ export interface ReloadSequence { hadStopper: boolean; stopped: boolean; at: string; } /** 读取换血序列观察;未发生过为 undefined。 */ export declare function reloadSequence(): ReloadSequence | undefined; /** * 自监视热重载,适配挂载中的 HMR 代际;两代都不可用时静默返回 * (dispose 逐出仍由 installUnloadCacheEviction 独立生效)。 */ /** * 自监视热重载,适配挂载中的 HMR 代际;两代都不可用时静默返回 * (dispose 逐出仍由 installUnloadCacheEviction 独立生效)。 * * **冷启动时序**:本插件常比 `hmr` 服务先就绪(base bundle 的 hmr 行 `root: []` * 且 disabled 依赖 `profileContext`)。此时 `ctx.get('hmr')` 为 undefined, * 直接返回会让 watcher 永久缺席——2026-10-18 重启后实测 `hotReloadWatch: null` * 就是这个原因(手动 set_plugin 往返一次才装上)。因此这里在服务缺失时挂 * `internal/service` 重试,与 session-delete / web-search 同款模式。 */ export declare function installSelfHotReload(ctx: HostContext): void; /** 刷新换血流使用的 ctx(换血 / 重新挂载后由 index.ts 调用)。 */ export declare function bindReloadContext(ctx: HostContext): void; /** 当前 watcher 安装状态(诊断用:看清到底装上几个、为什么失败)。 */ export interface WatchState { installed: number; failed: number; lastError?: string; /** 是否登记了 stopper(换血能否干净重装的关键)。 */ stopperRegistered?: boolean; /** 安装世代:每次真正安装 watcher 自增,用于判断换血后是否重装过。 */ generation?: number; /** watcher 回调被触发的累计次数(区分「watcher 死了」与「被 stamp 跳过」)。 */ fires?: number; /** 其中因内容未变而被跳过的次数。 */ skipped?: number; /** schedule() 被计划执行的次数(debounce 之后真正进入 run 的次数)。 */ runs?: number; /** reloadSelf 的调用次数与最近一次返回的 outcome(定位 early return)。 */ reloadCalls?: number; lastReloadOutcome?: string; } /** 读取 watcher 安装状态;从未安装为 undefined。 */ export declare function watchState(): WatchState | undefined; /** * 关闭进程级 watcher(只应在「本包整体卸载」或「换血前」调用)。 * @returns 是否有 watcher 被关闭。 */ export declare function stopHotReloadWatch(): Promise; //# sourceMappingURL=hot-reload.d.ts.map