refactor(client): 收敛 /sidebar envelope 解析与取扩展名双实现

- api.ts:call 与 fetchUpload 里逐字相同的响应 envelope 解析
  (json().catch(null) + ok/value 判断 + SidebarApiError 构造)收敛为
  私有 readEnvelope<T>(),两处调用;判断顺序与错误码回退逻辑不变。
- 取扩展名双实现收敛:extOf 的实现迁至零依赖的 paths.ts(lang.ts 与
  service.ts 均可达的基础模块),lang.ts re-export 保持既有导入面
  (tests/file-types.spec.ts 从 lang.ts 导入 extOf);service.ts 删除
  模块私有的同体 extOfPath,matchFileViewer 改用 extOf。方向说明:
  service.ts 不能反向 import lang.ts——lang.ts 携带全部 @codemirror/*
  依赖且只经 editor 懒加载 chunk 进入浏览器,core bundle 静态引用它
  会把 CodeMirror 拉进核心包(构建产物已验证:client.js /
  client-registry.js 中 0 处 codemirror,client-editor.js 不变)。
This commit is contained in:
Menghuan1918
2026-09-03 12:05:29 +08:00
parent ad392ceea4
commit 857646ea66
4 changed files with 41 additions and 34 deletions
+5 -7
View File
@@ -48,14 +48,12 @@ import { pascal } from '@codemirror/legacy-modes/mode/pascal'
import { vb } from '@codemirror/legacy-modes/mode/vb'
import { vhdl } from '@codemirror/legacy-modes/mode/vhdl'
import { stex } from '@codemirror/legacy-modes/mode/stex'
import { extOf } from './paths.ts'
/** The lowercased file extension of a path ('' when none). */
export function extOf(path: string): string {
const at = path.lastIndexOf('.')
if (at === -1) return ''
const base = path.slice(at + 1).toLowerCase()
return base.includes('/') || base.includes('\\') ? '' : base
}
// Re-exported for the established import surface (tests import extOf from
// this module); the implementation lives in the dependency-free paths.ts so
// core-bundle modules can share it without pulling the CodeMirror packages.
export { extOf }
/** Language key for an extension, or null for plain text. Pure (tested). */
export function languageKeyForExt(ext: string): string | null {