sussudio
Version:
An unofficial VS Code Internal API
566 lines (565 loc) • 31.7 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 { createCancelablePromise, timeout } from "../../../base/common/async.mjs";
import { CancellationToken } from "../../../base/common/cancellation.mjs";
import { getErrorMessage, isCancellationError } from "../../../base/common/errors.mjs";
import { Emitter, Event } from "../../../base/common/event.mjs";
import { Disposable, toDisposable } from "../../../base/common/lifecycle.mjs";
import { Mimes } from "../../../base/common/mime.mjs";
import { isWeb } from "../../../base/common/platform.mjs";
import { joinPath, relativePath } from "../../../base/common/resources.mjs";
import { isObject, isString } from "../../../base/common/types.mjs";
import { URI } from "../../../base/common/uri.mjs";
import { generateUuid } from "../../../base/common/uuid.mjs";
import { IConfigurationService } from "../../configuration/common/configuration.mjs";
import { IEnvironmentService } from "../../environment/common/environment.mjs";
import { IFileService } from "../../files/common/files.mjs";
import { IProductService } from "../../product/common/productService.mjs";
import { asJson, asText, asTextOrError, IRequestService, isSuccess as isSuccessContext } from "../../request/common/request.mjs";
import { getServiceMachineId } from "../../externalServices/common/serviceMachineId.mjs";
import { IStorageService } from "../../storage/common/storage.mjs";
import { CONFIGURATION_SYNC_STORE_KEY, HEADER_EXECUTION_ID, HEADER_OPERATION_ID, IUserDataSyncLogService, IUserDataSyncStoreManagementService, SYNC_SERVICE_URL_TYPE, UserDataSyncStoreError } from "./userDataSync.mjs";
const SYNC_PREVIOUS_STORE = 'sync.previous.store';
const DONOT_MAKE_REQUESTS_UNTIL_KEY = 'sync.donot-make-requests-until';
const USER_SESSION_ID_KEY = 'sync.user-session-id';
const MACHINE_SESSION_ID_KEY = 'sync.machine-session-id';
const REQUEST_SESSION_LIMIT = 100;
const REQUEST_SESSION_INTERVAL = 1000 * 60 * 5; /* 5 minutes */
let AbstractUserDataSyncStoreManagementService = class AbstractUserDataSyncStoreManagementService extends Disposable {
productService;
configurationService;
storageService;
_serviceBrand;
_onDidChangeUserDataSyncStore = this._register(new Emitter());
onDidChangeUserDataSyncStore = this._onDidChangeUserDataSyncStore.event;
_userDataSyncStore;
get userDataSyncStore() { return this._userDataSyncStore; }
get userDataSyncStoreType() {
return this.storageService.get(SYNC_SERVICE_URL_TYPE, -1 /* StorageScope.APPLICATION */);
}
set userDataSyncStoreType(type) {
this.storageService.store(SYNC_SERVICE_URL_TYPE, type, -1 /* StorageScope.APPLICATION */, isWeb ? 0 /* StorageTarget.USER */ : 1 /* StorageTarget.MACHINE */);
}
constructor(productService, configurationService, storageService) {
super();
this.productService = productService;
this.configurationService = configurationService;
this.storageService = storageService;
this.updateUserDataSyncStore();
this._register(Event.filter(storageService.onDidChangeValue, e => e.key === SYNC_SERVICE_URL_TYPE && e.scope === -1 /* StorageScope.APPLICATION */ && this.userDataSyncStoreType !== this.userDataSyncStore?.type)(() => this.updateUserDataSyncStore()));
}
updateUserDataSyncStore() {
this._userDataSyncStore = this.toUserDataSyncStore(this.productService[CONFIGURATION_SYNC_STORE_KEY], this.configurationService.getValue(CONFIGURATION_SYNC_STORE_KEY));
this._onDidChangeUserDataSyncStore.fire();
}
toUserDataSyncStore(productStore, configuredStore) {
// Check for web overrides for backward compatibility while reading previous store
productStore = isWeb && productStore?.web ? { ...productStore, ...productStore.web } : productStore;
const value = { ...(productStore || {}), ...(configuredStore || {}) };
if (value
&& isString(value.url)
&& isObject(value.authenticationProviders)
&& Object.keys(value.authenticationProviders).every(authenticationProviderId => Array.isArray(value.authenticationProviders[authenticationProviderId].scopes))) {
const syncStore = value;
const canSwitch = !!syncStore.canSwitch && !configuredStore?.url;
const defaultType = syncStore.url === syncStore.insidersUrl ? 'insiders' : 'stable';
const type = (canSwitch ? this.userDataSyncStoreType : undefined) || defaultType;
const url = configuredStore?.url ||
(type === 'insiders' ? syncStore.insidersUrl
: type === 'stable' ? syncStore.stableUrl
: syncStore.url);
return {
url: URI.parse(url),
type,
defaultType,
defaultUrl: URI.parse(syncStore.url),
stableUrl: URI.parse(syncStore.stableUrl),
insidersUrl: URI.parse(syncStore.insidersUrl),
canSwitch,
authenticationProviders: Object.keys(syncStore.authenticationProviders).reduce((result, id) => {
result.push({ id, scopes: syncStore.authenticationProviders[id].scopes });
return result;
}, [])
};
}
return undefined;
}
};
AbstractUserDataSyncStoreManagementService = __decorate([
__param(0, IProductService),
__param(1, IConfigurationService),
__param(2, IStorageService)
], AbstractUserDataSyncStoreManagementService);
export { AbstractUserDataSyncStoreManagementService };
let UserDataSyncStoreManagementService = class UserDataSyncStoreManagementService extends AbstractUserDataSyncStoreManagementService {
previousConfigurationSyncStore;
constructor(productService, configurationService, storageService) {
super(productService, configurationService, storageService);
const previousConfigurationSyncStore = this.storageService.get(SYNC_PREVIOUS_STORE, -1 /* StorageScope.APPLICATION */);
if (previousConfigurationSyncStore) {
this.previousConfigurationSyncStore = JSON.parse(previousConfigurationSyncStore);
}
const syncStore = this.productService[CONFIGURATION_SYNC_STORE_KEY];
if (syncStore) {
this.storageService.store(SYNC_PREVIOUS_STORE, JSON.stringify(syncStore), -1 /* StorageScope.APPLICATION */, 1 /* StorageTarget.MACHINE */);
}
else {
this.storageService.remove(SYNC_PREVIOUS_STORE, -1 /* StorageScope.APPLICATION */);
}
}
async switch(type) {
if (type !== this.userDataSyncStoreType) {
this.userDataSyncStoreType = type;
this.updateUserDataSyncStore();
}
}
async getPreviousUserDataSyncStore() {
return this.toUserDataSyncStore(this.previousConfigurationSyncStore);
}
};
UserDataSyncStoreManagementService = __decorate([
__param(0, IProductService),
__param(1, IConfigurationService),
__param(2, IStorageService)
], UserDataSyncStoreManagementService);
export { UserDataSyncStoreManagementService };
let UserDataSyncStoreClient = class UserDataSyncStoreClient extends Disposable {
requestService;
logService;
storageService;
userDataSyncStoreUrl;
authToken;
commonHeadersPromise;
session;
_onTokenFailed = this._register(new Emitter());
onTokenFailed = this._onTokenFailed.event;
_onTokenSucceed = this._register(new Emitter());
onTokenSucceed = this._onTokenSucceed.event;
_donotMakeRequestsUntil = undefined;
get donotMakeRequestsUntil() { return this._donotMakeRequestsUntil; }
_onDidChangeDonotMakeRequestsUntil = this._register(new Emitter());
onDidChangeDonotMakeRequestsUntil = this._onDidChangeDonotMakeRequestsUntil.event;
constructor(userDataSyncStoreUrl, productService, requestService, logService, environmentService, fileService, storageService) {
super();
this.requestService = requestService;
this.logService = logService;
this.storageService = storageService;
this.updateUserDataSyncStoreUrl(userDataSyncStoreUrl);
this.commonHeadersPromise = getServiceMachineId(environmentService, fileService, storageService)
.then(uuid => {
const headers = {
'X-Client-Name': `${productService.applicationName}${isWeb ? '-web' : ''}`,
'X-Client-Version': productService.version,
};
if (productService.commit) {
headers['X-Client-Commit'] = productService.commit;
}
return headers;
});
/* A requests session that limits requests per sessions */
this.session = new RequestsSession(REQUEST_SESSION_LIMIT, REQUEST_SESSION_INTERVAL, this.requestService, this.logService);
this.initDonotMakeRequestsUntil();
this._register(toDisposable(() => {
if (this.resetDonotMakeRequestsUntilPromise) {
this.resetDonotMakeRequestsUntilPromise.cancel();
this.resetDonotMakeRequestsUntilPromise = undefined;
}
}));
}
setAuthToken(token, type) {
this.authToken = { token, type };
}
updateUserDataSyncStoreUrl(userDataSyncStoreUrl) {
this.userDataSyncStoreUrl = userDataSyncStoreUrl ? joinPath(userDataSyncStoreUrl, 'v1') : undefined;
}
initDonotMakeRequestsUntil() {
const donotMakeRequestsUntil = this.storageService.getNumber(DONOT_MAKE_REQUESTS_UNTIL_KEY, -1 /* StorageScope.APPLICATION */);
if (donotMakeRequestsUntil && Date.now() < donotMakeRequestsUntil) {
this.setDonotMakeRequestsUntil(new Date(donotMakeRequestsUntil));
}
}
resetDonotMakeRequestsUntilPromise = undefined;
setDonotMakeRequestsUntil(donotMakeRequestsUntil) {
if (this._donotMakeRequestsUntil?.getTime() !== donotMakeRequestsUntil?.getTime()) {
this._donotMakeRequestsUntil = donotMakeRequestsUntil;
if (this.resetDonotMakeRequestsUntilPromise) {
this.resetDonotMakeRequestsUntilPromise.cancel();
this.resetDonotMakeRequestsUntilPromise = undefined;
}
if (this._donotMakeRequestsUntil) {
this.storageService.store(DONOT_MAKE_REQUESTS_UNTIL_KEY, this._donotMakeRequestsUntil.getTime(), -1 /* StorageScope.APPLICATION */, 1 /* StorageTarget.MACHINE */);
this.resetDonotMakeRequestsUntilPromise = createCancelablePromise(token => timeout(this._donotMakeRequestsUntil.getTime() - Date.now(), token).then(() => this.setDonotMakeRequestsUntil(undefined)));
this.resetDonotMakeRequestsUntilPromise.then(null, e => null /* ignore error */);
}
else {
this.storageService.remove(DONOT_MAKE_REQUESTS_UNTIL_KEY, -1 /* StorageScope.APPLICATION */);
}
this._onDidChangeDonotMakeRequestsUntil.fire();
}
}
// #region Collection
async getAllCollections(headers = {}) {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url = joinPath(this.userDataSyncStoreUrl, 'collection').toString();
headers = { ...headers };
headers['Content-Type'] = 'application/json';
const context = await this.request(url, { type: 'GET', headers }, [], CancellationToken.None);
return (await asJson(context))?.map(({ id }) => id) || [];
}
async createCollection(headers = {}) {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url = joinPath(this.userDataSyncStoreUrl, 'collection').toString();
headers = { ...headers };
headers['Content-Type'] = Mimes.text;
const context = await this.request(url, { type: 'POST', headers }, [], CancellationToken.None);
const collectionId = await asTextOrError(context);
if (!collectionId) {
throw new UserDataSyncStoreError('Server did not return the collection id', url, "NoCollection" /* UserDataSyncErrorCode.NoCollection */, context.res.statusCode, context.res.headers[HEADER_OPERATION_ID]);
}
return collectionId;
}
async deleteCollection(collection, headers = {}) {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url = collection ? joinPath(this.userDataSyncStoreUrl, 'collection', collection).toString() : joinPath(this.userDataSyncStoreUrl, 'collection').toString();
headers = { ...headers };
await this.request(url, { type: 'DELETE', headers }, [], CancellationToken.None);
}
// #endregion
// #region Resource
async getAllResourceRefs(resource, collection) {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const uri = this.getResourceUrl(this.userDataSyncStoreUrl, collection, resource);
const headers = {};
const context = await this.request(uri.toString(), { type: 'GET', headers }, [], CancellationToken.None);
const result = await asJson(context) || [];
return result.map(({ url, created }) => ({ ref: relativePath(uri, uri.with({ path: url })), created: created * 1000 /* Server returns in seconds */ }));
}
async resolveResourceContent(resource, ref, collection, headers = {}) {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url = joinPath(this.getResourceUrl(this.userDataSyncStoreUrl, collection, resource), ref).toString();
headers = { ...headers };
headers['Cache-Control'] = 'no-cache';
const context = await this.request(url, { type: 'GET', headers }, [], CancellationToken.None);
const content = await asTextOrError(context);
return content;
}
async deleteResource(resource, ref, collection) {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url = ref !== null ? joinPath(this.getResourceUrl(this.userDataSyncStoreUrl, collection, resource), ref).toString() : this.getResourceUrl(this.userDataSyncStoreUrl, collection, resource).toString();
const headers = {};
await this.request(url, { type: 'DELETE', headers }, [], CancellationToken.None);
}
async deleteResources() {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url = joinPath(this.userDataSyncStoreUrl, 'resource').toString();
const headers = { 'Content-Type': Mimes.text };
await this.request(url, { type: 'DELETE', headers }, [], CancellationToken.None);
}
async readResource(resource, oldValue, collection, headers = {}) {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url = joinPath(this.getResourceUrl(this.userDataSyncStoreUrl, collection, resource), 'latest').toString();
headers = { ...headers };
// Disable caching as they are cached by synchronisers
headers['Cache-Control'] = 'no-cache';
if (oldValue) {
headers['If-None-Match'] = oldValue.ref;
}
const context = await this.request(url, { type: 'GET', headers }, [304], CancellationToken.None);
let userData = null;
if (context.res.statusCode === 304) {
userData = oldValue;
}
if (userData === null) {
const ref = context.res.headers['etag'];
if (!ref) {
throw new UserDataSyncStoreError('Server did not return the ref', url, "NoRef" /* UserDataSyncErrorCode.NoRef */, context.res.statusCode, context.res.headers[HEADER_OPERATION_ID]);
}
const content = await asTextOrError(context);
if (!content && context.res.statusCode === 304) {
throw new UserDataSyncStoreError('Empty response', url, "EmptyResponse" /* UserDataSyncErrorCode.EmptyResponse */, context.res.statusCode, context.res.headers[HEADER_OPERATION_ID]);
}
userData = { ref, content };
}
return userData;
}
async writeResource(resource, data, ref, collection, headers = {}) {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url = this.getResourceUrl(this.userDataSyncStoreUrl, collection, resource).toString();
headers = { ...headers };
headers['Content-Type'] = Mimes.text;
if (ref) {
headers['If-Match'] = ref;
}
const context = await this.request(url, { type: 'POST', data, headers }, [], CancellationToken.None);
const newRef = context.res.headers['etag'];
if (!newRef) {
throw new UserDataSyncStoreError('Server did not return the ref', url, "NoRef" /* UserDataSyncErrorCode.NoRef */, context.res.statusCode, context.res.headers[HEADER_OPERATION_ID]);
}
return newRef;
}
// #endregion
async manifest(oldValue, headers = {}) {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url = joinPath(this.userDataSyncStoreUrl, 'manifest').toString();
headers = { ...headers };
headers['Content-Type'] = 'application/json';
if (oldValue) {
headers['If-None-Match'] = oldValue.ref;
}
const context = await this.request(url, { type: 'GET', headers }, [304], CancellationToken.None);
let manifest = null;
if (context.res.statusCode === 304) {
manifest = oldValue;
}
if (!manifest) {
const ref = context.res.headers['etag'];
if (!ref) {
throw new UserDataSyncStoreError('Server did not return the ref', url, "NoRef" /* UserDataSyncErrorCode.NoRef */, context.res.statusCode, context.res.headers[HEADER_OPERATION_ID]);
}
const content = await asTextOrError(context);
if (!content && context.res.statusCode === 304) {
throw new UserDataSyncStoreError('Empty response', url, "EmptyResponse" /* UserDataSyncErrorCode.EmptyResponse */, context.res.statusCode, context.res.headers[HEADER_OPERATION_ID]);
}
if (content) {
manifest = { ...JSON.parse(content), ref };
}
}
const currentSessionId = this.storageService.get(USER_SESSION_ID_KEY, -1 /* StorageScope.APPLICATION */);
if (currentSessionId && manifest && currentSessionId !== manifest.session) {
// Server session is different from client session so clear cached session.
this.clearSession();
}
if (manifest === null && currentSessionId) {
// server session is cleared so clear cached session.
this.clearSession();
}
if (manifest) {
// update session
this.storageService.store(USER_SESSION_ID_KEY, manifest.session, -1 /* StorageScope.APPLICATION */, 1 /* StorageTarget.MACHINE */);
}
return manifest;
}
async clear() {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
await this.deleteCollection();
await this.deleteResources();
// clear cached session.
this.clearSession();
}
getResourceUrl(userDataSyncStoreUrl, collection, resource) {
return collection ? joinPath(userDataSyncStoreUrl, 'collection', collection, 'resource', resource) : joinPath(userDataSyncStoreUrl, 'resource', resource);
}
clearSession() {
this.storageService.remove(USER_SESSION_ID_KEY, -1 /* StorageScope.APPLICATION */);
this.storageService.remove(MACHINE_SESSION_ID_KEY, -1 /* StorageScope.APPLICATION */);
}
async request(url, options, successCodes, token) {
if (!this.authToken) {
throw new UserDataSyncStoreError('No Auth Token Available', url, "Unauthorized" /* UserDataSyncErrorCode.Unauthorized */, undefined, undefined);
}
if (this._donotMakeRequestsUntil && Date.now() < this._donotMakeRequestsUntil.getTime()) {
throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of too many requests (429).`, url, "TooManyRequestsAndRetryAfter" /* UserDataSyncErrorCode.TooManyRequestsAndRetryAfter */, undefined, undefined);
}
this.setDonotMakeRequestsUntil(undefined);
const commonHeaders = await this.commonHeadersPromise;
options.headers = {
...(options.headers || {}),
...commonHeaders,
'X-Account-Type': this.authToken.type,
'authorization': `Bearer ${this.authToken.token}`,
};
// Add session headers
this.addSessionHeaders(options.headers);
this.logService.trace('Sending request to server', { url, type: options.type, headers: { ...options.headers, ...{ authorization: undefined } } });
let context;
try {
context = await this.session.request(url, options, token);
}
catch (e) {
if (!(e instanceof UserDataSyncStoreError)) {
let code = "RequestFailed" /* UserDataSyncErrorCode.RequestFailed */;
const errorMessage = getErrorMessage(e).toLowerCase();
// Request timed out
if (errorMessage.includes('xhr timeout')) {
code = "RequestTimeout" /* UserDataSyncErrorCode.RequestTimeout */;
}
// Request protocol not supported
else if (errorMessage.includes('protocol') && errorMessage.includes('not supported')) {
code = "RequestProtocolNotSupported" /* UserDataSyncErrorCode.RequestProtocolNotSupported */;
}
// Request path not escaped
else if (errorMessage.includes('request path contains unescaped characters')) {
code = "RequestPathNotEscaped" /* UserDataSyncErrorCode.RequestPathNotEscaped */;
}
// Request header not an object
else if (errorMessage.includes('headers must be an object')) {
code = "RequestHeadersNotObject" /* UserDataSyncErrorCode.RequestHeadersNotObject */;
}
// Request canceled
else if (isCancellationError(e)) {
code = "RequestCanceled" /* UserDataSyncErrorCode.RequestCanceled */;
}
e = new UserDataSyncStoreError(`Connection refused for the request '${url}'.`, url, code, undefined, undefined);
}
this.logService.info('Request failed', url);
throw e;
}
const operationId = context.res.headers[HEADER_OPERATION_ID];
const requestInfo = { url, status: context.res.statusCode, 'execution-id': options.headers[HEADER_EXECUTION_ID], 'operation-id': operationId };
const isSuccess = isSuccessContext(context) || (context.res.statusCode && successCodes.indexOf(context.res.statusCode) !== -1);
let failureMessage = '';
if (isSuccess) {
this.logService.trace('Request succeeded', requestInfo);
}
else {
this.logService.info('Request failed', requestInfo);
failureMessage = await asText(context) || '';
}
if (context.res.statusCode === 401) {
this.authToken = undefined;
this._onTokenFailed.fire();
throw new UserDataSyncStoreError(`Request '${url}' failed because of Unauthorized (401).`, url, "Unauthorized" /* UserDataSyncErrorCode.Unauthorized */, context.res.statusCode, operationId);
}
this._onTokenSucceed.fire();
if (context.res.statusCode === 404) {
throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because the requested resource is not found (404).`, url, "NotFound" /* UserDataSyncErrorCode.NotFound */, context.res.statusCode, operationId);
}
if (context.res.statusCode === 405) {
throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because the requested endpoint is not found (405). ${failureMessage}`, url, "MethodNotFound" /* UserDataSyncErrorCode.MethodNotFound */, context.res.statusCode, operationId);
}
if (context.res.statusCode === 409) {
throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of Conflict (409). There is new data for this resource. Make the request again with latest data.`, url, "Conflict" /* UserDataSyncErrorCode.Conflict */, context.res.statusCode, operationId);
}
if (context.res.statusCode === 410) {
throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because the requested resource is not longer available (410).`, url, "Gone" /* UserDataSyncErrorCode.Gone */, context.res.statusCode, operationId);
}
if (context.res.statusCode === 412) {
throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of Precondition Failed (412). There is new data for this resource. Make the request again with latest data.`, url, "PreconditionFailed" /* UserDataSyncErrorCode.PreconditionFailed */, context.res.statusCode, operationId);
}
if (context.res.statusCode === 413) {
throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of too large payload (413).`, url, "TooLarge" /* UserDataSyncErrorCode.TooLarge */, context.res.statusCode, operationId);
}
if (context.res.statusCode === 426) {
throw new UserDataSyncStoreError(`${options.type} request '${url}' failed with status Upgrade Required (426). Please upgrade the client and try again.`, url, "UpgradeRequired" /* UserDataSyncErrorCode.UpgradeRequired */, context.res.statusCode, operationId);
}
if (context.res.statusCode === 429) {
const retryAfter = context.res.headers['retry-after'];
if (retryAfter) {
this.setDonotMakeRequestsUntil(new Date(Date.now() + (parseInt(retryAfter) * 1000)));
throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of too many requests (429).`, url, "TooManyRequestsAndRetryAfter" /* UserDataSyncErrorCode.TooManyRequestsAndRetryAfter */, context.res.statusCode, operationId);
}
else {
throw new UserDataSyncStoreError(`${options.type} request '${url}' failed because of too many requests (429).`, url, "RemoteTooManyRequests" /* UserDataSyncErrorCode.TooManyRequests */, context.res.statusCode, operationId);
}
}
if (!isSuccess) {
throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, url, "Unknown" /* UserDataSyncErrorCode.Unknown */, context.res.statusCode, operationId);
}
return context;
}
addSessionHeaders(headers) {
let machineSessionId = this.storageService.get(MACHINE_SESSION_ID_KEY, -1 /* StorageScope.APPLICATION */);
if (machineSessionId === undefined) {
machineSessionId = generateUuid();
this.storageService.store(MACHINE_SESSION_ID_KEY, machineSessionId, -1 /* StorageScope.APPLICATION */, 1 /* StorageTarget.MACHINE */);
}
headers['X-Machine-Session-Id'] = machineSessionId;
const userSessionId = this.storageService.get(USER_SESSION_ID_KEY, -1 /* StorageScope.APPLICATION */);
if (userSessionId !== undefined) {
headers['X-User-Session-Id'] = userSessionId;
}
}
};
UserDataSyncStoreClient = __decorate([
__param(1, IProductService),
__param(2, IRequestService),
__param(3, IUserDataSyncLogService),
__param(4, IEnvironmentService),
__param(5, IFileService),
__param(6, IStorageService)
], UserDataSyncStoreClient);
export { UserDataSyncStoreClient };
let UserDataSyncStoreService = class UserDataSyncStoreService extends UserDataSyncStoreClient {
_serviceBrand;
constructor(userDataSyncStoreManagementService, productService, requestService, logService, environmentService, fileService, storageService) {
super(userDataSyncStoreManagementService.userDataSyncStore?.url, productService, requestService, logService, environmentService, fileService, storageService);
this._register(userDataSyncStoreManagementService.onDidChangeUserDataSyncStore(() => this.updateUserDataSyncStoreUrl(userDataSyncStoreManagementService.userDataSyncStore?.url)));
}
};
UserDataSyncStoreService = __decorate([
__param(0, IUserDataSyncStoreManagementService),
__param(1, IProductService),
__param(2, IRequestService),
__param(3, IUserDataSyncLogService),
__param(4, IEnvironmentService),
__param(5, IFileService),
__param(6, IStorageService)
], UserDataSyncStoreService);
export { UserDataSyncStoreService };
export class RequestsSession {
limit;
interval;
requestService;
logService;
requests = [];
startTime = undefined;
constructor(limit, interval, /* in ms */ requestService, logService) {
this.limit = limit;
this.interval = interval;
this.requestService = requestService;
this.logService = logService;
}
request(url, options, token) {
if (this.isExpired()) {
this.reset();
}
options.url = url;
if (this.requests.length >= this.limit) {
this.logService.info('Too many requests', ...this.requests);
throw new UserDataSyncStoreError(`Too many requests. Only ${this.limit} requests allowed in ${this.interval / (1000 * 60)} minutes.`, url, "LocalTooManyRequests" /* UserDataSyncErrorCode.LocalTooManyRequests */, undefined, undefined);
}
this.startTime = this.startTime || new Date();
this.requests.push(url);
return this.requestService.request(options, token);
}
isExpired() {
return this.startTime !== undefined && new Date().getTime() - this.startTime.getTime() > this.interval;
}
reset() {
this.requests = [];
this.startTime = undefined;
}
}