Commit graph

6 commits

Author SHA1 Message Date
Johannes Millan
8408ac5c0e fix(lint): change hydration guard and entity registry rules from warn to error
- Change local-rules/require-hydration-guard from warn to error
- Change local-rules/require-entity-registry from warn to error
- Improve require-hydration-guard rule to skip effects with { dispatch: false }
  since they only perform side effects (audio, UI) and never dispatch actions
- Add skipWhileApplyingRemoteOps() guard to autoShowOverlay$ effect
- Add skipWhileApplyingRemoteOps() guard to triggerIdleWhenEnabled$ effect
- Add eslint-disable comments to test files that intentionally test
  unknown entity type handling
2025-12-29 22:35:40 +01:00
Johannes Millan
1da70487f9 fix: address code review findings from Dec 27-29 changes
ESLint rule improvements:
- Fix require-hydration-guard to detect guards on combineLatest/forkJoin/zip
  results (eliminates 20+ false positive warnings)

Server improvements:
- Add OperationDownloadService unit tests (21 tests covering vector clock
  aggregation, gap detection, excludeClient parameter, snapshot optimization)
- Split ZIP bomb size limits: 10MB for /ops, 30MB for /snapshot
- Document storage quota update as intentionally non-atomic

E2E test improvements:
- Add waitForUISettle() helper using Angular stability instead of fixed timeouts
- Update supersync-cross-entity tests to use dynamic waits

Unit test improvements:
- Add HydrationStateService edge case tests (concurrent calls, cooldown cycles,
  timer cleanup, interleaved operations)
2025-12-29 10:37:12 +01:00
Johannes Millan
b3022c7285 fix(sync): rename skipDuringSync to skipWhileApplyingRemoteOps
- Rename operator to be more descriptive of actual behavior
- Add deprecated alias for backwards compatibility
- Remove inner guards from effects - inject() only works during class
  initialization, not inside switchMap callbacks at runtime
- Keep outer guards only (at createEffect level)
- Update ESLint rule to accept both names
- Add comprehensive unit tests for the operator (6 tests)

Key insight: The skipWhileApplyingRemoteOps() operator uses inject()
internally, which only works within Angular's injection context (during
class field initialization when createEffect() is called). Using it
inside switchMap callbacks causes NG0203 errors at runtime.

Also: currentTaskId$ is local UI state that doesn't sync between devices.
The operation log syncs entities (tasks, projects, tags), not UI state.
Therefore distinctUntilChanged() is sufficient for filtering spurious
emissions on inner observables.
2025-12-28 18:24:36 +01:00
Johannes Millan
58372626f1 refactor(sync): centralize entity configuration in single registry
Create central entity registry to eliminate scattered configuration
across 7+ files. This reduces technical debt and makes adding new
entity types easier.

Changes:
- Create entity-registry.ts with ENTITY_CONFIGS containing all 17
  entity types with their adapters, selectors, and dependencies
- Document storage patterns (adapter, singleton, map, array, virtual)
- Migrate lww-update.meta-reducer.ts to use registry
- Migrate dependency-resolver.service.ts to use registry
- Migrate conflict-resolution.service.ts to use registry (removes
  80-line switch statement)
- Migrate validate-operation-payload.ts to use getAllPayloadKeys()
- Add ESLint rule require-entity-registry to detect missing entity
  types and typos in entityType properties

Net reduction: 112 lines of code
2025-12-23 16:15:17 +01:00
Johannes Millan
3d4bfb5637 fix(sync): improve robustness of sync and counter operations
Code review improvements addressing critical and high priority issues:

Archive Handler:
- Rollback BOTH archiveYoung and archiveOld on flush failure
- Prevents data loss when partial write occurs

Cache Invalidation:
- Add _unsyncedCache invalidation in deleteOpsWhere
- Prevents stale data when deleted ops include unsynced operations

Simple Counter:
- Extract _getCounterValue helper to reduce code duplication
- Use selectSimpleCounterById (O(1)) instead of selectAllSimpleCounters+find (O(n))
- Update tests to properly mock both selectors

Operation Log Sync:
- Add infinite loop prevention when force download returns no clocks
- Add GREATER_THAN corruption detection (treats as CONCURRENT to be safe)

ESLint Hydration Guard Rule:
- Fix combineLatest detection at root level vs nested in operator callbacks
- Add comprehensive test suite (17 test cases)

E2E Tests:
- Fix flaky reminders-schedule-page tests (tasks disappear after scheduling)
2025-12-19 11:42:56 +01:00
Johannes Millan
4367fe2109 feat(lint): add ESLint rule to enforce hydration guards on selector effects
Add local-rules/require-hydration-guard ESLint rule that warns when
selector-based NgRx effects lack hydration guards (skipDuringSync() or
isApplyingRemoteOps()).

The rule correctly identifies:
- Effects that START with this.store.select() as primary source
- Does NOT flag selectors in withLatestFrom (secondary sources)
- Does NOT flag selectors inside operator callbacks (already guarded)

This prevents duplicate operations during sync replay where selector-based
effects would fire on intermediate states.

Install eslint-plugin-local-rules to enable the rule.
2025-12-18 17:33:37 +01:00