xpm
Version:
The xPack project manager command line tool
238 lines (221 loc) • 7.44 kB
text/typescript
/*
* 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.
*/
// ----------------------------------------------------------------------------
/**
* Platform information structure for runtime environment detection.
*
* @remarks
* This interface encapsulates operating system and architecture information
* used throughout the xPack library for platform-specific behaviour,
* path filtering, and binary distribution selection.
*/
export interface PlatformInfo {
/**
* The operating system platform identifier.
*
* @remarks
* Common values include:
*
* <ul>
* <li><code>darwin</code> (macOS)</li>
* <li><code>linux</code> (Linux)</li>
* <li><code>win32</code> (Windows)</li>
* </ul>
*
* Corresponds to <code>process.platform</code> from Node.js.
*/
platform: string
/**
* The CPU architecture identifier.
*
* @remarks
* Common values include:
*
* <ul>
* <li><code>x64</code> (64-bit Intel/AMD)</li>
* <li><code>arm64</code> (64-bit ARM)</li>
* <li><code>ia32</code> (32-bit Intel/AMD)</li>
* <li><code>arm</code> (32-bit ARM)</li>
* </ul>
*
* Corresponds to <code>process.arch</code> from Node.js.
*/
arch: string
}
/**
* Options for platform information retrieval.
*
* @remarks
* This interface defines configuration options that control how platform
* information is retrieved and processed, particularly for architecture
* coercion scenarios.
*/
export interface PlatformInfoOptions {
/**
* Whether to coerce 64-bit architectures to their 32-bit equivalents.
*
* @remarks
* When <code>true</code>, applies the following architecture mappings:
*
* <ul>
* <li><code>x64 → ia32</code></li>
* <li><code>arm64 → arm</code></li>
* </ul>
*
* This is useful for backward compatibility scenarios where only 32-bit
* binaries are available but can run on 64-bit systems.
*
* @defaultValue `false`
*/
doForce32bit?: boolean
}
// ============================================================================
/**
* Platform detection service for runtime environment identification.
*
* @remarks
* This class encapsulates platform and architecture detection logic,
* providing a mockable abstraction over Node.js process properties.
* It enables testable platform-specific behaviour without requiring
* execution on multiple operating systems or architectures.
*
* Key features:
*
* <ol>
* <li><b>Dependency injection:</b> Accepts a custom process object via
* constructor, enabling test environments to inject mock process
* implementations.</li>
* <li><b>Architecture coercion:</b> Provides optional 64-bit to 32-bit
* architecture mapping for backward compatibility scenarios.</li>
* <li><b>Consistent interface:</b> Returns standardised
* {@link PlatformInfo} objects for use throughout the xPack library.</li>
* </ol>
*
* This abstraction eliminates direct <code>process.platform</code> and
* <code>process.arch</code> access in business logic, making platform-specific
* code paths testable via mocked platform information.
*
* @example
* Default usage with actual runtime platform:
* ```typescript
* const detector = new PlatformDetector()
* const info = detector.getPlatformInfo()
* console.log(info.platform) // 'darwin', 'linux', or 'win32'
* console.log(info.arch) // 'x64', 'arm64', etc.
* ```
*
* @example
* Testing with mocked platform:
* ```typescript
* const mockProcess = {
* platform: 'darwin',
* arch: 'arm64'
* } as NodeJS.Process
*
* const detector = new PlatformDetector(mockProcess)
* const info = detector.getPlatformInfo({ doForce32bit: true })
* // info.platform === 'darwin'
* // info.arch === 'arm' (coerced from arm64)
* ```
*/
export class PlatformDetector {
// --------------------------------------------------------------------------
// Private Members.
/**
* The Node.js process object for accessing platform information.
*/
private readonly process: NodeJS.Process
// --------------------------------------------------------------------------
// Constructor.
/**
* Constructs a platform detector instance.
*
* @remarks
* This constructor accepts an optional process object parameter, enabling
* dependency injection for testing scenarios. When no process object is
* provided, the global Node.js <code>process</code> is used automatically.
*
* The injected process object must implement the <code>platform</code> and
* <code>arch</code> properties from the <code>NodeJS.Process</code>
* interface.
*
* @param _process - The Node.js process object providing platform and
* architecture information. Defaults to the global <code>process</code>
* object.
*/
constructor(_process: NodeJS.Process = globalThis.process) {
this.process = _process
}
// --------------------------------------------------------------------------
// Public Methods.
/**
* Retrieves current platform and architecture information.
*
* @remarks
* This method returns a {@link PlatformInfo} object containing the
* operating system platform and CPU architecture. When the
* <code>doForce32bit</code> option is enabled, 64-bit architectures
* are coerced to their 32-bit equivalents.
*
* Architecture coercion rules (when <code>doForce32bit</code> is
* <code>true</code>):
*
* <ul>
* <li><b>x64 → ia32:</b> Coerces 64-bit Intel/AMD to 32-bit.</li>
* <li><b>arm64 → arm:</b> Coerces 64-bit ARM to 32-bit.</li>
* </ul>
*
* This coercion is useful for backward compatibility scenarios where
* only 32-bit binaries are available but can run on 64-bit systems via
* compatibility layers.
*
* The platform identifier is never modified and always reflects the actual
* operating system (<code>darwin</code>, <code>linux</code>,
* <code>win32</code>).
*
* @param options - Configuration options controlling platform information
* retrieval.
* @returns Platform and architecture information.
*/
getPlatformInfo(options: PlatformInfoOptions = {}): PlatformInfo {
const { doForce32bit = false } = options
let arch = this.process.arch
if (doForce32bit) {
// https://nodejs.org/docs/latest/api/process.html#processarch
if (arch === 'x64') {
arch = 'ia32'
} else if (arch === 'arm64') {
arch = 'arm'
}
}
return {
platform: this.process.platform,
arch,
}
}
/**
* Checks whether the current platform is Windows.
*
* @remarks
* This convenience method provides a simple boolean check for Windows
* platform detection, commonly used for path handling and command
* formatting decisions.
*
* Equivalent to checking <code>platform === 'win32'</code>.
*
* @returns <code>true</code> if running on Windows,
* <code>false</code> otherwise.
*/
isWindows(): boolean {
return this.process.platform === 'win32'
}
}
// ----------------------------------------------------------------------------