super-productivity/docs/sync-and-op-log/package-boundaries.md
Mohamed Abdeltawab c2bf7b26a0
refactor(sync-core): drop shared-schema vector-clock compat re-export anda app enum (#8441)
* feat(sync-core): drop shared-schema vector-clock compat re-export and app enum

- Remove the shared-schema → sync-core compatibility re-export of
vector-clock types/functions; retarget app files
(vector-clock.ts,operation-log.const.ts) and the server (sync.types.ts)
to import from @sp/sync-core directly
- Add @sp/sync-core to super-sync-server/package.json deps (it was
load-bearing through the re-export)
- Convert VectorClockComparison from a bare type to an as const object +
derived type in sync-core,drop the app-side enum copy and the as cast
- Update spec imports and pa ckage-boundaries.md

* docs: remove stale shared-schema vector-clock references

- Update comments in vector-clocks.md, client vector-clock.ts, and
  server sync.types.ts to reference @sp/sync-core directly
- Remove @sp/sync-core dependency from shared-schema/package.json
- Regenerate package-lock.json to reflect the removed edge

* docs(sync): fix orphaned VectorClockComparison comment

The comment block describing VectorClockComparison was left dangling
above no declaration after the app-side enum was removed, and still
claimed 'Uses enum for client-side ergonomics'. Move it above the
re-export it documents and correct the wording.

---------

Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
2026-06-17 16:21:26 +02:00

163 lines
6.8 KiB
Markdown

# 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
```