refactor: 共有ノーマライザを configNormalizers.ts に抽出
globalConfig.ts と projectConfig.ts に重複していた normalizeProviderProfiles / denormalizeProviderProfiles / normalizePieceOverrides / denormalizePieceOverrides を configNormalizers.ts に集約した。
This commit is contained in:
parent
ecf0b02684
commit
8aa79d909c
79
src/infra/config/configNormalizers.ts
Normal file
79
src/infra/config/configNormalizers.ts
Normal file
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Shared normalizer/denormalizer functions for config snake_case <-> camelCase conversion.
|
||||
*
|
||||
* Used by both globalConfig.ts and projectConfig.ts.
|
||||
*/
|
||||
|
||||
import type { ProviderPermissionProfiles } from '../../core/models/provider-profiles.js';
|
||||
import type { PieceOverrides } from '../../core/models/persisted-global-config.js';
|
||||
|
||||
export function normalizeProviderProfiles(
|
||||
raw: Record<string, { default_permission_mode: unknown; movement_permission_overrides?: Record<string, unknown> }> | undefined,
|
||||
): ProviderPermissionProfiles | undefined {
|
||||
if (!raw) return undefined;
|
||||
|
||||
const entries = Object.entries(raw).map(([provider, profile]) => [provider, {
|
||||
defaultPermissionMode: profile.default_permission_mode,
|
||||
movementPermissionOverrides: profile.movement_permission_overrides,
|
||||
}]);
|
||||
|
||||
return Object.fromEntries(entries) as ProviderPermissionProfiles;
|
||||
}
|
||||
|
||||
export function denormalizeProviderProfiles(
|
||||
profiles: ProviderPermissionProfiles | undefined,
|
||||
): Record<string, { default_permission_mode: string; movement_permission_overrides?: Record<string, string> }> | undefined {
|
||||
if (!profiles) return undefined;
|
||||
const entries = Object.entries(profiles);
|
||||
if (entries.length === 0) return undefined;
|
||||
|
||||
return Object.fromEntries(entries.map(([provider, profile]) => [provider, {
|
||||
default_permission_mode: profile.defaultPermissionMode,
|
||||
...(profile.movementPermissionOverrides
|
||||
? { movement_permission_overrides: profile.movementPermissionOverrides }
|
||||
: {}),
|
||||
}])) as Record<string, { default_permission_mode: string; movement_permission_overrides?: Record<string, string> }>;
|
||||
}
|
||||
|
||||
export function normalizePieceOverrides(
|
||||
raw: { quality_gates?: string[]; quality_gates_edit_only?: boolean; movements?: Record<string, { quality_gates?: string[] }> } | undefined,
|
||||
): PieceOverrides | undefined {
|
||||
if (!raw) return undefined;
|
||||
return {
|
||||
qualityGates: raw.quality_gates,
|
||||
qualityGatesEditOnly: raw.quality_gates_edit_only,
|
||||
movements: raw.movements
|
||||
? Object.fromEntries(
|
||||
Object.entries(raw.movements).map(([name, override]) => [
|
||||
name,
|
||||
{ qualityGates: override.quality_gates },
|
||||
])
|
||||
)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export function denormalizePieceOverrides(
|
||||
overrides: PieceOverrides | undefined,
|
||||
): { quality_gates?: string[]; quality_gates_edit_only?: boolean; movements?: Record<string, { quality_gates?: string[] }> } | undefined {
|
||||
if (!overrides) return undefined;
|
||||
const result: { quality_gates?: string[]; quality_gates_edit_only?: boolean; movements?: Record<string, { quality_gates?: string[] }> } = {};
|
||||
if (overrides.qualityGates !== undefined) {
|
||||
result.quality_gates = overrides.qualityGates;
|
||||
}
|
||||
if (overrides.qualityGatesEditOnly !== undefined) {
|
||||
result.quality_gates_edit_only = overrides.qualityGatesEditOnly;
|
||||
}
|
||||
if (overrides.movements) {
|
||||
result.movements = Object.fromEntries(
|
||||
Object.entries(overrides.movements).map(([name, override]) => {
|
||||
const movementOverride: { quality_gates?: string[] } = {};
|
||||
if (override.qualityGates !== undefined) {
|
||||
movementOverride.quality_gates = override.qualityGates;
|
||||
}
|
||||
return [name, movementOverride];
|
||||
})
|
||||
);
|
||||
}
|
||||
return Object.keys(result).length > 0 ? result : undefined;
|
||||
}
|
||||
@ -10,12 +10,17 @@ import { isAbsolute } from 'node:path';
|
||||
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
||||
import { GlobalConfigSchema } from '../../../core/models/index.js';
|
||||
import type { Language } from '../../../core/models/index.js';
|
||||
import type { PersistedGlobalConfig, PersonaProviderEntry, PieceOverrides } from '../../../core/models/persisted-global-config.js';
|
||||
import type { ProviderPermissionProfiles } from '../../../core/models/provider-profiles.js';
|
||||
import type { PersistedGlobalConfig, PersonaProviderEntry } from '../../../core/models/persisted-global-config.js';
|
||||
import {
|
||||
normalizeConfigProviderReference,
|
||||
type ConfigProviderReference,
|
||||
} from '../providerReference.js';
|
||||
import {
|
||||
normalizeProviderProfiles,
|
||||
denormalizeProviderProfiles,
|
||||
normalizePieceOverrides,
|
||||
denormalizePieceOverrides,
|
||||
} from '../configNormalizers.js';
|
||||
import { getGlobalConfigPath } from '../paths.js';
|
||||
import { DEFAULT_LANGUAGE } from '../../../shared/constants.js';
|
||||
import { parseProviderModel } from '../../../shared/utils/providerModel.js';
|
||||
@ -112,79 +117,6 @@ function normalizePersonaProviders(
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeProviderProfiles(
|
||||
raw: Record<string, { default_permission_mode: unknown; movement_permission_overrides?: Record<string, unknown> }> | undefined,
|
||||
): ProviderPermissionProfiles | undefined {
|
||||
if (!raw) return undefined;
|
||||
|
||||
const entries = Object.entries(raw).map(([provider, profile]) => [provider, {
|
||||
defaultPermissionMode: profile.default_permission_mode,
|
||||
movementPermissionOverrides: profile.movement_permission_overrides,
|
||||
}]);
|
||||
|
||||
return Object.fromEntries(entries) as ProviderPermissionProfiles;
|
||||
}
|
||||
|
||||
function denormalizeProviderProfiles(
|
||||
profiles: ProviderPermissionProfiles | undefined,
|
||||
): Record<string, { default_permission_mode: string; movement_permission_overrides?: Record<string, string> }> | undefined {
|
||||
if (!profiles) return undefined;
|
||||
const entries = Object.entries(profiles);
|
||||
if (entries.length === 0) return undefined;
|
||||
|
||||
return Object.fromEntries(entries.map(([provider, profile]) => [provider, {
|
||||
default_permission_mode: profile.defaultPermissionMode,
|
||||
...(profile.movementPermissionOverrides
|
||||
? { movement_permission_overrides: profile.movementPermissionOverrides }
|
||||
: {}),
|
||||
}])) as Record<string, { default_permission_mode: string; movement_permission_overrides?: Record<string, string> }>;
|
||||
}
|
||||
|
||||
/** Normalize piece_overrides from snake_case (YAML) to camelCase (internal) */
|
||||
function normalizePieceOverrides(
|
||||
raw: { quality_gates?: string[]; quality_gates_edit_only?: boolean; movements?: Record<string, { quality_gates?: string[] }> } | undefined,
|
||||
): PieceOverrides | undefined {
|
||||
if (!raw) return undefined;
|
||||
return {
|
||||
qualityGates: raw.quality_gates,
|
||||
qualityGatesEditOnly: raw.quality_gates_edit_only,
|
||||
movements: raw.movements
|
||||
? Object.fromEntries(
|
||||
Object.entries(raw.movements).map(([name, override]) => [
|
||||
name,
|
||||
{ qualityGates: override.quality_gates },
|
||||
])
|
||||
)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/** Denormalize piece_overrides from camelCase (internal) to snake_case (YAML) */
|
||||
function denormalizePieceOverrides(
|
||||
overrides: PieceOverrides | undefined,
|
||||
): { quality_gates?: string[]; quality_gates_edit_only?: boolean; movements?: Record<string, { quality_gates?: string[] }> } | undefined {
|
||||
if (!overrides) return undefined;
|
||||
const result: { quality_gates?: string[]; quality_gates_edit_only?: boolean; movements?: Record<string, { quality_gates?: string[] }> } = {};
|
||||
if (overrides.qualityGates !== undefined) {
|
||||
result.quality_gates = overrides.qualityGates;
|
||||
}
|
||||
if (overrides.qualityGatesEditOnly !== undefined) {
|
||||
result.quality_gates_edit_only = overrides.qualityGatesEditOnly;
|
||||
}
|
||||
if (overrides.movements) {
|
||||
result.movements = Object.fromEntries(
|
||||
Object.entries(overrides.movements).map(([name, override]) => {
|
||||
const movementOverride: { quality_gates?: string[] } = {};
|
||||
if (override.qualityGates !== undefined) {
|
||||
movementOverride.quality_gates = override.qualityGates;
|
||||
}
|
||||
return [name, movementOverride];
|
||||
})
|
||||
);
|
||||
}
|
||||
return Object.keys(result).length > 0 ? result : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages global configuration loading and caching.
|
||||
* Singleton — use GlobalConfigManager.getInstance().
|
||||
|
||||
@ -10,13 +10,18 @@ import { parse, stringify } from 'yaml';
|
||||
import { ProjectConfigSchema } from '../../../core/models/index.js';
|
||||
import { copyProjectResourcesToDir } from '../../resources/index.js';
|
||||
import type { ProjectLocalConfig } from '../types.js';
|
||||
import type { ProviderPermissionProfiles } from '../../../core/models/provider-profiles.js';
|
||||
import type { AnalyticsConfig, PieceOverrides, SubmoduleSelection } from '../../../core/models/persisted-global-config.js';
|
||||
import type { AnalyticsConfig, SubmoduleSelection } from '../../../core/models/persisted-global-config.js';
|
||||
import { applyProjectConfigEnvOverrides } from '../env/config-env-overrides.js';
|
||||
import {
|
||||
normalizeConfigProviderReference,
|
||||
type ConfigProviderReference,
|
||||
} from '../providerReference.js';
|
||||
import {
|
||||
normalizeProviderProfiles,
|
||||
denormalizeProviderProfiles,
|
||||
normalizePieceOverrides,
|
||||
denormalizePieceOverrides,
|
||||
} from '../configNormalizers.js';
|
||||
import { invalidateResolvedConfigCache } from '../resolutionCache.js';
|
||||
|
||||
export type { ProjectLocalConfig } from '../types.js';
|
||||
@ -86,28 +91,6 @@ function getConfigPath(projectDir: string): string {
|
||||
return join(getConfigDir(projectDir), 'config.yaml');
|
||||
}
|
||||
|
||||
function normalizeProviderProfiles(raw: Record<string, { default_permission_mode: unknown; movement_permission_overrides?: Record<string, unknown> }> | undefined): ProviderPermissionProfiles | undefined {
|
||||
if (!raw) return undefined;
|
||||
return Object.fromEntries(
|
||||
Object.entries(raw).map(([provider, profile]) => [provider, {
|
||||
defaultPermissionMode: profile.default_permission_mode,
|
||||
movementPermissionOverrides: profile.movement_permission_overrides,
|
||||
}]),
|
||||
) as ProviderPermissionProfiles;
|
||||
}
|
||||
|
||||
function denormalizeProviderProfiles(profiles: ProviderPermissionProfiles | undefined): Record<string, { default_permission_mode: string; movement_permission_overrides?: Record<string, string> }> | undefined {
|
||||
if (!profiles) return undefined;
|
||||
const entries = Object.entries(profiles);
|
||||
if (entries.length === 0) return undefined;
|
||||
return Object.fromEntries(entries.map(([provider, profile]) => [provider, {
|
||||
default_permission_mode: profile.defaultPermissionMode,
|
||||
...(profile.movementPermissionOverrides
|
||||
? { movement_permission_overrides: profile.movementPermissionOverrides }
|
||||
: {}),
|
||||
}])) as Record<string, { default_permission_mode: string; movement_permission_overrides?: Record<string, string> }>;
|
||||
}
|
||||
|
||||
function normalizeAnalytics(raw: Record<string, unknown> | undefined): AnalyticsConfig | undefined {
|
||||
if (!raw) return undefined;
|
||||
const enabled = typeof raw.enabled === 'boolean' ? raw.enabled : undefined;
|
||||
@ -133,51 +116,6 @@ function denormalizeAnalytics(config: AnalyticsConfig | undefined): Record<strin
|
||||
return Object.keys(raw).length > 0 ? raw : undefined;
|
||||
}
|
||||
|
||||
/** Normalize piece_overrides from snake_case (YAML) to camelCase (internal) */
|
||||
function normalizePieceOverrides(
|
||||
raw: { quality_gates?: string[]; quality_gates_edit_only?: boolean; movements?: Record<string, { quality_gates?: string[] }> } | undefined,
|
||||
): PieceOverrides | undefined {
|
||||
if (!raw) return undefined;
|
||||
return {
|
||||
qualityGates: raw.quality_gates,
|
||||
qualityGatesEditOnly: raw.quality_gates_edit_only,
|
||||
movements: raw.movements
|
||||
? Object.fromEntries(
|
||||
Object.entries(raw.movements).map(([name, override]) => [
|
||||
name,
|
||||
{ qualityGates: override.quality_gates },
|
||||
])
|
||||
)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/** Denormalize piece_overrides from camelCase (internal) to snake_case (YAML) */
|
||||
function denormalizePieceOverrides(
|
||||
overrides: PieceOverrides | undefined,
|
||||
): { quality_gates?: string[]; quality_gates_edit_only?: boolean; movements?: Record<string, { quality_gates?: string[] }> } | undefined {
|
||||
if (!overrides) return undefined;
|
||||
const result: { quality_gates?: string[]; quality_gates_edit_only?: boolean; movements?: Record<string, { quality_gates?: string[] }> } = {};
|
||||
if (overrides.qualityGates !== undefined) {
|
||||
result.quality_gates = overrides.qualityGates;
|
||||
}
|
||||
if (overrides.qualityGatesEditOnly !== undefined) {
|
||||
result.quality_gates_edit_only = overrides.qualityGatesEditOnly;
|
||||
}
|
||||
if (overrides.movements) {
|
||||
result.movements = Object.fromEntries(
|
||||
Object.entries(overrides.movements).map(([name, override]) => {
|
||||
const movementOverride: { quality_gates?: string[] } = {};
|
||||
if (override.qualityGates !== undefined) {
|
||||
movementOverride.quality_gates = override.qualityGates;
|
||||
}
|
||||
return [name, movementOverride];
|
||||
})
|
||||
);
|
||||
}
|
||||
return Object.keys(result).length > 0 ? result : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load project configuration from .takt/config.yaml
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user