@sussudio/platform
Version:
Internal APIs for VS Code's service injection the base services.
495 lines (494 loc) • 17.8 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* 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 { URI } from '@sussudio/base/common/uri.mjs';
import { localize } from 'vscode-nls.mjs';
import { IEnvironmentService } from '../../environment/common/environment.mjs';
import { IFileService } from '../../files/common/files.mjs';
import { getServiceMachineId } from '../../externalServices/common/serviceMachineId.mjs';
import { IStorageService } from '../../storage/common/storage.mjs';
import { IUriIdentityService } from '../../uriIdentity/common/uriIdentity.mjs';
import {
IUserDataSyncBackupStoreService,
IUserDataSyncLogService,
IUserDataSyncStoreService,
UserDataSyncError,
USER_DATA_SYNC_SCHEME,
CONFIG_SYNC_KEYBINDINGS_PER_PLATFORM,
} from './userDataSync.mjs';
import { IUserDataProfilesService } from '../../userDataProfile/common/userDataProfile.mjs';
import { isSyncData } from './abstractSynchronizer.mjs';
import { parseSnippets } from './snippetsSync.mjs';
import { parseSettingsSyncContent } from './settingsSync.mjs';
import { getKeybindingsContentFromSyncContent } from './keybindingsSync.mjs';
import { IConfigurationService } from '../../configuration/common/configuration.mjs';
import { getTasksContentFromSyncContent } from './tasksSync.mjs';
import { LocalExtensionsProvider, parseExtensions, stringify as stringifyExtensions } from './extensionsSync.mjs';
import { LocalGlobalStateProvider, stringify as stringifyGlobalState } from './globalStateSync.mjs';
import { IInstantiationService } from '../../instantiation/common/instantiation.mjs';
import { parseUserDataProfilesManifest, stringifyLocalProfiles } from './userDataProfilesManifestSync.mjs';
import { toFormattedString } from '@sussudio/base/common/jsonFormatter.mjs';
let UserDataSyncResourceProviderService = class UserDataSyncResourceProviderService {
userDataSyncStoreService;
userDataSyncBackupStoreService;
logService;
environmentService;
storageService;
fileService;
userDataProfilesService;
configurationService;
instantiationService;
_serviceBrand;
static NOT_EXISTING_RESOURCE = 'not-existing-resource';
static REMOTE_BACKUP_AUTHORITY = 'remote-backup';
static LOCAL_BACKUP_AUTHORITY = 'local-backup';
extUri;
constructor(
userDataSyncStoreService,
userDataSyncBackupStoreService,
logService,
uriIdentityService,
environmentService,
storageService,
fileService,
userDataProfilesService,
configurationService,
instantiationService,
) {
this.userDataSyncStoreService = userDataSyncStoreService;
this.userDataSyncBackupStoreService = userDataSyncBackupStoreService;
this.logService = logService;
this.environmentService = environmentService;
this.storageService = storageService;
this.fileService = fileService;
this.userDataProfilesService = userDataProfilesService;
this.configurationService = configurationService;
this.instantiationService = instantiationService;
this.extUri = uriIdentityService.extUri;
}
async getRemoteSyncedProfiles() {
const userData = await this.userDataSyncStoreService.readResource(
'profiles' /* SyncResource.Profiles */,
null,
undefined,
);
if (userData.content) {
const syncData = this.parseSyncData(userData.content, 'profiles' /* SyncResource.Profiles */);
return parseUserDataProfilesManifest(syncData);
}
return [];
}
async getRemoteSyncResourceHandles(syncResource, profile) {
const handles = await this.userDataSyncStoreService.getAllResourceRefs(syncResource, profile?.collection);
return handles.map(({ created, ref }) => ({
created,
uri: this.toUri({
remote: true,
syncResource,
profile: profile?.id ?? this.userDataProfilesService.defaultProfile.id,
collection: profile?.collection,
ref,
node: undefined,
}),
}));
}
async getLocalSyncResourceHandles(syncResource, profile) {
const handles = await this.userDataSyncBackupStoreService.getAllRefs(profile, syncResource);
return handles.map(({ created, ref }) => ({
created,
uri: this.toUri({
remote: false,
syncResource,
profile: profile.id,
collection: undefined,
ref,
node: undefined,
}),
}));
}
resolveUserDataSyncResource({ uri }) {
const resolved = this.resolveUri(uri);
const profile = resolved ? this.userDataProfilesService.profiles.find((p) => p.id === resolved.profile) : undefined;
return resolved && profile ? { profile, syncResource: resolved?.syncResource } : undefined;
}
async getAssociatedResources({ uri }) {
const resolved = this.resolveUri(uri);
if (!resolved) {
return [];
}
const profile = this.userDataProfilesService.profiles.find((p) => p.id === resolved.profile);
switch (resolved.syncResource) {
case 'settings' /* SyncResource.Settings */:
return this.getSettingsAssociatedResources(uri, profile);
case 'keybindings' /* SyncResource.Keybindings */:
return this.getKeybindingsAssociatedResources(uri, profile);
case 'tasks' /* SyncResource.Tasks */:
return this.getTasksAssociatedResources(uri, profile);
case 'snippets' /* SyncResource.Snippets */:
return this.getSnippetsAssociatedResources(uri, profile);
case 'globalState' /* SyncResource.GlobalState */:
return this.getGlobalStateAssociatedResources(uri, profile);
case 'extensions' /* SyncResource.Extensions */:
return this.getExtensionsAssociatedResources(uri, profile);
case 'profiles' /* SyncResource.Profiles */:
return this.getProfilesAssociatedResources(uri, profile);
}
}
async getMachineId({ uri }) {
const resolved = this.resolveUri(uri);
if (!resolved) {
return undefined;
}
if (resolved.remote) {
if (resolved.ref) {
const { content } = await this.getUserData(resolved.syncResource, resolved.ref, resolved.collection);
if (content) {
const syncData = this.parseSyncData(content, resolved.syncResource);
return syncData?.machineId;
}
}
return undefined;
}
return getServiceMachineId(this.environmentService, this.fileService, this.storageService);
}
async resolveContent(uri) {
const resolved = this.resolveUri(uri);
if (!resolved) {
return null;
}
if (resolved.node === UserDataSyncResourceProviderService.NOT_EXISTING_RESOURCE) {
return null;
}
if (resolved.ref) {
const content = await this.getContentFromStore(
resolved.remote,
resolved.syncResource,
resolved.profile,
resolved.collection,
resolved.ref,
);
if (resolved.node && content) {
return this.resolveNodeContent(resolved.syncResource, content, resolved.node);
}
return content;
}
if (!resolved.remote && !resolved.node) {
return this.resolveLatestContent(resolved.syncResource, resolved.profile);
}
return null;
}
async getContentFromStore(remote, syncResource, profileId, collection, ref) {
if (remote) {
const { content } = await this.getUserData(syncResource, ref, collection);
return content;
}
const profile = this.userDataProfilesService.profiles.find((p) => p.id === profileId);
if (profile) {
return this.userDataSyncBackupStoreService.resolveContent(profile, syncResource, ref);
}
return null;
}
resolveNodeContent(syncResource, content, node) {
const syncData = this.parseSyncData(content, syncResource);
switch (syncResource) {
case 'settings' /* SyncResource.Settings */:
return this.resolveSettingsNodeContent(syncData, node);
case 'keybindings' /* SyncResource.Keybindings */:
return this.resolveKeybindingsNodeContent(syncData, node);
case 'tasks' /* SyncResource.Tasks */:
return this.resolveTasksNodeContent(syncData, node);
case 'snippets' /* SyncResource.Snippets */:
return this.resolveSnippetsNodeContent(syncData, node);
case 'globalState' /* SyncResource.GlobalState */:
return this.resolveGlobalStateNodeContent(syncData, node);
case 'extensions' /* SyncResource.Extensions */:
return this.resolveExtensionsNodeContent(syncData, node);
case 'profiles' /* SyncResource.Profiles */:
return this.resolveProfileNodeContent(syncData, node);
}
}
async resolveLatestContent(syncResource, profileId) {
const profile = this.userDataProfilesService.profiles.find((p) => p.id === profileId);
if (!profile) {
return null;
}
switch (syncResource) {
case 'globalState' /* SyncResource.GlobalState */:
return this.resolveLatestGlobalStateContent(profile);
case 'extensions' /* SyncResource.Extensions */:
return this.resolveLatestExtensionsContent(profile);
case 'profiles' /* SyncResource.Profiles */:
return this.resolveLatestProfilesContent(profile);
case 'settings' /* SyncResource.Settings */:
return null;
case 'keybindings' /* SyncResource.Keybindings */:
return null;
case 'tasks' /* SyncResource.Tasks */:
return null;
case 'snippets' /* SyncResource.Snippets */:
return null;
}
}
getSettingsAssociatedResources(uri, profile) {
const resource = this.extUri.joinPath(uri, 'settings.json');
const comparableResource = profile
? profile.settingsResource
: this.extUri.joinPath(uri, UserDataSyncResourceProviderService.NOT_EXISTING_RESOURCE);
return [{ resource, comparableResource }];
}
resolveSettingsNodeContent(syncData, node) {
switch (node) {
case 'settings.json':
return parseSettingsSyncContent(syncData.content).settings;
}
return null;
}
getKeybindingsAssociatedResources(uri, profile) {
const resource = this.extUri.joinPath(uri, 'keybindings.json');
const comparableResource = profile
? profile.keybindingsResource
: this.extUri.joinPath(uri, UserDataSyncResourceProviderService.NOT_EXISTING_RESOURCE);
return [{ resource, comparableResource }];
}
resolveKeybindingsNodeContent(syncData, node) {
switch (node) {
case 'keybindings.json':
return getKeybindingsContentFromSyncContent(
syncData.content,
!!this.configurationService.getValue(CONFIG_SYNC_KEYBINDINGS_PER_PLATFORM),
this.logService,
);
}
return null;
}
getTasksAssociatedResources(uri, profile) {
const resource = this.extUri.joinPath(uri, 'tasks.json');
const comparableResource = profile
? profile.tasksResource
: this.extUri.joinPath(uri, UserDataSyncResourceProviderService.NOT_EXISTING_RESOURCE);
return [{ resource, comparableResource }];
}
resolveTasksNodeContent(syncData, node) {
switch (node) {
case 'tasks.json':
return getTasksContentFromSyncContent(syncData.content, this.logService);
}
return null;
}
async getSnippetsAssociatedResources(uri, profile) {
const content = await this.resolveContent(uri);
if (content) {
const syncData = this.parseSyncData(content, 'snippets' /* SyncResource.Snippets */);
if (syncData) {
const snippets = parseSnippets(syncData);
const result = [];
for (const snippet of Object.keys(snippets)) {
const resource = this.extUri.joinPath(uri, snippet);
const comparableResource = profile
? this.extUri.joinPath(profile.snippetsHome, snippet)
: this.extUri.joinPath(uri, UserDataSyncResourceProviderService.NOT_EXISTING_RESOURCE);
result.push({ resource, comparableResource });
}
return result;
}
}
return [];
}
resolveSnippetsNodeContent(syncData, node) {
return parseSnippets(syncData)[node] || null;
}
getExtensionsAssociatedResources(uri, profile) {
const resource = this.extUri.joinPath(uri, 'extensions.json');
const comparableResource = profile
? this.toUri({
remote: false,
syncResource: 'extensions' /* SyncResource.Extensions */,
profile: profile.id,
collection: undefined,
ref: undefined,
node: undefined,
})
: this.extUri.joinPath(uri, UserDataSyncResourceProviderService.NOT_EXISTING_RESOURCE);
return [{ resource, comparableResource }];
}
resolveExtensionsNodeContent(syncData, node) {
switch (node) {
case 'extensions.json':
return stringifyExtensions(parseExtensions(syncData), true);
}
return null;
}
async resolveLatestExtensionsContent(profile) {
const { localExtensions } = await this.instantiationService
.createInstance(LocalExtensionsProvider)
.getLocalExtensions(profile);
return stringifyExtensions(localExtensions, true);
}
getGlobalStateAssociatedResources(uri, profile) {
const resource = this.extUri.joinPath(uri, 'globalState.json');
const comparableResource = profile
? this.toUri({
remote: false,
syncResource: 'globalState' /* SyncResource.GlobalState */,
profile: profile.id,
collection: undefined,
ref: undefined,
node: undefined,
})
: this.extUri.joinPath(uri, UserDataSyncResourceProviderService.NOT_EXISTING_RESOURCE);
return [{ resource, comparableResource }];
}
resolveGlobalStateNodeContent(syncData, node) {
switch (node) {
case 'globalState.json':
return stringifyGlobalState(JSON.parse(syncData.content), true);
}
return null;
}
async resolveLatestGlobalStateContent(profile) {
const localGlobalState = await this.instantiationService
.createInstance(LocalGlobalStateProvider)
.getLocalGlobalState(profile);
return stringifyGlobalState(localGlobalState, true);
}
getProfilesAssociatedResources(uri, profile) {
const resource = this.extUri.joinPath(uri, 'profiles.json');
const comparableResource = this.toUri({
remote: false,
syncResource: 'profiles' /* SyncResource.Profiles */,
profile: this.userDataProfilesService.defaultProfile.id,
collection: undefined,
ref: undefined,
node: undefined,
});
return [{ resource, comparableResource }];
}
resolveProfileNodeContent(syncData, node) {
switch (node) {
case 'profiles.json':
return toFormattedString(JSON.parse(syncData.content), {});
}
return null;
}
async resolveLatestProfilesContent(profile) {
return stringifyLocalProfiles(
this.userDataProfilesService.profiles.filter((p) => !p.isDefault && !p.isTransient),
true,
);
}
toUri(syncResourceUriInfo) {
const authority = syncResourceUriInfo.remote
? UserDataSyncResourceProviderService.REMOTE_BACKUP_AUTHORITY
: UserDataSyncResourceProviderService.LOCAL_BACKUP_AUTHORITY;
const paths = [syncResourceUriInfo.syncResource, syncResourceUriInfo.profile];
if (syncResourceUriInfo.collection) {
paths.push(`collection:${syncResourceUriInfo.collection}`);
}
if (syncResourceUriInfo.ref) {
paths.push(`ref:${syncResourceUriInfo.ref}`);
}
if (syncResourceUriInfo.node) {
paths.push(syncResourceUriInfo.node);
}
return this.extUri.joinPath(URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority, path: `/` }), ...paths);
}
resolveUri(uri) {
if (uri.scheme !== USER_DATA_SYNC_SCHEME) {
return undefined;
}
if (
uri.authority !== UserDataSyncResourceProviderService.LOCAL_BACKUP_AUTHORITY &&
uri.authority !== UserDataSyncResourceProviderService.REMOTE_BACKUP_AUTHORITY
) {
return undefined;
}
const paths = [];
while (uri.path !== '/') {
paths.unshift(this.extUri.basename(uri));
uri = this.extUri.dirname(uri);
}
if (paths.length < 2) {
return undefined;
}
const remote = uri.authority === UserDataSyncResourceProviderService.REMOTE_BACKUP_AUTHORITY;
const syncResource = paths.shift();
const profile = paths.shift();
let collection;
let ref;
let node;
while (paths.length) {
const path = paths.shift();
if (path.startsWith('collection:')) {
collection = path.substring('collection:'.length);
} else if (path.startsWith('ref:')) {
ref = path.substring('ref:'.length);
} else {
node = path;
}
}
return {
remote,
syncResource,
profile,
collection,
ref,
node,
};
}
parseSyncData(content, syncResource) {
try {
const syncData = JSON.parse(content);
if (isSyncData(syncData)) {
return syncData;
}
} catch (error) {
this.logService.error(error);
}
throw new UserDataSyncError(
localize('incompatible sync data', 'Cannot parse sync data as it is not compatible with the current version.'),
'IncompatibleRemoteContent' /* UserDataSyncErrorCode.IncompatibleRemoteContent */,
syncResource,
);
}
async getUserData(syncResource, ref, collection) {
const content = await this.userDataSyncStoreService.resolveResourceContent(syncResource, ref, collection);
return { ref, content };
}
};
UserDataSyncResourceProviderService = __decorate(
[
__param(0, IUserDataSyncStoreService),
__param(1, IUserDataSyncBackupStoreService),
__param(2, IUserDataSyncLogService),
__param(3, IUriIdentityService),
__param(4, IEnvironmentService),
__param(5, IStorageService),
__param(6, IFileService),
__param(7, IUserDataProfilesService),
__param(8, IConfigurationService),
__param(9, IInstantiationService),
],
UserDataSyncResourceProviderService,
);
export { UserDataSyncResourceProviderService };