mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-17 16:37:43 +00:00
refactor: integrate pfapi into oplog 3
This commit is contained in:
parent
db990b7018
commit
6017da13c7
53 changed files with 129 additions and 182 deletions
2
electron/electronAPI.d.ts
vendored
2
electron/electronAPI.d.ts
vendored
|
|
@ -8,7 +8,7 @@ import { JiraCfg } from '../src/app/features/issue/providers/jira/jira.model';
|
|||
import { AppDataCompleteLegacy, SyncGetRevResult } from '../src/app/imex/sync/sync.model';
|
||||
import { Task } from '../src/app/features/tasks/task.model';
|
||||
import { LocalBackupMeta } from '../src/app/imex/local-backup/local-backup.model';
|
||||
import { AppDataComplete } from '../src/app/sync/model-config';
|
||||
import { AppDataComplete } from '../src/app/op-log/model/model-config';
|
||||
import {
|
||||
PluginNodeScriptRequest,
|
||||
PluginNodeScriptResult,
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ import { inject, Injectable } from '@angular/core';
|
|||
import { AppDataCompleteLegacy } from '../../imex/sync/sync.model';
|
||||
import { T } from '../../t.const';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { isDataRepairPossible } from '../../sync/validation/is-data-repair-possible.util';
|
||||
import { getLastValidityError } from '../../sync/validation/is-related-model-data-valid';
|
||||
import { isDataRepairPossible } from '../../op-log/validation/is-data-repair-possible.util';
|
||||
import { getLastValidityError } from '../../op-log/validation/is-related-model-data-valid';
|
||||
import { IS_ELECTRON } from '../../app.constants';
|
||||
import { AppDataComplete } from '../../sync/model-config';
|
||||
import { AppDataComplete } from '../../op-log/model/model-config';
|
||||
import { Log } from '../log';
|
||||
|
||||
@Injectable({
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { ConfigFormSection, SyncConfig } from '../global-config.model';
|
|||
import { LegacySyncProvider } from '../../../imex/sync/legacy-sync-provider.model';
|
||||
import { IS_ANDROID_WEB_VIEW } from '../../../util/is-android-web-view';
|
||||
import { IS_ELECTRON } from '../../../app.constants';
|
||||
import { fileSyncDroid, fileSyncElectron } from '../../../sync/model-config';
|
||||
import { fileSyncDroid, fileSyncElectron } from '../../../op-log/model/model-config';
|
||||
import { FormlyFieldConfig } from '@ngx-formly/core';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { globalConfigReducer, initialGlobalConfigState } from './global-config.r
|
|||
import { loadAllData } from '../../../root-store/meta/load-all-data.action';
|
||||
import { GlobalConfigState } from '../global-config.model';
|
||||
import { LegacySyncProvider } from '../../../imex/sync/legacy-sync-provider.model';
|
||||
import { AppDataComplete } from '../../../sync/model-config';
|
||||
import { AppDataComplete } from '../../../op-log/model/model-config';
|
||||
|
||||
describe('GlobalConfigReducer', () => {
|
||||
describe('loadAllData action', () => {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
syncTimeTracking,
|
||||
} from './time-tracking.actions';
|
||||
import { loadAllData } from '../../../root-store/meta/load-all-data.action';
|
||||
import { AppDataComplete } from '../../../sync/model-config';
|
||||
import { AppDataComplete } from '../../../op-log/model/model-config';
|
||||
import { TaskCopy } from '../../tasks/task.model';
|
||||
import { WorkContextType } from '../../work-context/work-context.model';
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
import { createFeature, createReducer, on } from '@ngrx/store';
|
||||
import { TimeTrackingState } from '../time-tracking.model';
|
||||
import { loadAllData } from '../../../root-store/meta/load-all-data.action';
|
||||
import { AppDataComplete } from '../../../sync/model-config';
|
||||
import { AppDataComplete } from '../../../op-log/model/model-config';
|
||||
import { roundTsToMinutes } from '../../../util/round-ts-to-minutes';
|
||||
import { TODAY_TAG } from '../../tag/tag.const';
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import { MatIcon } from '@angular/material/icon';
|
|||
import { MatButton } from '@angular/material/button';
|
||||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { AppDataComplete } from '../../sync/model-config';
|
||||
import { AppDataComplete } from '../../op-log/model/model-config';
|
||||
import { BackupService } from '../../sync/backup.service';
|
||||
import { IS_ANDROID_WEB_VIEW } from '../../util/is-android-web-view';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { StateSnapshotService } from '../../sync/state-snapshot.service';
|
|||
import { BackupService } from '../../sync/backup.service';
|
||||
import { T } from '../../t.const';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { AppDataComplete } from '../../sync/model-config';
|
||||
import { AppDataComplete } from '../../op-log/model/model-config';
|
||||
import { SnackService } from '../../core/snack/snack.service';
|
||||
import { Log } from '../../core/log';
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { SnackService } from '../../core/snack/snack.service';
|
|||
import { SyncProviderId } from '../../sync/providers/provider.const';
|
||||
import { SuperSyncProvider } from '../../sync/providers/super-sync/super-sync';
|
||||
import { RestoreCapable, RestorePoint } from '../../sync/providers/provider.interface';
|
||||
import { AppDataComplete } from '../../sync/model-config';
|
||||
import { AppDataComplete } from '../../op-log/model/model-config';
|
||||
import { T } from '../../t.const';
|
||||
import { SyncProviderManager } from '../../sync/provider-manager.service';
|
||||
import { BackupService } from '../../sync/backup.service';
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ export const isArchiveAffectingAction = (action: Action): action is PersistentAc
|
|||
*
|
||||
* ## Why This Architecture
|
||||
*
|
||||
* Archive data is stored in IndexedDB (via PFAPI), not in NgRx state. Previously,
|
||||
* Archive data is stored in IndexedDB, not in NgRx state. Previously,
|
||||
* archive operations were duplicated across multiple effect files. This handler
|
||||
* consolidates all archive logic to:
|
||||
*
|
||||
|
|
@ -108,15 +108,13 @@ export class ArchiveOperationHandler {
|
|||
//
|
||||
// Some services use lazyInject() to break circular dependency chains:
|
||||
// DataInitService -> OperationLogHydratorService -> OperationApplierService
|
||||
// -> ArchiveOperationHandler -> ArchiveService/TaskArchiveService -> PfapiService
|
||||
// DataInitService also injects PfapiService directly, causing the cycle.
|
||||
// -> ArchiveOperationHandler -> ArchiveService/TaskArchiveService
|
||||
//
|
||||
// ArchiveDbAdapter is used for direct IndexedDB access to break the PfapiService
|
||||
// dependency for archive operations (_handleFlushYoungToOld, _handleLoadAllData).
|
||||
// This avoids the circular dependency while providing the same functionality.
|
||||
// ArchiveDbAdapter is used for direct IndexedDB access for archive operations
|
||||
// (_handleFlushYoungToOld, _handleLoadAllData) to avoid potential circular deps.
|
||||
//
|
||||
// Other services still use lazyInject because they have their own complex
|
||||
// dependency chains through PfapiService that would require deeper refactoring.
|
||||
// dependency chains that would require deeper refactoring.
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
private _injector = inject(Injector);
|
||||
private _archiveDbAdapter = inject(ArchiveDbAdapter);
|
||||
|
|
@ -366,7 +364,7 @@ export class ArchiveOperationHandler {
|
|||
* This operation is deterministic - given the same timestamp, it produces
|
||||
* the same result on all clients.
|
||||
*
|
||||
* Uses ArchiveDbAdapter which directly accesses IndexedDB without PFAPI's lock.
|
||||
* Uses ArchiveDbAdapter which directly accesses IndexedDB.
|
||||
*/
|
||||
private async _handleCompressArchive(action: PersistentAction): Promise<void> {
|
||||
const { oneYearAgoTimestamp } = action as ReturnType<typeof compressArchive>;
|
||||
|
|
@ -473,12 +471,12 @@ export class ArchiveOperationHandler {
|
|||
* Fixes bug where SYNC_IMPORT updated NgRx state but never persisted archive
|
||||
* data to IndexedDB on remote client, causing data loss on restart.
|
||||
*
|
||||
* @localBehavior SKIP - Archive written by PfapiService._updateModelCtrlCaches()
|
||||
* @remoteBehavior Executes - Uses ArchiveDbAdapter for direct IndexedDB access (bypasses PfapiService)
|
||||
* @localBehavior SKIP - Archive written by local backup import flow
|
||||
* @remoteBehavior Executes - Uses ArchiveDbAdapter for direct IndexedDB access
|
||||
*/
|
||||
private async _handleLoadAllData(action: PersistentAction): Promise<void> {
|
||||
if (!action.meta?.isRemote) {
|
||||
return; // Local: already written by PfapiService._updateModelCtrlCaches
|
||||
return; // Local: already written by local backup import flow
|
||||
}
|
||||
|
||||
const loadAllDataAction = action as unknown as ReturnType<typeof loadAllData>;
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import { InjectionToken } from '@angular/core';
|
|||
* - SYNC_WAIT_TIMEOUT_MS (40s) - Max wait for ongoing sync to complete
|
||||
* - INITIAL_SYNC_DELAY_MS (500ms) - Delay before triggering initial sync
|
||||
*
|
||||
* **Meta Sync Lock** (`src/app/pfapi/api/sync/meta-sync.service.ts`):
|
||||
* - LOCK_TTL_MS (5 min) - Lock TTL for distributed lock on remote metadata
|
||||
* **Meta Sync Lock** (`src/app/sync/providers/webdav/webdav.ts`):
|
||||
* - LOCK_TTL_MS (5 min) - Lock TTL for distributed lock on remote metadata (WebDAV)
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { PFLog } from '../../core/log';
|
||||
import { DecompressError } from '../errors/sync-errors';
|
||||
import { DecompressError } from '../../sync/errors/sync-errors';
|
||||
import {
|
||||
compressWithGzip,
|
||||
compressWithGzipToString,
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { CompressError, DecompressError } from '../errors/sync-errors';
|
||||
import { CompressError, DecompressError } from '../../sync/errors/sync-errors';
|
||||
import { PFLog } from '../../core/log';
|
||||
|
||||
/**
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { PFLog } from '../../core/log';
|
||||
import { JsonParseError } from '../errors/sync-errors';
|
||||
import { JsonParseError } from '../../sync/errors/sync-errors';
|
||||
import { EncryptAndCompressHandlerService } from './encrypt-and-compress-handler.service';
|
||||
import { getErrorTxt } from '../../util/get-error-text';
|
||||
|
||||
|
|
@ -1,16 +1,19 @@
|
|||
import { extractSyncFileStateFromPrefix, getSyncFilePrefix } from './sync-file-prefix';
|
||||
import {
|
||||
extractSyncFileStateFromPrefix,
|
||||
getSyncFilePrefix,
|
||||
} from '../../sync/util/sync-file-prefix';
|
||||
import { PFLog } from '../../core/log';
|
||||
import { decrypt, encrypt } from './encryption';
|
||||
import {
|
||||
DecryptError,
|
||||
DecryptNoPasswordError,
|
||||
JsonParseError,
|
||||
} from '../errors/sync-errors';
|
||||
} from '../../sync/errors/sync-errors';
|
||||
import {
|
||||
compressWithGzipToString,
|
||||
decompressGzipFromString,
|
||||
} from './compression-handler';
|
||||
import { EncryptAndCompressCfg } from '../sync.types';
|
||||
import { EncryptAndCompressCfg } from '../../sync/sync.types';
|
||||
|
||||
export class EncryptAndCompressHandlerService {
|
||||
private static readonly L = 'EncryptAndCompressHandlerService';
|
||||
|
|
@ -1,57 +1,60 @@
|
|||
import { AllModelData, ModelCfg, ModelCfgs } from './sync.types';
|
||||
import { DataRepairNotPossibleError } from './errors/sync-errors';
|
||||
import { Dropbox } from './providers/dropbox/dropbox';
|
||||
import { ProjectState } from '../features/project/project.model';
|
||||
import { MenuTreeState } from '../features/menu-tree/store/menu-tree.model';
|
||||
import { GlobalConfigState } from '../features/config/global-config.model';
|
||||
import { Reminder } from '../features/reminder/reminder.model';
|
||||
import { AllModelData, ModelCfg, ModelCfgs } from '../../sync/sync.types';
|
||||
import { DataRepairNotPossibleError } from '../../sync/errors/sync-errors';
|
||||
import { Dropbox } from '../../sync/providers/dropbox/dropbox';
|
||||
import { ProjectState } from '../../features/project/project.model';
|
||||
import { MenuTreeState } from '../../features/menu-tree/store/menu-tree.model';
|
||||
import { GlobalConfigState } from '../../features/config/global-config.model';
|
||||
import { Reminder } from '../../features/reminder/reminder.model';
|
||||
import {
|
||||
plannerInitialState,
|
||||
PlannerState,
|
||||
} from '../features/planner/store/planner.reducer';
|
||||
import { BoardsState, initialBoardsState } from '../features/boards/store/boards.reducer';
|
||||
import { NoteState } from '../features/note/note.model';
|
||||
import { IssueProviderState } from '../features/issue/issue.model';
|
||||
import { MetricState } from '../features/metric/metric.model';
|
||||
import { TaskState } from '../features/tasks/task.model';
|
||||
import { TagState } from '../features/tag/tag.model';
|
||||
import { SimpleCounterState } from '../features/simple-counter/simple-counter.model';
|
||||
import { TaskRepeatCfgState } from '../features/task-repeat-cfg/task-repeat-cfg.model';
|
||||
import { initialProjectState } from '../features/project/store/project.reducer';
|
||||
import { DEFAULT_GLOBAL_CONFIG } from '../features/config/default-global-config.const';
|
||||
import { initialNoteState } from '../features/note/store/note.reducer';
|
||||
import { issueProviderInitialState } from '../features/issue/store/issue-provider.reducer';
|
||||
import { initialMetricState } from '../features/metric/store/metric.reducer';
|
||||
import { initialTaskState } from '../features/tasks/store/task.reducer';
|
||||
import { initialTagState } from '../features/tag/store/tag.reducer';
|
||||
import { initialSimpleCounterState } from '../features/simple-counter/store/simple-counter.reducer';
|
||||
import { initialTaskRepeatCfgState } from '../features/task-repeat-cfg/store/task-repeat-cfg.reducer';
|
||||
import { DROPBOX_APP_KEY } from '../imex/sync/dropbox/dropbox.const';
|
||||
import { Webdav } from './providers/webdav/webdav';
|
||||
import { SuperSyncProvider } from './providers/super-sync/super-sync';
|
||||
import { isDataRepairPossible } from './validation/is-data-repair-possible.util';
|
||||
import { dataRepair } from './validation/data-repair';
|
||||
import { LocalFileSyncElectron } from './providers/local-file/local-file-sync-electron';
|
||||
import { IS_ELECTRON } from '../app.constants';
|
||||
import { IS_ANDROID_WEB_VIEW } from '../util/is-android-web-view';
|
||||
import { LocalFileSyncAndroid } from './providers/local-file/local-file-sync-android';
|
||||
import { environment } from '../../environments/environment';
|
||||
} from '../../features/planner/store/planner.reducer';
|
||||
import {
|
||||
BoardsState,
|
||||
initialBoardsState,
|
||||
} from '../../features/boards/store/boards.reducer';
|
||||
import { NoteState } from '../../features/note/note.model';
|
||||
import { IssueProviderState } from '../../features/issue/issue.model';
|
||||
import { MetricState } from '../../features/metric/metric.model';
|
||||
import { TaskState } from '../../features/tasks/task.model';
|
||||
import { TagState } from '../../features/tag/tag.model';
|
||||
import { SimpleCounterState } from '../../features/simple-counter/simple-counter.model';
|
||||
import { TaskRepeatCfgState } from '../../features/task-repeat-cfg/task-repeat-cfg.model';
|
||||
import { initialProjectState } from '../../features/project/store/project.reducer';
|
||||
import { DEFAULT_GLOBAL_CONFIG } from '../../features/config/default-global-config.const';
|
||||
import { initialNoteState } from '../../features/note/store/note.reducer';
|
||||
import { issueProviderInitialState } from '../../features/issue/store/issue-provider.reducer';
|
||||
import { initialMetricState } from '../../features/metric/store/metric.reducer';
|
||||
import { initialTaskState } from '../../features/tasks/store/task.reducer';
|
||||
import { initialTagState } from '../../features/tag/store/tag.reducer';
|
||||
import { initialSimpleCounterState } from '../../features/simple-counter/store/simple-counter.reducer';
|
||||
import { initialTaskRepeatCfgState } from '../../features/task-repeat-cfg/store/task-repeat-cfg.reducer';
|
||||
import { DROPBOX_APP_KEY } from '../../imex/sync/dropbox/dropbox.const';
|
||||
import { Webdav } from '../../sync/providers/webdav/webdav';
|
||||
import { SuperSyncProvider } from '../../sync/providers/super-sync/super-sync';
|
||||
import { isDataRepairPossible } from '../validation/is-data-repair-possible.util';
|
||||
import { dataRepair } from '../validation/data-repair';
|
||||
import { LocalFileSyncElectron } from '../../sync/providers/local-file/local-file-sync-electron';
|
||||
import { IS_ELECTRON } from '../../app.constants';
|
||||
import { IS_ANDROID_WEB_VIEW } from '../../util/is-android-web-view';
|
||||
import { LocalFileSyncAndroid } from '../../sync/providers/local-file/local-file-sync-android';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import {
|
||||
ArchiveModel,
|
||||
TimeTrackingState,
|
||||
} from '../features/time-tracking/time-tracking.model';
|
||||
import { initialTimeTrackingState } from '../features/time-tracking/store/time-tracking.reducer';
|
||||
import { appDataValidators, validateFull } from './validation/validation-fn';
|
||||
import { fixEntityStateConsistency } from '../util/check-fix-entity-state-consistency';
|
||||
} from '../../features/time-tracking/time-tracking.model';
|
||||
import { initialTimeTrackingState } from '../../features/time-tracking/store/time-tracking.reducer';
|
||||
import { appDataValidators, validateFull } from '../validation/validation-fn';
|
||||
import { fixEntityStateConsistency } from '../../util/check-fix-entity-state-consistency';
|
||||
import { IValidation } from 'typia';
|
||||
import { PFLog } from '../core/log';
|
||||
import { PFLog } from '../../core/log';
|
||||
import {
|
||||
initialPluginMetaDataState,
|
||||
initialPluginUserDataState,
|
||||
PluginMetaDataState,
|
||||
PluginUserDataState,
|
||||
} from '../plugins/plugin-persistence.model';
|
||||
import { menuTreeInitialState } from '../features/menu-tree/store/menu-tree.reducer';
|
||||
} from '../../plugins/plugin-persistence.model';
|
||||
import { menuTreeInitialState } from '../../features/menu-tree/store/menu-tree.reducer';
|
||||
|
||||
export const CROSS_MODEL_VERSION = 4.5 as const;
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ import {
|
|||
import { CURRENT_SCHEMA_VERSION } from './schema-migration.service';
|
||||
import { OperationLogEntry } from '../core/operation.types';
|
||||
import { OpLog } from '../../core/log';
|
||||
import { MODEL_CONFIGS } from '../../sync/model-config';
|
||||
import { MODEL_CONFIGS } from '../model/model-config';
|
||||
|
||||
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
||||
|
||||
|
|
|
|||
|
|
@ -67,9 +67,8 @@ export class OperationLogHydratorService {
|
|||
const [, , hasBackup] = await Promise.all([
|
||||
// Check for pending remote ops from crashed sync (touches 'ops' store)
|
||||
this.recoveryService.recoverPendingRemoteOps(),
|
||||
// Migrate vector clock from pf.META_MODEL to SUP_OPS.vector_clock if needed
|
||||
// (touches 'vector_clock' store). One-time migration for DB version 1 to 2.
|
||||
this._migrateVectorClockFromPfapiIfNeeded(),
|
||||
// Legacy migration placeholder - kept for future DB migrations if needed
|
||||
this._runLegacyMigrationIfNeeded(),
|
||||
// A.7.12: Check for interrupted migration (touches 'state_cache' store)
|
||||
this.opLogStore.hasStateCacheBackup(),
|
||||
]);
|
||||
|
|
@ -330,9 +329,8 @@ export class OperationLogHydratorService {
|
|||
OpLog.normal('OperationLogHydratorService: Full replay complete.');
|
||||
}
|
||||
|
||||
// Sync PFAPI vector clock with SUP_OPS to ensure consistency
|
||||
// This recovers from any failed PFAPI updates during previous operations
|
||||
await this._syncPfapiVectorClock();
|
||||
// Legacy cleanup placeholder - kept for future maintenance operations if needed
|
||||
await this._runLegacyCleanupIfNeeded();
|
||||
|
||||
// Retry any failed remote ops from previous conflict resolution attempts
|
||||
// Now that state is fully hydrated, dependencies might be resolved
|
||||
|
|
@ -518,11 +516,11 @@ export class OperationLogHydratorService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Legacy method - previously synced vector clock to PFAPI meta model.
|
||||
* Now a no-op since PFAPI layer was removed.
|
||||
* Legacy cleanup placeholder.
|
||||
* Kept for future maintenance operations if needed.
|
||||
*/
|
||||
private async _syncPfapiVectorClock(): Promise<void> {
|
||||
// No-op: PFAPI layer was removed, vector clock is managed by SUP_OPS only
|
||||
private async _runLegacyCleanupIfNeeded(): Promise<void> {
|
||||
// No-op: placeholder for future cleanup operations
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -583,10 +581,10 @@ export class OperationLogHydratorService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Legacy method - previously migrated vector clock from PFAPI meta model.
|
||||
* Now a no-op since PFAPI layer was removed.
|
||||
* Legacy migration placeholder.
|
||||
* Kept for future DB migrations if needed.
|
||||
*/
|
||||
private async _migrateVectorClockFromPfapiIfNeeded(): Promise<void> {
|
||||
// No-op: PFAPI layer was removed, no legacy migration needed
|
||||
private async _runLegacyMigrationIfNeeded(): Promise<void> {
|
||||
// No-op: placeholder for future migrations
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,13 +12,13 @@ import {
|
|||
} from './dialog-legacy-migration/dialog-legacy-migration.component';
|
||||
import { loadAllData } from '../../root-store/meta/load-all-data.action';
|
||||
import { download } from '../../util/download';
|
||||
import { validateFull } from '../../sync/validation/validation-fn';
|
||||
import { isDataRepairPossible } from '../../sync/validation/is-data-repair-possible.util';
|
||||
import { dataRepair } from '../../sync/validation/data-repair';
|
||||
import { validateFull } from '../validation/validation-fn';
|
||||
import { isDataRepairPossible } from '../validation/is-data-repair-possible.util';
|
||||
import { dataRepair } from '../validation/data-repair';
|
||||
import { uuidv7 } from '../../util/uuid-v7';
|
||||
import { ActionType, Operation, OpType } from '../core/operation.types';
|
||||
import { CURRENT_SCHEMA_VERSION } from './schema-migration.service';
|
||||
import { AppDataComplete } from '../../sync/model-config';
|
||||
import { AppDataComplete } from '../model/model-config';
|
||||
|
||||
/**
|
||||
* Service to check for valid operation log state during startup and migrate
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { Operation, OpType, ActionType } from '../core/operation.types';
|
|||
import { uuidv7 } from '../../util/uuid-v7';
|
||||
import { PENDING_OPERATION_EXPIRY_MS } from '../core/operation-log.const';
|
||||
import { OpLog } from '../../core/log';
|
||||
import { AppDataComplete } from '../../sync/model-config';
|
||||
import { AppDataComplete } from '../model/model-config';
|
||||
|
||||
/**
|
||||
* Handles crash recovery and data restoration for the operation log system.
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { Operation, OpType, ActionType } from '../core/operation.types';
|
|||
import { uuidv7 } from '../../util/uuid-v7';
|
||||
import { incrementVectorClock, mergeVectorClocks } from '../../core/util/vector-clock';
|
||||
import { OpLog } from '../../core/log';
|
||||
import { AppDataComplete } from '../../sync/model-config';
|
||||
import { AppDataComplete } from '../model/model-config';
|
||||
|
||||
/**
|
||||
* Handles hydration after remote sync downloads.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { SyncOperation } from '../../sync/providers/provider.interface';
|
|||
import { DecryptError } from '../../sync/errors/sync-errors';
|
||||
import { ActionType } from '../core/operation.types';
|
||||
import { mockEncrypt, mockDecrypt } from '../testing/helpers/mock-encryption.helper';
|
||||
import { ENCRYPT_FN, DECRYPT_FN } from '../../sync/util/encryption.token';
|
||||
import { ENCRYPT_FN, DECRYPT_FN } from '../encryption/encryption.token';
|
||||
|
||||
describe('OperationEncryptionService', () => {
|
||||
let service: OperationEncryptionService;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { inject, Injectable } from '@angular/core';
|
||||
import { ENCRYPT_FN, DECRYPT_FN } from '../../sync/util/encryption.token';
|
||||
import { ENCRYPT_FN, DECRYPT_FN } from '../encryption/encryption.token';
|
||||
import { SyncOperation } from '../../sync/providers/provider.interface';
|
||||
import { DecryptError } from '../../sync/errors/sync-errors';
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {
|
|||
ServerSyncOperation,
|
||||
SnapshotUploadResponse,
|
||||
} from '../../../../sync/providers/provider.interface';
|
||||
import { EncryptAndCompressHandlerService } from '../../../../sync/util/encrypt-and-compress-handler.service';
|
||||
import { EncryptAndCompressHandlerService } from '../../../encryption/encrypt-and-compress-handler.service';
|
||||
import { EncryptAndCompressCfg } from '../../../../sync/sync.types';
|
||||
import { CompactOperation } from '../../../../core/persistence/operation-log/compact/compact-operation.types';
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
*
|
||||
* Usage with Jasmine spyOn (recommended):
|
||||
* ```typescript
|
||||
* import * as encryptionModule from '../../sync/util/encryption';
|
||||
* import * as encryptionModule from '../../encryption/encryption';
|
||||
* import { mockEncrypt, mockDecrypt } from './mock-encryption.helper';
|
||||
*
|
||||
* beforeEach(() => {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ import { resetTestUuidCounter } from './helpers/test-client.helper';
|
|||
import { LockService } from '../../sync/lock.service';
|
||||
import { SchemaMigrationService } from '../../store/schema-migration.service';
|
||||
import { mockDecrypt, mockEncrypt } from '../helpers/mock-encryption.helper';
|
||||
import { ENCRYPT_FN, DECRYPT_FN } from '../../../sync/util/encryption.token';
|
||||
import { ENCRYPT_FN, DECRYPT_FN } from '../../encryption/encryption.token';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { SuperSyncStatusService } from '../../sync/super-sync-status.service';
|
||||
import { ServerMigrationService } from '../../sync/server-migration.service';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { AppDataComplete } from '../../sync/model-config';
|
||||
import { AppDataComplete } from '../model/model-config';
|
||||
import { IValidation } from 'typia';
|
||||
import { DEFAULT_GLOBAL_CONFIG } from '../../features/config/default-global-config.const';
|
||||
import { PFLog } from '../../core/log';
|
||||
|
|
@ -12,7 +12,7 @@ import {
|
|||
TaskRepeatCfg,
|
||||
} from '../../features/task-repeat-cfg/task-repeat-cfg.model';
|
||||
import { IssueProvider } from '../../features/issue/issue.model';
|
||||
import { AppDataComplete } from '../../sync/model-config';
|
||||
import { AppDataComplete } from '../model/model-config';
|
||||
import { dirtyDeepCopy } from '../../util/dirtyDeepCopy';
|
||||
|
||||
const FAKE_PROJECT_ID = 'FAKE_PROJECT_ID';
|
||||
|
|
@ -10,7 +10,7 @@ import { unique } from '../../util/unique';
|
|||
import { TODAY_TAG } from '../../features/tag/tag.const';
|
||||
import { TaskRepeatCfgCopy } from '../../features/task-repeat-cfg/task-repeat-cfg.model';
|
||||
import { IssueProvider } from '../../features/issue/issue.model';
|
||||
import { AppDataComplete } from '../../sync/model-config';
|
||||
import { AppDataComplete } from '../model/model-config';
|
||||
import { INBOX_PROJECT } from '../../features/project/project.const';
|
||||
import { autoFixTypiaErrors } from './auto-fix-typia-errors';
|
||||
import { IValidation } from 'typia';
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { AppDataCompleteLegacy } from '../../imex/sync/sync.model';
|
||||
import { AppDataComplete } from '../../sync/model-config';
|
||||
import { AppDataComplete } from '../model/model-config';
|
||||
|
||||
export const isDataRepairPossible = (
|
||||
data: AppDataCompleteLegacy | AppDataComplete,
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { AppDataComplete } from '../../sync/model-config';
|
||||
import { AppDataComplete } from '../model/model-config';
|
||||
import { PFLog } from '../../core/log';
|
||||
import { TODAY_TAG } from '../../features/tag/tag.const';
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { devError } from '../../util/dev-error';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { AppDataComplete } from '../../sync/model-config';
|
||||
import { AppDataComplete } from '../model/model-config';
|
||||
import { PFLog } from '../../core/log';
|
||||
import { MenuTreeKind } from '../../features/menu-tree/store/menu-tree.model';
|
||||
import { TODAY_TAG } from '../../features/tag/tag.const';
|
||||
|
|
@ -25,7 +25,7 @@ import {
|
|||
addProjectToAppData,
|
||||
addTagToAppData,
|
||||
} from './state-validity-test-utils';
|
||||
import { AppDataComplete } from '../../sync/model-config';
|
||||
import { AppDataComplete } from '../model/model-config';
|
||||
import { RootState } from '../../root-store/root-state';
|
||||
import { TaskSharedActions } from '../../root-store/meta/task-shared.actions';
|
||||
import { WorkContextType } from '../../features/work-context/work-context.model';
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
* These helpers construct valid AppDataComplete states and
|
||||
* validate them using the same validation functions used in production.
|
||||
*/
|
||||
import { AppDataComplete } from '../../sync/model-config';
|
||||
import { AppDataComplete } from '../model/model-config';
|
||||
import { validateFull } from './validation-fn';
|
||||
import { initialTaskState } from '../../features/tasks/store/task.reducer';
|
||||
import { initialProjectState } from '../../features/project/store/project.reducer';
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
import { inject, Injectable } from '@angular/core';
|
||||
import { IValidation } from 'typia';
|
||||
import { validateFull } from '../../sync/validation/validation-fn';
|
||||
import { validateFull } from './validation-fn';
|
||||
// TEMPORARILY DISABLED - repair is disabled for debugging
|
||||
// import { dataRepair } from '../../sync/validation/data-repair';
|
||||
// import { isDataRepairPossible } from '../../sync/validation/is-data-repair-possible.util';
|
||||
// import { dataRepair } from './data-repair';
|
||||
// import { isDataRepairPossible } from './is-data-repair-possible.util';
|
||||
import { RepairSummary } from '../core/operation.types';
|
||||
import { OpLog } from '../../core/log';
|
||||
// DISABLED: Repair system is non-functional
|
||||
// import { RepairOperationService } from './repair-operation.service';
|
||||
import { StateSnapshotService } from '../../sync/state-snapshot.service';
|
||||
// DISABLED: Repair system is non-functional
|
||||
// import { PfapiService } from '../../sync/sync.service';
|
||||
// import { SyncService } from '../../sync/sync.service';
|
||||
// import { loadAllData } from '../../root-store/meta/load-all-data.action';
|
||||
|
||||
/* DISABLED: Repair system helper types and functions - unused while repair is disabled
|
||||
|
|
@ -56,7 +56,7 @@ export interface ValidateAndRepairResult {
|
|||
|
||||
/**
|
||||
* Service for validating and repairing application state.
|
||||
* Wraps PFAPI's validation (Typia + cross-model) and repair functionality.
|
||||
* Wraps validation (Typia + cross-model) and repair functionality.
|
||||
*
|
||||
* Validation happens at key checkpoints:
|
||||
* - Checkpoint B: After loading snapshot during hydration
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ import { BoardsState } from '../../features/boards/store/boards.reducer';
|
|||
import { IssueProviderState } from '../../features/issue/issue.model';
|
||||
import { MetricState } from '../../features/metric/metric.model';
|
||||
import { GlobalConfigState } from '../../features/config/global-config.model';
|
||||
import { AppDataComplete } from '../../sync/model-config';
|
||||
import { ValidationResult } from '../sync.types';
|
||||
import { AppDataComplete } from '../model/model-config';
|
||||
import { ValidationResult } from '../../sync/sync.types';
|
||||
import { PFLog } from '../../core/log';
|
||||
import {
|
||||
PluginMetaDataState,
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
import { Injectable, inject } from '@angular/core';
|
||||
import { createEffect, ofType } from '@ngrx/effects';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { tap, withLatestFrom } from 'rxjs/operators';
|
||||
import {
|
||||
upsertPluginUserData,
|
||||
deletePluginUserData,
|
||||
upsertPluginMetadata,
|
||||
deletePluginMetadata,
|
||||
} from './plugin.actions';
|
||||
import { selectPluginUserDataFeatureState } from './plugin-user-data.reducer';
|
||||
import { selectPluginMetadataFeatureState } from './plugin-metadata.reducer';
|
||||
import { LOCAL_ACTIONS } from '../../util/local-actions.token';
|
||||
|
||||
/**
|
||||
* Plugin effects for persistence.
|
||||
*
|
||||
* Note: In the operation-log architecture, persistence happens automatically
|
||||
* through the operation capture meta-reducer. These effects are kept as no-ops
|
||||
* for potential future use (e.g., side effects other than persistence).
|
||||
*/
|
||||
@Injectable()
|
||||
export class PluginEffects {
|
||||
private _actions$ = inject(LOCAL_ACTIONS);
|
||||
private _store = inject(Store);
|
||||
|
||||
// Note: Persistence is now handled by the operation-log meta-reducer.
|
||||
// These effects are kept as placeholders for potential non-persistence side effects.
|
||||
|
||||
persistPluginUserData$ = createEffect(
|
||||
() =>
|
||||
this._actions$.pipe(
|
||||
ofType(upsertPluginUserData, deletePluginUserData),
|
||||
withLatestFrom(this._store.select(selectPluginUserDataFeatureState)),
|
||||
tap(([_, _state]) => {
|
||||
// No-op: Persistence handled by operation-log
|
||||
}),
|
||||
),
|
||||
{ dispatch: false },
|
||||
);
|
||||
|
||||
persistPluginMetadata$ = createEffect(
|
||||
() =>
|
||||
this._actions$.pipe(
|
||||
ofType(upsertPluginMetadata, deletePluginMetadata),
|
||||
withLatestFrom(this._store.select(selectPluginMetadataFeatureState)),
|
||||
tap(([_, _state]) => {
|
||||
// No-op: Persistence handled by operation-log
|
||||
}),
|
||||
),
|
||||
{ dispatch: false },
|
||||
);
|
||||
}
|
||||
|
|
@ -92,7 +92,6 @@ import {
|
|||
PLUGIN_METADATA_FEATURE_NAME,
|
||||
pluginMetadataReducer,
|
||||
} from '../plugins/store/plugin-metadata.reducer';
|
||||
import { PluginEffects } from '../plugins/store/plugin.effects';
|
||||
import {
|
||||
REMINDER_FEATURE_NAME,
|
||||
reminderReducer,
|
||||
|
|
@ -172,7 +171,6 @@ import {
|
|||
|
||||
StoreModule.forFeature(PLUGIN_USER_DATA_FEATURE_NAME, pluginUserDataReducer),
|
||||
StoreModule.forFeature(PLUGIN_METADATA_FEATURE_NAME, pluginMetadataReducer),
|
||||
EffectsModule.forFeature([PluginEffects]),
|
||||
StoreModule.forFeature(REMINDER_FEATURE_NAME, reminderReducer),
|
||||
StoreModule.forFeature(ARCHIVE_YOUNG_FEATURE_NAME, archiveYoungReducer),
|
||||
StoreModule.forFeature(ARCHIVE_OLD_FEATURE_NAME, archiveOldReducer),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createAction, props } from '@ngrx/store';
|
||||
import { AppDataCompleteLegacy } from '../../imex/sync/sync.model';
|
||||
import { AppDataComplete } from '../../sync/model-config';
|
||||
import { AppDataComplete } from '../../op-log/model/model-config';
|
||||
|
||||
export const loadAllData = createAction(
|
||||
'[SP_ALL] Load(import) all data',
|
||||
|
|
|
|||
|
|
@ -10,11 +10,15 @@ import { CURRENT_SCHEMA_VERSION } from '../op-log/store/schema-migration.service
|
|||
import { incrementVectorClock } from '../core/util/vector-clock';
|
||||
import { uuidv7 } from '../util/uuid-v7';
|
||||
import { loadAllData } from '../root-store/meta/load-all-data.action';
|
||||
import { validateFull } from './validation/validation-fn';
|
||||
import { dataRepair } from './validation/data-repair';
|
||||
import { isDataRepairPossible } from './validation/is-data-repair-possible.util';
|
||||
import { validateFull } from '../op-log/validation/validation-fn';
|
||||
import { dataRepair } from '../op-log/validation/data-repair';
|
||||
import { isDataRepairPossible } from '../op-log/validation/is-data-repair-possible.util';
|
||||
import { PFLog } from '../core/log';
|
||||
import { AppDataComplete, CROSS_MODEL_VERSION, AllModelConfig } from './model-config';
|
||||
import {
|
||||
AppDataComplete,
|
||||
CROSS_MODEL_VERSION,
|
||||
AllModelConfig,
|
||||
} from '../op-log/model/model-config';
|
||||
import { CompleteBackup } from './sync.types';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1155,7 +1155,7 @@ describe('SuperSyncProvider', () => {
|
|||
): Promise<{ base64Gzip: string; headers: Record<string, string>; url: string }> {
|
||||
// Instead of actually calling CapacitorHttp, return what would be sent
|
||||
const { compressWithGzipToString } = await import(
|
||||
'../../util/compression-handler'
|
||||
'../../../op-log/encryption/compression-handler'
|
||||
);
|
||||
const base64Gzip = await compressWithGzipToString(jsonPayload);
|
||||
const baseUrl = cfg.baseUrl.replace(/\/$/, '');
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import { SyncLog } from '../../../core/log';
|
|||
import {
|
||||
compressWithGzip,
|
||||
compressWithGzipToString,
|
||||
} from '../../util/compression-handler';
|
||||
} from '../../../op-log/encryption/compression-handler';
|
||||
import { IS_ANDROID_WEB_VIEW } from '../../../util/is-android-web-view';
|
||||
|
||||
const LAST_SERVER_SEQ_KEY_PREFIX = 'super_sync_last_server_seq_';
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import { selectTaskRepeatCfgFeatureState } from '../features/task-repeat-cfg/sto
|
|||
import { selectTimeTrackingState } from '../features/time-tracking/store/time-tracking.selectors';
|
||||
|
||||
import { AllSyncModels } from './sync.types';
|
||||
import { AllModelConfig } from '../sync/model-config';
|
||||
import { AllModelConfig } from '../op-log/model/model-config';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { ArchiveModel } from '../features/time-tracking/time-tracking.model';
|
||||
import { initialTimeTrackingState } from '../features/time-tracking/store/time-tracking.reducer';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
// Barrel export for sync module - replaces pfapi/api
|
||||
// This provides backward compatibility for existing imports
|
||||
// Barrel export for sync module - providers and related types
|
||||
|
||||
// Types from sync.types.ts
|
||||
export {
|
||||
|
|
@ -59,6 +58,3 @@ export { DropboxPrivateCfg } from './providers/dropbox/dropbox';
|
|||
|
||||
// VectorClock from core
|
||||
export { VectorClock } from '../core/util/vector-clock';
|
||||
|
||||
// Legacy type stubs for backward compatibility
|
||||
export type ModelCfgToModelCtrl<T> = T;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { DEFAULT_GLOBAL_CONFIG } from '../features/config/default-global-config.const';
|
||||
import { createEmptyEntity } from './create-empty-entity';
|
||||
import { AppDataComplete } from '../sync/model-config';
|
||||
import { AppDataComplete } from '../op-log/model/model-config';
|
||||
import { initialTimeTrackingState } from '../features/time-tracking/store/time-tracking.reducer';
|
||||
|
||||
export const createAppDataCompleteMock = (): AppDataComplete => ({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue