refactor: share plain object guard across config and utils

This commit is contained in:
Peter Steinberger
2026-02-19 14:10:58 +00:00
parent 397f243ded
commit ba538c98c7
4 changed files with 33 additions and 21 deletions

View File

@@ -1,3 +1,5 @@
import { isPlainObject } from "../infra/plain-object.js";
/**
* Preserves `${VAR}` environment variable references during config write-back.
*
@@ -16,15 +18,6 @@
const ENV_VAR_PATTERN = /\$\{[A-Z_][A-Z0-9_]*\}/;
function isPlainObject(value: unknown): value is Record<string, unknown> {
return (
typeof value === "object" &&
value !== null &&
!Array.isArray(value) &&
Object.prototype.toString.call(value) === "[object Object]"
);
}
/**
* Check if a string contains any `${VAR}` env var references.
*/

View File

@@ -0,0 +1,18 @@
import { describe, expect, it } from "vitest";
import { isPlainObject } from "./plain-object.js";
describe("isPlainObject", () => {
it("accepts plain objects", () => {
expect(isPlainObject({})).toBe(true);
expect(isPlainObject({ a: 1 })).toBe(true);
});
it("rejects non-plain values", () => {
expect(isPlainObject(null)).toBe(false);
expect(isPlainObject([])).toBe(false);
expect(isPlainObject(new Date())).toBe(false);
expect(isPlainObject(/re/)).toBe(false);
expect(isPlainObject("x")).toBe(false);
expect(isPlainObject(42)).toBe(false);
});
});

11
src/infra/plain-object.ts Normal file
View File

@@ -0,0 +1,11 @@
/**
* Strict plain-object guard (excludes arrays and host objects).
*/
export function isPlainObject(value: unknown): value is Record<string, unknown> {
return (
typeof value === "object" &&
value !== null &&
!Array.isArray(value) &&
Object.prototype.toString.call(value) === "[object Object]"
);
}

View File

@@ -8,6 +8,7 @@ import {
resolveEffectiveHomeDir,
resolveRequiredHomeDir,
} from "./infra/home-dir.js";
import { isPlainObject } from "./infra/plain-object.js";
export async function ensureDir(dir: string) {
await fs.promises.mkdir(dir, { recursive: true });
@@ -54,18 +55,7 @@ export function safeParseJson<T>(raw: string): T | null {
}
}
/**
* Type guard for plain objects (not arrays, null, Date, RegExp, etc.).
* Uses Object.prototype.toString for maximum safety.
*/
export function isPlainObject(value: unknown): value is Record<string, unknown> {
return (
typeof value === "object" &&
value !== null &&
!Array.isArray(value) &&
Object.prototype.toString.call(value) === "[object Object]"
);
}
export { isPlainObject };
/**
* Type guard for Record<string, unknown> (less strict than isPlainObject).