Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/sync/apply.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { promises as fs } from 'node:fs';
import path from 'node:path';

import { deepMerge, parseJsonc, pathExists, stripOverrides, writeJsonFile } from './config.js';
import {
deepMerge,
hasOwn,
parseJsonc,
pathExists,
stripOverrides,
writeJsonFile,
} from './config.js';
import {
extractMcpSecrets,
hasOverrides,
Expand Down Expand Up @@ -283,7 +290,7 @@ function isDeepEqual(left: unknown, right: unknown): boolean {
const rightKeys = Object.keys(right as Record<string, unknown>);
if (leftKeys.length !== rightKeys.length) return false;
for (const key of leftKeys) {
if (!Object.hasOwn(right, key)) return false;
if (!hasOwn(right as Record<string, unknown>, key)) return false;
if (
!isDeepEqual(
(left as Record<string, unknown>)[key],
Expand Down
6 changes: 5 additions & 1 deletion src/sync/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,15 @@ export async function writeJsonFile(
}
}

function isPlainObject(value: unknown): value is Record<string, unknown> {
export function isPlainObject(value: unknown): value is Record<string, unknown> {
if (!value || typeof value !== 'object') return false;
return Object.getPrototypeOf(value) === Object.prototype;
}

export function hasOwn(target: Record<string, unknown>, key: string): boolean {
return Object.hasOwn(target, key);
}

function stripJsonComments(input: string): string {
let output = '';
let inString = false;
Expand Down
12 changes: 2 additions & 10 deletions src/sync/mcp-secrets.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { deepMerge } from './config.js';
import { deepMerge, hasOwn, isPlainObject } from './config.js';

export interface McpSecretExtraction {
sanitizedConfig: Record<string, unknown>;
Expand Down Expand Up @@ -106,15 +106,7 @@ function getPlainObject(value: unknown): Record<string, unknown> | null {
return isPlainObject(value) ? (value as Record<string, unknown>) : null;
}

function isPlainObject(value: unknown): value is Record<string, unknown> {
if (!value || typeof value !== 'object') return false;
return Object.getPrototypeOf(value) === Object.prototype;
}

function cloneConfig(config: Record<string, unknown>): Record<string, unknown> {
if (typeof structuredClone === 'function') {
return structuredClone(config);
}
return JSON.parse(JSON.stringify(config)) as Record<string, unknown>;
}

Expand All @@ -136,7 +128,7 @@ export function stripOverrideKeys(
const result: Record<string, unknown> = { ...base };

for (const [key, removeValue] of Object.entries(toRemove)) {
if (!Object.hasOwn(result, key)) continue;
if (!hasOwn(result, key)) continue;
const currentValue = result[key];
if (isPlainObject(removeValue) && isPlainObject(currentValue)) {
const stripped = stripOverrideKeys(
Expand Down
Loading