news 2026/9/13 4:39:19

Super Productivity 同步栈包边界:@sp/sync-core、@sp/sync-providers 与 App 层的依赖治理实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Super Productivity 同步栈包边界:@sp/sync-core、@sp/sync-providers 与 App 层的依赖治理实战

Super Productivity 同步栈包边界:@sp/sync-core、@sp/sync-providers 与 App 层的依赖治理实战

【免费下载链接】super-productivitySuper Productivity is an advanced todo list app with integrated Timeboxing and time tracking capabilities. It also comes with integrations for Jira, GitLab, GitHub and Open Project.项目地址: https://gitcode.com/GitHub_Trending/su/super-productivity

Super Productivity 的操作日志同步栈(Operation Log Sync Stack)被拆分为框架无关的核心包@sp/sync-core、内置提供方包@sp/sync-providers以及负责 Angular/NgRx 域装配的src/app应用层。本文基于仓库内的边界契约文档 package-boundaries.md,结合各包的package.json导出配置、ESLint 边界规则和源码 barrel 文件,完整讲解这套"依赖方向—所有权—公共导出—隐私边界"的四段式包治理方案,帮助你在改动同步相关代码时判断代码应落在哪个包、允许引入哪些依赖、以及如何在提交前完成边界验证。

一、设计目标:框架无关与域装配分离

边界文档开宗明义:这套包拆分的目标是让可复用的同步逻辑保持框架无关(framework-agnostic),而把 Super Productivity 的域装配留在应用内。这个目标在包描述中可以直接得到印证——packages/sync-core/package.json 将自身描述为 "Framework-agnostic core types and utilities for Super Productivity sync",packages/sync-providers/package.json 则是 "Framework-agnostic sync provider contracts for Super Productivity sync"。

两者共同点:均声明"sideEffects": false以利于 tree-shaking,均使用tsup构建、vitest测试,且测试脚本都包含一个独立的类型检查步骤(test:typecheck运行tsc --noEmit -p tsconfig.spec.json)。@sp/sync-providers还把@sp/sync-core声明为peerDependencies(生产依赖中仅hash-wasm),从依赖元数据层面明确了"providers 构建于 core 之上"的关系。

这套边界并非孤立存在。它隶属于更大的同步文档体系 docs/sync-and-op-log/README.md,其中package-boundaries.md被定位为Contract(契约类)文档,与向量时钟契约 vector-clocks.md、贡献者同步模型 contributor-sync-model.md 等并列,约束的是"代码应该放在哪里"这一结构性问题。

二、依赖方向:单向链与五条硬规则

2.1 允许的依赖方向

文档给出的允许方向是一个单向链:

src/app -> @sp/sync-providers -> @sp/sync-core src/app -> @sp/sync-core packages/super-sync-server -> @sp/sync-core -> @sp/shared-schema

即:应用层可以同时依赖两个包;@sp/sync-providers只依赖@sp/sync-core;自托管服务端 packages/super-sync-server 依赖@sp/sync-core(复用向量时钟算法)和@sp/shared-schema(HTTP 契约类型与校验 schema)。

2.2 逐条规则

边界文档列出了五条必须遵守的规则:

  1. @sp/sync-core不得导入Angular、NgRx、src/app@sp/shared-schema,或任何提供方特定代码。它必须保持域无关。
  2. @sp/sync-providers只能导入@sp/sync-core的公共导出,禁止 deep-import@sp/sync-core/*、Angular、NgRx、src/app@sp/shared-schema
  3. 应用层(src/app可以同时导入两个包,并负责:Angular 依赖注入、NgRx、Electron/Capacitor 桥接、配置 UI、OAuth 路由,以及 Super Productivity 特有的模型装配。
  4. packages/shared-schema拥有应用与服务端共享的 schema 契约与校验器,它不依赖@sp/sync-core,也不应成为 core/providers 的依赖。
  5. packages/super-sync-server同时依赖@sp/shared-schema(HTTP 契约类型与校验 schema)和@sp/sync-core(向量时钟算法)。

2.3 ESLint 如何强制这些规则

这些规则不只是文档约定,仓库根部的 eslint.config.js 中对packages/sync-core/**/*.tspackages/sync-providers/**/*.ts设置了专门的import/no-restricted-paths/ 受限模块 override(约 L156–L240),错误信息直接复述了文档措辞,例如:

  • @sp/sync-core must stay domain-agnostic; shared-schema is SP-specific.
  • @sp/sync-core must not import Angular, NgRx, app code, or SP-specific schema packages.
  • @sp/sync-core must not use dynamic imports; they bypass package-boundary checks.(动态import()会绕过静态边界检查,因此被一并禁止)
  • @sp/sync-providers must use only public @sp/sync-core exports and must not import Angular, NgRx, app code, or SP-specific schema packages.

