All files / src/configuration index.ts

100% Statements 36/36
100% Branches 12/12
92.85% Functions 13/14
100% Lines 32/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79          37x 37x 37x 37x 37x 37x             37x   194x                     176x 21x 21x 13x 12x   20x   8x     8x 1x         8x     1x 1x   176x         37x 219x 23x 23x   8x   42x 21x       219x       37x 9x 9x    
/**
 * The process's configuration service, wired to the settings store of the deployment,
 * the preset loader and per-session tool-list notifications.
 */
 
import { logWarn } from '../logger';
import { isOAuthEnabled } from '../oauth/config';
import { sessionStore } from '../oauth/session-store';
import { ProfileLoader } from '../profiles/loader';
import { ConfigurationService } from './service';
import {
  LocalSettingsFile,
  localSettingsDir,
  usableStoredRecord,
  type SettingsStore,
} from './settings-store';
 
export { ConfigurationError } from './service';
export type { ResolvedConfiguration } from './service';
export { resolveCaller, runWithCaller, currentCaller, type Caller } from './caller';
 
let settingsStore: Promise<SettingsStore> | undefined;
 
/**
 * Settings live with the sessions when that storage can keep them (file or PostgreSQL).
 * Otherwise (a local server, sessions held in memory as
 * the OAuth default, or a database package older than the server) they live in the
 * local settings directory, so saved settings survive a restart and calls keep working.
 */
function storeOfDeployment(): Promise<SettingsStore> {
  settingsStore ??= (async (): Promise<SettingsStore> => {
    const sessionsStored = isOAuthEnabled() || Boolean(process.env.OAUTH_STORAGE_TYPE);
    if (sessionsStored && sessionStore.keepsAccountSettings()) {
      await sessionStore.initialize();
      return {
        get: async (accountKey) =>
          usableStoredRecord(await sessionStore.getAccountSettings(accountKey)),
        put: (accountKey, settings, expectedVersion) =>
          sessionStore.putAccountSettings(accountKey, settings, expectedVersion),
      };
    }
    if (sessionsStored && sessionStore.getBackendType() !== 'memory') {
      logWarn(
        'The session storage cannot keep account settings; upgrade @structured-world/gitlab-mcp-db to the version of this server. Settings are kept in the local settings directory meanwhile.',
        { backend: sessionStore.getBackendType() },
      );
    }
    return new LocalSettingsFile(localSettingsDir());
  })().catch((error: unknown) => {
    // A failed open is retried by the next request instead of failing until restart.
    settingsStore = undefined;
    throw error;
  });
  return settingsStore;
}
 
let service: ConfigurationService | undefined;
 
export function getConfigurationService(): ConfigurationService {
  if (!service) {
    const loader = new ProfileLoader();
    service = new ConfigurationService(
      storeOfDeployment,
      { load: (name) => loader.loadPreset(name) },
      async (sessionKeys) => {
        const { getSessionManager } = await import('../session-manager');
        await getSessionManager().notifyToolsListChanged(sessionKeys);
      },
    );
  }
  return service;
}
 
/** Drop the service and its store (tests). */
export function resetConfigurationService(): void {
  service = undefined;
  settingsStore = undefined;
}