Dev KnowledgeV2.0
输入关键词开始搜索

    本地开发、进程与构建速记

    按网络、workspace、进程环境与退出状态整理本地工程的常见易错点。

    类型
    速记知识
    适合读者
    开发或调试本地服务、Node.js 进程和构建脚本的开发者
    最近核验

    网络与本地服务

    localhost、127.0.0.1 与 ::1

    三者都用于访问本机,但不代表完全相同的监听和浏览器安全边界。

    写法 类型 关键区别
    localhost 特殊用途主机名 需要由应用或系统解析到回环地址,可能涉及 IPv4 和 IPv6
    127.0.0.1 IPv4 地址字面量 明确绑定 IPv4 回环接口,不经过主机名解析
    ::1 IPv6 地址字面量 明确绑定 IPv6 回环接口
    • 适合场景:希望本地服务监听行为不受主机名解析顺序影响时,明确选择 127.0.0.1 或 ::1;希望 URL 易读且工具链已正确处理 IPv4/IPv6 时,可以使用 localhost。
    • 记忆点:服务绑定到 127.0.0.1 后,如果客户端把 localhost 解析为 ::1,客户端和服务端选择的地址族不同就可能连接失败。反过来也一样。
    • 浏览器边界:http://localhost:3000 与 http://127.0.0.1:3000 的 host 不同,所以是不同 Origin;CORS、Web Storage 等按 Origin 隔离的行为不能因为它们都指向本机就混用。
    • 来源:RFC 6761 对 localhost 的特殊用途规定、IANA IPv4 特殊地址注册表中的 127.0.0.0/8、WHATWG HTML Origin 定义。

    二维码链接不会自动发布网站

    二维码生成器把提供的文本或 URL 编码成图像;网站托管、网络可达性与访问权限仍由目标地址决定。生成成功只能证明编码完成,不能证明手机能够打开页面。node-qrcode 使用说明

    • 本机地址边界:手机扫描含 localhost、127.0.0.1 或 ::1 的链接时,回环地址指向手机自身,不会指向开发电脑。这是回环语义在另一设备上的直接推论;依据见 RFC 6761 §6.3。
    • 交付顺序:先部署到目标设备可访问的地址,再将实际 URL 生成二维码,最后使用接收者的设备、网络与权限实扫;私有网络或登录要求不会因转成二维码而消失。
    • 验证日期:2026-10-24 对照生成器官方接口和回环地址规范核验;不把二维码文件生成当成跨设备访问实测。

    固定端口与端口 0

    Node.js 服务监听端口 0 时,由操作系统分配当前未使用的端口;指定正整数端口则使用固定端口。

    • 适合场景:测试、并行任务和一次性本地工具适合端口 0,可以减少冲突;书签、回调地址或固定代理配置需要稳定 URL 时应指定固定端口。
    • 记忆点:固定端口被其他进程占用时,服务通常以 EADDRINUSE 启动失败;端口 0 只保证本次绑定时由系统选择可用端口,不保证下次仍相同。
    • 传给下游的地址:TCP 服务监听成功后,通过 server.address() 读取实际端口,再结合可供客户端访问的主机名构造 URL。不能把配置中的 0 或另一份固定默认端口原样传给浏览器、Worker 等调用方。2026-10-23 用随机端口 HTTP 服务验证了实际端口传递与目标 URL 可达性。
    • 来源:Node.js server.listen() 文档。

    curl:连接超时、登录跳转与API鉴权失败

    先判断失败在哪一层,再决定检查网络、登录还是API Key。明确的DNS解析失败或TCP连接超时,还不能用于判断业务凭据是否有效;返回登录跳转说明请求遇到了登录入口,也不代表业务接口已校验API Key。

    • 分阶段限时:curl -v --connect-timeout 5 --max-time 15 https://api.example.com/health。前者限制连接阶段,包含DNS及请求的TCP、TLS或QUIC握手;后者限制整次操作。
    • 错误28不只指连接失败:它表示某个超时条件到期。结合错误消息与详细输出区分连接超时、已连接但迟迟没有响应,以及传输阶段超时;不能看到28就断言是防火墙或Token问题。
    • 独立认证边界:浏览器登录态与业务API Key不能默认互相替代。确认实际运行环境允许的调用入口与认证方式,再检查Key及绑定身份;不要自动携带登录Cookie到另一个域名。
    • 响应分层:HTTP成功状态仍需按接口合同校验业务结果,不能把2xx直接显示成模型或业务执行成功。
    • 验证与来源:2026-10-26对照本机curl手册核验两个超时选项和退出码28。参见curl连接超时、总耗时限制、HTTP重定向。

    文件系统与访问边界

    Vite 文件访问限制与 Git 忽略规则是两层边界

    .gitignore 不提供 HTTP 访问控制;把运行数据放在被 Git 忽略的目录里,不能据此认定开发服务器不会提供它。Vite 的 server.fs.strict 主要限制工作区之外的访问,工作区内部的敏感文件需另行限制。

    • 关键配置:在 server.fs.deny 中补充私有运行数据等明确的拒绝模式,并保留当前版本的默认拒绝项。拒绝列表优先于 server.fs.allow。
    • 边界:public 目录不受该拒绝列表过滤,不要在其中存放私有数据。插件自定义读取或其他服务路由仍须单独检查;开发服务器限制也不替代业务 API 的身份与成员权限校验。
    • 验证与来源:2026-11-02 在 Vite 8.3.5 中补充拒绝规则后,直接请求私有运行数据与服务端模块均返回 403;仅核验了这些直接入口。参见 Vite 文件系统限制。

    文件URL转成本机路径要用 fileURLToPath

    new URL(...).pathname 仍属于URL表示,会保留空格、中文等字符的百分号编码,也不能直接处理所有平台的盘符或共享路径语义。需要给只接受路径字符串的库传参时,使用 fileURLToPath(url),不要把 .pathname 当成文件系统路径。

    import { fileURLToPath } from 'node:url';
    
    const target = new URL('./report draft.txt', import.meta.url);
    const filename = fileURLToPath(target);
    • 记忆点:部分Node.js文件系统接口直接接受 file: URL对象,可省去字符串转换;第三方库是否支持URL对象须查其接口。不要用手工字符串替换或简单解码假装完成跨平台转换。
    • 边界:路径转换不提供访问授权或目录隔离。对不可信目标仍需按实际访问边界校验。
    • 验证与来源:2026-10-24 用含空格与中文的文件URL核验 .pathname 保留编码,fileURLToPath 返回解码后的本机路径;对照 Node.js URL官方文档。

    resolve 的目录检查拦不住符号链接越界

    path.resolve 与 path.relative 处理路径字符串,不解析符号链接。一个表面位于允许目录内的文件名,可能指向目录外的文件,随后 stat、readFile 会访问链接目标。

    • 处理方式:读取现有文件时,对允许根目录与目标解析 realpath,再按路径分段检查包含关系;或者在相应操作中拒绝符号链接。只禁止 .. 或绝对路径不足以证明真实访问受限。Node.js realpath、stat 与 lstat 的区别
    • 安全边界:检查后再读取仍可能受到并发换链的竞态影响;面对不可信可变目录,需要额外隔离和操作系统层面的权限边界,不能把一次 realpath 检查称为完整沙箱。
    • 验证:2026-10-19 在临时目录创建一个指向外部合成文本的符号链接,复现只做词法目录检查的工具读取到外部标记;未访问真实敏感文件。

    ZIP内容完整不代表中文文件名能正确解码

    ZIP校验通过只能说明条目数据未损坏。文件名使用UTF-8字节却缺少相应编码标记时,不同解压器可能用另一字符集解释,出现“文件内容能解开、中文名称乱码”。

    • 处理方式:选用明确处理Unicode文件名的归档接口;验收同时检查CRC、实际文件名和Unicode标记,不只检查压缩命令退出码。JSZip默认使用UTF-8编码文件名,其生成器会为非ASCII名称设置语言编码位,并生成Unicode路径扩展字段。
    • 验证:2026-10-24 对同一批含中文名称的合成材料分别归档,再用独立ZIP读取器检查名称与标记,确认编码问题与内容损坏是不同边界。归档接口的支持不保证所有旧解压器都兼容。
    • 来源:JSZip归档选项、生成器的UTF-8标记实现。

    命令行参数

    参数名合法不等于参数值被 CLI 支持

    允许传入 --type 或 --format,只说明参数名称合法;具体取值仍可能受当前 CLI 的枚举约束。界面选项、默认值、兜底值和命令示例都要与实际执行版本的允许值一致,不能根据显示名称猜测参数值。

    • 测试边界:只记录调用参数的 mock、或只验证参数名的白名单,不能证明 CLI 会接受该值。接口测试应保留映射检查,再补充实际命令契约核验;CLI 升级后也需要重新核对。
    • 本地核验:支持枚举诊断的 CLI 可以用无效枚举值触发参数校验,读取允许值列表;需先确认该校验在网络或业务执行之前发生。成功数据请求的实际运行结果可以进一步验证正确值,但不应把这类联网验证混入离线单测。
    • 验证:2026-10-11 对当前本地 CLI 实测,一个界面默认值被参数解析器拒绝,使用允许的默认值后成功返回结构化结果;新增本地契约测试直接核对实际 CLI 的枚举列表,避免测试桩重复接受错误默认值。

    桌面应用脚本化

    Blender:通过 Python API 与后台模式自动化

    Blender 提供 bpy Python API,可创建和修改场景对象;非交互任务可以通过 --background 配合 --python 执行脚本,不必先接入 MCP 或模拟鼠标操作。

    • 执行环境:脚本应在 Blender 提供的 Python 环境中运行;不能默认普通系统 Python 已有 bpy。后台调用启动的是独立 Blender 进程,不会自动操作另一个窗口中尚未保存的场景。
    • 失败检测:命令行的 --python-exit-code 1 可让脚本抛出 Python 异常时返回指定非零退出码,便于上层自动化识别失败;它只适用于从命令行执行的脚本。
    • 结果验收:退出成功不等于模型符合要求;自动化流程仍需检查保存的工程和渲染结果。连接当前交互会话与运行独立后台任务是不同需求。
    • 来源:Blender Python 对象操作示例、后台执行脚本与结果检查、Python 命令行参数。2026-10-20 对照官方文档核验,未声称本地执行验证。

    Workspace 与构建

    检查与构建共用缓存时应串行

    命令名字不同,不代表它们没有共享写入。类型检查、内容同步和构建都可能更新同一份生成缓存,不能仅因逻辑上互不依赖就并行执行。

    • 排查方式:先检查脚本副作用和错误中的临时文件。固定临时名称的“写入后重命名”并不为两个完整写入流程提供互斥;一个进程完成重命名后,另一个进程可能找不到原临时文件。
    • 处理方式:共用缓存时先运行检查,再运行构建;需要并行时使用工具支持的独立缓存或输出目录,不猜测隔离开关。
    • 验证边界:2026-10-30在Astro7.2.9中观察到检查与构建并发同步内容时发生重命名ENOENT;已安装发行源码采用固定临时文件,改为串行后两项均通过。该现象与临时文件竞争一致,不代表所有ENOENT都是并发造成。
    • 来源:Astro CLI中的内容同步与构建,并结合对应版本发行源码和本地执行结果核验。

    Cocos Creator 3.8 的成功退出码是 36

    Cocos Dashboard管理编辑器安装;Dashboard已安装或已登录,不代表Creator编辑器已经安装。先核对编辑器列表,再决定是否下载。

    Creator 3.8命令行构建以36表示成功,32表示参数不合法,34表示构建过程失败。包装脚本应显式识别这些状态,不能直接把所有非零退出码判为失败,也不能将任意非零值当成功。仍需核对构建日志和产物内容。

    构建面板可导出带版本的JSON配置供--build "configPath=build-config.json"使用;保留配置版本信息。命令行运行仍需要GUI环境,不能直接假定适合无桌面的CI容器。

    来源:Creator 3.8命令行发布、构建发布面板。Dashboard与编辑器区分、退出码36均经本机实际操作核对。

    工具导入的工程根目录可能位于仓库内部

    “项目根目录”取决于工具要求的配置与入口,不一定是最外层工作目录。若工具提示配置文件不存在,先检查它实际选择的目录,以及所需文件是否直接位于这一层;报错不自动证明配置需要新建。

    • 处理:确认配置已存在于内层工程时,优先修正导入目录,避免在外层复制一套配置后引出入口、资源或分包相对路径问题。ZIP导入也应选解压后直接包含必需配置的目录,而非压缩包文件或多余外层。
    • 验证:2026-10-26 实际桌面工具在导入外层目录时报告缺少配置;改选已有配置的内层目录后错误消失并进入编辑器。未新建配置或移动工程内容。

    Vite 的资源字符串不等于资源导入

    在 JavaScript 中拼出的图片路径只是运行时字符串,不能据此认定图片已经进入构建图。开发服务器能从项目目录读到图片,也不能证明生产包包含它。

    import photoUrl from './photo.png';
    
    image.src = photoUrl;
    • 选择方式:需要构建器管理、哈希和改写URL的资源使用导入;必须保持原文件名的资源可放入公开静态资源目录。TypeScript项目通过 vite/client 提供常见资源类型声明。
    • 验证:2026-10-25 在Vite 6.4.5中将模板字符串中的图片路径改为静态导入,生产构建输出两张带哈希图片;通过生产预览确认图片真实加载。该观察证明此构建形式,不替代部署到实际子路径后的检查。
    • 来源:Vite静态资源处理。

    Vite 的TypeScript转换会读取匹配的配置

    Vite会读取文件附近匹配的TypeScript配置;被继承的配置不存在时,可能在模块转换阶段报错,即使另一个独立的 tsc --noEmit 检查已经通过。

    • 适合场景:同一工程同时包含网页预览和需要生成配置的其他工具链。为网页目录提供可独立解析的配置,保留它需要的编译选项;不要用空的生成文件冒充另一个工具链已初始化。
    • 记忆点:Vite的TypeScript转换不等于类型检查。分别验证 tsc --noEmit、开发入口和生产构建;局部配置能修复源码转换,也不保证位于其他目录的构建配置加载不再报警。
    • 验证与来源:2026-10-25 在Vite 6.4.5复现根配置继承缺失文件导致页面转换失败,局部有效配置恢复页面;再修正根配置后构建告警消失。参见Vite TypeScript编译选项与独立类型检查。

    Vite glob 与隐藏文件

    import.meta.glob() 在构建期展开匹配。隐藏文件不一定进入默认匹配;返回空对象也可能构建成功,直到运行时使用元数据才报错。

    const metadata = import.meta.glob('./.build-meta.json', {
      eager: true,
      import: 'default',
      exhaustive: true,
    });
    • 关键区别:eager 控制静态导入,不能代替 exhaustive 扩大文件搜索范围。后者默认关闭,启用后也会搜索隐藏目录和依赖目录,因此模式应保持具体。参见 Vite glob 选项定义与编译期转换说明。
    • 发布检查:不要仅检查构建退出码。对依赖生成元数据的功能,检查匹配结果和产物中的必要标识,再验证真实入口。
    • 验证:2026-10-25 使用 Vite 8.3.5、独立临时目录和虚构元数据做生产构建:默认参数未纳入点文件,显式设置 exhaustive: true 后纳入;两次构建均成功。该结论限定于已核验的版本与默认插件行为。
    • 可删除模块:可选模块可以使用具体的glob模式发现入口,再判断匹配结果是否存在后调用。要检查整个模块目录缺失时的构建、浏览器运行与测试发现,不能只让主入口可选,却在测试或其他入口中保留必需的静态导入。2026-10-26 用独立副本移除辅助模块后,验证构建与原功能测试通过、产物不含该模块代码、浏览器仍能进入主流程;空匹配在这个场景是预期结果。

    workspace:* 的本地包从哪里解析

    workspace:* 是 pnpm、现代 Yarn 支持的依赖协议:依赖必须来自当前工作区,* 不额外限制本地包版本。它本身是包管理器语法,不需要在项目中另行定义。

    • 成员范围:pnpm 用根级 pnpm-workspace.yaml 的 packages 字段声明成员目录;Yarn 用根级 package.json 的 workspaces 字段。
    • 目标包身份:例如依赖声明 "@demo/utils": "workspace:*",包管理器在成员中寻找 package.json 的 name 为 @demo/utils 的包;包名不必等于目录名,其版本由目标包的 version 声明。
    • 排查顺序:先核对成员目录是否被工作区配置包含,再核对目标包的 name 是否与依赖名一致。pnpm 找不到符合声明的本地包时会安装失败,不会改用 registry 上的同名包。
    • 来源:pnpm Workspace protocol、Yarn 工作区声明与交叉依赖、Yarn 包清单的 name 与 version。核验日期:2026-10-19。

    Workspace 包能在源码树运行,不等于打包后能安装

    浏览器入口直接指向 .ts 源码时,类型检查通过也不能保证宿主打包器会把 import './module.js' 解析到尚未生成的 module.ts。对需要交付的包,先生成真实的 JS、类型声明和 CSS,再让 exports 指向产物;按依赖顺序构建后,在实际消费应用中构建验证。

    • 依赖边界:pnpm 打包会把 workspace:* 改写为具体版本。若该工作区包只是编译期类型工具且未发布,放在运行依赖会让仓外安装寻找不存在的包;可改为开发依赖,并检查生成的声明文件不再引用它。真实运行依赖仍需发布或随交付提供,不能一概移走。
    • 最小验收:检查 tgz 内容与包清单,在不访问源码工作区的空目录安装 tgz;分别验证服务端入口加载和浏览器端构建、样式输出。仓内 tsc、源码预览和 pnpm pack 成功各只证明其中一段。
    • 验证:2026-10-26 在 TypeScript 5.9.3、Next.js 16.2.6、pnpm 10.33.2 下观察到源码入口的 .js 相对导入使 Next 构建报模块缺失;改为编译产物入口后通过。打包清单显示 workspace:* 被改写为版本;去掉仅用于类型的运行依赖后,独立目录安装并用 Vite 8.3.4 构建浏览器入口通过。结论限于这些实际构建形态。

    Vite server.open:服务启动成功与浏览器打开成功是两件事

    server.open: true 会在开发服务器完成首次监听后触发浏览器打开动作;Vite 7.0.6 的配置重启分支会跳过这一步。因此配置已被读取、服务已显示 ready,都不能单独证明浏览器已经打开。

    • 排查顺序:核对最终解析的 server.open,检查实际监听地址和 HTTP 响应,再查看浏览器打开子进程是否结束及目标标签页是否出现。
    • macOS 边界:Vite 7.0.6 检测到已运行的 Chromium 系浏览器时,会先通过 AppleScript 尝试复用标签页;该调用没有由 Vite 设置超时,错误被捕获后才回退到系统打开方式。自动化调用挂起时,服务可以正常响应而浏览器迟迟不打开。
    • 默认浏览器与标签页复用:open: true 只启用打开动作,不保证 macOS 始终选择系统默认浏览器。Vite 维护者说明,优先操作已运行的 Chromium 浏览器是为了复用已有标签页;这是启动工具的选择策略,与应用使用 Vue、React 或其他框架无关。维护者解释。
    • 已有环境控制:Vite 支持 BROWSER;未配置时走默认选择流程,none 会禁用自动打开,macOS 的 open 值会跳过上述 Chromium AppleScript 分支并使用系统浏览器。这是 Vite 支持的配置能力,不代表当前终端已设置;排查时不要擅自写入用户的环境配置。
    • 验证:2026-10-18 在 macOS、Vite 7.0.6 上核对最终配置为 true,开发服务返回 HTTP 200;同时观察到浏览器打开脚本持续运行至少 112 秒,脚本结束后目标页面才出现在 Chrome。该观察确认打开调用发生延迟,未据此断言具体权限或浏览器实例冲突是根因。
    • 单次复测:同日以进程作用域设置 BROWSER=open,目标页面正常出现为新的 Chrome 标签页,未再观察到挂起的 Chrome 打开脚本;该设置只属于这次进程启动,不代表项目或全局环境已被修改。
    • 项目级本机设置:在 macOS 上可将 BROWSER=open 放入 .env.development.local,供默认开发模式读取;.env.local 则适用于各模式。保持 server.open: true 并重启开发进程。BROWSER 是 Vite 特殊读取的 Node 侧控制变量,不需要 VITE_ 前缀;已有进程环境中的值优先。是否被 Git 忽略须以项目规则为准。2026-10-19 已对照 Vite 7.0.6 的环境文件加载及浏览器打开实现核验。
    • 跨平台边界:BROWSER=open 是 Vite 的 macOS 专用处理分支,不能把它当作所有系统通用的浏览器名。团队配置若采用它,应限定 macOS;Windows/Linux 继续使用默认打开流程。2026-10-19 对照 官方实现 核验,主分支改用 JXA 后仍保留 Chromium 标签页复用策略。
    • 来源:Vite server.open,并对照已安装 Vite 7.0.6 的启动与浏览器打开实现核验。

    Node.js 直接执行 TypeScript 要核对最低运行版本

    脚本能在当前 Node 中直接导入 .ts,不代表项目声明支持的全部 Node 版本都能执行;类型检查成功也不能补上运行时加载能力。

    • 版本边界:原生类型擦除从 Node 22.6.0 开始提供,Node 22.18.0 和 23.6.0 起默认开启;Node 20 不提供这项能力,22.12.0 默认也未开启。
    • 适合场景:把读取 TypeScript 模块的生成器或校验器接入 check、CI 等入口时,同时核对项目的最低 Node 版本、执行参数和加载器。
    • 处理方法:保留旧版 Node 支持时,使用兼容的转译或加载方案;确实提高运行版本要求时,同步更新版本声明与执行环境。原生类型擦除不读取 tsconfig.json,不能据此假设路径别名或需要生成代码的 TypeScript 语法也可用。
    • 来源与验证:Node.js TypeScript 支持与版本历史;2026-10-15 在 Node 24.19.0 上关闭类型擦除后,确认 ESM 导入 .ts 会以未知扩展名错误失败。

    Babel 的数组假设会破坏 Set 展开和遍历

    源码在现代 Node 中正确,不代表经过目标转译后仍保留可迭代对象语义。Babel 的 spread loose 或 iterableIsArray 假设把输入视为数组;for-of 的 assumeArray 也只适合数组,不能用于实际的 Set、Map 或其他迭代器。

    • 典型症状:[...listeners] 被编译为 [].concat(listeners),得到的是包含 Set 对象的数组;遍历时成员检查失败,可能静默漏掉全部监听器。普通 Set 也没有数组的 length,按数组索引编译的循环不能正确遍历它。
    • 处理:优先移除与实际输入不符的编译假设;不能控制宿主构建时,先用 Array.from(listeners) 显式转为数组,再执行后续展开或循环。Array.from 本身需要目标运行时支持或适配,不能据此声称兼容所有旧环境。
    • 验证:不仅运行源码单测,还应按实际插件及选项转译最小反例,执行产物并检查监听分发、取消与清理。2026-10-27 用 Babel 7.28.5、spread 7.27.1 的 loose 模式及 for-of 7.27.1 的 assumeArray 模式复现差异,显式数组转换后的分发与清理回归通过。
    • 来源:Babel spread 的 loose 与 iterableIsArray、Babel for-of 选项。这描述所列选项的语义,不代表 Babel 默认会把所有迭代器编译错。

    少色 PNG 可以用精确调色板无损缩小

    RGBA 图像的实际颜色数不超过 256 时,可以把每一种完整 RGBA 值映射为一个调色板索引,以 RGB 调色板和逐项 alpha 保存 PNG。抗锯齿产生的半透明像素也属于颜色,不能只数 RGB。

    • 适合场景:构建阶段输出字形图集、遮罩或少色图标,需要减少文件字节且保留透明边缘。
    • 记忆点:自动量化可能改变像素;精确重映射必须先确认颜色数,并在写出后将图像解码为 RGBA,逐字节比较 decoded.tobytes() == original.tobytes()。超过调色板容量时保留原格式。
    • 运行时边界:文件更小不代表解码后的纹理尺寸、GPU 显存或绘制成本更低,仍需在目标渲染环境中检查透明度和采样。
    • 验证:2026-10-26 使用 Pillow 对少色 RGBA 图集作精确索引编码,重新解码后的 RGBA 字节完全相同,并实际验证 WebGL 图像与文字显示;不把节省量推广到任意 PNG。

    包体校验必须对应实际发布目标的文件集合

    一个打包流程会排除的文档、截图或平台展示素材,另一个工具未必识别。按第一套排除规则计算的大小,不能直接证明第二套构建满足限制;也不能凭 ZIP 文件大小推断解包后各包的大小。

    • 处理:为不同交付目标生成明确的文件清单,按实际分包根目录计算主包、分包与总量,并保留原始字节数。仅供提交展示的素材与运行资源分开导出,不用猜测的忽略字段代替实际验证。
    • 完整性:导出后逐项核对运行文件与源码的哈希,确认必要资源没有为减小体积而丢失;失败时保留上一份可用产物,拒绝覆盖非生成目录。
    • 验证:2026-10-26 实际工具对包含额外展示素材的工程报首包超限;仅排除非运行文件并保持运行字节一致后,真实预览生成通过。该结果不等同于设备运行或性能验收。

    构建产物的特征字符串检查必须做反例验证

    “没有搜到某段报错文案”不能单独证明对应依赖没有进入产物:生产分支裁剪和 tree shaking 可能删掉文案,同时保留实际功能。

    • 验证方法:用真实生产配置构造一个明确包含目标功能的最小产物,确认检查脚本会拒绝它;再用明确不包含目标的产物确认能通过。只在当前干净产物上运行一次检查,无法验证漏报率。

    • Vue 示例:2026-10-15 使用 Vue 3.5.42 与 Vite 7.0.6,将完整 ESM 构建的 compile 暴露到全局;产物仍能把模板编译成渲染函数,但不含几条开发期编译错误文案,依赖这些文案的扫描器仍返回成功。

    • 改进方向:优先检查打包器实际纳入的模块图;使用产物特征时,明确适用构建形式,并保留上述应当被拒绝的回归样本。

    • 内嵌资源边界:只断言没有某个路径前缀,不能证明图片已经内嵌。资源可能表达成 new URL("photo-hash.webp", import.meta.url),并不包含该前缀。应以构建清单逐个核对原资源名不再残留、预期图片字节确实存在,并通过普通HTTP检查实际显示;这仍不能替代文件协议验收。2026-10-24 用独立字符串反例、产物逐资源校验与浏览器加载行为核验。

    ESM 依赖需要分别检查 Node、TypeScript 和 Jest

    Node 能加载一个 ESM 包,不代表 TypeScript 类型检查和 Jest 测试运行器也已支持同样的加载方式。

    • 运行时:现代 Node 可以从 CommonJS 使用 require() 加载同步 ESM 模块,但模块及依赖链不能包含顶层 await。
    • 编译器:TypeScript 5.8 才在 module: nodenext 下支持这项互操作;旧版即使配合新 Node,仍可能对编译为 require() 的静态导入报告 TS1479。esModuleInterop 不能代替这项版本支持。
    • 测试器:Jest 默认跳过依赖目录的转译。CommonJS 测试导入 lodash-es 等 ESM 包时,可为该包开放 transformIgnorePatterns 并配置 JavaScript 转译;pnpm 的实际路径还经过 .pnpm,只匹配包名所在的一层可能仍被忽略。使用 ts-jest 转译依赖中的 JavaScript 时,需要对这项转译开启 allowJs。
    • 验证:2026-10-12 在 Node 24、TypeScript 5.6、Jest 30 环境中分别复现运行时加载成功、静态导入 TS1479、Jest 拒绝 export,并验证定向转译能消除测试加载错误。
    • 来源:TypeScript 5.8 的 require(ESM) 支持、Jest 转译忽略规则及 pnpm 示例。

    Monorepo 中同名 pnpm run 脚本取决于当前 package

    pnpm run <script> 执行当前 package manifest 中定义的脚本。Workspace 根目录和子包可以存在同名脚本;如果在不同目录执行相同命令,实际启动的程序和端口可能完全不同。

    # 执行前先确认当前目录、package 名称和脚本内容
    pwd
    node -p "require('./package.json').name"
    node -p "require('./package.json').scripts['dev:server']"
    
    # 需要避免目录歧义时,从 workspace 中显式选择 package
    pnpm --filter <package-name> run dev:server
    • 诊断线索:启动日志显示“成功”,但目标端口没有监听时,先确认日志对应的是哪个框架、进程工作目录和 package,而不是继续等待输出。
    • 记忆点:长驻的 dev/watch 服务启动成功后没有新日志通常只是进入等待状态;应使用 lsof -nP -iTCP:<port> -sTCP:LISTEN 或 HTTP 探针验证目标端口,不能把“终端仍被占用”当作目标服务已经启动。
    • 来源:pnpm run 说明它执行 package manifest 中的脚本;pnpm filtering 说明 --filter 可以把命令限定到明确的 workspace package。结论也通过同一 workspace 中两个同名脚本的实际进程工作目录和监听端口复核。

    Vue 的 CommonJS 依赖可能引入运行时模板编译器

    应用自己的 import 'vue' 使用纯运行时构建,不代表第三方依赖的 require('vue') 也解析到同一个入口。条件导出可能让两条路径选择不同产物。

    • 判断方法:检查依赖发布入口、框架的 exports.import / exports.require,再查看生产构建实际保留的模块。不要仅凭依赖名字判断它是否需要替换。
    • 可选处理:若所有模板都在构建期编译,可在 Vite 用精确匹配 ^vue$ 的别名指向 vue/dist/vue.runtime.esm-bundler.js。精确匹配避免误改 vue/server-renderer 等子路径;依赖运行时字符串模板的应用不能直接套用。
    • 权衡:直接操作 DOM 的排序库仍需要与框架数据更新、元素复用和卸载生命周期协调。移除 Vue 包装组件能减少部分体积,也会把这部分维护职责交给项目;应先排除错误入口造成的额外体积,再决定是否重写。
    • 验证:2026-10-19 核对 Vue 3.5.42 的条件导出与 vuedraggable 4.1.0 的 UMD 入口;采用上述别名后的 Vite 7 生产构建通过模块图检查,浏览器中鼠标拖拽与键盘重排都正常。

    限制导入语句优先使用语法规则

    按行扫描导入关键字的正则不理解代码上下文,注释或字符串中的示例也可能被当成真实依赖。项目已有语法检查器时,应优先用相应规则表达导入限制。

    • ESLint 示例:no-restricted-imports 检查实际导入和相关导出节点,适合限制明确的模块入口;动态加载等额外语法需要另行核对规则覆盖范围。
    • 验证:2026-10-18 用同一段仅包含注释示例的代码复现了文本扫描误报;本地 ESLint 规则对注释返回零诊断,对真实的受限静态导入返回错误,并核对了已安装规则的语法节点访问逻辑。

    批量构建必须传播失败退出码

    批量构建多个子项目时,可以继续处理后续目标以收集完整结果,但最终命令必须在任一子构建失败时返回非零退出码。

    • 适合场景:一个脚本循环执行多个应用、包或平台构建,并被 check、CI 或发布流程当作质量门禁。
    • 记忆点:在循环内部 catch 后只打印错误,会让脚本最终以 0 退出,形成“日志里失败、门禁却通过”的假成功。应收集失败项,并在循环结束后设置 process.exitCode = 1 或抛出汇总错误。
    • 验证方法:故意让其中一个最小测试目标失败,确认后续目标仍按策略执行,同时父命令退出码非零且不会打印“全部成功”。
    • 来源:通过 Node.js 脚本控制流与子进程退出行为检查验证。

    精简注释时保留工具指令

    普通说明与工具指令都可能写在注释里,不能用“删除全部注释”替代整理。@vitest-environment 会选择测试环境,eslint-disable 会影响规则诊断。

    • 保留内容:简明解释设计原因、输入边界、兼容约束和数据格式;类型文档中的有效 @param / @returns 也应保留。
    • 可精简内容:重复描述代码动作、已由测试名表达的行为、旧实现片段、一次性的排查过程与性能数字。修改说明时需对照当前实现,避免把过时叙述缩短后继续保留。
    • 验证方法:对比移除普通注释后的脚本语法结构、模板编译结果和 CSS 规则,同时单独核对工具指令,再运行相应检查。仅比较去注释后的文本不足以覆盖工具读取注释的行为。
    • 验证:2026-10-19 结合本地 Vitest 3.2.7、ESLint 9.32 的指令解析逻辑和注释整理回归验证;未把临时核验工具加入项目命令。

    在脏工作区运行 lint 前先检查脚本

    pnpm lint、npm run lint 只是脚本名,不保证是只读检查;项目可能把它定义成 eslint ... --fix,一执行就会改写整个匹配范围。

    • 安全检查:先读取 package.json 中对应脚本;需要只读验证时,直接运行不带 --fix 的底层命令,并把路径收窄到本次改动。
    • 变更确认:运行前后比较 git status --short 与 git diff --name-only,可以及时发现格式化器越界修改。
    • 恢复边界:如果工作区原本已有用户改动,不要直接用覆盖式 Git 命令回退;先精确区分本次工具产生的差异,再取得用户授权或逐项恢复。
    • 验证依据:npm/pnpm 脚本会原样执行 package.json 中配置的命令;eslint --fix 的语义就是把可自动修复的问题写回文件。实际运行也应以脚本内容和前后 Git 差异为准。

    持续集成与定时任务

    GitHub Actions schedule 适合可延迟的批处理

    Actions 在 runner 上运行工作流,Vercel Cron 请求生产函数。执行位置决定任务能力和超时;两者都需要考虑延迟、补跑与幂等。频率、时区、费用及适用场景见 GitHub Actions 与 Vercel Cron 的执行边界。

    自动化测试

    测试源码与运行数据目录不是同一类文件

    *.test.ts、*.test.tsx 或 *.spec.ts 通常保存断言和测试流程;具体哪些文件会被运行,由 Vitest、Jest 等测试器的发现规则决定。测试执行时可另用临时目录保存 fixture,并在结束后清理。启动完整应用做页面或 HTTP 联调时,显式指定的数据根则可能长期保存 SQLite、JSON 和素材;它是隔离运行状态,不是测试套件源码,也不会因放在 qa 目录就自动成为单测。

    • 清理顺序:先停止仍使用该数据根的服务,再决定是否需要保留复现证据;删除运行数据不应连同测试源码一起处理。
    • 验证:2026-10-27 核对一个 Vitest 工程:测试入口位于包级测试目录,使用独立临时数据;另一个完整应用进程将项目与画布状态写入显式数据根。两者的文件角色与生命周期不同。测试文件发现规则另见下文 Jest 与 Node.js 条目。

    优先级回归要跨越候选集边界

    “在当前可执行对象中选最高优先级”与“先锁定全局最高优先级,再完成执行所需的准备”是两种不同合同。只测试所有对象都可执行的样例,无法区分它们。

    • 最小反例:A优先级更高但还需准备,B次优且可立即执行。前一种合同选B,后一种应先处理A的准备条件;预期必须来自需求,不能从现有过滤实现倒推。
    • 覆盖方式:同时保留全部可执行、跨候选集边界、当前目标失效和同优先级等样例;检查选中对象以及后续动作,不能仅验证过滤后的列表已排序。
    • 验证:2026-10-26 用独立Node断言确认全局选择与仅选可执行对象的结果不同,并通过聚焦回归检查选择与后续动作;结论不意味着任何一类合同普遍更优。

    门槛规则要同时验证阈值两侧和影响结果的组合

    需求规定较弱配置应失败、较强配置应成功时,两侧都要进入回归;只验证成功一侧,会漏掉较弱配置也意外成功的情况。

    • 控制条件:先固定默认资源、数据和执行条件做成对比较,再覆盖位置、顺序和不同执行轮次等影响因子。涉及随机过程时,少量固定样例通过不代表结果稳定。
    • 夹具边界:默认资源变化后,交互测试可以显式设置完成该交互所需的资源;门槛验收仍须使用真实默认输入,不能把补资源藏进所有测试共用的默认构造器。
    • 验证:2026-10-28,小范围位置样例符合预期,扩大配置枚举后发现一个较弱配置也成功的反例;补充执行轮次又发现偶然成功。调整后同时复核两侧配置,并将显式资源夹具与默认输入验收分开。

    Node.js --test 也会发现备份目录里的测试文件

    没有指定入口时,Node.js 测试运行器会从当前目录匹配默认测试模式;backup、archive 等目录名不是自动排除规则,复制过去的 .test.cjs 仍可能作为独立测试入口执行。

    • 限制范围:在支持测试 glob 的 Node.js 版本中,使用 node --test "tests/*.test.cjs",把模式放在引号中交给 Node 解析;* 只覆盖该层,需要包含子目录时使用明确的递归模式。
    • 备份与辅助文件:归档可以改用非测试后缀;共享构造器仍可由测试导入,但不要仅凭目录名判断它不会被发现。
    • 验证:2026-10-24 在本机 Node.js 26 观察到归档副本被无参数 --test 收集;限定实际入口模式后只执行预期集合。更早版本应核对相应版本的命令行模式支持。
    • 来源:Node.js 测试发现与命令行运行。

    Vitest:不要无意中从 beforeEach 返回 mock 函数

    Vitest 会把 beforeEach 返回的函数当作测试结束后的清理回调。mockReset() 返回 mock 本身,因此 beforeEach(() => mock.mockReset()) 会让框架在测试结束后再次调用这个 mock。

    • 症状:测试正文已经用 rejects 正确断言了失败,但整条测试仍因同一个错误失败;也可能出现意外的额外调用。
    • 正确写法:beforeEach(() => { mock.mockReset(); });,没有清理需求时让钩子返回 undefined。
    • 边界:返回一个专门的清理函数是有意支持的用法,不应一概禁止钩子返回函数。
    • 验证依据:2026-10-15 对照本地 Vitest 3.0.5 runner 的 callSuiteHook / callCleanupHooks 实现,并通过失败断言测试复现;改为块体且不返回 mock 后通过。不假定其他测试框架具有相同约定。

    Jest 全部 PASS,但进程不退出

    测试断言通过与测试进程正常结束是两件事;未关闭的服务器、子进程或定时器都可能继续占用事件循环。

    • 诊断命令:pnpm exec jest --detectOpenHandles,它会尝试给出残留句柄的创建堆栈,并隐含串行运行。
    • 导入也可能有副作用:测试为使用校验器或注册表而导入模块时,其运行时依赖仍可能在顶层启动日志上报、服务发现或定时任务;应在测试边界替换依赖,或由创建者在 teardown 关闭资源。纯 import type 不属于这种运行时导入。
    • 记忆点:--forceExit 只能强制结束进程,不证明资源已经正确释放;--detectOpenHandles 有性能开销,适合排障使用。
    • 来源:Jest CLI 的 detectOpenHandles;2026-10-09 本地执行验证了“全部断言通过、导入依赖的定时器仍阻止退出”的情况,未保留业务日志或标识。

    Jest 的测试入口由发现规则决定

    测试目录可以同时放测试入口、共享测试数据构造器和辅助脚本;文件是否作为独立测试套件运行,要看 Jest 的发现配置。

    • 默认约定:Jest 的默认匹配包含 __tests__ 目录中的测试源文件,以及其他位置的 .test / .spec 文件,不能单凭“没有 spec 后缀”断言不会被收集。
    • 显式规则:配置 testRegex: '.*\\.spec\\.ts$' 后,测试入口限定为 .spec.ts 结尾;普通 fixtures.ts 或 helpers.ts 可以被测试导入,但不会因此成为独立测试套件。testMatch 与 testRegex 不能同时配置。
    • 辅助脚本:子进程测试还可能需要独立运行的 .cjs 文件,它模拟进程行为,由测试启动,不需要包含 describe 或 it。
    • 验证:用 jest --listTests --runInBand 查看实际收集结果,区分“没有被当成测试入口”与“被其他测试调用时会执行”。
    • 来源:Jest 测试发现配置、testRegex;2026-09-10 通过本地测试发现命令验证显式后缀规则与辅助文件未被单独收集。

    Playwright:DOM 存在不等于元素可见

    locator.waitFor() 默认等待 visible,要求元素具有非空边界框且不是 visibility: hidden。元素即使已经存在、也已经加上“收起”类,只要尺寸变成零,等待可见仍会超时。

    • 只确认节点和状态类存在:使用 state: 'attached';例如等待折叠后的面板。
    • 确认用户看得到内容:使用 visible;确认移除节点则使用 detached。
    • 注意 hidden:它同时接受节点不存在和节点不可见,不能单独证明某个业务操作已经完成。
    • 诊断方法:区分“找不到元素”与“找到了但不可见”;检查 class/属性、边界框及明确的业务完成状态,不要仅增加超时或重复执行已经完成的操作。
    await page.locator('#settings-panel.is-collapsed').waitFor({ state: 'attached' });

    来源:Playwright Locator.waitFor 定义四种等待状态;本地浏览器验证也确认零高度折叠容器会被默认可见性等待判为未就绪。核验日期:2026-09-07。

    BrowserContext 的 extraHTTPHeaders 作用于该上下文页面发起的每个请求。把整串 Cookie 放进去,跨主机的脚本等资源请求也可能携带它。

    • 适合场景:给自动化浏览器注入登录态前,区分“浏览器 Cookie 存储”与“人为追加全局请求头”。
    • 正确边界:需要登录 Cookie 时,用 addCookies 明确配置 url,或同时指定 domain 与 path;访问公共资源不应额外携带凭据。不需要登录态的执行链不要注入 Cookie。
    • 风险判断:收到请求的其他服务能看到人为追加的 Cookie,凭据还可能进入其日志或监控。如果其中是有效、可重用的会话凭据,获得者可能冒用会话;是否可用仍取决于过期、撤销、设备绑定等服务端校验。HTTPS 保护传输过程,不能阻止目标服务读取自己收到的头。合成 Cookie 实验只能证明发送范围,不能据此声称真实账号已泄露。OWASP 会话管理
    • 验证方法:两个本地 HTTP 服务分别使用 127.0.0.1 和 localhost,页面从另一主机加载脚本;只使用虚构 Cookie,检查脚本服务收到的请求头。
    • 来源:Playwright 全局请求头、addCookies 的作用域参数;2026-10-09 用本地无头 Chrome 与合成凭据验证跨主机请求确实收到全局 Cookie 头。

    性能测量与重复计算

    循环中不变的预处理应按请求复用

    当多个候选都要与同一段文本比较时,把只依赖文本的规范化和位置映射移出候选循环。若原先每个候选都重算,这部分工作从约 候选数 × 文本长度 降为每个不同文本计算一次;候选自身的匹配成本仍然存在。

    • 缓存边界:先选择请求内的普通局部变量或映射,明确何时释放;需要跨调用记忆时可用 functools.lru_cache,但参数必须可哈希。缓存会持有参数和返回值,直到淘汰或 cache_clear(),容量限制不等于按请求自动清理。Python 缓存接口说明
    • 适用条件:同一输入与配置必须产生可复用结果;若转换依赖语言、规则版本或其他状态,应把这些依赖纳入缓存键。不要缓存需要独立可变返回对象或带副作用的操作。
    • 验收:对同一批输入比较完整结果,包括顺序、原文位置和错误原因,再交替运行原版与优化版多轮计时。输出一致只证明此次行为等价,不证明原版逻辑正确;已有错误也会被保留。
    • 验证:2026-10-26 用 Python 3.9 的同进程对照实验核验请求内规范化复用,完整序列化输出一致,重复预处理耗时下降。具体倍数依赖输入规模与调用分布,不推广为固定收益;不把进程内处理时间表述为包含网络和初始化的端到端延迟。

    进程与环境

    关闭终端、父进程退出与子进程退出是不同事件

    在 macOS 等非 Windows 系统上,父进程退出不会自动递归终止所有后代;Node.js 子进程即使没有设置 detached,也可能继续运行。Node.js 子进程生命周期

    • 终端挂断:关闭终端可能触发 SIGHUP,但不能把它理解为“对整棵进程树执行强制终止”。进程能否退出取决于信号是否送达、信号处理方式,以及启动器有没有清理子进程。
    • 信号与异常分开看:Node.js 在非 Windows 系统上默认收到 SIGHUP 会退出,安装监听器后默认行为改变;uncaughtException 处理器改变的是 JavaScript 未捕获异常的退出行为,并不会自动屏蔽终止信号。保活定时器避免事件循环空闲退出,也不能抵抗 SIGKILL。Node.js 信号、未捕获异常
    • 证据边界:终端描述符失效、旧服务仍存活,只能证明输出通道与进程生命周期已经脱节;没有当时的信号记录,不能断言某个进程忽略了 SIGHUP,也不能认定是某个终端应用造成。
    • 实际操作:开发服务先用其停止命令或 Ctrl+C 请求退出,再确认相关进程和监听端口消失;关闭窗口本身不能充当退出验证。以上机制于 2026-10-19 对照官方文档核验。

    终端失效也会触发 write EIO 和日志循环

    write EIO 表示一次写操作发生 I/O 错误,不能只凭错误码判断硬盘损坏;若堆栈落在 process.stdout.write、process.stderr.write 或控制台 transport,应先检查进程连接的终端。

    • 定位对象:lsof -a -p <PID> -d 0,1,2 查看标准输入、输出和错误描述符;在 macOS 上,(revoked) 是终端描述符已经失效的直接线索。结合进程启动时间与父子关系,区分旧服务和新服务。
    • 循环机制:若未捕获异常处理器把输出失败再次写入同一个失效输出流,新错误可能再次进入处理器;同时启用文件日志时,错误堆栈会持续落盘。检查日志尾部、间隔采样文件大小和 CPU;一次短采样没有增长不能证明循环已经结束。
    • 处理边界:先确认并停止异常写入者,再按需保留样本、清理历史;只清空文件不会修复输出流。按天轮转限制的是单文件时间范围,不能限制一天内的突发写入量,还需独立的大小、保留数量和异常处理边界。
    • 来源与验证:2026-10-19 在 macOS / Node.js 24 的本地进程中观察到标准描述符全部为 (revoked),错误堆栈落在控制台写入,且未捕获异常处理器再次调用同一输出链。结论仅保留通用进程与日志机制;Node.js 系统错误说明错误码需结合调用上下文解释。

    Node.js child.kill() 不等于子进程已经退出

    在 POSIX 系统上,child.kill() 默认发送 SIGTERM;子进程可以处理该信号并继续运行。child.killed 为 true 只说明信号已成功发送。

    • 超时与取消:父进程的 Promise 拒绝、AbortSignal 触发或超时计时结束,都不能单独证明子进程停止执行。
    • 清理策略:向仍属于本任务的子进程请求退出,等待有限宽限期;仍未退出时按平台使用强制终止,再确认 exit/close。不要在 PID 已回收后延迟向旧号码发信号,避免命中复用的 PID。
    • 有界退出等待(bounded wait):为退出等待设置明确时限。比如发送 SIGTERM 后最多等 2 秒,提前退出就立即继续;超时仍存活则发送 SIGKILL,再用另一段有限等待确认退出。数字只是策略示例,不是 Node.js 默认值;最终仍未确认退出时应记录清理失败,不能无限挂住,也不能报告已清理。
    • 进程树边界:终止直接子进程也不自动等于终止所有后代,需按任务拥有的进程树或进程组设计清理。
    • 来源:Node.js subprocess.kill、subprocess.killed;2026-10-09 在 macOS 使用捕获 SIGTERM 的本地 Node.js 替身,分别验证了超时与 AbortSignal 返回后子进程仍存活,并在测试结束时显式清理。

    Python subprocess:超时与进程组清理

    subprocess.run(timeout=...) 超时会终止并等待直接子进程;Popen.communicate(timeout=...) 超时只抛异常,清理需要调用方负责。两者都不能据此保证所有后代已退出。Python 超时契约

    • POSIX 用法:由本任务启动的命令可设置 Popen(..., start_new_session=True),再用 os.killpg(proc.pid, signal.SIGTERM) 请求其进程组退出;它在子进程执行前调用 setsid()。不要给未经隔离、归属不明的进程组发信号。会话参数、killpg
    • 等待与升级:沿用上文的有界退出策略,宽限期后必要时对该组发送 SIGKILL,再回收直接子进程并处理输出管道;超时异常本身不等于任务已经停止。
    • 边界:后代若主动建立新会话或转移进程组,可能不再属于原组;这不是任意进程树的通用回收保证,也不适用于 Windows。
    • 验证:2026-10-28 在 macOS / Python 3.9 用“父进程再启动延迟写文件的子进程”验证进程组终止后未发生后代写入;测试只涉及合成进程。

    Node.js os.userInfo().username:当前进程的系统用户名

    os 是 Node.js 内置的操作系统模块。os.userInfo() 同步返回当前有效操作系统用户的信息对象,.username 是读取该对象的用户名属性,默认得到字符串。

    import os from 'node:os';
    
    const username = os.userInfo().username;
    • 返回内容:对象还包含 uid、gid、homedir 和 shell;Windows 下 uid、gid 为 -1,shell 为 null。
    • 身份边界:结果对应运行进程的系统账号。以服务账号或容器内账号运行时,得到的是该运行账号,不能据此确定网页操作者或已登录的业务用户。
    • 调用边界:不需要 await;默认编码为 UTF-8。操作系统缺少用户名或主目录信息时可能抛出 SystemError,调用方应按需要处理。
    • 来源与验证:Node.js os.userInfo();2026-09-10 在 Node.js v24.19.0 上核验返回值非 Promise、用户名为字符串、uid 与 process.geteuid() 一致,未记录真实用户名或用户目录。

    IPC:进程间通信

    IPC 是 Inter-Process Communication 的缩写,表示进程之间交换消息和数据。Node.js 的 child_process.fork() 内置父子进程消息通道:父进程用 child.send(message),子进程用 process.send(message),双方通过 message 事件接收。

    • 记忆点:IPC 负责通信,不负责创建线程或保证任务并发。普通对象消息经过序列化和解析,接收方不会直接拿到发送方原来的对象引用。
    • 用途:主进程发送开始、取消等命令,子进程回报进度、结果或错误。
    • 来源:Node.js subprocess.send。

    fork()、worker_threads 与异步并发池

    任务被命名为 worker,不代表它创建了线程或进程;应查看真正的启动 API。

    • child_process.fork():启动新的 Node.js 子进程,各自拥有 V8 实例与内存,并内置 IPC 消息通道。模块单例与普通 JS 对象不会直接与父进程共用;文件、数据库等外部资源仍可能共享。
    • worker_threads.Worker:在同一进程中并行执行 JavaScript 的线程,适合 CPU 密集计算;可通过 SharedArrayBuffer 显式共享内存。
    • async 循环和 Promise.all():不创建新的 JS 线程或进程;完整机制见单线程异步并发,独立任务状态仍需程序显式管理。
    • 来源:Node.js 子进程与 fork、Worker threads;2026-09-09 对照调用入口、异步循环和共享对象生命周期核验。

    macOS 应用打不开:-600 / procNotFound

    macOS 的 OSStatus -600 对应 procNotFound,SDK 定义为“没有符合指定描述符的可用进程”。它描述一次进程定位或通信失败,不能仅凭错误码认定应用文件损坏、内存不足或硬件故障。

    • 先确认对象:保留完整错误域、错误码和 AppPath。桌面弹窗中的应用名称可能不够明确,系统日志中的路径有助于确认实际启动失败的应用。
    • 关联日志:应用启动失败可查 CoreServicesUIAgent、launchservicesd;打开或保存对话框异常可查 com.apple.appkit.xpc.openAndSavePanelService,对照同一时间段的退出、故障和连接错误。
    • 判断边界:多个应用同时异常、重启后恢复,是检查共享系统服务的线索,不足以证明它们有同一个根因;服务退出日志证明退出事实,也未必给出最初触发原因。
    • 证据来源:Apple macOS SDK 的 CarbonCore.framework/Headers/MacErrors.h 明确声明 procNotFound = -600;Apple 的 procNotFound 条目提供符号入口。系统统一日志也可直接出现 NSOSStatusErrorDomain Code=-600 与该描述。核验日期:2026-09-08。

    Node.js spawn() 子进程只能看到传入的环境

    child_process.spawn() 的 options.env 默认是父进程当前的 process.env。一旦显式传入 env,子进程看到的就是该对象中的键值;需要在保留原环境的基础上新增变量时,应明确展开 process.env。

    const env = { ...process.env, SERVICE_MODE: 'local' };
    spawn(process.execPath, ['worker.js'], { env });
    • 诊断线索:子程序提示缺少环境变量时,先在实际启动它的父进程中检查 Boolean(process.env.REQUIRED_VAR),并保留子进程的 stderr。不要为了排查而打印令牌、会话 ID 或其他秘密值。
    • 记忆点:环境属于具体进程。长驻服务不会因为稍后在另一个终端 export 变量,或另一个应用向自身进程注入变量,就自动获得更新;应从具备正确环境的入口重启父进程,或由父进程在 spawn() 时显式传入所需变量。
    • 边界:写成 { env: { REQUIRED_VAR: value } } 会让子进程只收到这个精简环境,PATH 等原有变量也可能丢失;通常使用 { ...process.env, REQUIRED_VAR: value },除非确实需要隔离环境。
    • 来源:Node.js child_process.spawn() 说明 env 的默认值是 process.env,并由传入的 env 决定子进程可见变量;Node.js 环境变量 说明 process.env 是当前 Node.js 进程的用户环境对象。