任务:核心的 xlsx@1 解析器(上真实台架第 2 件的通用部分)

依据:docs/hub-r4-device-run.md 第 3 节第 1 条,第 8 节第 2 件;docs/hub-r4-tasks/hub-case-workbook.md 第 1 条(这一件就是那一条,拆出来先做)。

Hub 要回读 Circle 上传的工作簿,读取分两层:

  • 核心加一个通用的 xlsx@1 解析器,只给出格式上的结构,不带运行器和生成器的任何字;
  • 按运行器的写法解释单元格、判“只认生成器写的子集”,是 case-compile 包的核验检查的事,在 hub-case-workbook 里做,不在这一件。

  • 分支和工作树:xlsx-parser,目录 circle-hub-xlsx-parser(从 hub-redesign 开出,依赖已装好)
  • 可以改的路径:
    • hub/refs/parsers/xlsx.ts(新文件)和它的测试;
    • hub/refs/parsers/index.ts 里注册的一行;
    • hub/refs/parser.test.ts 里固定默认解析器清单和数量的两处断言;
    • hub/server/upload.ts 的扩展名和媒体类型表加 xlsx;
    • hub/refs/README.md 加一节。

    别的路径不动。不加新的 npm 依赖:解压用 Node 自带的 zlib(带输出上限和 CRC 校验)或已有的 fflate,XML 用现有的 xml@1 解析器。

  • 规则:同前(不推送、不建 PR、不发布、不部署;不碰 7420、钥匙串、环境配置文件和任何真实系统;测试只用合成的工作簿,在测试里现生成;git commit -s)。所有者的规矩:未知只能是过渡状态,写明缺的事实和下一步动作;Hub 只核验,不生成内容。
  • 验证:npx tsc --noEmit;node --test --test-concurrency=1 对 hub/refs/**、hub/server/**。

要做的

  1. 只因通用理由整份拒绝,并写明是哪一项:
    • zip 上限:总字节数、成员数、单个和总解压量、解压比;
    • 成员路径:不许绝对路径、..、反斜杠、NUL;
    • 加密成员、重复成员;
    • 缺工作簿部件,或关系指向不存在的部件;
    • 某个部件的 XML 被 xml@1 修复过;
    • 字节不是 zip:不被识别。
  2. 模型如实给出结构:
    • 成员清单(名字、大小、内容类型);
    • 工作表(名字、可见性);
    • 行(行号、是否隐藏);
    • 单元格(引用、类型、值文本、是否公式、样式号);
    • 共享字符串解开后的文本;
    • 定义名(名字、作用域、引用);
    • 每张表的最后一行,只有样式的单元格也算。

    样式、主题、文档属性这类部件不解释,只出现在成员清单里;宏和外部链接也只列出,从不跟随。选择器写法照其他解析器(例如 c/<表>/<单元格>、n/<定义名>),写进 README。

  3. 同样的字节给出同样的模型;解析失败时报的是确定的原因,不是未知。
  4. 测试:
    • 模型:内联和共享字符串、数字、公式标记、隐藏行、隐藏的表、只有样式的空行计入最后一行、表级和工作簿级定义名、以 = 开头的值原样作文本;
    • 拒绝:超大、解压比过大(炸弹)、成员过多、路径穿越、加密成员、重复成员、关系断链、XML 被修复过;
    • 不是 zip 的字节不被识别;
    • 两次解析的模型相同。

完成后在终端里回报:提交号、测试数量、没做完或拿不准的地方。