@codingame/monaco-vscode-user-data-profile-service-override 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +31 -0
- package/userDataProfile.js +118 -0
- package/vscode/src/vs/platform/userDataProfile/browser/userDataProfile.js +95 -0
- package/vscode/src/vs/platform/userDataProfile/common/userDataProfileStorageService.js +84 -0
- package/vscode/src/vs/platform/userDataSync/common/extensionsMerge.js +331 -0
- package/vscode/src/vs/platform/userDataSync/common/extensionsSync.js +545 -0
- package/vscode/src/vs/platform/userDataSync/common/globalStateMerge.js +102 -0
- package/vscode/src/vs/platform/userDataSync/common/globalStateSync.js +431 -0
- package/vscode/src/vs/platform/userDataSync/common/keybindingsMerge.js +277 -0
- package/vscode/src/vs/platform/userDataSync/common/keybindingsSync.js +328 -0
- package/vscode/src/vs/platform/userDataSync/common/settingsSync.js +322 -0
- package/vscode/src/vs/platform/userDataSync/common/snippetsMerge.js +126 -0
- package/vscode/src/vs/platform/userDataSync/common/snippetsSync.js +478 -0
- package/vscode/src/vs/platform/userDataSync/common/tasksSync.js +245 -0
- package/vscode/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.js +9 -0
- package/vscode/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.js +495 -0
- package/vscode/src/vs/workbench/contrib/userDataProfile/browser/userDataProfileActions.js +159 -0
- package/vscode/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilePreview.js +24 -0
- package/vscode/src/vs/workbench/services/userData/browser/userDataInit.js +62 -0
- package/vscode/src/vs/workbench/services/userDataProfile/browser/extensionsResource.js +328 -0
- package/vscode/src/vs/workbench/services/userDataProfile/browser/globalStateResource.js +144 -0
- package/vscode/src/vs/workbench/services/userDataProfile/browser/keybindingsResource.js +119 -0
- package/vscode/src/vs/workbench/services/userDataProfile/browser/media/userDataProfileView.css.js +6 -0
- package/vscode/src/vs/workbench/services/userDataProfile/browser/settingsResource.js +140 -0
- package/vscode/src/vs/workbench/services/userDataProfile/browser/snippetsResource.js +155 -0
- package/vscode/src/vs/workbench/services/userDataProfile/browser/tasksResource.js +118 -0
- package/vscode/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.js +1453 -0
- package/vscode/src/vs/workbench/services/userDataProfile/browser/userDataProfileInit.js +151 -0
- package/vscode/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.js +180 -0
- package/vscode/src/vs/workbench/services/userDataProfile/browser/userDataProfileStorageService.js +39 -0
- package/vscode/src/vs/workbench/services/userDataSync/browser/userDataSyncInit.js +448 -0
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
import { __decorate, __param } from 'vscode/external/tslib/tslib.es6.js';
|
|
2
|
+
import { Promises } from 'vscode/vscode/vs/base/common/async';
|
|
3
|
+
import { CancellationToken } from 'vscode/vscode/vs/base/common/cancellation';
|
|
4
|
+
import { getErrorMessage } from 'vscode/vscode/vs/base/common/errors';
|
|
5
|
+
import { Event } from 'vscode/vscode/vs/base/common/event';
|
|
6
|
+
import { toFormattedString } from 'vscode/vscode/vs/base/common/jsonFormatter';
|
|
7
|
+
import { DisposableStore } from 'vscode/vscode/vs/base/common/lifecycle';
|
|
8
|
+
import { compare } from 'vscode/vscode/vs/base/common/strings';
|
|
9
|
+
import { IConfigurationService } from 'vscode/vscode/vs/platform/configuration/common/configuration.service';
|
|
10
|
+
import { IEnvironmentService } from 'vscode/vscode/vs/platform/environment/common/environment.service';
|
|
11
|
+
import { GlobalExtensionEnablementService } from 'vscode/vscode/vs/platform/extensionManagement/common/extensionEnablementService';
|
|
12
|
+
import { DISABLED_EXTENSIONS_STORAGE_PATH, EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT, EXTENSION_INSTALL_SYNC_CONTEXT, ExtensionManagementError, ExtensionManagementErrorCode } from 'vscode/vscode/vs/platform/extensionManagement/common/extensionManagement';
|
|
13
|
+
import { IExtensionManagementService, IExtensionGalleryService } from 'vscode/vscode/vs/platform/extensionManagement/common/extensionManagement.service';
|
|
14
|
+
import { areSameExtensions } from 'vscode/vscode/vs/platform/extensionManagement/common/extensionManagementUtil';
|
|
15
|
+
import { ExtensionStorageService } from 'vscode/vscode/vs/platform/extensionManagement/common/extensionStorage';
|
|
16
|
+
import { IExtensionStorageService } from 'vscode/vscode/vs/platform/extensionManagement/common/extensionStorage.service';
|
|
17
|
+
import { isApplicationScopedExtension } from 'vscode/vscode/vs/platform/extensions/common/extensions';
|
|
18
|
+
import { IFileService } from 'vscode/vscode/vs/platform/files/common/files.service';
|
|
19
|
+
import { IInstantiationService } from 'vscode/vscode/vs/platform/instantiation/common/instantiation';
|
|
20
|
+
import { ServiceCollection } from 'vscode/vscode/vs/platform/instantiation/common/serviceCollection';
|
|
21
|
+
import { ILogService } from 'vscode/vscode/vs/platform/log/common/log.service';
|
|
22
|
+
import { IStorageService } from 'vscode/vscode/vs/platform/storage/common/storage.service';
|
|
23
|
+
import { ITelemetryService } from 'vscode/vscode/vs/platform/telemetry/common/telemetry.service';
|
|
24
|
+
import { IUriIdentityService } from 'vscode/vscode/vs/platform/uriIdentity/common/uriIdentity.service';
|
|
25
|
+
import { IUserDataProfilesService } from 'vscode/vscode/vs/platform/userDataProfile/common/userDataProfile.service';
|
|
26
|
+
import { AbstractSynchroniser, getSyncResourceLogLabel, AbstractInitializer } from 'vscode/vscode/vs/platform/userDataSync/common/abstractSynchronizer';
|
|
27
|
+
import { merge } from './extensionsMerge.js';
|
|
28
|
+
import { IIgnoredExtensionsManagementService } from 'vscode/vscode/vs/platform/userDataSync/common/ignoredExtensions.service';
|
|
29
|
+
import { USER_DATA_SYNC_SCHEME } from 'vscode/vscode/vs/platform/userDataSync/common/userDataSync';
|
|
30
|
+
import { IUserDataSyncStoreService, IUserDataSyncLocalStoreService, IUserDataSyncLogService, IUserDataSyncEnablementService } from 'vscode/vscode/vs/platform/userDataSync/common/userDataSync.service';
|
|
31
|
+
import { IUserDataProfileStorageService } from 'vscode/vscode/vs/platform/userDataProfile/common/userDataProfileStorageService.service';
|
|
32
|
+
|
|
33
|
+
async function parseAndMigrateExtensions(syncData, extensionManagementService) {
|
|
34
|
+
const extensions = JSON.parse(syncData.content);
|
|
35
|
+
if (syncData.version === 1
|
|
36
|
+
|| syncData.version === 2) {
|
|
37
|
+
const builtinExtensions = (await extensionManagementService.getInstalled(0 )).filter(e => e.isBuiltin);
|
|
38
|
+
for (const extension of extensions) {
|
|
39
|
+
if (syncData.version === 1) {
|
|
40
|
+
if (extension.enabled === false) {
|
|
41
|
+
extension.disabled = true;
|
|
42
|
+
}
|
|
43
|
+
delete extension.enabled;
|
|
44
|
+
}
|
|
45
|
+
if (syncData.version === 2) {
|
|
46
|
+
if (builtinExtensions.every(installed => !areSameExtensions(installed.identifier, extension.identifier))) {
|
|
47
|
+
extension.installed = true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return extensions;
|
|
53
|
+
}
|
|
54
|
+
function parseExtensions(syncData) {
|
|
55
|
+
return JSON.parse(syncData.content);
|
|
56
|
+
}
|
|
57
|
+
function stringify(extensions, format) {
|
|
58
|
+
extensions.sort((e1, e2) => {
|
|
59
|
+
if (!e1.identifier.uuid && e2.identifier.uuid) {
|
|
60
|
+
return -1;
|
|
61
|
+
}
|
|
62
|
+
if (e1.identifier.uuid && !e2.identifier.uuid) {
|
|
63
|
+
return 1;
|
|
64
|
+
}
|
|
65
|
+
return compare(e1.identifier.id, e2.identifier.id);
|
|
66
|
+
});
|
|
67
|
+
return format ? toFormattedString(extensions, {}) : JSON.stringify(extensions);
|
|
68
|
+
}
|
|
69
|
+
let ExtensionsSynchroniser = class ExtensionsSynchroniser extends AbstractSynchroniser {
|
|
70
|
+
constructor(
|
|
71
|
+
profile, collection, environmentService, fileService, storageService, userDataSyncStoreService, userDataSyncLocalStoreService, extensionManagementService, ignoredExtensionsManagementService, logService, configurationService, userDataSyncEnablementService, telemetryService, extensionStorageService, uriIdentityService, userDataProfileStorageService, instantiationService) {
|
|
72
|
+
super({ syncResource: "extensions" , profile }, collection, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncLocalStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService, uriIdentityService);
|
|
73
|
+
this.extensionManagementService = extensionManagementService;
|
|
74
|
+
this.ignoredExtensionsManagementService = ignoredExtensionsManagementService;
|
|
75
|
+
this.instantiationService = instantiationService;
|
|
76
|
+
this.version = 6;
|
|
77
|
+
this.previewResource = this.extUri.joinPath(this.syncPreviewFolder, 'extensions.json');
|
|
78
|
+
this.baseResource = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'base' });
|
|
79
|
+
this.localResource = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local' });
|
|
80
|
+
this.remoteResource = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' });
|
|
81
|
+
this.acceptedResource = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' });
|
|
82
|
+
this.localExtensionsProvider = this.instantiationService.createInstance(LocalExtensionsProvider);
|
|
83
|
+
this._register(Event.any(Event.filter(this.extensionManagementService.onDidInstallExtensions, (e => ( e.some(({ local }) => !!local)))), Event.filter(this.extensionManagementService.onDidUninstallExtension, (e => !e.error)), Event.filter(userDataProfileStorageService.onDidChange, e => ( e.valueChanges.some(
|
|
84
|
+
({ profile, changes }) => this.syncResource.profile.id === profile.id && ( changes.some(change => change.key === DISABLED_EXTENSIONS_STORAGE_PATH))
|
|
85
|
+
))), extensionStorageService.onDidChangeExtensionStorageToSync)(() => this.triggerLocalChange()));
|
|
86
|
+
}
|
|
87
|
+
async generateSyncPreview(remoteUserData, lastSyncUserData) {
|
|
88
|
+
const remoteExtensions = remoteUserData.syncData ? await parseAndMigrateExtensions(remoteUserData.syncData, this.extensionManagementService) : null;
|
|
89
|
+
const skippedExtensions = lastSyncUserData?.skippedExtensions ?? [];
|
|
90
|
+
const builtinExtensions = lastSyncUserData?.builtinExtensions ?? null;
|
|
91
|
+
const lastSyncExtensions = lastSyncUserData?.syncData ? await parseAndMigrateExtensions(lastSyncUserData.syncData, this.extensionManagementService) : null;
|
|
92
|
+
const { localExtensions, ignoredExtensions } = await this.localExtensionsProvider.getLocalExtensions(this.syncResource.profile);
|
|
93
|
+
if (remoteExtensions) {
|
|
94
|
+
this.logService.trace(`${this.syncResourceLogLabel}: Merging remote extensions with local extensions...`);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
this.logService.trace(`${this.syncResourceLogLabel}: Remote extensions does not exist. Synchronizing extensions for the first time.`);
|
|
98
|
+
}
|
|
99
|
+
const { local, remote } = merge(localExtensions, remoteExtensions, lastSyncExtensions, skippedExtensions, ignoredExtensions, builtinExtensions);
|
|
100
|
+
const previewResult = {
|
|
101
|
+
local, remote,
|
|
102
|
+
content: this.getPreviewContent(localExtensions, local.added, local.updated, local.removed),
|
|
103
|
+
localChange: local.added.length > 0 || local.removed.length > 0 || local.updated.length > 0 ? 2 : 0 ,
|
|
104
|
+
remoteChange: remote !== null ? 2 : 0 ,
|
|
105
|
+
};
|
|
106
|
+
const localContent = this.stringify(localExtensions, false);
|
|
107
|
+
return [{
|
|
108
|
+
skippedExtensions,
|
|
109
|
+
builtinExtensions,
|
|
110
|
+
baseResource: this.baseResource,
|
|
111
|
+
baseContent: lastSyncExtensions ? this.stringify(lastSyncExtensions, false) : localContent,
|
|
112
|
+
localResource: this.localResource,
|
|
113
|
+
localContent,
|
|
114
|
+
localExtensions,
|
|
115
|
+
remoteResource: this.remoteResource,
|
|
116
|
+
remoteExtensions,
|
|
117
|
+
remoteContent: remoteExtensions ? this.stringify(remoteExtensions, false) : null,
|
|
118
|
+
previewResource: this.previewResource,
|
|
119
|
+
previewResult,
|
|
120
|
+
localChange: previewResult.localChange,
|
|
121
|
+
remoteChange: previewResult.remoteChange,
|
|
122
|
+
acceptedResource: this.acceptedResource,
|
|
123
|
+
}];
|
|
124
|
+
}
|
|
125
|
+
async hasRemoteChanged(lastSyncUserData) {
|
|
126
|
+
const lastSyncExtensions = lastSyncUserData.syncData ? await parseAndMigrateExtensions(lastSyncUserData.syncData, this.extensionManagementService) : null;
|
|
127
|
+
const { localExtensions, ignoredExtensions } = await this.localExtensionsProvider.getLocalExtensions(this.syncResource.profile);
|
|
128
|
+
const { remote } = merge(localExtensions, lastSyncExtensions, lastSyncExtensions, lastSyncUserData.skippedExtensions || [], ignoredExtensions, lastSyncUserData.builtinExtensions || []);
|
|
129
|
+
return remote !== null;
|
|
130
|
+
}
|
|
131
|
+
getPreviewContent(localExtensions, added, updated, removed) {
|
|
132
|
+
const preview = [...added, ...updated];
|
|
133
|
+
const idsOrUUIDs = ( new Set());
|
|
134
|
+
const addIdentifier = (identifier) => {
|
|
135
|
+
idsOrUUIDs.add(identifier.id.toLowerCase());
|
|
136
|
+
if (identifier.uuid) {
|
|
137
|
+
idsOrUUIDs.add(identifier.uuid);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
preview.forEach(({ identifier }) => addIdentifier(identifier));
|
|
141
|
+
removed.forEach(addIdentifier);
|
|
142
|
+
for (const localExtension of localExtensions) {
|
|
143
|
+
if (( idsOrUUIDs.has(localExtension.identifier.id.toLowerCase())) || (localExtension.identifier.uuid && ( idsOrUUIDs.has(localExtension.identifier.uuid)))) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
preview.push(localExtension);
|
|
147
|
+
}
|
|
148
|
+
return this.stringify(preview, false);
|
|
149
|
+
}
|
|
150
|
+
async getMergeResult(resourcePreview, token) {
|
|
151
|
+
return { ...resourcePreview.previewResult, hasConflicts: false };
|
|
152
|
+
}
|
|
153
|
+
async getAcceptResult(resourcePreview, resource, content, token) {
|
|
154
|
+
if (this.extUri.isEqual(resource, this.localResource)) {
|
|
155
|
+
return this.acceptLocal(resourcePreview);
|
|
156
|
+
}
|
|
157
|
+
if (this.extUri.isEqual(resource, this.remoteResource)) {
|
|
158
|
+
return this.acceptRemote(resourcePreview);
|
|
159
|
+
}
|
|
160
|
+
if (this.extUri.isEqual(resource, this.previewResource)) {
|
|
161
|
+
return resourcePreview.previewResult;
|
|
162
|
+
}
|
|
163
|
+
throw ( new Error(`Invalid Resource: ${( resource.toString())}`));
|
|
164
|
+
}
|
|
165
|
+
async acceptLocal(resourcePreview) {
|
|
166
|
+
const installedExtensions = await this.extensionManagementService.getInstalled(undefined, this.syncResource.profile.extensionsResource);
|
|
167
|
+
const ignoredExtensions = this.ignoredExtensionsManagementService.getIgnoredExtensions(installedExtensions);
|
|
168
|
+
const mergeResult = merge(resourcePreview.localExtensions, null, null, resourcePreview.skippedExtensions, ignoredExtensions, resourcePreview.builtinExtensions);
|
|
169
|
+
const { local, remote } = mergeResult;
|
|
170
|
+
return {
|
|
171
|
+
content: resourcePreview.localContent,
|
|
172
|
+
local,
|
|
173
|
+
remote,
|
|
174
|
+
localChange: local.added.length > 0 || local.removed.length > 0 || local.updated.length > 0 ? 2 : 0 ,
|
|
175
|
+
remoteChange: remote !== null ? 2 : 0 ,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
async acceptRemote(resourcePreview) {
|
|
179
|
+
const installedExtensions = await this.extensionManagementService.getInstalled(undefined, this.syncResource.profile.extensionsResource);
|
|
180
|
+
const ignoredExtensions = this.ignoredExtensionsManagementService.getIgnoredExtensions(installedExtensions);
|
|
181
|
+
const remoteExtensions = resourcePreview.remoteContent ? JSON.parse(resourcePreview.remoteContent) : null;
|
|
182
|
+
if (remoteExtensions !== null) {
|
|
183
|
+
const mergeResult = merge(resourcePreview.localExtensions, remoteExtensions, resourcePreview.localExtensions, [], ignoredExtensions, resourcePreview.builtinExtensions);
|
|
184
|
+
const { local, remote } = mergeResult;
|
|
185
|
+
return {
|
|
186
|
+
content: resourcePreview.remoteContent,
|
|
187
|
+
local,
|
|
188
|
+
remote,
|
|
189
|
+
localChange: local.added.length > 0 || local.removed.length > 0 || local.updated.length > 0 ? 2 : 0 ,
|
|
190
|
+
remoteChange: remote !== null ? 2 : 0 ,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
return {
|
|
195
|
+
content: resourcePreview.remoteContent,
|
|
196
|
+
local: { added: [], removed: [], updated: [] },
|
|
197
|
+
remote: null,
|
|
198
|
+
localChange: 0 ,
|
|
199
|
+
remoteChange: 0 ,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async applyResult(remoteUserData, lastSyncUserData, resourcePreviews, force) {
|
|
204
|
+
let { skippedExtensions, builtinExtensions, localExtensions } = resourcePreviews[0][0];
|
|
205
|
+
const { local, remote, localChange, remoteChange } = resourcePreviews[0][1];
|
|
206
|
+
if (localChange === 0 && remoteChange === 0 ) {
|
|
207
|
+
this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing extensions.`);
|
|
208
|
+
}
|
|
209
|
+
if (localChange !== 0 ) {
|
|
210
|
+
await this.backupLocal(JSON.stringify(localExtensions));
|
|
211
|
+
skippedExtensions = await this.localExtensionsProvider.updateLocalExtensions(local.added, local.removed, local.updated, skippedExtensions, this.syncResource.profile);
|
|
212
|
+
}
|
|
213
|
+
if (remote) {
|
|
214
|
+
this.logService.trace(`${this.syncResourceLogLabel}: Updating remote extensions...`);
|
|
215
|
+
const content = JSON.stringify(remote.all);
|
|
216
|
+
remoteUserData = await this.updateRemoteUserData(content, force ? null : remoteUserData.ref);
|
|
217
|
+
this.logService.info(`${this.syncResourceLogLabel}: Updated remote extensions.${remote.added.length ? ` Added: ${JSON.stringify(( remote.added.map(e => e.identifier.id)))}.` : ''}${remote.updated.length ? ` Updated: ${JSON.stringify(( remote.updated.map(e => e.identifier.id)))}.` : ''}${remote.removed.length ? ` Removed: ${JSON.stringify(( remote.removed.map(e => e.identifier.id)))}.` : ''}`);
|
|
218
|
+
}
|
|
219
|
+
if (lastSyncUserData?.ref !== remoteUserData.ref) {
|
|
220
|
+
this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized extensions...`);
|
|
221
|
+
builtinExtensions = this.computeBuiltinExtensions(localExtensions, builtinExtensions);
|
|
222
|
+
await this.updateLastSyncUserData(remoteUserData, { skippedExtensions, builtinExtensions });
|
|
223
|
+
this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized extensions.${skippedExtensions.length ? ` Skipped: ${JSON.stringify(( skippedExtensions.map(e => e.identifier.id)))}.` : ''}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
computeBuiltinExtensions(localExtensions, previousBuiltinExtensions) {
|
|
227
|
+
const localExtensionsSet = ( new Set());
|
|
228
|
+
const builtinExtensions = [];
|
|
229
|
+
for (const localExtension of localExtensions) {
|
|
230
|
+
localExtensionsSet.add(localExtension.identifier.id.toLowerCase());
|
|
231
|
+
if (!localExtension.installed) {
|
|
232
|
+
builtinExtensions.push(localExtension.identifier);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (previousBuiltinExtensions) {
|
|
236
|
+
for (const builtinExtension of previousBuiltinExtensions) {
|
|
237
|
+
if (!( localExtensionsSet.has(builtinExtension.id.toLowerCase()))) {
|
|
238
|
+
builtinExtensions.push(builtinExtension);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return builtinExtensions;
|
|
243
|
+
}
|
|
244
|
+
async resolveContent(uri) {
|
|
245
|
+
if (this.extUri.isEqual(this.remoteResource, uri)
|
|
246
|
+
|| this.extUri.isEqual(this.baseResource, uri)
|
|
247
|
+
|| this.extUri.isEqual(this.localResource, uri)
|
|
248
|
+
|| this.extUri.isEqual(this.acceptedResource, uri)) {
|
|
249
|
+
const content = await this.resolvePreviewContent(uri);
|
|
250
|
+
return content ? this.stringify(JSON.parse(content), true) : content;
|
|
251
|
+
}
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
stringify(extensions, format) {
|
|
255
|
+
return stringify(extensions, format);
|
|
256
|
+
}
|
|
257
|
+
async hasLocalData() {
|
|
258
|
+
try {
|
|
259
|
+
const { localExtensions } = await this.localExtensionsProvider.getLocalExtensions(this.syncResource.profile);
|
|
260
|
+
if (( localExtensions.some(e => e.installed || e.disabled))) {
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
}
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
ExtensionsSynchroniser = ( __decorate([
|
|
270
|
+
( __param(2, IEnvironmentService)),
|
|
271
|
+
( __param(3, IFileService)),
|
|
272
|
+
( __param(4, IStorageService)),
|
|
273
|
+
( __param(5, IUserDataSyncStoreService)),
|
|
274
|
+
( __param(6, IUserDataSyncLocalStoreService)),
|
|
275
|
+
( __param(7, IExtensionManagementService)),
|
|
276
|
+
( __param(8, IIgnoredExtensionsManagementService)),
|
|
277
|
+
( __param(9, IUserDataSyncLogService)),
|
|
278
|
+
( __param(10, IConfigurationService)),
|
|
279
|
+
( __param(11, IUserDataSyncEnablementService)),
|
|
280
|
+
( __param(12, ITelemetryService)),
|
|
281
|
+
( __param(13, IExtensionStorageService)),
|
|
282
|
+
( __param(14, IUriIdentityService)),
|
|
283
|
+
( __param(15, IUserDataProfileStorageService)),
|
|
284
|
+
( __param(16, IInstantiationService))
|
|
285
|
+
], ExtensionsSynchroniser));
|
|
286
|
+
let LocalExtensionsProvider = class LocalExtensionsProvider {
|
|
287
|
+
constructor(extensionManagementService, userDataProfileStorageService, extensionGalleryService, ignoredExtensionsManagementService, instantiationService, logService) {
|
|
288
|
+
this.extensionManagementService = extensionManagementService;
|
|
289
|
+
this.userDataProfileStorageService = userDataProfileStorageService;
|
|
290
|
+
this.extensionGalleryService = extensionGalleryService;
|
|
291
|
+
this.ignoredExtensionsManagementService = ignoredExtensionsManagementService;
|
|
292
|
+
this.instantiationService = instantiationService;
|
|
293
|
+
this.logService = logService;
|
|
294
|
+
}
|
|
295
|
+
async getLocalExtensions(profile) {
|
|
296
|
+
const installedExtensions = await this.extensionManagementService.getInstalled(undefined, profile.extensionsResource);
|
|
297
|
+
const ignoredExtensions = this.ignoredExtensionsManagementService.getIgnoredExtensions(installedExtensions);
|
|
298
|
+
const localExtensions = await this.withProfileScopedServices(profile, async (extensionEnablementService, extensionStorageService) => {
|
|
299
|
+
const disabledExtensions = extensionEnablementService.getDisabledExtensions();
|
|
300
|
+
return ( installedExtensions
|
|
301
|
+
.map(extension => {
|
|
302
|
+
const { identifier, isBuiltin, manifest, preRelease, pinned, isApplicationScoped } = extension;
|
|
303
|
+
const syncExntesion = { identifier, preRelease, version: manifest.version, pinned: !!pinned };
|
|
304
|
+
if (isApplicationScoped && !isApplicationScopedExtension(manifest)) {
|
|
305
|
+
syncExntesion.isApplicationScoped = isApplicationScoped;
|
|
306
|
+
}
|
|
307
|
+
if (( disabledExtensions.some(disabledExtension => areSameExtensions(disabledExtension, identifier)))) {
|
|
308
|
+
syncExntesion.disabled = true;
|
|
309
|
+
}
|
|
310
|
+
if (!isBuiltin) {
|
|
311
|
+
syncExntesion.installed = true;
|
|
312
|
+
}
|
|
313
|
+
try {
|
|
314
|
+
const keys = extensionStorageService.getKeysForSync({ id: identifier.id, version: manifest.version });
|
|
315
|
+
if (keys) {
|
|
316
|
+
const extensionStorageState = extensionStorageService.getExtensionState(extension, true) || {};
|
|
317
|
+
syncExntesion.state = ( Object.keys(extensionStorageState)).reduce((state, key) => {
|
|
318
|
+
if (keys.includes(key)) {
|
|
319
|
+
state[key] = extensionStorageState[key];
|
|
320
|
+
}
|
|
321
|
+
return state;
|
|
322
|
+
}, {});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
this.logService.info(`${getSyncResourceLogLabel("extensions" , profile)}: Error while parsing extension state`, getErrorMessage(error));
|
|
327
|
+
}
|
|
328
|
+
return syncExntesion;
|
|
329
|
+
}));
|
|
330
|
+
});
|
|
331
|
+
return { localExtensions, ignoredExtensions };
|
|
332
|
+
}
|
|
333
|
+
async updateLocalExtensions(added, removed, updated, skippedExtensions, profile) {
|
|
334
|
+
const syncResourceLogLabel = getSyncResourceLogLabel("extensions" , profile);
|
|
335
|
+
const extensionsToInstall = [];
|
|
336
|
+
const syncExtensionsToInstall = ( new Map());
|
|
337
|
+
const removeFromSkipped = [];
|
|
338
|
+
const addToSkipped = [];
|
|
339
|
+
const installedExtensions = await this.extensionManagementService.getInstalled(undefined, profile.extensionsResource);
|
|
340
|
+
if (added.length || updated.length) {
|
|
341
|
+
await this.withProfileScopedServices(profile, async (extensionEnablementService, extensionStorageService) => {
|
|
342
|
+
await Promises.settled(( [...added, ...updated].map(async (e) => {
|
|
343
|
+
const installedExtension = installedExtensions.find(installed => areSameExtensions(installed.identifier, e.identifier));
|
|
344
|
+
if (installedExtension && installedExtension.isBuiltin) {
|
|
345
|
+
if (e.state && installedExtension.manifest.version === e.version) {
|
|
346
|
+
this.updateExtensionState(e.state, installedExtension, installedExtension.manifest.version, extensionStorageService);
|
|
347
|
+
}
|
|
348
|
+
const isDisabled = ( extensionEnablementService.getDisabledExtensions().some(disabledExtension => areSameExtensions(disabledExtension, e.identifier)));
|
|
349
|
+
if (isDisabled !== !!e.disabled) {
|
|
350
|
+
if (e.disabled) {
|
|
351
|
+
this.logService.trace(`${syncResourceLogLabel}: Disabling extension...`, e.identifier.id);
|
|
352
|
+
await extensionEnablementService.disableExtension(e.identifier);
|
|
353
|
+
this.logService.info(`${syncResourceLogLabel}: Disabled extension`, e.identifier.id);
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
this.logService.trace(`${syncResourceLogLabel}: Enabling extension...`, e.identifier.id);
|
|
357
|
+
await extensionEnablementService.enableExtension(e.identifier);
|
|
358
|
+
this.logService.info(`${syncResourceLogLabel}: Enabled extension`, e.identifier.id);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
removeFromSkipped.push(e.identifier);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
const version = e.pinned ? e.version : undefined;
|
|
365
|
+
const extension = (await this.extensionGalleryService.getExtensions([{ ...e.identifier, version, preRelease: version ? undefined : e.preRelease }], CancellationToken.None))[0];
|
|
366
|
+
if (e.state &&
|
|
367
|
+
((installedExtension ? installedExtension.manifest.version === e.version : !!extension) )) {
|
|
368
|
+
this.updateExtensionState(e.state, installedExtension || extension, installedExtension?.manifest.version, extensionStorageService);
|
|
369
|
+
}
|
|
370
|
+
if (extension) {
|
|
371
|
+
try {
|
|
372
|
+
const isDisabled = ( extensionEnablementService.getDisabledExtensions().some(disabledExtension => areSameExtensions(disabledExtension, e.identifier)));
|
|
373
|
+
if (isDisabled !== !!e.disabled) {
|
|
374
|
+
if (e.disabled) {
|
|
375
|
+
this.logService.trace(`${syncResourceLogLabel}: Disabling extension...`, e.identifier.id, extension.version);
|
|
376
|
+
await extensionEnablementService.disableExtension(extension.identifier);
|
|
377
|
+
this.logService.info(`${syncResourceLogLabel}: Disabled extension`, e.identifier.id, extension.version);
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
this.logService.trace(`${syncResourceLogLabel}: Enabling extension...`, e.identifier.id, extension.version);
|
|
381
|
+
await extensionEnablementService.enableExtension(extension.identifier);
|
|
382
|
+
this.logService.info(`${syncResourceLogLabel}: Enabled extension`, e.identifier.id, extension.version);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (!installedExtension
|
|
386
|
+
|| installedExtension.preRelease !== e.preRelease
|
|
387
|
+
|| installedExtension.pinned !== e.pinned
|
|
388
|
+
|| (version && installedExtension.manifest.version !== version)
|
|
389
|
+
) {
|
|
390
|
+
if (await this.extensionManagementService.canInstall(extension)) {
|
|
391
|
+
extensionsToInstall.push({
|
|
392
|
+
extension, options: {
|
|
393
|
+
isMachineScoped: false ,
|
|
394
|
+
donotIncludePackAndDependencies: true,
|
|
395
|
+
installGivenVersion: e.pinned && !!e.version,
|
|
396
|
+
installPreReleaseVersion: e.preRelease,
|
|
397
|
+
profileLocation: profile.extensionsResource,
|
|
398
|
+
isApplicationScoped: e.isApplicationScoped,
|
|
399
|
+
context: { [EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT]: true, [EXTENSION_INSTALL_SYNC_CONTEXT]: true }
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
syncExtensionsToInstall.set(extension.identifier.id.toLowerCase(), e);
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
this.logService.info(`${syncResourceLogLabel}: Skipped synchronizing extension because it cannot be installed.`, extension.displayName || extension.identifier.id);
|
|
406
|
+
addToSkipped.push(e);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
addToSkipped.push(e);
|
|
412
|
+
this.logService.error(error);
|
|
413
|
+
this.logService.info(`${syncResourceLogLabel}: Skipped synchronizing extension`, extension.displayName || extension.identifier.id);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
addToSkipped.push(e);
|
|
418
|
+
this.logService.info(`${syncResourceLogLabel}: Skipped synchronizing extension because the extension is not found.`, e.identifier.id);
|
|
419
|
+
}
|
|
420
|
+
})));
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
if (removed.length) {
|
|
424
|
+
const extensionsToRemove = installedExtensions.filter(({ identifier, isBuiltin }) => !isBuiltin && ( removed.some(r => areSameExtensions(identifier, r))));
|
|
425
|
+
await Promises.settled(( extensionsToRemove.map(async (extensionToRemove) => {
|
|
426
|
+
this.logService.trace(`${syncResourceLogLabel}: Uninstalling local extension...`, extensionToRemove.identifier.id);
|
|
427
|
+
await this.extensionManagementService.uninstall(extensionToRemove, { donotIncludePack: true, donotCheckDependents: true, profileLocation: profile.extensionsResource });
|
|
428
|
+
this.logService.info(`${syncResourceLogLabel}: Uninstalled local extension.`, extensionToRemove.identifier.id);
|
|
429
|
+
removeFromSkipped.push(extensionToRemove.identifier);
|
|
430
|
+
})));
|
|
431
|
+
}
|
|
432
|
+
const results = await this.extensionManagementService.installGalleryExtensions(extensionsToInstall);
|
|
433
|
+
for (const { identifier, local, error, source } of results) {
|
|
434
|
+
const gallery = source;
|
|
435
|
+
if (local) {
|
|
436
|
+
this.logService.info(`${syncResourceLogLabel}: Installed extension.`, identifier.id, gallery.version);
|
|
437
|
+
removeFromSkipped.push(identifier);
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
const e = syncExtensionsToInstall.get(identifier.id.toLowerCase());
|
|
441
|
+
if (e) {
|
|
442
|
+
addToSkipped.push(e);
|
|
443
|
+
this.logService.info(`${syncResourceLogLabel}: Skipped synchronizing extension`, gallery.displayName || gallery.identifier.id);
|
|
444
|
+
}
|
|
445
|
+
if (error instanceof ExtensionManagementError && [ExtensionManagementErrorCode.Incompatible, ExtensionManagementErrorCode.IncompatibleTargetPlatform].includes(error.code)) {
|
|
446
|
+
this.logService.info(`${syncResourceLogLabel}: Skipped synchronizing extension because the compatible extension is not found.`, gallery.displayName || gallery.identifier.id);
|
|
447
|
+
}
|
|
448
|
+
else if (error) {
|
|
449
|
+
this.logService.error(error);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
const newSkippedExtensions = [];
|
|
454
|
+
for (const skippedExtension of skippedExtensions) {
|
|
455
|
+
if (!( removeFromSkipped.some(e => areSameExtensions(e, skippedExtension.identifier)))) {
|
|
456
|
+
newSkippedExtensions.push(skippedExtension);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
for (const skippedExtension of addToSkipped) {
|
|
460
|
+
if (!( newSkippedExtensions.some(e => areSameExtensions(e.identifier, skippedExtension.identifier)))) {
|
|
461
|
+
newSkippedExtensions.push(skippedExtension);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return newSkippedExtensions;
|
|
465
|
+
}
|
|
466
|
+
updateExtensionState(state, extension, version, extensionStorageService) {
|
|
467
|
+
const extensionState = extensionStorageService.getExtensionState(extension, true) || {};
|
|
468
|
+
const keys = version ? extensionStorageService.getKeysForSync({ id: extension.identifier.id, version }) : undefined;
|
|
469
|
+
if (keys) {
|
|
470
|
+
keys.forEach(key => { extensionState[key] = state[key]; });
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
( Object.keys(state)).forEach(key => extensionState[key] = state[key]);
|
|
474
|
+
}
|
|
475
|
+
extensionStorageService.setExtensionState(extension, extensionState, true);
|
|
476
|
+
}
|
|
477
|
+
async withProfileScopedServices(profile, fn) {
|
|
478
|
+
return this.userDataProfileStorageService.withProfileScopedStorageService(profile, async (storageService) => {
|
|
479
|
+
const disposables = ( new DisposableStore());
|
|
480
|
+
const instantiationService = this.instantiationService.createChild(( new ServiceCollection([IStorageService, storageService])));
|
|
481
|
+
const extensionEnablementService = disposables.add(instantiationService.createInstance(GlobalExtensionEnablementService));
|
|
482
|
+
const extensionStorageService = disposables.add(instantiationService.createInstance(ExtensionStorageService));
|
|
483
|
+
try {
|
|
484
|
+
return await fn(extensionEnablementService, extensionStorageService);
|
|
485
|
+
}
|
|
486
|
+
finally {
|
|
487
|
+
disposables.dispose();
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
LocalExtensionsProvider = ( __decorate([
|
|
493
|
+
( __param(0, IExtensionManagementService)),
|
|
494
|
+
( __param(1, IUserDataProfileStorageService)),
|
|
495
|
+
( __param(2, IExtensionGalleryService)),
|
|
496
|
+
( __param(3, IIgnoredExtensionsManagementService)),
|
|
497
|
+
( __param(4, IInstantiationService)),
|
|
498
|
+
( __param(5, IUserDataSyncLogService))
|
|
499
|
+
], LocalExtensionsProvider));
|
|
500
|
+
let AbstractExtensionsInitializer = class AbstractExtensionsInitializer extends AbstractInitializer {
|
|
501
|
+
constructor(extensionManagementService, ignoredExtensionsManagementService, fileService, userDataProfilesService, environmentService, logService, storageService, uriIdentityService) {
|
|
502
|
+
super("extensions" , userDataProfilesService, environmentService, logService, fileService, storageService, uriIdentityService);
|
|
503
|
+
this.extensionManagementService = extensionManagementService;
|
|
504
|
+
this.ignoredExtensionsManagementService = ignoredExtensionsManagementService;
|
|
505
|
+
}
|
|
506
|
+
async parseExtensions(remoteUserData) {
|
|
507
|
+
return remoteUserData.syncData ? await parseAndMigrateExtensions(remoteUserData.syncData, this.extensionManagementService) : null;
|
|
508
|
+
}
|
|
509
|
+
generatePreview(remoteExtensions, localExtensions) {
|
|
510
|
+
const installedExtensions = [];
|
|
511
|
+
const newExtensions = [];
|
|
512
|
+
const disabledExtensions = [];
|
|
513
|
+
for (const extension of remoteExtensions) {
|
|
514
|
+
if (this.ignoredExtensionsManagementService.hasToNeverSyncExtension(extension.identifier.id)) {
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
const installedExtension = localExtensions.find(i => areSameExtensions(i.identifier, extension.identifier));
|
|
518
|
+
if (installedExtension) {
|
|
519
|
+
installedExtensions.push(installedExtension);
|
|
520
|
+
if (extension.disabled) {
|
|
521
|
+
disabledExtensions.push(extension.identifier);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
else if (extension.installed) {
|
|
525
|
+
newExtensions.push({ ...extension.identifier, preRelease: !!extension.preRelease });
|
|
526
|
+
if (extension.disabled) {
|
|
527
|
+
disabledExtensions.push(extension.identifier);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return { installedExtensions, newExtensions, disabledExtensions, remoteExtensions };
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
AbstractExtensionsInitializer = ( __decorate([
|
|
535
|
+
( __param(0, IExtensionManagementService)),
|
|
536
|
+
( __param(1, IIgnoredExtensionsManagementService)),
|
|
537
|
+
( __param(2, IFileService)),
|
|
538
|
+
( __param(3, IUserDataProfilesService)),
|
|
539
|
+
( __param(4, IEnvironmentService)),
|
|
540
|
+
( __param(5, ILogService)),
|
|
541
|
+
( __param(6, IStorageService)),
|
|
542
|
+
( __param(7, IUriIdentityService))
|
|
543
|
+
], AbstractExtensionsInitializer));
|
|
544
|
+
|
|
545
|
+
export { AbstractExtensionsInitializer, ExtensionsSynchroniser, LocalExtensionsProvider, parseExtensions, stringify };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { deepClone, equals } from 'vscode/vscode/vs/base/common/objects';
|
|
2
|
+
import { SYNC_SERVICE_URL_TYPE } from 'vscode/vscode/vs/platform/userDataSync/common/userDataSync';
|
|
3
|
+
|
|
4
|
+
function merge(localStorage, remoteStorage, baseStorage, storageKeys, logService) {
|
|
5
|
+
if (!remoteStorage) {
|
|
6
|
+
return { remote: { added: ( Object.keys(localStorage)), removed: [], updated: [], all: ( Object.keys(localStorage)).length > 0 ? localStorage : null }, local: { added: {}, removed: [], updated: {} } };
|
|
7
|
+
}
|
|
8
|
+
const localToRemote = compare(localStorage, remoteStorage);
|
|
9
|
+
if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) {
|
|
10
|
+
return { remote: { added: [], removed: [], updated: [], all: null }, local: { added: {}, removed: [], updated: {} } };
|
|
11
|
+
}
|
|
12
|
+
const baseToRemote = baseStorage ? compare(baseStorage, remoteStorage) : { added: ( Object.keys(remoteStorage)).reduce((r, k) => { r.add(k); return r; }, ( new Set())), removed: ( new Set()), updated: ( new Set()) };
|
|
13
|
+
const baseToLocal = baseStorage ? compare(baseStorage, localStorage) : { added: ( Object.keys(localStorage)).reduce((r, k) => { r.add(k); return r; }, ( new Set())), removed: ( new Set()), updated: ( new Set()) };
|
|
14
|
+
const local = { added: {}, removed: [], updated: {} };
|
|
15
|
+
const remote = deepClone(remoteStorage);
|
|
16
|
+
const isFirstTimeSync = !baseStorage;
|
|
17
|
+
for (const key of ( baseToLocal.added.values())) {
|
|
18
|
+
if (key !== SYNC_SERVICE_URL_TYPE && isFirstTimeSync && ( baseToRemote.added.has(key))) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
remote[key] = localStorage[key];
|
|
22
|
+
}
|
|
23
|
+
for (const key of ( baseToLocal.updated.values())) {
|
|
24
|
+
remote[key] = localStorage[key];
|
|
25
|
+
}
|
|
26
|
+
for (const key of ( baseToLocal.removed.values())) {
|
|
27
|
+
if (storageKeys.unregistered.includes(key)) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
delete remote[key];
|
|
31
|
+
}
|
|
32
|
+
for (const key of ( baseToRemote.added.values())) {
|
|
33
|
+
const remoteValue = remoteStorage[key];
|
|
34
|
+
if (storageKeys.machine.includes(key)) {
|
|
35
|
+
logService.info(`GlobalState: Skipped adding ${key} in local storage because it is declared as machine scoped.`);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (baseStorage && ( baseToLocal.added.has(key))) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const localValue = localStorage[key];
|
|
42
|
+
if (localValue && localValue.value === remoteValue.value) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (key === SYNC_SERVICE_URL_TYPE && isFirstTimeSync && ( baseToLocal.added.has(key))) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (localValue) {
|
|
49
|
+
local.updated[key] = remoteValue;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
local.added[key] = remoteValue;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
for (const key of ( baseToRemote.updated.values())) {
|
|
56
|
+
const remoteValue = remoteStorage[key];
|
|
57
|
+
if (storageKeys.machine.includes(key)) {
|
|
58
|
+
logService.info(`GlobalState: Skipped updating ${key} in local storage because it is declared as machine scoped.`);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (( baseToLocal.updated.has(key)) || ( baseToLocal.removed.has(key))) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const localValue = localStorage[key];
|
|
65
|
+
if (localValue && localValue.value === remoteValue.value) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
local.updated[key] = remoteValue;
|
|
69
|
+
}
|
|
70
|
+
for (const key of ( baseToRemote.removed.values())) {
|
|
71
|
+
if (storageKeys.machine.includes(key)) {
|
|
72
|
+
logService.trace(`GlobalState: Skipped removing ${key} in local storage because it is declared as machine scoped.`);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (( baseToLocal.updated.has(key)) || ( baseToLocal.removed.has(key))) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
local.removed.push(key);
|
|
79
|
+
}
|
|
80
|
+
const result = compare(remoteStorage, remote);
|
|
81
|
+
return { local, remote: { added: [...result.added], updated: [...result.updated], removed: [...result.removed], all: result.added.size === 0 && result.removed.size === 0 && result.updated.size === 0 ? null : remote } };
|
|
82
|
+
}
|
|
83
|
+
function compare(from, to) {
|
|
84
|
+
const fromKeys = ( Object.keys(from));
|
|
85
|
+
const toKeys = ( Object.keys(to));
|
|
86
|
+
const added = toKeys.filter(key => !fromKeys.includes(key)).reduce((r, key) => { r.add(key); return r; }, ( new Set()));
|
|
87
|
+
const removed = fromKeys.filter(key => !toKeys.includes(key)).reduce((r, key) => { r.add(key); return r; }, ( new Set()));
|
|
88
|
+
const updated = ( new Set());
|
|
89
|
+
for (const key of fromKeys) {
|
|
90
|
+
if (( removed.has(key))) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const value1 = from[key];
|
|
94
|
+
const value2 = to[key];
|
|
95
|
+
if (!equals(value1, value2)) {
|
|
96
|
+
updated.add(key);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return { added, removed, updated };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export { merge };
|