1
>
/*---------------------------------------------------------------------------------------------
storage.ts
2
>
* Copyright (c) Microsoft Corporation. All rights reserved.
3
>
* Licensed under the MIT License. See License.txt in the project root for license information.
4
>
*--------------------------------------------------------------------------------------------*/
5
>
6
>
import { Promises, RunOnceScheduler, runWhenGlobalIdle } from '../../../base/common/async.js';
7
>
import { Emitter, Event, PauseableEmitter } from '../../../base/common/event.js';
8
>
import { Disposable, DisposableStore, dispose, MutableDisposable } from '../../../base/common/lifecycle.js';
9
>
import { mark } from '../../../base/common/performance.js';
10
>
import { isUndefinedOrNull } from '../../../base/common/types.js';
11
>
import { InMemoryStorageDatabase, IStorage, IStorageChangeEvent, Storage, StorageHint, StorageValue } from '../../../base/parts/storage/common/storage.js';
12
>
import { createDecorator } from '../../instantiation/common/instantiation.js';
13
>
import { isUserDataProfile, IUserDataProfile } from '../../userDataProfile/common/userDataProfile.js';
14
>
import { IAnyWorkspaceIdentifier } from '../../workspace/common/workspace.js';
15
>
16
>
export const IS_NEW_KEY = '__$__isNewStorageMarker';
17
>
export const TARGET_KEY = '__$__targetStorageMarker';
18
>
19
>
export const IStorageService = createDecorator<IStorageService>('storageService');
20
>
21
>
export enum WillSaveStateReason {
22
>
23
>
/**
24
>
* No specific reason to save state.
25
>
*/
26
>
NONE,
27
>
28
>
/**
29
>
* A hint that the workbench is about to shutdown.
30
>
*/
31
>
SHUTDOWN
32
>
}
33
>
34
>
export interface IWillSaveStateEvent {
35
>
readonly reason: WillSaveStateReason;
36
>
}
37
>
38
>
export interface IStorageEntry {
39
>
readonly key: string;
40
>
readonly value: StorageValue;
41
>
readonly scope: StorageScope;
42
>
readonly target: StorageTarget;
43
>
}
44
>
45
>
export interface IWorkspaceStorageValueChangeEvent extends IStorageValueChangeEvent {
46
>
readonly scope: StorageScope.WORKSPACE;
47
>
}
48
>
49
>
export interface IProfileStorageValueChangeEvent extends IStorageValueChangeEvent {
50
>
readonly scope: StorageScope.PROFILE;
51
>
}
52
>
53
>
export interface IApplicationStorageValueChangeEvent extends IStorageValueChangeEvent {
54
>
readonly scope: StorageScope.APPLICATION;
55
>
}
56
>
57
>
export interface IApplicationSharedStorageValueChangeEvent extends IStorageValueChangeEvent {
58
>
readonly scope: StorageScope.APPLICATION_SHARED;
59
>
}
60
>
61
>
export interface IStorageService {
62
>
63
>
readonly _serviceBrand: undefined;
64
>
65
>
/**
66
>
* Emitted whenever data is updated or deleted on the given
67
>
* scope and optional key.
68
>
*
69
>
* @param scope the `StorageScope` to listen to changes
70
>
* @param key the optional key to filter for or all keys of
71
>
* the scope if `undefined`
72
>
*/
73
>
onDidChangeValue(scope: StorageScope.WORKSPACE, key: string | undefined, disposable: DisposableStore): Event<IWorkspaceStorageValueChangeEvent>;
74
>
onDidChangeValue(scope: StorageScope.PROFILE, key: string | undefined, disposable: DisposableStore): Event<IProfileStorageValueChangeEvent>;
75
>
onDidChangeValue(scope: StorageScope.APPLICATION, key: string | undefined, disposable: DisposableStore): Event<IApplicationStorageValueChangeEvent>;
76
>
onDidChangeValue(scope: StorageScope.APPLICATION_SHARED, key: string | undefined, disposable: DisposableStore): Event<IApplicationSharedStorageValueChangeEvent>;
77
>
onDidChangeValue(scope: StorageScope, key: string | undefined, disposable: DisposableStore): Event<IStorageValueChangeEvent>;
78
>
79
>
/**
80
>
* Emitted whenever target of a storage entry changes.
81
>
*/
82
>
readonly onDidChangeTarget: Event<IStorageTargetChangeEvent>;
83
>
84
>
/**
85
>
* Emitted when the storage is about to persist. This is the right time
86
>
* to persist data to ensure it is stored before the application shuts
87
>
* down.
88
>
*
89
>
* The will save state event allows to optionally ask for the reason of
90
>
* saving the state, e.g. to find out if the state is saved due to a
91
>
* shutdown.
92
>
*
93
>
* Note: this event may be fired many times, not only on shutdown to prevent
94
>
* loss of state in situations where the shutdown is not sufficient to
95
>
* persist the data properly.
96
>
*/
97
>
readonly onWillSaveState: Event<IWillSaveStateEvent>;
98
>
99
>
/**
100
>
* Retrieve an element stored with the given key from storage. Use
101
>
* the provided `defaultValue` if the element is `null` or `undefined`.
102
>
*
103
>
* @param scope allows to define the scope of the storage operation
104
>
* to either the current workspace only, all workspaces or all profiles.
105
>
*/
106
>
get(key: string, scope: StorageScope, fallbackValue: string): string;
107
>
get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined;
108
>
109
>
/**
110
>
* Retrieve an element stored with the given key from storage. Use
111
>
* the provided `defaultValue` if the element is `null` or `undefined`.
112
>
* The element will be converted to a `boolean`.
113
>
*
114
>
* @param scope allows to define the scope of the storage operation
115
>
* to either the current workspace only, all workspaces or all profiles.
116
>
*/
117
>
getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean;
118
>
getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined;
119
>
120
>
/**
121
>
* Retrieve an element stored with the given key from storage. Use
122
>
* the provided `defaultValue` if the element is `null` or `undefined`.
123
>
* The element will be converted to a `number` using `parseInt` with a
124
>
* base of `10`.
125
>
*
126
>
* @param scope allows to define the scope of the storage operation
127
>
* to either the current workspace only, all workspaces or all profiles.
128
>
*/
129
>
getNumber(key: string, scope: StorageScope, fallbackValue: number): number;
130
>
getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined;
131
>
132
>
/**
133
>
* Retrieve an element stored with the given key from storage. Use
134
>
* the provided `defaultValue` if the element is `null` or `undefined`.
135
>
* The element will be converted to a `object` using `JSON.parse`.
136
>
*
137
>
* @param scope allows to define the scope of the storage operation
138
>
* to either the current workspace only, all workspaces or all profiles.
139
>
*/
140
>
getObject<T extends object>(key: string, scope: StorageScope, fallbackValue: T): T;
141
>
getObject<T extends object>(key: string, scope: StorageScope, fallbackValue?: T): T | undefined;
142
>
143
>
/**
144
>
* Store a value under the given key to storage. The value will be
145
>
* converted to a `string`. Storing either `undefined` or `null` will
146
>
* remove the entry under the key.
147
>
*
148
>
* @param scope allows to define the scope of the storage operation
149
>
* to either the current workspace only, all workspaces or all profiles.
150
>
*
151
>
* @param target allows to define the target of the storage operation
152
>
* to either the current machine or user.
153
>
*/
154
>
store(key: string, value: StorageValue, scope: StorageScope, target: StorageTarget): void;
155
>
156
>
/**
157
>
* Allows to store multiple values in a bulk operation. Events will only
158
>
* be emitted when all values have been stored.
159
>
*
160
>
* @param external a hint to indicate the source of the operation is external,
161
>
* such as settings sync or profile changes.
162
>
*/
163
>
storeAll(entries: Array<IStorageEntry>, external: boolean): void;
164
>
165
>
/**
166
>
* Delete an element stored under the provided key from storage.
167
>
*
168
>
* The scope argument allows to define the scope of the storage
169
>
* operation to either the current workspace only, all workspaces
170
>
* or all profiles.
171
>
*/
172
>
remove(key: string, scope: StorageScope): void;
173
>
174
>
/**
175
>
* Returns all the keys used in the storage for the provided `scope`
176
>
* and `target`.
177
>
*
178
>
* Note: this will NOT return all keys stored in the storage layer.
179
>
* Some keys may not have an associated `StorageTarget` and thus
180
>
* will be excluded from the results.
181
>
*
182
>
* @param scope allows to define the scope for the keys
183
>
* to either the current workspace only, all workspaces or all profiles.
184
>
*
185
>
* @param target allows to define the target for the keys
186
>
* to either the current machine or user.
187
>
*/
188
>
keys(scope: StorageScope, target: StorageTarget): string[];
189
>
190
>
/**
191
>
* Log the contents of the storage to the console.
192
>
*/
193
>
log(): void;
194
>
195
>
/**
196
>
* Returns true if the storage service handles the provided scope.
197
>
*/
198
>
hasScope(scope: IAnyWorkspaceIdentifier | IUserDataProfile): boolean;
199
>
200
>
/**
201
>
* Switch storage to another workspace or profile. Optionally preserve the
202
>
* current data to the new storage.
203
>
*/
204
>
switch(to: IAnyWorkspaceIdentifier | IUserDataProfile, preserveData: boolean): Promise<void>;
205
>
206
>
/**
207
>
* Whether the storage for the given scope was created during this session or
208
>
* existed before.
209
>
*/
210
>
isNew(scope: StorageScope): boolean;
211
>
212
>
/**
213
>
* Attempts to reduce the DB size via optimization commands if supported.
214
>
*/
215
>
optimize(scope: StorageScope): Promise<void>;
216
>
217
>
/**
218
>
* Allows to flush state, e.g. in cases where a shutdown is
219
>
* imminent. This will send out the `onWillSaveState` to ask
220
>
* everyone for latest state.
221
>
*
222
>
* @returns a `Promise` that can be awaited on when all updates
223
>
* to the underlying storage have been flushed.
224
>
*/
225
>
flush(reason?: WillSaveStateReason): Promise<void>;
226
>
}
227
>
228
>
export const enum StorageScope {
229
>
230
>
/**
231
>
* The stored data will be scoped to all workspaces across all profiles
232
>
* and shared across VS Code and Sessions app.
233
>
*/
234
>
APPLICATION_SHARED = -2,
235
>
236
>
/**
237
>
* The stored data will be scoped to all workspaces across all profiles.
238
>
*/
239
>
APPLICATION = -1,
240
>
241
>
/**
242
>
* The stored data will be scoped to all workspaces of the same profile.
243
>
*/
244
>
PROFILE = 0,
245
>
246
>
/**
247
>
* The stored data will be scoped to the current workspace.
248
>
*/
249
>
WORKSPACE = 1
250
>
}
251
>
252
>
export const enum StorageTarget {
253
>
254
>
/**
255
>
* The stored data is user specific and applies across machines.
256
>
*/
257
>
USER,
258
>
259
>
/**
260
>
* The stored data is machine specific.
261
>
*/
262
>
MACHINE
263
>
}
264
>
265
>
export interface IStorageValueChangeEvent {
266
>
267
>
/**
268
>
* The scope for the storage entry that changed
269
>
* or was removed.
270
>
*/
271
>
readonly scope: StorageScope;
272
>
273
>
/**
274
>
* The `key` of the storage entry that was changed
275
>
* or was removed.
276
>
*/
277
>
readonly key: string;
278
>
279
>
/**
280
>
* The `target` can be `undefined` if a key is being
281
>
* removed.
282
>
*/
283
>
readonly target: StorageTarget | undefined;
284
>
285
>
/**
286
>
* A hint how the storage change event was triggered. If
287
>
* `true`, the storage change was triggered by an external
288
>
* source, such as:
289
>
* - another process (for example another window)
290
>
* - operations such as settings sync or profiles change
291
>
*/
292
>
readonly external?: boolean;
293
>
}
294
>
295
>
export interface IStorageTargetChangeEvent {
296
>
297
>
/**
298
>
* The scope for the target that changed. Listeners
299
>
* should use `keys(scope, target)` to get an updated
300
>
* list of keys for the given `scope` and `target`.
301
>
*/
302
>
readonly scope: StorageScope;
303
>
}
304
>
305
>
interface IKeyTargets {
306
>
[key: string]: StorageTarget;
307
>
}
308
>
309
>
export interface IStorageServiceOptions {
310
>
readonly flushInterval: number;
311
>
}
312
>
313
>
export function loadKeyTargets(storage: IStorage): IKeyTargets {
314
const keysRaw = storage.get(TARGET_KEY);
315
if (keysRaw) {