From 038a722ed8f58f6ffeaf6955b282ec2d0c42efb2 Mon Sep 17 00:00:00 2001 From: Johannes Millan Date: Sat, 10 Jan 2026 17:09:14 +0100 Subject: [PATCH] style: apply prettier formatting --- CLAUDE.md | 1 - .../long-term-plans/e2e-encryption-plan.md | 2 - .../hybrid-manifest-architecture.md | 4 - .../operation-log-architecture.md | 8 -- .../tests/storage-quota-cleanup.spec.ts | 90 ++++++++----------- 5 files changed, 36 insertions(+), 69 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 770707ef2..cd75d8949 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -47,7 +47,6 @@ npm run test:file - Unit tests: `npm test` - Uses Jasmine/Karma, tests are co-located with source files (`.spec.ts`) - E2E tests: `npm run e2e` - Uses Playwright, located in `/e2e/tests/` - - `npm run e2e` - Run all tests with minimal output (shows failures clearly) - `npm run e2e:file ` - Run a single test file with detailed output - Example: `npm run e2e:file tests/work-view/work-view.spec.ts` diff --git a/docs/sync-and-op-log/long-term-plans/e2e-encryption-plan.md b/docs/sync-and-op-log/long-term-plans/e2e-encryption-plan.md index 13fa04538..60addd374 100644 --- a/docs/sync-and-op-log/long-term-plans/e2e-encryption-plan.md +++ b/docs/sync-and-op-log/long-term-plans/e2e-encryption-plan.md @@ -401,13 +401,11 @@ When encryption is enabled on one client: ## Testing Strategy 1. **Unit tests** for `OperationEncryptionService` - - Encrypt/decrypt round-trips with various payload types - Non-encrypted ops pass through unchanged - Wrong password throws DecryptError 2. **Integration tests** for upload/download - - Encrypted operations sync correctly - Mixed encrypted/unencrypted history works - Piggybacked operations decrypt correctly diff --git a/docs/sync-and-op-log/long-term-plans/hybrid-manifest-architecture.md b/docs/sync-and-op-log/long-term-plans/hybrid-manifest-architecture.md index eaaa4ce63..e42e98ef9 100644 --- a/docs/sync-and-op-log/long-term-plans/hybrid-manifest-architecture.md +++ b/docs/sync-and-op-log/long-term-plans/hybrid-manifest-architecture.md @@ -507,12 +507,10 @@ All phases have been implemented as of December 2025: ### ✅ Phase 1: Core Infrastructure (Complete) 1. **Types** (`operation.types.ts`): - - `HybridManifest`, `SnapshotReference`, `OperationFileReference` interfaces defined - Backward compatibility maintained with existing `OperationLogManifest` 2. **Manifest Handling** (`operation-log-manifest.service.ts`): - - `loadManifest()` handles v1 and v2 formats - Automatic v1 to v2 migration on first write - Buffer/overflow logic in upload services @@ -524,7 +522,6 @@ All phases have been implemented as of December 2025: ### ✅ Phase 2: Snapshot Support (Complete) 4. **Snapshot Operations** (in `operation-log-upload.service.ts` and `operation-log-download.service.ts`): - - Snapshot generation with current state serialization - Upload with retry logic - Download + validate + apply @@ -536,7 +533,6 @@ All phases have been implemented as of December 2025: ### ✅ Phase 3: Robustness (Complete) 6. **Concurrency Control**: - - Provider-specific revision checking (Dropbox rev, WebDAV ETag) - Retry-on-conflict logic implemented diff --git a/docs/sync-and-op-log/operation-log-architecture.md b/docs/sync-and-op-log/operation-log-architecture.md index f140cac3f..8bafae0ee 100644 --- a/docs/sync-and-op-log/operation-log-architecture.md +++ b/docs/sync-and-op-log/operation-log-architecture.md @@ -912,19 +912,16 @@ Remote Op (v1) Local Op (v2) When deploying a schema migration: 1. **Release new version with migration code** - - Add migration to `MIGRATIONS` array - Bump `CURRENT_SCHEMA_VERSION` - Migration handles both state and operations 2. **Graceful degradation period** - - Old clients continue working (they don't know about new schema) - New clients migrate incoming old ops seamlessly - Mixed-version sync works via receiver-side migration 3. **Monitoring** (future) - - Track `op.schemaVersion` distribution in server logs - Alert if many clients are > 2 versions behind @@ -988,13 +985,11 @@ export const MIGRATIONS: SchemaMigration[] = [ Before releasing any migration: 1. **Unit tests** in `schema-migration.service.spec.ts`: - - State migration correctness - Operation migration correctness - Null return for dropped operations 2. **Integration tests** in `cross-version-sync.integration.spec.ts`: - - Client A (v1) syncs with Client B (v2) - Both clients converge to same state - No data loss during migration @@ -1729,19 +1724,16 @@ When IndexedDB storage quota is exceeded, the system handles it gracefully: **Implementation** (see `operation-log.effects.ts`): 1. **Error Detection**: Catches `QuotaExceededError` including browser variants: - - Standard: `DOMException` with name `QuotaExceededError` - Firefox: `NS_ERROR_DOM_QUOTA_REACHED` - Safari (legacy): Error code 22 2. **Emergency Compaction**: Triggers `emergencyCompact()` with shorter retention: - - Normal retention: 7 days (`COMPACTION_RETENTION_MS`) - Emergency retention: 24 hours (`EMERGENCY_COMPACTION_RETENTION_MS`) - Only deletes ops that have been synced (`syncedAt` set) 3. **Circuit Breaker**: Flag `isHandlingQuotaExceeded` prevents infinite retry loops: - - If quota exceeded during retry attempt, aborts immediately - Shows error to user instead of looping forever diff --git a/packages/super-sync-server/tests/storage-quota-cleanup.spec.ts b/packages/super-sync-server/tests/storage-quota-cleanup.spec.ts index 8218c2aca..dc6a64e1c 100644 --- a/packages/super-sync-server/tests/storage-quota-cleanup.spec.ts +++ b/packages/super-sync-server/tests/storage-quota-cleanup.spec.ts @@ -286,9 +286,8 @@ describe('Storage Quota Cleanup', () => { describe('deleteOldestRestorePointAndOps', () => { it('should return failure when no restore points exist', async () => { // Import after mocks are set up - const { initSyncService, getSyncService } = await import( - '../src/sync/sync.service' - ); + const { initSyncService, getSyncService } = + await import('../src/sync/sync.service'); initSyncService(); const service = getSyncService(); @@ -305,9 +304,8 @@ describe('Storage Quota Cleanup', () => { }); it('should delete oldest restore point and all ops before it when 2+ restore points exist', async () => { - const { initSyncService, getSyncService } = await import( - '../src/sync/sync.service' - ); + const { initSyncService, getSyncService } = + await import('../src/sync/sync.service'); initSyncService(); const service = getSyncService(); @@ -330,9 +328,8 @@ describe('Storage Quota Cleanup', () => { }); it('should keep single restore point but delete ops before it', async () => { - const { initSyncService, getSyncService } = await import( - '../src/sync/sync.service' - ); + const { initSyncService, getSyncService } = + await import('../src/sync/sync.service'); initSyncService(); const service = getSyncService(); @@ -353,9 +350,8 @@ describe('Storage Quota Cleanup', () => { }); it('should return failure when restore point is at seq 1 with no ops before it', async () => { - const { initSyncService, getSyncService } = await import( - '../src/sync/sync.service' - ); + const { initSyncService, getSyncService } = + await import('../src/sync/sync.service'); initSyncService(); const service = getSyncService(); @@ -375,9 +371,8 @@ describe('Storage Quota Cleanup', () => { }); it('should handle BACKUP_IMPORT as restore point', async () => { - const { initSyncService, getSyncService } = await import( - '../src/sync/sync.service' - ); + const { initSyncService, getSyncService } = + await import('../src/sync/sync.service'); initSyncService(); const service = getSyncService(); @@ -393,9 +388,8 @@ describe('Storage Quota Cleanup', () => { }); it('should handle REPAIR as restore point', async () => { - const { initSyncService, getSyncService } = await import( - '../src/sync/sync.service' - ); + const { initSyncService, getSyncService } = + await import('../src/sync/sync.service'); initSyncService(); const service = getSyncService(); @@ -413,9 +407,8 @@ describe('Storage Quota Cleanup', () => { describe('Storage quota with auto-cleanup', () => { it('should recalculate storage when quota check fails', async () => { - const { initSyncService, getSyncService } = await import( - '../src/sync/sync.service' - ); + const { initSyncService, getSyncService } = + await import('../src/sync/sync.service'); initSyncService(); const service = getSyncService(); @@ -434,9 +427,8 @@ describe('Storage Quota Cleanup', () => { }); it('should allow upload after storage recalculation shows actual usage is lower', async () => { - const { initSyncService, getSyncService } = await import( - '../src/sync/sync.service' - ); + const { initSyncService, getSyncService } = + await import('../src/sync/sync.service'); initSyncService(); const service = getSyncService(); @@ -457,9 +449,8 @@ describe('Storage Quota Cleanup', () => { describe('freeStorageForUpload - iterative cleanup', () => { it('should return success immediately if quota is already satisfied', async () => { - const { initSyncService, getSyncService } = await import( - '../src/sync/sync.service' - ); + const { initSyncService, getSyncService } = + await import('../src/sync/sync.service'); initSyncService(); const service = getSyncService(); @@ -480,9 +471,8 @@ describe('Storage Quota Cleanup', () => { }); it('should return failure when no restore points exist and quota exceeded', async () => { - const { initSyncService, getSyncService } = await import( - '../src/sync/sync.service' - ); + const { initSyncService, getSyncService } = + await import('../src/sync/sync.service'); initSyncService(); const service = getSyncService(); @@ -507,9 +497,8 @@ describe('Storage Quota Cleanup', () => { }); it('should return failure when only one restore point exists', async () => { - const { initSyncService, getSyncService } = await import( - '../src/sync/sync.service' - ); + const { initSyncService, getSyncService } = + await import('../src/sync/sync.service'); initSyncService(); const service = getSyncService(); @@ -535,9 +524,8 @@ describe('Storage Quota Cleanup', () => { }); it('should delete multiple restore points iteratively until quota is satisfied', async () => { - const { initSyncService, getSyncService } = await import( - '../src/sync/sync.service' - ); + const { initSyncService, getSyncService } = + await import('../src/sync/sync.service'); initSyncService(); const service = getSyncService(); @@ -597,9 +585,8 @@ describe('Storage Quota Cleanup', () => { }); it('should stop when only one restore point remains even if quota still exceeded', async () => { - const { initSyncService, getSyncService } = await import( - '../src/sync/sync.service' - ); + const { initSyncService, getSyncService } = + await import('../src/sync/sync.service'); initSyncService(); const service = getSyncService(); @@ -636,9 +623,8 @@ describe('Storage Quota Cleanup', () => { }); it('should return stats even when cleanup fails', async () => { - const { initSyncService, getSyncService } = await import( - '../src/sync/sync.service' - ); + const { initSyncService, getSyncService } = + await import('../src/sync/sync.service'); initSyncService(); const service = getSyncService(); @@ -667,9 +653,8 @@ describe('Storage Quota Cleanup', () => { describe('Stale snapshot cache cleanup', () => { it('should clear snapshot cache when deleted ops include the cached snapshot seq', async () => { - const { initSyncService, getSyncService } = await import( - '../src/sync/sync.service' - ); + const { initSyncService, getSyncService } = + await import('../src/sync/sync.service'); const { prisma } = await import('../src/db'); initSyncService(); const service = getSyncService(); @@ -708,9 +693,8 @@ describe('Storage Quota Cleanup', () => { }); it('should NOT clear snapshot cache when cached seq is after deleted ops', async () => { - const { initSyncService, getSyncService } = await import( - '../src/sync/sync.service' - ); + const { initSyncService, getSyncService } = + await import('../src/sync/sync.service'); const { prisma } = await import('../src/db'); initSyncService(); const service = getSyncService(); @@ -749,9 +733,8 @@ describe('Storage Quota Cleanup', () => { }); it('should clear snapshot cache when cached seq equals deleteUpToSeq exactly', async () => { - const { initSyncService, getSyncService } = await import( - '../src/sync/sync.service' - ); + const { initSyncService, getSyncService } = + await import('../src/sync/sync.service'); const { prisma } = await import('../src/db'); initSyncService(); const service = getSyncService(); @@ -789,9 +772,8 @@ describe('Storage Quota Cleanup', () => { }); it('should not crash when no cached snapshot exists', async () => { - const { initSyncService, getSyncService } = await import( - '../src/sync/sync.service' - ); + const { initSyncService, getSyncService } = + await import('../src/sync/sync.service'); initSyncService(); const service = getSyncService();