- 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
- 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.
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
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)
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.