/* * This file is part of the xPack project (http://xpack.github.io). * Copyright (c) 2021-2026 Liviu Ionescu. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software * for any purpose is hereby granted, under the terms of the MIT license. * * If a copy of the license was not distributed with this file, it can * be obtained from https://opensource.org/license/mit. */ // ============================================================================ /** * Determines whether a value is a JavaScript primitive. * * @remarks * In JavaScript, primitives are immutable values that are not objects: * `string`, `number`, `bigint`, `boolean`, `undefined`, and `symbol`. * This function * also treats null as a primitive, following JavaScript's `typeof` behavior * despite null being technically an object type. * * Returns `true` for: `string`, `number`, `bigint`, `boolean`, `undefined`, * `symbol`, and `null`. * * Returns `false` for: objects, arrays, functions, and class instances. * * Useful for distinguishing between value types and reference types when * processing JSON data or validating configuration inputs. * * @param value - The value to test. * @returns `true` if the value is a primitive or `null`, `false` otherwise. */ export function isPrimitive(value: unknown): boolean { return ( (typeof value !== 'object' && typeof value !== 'function') || value === null ) } /** * Determines whether a value is a string. * * @remarks * This function acts as a TypeScript type guard, narrowing the type to * `string` within conditional blocks. This enables safe string operations * without type assertions. * * Example usage: * ```typescript * if (isString(value)) { * // TypeScript knows value is a string here * const length = value.length; * } * ``` * * Only returns `true` for primitive string values, not String objects * created with `new String()`. * * @param value - The value to test. * @returns `true` if the value is a string, `false` otherwise. */ export function isString(value: unknown): boolean { return typeof value === 'string' } /** * Determines whether a value is a finite number. * * @remarks * This function validates that a value is both a number type and * mathematically finite, excluding `NaN`, `Infinity`, and `-Infinity`. * This is essential when validating numeric configuration values or * processing JSON data where numeric fields must contain valid, * computable values. * * Returns `true` for: all finite numeric values, including `0`, negative * numbers, and floating-point numbers. * * Returns `false` for: `NaN`, `Infinity`, `-Infinity`, strings, objects, * arrays, and any non-number types. * * Only returns `true` for primitive number values, not Number objects * created with `new Number()`. * * The finiteness check is crucial for mathematical operations and ensures * that numeric properties in `package.json` or configuration files contain * usable values rather than special numeric constants that could cause * unexpected behaviour in calculations. * * @param value - The value to test. * @returns `true` if the value is a finite number, `false` otherwise. */ export function isNumber(value: unknown): boolean { return typeof value === 'number' && isFinite(value) } /** * Determines whether a value is a boolean. * * @remarks * Tests for primitive boolean values (`true` or `false`). Useful when * validating configuration options or parsing JSON where boolean flags * need to be distinguished from truthy/falsy values. * * Only returns `true` for the primitive boolean values `true` and `false`, * not for boolean objects created with `new Boolean()`. * * Note: This does not check for truthy or falsy values - it only returns * `true` for actual boolean primitives. Use standard JavaScript truthiness * checks for conditional logic. * * @param value - The value to test. * @returns `true` if the value is a boolean, `false` otherwise. */ export function isBoolean(value: unknown): boolean { return typeof value === 'boolean' } /** * Determines whether a value is a non-array object. * * @remarks * This function distinguishes between objects and arrays, which is crucial * when processing JSON structures where both use the object type but require * different handling. * * Returns `true` for: plain objects, class instances, null and other * object types. * * Returns `false` for: arrays, primitives, and functions. * * Note: Arrays in JavaScript are objects, so this function explicitly * excludes them using `Array.isArray()`. Use {@link isJsonObject} for * stricter JSON object validation that also excludes `undefined` and `null`. * * @param value - The value to test. * @returns `true` if the value is a non-array object, `false` otherwise. */ export function isObject(value: unknown): boolean { return typeof value === 'object' && !Array.isArray(value) } // ---------------------------------------------------------------------------- /** * Determines whether a value is a JSON object. * * @remarks * Validates that a value represents a JSON object, which is stricter than * JavaScript's general object type. This is essential when working with * parsed JSON data or `package.json` structures. * * Returns `true` for: plain objects (non-null, * non-primitive, non-array values). * * Returns `false` for: undefined, null, primitives (string, number, * boolean, etc.), and arrays. * * This is the primary validation function for JSON objects in the xpm * codebase, used extensively when parsing `package.json` sections like * `xpack.properties`, `xpack.buildConfigurations`, etc. * * @param value - The value to test. * @returns `true` if the value is a JSON object, `false` otherwise. */ export function isJsonObject(value: unknown): boolean { return value !== undefined && !isPrimitive(value) && !Array.isArray(value) } /** * Determines whether a value is a JSON array. * * @remarks * Validates that a value represents a JSON array, excluding `undefined`. * This ensures the value is a proper array that could have been parsed * from JSON. * * Returns `true` for: any array, including empty arrays. * * Returns `false` for: undefined, null, objects, and primitives. * * The undefined check is important for distinguishing between optional * properties that are missing (undefined) versus properties that are * explicitly empty arrays. This is common in package.json where array * fields may be absent or present but empty. * * @param value - The value to test. * @returns `true` if the value is a JSON array, `false` otherwise. */ export function isJsonArray(value: unknown): boolean { return value !== undefined && Array.isArray(value) } /** * Determines whether a value is a non-empty JSON object. * * @remarks * Combines JSON object validation with a non-empty check, ensuring the * object has at least one property. This is useful for validating * configuration sections that must contain data to be meaningful. * * Returns `true` for: objects with one or more enumerable properties. * * Returns `false` for: empty objects `{}`, undefined, null, primitives, * arrays, and any non-object types. * * The enumerable properties check uses `Object.keys()`, which only counts * own enumerable string-keyed properties, not inherited properties or * symbol-keyed properties. * * Common use cases include validating `xpack.actions`, `xpack.dependencies`, * and other `package.json` sections where an empty object would be * meaningless. * * @param value - The value to test. * @returns `true` if the value is a non-empty JSON object, `false` otherwise. */ export function isNonEmptyJsonObject(value: unknown): boolean { return isJsonObject(value) && Object.keys(value as object).length > 0 } // ----------------------------------------------------------------------------