@nanocollective/nanocoder
Version:
A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter
220 lines • 6.6 kB
JavaScript
// source/utils/type-helpers.ts
/**
* Type-safe helper utilities for handling non-string content
*
* This module provides type guards and conversion functions that:
* 1. Accept unknown types (string, object, array, null, undefined)
* 2. Convert to appropriate types for processing
* 3. Preserve types in memory (critical for ToolCall.arguments)
* 4. Provide safe fallbacks for edge cases
*
* TYPE PRESERVATION STRATEGY
* ===========================
*
* "Preserving types" means:
* 1. When receiving LLM responses (which can be ANY type):
* - We accept unknown types (string, object, array, null, undefined)
* - We convert to string ONLY for PARSING OPERATIONS
* - We preserve the original type in the tool call structure
*
* 2. When storing ToolCall.arguments:
* - MUST preserve as Record<string, unknown> (object type)
* - NOT convert to string
* - Enables direct property access without JSON.parse
*
* 3. When displaying/writing to disk:
* - Convert to string for display/storage operations
* - Use JSON.stringify for objects/arrays
* - Use String() for primitives
*
* The confusion comes from mixing up:
* - "Preserve types in memory" (CRITICAL: ToolCall.arguments stays as object)
* - "Convert to string for processing" (NECESSARY: Parser expects strings)
*/
// ============================================================================
// TYPE GUARDS
// ============================================================================
/**
* Type guard to check if value is a string
*
* @param value - Value to check
* @returns True if value is a string, false otherwise
*/
export function isString(value) {
return typeof value === 'string';
}
/**
* Type guard to check if value is a non-null object
*
* @param value - Value to check
* @returns True if value is a non-null object, false otherwise
*/
export function isObject(value) {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
/**
* Type guard to check if value is an array
*
* @param value - Value to check
* @returns True if value is an array, false otherwise
*/
export function isArray(value) {
return Array.isArray(value);
}
/**
* Type guard to check if value is a plain object (not null, not array, not instance)
*
* @param value - Value to check
* @returns True if value is a plain object, false otherwise
*/
export function isPlainObject(value) {
if (value === null || typeof value !== 'object') {
return false;
}
// Check for prototype chain
const prototype = Object.getPrototypeOf(value);
return prototype === null || prototype === Object.prototype;
}
/**
* Type guard to check if value is a valid function
*
* @param value - Value to check
* @returns True if value is a function, false otherwise
*/
export function isFunction(value) {
return typeof value === 'function';
}
/**
* Type guard to check if value is a valid number
*
* @param value - Value to check
* @returns True if value is a number, false otherwise
*/
export function isNumber(value) {
return typeof value === 'number' && !isNaN(value) && isFinite(value);
}
/**
* Type guard to check if value is a valid boolean
*
* @param value - Value to check
* @returns True if value is a boolean, false otherwise
*/
export function isBoolean(value) {
return typeof value === 'boolean';
}
/**
* Type guard to check if value is a valid null value
*
* @param value - Value to check
* @returns True if value is null, false otherwise
*/
export function isNull(value) {
return value === null;
}
/**
* Type guard to check if value is undefined
*
* @param value - Value to check
* @returns True if value is undefined, false otherwise
*/
export function isUndefined(value) {
return value === undefined;
}
// ============================================================================
// STRING CONVERSION FUNCTIONS
// ============================================================================
/**
* Ensures value is a string for display/storage operations
*
* This function is used for:
* 1. DISPLAY OPERATIONS - where we need to display content
* 2. STORAGE OPERATIONS - where we write to disk
*
* For display/storage, we convert to string.
*
* @param value - Value to convert (unknown type)
* @returns String representation of the value
*
* @example
* ```typescript
* // ToolCall.arguments is an object in memory
* const toolCall = {
* function: {
* name: 'write_file',
* arguments: {path: "/tmp/test.txt", content: "hello"}
* }
* };
*
* // For storage, convert to string
* const contentStr = ensureString(toolCall.function.arguments);
* // contentStr = '{"path": "/tmp/test.txt", "content": "hello"}'
*
* await writeFile(path, contentStr, 'utf-8');
* ```
*/
export function ensureString(value) {
// Handle null/undefined
if (value === null || value === undefined) {
return '';
}
// Handle string - return as-is
if (isString(value)) {
return value;
}
// Handle number - convert to string
if (isNumber(value)) {
return String(value);
}
// Handle boolean - convert to string
if (isBoolean(value)) {
return String(value);
}
// Handle array - convert to JSON string
if (isArray(value)) {
return JSON.stringify(value);
}
// Handle object - convert to JSON string
if (isObject(value)) {
return JSON.stringify(value);
}
// Fallback for unknown types
return String(value);
}
// ============================================================================
// UTILITY FUNCTIONS
// ============================================================================
/**
* Checks if value is empty
*
* @param value - Value to check
* @returns True if value is empty, false otherwise
*/
export function isEmpty(value) {
// Check for null or undefined
if (value === null || value === undefined) {
return true;
}
// Check for empty string
if (isString(value) && value === '') {
return true;
}
// Check for empty array
if (isArray(value) && value.length === 0) {
return true;
}
// Check for empty object
if (isObject(value) && Object.keys(value).length === 0) {
return true;
}
return false;
}
/**
* Checks if value is non-empty (opposite of isEmpty)
*
* @param value - Value to check
* @returns True if value is non-empty, false otherwise
*/
export function isNotEmpty(value) {
return !isEmpty(value);
}
//# sourceMappingURL=type-helpers.js.map