UNPKG

@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
// 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