Effect 平台库变更解读:FileSystem.seek 拒绝负位置并返回 BadArgument

发布时间:2026/9/15 18:41:07
Effect 平台库变更解读:FileSystem.seek 拒绝负位置并返回 BadArgument Effect 平台库变更解读FileSystem.seek 拒绝负位置并返回 BadArgument【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect本文基于effect仓库 .changeset/pre/fix-negative-file-seek.md 记录的一次 patch 级行为变更深入解析FileSystem.File.seek在 Node 与 Deno 平台实现上的负位置校验逻辑、BadArgument平台错误的构造与捕获方式以及由此带来的返回类型收紧。读完本文你将清楚理解 effect 中文件游标cursor的语义约定并能在自己的代码中正确处理seek越界失败。变更概览一次针对 seek 越界的防御性修复该 changeset 声明了三个包同时以patch级别发布更新受影响包版本变更effectpatcheffect/platform-node-sharedpatcheffect/platform-denopatch变更的核心内容是在 Node 和 Deno 运行时上FileSystem.File.seek现在会拒绝计算出负位置的 seek 请求并以BadArgument平台错误失败失败时文件游标保持不变即不产生副作用seek的返回类型收紧为Effectbigint, PlatformError。一次seek之所以可能计算出负位置是因为它支持两种基准模式从文件开头start指定绝对偏移或从当前游标位置current指定相对偏移。当current模式下的相对偏移为负数且绝对值大于当前游标位置时结果位置就会越过文件起点这是本次变更要拦截的非法场景。seek 的接口语义SeekMode 与返回类型seek是FileSystem.File打开的文件句柄接口上的核心方法接口定义位于 packages/effect/src/FileSystem.tsexport interface File { readonly [FileTypeId]: typeof FileTypeId readonly stat: Effect.EffectFile.Info, PlatformError /** * Seeks before the start fail with BadArgument and leave the cursor unchanged. */ readonly seek: (offset: bigint, from: SeekMode) Effect.Effectbigint, PlatformError readonly sync: Effect.Effectvoid, PlatformError readonly read: (buffer: Uint8Array) Effect.Effectnumber, PlatformError readonly readAlloc: (size: number) Effect.EffectOption.OptionUint8Array, PlatformError readonly truncate: (length?: number) Effect.Effectvoid, PlatformError readonly write: (buffer: Uint8Array) Effect.Effectnumber, PlatformError readonly writeAll: (buffer: Uint8Array) Effect.Effectvoid, PlatformError }接口的 JSDoc 注释packages/effect/src/FileSystem.ts已明确写出本次变更的契约Seeks before the start fail withBadArgumentand leave the cursor unchanged.越过文件开头的 seek 以BadArgument失败且游标保持不变。值得注意的细节偏移量使用bigint由于文件偏移可能超出 JavaScriptnumber的安全整数范围offset参数与返回值均为bigint返回值成功时返回Effectbigint, PlatformError携带 seek 之后的新位置SeekMode只有两种取值start从文件开头定位与current从当前游标相对定位。该类型定义同样位于 packages/effect/src/FileSystem.ts并被设计为可扩展的字符串字面量联合类型。变更前后的行为差异变更前未记录于文档可从接口演化推断从变更记录现在会拒绝负位置的措辞可以推断在本次修复之前负位置的 seek 并不会被显式拦截行为取决于底层运行时的表现Node 的fs.read/fs.write等 API 在遇到负位置参数时可能抛出ERR_OUT_OF_RANGE等底层异常或产生难以预测的游标状态错误的类型归因也让调用方难以做结构化处理。变更后一旦计算结果position 0seek立即以BadArgument平台错误失败游标位置不被修改this.position维持原值保证失败是无副作用的错误被统一建模为 effect 的 typed error调用方可以通过类型系统与Effect.flip、Effect.catchTag等机制精确捕获。源码实现Node 与 Deno 的同一套校验逻辑Node含 Bun 等 Node 兼容运行时实现effect/platform-node-shared是 Node 兼容运行时的共享实现FileImpl.seek位于 packages/platform/node-shared/src/NodeFileSystem.tsseek(offset: bigint, from: FileSystem.SeekMode) { return Effect.suspend(() { const position from start ? offset : this.position offset if (position BigInt(0)) { return Effect.fail(Error.badArgument({ module: FileSystem, method: seek, description: Cannot seek before the start of the file })) } this.position position return Effect.succeed(position) }) }关键实现细节位置计算from start时直接采用传入的offsetcurrent时用当前游标this.position加上相对偏移。这里正是计算出负位置的来源越界判定position BigInt(0)即拒绝——文件偏移的下界是 0不允许负偏移无副作用失败校验通过后才执行this.position position并Effect.succeed(position)因此失败路径上游标不变延迟执行整体包裹在Effect.suspend中确保副作用只在 effect 真正执行时发生符合 effect 的惰性求值原则。Deno 实现effect/platform-deno的FileImpl.seek位于 packages/platform/deno/src/DenoFileSystem.ts逻辑与 Node 实现完全一致seek(offset: bigint, from: FileSystem.SeekMode) { return Effect.suspend(() { const position from start ? offset : this.position offset if (position BigInt(0)) { return Effect.fail(PlatformError.badArgument({ module: FileSystem, method: seek, description: Cannot seek before the start of the file })) } this.position position return Effect.succeed(position) }) }从源码结构可以看出两个运行时各自维护了一个private position: bigint字段Node 版本见 NodeFileSystem.tsDeno 版本见 DenoFileSystem.tsseek、read、write系列方法都围绕该游标字段协作。Deno 实现还额外维护了nativePosition用于与底层Deno.FsFile的原生游标同步见 DenoFileSystem.ts但seek本身的越界校验在两个平台上是同构的。BadArgument平台错误的建模与捕获BadArgument是 effect 中用于平台 API 在执行底层操作前拒绝调用方输入的标准化错误类别构造函数badArgument位于 packages/effect/src/PlatformError.tsexport const badArgument (options: { readonly module: string readonly method: string readonly description?: string | undefined readonly cause?: unknown }): PlatformError new PlatformError(new BadArgument(options))其错误类定义为 packages/effect/src/PlatformError.tsexport class BadArgument extends Data.TaggedError(BadArgument){ module: string method: string description?: string | undefined cause?: unknown } { override get message(): string { return ${this.module}.${this.method}${this.description ? : ${this.description} : } } }由此可推导出本次 seek 越界失败时的结构化信息reason._tag BadArgument可用于类型判别module FileSystem、method seekdescription Cannot seek before the start of the file格式化后的message为FileSystem.seek: Cannot seek before the start of the file。调用方的捕获示例在 effect 中捕获该错误的标准写法import { Effect, FileSystem } from effect const program Effect.gen(function*() { const fs yield* FileSystem.FileSystem const file yield* fs.open(example.bin, { flag: r }) return yield* file.seek(-10n, current) }) const result yield* Effect.either(program) if (Either.isLeft(result)) { const error result.left // error.reason._tag BadArgument // error.message FileSystem.seek: Cannot seek before the start of the file }需要强调的是offset使用bigint类型因此即使想表达当前位置往前 10 字节也必须写作-10n而非-10类型系统会拒绝后者。测试验证游标位置与错误类型仓库中的测试用例印证了 seek 行为契约Node 侧seek 后位置可被查询在 packages/platform/node-shared/test/NodeFileSystem.test.ts 中测试先把游标 seek 到maxSafeNumber.MAX_SAFE_INTEGER位置随后用file.seek(0n, current)查询当前位置断言结果等于maxSafe 1n写操作成功后游标前进 1 字节yield* file.seek(maxSafe, start) // ... assert.strictEqual(yield* file.seek(0n, current), maxSafe 1n)该用例同时验证了写入超界位置时以BadArgument失败error.reason._tag BadArgument说明BadArgument是FileSystem模块内统一的参数校验错误类别seek 的越界拒绝与写操作的越界拒绝遵循同一套错误模型。Deno 侧游标在 seek 后保持在 packages/platform/deno/test/DenoFileSystem.test.ts 中先file.seek(BigInt(4), start)将游标定位到偏移 4执行一次失败的truncate之后再用file.seek(BigInt(0), current)查询位置断言仍为BigInt(4)——证明失败操作不会破坏游标状态yield* file.seek(BigInt(4), start) // ... 触发一次调用期失败的 truncate ... position: yield* file.seek(BigInt(0), current) // 断言 position BigInt(4)这些测试表明seek既可以被当作定位游标的命令使用也可以被当作查询当前游标的只读手段seek(0n, current)而本次变更保证了它在越界时以可预测、可捕获的方式失败而不是悄悄改变游标。对现有代码的影响与适配建议虽然这是一个 patch 级变更但涉及公开 API 的返回类型收紧与新增失败路径如果你的代码直接调用了FileSystem.File.seek建议按以下清单核对检查相对 seek 的偏移符号seek(offset, current)的offset若为负且绝对值超过当前游标现在会确定性地失败而非产生底层运行时的怪异行为处理新的失败分支如果之前没有为seek编写错误处理因为它通常被认为不会失败现在需要通过Effect.either、Effect.catchTag或Effect.catchAll至少兜底BadArgument场景防止错误冒泡导致整个 effect 中断善用seek(0n, current)查询游标该调用既不会越界也不会改变位置是安全的位置查询手段利用类型收窄返回值类型统一为Effectbigint, PlatformError后模式匹配error.reason._tag BadArgument即可精确区分参数被拒绝与系统级 I/O 错误SystemError。小结本次 changeset 记录的变更虽然短小却是 effect 平台层错误建模规范化的一个典型样本通过显式的BadArgument拦截把原本依赖底层运行时表现的边界行为收敛为跨 Node/Deno 一致的、无副作用的、类型可表达的错误。从接口 JSDocpackages/effect/src/FileSystem.ts到两套平台实现NodeFileSystem.ts、DenoFileSystem.ts再到错误构造器PlatformError.ts与测试用例整个链路都贯彻了拒绝非法输入、保持状态一致、错误结构化的设计原则。升级到包含该修复的版本后FileSystem.seek的失败路径将完全处于类型系统与 effect 错误通道的掌控之中。【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询