From f5459e93ed8e1a592572ad9b03dab7ff5714e924 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 27 Mar 2023 00:36:18 -0400 Subject: [PATCH] Adds type-safe core configuration checks --- src/annotations/annotations.ts | 3 +- src/commands/git/push.ts | 6 +- src/constants.ts | 24 +++++-- src/env/node/fetch.ts | 11 ++- src/env/node/git/localGitProvider.ts | 20 +++--- src/git/gitProviderService.ts | 14 ++-- src/git/models/repository.ts | 5 +- src/plus/github/github.ts | 3 +- src/plus/gitlab/gitlab.ts | 3 +- src/system/configuration.ts | 82 +++++++++++++--------- src/telemetry/telemetry.ts | 2 +- src/webviews/commitDetails/commitDetailsWebview.ts | 14 ++-- src/webviews/rebase/rebaseEditor.ts | 3 + src/webviews/webviewWithConfigBase.ts | 18 +++-- 14 files changed, 127 insertions(+), 81 deletions(-) diff --git a/src/annotations/annotations.ts b/src/annotations/annotations.ts index 4a6ef82..8e263ba 100644 --- a/src/annotations/annotations.ts +++ b/src/annotations/annotations.ts @@ -9,6 +9,7 @@ import type { import { OverviewRulerLane, ThemeColor, Uri, window } from 'vscode'; import type { Config } from '../config'; import { HeatmapLocations } from '../config'; +import type { CoreConfiguration } from '../constants'; import { Colors, GlyphChars } from '../constants'; import type { CommitFormatOptions } from '../git/formatters/commitFormatter'; import { CommitFormatter } from '../git/formatters/commitFormatter'; @@ -192,7 +193,7 @@ export function getGutterRenderOptions( let width; if (chars >= 0) { - const spacing = configuration.getAny('editor.letterSpacing'); + const spacing = configuration.getAny('editor.letterSpacing'); if (spacing != null && spacing !== 0) { width = `calc(${chars}ch + ${Math.round(chars * spacing) + (avatars ? 13 : -6)}px)`; } else { diff --git a/src/commands/git/push.ts b/src/commands/git/push.ts index 0f47514..14c49c2 100644 --- a/src/commands/git/push.ts +++ b/src/commands/git/push.ts @@ -1,4 +1,5 @@ -import { CoreGitConfiguration, GlyphChars } from '../../constants'; +import type { CoreGitConfiguration } from '../../constants'; +import { GlyphChars } from '../../constants'; import type { Container } from '../../container'; import { getRemoteNameFromBranchName } from '../../git/models/branch'; import type { GitBranchReference, GitReference } from '../../git/models/reference'; @@ -160,7 +161,8 @@ export class PushGitCommand extends QuickCommand { } private async *confirmStep(state: PushStepState, context: Context): AsyncStepResultGenerator { - const useForceWithLease = configuration.getAny(CoreGitConfiguration.UseForcePushWithLease) ?? false; + const useForceWithLease = + configuration.getAny('git.useForcePushWithLease') ?? false; let step: QuickPickStep>; diff --git a/src/constants.ts b/src/constants.ts index 6123bc8..293c816 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -357,12 +357,24 @@ export type CoreGitCommands = | 'git.pushForce' | 'git.undoCommit'; -export const enum CoreGitConfiguration { - AutoRepositoryDetection = 'git.autoRepositoryDetection', - RepositoryScanMaxDepth = 'git.repositoryScanMaxDepth', - FetchOnPull = 'git.fetchOnPull', - UseForcePushWithLease = 'git.useForcePushWithLease', -} +export type CoreConfiguration = + | 'editor.letterSpacing' + | 'files.encoding' + | 'files.exclude' + | 'http.proxy' + | 'http.proxySupport' + | 'http.proxyStrictSSL' + | 'search.exclude' + | 'workbench.editorAssociations' + | 'workbench.tree.renderIndentGuides'; + +export type CoreGitConfiguration = + | 'git.autoRepositoryDetection' + | 'git.enabled' + | 'git.fetchOnPull' + | 'git.path' + | 'git.repositoryScanMaxDepth' + | 'git.useForcePushWithLease'; export const enum GlyphChars { AngleBracketLeftHeavy = '\u2770', diff --git a/src/env/node/fetch.ts b/src/env/node/fetch.ts index 232c13e..9354060 100644 --- a/src/env/node/fetch.ts +++ b/src/env/node/fetch.ts @@ -2,6 +2,7 @@ import * as process from 'process'; import * as url from 'url'; import { HttpsProxyAgent } from 'https-proxy-agent'; import fetch from 'node-fetch'; +import type { CoreConfiguration } from '../../constants'; import { configuration } from '../../system/configuration'; import { Logger } from '../../system/logger'; @@ -16,7 +17,7 @@ export function getProxyAgent(strictSSL?: boolean): HttpsProxyAgent | undefined proxyUrl = proxy.url ?? undefined; strictSSL = strictSSL ?? proxy.strictSSL; } else { - const proxySupport = configuration.getAny<'off' | 'on' | 'override' | 'fallback'>( + const proxySupport = configuration.getAny( 'http.proxySupport', undefined, 'override', @@ -25,8 +26,12 @@ export function getProxyAgent(strictSSL?: boolean): HttpsProxyAgent | undefined if (proxySupport === 'off') { strictSSL = strictSSL ?? true; } else { - strictSSL = strictSSL ?? configuration.getAny('http.proxyStrictSSL', undefined, true); - proxyUrl = configuration.getAny('http.proxy') || process.env.HTTPS_PROXY || process.env.HTTP_PROXY; + strictSSL = + strictSSL ?? configuration.getAny('http.proxyStrictSSL', undefined, true); + proxyUrl = + configuration.getAny('http.proxy') || + process.env.HTTPS_PROXY || + process.env.HTTP_PROXY; } } diff --git a/src/env/node/git/localGitProvider.ts b/src/env/node/git/localGitProvider.ts index 7457c04..164b27f 100644 --- a/src/env/node/git/localGitProvider.ts +++ b/src/env/node/git/localGitProvider.ts @@ -14,7 +14,8 @@ import type { GitExtension, } from '../../../@types/vscode.git'; import { getCachedAvatarUri } from '../../../avatars'; -import { CoreGitConfiguration, GlyphChars, Schemes } from '../../../constants'; +import type { CoreConfiguration, CoreGitConfiguration } from '../../../constants'; +import { GlyphChars, Schemes } from '../../../constants'; import type { Container } from '../../../container'; import { emojify } from '../../../emojis'; import { Features } from '../../../features'; @@ -304,7 +305,7 @@ export class LocalGitProvider implements GitProvider, Disposable { private async findGit(): Promise { const scope = getLogScope(); - if (!configuration.getAny('git.enabled', null, true)) { + if (!configuration.getAny('git.enabled', null, true)) { Logger.log(scope, 'Built-in Git is disabled ("git.enabled": false)'); void showGitDisabledErrorMessage(); @@ -335,7 +336,8 @@ export class LocalGitProvider implements GitProvider, Disposable { void subscribeToScmOpenCloseRepository.call(this); const potentialGitPaths = - configuration.getAny('git.path') ?? this.container.storage.getWorkspace('gitPath'); + configuration.getAny('git.path') ?? + this.container.storage.getWorkspace('gitPath'); const start = hrtime(); @@ -389,8 +391,8 @@ export class LocalGitProvider implements GitProvider, Disposable { void (await this.ensureGit()); const autoRepositoryDetection = - configuration.getAny( - CoreGitConfiguration.AutoRepositoryDetection, + configuration.getAny( + 'git.autoRepositoryDetection', ) ?? true; const folder = workspace.getWorkspaceFolder(uri); @@ -576,7 +578,7 @@ export class LocalGitProvider implements GitProvider, Disposable { depth = depth ?? configuration.get('advanced.repositorySearchDepth', folder.uri) ?? - configuration.getAny(CoreGitConfiguration.RepositoryScanMaxDepth, folder.uri, 1); + configuration.getAny('git.repositoryScanMaxDepth', folder.uri, 1); Logger.log(scope, `searching (depth=${depth})...`); @@ -602,8 +604,8 @@ export class LocalGitProvider implements GitProvider, Disposable { // Get any specified excludes -- this is a total hack, but works for some simple cases and something is better than nothing :) const excludedConfig = { - ...configuration.getAny>('files.exclude', folder.uri, {}), - ...configuration.getAny>('search.exclude', folder.uri, {}), + ...configuration.getAny>('files.exclude', folder.uri, {}), + ...configuration.getAny>('search.exclude', folder.uri, {}), }; const excludedPaths = [ @@ -4920,7 +4922,7 @@ export class LocalGitProvider implements GitProvider, Disposable { } async function getEncoding(uri: Uri): Promise { - const encoding = configuration.getAny('files.encoding', uri); + const encoding = configuration.getAny('files.encoding', uri); if (encoding == null || encoding === 'utf8') return 'utf8'; const encodingExists = (await import(/* webpackChunkName: "encoding" */ 'iconv-lite')).encodingExists; diff --git a/src/git/gitProviderService.ts b/src/git/gitProviderService.ts index dacebf0..c4a14e2 100644 --- a/src/git/gitProviderService.ts +++ b/src/git/gitProviderService.ts @@ -11,7 +11,8 @@ import type { } from 'vscode'; import { Disposable, EventEmitter, FileType, ProgressLocation, Uri, window, workspace } from 'vscode'; import { resetAvatarCache } from '../avatars'; -import { ContextKeys, CoreGitConfiguration, GlyphChars, Schemes } from '../constants'; +import type { CoreGitConfiguration } from '../constants'; +import { ContextKeys, GlyphChars, Schemes } from '../constants'; import type { Container } from '../container'; import { setContext } from '../context'; import { AccessDeniedError, ProviderNotFoundError } from '../errors'; @@ -505,9 +506,10 @@ export class GitProviderService implements Disposable { this.updateContext(); } - const autoRepositoryDetection = configuration.getAny( - CoreGitConfiguration.AutoRepositoryDetection, - ); + const autoRepositoryDetection = configuration.getAny< + CoreGitConfiguration, + boolean | 'subFolders' | 'openEditors' + >('git.autoRepositoryDetection'); if (this.container.telemetry.enabled) { queueMicrotask(() => @@ -2311,8 +2313,8 @@ export class GitProviderService implements Disposable { } const autoRepositoryDetection = - configuration.getAny( - CoreGitConfiguration.AutoRepositoryDetection, + configuration.getAny( + 'git.autoRepositoryDetection', ) ?? true; const closed = diff --git a/src/git/models/repository.ts b/src/git/models/repository.ts index 0d3addc..8d4893c 100644 --- a/src/git/models/repository.ts +++ b/src/git/models/repository.ts @@ -3,7 +3,8 @@ import { Disposable, EventEmitter, ProgressLocation, RelativePattern, Uri, windo import { md5 } from '@env/crypto'; import { ForcePushMode } from '../../@types/vscode.git.enums'; import type { CreatePullRequestActionContext } from '../../api/gitlens'; -import { CoreGitConfiguration, Schemes } from '../../constants'; +import type { CoreGitConfiguration } from '../../constants'; +import { Schemes } from '../../constants'; import type { Container } from '../../container'; import type { FeatureAccess, Features, PlusFeatures } from '../../features'; import { showCreatePullRequestPrompt, showGenericErrorMessage } from '../../messages'; @@ -804,7 +805,7 @@ export class Repository implements Disposable { const upstream = await this.hasUpstreamBranch(); if (upstream) { void (await executeCoreGitCommand(options?.rebase ? 'git.pullRebase' : 'git.pull', this.path)); - } else if (configuration.getAny(CoreGitConfiguration.FetchOnPull, Uri.file(this.path))) { + } else if (configuration.getAny('git.fetchOnPull', Uri.file(this.path))) { await this.container.git.fetch(this.path); } diff --git a/src/plus/github/github.ts b/src/plus/github/github.ts index a970b59..3d685ee 100644 --- a/src/plus/github/github.ts +++ b/src/plus/github/github.ts @@ -7,6 +7,7 @@ import type { Event } from 'vscode'; import { Disposable, EventEmitter, Uri, window } from 'vscode'; import { fetch, getProxyAgent, wrapForForcedInsecureSSL } from '@env/fetch'; import { isWeb } from '@env/platform'; +import type { CoreConfiguration } from '../../constants'; import type { Container } from '../../container'; import { AuthenticationError, @@ -196,7 +197,7 @@ export class GitHubApi implements Disposable { } }), configuration.onDidChangeOther(e => { - if (e.affectsConfiguration('http.proxy') || e.affectsConfiguration('http.proxyStrictSSL')) { + if (configuration.changedAny(e, ['http.proxy', 'http.proxyStrictSSL'])) { this.resetCaches(); } }), diff --git a/src/plus/gitlab/gitlab.ts b/src/plus/gitlab/gitlab.ts index c845982..9c75cf5 100644 --- a/src/plus/gitlab/gitlab.ts +++ b/src/plus/gitlab/gitlab.ts @@ -3,6 +3,7 @@ import { Disposable, Uri, window } from 'vscode'; import type { RequestInit, Response } from '@env/fetch'; import { fetch, getProxyAgent, wrapForForcedInsecureSSL } from '@env/fetch'; import { isWeb } from '@env/platform'; +import type { CoreConfiguration } from '../../constants'; import type { Container } from '../../container'; import { AuthenticationError, @@ -46,7 +47,7 @@ export class GitLabApi implements Disposable { } }), configuration.onDidChangeOther(e => { - if (e.affectsConfiguration('http.proxy') || e.affectsConfiguration('http.proxyStrictSSL')) { + if (configuration.changedAny(e, ['http.proxy', 'http.proxyStrictSSL'])) { this._projectIds.clear(); this._proxyAgents.clear(); } diff --git a/src/system/configuration.ts b/src/system/configuration.ts index 456e33d..ac5eb1e 100644 --- a/src/system/configuration.ts +++ b/src/system/configuration.ts @@ -64,23 +64,23 @@ export class Configuration { queueMicrotask(() => (this._overrides = undefined)); } - get(section: T, scope?: ConfigurationScope | null): ConfigPathValue; - get( - section: T, + get(section: S, scope?: ConfigurationScope | null): ConfigPathValue; + get( + section: S, scope: ConfigurationScope | null | undefined, - defaultValue: NonNullable>, - ): NonNullable>; - get( - section: T, + defaultValue: NonNullable>, + ): NonNullable>; + get( + section: S, scope?: ConfigurationScope | null, - defaultValue?: NonNullable>, + defaultValue?: NonNullable>, skipOverrides?: boolean, - ): ConfigPathValue { + ): ConfigPathValue { const value = defaultValue === undefined - ? workspace.getConfiguration(configPrefix, scope).get>(section)! - : workspace.getConfiguration(configPrefix, scope).get>(section, defaultValue)!; - return skipOverrides || this._overrides?.get == null ? value : this._overrides.get(section, value); + ? workspace.getConfiguration(configPrefix, scope).get>(section)! + : workspace.getConfiguration(configPrefix, scope).get>(section, defaultValue)!; + return skipOverrides || this._overrides?.get == null ? value : this._overrides.get(section, value); } getAll(skipOverrides?: boolean): Config { @@ -88,17 +88,17 @@ export class Configuration { return skipOverrides || this._overrides?.getAll == null ? config : this._overrides.getAll(config); } - getAny(section: string, scope?: ConfigurationScope | null): T | undefined; - getAny(section: string, scope: ConfigurationScope | null | undefined, defaultValue: T): T; - getAny(section: string, scope?: ConfigurationScope | null, defaultValue?: T): T | undefined { + getAny(section: S, scope?: ConfigurationScope | null): T | undefined; + getAny(section: S, scope: ConfigurationScope | null | undefined, defaultValue: T): T; + getAny(section: S, scope?: ConfigurationScope | null, defaultValue?: T): T | undefined { return defaultValue === undefined ? workspace.getConfiguration(undefined, scope).get(section) : workspace.getConfiguration(undefined, scope).get(section, defaultValue); } - changed( + changed( e: ConfigurationChangeEvent | undefined, - section: T | T[], + section: S | S[], scope?: ConfigurationScope | null | undefined, ): boolean { if (e == null) return true; @@ -108,17 +108,29 @@ export class Configuration { : e.affectsConfiguration(`${configPrefix}.${section}`, scope!); } - inspect>(section: T, scope?: ConfigurationScope | null) { + changedAny( + e: ConfigurationChangeEvent | undefined, + section: S | S[], + scope?: ConfigurationScope | null | undefined, + ): boolean { + if (e == null) return true; + + return Array.isArray(section) + ? section.some(s => e.affectsConfiguration(s, scope!)) + : e.affectsConfiguration(section, scope!); + } + + inspect>(section: S, scope?: ConfigurationScope | null) { return workspace .getConfiguration(configPrefix, scope) .inspect(section === undefined ? configPrefix : section); } - inspectAny(section: string, scope?: ConfigurationScope | null) { + inspectAny(section: S, scope?: ConfigurationScope | null) { return workspace.getConfiguration(undefined, scope).inspect(section); } - isUnset(section: T, scope?: ConfigurationScope | null): boolean { + isUnset(section: S, scope?: ConfigurationScope | null): boolean { const inspect = this.inspect(section, scope)!; if (inspect.workspaceFolderValue !== undefined) return false; if (inspect.workspaceValue !== undefined) return false; @@ -127,10 +139,10 @@ export class Configuration { return true; } - async migrate( + async migrate( from: string, - to: T, - options: { fallbackValue?: ConfigPathValue; migrationFn?(value: any): ConfigPathValue }, + to: S, + options: { fallbackValue?: ConfigPathValue; migrationFn?(value: any): ConfigPathValue }, ): Promise { const inspection = this.inspect(from as any); if (inspection === undefined) return false; @@ -196,10 +208,10 @@ export class Configuration { return migrated; } - async migrateIfMissing( + async migrateIfMissing( from: string, - to: T, - options: { migrationFn?(value: any): ConfigPathValue }, + to: S, + options: { migrationFn?(value: any): ConfigPathValue }, ): Promise { const fromInspection = this.inspect(from as any); if (fromInspection === undefined) return; @@ -263,25 +275,25 @@ export class Configuration { } } - matches(match: T, section: ConfigPath, value: unknown): value is ConfigPathValue { + matches(match: S, section: ConfigPath, value: unknown): value is ConfigPathValue { return match === section; } - name(section: T): string { + name(section: S): string { return section; } - update( - section: T, - value: ConfigPathValue | undefined, + update( + section: S, + value: ConfigPathValue | undefined, target: ConfigurationTarget, ): Thenable { return workspace.getConfiguration(configPrefix).update(section, value, target); } - updateAny( - section: string, - value: any, + updateAny( + section: S, + value: T, target: ConfigurationTarget, scope?: ConfigurationScope | null, ): Thenable { @@ -290,7 +302,7 @@ export class Configuration { .update(section, value, target); } - updateEffective(section: T, value: ConfigPathValue | undefined): Thenable { + updateEffective(section: S, value: ConfigPathValue | undefined): Thenable { const inspect = this.inspect(section)!; if (inspect.workspaceFolderValue !== undefined) { if (value === inspect.workspaceFolderValue) return Promise.resolve(undefined); diff --git a/src/telemetry/telemetry.ts b/src/telemetry/telemetry.ts index 64a2acd..0ec8db3 100644 --- a/src/telemetry/telemetry.ts +++ b/src/telemetry/telemetry.ts @@ -50,7 +50,7 @@ export class TelemetryService implements Disposable { constructor(private readonly container: Container) { container.context.subscriptions.push( configuration.onDidChange(e => { - if (!e.affectsConfiguration('telemetry.enabled')) return; + if (!configuration.changed(e, 'telemetry.enabled')) return; this.ensureTelemetry(container); }), diff --git a/src/webviews/commitDetails/commitDetailsWebview.ts b/src/webviews/commitDetails/commitDetailsWebview.ts index 6bc6848..60e3376 100644 --- a/src/webviews/commitDetails/commitDetailsWebview.ts +++ b/src/webviews/commitDetails/commitDetailsWebview.ts @@ -2,6 +2,7 @@ import type { CancellationToken, ConfigurationChangeEvent, TextDocumentShowOptio import { CancellationTokenSource, Disposable, Uri, ViewColumn, window } from 'vscode'; import { serializeAutolink } from '../../annotations/autolinks'; import type { CopyShaToClipboardCommandArgs } from '../../commands'; +import type { CoreConfiguration } from '../../constants'; import { Commands, ContextKeys } from '../../constants'; import type { Container } from '../../container'; import { getContext } from '../../context'; @@ -114,7 +115,9 @@ export class CommitDetailsWebviewProvider implements WebviewProvider('workbench.tree.renderIndentGuides') ?? + 'onHover', navigationStack: { count: 0, position: 0, @@ -203,14 +206,17 @@ export class CommitDetailsWebviewProvider implements WebviewProvider(e, 'workbench.tree.indent')) { // this.updatePendingContext({ indent: configuration.getAny('workbench.tree.indent') ?? 8 }); // this.updateState(); // } - if (e.affectsConfiguration('workbench.tree.renderIndentGuides')) { + if (configuration.changedAny(e, 'workbench.tree.renderIndentGuides')) { this.updatePendingContext({ - indentGuides: configuration.getAny('workbench.tree.renderIndentGuides') ?? 'onHover', + indentGuides: + configuration.getAny( + 'workbench.tree.renderIndentGuides', + ) ?? 'onHover', }); this.updateState(); } diff --git a/src/webviews/rebase/rebaseEditor.ts b/src/webviews/rebase/rebaseEditor.ts index 6ca6ed7..4fd3250 100644 --- a/src/webviews/rebase/rebaseEditor.ts +++ b/src/webviews/rebase/rebaseEditor.ts @@ -8,6 +8,7 @@ import type { import { ConfigurationTarget, Disposable, Position, Range, Uri, window, workspace, WorkspaceEdit } from 'vscode'; import { getNonce } from '@env/crypto'; import { ShowCommitsInViewCommand } from '../../commands'; +import type { CoreConfiguration } from '../../constants'; import { ContextKeys } from '../../constants'; import type { Container } from '../../container'; import { emojify } from '../../emojis'; @@ -138,6 +139,7 @@ export class RebaseEditorProvider implements CustomTextEditorProvider, Disposabl get enabled(): boolean { const associations = configuration.inspectAny< + CoreConfiguration, { [key: string]: string } | { viewType: string; filenamePattern: string }[] >('workbench.editorAssociations')?.globalValue; if (associations == null || associations.length === 0) return true; @@ -163,6 +165,7 @@ export class RebaseEditorProvider implements CustomTextEditorProvider, Disposabl this._disableAfterNextUse = false; const inspection = configuration.inspectAny< + CoreConfiguration, { [key: string]: string } | { viewType: string; filenamePattern: string }[] >('workbench.editorAssociations'); diff --git a/src/webviews/webviewWithConfigBase.ts b/src/webviews/webviewWithConfigBase.ts index b730bf8..50cc71a 100644 --- a/src/webviews/webviewWithConfigBase.ts +++ b/src/webviews/webviewWithConfigBase.ts @@ -1,11 +1,14 @@ import type { ConfigurationChangeEvent } from 'vscode'; import { ConfigurationTarget, Disposable } from 'vscode'; +import type { CoreConfiguration } from '../constants'; import type { Container } from '../container'; import { CommitFormatter } from '../git/formatters/commitFormatter'; import { GitCommit, GitCommitIdentity } from '../git/models/commit'; import { GitFileChange, GitFileIndexStatus } from '../git/models/file'; import { PullRequest, PullRequestState } from '../git/models/pullRequest'; +import type { ConfigPath } from '../system/configuration'; import { configuration } from '../system/configuration'; +import { map } from '../system/iterable'; import { Logger } from '../system/logger'; import type { CustomConfigPath, IpcMessage } from './protocol'; import { @@ -173,14 +176,9 @@ export abstract class WebviewProviderWithConfigBase implements WebviewPro } private onOtherConfigurationChanged(e: ConfigurationChangeEvent) { - let notify = false; - for (const setting of this.customSettings.values()) { - if (e.affectsConfiguration(setting.name)) { - notify = true; - break; - } - } - + const notify = configuration.changedAny(e, [ + ...map(this.customSettings.values(), s => s.name), + ]); if (!notify) return; void this.notifyDidChangeConfiguration(); @@ -205,7 +203,7 @@ export abstract class WebviewProviderWithConfigBase implements WebviewPro [ 'currentLine.useUncommittedChangesFormat', { - name: 'currentLine.useUncommittedChangesFormat', + name: 'currentLine.uncommittedChangesFormat', enabled: () => configuration.get('currentLine.uncommittedChangesFormat') != null, update: async enabled => configuration.updateEffective( @@ -238,7 +236,7 @@ export abstract class WebviewProviderWithConfigBase implements WebviewPro } interface CustomSetting { - name: string; + name: ConfigPath | CoreConfiguration; enabled: () => boolean; update: (enabled: boolean) => Promise; }