UNPKG

@sussudio/platform

Version:

Internal APIs for VS Code's service injection the base services.

975 lines (974 loc) 35.7 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? (desc = Object.getOwnPropertyDescriptor(target, key)) : desc, d; if (typeof Reflect === 'object' && typeof Reflect.decorate === 'function') r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if ((d = decorators[i])) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); }; }; import { equals } from '@sussudio/base/common/arrays.mjs'; import { createCancelablePromise } from '@sussudio/base/common/async.mjs'; import { CancellationTokenSource } from '@sussudio/base/common/cancellation.mjs'; import { toErrorMessage } from '@sussudio/base/common/errorMessage.mjs'; import { Emitter } from '@sussudio/base/common/event.mjs'; import { Disposable, DisposableStore, toDisposable } from '@sussudio/base/common/lifecycle.mjs'; import { isEqual } from '@sussudio/base/common/resources.mjs'; import { isBoolean, isUndefined } from '@sussudio/base/common/types.mjs'; import { generateUuid } from '@sussudio/base/common/uuid.mjs'; import { IConfigurationService } from '../../configuration/common/configuration.mjs'; import { IExtensionGalleryService } from '../../extensionManagement/common/extensionManagement.mjs'; import { IInstantiationService } from '../../instantiation/common/instantiation.mjs'; import { IStorageService } from '../../storage/common/storage.mjs'; import { ITelemetryService } from '../../telemetry/common/telemetry.mjs'; import { IUserDataProfilesService } from '../../userDataProfile/common/userDataProfile.mjs'; import { ExtensionsSynchroniser } from './extensionsSync.mjs'; import { GlobalStateSynchroniser } from './globalStateSync.mjs'; import { KeybindingsSynchroniser } from './keybindingsSync.mjs'; import { SettingsSynchroniser } from './settingsSync.mjs'; import { SnippetsSynchroniser } from './snippetsSync.mjs'; import { TasksSynchroniser } from './tasksSync.mjs'; import { UserDataProfilesManifestSynchroniser } from './userDataProfilesManifestSync.mjs'; import { ALL_SYNC_RESOURCES, createSyncHeaders, IUserDataSyncEnablementService, IUserDataSyncLogService, IUserDataSyncStoreManagementService, IUserDataSyncStoreService, UserDataSyncError, UserDataSyncStoreError, USER_DATA_SYNC_CONFIGURATION_SCOPE, IUserDataSyncResourceProviderService, } from './userDataSync.mjs'; const LAST_SYNC_TIME_KEY = 'sync.lastSyncTime'; let UserDataSyncService = class UserDataSyncService extends Disposable { userDataSyncStoreService; userDataSyncStoreManagementService; instantiationService; logService; telemetryService; storageService; userDataSyncEnablementService; userDataProfilesService; userDataSyncResourceProviderService; _serviceBrand; _status = 'uninitialized' /* SyncStatus.Uninitialized */; get status() { return this._status; } _onDidChangeStatus = this._register(new Emitter()); onDidChangeStatus = this._onDidChangeStatus.event; _onDidChangeLocal = this._register(new Emitter()); onDidChangeLocal = this._onDidChangeLocal.event; _conflicts = []; get conflicts() { return this._conflicts; } _onDidChangeConflicts = this._register(new Emitter()); onDidChangeConflicts = this._onDidChangeConflicts.event; _syncErrors = []; _onSyncErrors = this._register(new Emitter()); onSyncErrors = this._onSyncErrors.event; _lastSyncTime = undefined; get lastSyncTime() { return this._lastSyncTime; } _onDidChangeLastSyncTime = this._register(new Emitter()); onDidChangeLastSyncTime = this._onDidChangeLastSyncTime.event; _onDidResetLocal = this._register(new Emitter()); onDidResetLocal = this._onDidResetLocal.event; _onDidResetRemote = this._register(new Emitter()); onDidResetRemote = this._onDidResetRemote.event; activeProfileSynchronizers = new Map(); constructor( userDataSyncStoreService, userDataSyncStoreManagementService, instantiationService, logService, telemetryService, storageService, userDataSyncEnablementService, userDataProfilesService, userDataSyncResourceProviderService, ) { super(); this.userDataSyncStoreService = userDataSyncStoreService; this.userDataSyncStoreManagementService = userDataSyncStoreManagementService; this.instantiationService = instantiationService; this.logService = logService; this.telemetryService = telemetryService; this.storageService = storageService; this.userDataSyncEnablementService = userDataSyncEnablementService; this.userDataProfilesService = userDataProfilesService; this.userDataSyncResourceProviderService = userDataSyncResourceProviderService; this._status = userDataSyncStoreManagementService.userDataSyncStore ? 'idle' /* SyncStatus.Idle */ : 'uninitialized' /* SyncStatus.Uninitialized */; this._lastSyncTime = this.storageService.getNumber( LAST_SYNC_TIME_KEY, -1 /* StorageScope.APPLICATION */, undefined, ); this._register(toDisposable(() => this.clearActiveProfileSynchronizers())); } async createSyncTask(manifest, disableCache) { this.checkEnablement(); this.logService.info('Sync started.'); const startTime = new Date().getTime(); const executionId = generateUuid(); try { const syncHeaders = createSyncHeaders(executionId); if (disableCache) { syncHeaders['Cache-Control'] = 'no-cache'; } manifest = await this.userDataSyncStoreService.manifest(manifest, syncHeaders); } catch (error) { const userDataSyncError = UserDataSyncError.toUserDataSyncError(error); reportUserDataSyncError( userDataSyncError, executionId, this.userDataSyncStoreManagementService, this.telemetryService, ); throw userDataSyncError; } const executed = false; const that = this; let cancellablePromise; return { manifest, async run() { if (executed) { throw new Error('Can run a task only once'); } cancellablePromise = createCancelablePromise((token) => that.sync(manifest, false, executionId, token)); await cancellablePromise.finally(() => (cancellablePromise = undefined)); that.logService.info(`Sync done. Took ${new Date().getTime() - startTime}ms`); that.updateLastSyncTime(); }, stop() { cancellablePromise?.cancel(); return that.stop(); }, }; } async createManualSyncTask() { this.checkEnablement(); if (this.userDataSyncEnablementService.isEnabled()) { throw new UserDataSyncError( 'Cannot start manual sync when sync is enabled', 'LocalError' /* UserDataSyncErrorCode.LocalError */, ); } this.logService.info('Sync started.'); const startTime = new Date().getTime(); const executionId = generateUuid(); const syncHeaders = createSyncHeaders(executionId); let manifest; try { manifest = await this.userDataSyncStoreService.manifest(null, syncHeaders); } catch (error) { const userDataSyncError = UserDataSyncError.toUserDataSyncError(error); reportUserDataSyncError( userDataSyncError, executionId, this.userDataSyncStoreManagementService, this.telemetryService, ); throw userDataSyncError; } /* Manual sync shall start on clean local state */ await this.resetLocal(); const that = this; const cancellableToken = new CancellationTokenSource(); return { id: executionId, async merge() { return that.sync(manifest, true, executionId, cancellableToken.token); }, async apply() { try { try { await that.applyManualSync(manifest, executionId, cancellableToken.token); } catch (error) { if ( UserDataSyncError.toUserDataSyncError(error).code === 'MethodNotFound' /* UserDataSyncErrorCode.MethodNotFound */ ) { that.logService.info('Client is making invalid requests. Cleaning up data...'); await that.cleanUpRemoteData(); that.logService.info('Applying manual sync again...'); await that.applyManualSync(manifest, executionId, cancellableToken.token); } else { throw error; } } } catch (error) { that.logService.error(error); throw error; } that.logService.info(`Sync done. Took ${new Date().getTime() - startTime}ms`); that.updateLastSyncTime(); }, async stop() { cancellableToken.cancel(); await that.stop(); await that.resetLocal(); }, }; } async sync(manifest, merge, executionId, token) { this._syncErrors = []; try { if (this.status !== 'hasConflicts' /* SyncStatus.HasConflicts */) { this.setStatus('syncing' /* SyncStatus.Syncing */); } // Sync Default Profile First const defaultProfileSynchronizer = this.getOrCreateActiveProfileSynchronizer( this.userDataProfilesService.defaultProfile, undefined, ); this._syncErrors.push( ...(await this.syncProfile(defaultProfileSynchronizer, manifest, merge, executionId, token)), ); // Sync other profiles const userDataProfileManifestSynchronizer = defaultProfileSynchronizer.enabled.find( (s) => s.resource === 'profiles' /* SyncResource.Profiles */, ); if (userDataProfileManifestSynchronizer) { const syncProfiles = (await userDataProfileManifestSynchronizer.getLastSyncedProfiles()) || []; if (token.isCancellationRequested) { return; } await this.syncRemoteProfiles(syncProfiles, manifest, merge, executionId, token); } } finally { this._onSyncErrors.fire(this._syncErrors); } } async syncRemoteProfiles(remoteProfiles, manifest, merge, executionId, token) { for (const syncProfile of remoteProfiles) { if (token.isCancellationRequested) { return; } const profile = this.userDataProfilesService.profiles.find((p) => p.id === syncProfile.id); if (!profile) { this.logService.error( `Profile with id:${syncProfile.id} and name: ${syncProfile.name} does not exist locally to sync.`, ); continue; } this.logService.info('Syncing profile.', syncProfile.name); const profileSynchronizer = this.getOrCreateActiveProfileSynchronizer(profile, syncProfile); this._syncErrors.push(...(await this.syncProfile(profileSynchronizer, manifest, merge, executionId, token))); } // Dispose & Delete profile synchronizers which do not exist anymore for (const [key, profileSynchronizerItem] of this.activeProfileSynchronizers.entries()) { if (this.userDataProfilesService.profiles.some((p) => p.id === profileSynchronizerItem[0].profile.id)) { continue; } profileSynchronizerItem[1].dispose(); this.activeProfileSynchronizers.delete(key); } } async applyManualSync(manifest, executionId, token) { const profileSynchronizers = this.getActiveProfileSynchronizers(); for (const profileSynchronizer of profileSynchronizers) { if (token.isCancellationRequested) { return; } await profileSynchronizer.apply(executionId, token); } const defaultProfileSynchronizer = profileSynchronizers.find((s) => s.profile.isDefault); if (!defaultProfileSynchronizer) { return; } const userDataProfileManifestSynchronizer = defaultProfileSynchronizer.enabled.find( (s) => s.resource === 'profiles' /* SyncResource.Profiles */, ); if (!userDataProfileManifestSynchronizer) { return; } // Sync remote profiles which are not synced locally const remoteProfiles = (await userDataProfileManifestSynchronizer.getRemoteSyncedProfiles(manifest?.latest ?? null)) || []; const remoteProfilesToSync = remoteProfiles.filter((remoteProfile) => profileSynchronizers.every((s) => s.profile.id !== remoteProfile.id), ); if (remoteProfilesToSync.length) { await this.syncRemoteProfiles(remoteProfilesToSync, manifest, false, executionId, token); } } async syncProfile(profileSynchronizer, manifest, merge, executionId, token) { const errors = await profileSynchronizer.sync(manifest, merge, executionId, token); return errors.map(([syncResource, error]) => ({ profile: profileSynchronizer.profile, syncResource, error })); } async stop() { if (this.status !== 'idle' /* SyncStatus.Idle */) { await Promise.allSettled( this.getActiveProfileSynchronizers().map((profileSynchronizer) => profileSynchronizer.stop()), ); } } async resolveContent(resource) { const content = await this.userDataSyncResourceProviderService.resolveContent(resource); if (content) { return content; } for (const profileSynchronizer of this.getActiveProfileSynchronizers()) { for (const synchronizer of profileSynchronizer.enabled) { const content = await synchronizer.resolveContent(resource); if (content) { return content; } } } return null; } async replace(syncResourceHandle) { this.checkEnablement(); const profileSyncResource = this.userDataSyncResourceProviderService.resolveUserDataSyncResource(syncResourceHandle); if (!profileSyncResource) { return; } const content = await this.resolveContent(syncResourceHandle.uri); if (!content) { return; } await this.performAction(profileSyncResource.profile, async (synchronizer) => { if (profileSyncResource.syncResource === synchronizer.resource) { await synchronizer.replace(content); return true; } return undefined; }); return; } async accept(syncResource, resource, content, apply) { this.checkEnablement(); await this.performAction(syncResource.profile, async (synchronizer) => { if (syncResource.syncResource === synchronizer.resource) { await synchronizer.accept(resource, content); if (apply) { await synchronizer.apply(isBoolean(apply) ? false : apply.force, createSyncHeaders(generateUuid())); } return true; } return undefined; }); } getRemoteProfiles() { return this.userDataSyncResourceProviderService.getRemoteSyncedProfiles(); } getRemoteSyncResourceHandles(syncResource, profile) { return this.userDataSyncResourceProviderService.getRemoteSyncResourceHandles(syncResource, profile); } async getLocalSyncResourceHandles(syncResource, profile) { return this.userDataSyncResourceProviderService.getLocalSyncResourceHandles( syncResource, profile ?? this.userDataProfilesService.defaultProfile, ); } async getAssociatedResources(syncResourceHandle) { return this.userDataSyncResourceProviderService.getAssociatedResources(syncResourceHandle); } async getMachineId(syncResourceHandle) { return this.userDataSyncResourceProviderService.getMachineId(syncResourceHandle); } async hasLocalData() { const result = await this.performAction(this.userDataProfilesService.defaultProfile, async (synchronizer) => { // skip global state synchronizer if ( synchronizer.resource !== 'globalState' /* SyncResource.GlobalState */ && (await synchronizer.hasLocalData()) ) { return true; } return undefined; }); return !!result; } async hasPreviouslySynced() { const result = await this.performAction(this.userDataProfilesService.defaultProfile, async (synchronizer) => { if (await synchronizer.hasPreviouslySynced()) { return true; } return undefined; }); return !!result; } async reset() { this.checkEnablement(); await this.resetRemote(); await this.resetLocal(); } async resetRemote() { this.checkEnablement(); try { await this.userDataSyncStoreService.clear(); this.logService.info('Cleared data on server'); } catch (e) { this.logService.error(e); } this._onDidResetRemote.fire(); } async resetLocal() { this.checkEnablement(); this._lastSyncTime = undefined; this.storageService.remove(LAST_SYNC_TIME_KEY, -1 /* StorageScope.APPLICATION */); for (const [synchronizer] of this.activeProfileSynchronizers.values()) { try { await synchronizer.resetLocal(); } catch (e) { this.logService.error(e); } } this.clearActiveProfileSynchronizers(); this._onDidResetLocal.fire(); this.logService.info('Did reset the local sync state.'); } async cleanUpRemoteData() { const remoteProfiles = await this.userDataSyncResourceProviderService.getRemoteSyncedProfiles(); const remoteProfileCollections = remoteProfiles.map((profile) => profile.collection); const allCollections = await this.userDataSyncStoreService.getAllCollections(); const redundantCollections = allCollections.filter((c) => !remoteProfileCollections.includes(c)); if (redundantCollections.length) { this.logService.info(`Deleting ${redundantCollections.length} redundant collections on server`); await Promise.allSettled( redundantCollections.map((collectionId) => this.userDataSyncStoreService.deleteCollection(collectionId)), ); this.logService.info(`Deleted redundant collections on server`); } const updatedRemoteProfiles = remoteProfiles.filter((profile) => allCollections.includes(profile.collection)); if (updatedRemoteProfiles.length !== remoteProfiles.length) { this.logService.info(`Updating remote profiles with invalid collections on server`); const profileManifestSynchronizer = this.instantiationService.createInstance( UserDataProfilesManifestSynchroniser, this.userDataProfilesService.defaultProfile, undefined, ); try { await profileManifestSynchronizer.updateRemoteProfiles(updatedRemoteProfiles, null); this.logService.info(`Updated remote profiles on server`); } finally { profileManifestSynchronizer.dispose(); } } } async performAction(profile, action) { const disposables = new DisposableStore(); try { const activeProfileSyncronizer = this.activeProfileSynchronizers.get(profile.id); if (activeProfileSyncronizer) { const result = await this.performActionWithProfileSynchronizer( activeProfileSyncronizer[0], action, disposables, ); return isUndefined(result) ? null : result; } if (profile.isDefault) { const defaultProfileSynchronizer = disposables.add( this.instantiationService.createInstance(ProfileSynchronizer, profile, undefined), ); const result = await this.performActionWithProfileSynchronizer(defaultProfileSynchronizer, action, disposables); return isUndefined(result) ? null : result; } if (this.userDataProfilesService.isEnabled()) { return null; } const userDataProfileManifestSynchronizer = disposables.add( this.instantiationService.createInstance(UserDataProfilesManifestSynchroniser, profile, undefined), ); const manifest = await this.userDataSyncStoreService.manifest(null); const syncProfiles = (await userDataProfileManifestSynchronizer.getRemoteSyncedProfiles(manifest?.latest ?? null)) || []; const syncProfile = syncProfiles.find((syncProfile) => syncProfile.id === profile.id); if (syncProfile) { const profileSynchronizer = disposables.add( this.instantiationService.createInstance(ProfileSynchronizer, profile, syncProfile.collection), ); const result = await this.performActionWithProfileSynchronizer(profileSynchronizer, action, disposables); return isUndefined(result) ? null : result; } return null; } finally { disposables.dispose(); } } async performActionWithProfileSynchronizer(profileSynchronizer, action, disposables) { const allSynchronizers = [ ...profileSynchronizer.enabled, ...profileSynchronizer.disabled.map((syncResource) => disposables.add(profileSynchronizer.createSynchronizer(syncResource)), ), ]; for (const synchronizer of allSynchronizers) { const result = await action(synchronizer); if (!isUndefined(result)) { return result; } } return undefined; } setStatus(status) { const oldStatus = this._status; if (this._status !== status) { this._status = status; this._onDidChangeStatus.fire(status); if (oldStatus === 'hasConflicts' /* SyncStatus.HasConflicts */) { this.updateLastSyncTime(); } } } updateConflicts() { const conflicts = this.getActiveProfileSynchronizers() .map((synchronizer) => synchronizer.conflicts) .flat(); if ( !equals( this._conflicts, conflicts, (a, b) => a.profile.id === b.profile.id && a.syncResource === b.syncResource && equals(a.conflicts, b.conflicts, (a, b) => isEqual(a.previewResource, b.previewResource)), ) ) { this._conflicts = conflicts; this._onDidChangeConflicts.fire(conflicts); } } updateLastSyncTime() { if (this.status === 'idle' /* SyncStatus.Idle */) { this._lastSyncTime = new Date().getTime(); this.storageService.store( LAST_SYNC_TIME_KEY, this._lastSyncTime, -1 /* StorageScope.APPLICATION */, 1 /* StorageTarget.MACHINE */, ); this._onDidChangeLastSyncTime.fire(this._lastSyncTime); } } getOrCreateActiveProfileSynchronizer(profile, syncProfile) { let activeProfileSynchronizer = this.activeProfileSynchronizers.get(profile.id); if (activeProfileSynchronizer && activeProfileSynchronizer[0].collection !== syncProfile?.collection) { this.logService.error('Profile synchronizer collection does not match with the remote sync profile collection'); activeProfileSynchronizer[1].dispose(); activeProfileSynchronizer = undefined; this.activeProfileSynchronizers.delete(profile.id); } if (!activeProfileSynchronizer) { const disposables = new DisposableStore(); const profileSynchronizer = disposables.add( this.instantiationService.createInstance(ProfileSynchronizer, profile, syncProfile?.collection), ); disposables.add(profileSynchronizer.onDidChangeStatus((e) => this.setStatus(e))); disposables.add(profileSynchronizer.onDidChangeConflicts((conflicts) => this.updateConflicts())); disposables.add(profileSynchronizer.onDidChangeLocal((e) => this._onDidChangeLocal.fire(e))); this.activeProfileSynchronizers.set(profile.id, (activeProfileSynchronizer = [profileSynchronizer, disposables])); } return activeProfileSynchronizer[0]; } getActiveProfileSynchronizers() { const profileSynchronizers = []; for (const [profileSynchronizer] of this.activeProfileSynchronizers.values()) { profileSynchronizers.push(profileSynchronizer); } return profileSynchronizers; } clearActiveProfileSynchronizers() { this.activeProfileSynchronizers.forEach(([, disposable]) => disposable.dispose()); this.activeProfileSynchronizers.clear(); } checkEnablement() { if (!this.userDataSyncStoreManagementService.userDataSyncStore) { throw new Error('Not enabled'); } } }; UserDataSyncService = __decorate( [ __param(0, IUserDataSyncStoreService), __param(1, IUserDataSyncStoreManagementService), __param(2, IInstantiationService), __param(3, IUserDataSyncLogService), __param(4, ITelemetryService), __param(5, IStorageService), __param(6, IUserDataSyncEnablementService), __param(7, IUserDataProfilesService), __param(8, IUserDataSyncResourceProviderService), ], UserDataSyncService, ); export { UserDataSyncService }; let ProfileSynchronizer = class ProfileSynchronizer extends Disposable { profile; collection; userDataSyncEnablementService; instantiationService; extensionGalleryService; userDataSyncStoreManagementService; telemetryService; logService; userDataProfilesService; configurationService; _enabled = []; get enabled() { return this._enabled.sort((a, b) => a[1] - b[1]).map(([synchronizer]) => synchronizer); } get disabled() { return ALL_SYNC_RESOURCES.filter( (syncResource) => !this.userDataSyncEnablementService.isResourceEnabled(syncResource), ); } _status = 'idle' /* SyncStatus.Idle */; get status() { return this._status; } _onDidChangeStatus = this._register(new Emitter()); onDidChangeStatus = this._onDidChangeStatus.event; _onDidChangeLocal = this._register(new Emitter()); onDidChangeLocal = this._onDidChangeLocal.event; _conflicts = []; get conflicts() { return this._conflicts; } _onDidChangeConflicts = this._register(new Emitter()); onDidChangeConflicts = this._onDidChangeConflicts.event; constructor( profile, collection, userDataSyncEnablementService, instantiationService, extensionGalleryService, userDataSyncStoreManagementService, telemetryService, logService, userDataProfilesService, configurationService, ) { super(); this.profile = profile; this.collection = collection; this.userDataSyncEnablementService = userDataSyncEnablementService; this.instantiationService = instantiationService; this.extensionGalleryService = extensionGalleryService; this.userDataSyncStoreManagementService = userDataSyncStoreManagementService; this.telemetryService = telemetryService; this.logService = logService; this.userDataProfilesService = userDataProfilesService; this.configurationService = configurationService; this._register( userDataSyncEnablementService.onDidChangeResourceEnablement(([syncResource, enablement]) => this.onDidChangeResourceEnablement(syncResource, enablement), ), ); this._register( toDisposable(() => this._enabled.splice(0, this._enabled.length).forEach(([, , disposable]) => disposable.dispose()), ), ); for (const syncResource of ALL_SYNC_RESOURCES) { if (userDataSyncEnablementService.isResourceEnabled(syncResource)) { this.registerSynchronizer(syncResource); } } } onDidChangeResourceEnablement(syncResource, enabled) { if (enabled) { this.registerSynchronizer(syncResource); } else { this.deRegisterSynchronizer(syncResource); } } registerSynchronizer(syncResource) { if (this._enabled.some(([synchronizer]) => synchronizer.resource === syncResource)) { return; } if (syncResource === 'extensions' /* SyncResource.Extensions */ && !this.extensionGalleryService.isEnabled()) { this.logService.info('Skipping extensions sync because gallery is not configured'); return; } if (syncResource === 'profiles' /* SyncResource.Profiles */) { if (!this.profile.isDefault) { return; } if (!this.userDataProfilesService.isEnabled()) { return; } } const disposables = new DisposableStore(); const synchronizer = disposables.add(this.createSynchronizer(syncResource)); disposables.add(synchronizer.onDidChangeStatus(() => this.updateStatus())); disposables.add(synchronizer.onDidChangeConflicts(() => this.updateConflicts())); disposables.add(synchronizer.onDidChangeLocal(() => this._onDidChangeLocal.fire(syncResource))); const order = this.getOrder(syncResource); this._enabled.push([synchronizer, order, disposables]); } deRegisterSynchronizer(syncResource) { const index = this._enabled.findIndex(([synchronizer]) => synchronizer.resource === syncResource); if (index !== -1) { const [[synchronizer, , disposable]] = this._enabled.splice(index, 1); disposable.dispose(); this.updateStatus(); Promise.allSettled([synchronizer.stop(), synchronizer.resetLocal()]).then(null, (error) => this.logService.error(error), ); } } createSynchronizer(syncResource) { switch (syncResource) { case 'settings' /* SyncResource.Settings */: return this.instantiationService.createInstance(SettingsSynchroniser, this.profile, this.collection); case 'keybindings' /* SyncResource.Keybindings */: return this.instantiationService.createInstance(KeybindingsSynchroniser, this.profile, this.collection); case 'snippets' /* SyncResource.Snippets */: return this.instantiationService.createInstance(SnippetsSynchroniser, this.profile, this.collection); case 'tasks' /* SyncResource.Tasks */: return this.instantiationService.createInstance(TasksSynchroniser, this.profile, this.collection); case 'globalState' /* SyncResource.GlobalState */: return this.instantiationService.createInstance(GlobalStateSynchroniser, this.profile, this.collection); case 'extensions' /* SyncResource.Extensions */: return this.instantiationService.createInstance(ExtensionsSynchroniser, this.profile, this.collection); case 'profiles' /* SyncResource.Profiles */: return this.instantiationService.createInstance( UserDataProfilesManifestSynchroniser, this.profile, this.collection, ); } } async sync(manifest, merge, executionId, token) { // Return if cancellation is requested if (token.isCancellationRequested) { return []; } const synchronizers = this.enabled; if (!synchronizers.length) { return []; } try { const syncErrors = []; const syncHeaders = createSyncHeaders(executionId); const resourceManifest = (this.collection ? manifest?.collections?.[this.collection]?.latest : manifest?.latest) ?? null; const userDataSyncConfiguration = merge ? await this.getUserDataSyncConfiguration(resourceManifest) : {}; for (const synchroniser of synchronizers) { // Return if cancellation is requested if (token.isCancellationRequested) { return []; } // Return if resource is not enabled if (!this.userDataSyncEnablementService.isResourceEnabled(synchroniser.resource)) { return []; } try { if (merge) { const preview = await synchroniser.preview(resourceManifest, userDataSyncConfiguration, syncHeaders); if (preview) { for (const resourcePreview of preview.resourcePreviews) { if ( (resourcePreview.localChange !== 0 /* Change.None */ || resourcePreview.remoteChange !== 0) /* Change.None */ && resourcePreview.mergeState === 'preview' /* MergeState.Preview */ ) { await synchroniser.merge(resourcePreview.previewResource); } } } } else { await synchroniser.sync(resourceManifest, syncHeaders); } } catch (e) { const userDataSyncError = UserDataSyncError.toUserDataSyncError(e); reportUserDataSyncError( userDataSyncError, executionId, this.userDataSyncStoreManagementService, this.telemetryService, ); if (canBailout(e)) { throw userDataSyncError; } // Log and and continue this.logService.error(e); this.logService.error(`${synchroniser.resource}: ${toErrorMessage(e)}`); syncErrors.push([synchroniser.resource, userDataSyncError]); } } return syncErrors; } finally { this.updateStatus(); } } async apply(executionId, token) { const syncHeaders = createSyncHeaders(executionId); for (const synchroniser of this.enabled) { if (token.isCancellationRequested) { return; } try { await synchroniser.apply(false, syncHeaders); } catch (e) { const userDataSyncError = UserDataSyncError.toUserDataSyncError(e); reportUserDataSyncError( userDataSyncError, executionId, this.userDataSyncStoreManagementService, this.telemetryService, ); if (canBailout(e)) { throw userDataSyncError; } // Log and and continue this.logService.error(e); this.logService.error(`${synchroniser.resource}: ${toErrorMessage(e)}`); } } } async stop() { for (const synchroniser of this.enabled) { try { if (synchroniser.status !== 'idle' /* SyncStatus.Idle */) { await synchroniser.stop(); } } catch (e) { this.logService.error(e); } } } async resetLocal() { for (const synchroniser of this.enabled) { try { await synchroniser.resetLocal(); } catch (e) { this.logService.error(`${synchroniser.resource}: ${toErrorMessage(e)}`); this.logService.error(e); } } } async getUserDataSyncConfiguration(manifest) { if (!this.profile.isDefault) { return {}; } const local = this.configurationService.getValue(USER_DATA_SYNC_CONFIGURATION_SCOPE); const settingsSynchronizer = this.enabled.find((synchronizer) => synchronizer instanceof SettingsSynchroniser); if (settingsSynchronizer) { const remote = await settingsSynchronizer.getRemoteUserDataSyncConfiguration(manifest); return { ...local, ...remote }; } return local; } setStatus(status) { if (this._status !== status) { this._status = status; this._onDidChangeStatus.fire(status); } } updateStatus() { this.updateConflicts(); if (this.enabled.some((s) => s.status === 'hasConflicts' /* SyncStatus.HasConflicts */)) { return this.setStatus('hasConflicts' /* SyncStatus.HasConflicts */); } if (this.enabled.some((s) => s.status === 'syncing' /* SyncStatus.Syncing */)) { return this.setStatus('syncing' /* SyncStatus.Syncing */); } return this.setStatus('idle' /* SyncStatus.Idle */); } updateConflicts() { const conflicts = this.enabled .filter((s) => s.status === 'hasConflicts' /* SyncStatus.HasConflicts */) .filter((s) => s.conflicts.conflicts.length > 0) .map((s) => s.conflicts); if ( !equals( this._conflicts, conflicts, (a, b) => a.syncResource === b.syncResource && equals(a.conflicts, b.conflicts, (a, b) => isEqual(a.previewResource, b.previewResource)), ) ) { this._conflicts = conflicts; this._onDidChangeConflicts.fire(conflicts); } } getOrder(syncResource) { switch (syncResource) { case 'settings' /* SyncResource.Settings */: return 0; case 'keybindings' /* SyncResource.Keybindings */: return 1; case 'snippets' /* SyncResource.Snippets */: return 2; case 'tasks' /* SyncResource.Tasks */: return 3; case 'globalState' /* SyncResource.GlobalState */: return 4; case 'extensions' /* SyncResource.Extensions */: return 5; case 'profiles' /* SyncResource.Profiles */: return 6; } } }; ProfileSynchronizer = __decorate( [ __param(2, IUserDataSyncEnablementService), __param(3, IInstantiationService), __param(4, IExtensionGalleryService), __param(5, IUserDataSyncStoreManagementService), __param(6, ITelemetryService), __param(7, IUserDataSyncLogService), __param(8, IUserDataProfilesService), __param(9, IConfigurationService), ], ProfileSynchronizer, ); function canBailout(e) { if (e instanceof UserDataSyncError) { switch (e.code) { case 'MethodNotFound' /* UserDataSyncErrorCode.MethodNotFound */: case 'TooLarge' /* UserDataSyncErrorCode.TooLarge */: case 'RemoteTooManyRequests' /* UserDataSyncErrorCode.TooManyRequests */: case 'TooManyRequestsAndRetryAfter' /* UserDataSyncErrorCode.TooManyRequestsAndRetryAfter */: case 'LocalTooManyRequests' /* UserDataSyncErrorCode.LocalTooManyRequests */: case 'LocalTooManyProfiles' /* UserDataSyncErrorCode.LocalTooManyProfiles */: case 'Gone' /* UserDataSyncErrorCode.Gone */: case 'UpgradeRequired' /* UserDataSyncErrorCode.UpgradeRequired */: case 'IncompatibleRemoteContent' /* UserDataSyncErrorCode.IncompatibleRemoteContent */: case 'IncompatibleLocalContent' /* UserDataSyncErrorCode.IncompatibleLocalContent */: return true; } } return false; } function reportUserDataSyncError(userDataSyncError, executionId, userDataSyncStoreManagementService, telemetryService) { telemetryService.publicLog2('sync/error', { code: userDataSyncError.code, serverCode: userDataSyncError instanceof UserDataSyncStoreError ? String(userDataSyncError.serverCode) : undefined, url: userDataSyncError instanceof UserDataSyncStoreError ? userDataSyncError.url : undefined, resource: userDataSyncError.resource, executionId, service: userDataSyncStoreManagementService.userDataSyncStore.url.toString(), }); }