@sussudio/platform
Version:
Internal APIs for VS Code's service injection the base services.
138 lines (137 loc) • 4.46 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from '@sussudio/base/common/event.mjs';
import { Disposable } from '@sussudio/base/common/lifecycle.mjs';
import { revive } from '@sussudio/base/common/marshalling.mjs';
import { reviveIdentifier } from '../../workspace/common/workspace.mjs';
export class StorageDatabaseChannel extends Disposable {
logService;
storageMainService;
static STORAGE_CHANGE_DEBOUNCE_TIME = 100;
onDidChangeApplicationStorageEmitter = this._register(new Emitter());
mapProfileToOnDidChangeProfileStorageEmitter = new Map();
constructor(logService, storageMainService) {
super();
this.logService = logService;
this.storageMainService = storageMainService;
this.registerStorageChangeListeners(
storageMainService.applicationStorage,
this.onDidChangeApplicationStorageEmitter,
);
}
//#region Storage Change Events
registerStorageChangeListeners(storage, emitter) {
// Listen for changes in provided storage to send to listeners
// that are listening. Use a debouncer to reduce IPC traffic.
this._register(
Event.debounce(
storage.onDidChangeStorage,
(prev, cur) => {
if (!prev) {
prev = [cur];
} else {
prev.push(cur);
}
return prev;
},
StorageDatabaseChannel.STORAGE_CHANGE_DEBOUNCE_TIME,
)((events) => {
if (events.length) {
emitter.fire(this.serializeStorageChangeEvents(events, storage));
}
}),
);
}
serializeStorageChangeEvents(events, storage) {
const changed = new Map();
const deleted = new Set();
events.forEach((event) => {
const existing = storage.get(event.key);
if (typeof existing === 'string') {
changed.set(event.key, existing);
} else {
deleted.add(event.key);
}
});
return {
changed: Array.from(changed.entries()),
deleted: Array.from(deleted.values()),
};
}
listen(_, event, arg) {
switch (event) {
case 'onDidChangeStorage': {
const profile = arg.profile ? revive(arg.profile) : undefined;
// Without profile: application scope
if (!profile) {
return this.onDidChangeApplicationStorageEmitter.event;
}
// With profile: profile scope for the profile
let profileStorageChangeEmitter = this.mapProfileToOnDidChangeProfileStorageEmitter.get(profile.id);
if (!profileStorageChangeEmitter) {
profileStorageChangeEmitter = this._register(new Emitter());
this.registerStorageChangeListeners(
this.storageMainService.profileStorage(profile),
profileStorageChangeEmitter,
);
this.mapProfileToOnDidChangeProfileStorageEmitter.set(profile.id, profileStorageChangeEmitter);
}
return profileStorageChangeEmitter.event;
}
}
throw new Error(`Event not found: ${event}`);
}
//#endregion
async call(_, command, arg) {
const profile = arg.profile ? revive(arg.profile) : undefined;
const workspace = reviveIdentifier(arg.workspace);
// Get storage to be ready
const storage = await this.withStorageInitialized(profile, workspace);
// handle call
switch (command) {
case 'getItems': {
return Array.from(storage.items.entries());
}
case 'updateItems': {
const items = arg;
if (items.insert) {
for (const [key, value] of items.insert) {
storage.set(key, value);
}
}
items.delete?.forEach((key) => storage.delete(key));
break;
}
case 'isUsed': {
const path = arg.payload;
if (typeof path === 'string') {
return this.storageMainService.isUsed(path);
}
}
default:
throw new Error(`Call not found: ${command}`);
}
}
async withStorageInitialized(profile, workspace) {
let storage;
if (workspace) {
storage = this.storageMainService.workspaceStorage(workspace);
} else if (profile) {
storage = this.storageMainService.profileStorage(profile);
} else {
storage = this.storageMainService.applicationStorage;
}
try {
await storage.init();
} catch (error) {
this.logService.error(
`StorageIPC#init: Unable to init ${
workspace ? 'workspace' : profile ? 'profile' : 'application'
} storage due to ${error}`,
);
}
return storage;
}
}