# Sync Package Boundaries **Status:** Active **Last Updated:** May 13, 2026 This note documents the package split used by the operation-log sync stack. The goal is to keep reusable sync logic framework-agnostic while leaving Super Productivity domain wiring in the app. ## Dependency Direction Allowed direction: ```text src/app -> @sp/sync-providers -> @sp/sync-core src/app -> @sp/sync-core packages/super-sync-server -> @sp/sync-core -> @sp/shared-schema ``` Rules: - `@sp/sync-core` must not import Angular, NgRx, `src/app`, `@sp/shared-schema`, `@sp/sync-providers`, or provider-specific code. - `@sp/sync-providers` may import only public `@sp/sync-core` exports. It must not deep-import `@sp/sync-core/*`, Angular, NgRx, `src/app`, or `@sp/shared-schema`. - The app may import both packages and is responsible for Angular dependency injection, NgRx, Electron/Capacitor bridges, config UI, OAuth routing, and Super Productivity-specific model wiring. - `packages/shared-schema` owns schema contracts and validators shared between app and server. It has no dependency on `@sp/sync-core`. - `packages/super-sync-server` depends on both `@sp/shared-schema` (HTTP contract types and validation schemas) and `@sp/sync-core` (vector-clock algorithms). ## Ownership `@sp/sync-core` owns reusable sync-engine primitives: - generic operation and apply types; - vector-clock compare, merge, and prune algorithms; - pure conflict, import-filter, upload/download, replay, compression, and prefix helpers; - structural entity-registry contracts; - app-facing port contracts and the privacy-aware `SyncLogger` interface. `@sp/sync-providers` owns bundled provider implementations and provider-neutral contracts: - Dropbox, WebDAV, Nextcloud, SuperSync, and LocalFile provider classes; - file-based sync envelope types and provider response contracts; - provider-owned file envelope constants such as `sync-data.json` and file-sync version keys; - credential, file-adapter, platform-info, web-fetch, native-HTTP, storage, and response-validator ports; - provider-shared error classes, PKCE helpers, retry helpers, and safe logging metadata helpers. Cross-provider utilities belong in `@sp/sync-providers` when they are reusable by provider implementations but not by the generic engine. Existing examples are provider-shared error classes, PKCE, retry predicates, native-HTTP retry, and safe log-metadata helpers. New bundled providers should follow the Dropbox/WebDAV/SuperSync/LocalFile pattern: put provider-owned protocol logic and provider-neutral contracts in `@sp/sync-providers`, then compose app-only credentials, platform bridges, validators, OAuth routing, and UI config in thin app-side factories. If a provider is app-specific or plugin-provided rather than bundled, implement it app-side against the provider contracts instead of widening the package surface. `src/app` owns host-specific configuration and choreography: - `ActionType`, `ENTITY_TYPES`, `SyncProviderId`, provider lists, and storage prefixes such as `REMOTE_FILE_CONTENT_PREFIX` and `PRIVATE_CFG_PREFIX`; - entity registry construction from feature reducers/selectors; - wrapped full-state payload shape, import reasons, repair payloads, and validation against `@sp/shared-schema`; - Angular services, NgRx dispatch/replay conversion, local-action filtering, hydration windows, archive side effects, provider factories, OAuth callbacks, config dialogs, and platform bridge implementations. `packages/shared-schema` owns Super Productivity schema contracts and validators that are shared between app and server. In this boundary it should stay SP-coupled and should not become a dependency of `@sp/sync-core` or `@sp/sync-providers`. ## Public Exports Package consumers should import from package barrels only: ```ts import { compareVectorClocks } from '@sp/sync-core'; import { Dropbox, PROVIDER_ID_DROPBOX } from '@sp/sync-providers/dropbox'; ``` Do not import from package internals such as `@sp/sync-core/src/*`, `@sp/sync-providers/src/*`, or `dist/*`. If a host needs a symbol, promote it to the package barrel deliberately and check that it is not app-owned. The root `@sp/sync-providers` barrel has been removed; consumers MUST import from focused subpath barrels: `@sp/sync-providers/dropbox`, `/webdav`, `/super-sync`, `/local-file`, `/http`, `/errors`, `/file-based`, `/pkce`, `/platform`, `/provider-types`, `/credential-store`, and `/log`. Provider classes, provider-owned string constants, and shared privacy-boundary logging helpers are exported there, but app enums such as `SyncProviderId` are not. Internal helpers such as WebDAV API/adapter classes stay unexported unless a second host needs them. `@sp/sync-core` still exports deprecated full-state op compatibility defaults and host-defined `OpType.SyncImport` / `BackupImport` / `Repair` strings for existing consumers. New reusable hosts should provide their own full-state operation strings through `createFullStateOpTypeHelpers()`. ## Privacy Boundary Package logging must use `SyncLogger` and safe structured metadata only. `SyncLogger` is a privacy-aware port shape; it does not sanitize arbitrary metadata. Call sites are responsible for passing already-scrubbed values, and enforcement is currently code review plus focused tests. IDs, counts, action strings, entity types, provider IDs, and error names/codes are acceptable. URL metadata is acceptable only after the caller strips query strings, fragments, credentials, tokens, raw response bodies, and user-provided path segments such as file names, emails, share IDs, or folder names. Prefer coarse path templates, provider operation names, host-only values, or a provider-owned relative path category over raw URL paths. Full entities, operation payloads, task titles, note text, raw provider responses, credentials, headers, and encryption material must stay out of exportable logs. A lint rule for unsafe direct logging remains a possible follow-up; until then, new movable/provider code should use `SyncLogger` and tests should assert privacy-sensitive catch paths. ## Tests The ESLint package-boundary overrides apply to all TypeScript files under `packages/sync-core/**` and `packages/sync-providers/**`, including tests. Tests may import their own package internals through relative paths for white-box coverage. `@sp/sync-providers` tests may import public `@sp/sync-core` exports, but should not import `@sp/sync-core` internals or sync-core test helpers. ## Verification Before moving code across these boundaries, run: ```bash npm run lint npm run sync-core:build npm run sync-providers:build npm run packages:test ``` For a quick boundary spot-check, use: ```bash rg -n "from ['\"](@angular|@ngrx|@sp/shared-schema|src/app|@sp/sync-core/src|@sp/sync-core/)|import\(['\"](@angular|@ngrx|@sp/shared-schema|src/app|@sp/sync-core/src|@sp/sync-core/)" packages/sync-core/src packages/sync-providers/src ```