providers 的受限模式同时覆盖了@sp/sync-core/src@sp/sync-core/深路径以及**/sync-core/*形式的路径变体,确保无论是 npm 风格导入还是相对路径兜底都无法绕过。

三、所有权边界:四个包各自"拥有什么"

3.1@sp/sync-core:可复用的同步引擎原语

core 包拥有通用同步引擎的全部纯逻辑。对照其唯一公共入口 packages/sync-core/src/index.ts,文档所列所有权清单与实际导出完全对应:

  • 通用操作与 apply 类型OpTypeisMultiEntityPayloadisLwwUpdatePayloadextractActionPayloadOperationOperationLogEntry等(来自operation.types);
  • 向量时钟比较、合并与裁剪算法compareVectorClocksmergeVectorClockslimitVectorClockSizeMAX_VECTOR_CLOCK_SIZE——barrel 注释明确称其为 "single source of truth for client/server parity",即客户端与服务端共用的单一事实来源;
  • 纯函数式的冲突、导入过滤、上/下载规划、重放、压缩、前缀助手classifyOpAgainstSyncImportcreateSyncFilePrefixHelperscompressWithGzip/decompressGzipFromStringreplayOperationBatchapplyRemoteOperationsplanUploadLastServerSeqUpdateplanSnapshotHydration等;
  • 结构化的实体注册表契约EntityRegistryEntityConfig类型以及getEntityConfigisAdapterEntityisMapEntity等判定函数(entity-registry.types);
  • 面向应用的端口契约与隐私感知的SyncLogger接口ActionDispatchPortConflictUiPortOperationApplyPort等 9 个端口类型(ports),以及NOOP_SYNC_LOGGERtoSyncLogErrorsync-logger)。

core 的依赖面极窄:生产依赖只有@noble/ciphershash-wasm(服务于 Argon2id KDF + AES-GCM 加密原语),没有任何 UI 框架依赖——这正是"框架无关"承诺的直接体现。

3.2@sp/sync-providers:内置提供方实现与提供方无关契约

providers 包拥有各内置提供方的实现及跨提供方共享契约:

  • 提供方类:Dropbox、OneDrive、WebDAV(含 Nextcloud 变体)、SuperSync、LocalFile,对应源码目录 packages/sync-providers/src/file-based/ 下的dropbox/onedrive/webdav/local-file/与 packages/sync-providers/src/super-sync/;
  • 基于文件的同步信封类型与提供方响应契约file-based-sync-data.ts等);
  • 提供方拥有的文件信封常量:如sync-data.json文件名与 file-sync 版本键;
  • 端口:credential、file-adapter、platform-info、web-fetch、native-HTTP、storage、response-validator;
  • 提供方共享的错误类、PKCE 助手、重试助手与安全日志元数据助手

文档还给出了一条"放置准则":跨提供方但非引擎通用的工具放@sp/sync-providers,而非 core。现有例子包括提供方共享错误类、PKCE、重试判定谓词(retryable-upload-error.ts)、native-HTTP 重试(native-http-retry.ts)和安全日志元数据助手(log/error-meta.ts)。

3.3src/app:宿主特定的配置与编排

应用层拥有宿主特定的配置与编排逻辑,包括:

  • ActionTypeENTITY_TYPESSyncProviderId、提供方列表,以及REMOTE_FILE_CONTENT_PREFIXPRIVATE_CFG_PREFIX等存储前缀;
  • 从 feature reducers/selectors 构建实体注册表;
  • 包装后的完整状态载荷形状、导入原因、修复载荷,以及对照@sp/shared-schema的校验;
  • Angular 服务、NgRx dispatch/replay 转换、本地动作过滤、hydration 窗口、归档副作用、提供方工厂、OAuth 回调、配置对话框与平台桥接实现。

3.4packages/shared-schemapackages/super-sync-server

packages/shared-schema拥有应用与服务端共享的 Super Productivity schema 契约与校验器。文档特别强调:在这个边界内它应该保持 SP 耦合(SP-coupled),绝不应成为@sp/sync-core@sp/sync-providers的依赖。而packages/super-sync-server作为第二宿主,同时消费@sp/shared-schema(HTTP 契约)与@sp/sync-core(向量时钟算法)——core 的框架无关性正是让它能同时服务 Web 客户端与 Node 服务端两个宿主的根本原因。

四、公共导出:只从 barrel 导入

4.1 基本原则

文档要求:包的使用方只能从包 barrel 导入

import { compareVectorClocks } from '@sp/sync-core'; import { Dropbox, PROVIDER_ID_DROPBOX } from '@sp/sync-providers/dropbox';

严禁导入@sp/sync-core/src/*@sp/sync-providers/src/*dist/*等包内部路径。若宿主确实需要一个符号,应当刻意地将其提升到包 barrel,并确认该符号不属于 app 所有物。

4.2 providers 的子路径 barrel 清单

文档指出:@sp/sync-providers的根 barrel 已被移除,使用方必须从聚焦的子路径 barrel 导入。对照 packages/sync-providers/package.json 的exports字段,实际可用的子路径与文档清单一致:

子路径 barrel导出的提供方/能力
@sp/sync-providers/dropboxDropbox 提供方类
@sp/sync-providers/onedriveOneDrive 提供方类
@sp/sync-providers/webdavWebDAV/Nextcloud 提供方
@sp/sync-providers/super-syncSuperSync(有序操作 API)提供方
@sp/sync-providers/local-fileLocalFile 提供方
@sp/sync-providers/httpnative-HTTP 重试、可重试上传错误
@sp/sync-providers/errors提供方共享错误类
@sp/sync-providers/file-based文件信封类型与常量
@sp/sync-providers/pkcePKCE 助手
@sp/sync-providers/platform平台信息、web-fetch 工厂
@sp/sync-providers/provider-types提供方无关契约类型
@sp/sync-providers/credential-store凭据存储端口
@sp/sync-providers/log隐私边界安全日志助手

文档还划定了两条补充线:其一,提供方类、提供方拥有的字符串常量和隐私边界日志助手从这里导出,但应用层枚举(如SyncProviderId)不在其中——它们是 app 所有物;其二,内部助手(如 WebDAV 的 API/adapter 类)保持不导出,除非出现第二个宿主真正需要它们。这实际上是在用导出面控制包的 API 表面积增长。

4.3 弃用兼容导出的处理

文档说明@sp/sync-core仍然导出弃用的完整状态操作兼容默认值,以及宿主自定义的OpType.SyncImport/BackupImport/Repair字符串,供现有消费方使用;新的可复用宿主应通过createFullStateOpTypeHelpers()提供自己的完整状态操作字符串,而不是依赖内置默认值。在 packages/sync-core/src/index.ts(L80–L87)中可以看到对应设计:createFullStateOpTypeHelperscreateLwwUpdateActionTypeHelpers都是由宿主传入实体类型列表/操作字符串的工厂,注释明确写着 "the lib stays domain-agnostic"——core 通过"宿主注入域常量"的方式保持通用,这正是前文所有权边界的代码级落实。

五、隐私边界:SyncLogger 与可导出日志

同步包的日志必须使用SyncLogger且只携带安全的结构化元数据。实现见 packages/sync-core/src/sync-logger.ts,其文件头注释与文档逐句对应:日志历史可能被宿主应用导出(exportable),因此通过该端口传递的元数据必须限于 ID、计数、动作字符串、实体类型、op ID 和清洗后的错误标识。

关键约束可以归纳为三档:

  1. 允许:ID、计数、动作字符串、实体类型、提供方 ID、错误名/错误码。toSyncLogError()的实现印证了这一点——它只提取namecode(缺失时降级为StringError/UnknownError),从不返回message或堆栈,从 API 形状上就把自由文本挡在错误标识之外。
  2. 有条件允许:URL 元数据仅在调用方剥离查询串、fragment、凭据、token、原始响应体以及用户提供的路径段(文件名、邮箱、分享 ID、文件夹名)之后才可记录。文档建议优先使用粗粒度路径模板、提供方操作名、仅含 host 的值,或提供方拥有的相对路径类别,而非原始 URL 路径。
  3. 禁止:完整实体、操作载荷、任务标题、笔记文本、原始提供方响应、凭据、请求头、加密材料,一律不得进入可导出日志。

文档同时坦诚了执行现状:SyncLogger是隐私感知的端口形状(port shape),它本身不对任意元数据做消毒,调用点有责任传入已清洗的值;当前执行手段是代码评审加针对性测试,针对"不安全直接打日志"的 lint 规则仍是一个可能的后续项。在那之前,新增的可迁移/提供方代码应一律使用SyncLogger,测试应断言隐私敏感的 catch 路径。

六、测试如何参与边界治理

文档对测试的边界规则单独成节,要点有三:

  • ESLint 包边界 override 覆盖packages/sync-core/**packages/sync-providers/**下的所有 TypeScript 文件,包括测试
  • 测试可以通过相对路径导入自己包的内部实现(白盒覆盖所必需);
  • @sp/sync-providers的测试可以导入@sp/sync-core公共导出,但不应导入 sync-core 的内部实现或其测试助手。

从实际测试布局可以印证这种分层:packages/sync-providers/tests/ 按file-based/(dropbox、local-file、webdav 子目录)、super-sync/log/组织,与 barrel 子路径一一对应,且存在tests/helpers/credential-store.tstests/helpers/sync-logger.ts这类本包内自建的测试桩——后者正体现了"core 端口由宿主/测试自行注入实现"的端口模式。两个包的测试脚本(npm run test)均先跑test:typecheck再跑vitest run,配合根 package.json 中的聚合脚本packages:test(依次执行 sync-core、shared-schema、sync-providers 三包的测试),构成边界合规的第一道自动化防线。

七、跨边界移动代码前的验证清单

文档给出的标准验证流程是四条命令(均来自仓库根 package.json 中真实存在的脚本):

npm run lint npm run sync-core:build npm run sync-providers:build npm run packages:test

其中sync-core:build/sync-providers:build分别在对应包目录下执行tsup构建(package.json L176–L178),packages:test聚合三个包的类型检查+单测(L175)。构建通过的意义在于:exports字段锁死了子路径面,任何"想从包内偷符号"的深路径导入都会在构建期直接失败。

文档还提供了更快的边界抽查命令,用 ripgrep 在两个包的src下扫描违禁导入模式(Angular/NgRx 框架包、@sp/shared-schemasrc/app@sp/sync-core/src深路径,含动态import()形式):

rg -n "from '\"|import\('\"" packages/sync-core/src packages/sync-providers/src

该模式与 ESLint override 的受限目标互为镜像,适合作为移动代码后的即时 sanity check。

八、新增内置提供方的落地路径

文档最后给出了一条面向未来的扩展指引:新增内置提供方应遵循 Dropbox/OneDrive/WebDAV/SuperSync/LocalFile 模式——

  1. 把提供方拥有的协议逻辑与提供方无关契约放进@sp/sync-providers(新增独立子目录 + 对应的子路径 barrel);
  2. 在应用侧用薄的工厂组合 app 专有的凭据、平台桥接、校验器、OAuth 路由与 UI 配置;
  3. 如果某个提供方是 app 专有或插件提供的(而非内置),则直接在应用侧针对 provider 契约实现,不要为它扩大包表面

这条规则把"导出面即 API 承诺"的原则延伸到了扩展场景:包表面只随真正的复用需求增长,一次性宿主需求一律留在应用层。

小结

package-boundaries.md 定义的这套包边界,本质上是 Super Productivity 同步栈在"单一代码库、多个宿主(Web 应用 + 自托管服务端)"现实下的一次工程治理:@sp/sync-core用极窄依赖(仅@noble/ciphershash-wasm)与宿主注入式工厂守住框架无关性;@sp/sync-providers用 13 个子路径 barrel 与已移除的根 barrel 守住提供方 API 表面积;src/app独享 Angular/NgRx/域模型装配;@sp/shared-schema保持 SP 耦合而不被下沉包反向依赖。整套约束由 ESLint override、exports构建期锁死、packages:test聚合测试以及可复制的rg抽查命令共同执行,而SyncLogger隐私边界则把"日志可能被用户导出"这一产品事实编码进了端口设计之中。对维护者而言,本文第二节的方向图与第七节的验证清单,就是在改动任何同步相关代码之前需要过的两道门。

【免费下载链接】super-productivitySuper Productivity is an advanced todo list app with integrated Timeboxing and time tracking capabilities. It also comes with integrations for Jira, GitLab, GitHub and Open Project.项目地址: https://gitcode.com/GitHub_Trending/su/super-productivity

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 4:39:08

LunaTranslator视觉小说翻译工具:3步完成第一次游戏汉化

LunaTranslator视觉小说翻译工具:3步完成第一次游戏汉化 【免费下载链接】LunaTranslator 视觉小说翻译器 / Visual Novel Translator 项目地址: https://gitcode.com/GitHub_Trending/lu/LunaTranslator 你喜欢的视觉小说是日文的,剧情对白一闪而…

作者头像 李华
网站建设 2026/9/13 4:38:27

Windows下DeepSeek Harness一键启动:bat脚本与Docker封装实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 4:36:18

OpenClaw本地部署指南:从环境准备到金融分析配置

1. OpenClaw本地部署全景解析OpenClaw作为当前最热门的AI开发框架之一,其本地部署能力让开发者可以在私有环境中构建智能应用。不同于云端服务,本地部署提供了数据隐私保障和计算资源独占性,特别适合金融分析、企业知识库等对数据敏感的场景。…

作者头像 李华
网站建设 2026/9/13 4:35:58

Microduck:面向嵌入式边缘的轻量级Unix socket微服务运行时

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 4:33:51

reinstall 一键重装别名配置指南

reinstall 一键重装别名配置指南 【免费下载链接】reinstall 一键DD/重装脚本 (One-click reinstall OS on VPS) 项目地址: https://gitcode.com/GitHub_Trending/re/reinstall reinstall 是一键 VPS 系统重装脚本。本篇解决一个具体问题:把常用的重装命令写…

作者头像 李华