FoundryKit

Getting Started

Get started with FoundryKit utilities for common development tasks

Getting Started

Learn how to install and use FoundryKit utilities to streamline common development tasks and improve code quality.

Installation

Install the Utils Package

Install the FoundryKit utils package in your project.

npm install @foundrykit/utils

Verify Installation

Check that the package is properly installed and accessible.

npm list @foundrykit/utils

Prerequisites

Before using FoundryKit utils, ensure you have the following:

  • Node.js (v18 or higher)
  • TypeScript (v4.9 or higher) - for type support
  • React (v18 or higher) - for React-specific utilities

Check Your Environment

# Check Node.js version
node --version

# Check TypeScript version (if using TypeScript)
npx tsc --version

# Check React version
npm list react

Quick Start

Basic Usage

Import and use utilities in your project.

import { cn, formatDate, debounce, throttle } from '@foundrykit/utils'

// Class name utility
const className = cn('base-class', {
  'active': isActive,
  'disabled': isDisabled
})

// Date formatting
const formattedDate = formatDate(new Date(), 'MMM dd, yyyy')

// Function utilities
const debouncedSearch = debounce(searchFunction, 300)
const throttledScroll = throttle(scrollHandler, 100)

TypeScript Support

FoundryKit utils includes comprehensive TypeScript definitions.

import type { ClassValue, DateFormat, DebounceOptions } from '@foundrykit/utils'

// Type-safe usage
const classNames: ClassValue[] = ['base', { active: true }]
const format: DateFormat = 'yyyy-MM-dd'
const options: DebounceOptions = { leading: true, trailing: false }

Package Structure

The utils package is organized into logical modules for easy importing.

index.ts

Utility Categories

Class Name Utilities

Handle conditional class names and styling.

import { cn, clsx } from '@foundrykit/utils'

// Conditional classes
const buttonClass = cn(
  'px-4 py-2 rounded',
  variant === 'primary' && 'bg-blue-600 text-white',
  variant === 'secondary' && 'bg-gray-200 text-gray-900',
  disabled && 'opacity-50 cursor-not-allowed'
)

// Complex class merging
const cardClass = clsx(
  'card',
  {
    'card-elevated': elevated,
    'card-interactive': interactive,
    'card-loading': isLoading
  },
  className
)

Date Utilities

Format and manipulate dates consistently.

import { formatDate, parseDate, isValidDate, getRelativeTime } from '@foundrykit/utils'

// Format dates
const formatted = formatDate(new Date(), 'MMM dd, yyyy') // "Jan 15, 2024"
const iso = formatDate(new Date(), 'yyyy-MM-dd') // "2024-01-15"

// Parse dates
const date = parseDate('2024-01-15', 'yyyy-MM-dd')

// Validation
const isValid = isValidDate('2024-01-15')

// Relative time
const relative = getRelativeTime(new Date(Date.now() - 3600000)) // "1 hour ago"

Function Utilities

Optimize function calls with debouncing and throttling.

import { debounce, throttle, once, memoize } from '@foundrykit/utils'

// Debounce search input
const debouncedSearch = debounce((query: string) => {
  performSearch(query)
}, 300)

// Throttle scroll events
const throttledScroll = throttle(() => {
  updateScrollPosition()
}, 100)

// Execute only once
const initialize = once(() => {
  setupApplication()
})

// Memoize expensive calculations
const memoizedCalculation = memoize((input: number) => {
  return expensiveCalculation(input)
})

Object Utilities

Manipulate and work with objects safely.

import { pick, omit, deepMerge, isEqual, isEmpty } from '@foundrykit/utils'

// Pick specific properties
const userInfo = pick(user, ['name', 'email', 'avatar'])

// Omit properties
const publicUser = omit(user, ['password', 'apiKey'])

// Deep merge objects
const config = deepMerge(defaultConfig, userConfig)

// Compare objects
const hasChanged = !isEqual(previousData, currentData)

// Check if empty
const hasData = !isEmpty(responseData)

Array Utilities

Work with arrays more effectively.

import { unique, groupBy, sortBy, chunk, flatten } from '@foundrykit/utils'

// Remove duplicates
const uniqueItems = unique(items, 'id')

// Group by property
const groupedUsers = groupBy(users, 'department')

// Sort by property
const sortedProducts = sortBy(products, 'price')

// Split into chunks
const chunks = chunk(largeArray, 10)

// Flatten nested arrays
const flattened = flatten(nestedArray)

String Utilities

Format and manipulate strings.

import { capitalize, camelCase, kebabCase, truncate, slugify } from '@foundrykit/utils'

// Capitalize
const title = capitalize('hello world') // "Hello world"

// Case conversion
const camel = camelCase('hello-world') // "helloWorld"
const kebab = kebabCase('HelloWorld') // "hello-world"

// Truncate text
const excerpt = truncate(longText, 100)

// Create URL-friendly slugs
const slug = slugify('Hello World! 123') // "hello-world-123"

Validation Utilities

Validate data types and formats.

import { isEmail, isUrl, isPhoneNumber, isValidJSON, validateSchema } from '@foundrykit/utils'

// Email validation
const validEmail = isEmail('user@example.com')

// URL validation
const validUrl = isUrl('https://example.com')

// Phone number validation
const validPhone = isPhoneNumber('+1-555-123-4567')

// JSON validation
const validJson = isValidJSON('{"key": "value"}')

// Schema validation
const isValid = validateSchema(data, {
  name: { type: 'string', required: true },
  age: { type: 'number', min: 0 }
})

Integration Examples

React Component Example

import React, { useState, useMemo } from 'react'
import { cn, debounce, formatDate, truncate } from '@foundrykit/utils'

interface SearchableListProps {
  items: Array<{
    id: string
    title: string
    description: string
    createdAt: Date
  }>
  className?: string
}

export function SearchableList({ items, className }: SearchableListProps) {
  const [query, setQuery] = useState('')

  // Debounced search
  const debouncedSetQuery = useMemo(
    () => debounce(setQuery, 300),
    []
  )

  // Filter items based on query
  const filteredItems = useMemo(() => {
    if (!query) return items
    
    return items.filter(item =>
      item.title.toLowerCase().includes(query.toLowerCase()) ||
      item.description.toLowerCase().includes(query.toLowerCase())
    )
  }, [items, query])

  return (
    <div className={cn('searchable-list', className)}>
      <input
        type="text"
        placeholder="Search..."
        onChange={(e) => debouncedSetQuery(e.target.value)}
        className="w-full p-2 border rounded"
      />
      
      <div className="mt-4 space-y-2">
        {filteredItems.map((item) => (
          <div
            key={item.id}
            className={cn(
              'p-4 border rounded',
              'hover:bg-gray-50 transition-colors'
            )}
          >
            <h3 className="font-semibold">{item.title}</h3>
            <p className="text-gray-600 text-sm">
              {truncate(item.description, 100)}
            </p>
            <time className="text-xs text-gray-500">
              {formatDate(item.createdAt, 'MMM dd, yyyy')}
            </time>
          </div>
        ))}
      </div>
    </div>
  )
}

Form Validation Example

import React, { useState } from 'react'
import { cn, isEmail, validateSchema, debounce } from '@foundrykit/utils'

interface FormData {
  name: string
  email: string
  age: number
}

const formSchema = {
  name: { type: 'string', required: true, minLength: 2 },
  email: { type: 'string', required: true, validator: isEmail },
  age: { type: 'number', required: true, min: 0, max: 120 }
}

export function ValidatedForm() {
  const [formData, setFormData] = useState<FormData>({
    name: '',
    email: '',
    age: 0
  })
  const [errors, setErrors] = useState<Record<string, string>>({})

  // Debounced validation
  const validateField = useMemo(
    () => debounce((field: keyof FormData, value: any) => {
      const fieldSchema = { [field]: formSchema[field] }
      const fieldData = { [field]: value }
      
      const validation = validateSchema(fieldData, fieldSchema)
      
      setErrors(prev => ({
        ...prev,
        [field]: validation.errors[field] || ''
      }))
    }, 300),
    []
  )

  const handleInputChange = (field: keyof FormData, value: any) => {
    setFormData(prev => ({ ...prev, [field]: value }))
    validateField(field, value)
  }

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault()
    
    const validation = validateSchema(formData, formSchema)
    
    if (validation.isValid) {
      console.log('Form is valid:', formData)
    } else {
      setErrors(validation.errors)
    }
  }

  return (
    <form onSubmit={handleSubmit} className="space-y-4">
      <div>
        <label htmlFor="name" className="block text-sm font-medium">
          Name
        </label>
        <input
          id="name"
          type="text"
          value={formData.name}
          onChange={(e) => handleInputChange('name', e.target.value)}
          className={cn(
            'mt-1 block w-full px-3 py-2 border rounded-md',
            errors.name && 'border-red-500'
          )}
        />
        {errors.name && (
          <p className="mt-1 text-sm text-red-600">{errors.name}</p>
        )}
      </div>

      <div>
        <label htmlFor="email" className="block text-sm font-medium">
          Email
        </label>
        <input
          id="email"
          type="email"
          value={formData.email}
          onChange={(e) => handleInputChange('email', e.target.value)}
          className={cn(
            'mt-1 block w-full px-3 py-2 border rounded-md',
            errors.email && 'border-red-500'
          )}
        />
        {errors.email && (
          <p className="mt-1 text-sm text-red-600">{errors.email}</p>
        )}
      </div>

      <div>
        <label htmlFor="age" className="block text-sm font-medium">
          Age
        </label>
        <input
          id="age"
          type="number"
          value={formData.age}
          onChange={(e) => handleInputChange('age', parseInt(e.target.value))}
          className={cn(
            'mt-1 block w-full px-3 py-2 border rounded-md',
            errors.age && 'border-red-500'
          )}
        />
        {errors.age && (
          <p className="mt-1 text-sm text-red-600">{errors.age}</p>
        )}
      </div>

      <button
        type="submit"
        className="w-full py-2 px-4 bg-blue-600 text-white rounded-md hover:bg-blue-700"
      >
        Submit
      </button>
    </form>
  )
}

Tree Shaking

FoundryKit utils supports tree shaking for optimal bundle sizes.

// Import only what you need
import { cn } from '@foundrykit/utils/cn'
import { formatDate } from '@foundrykit/utils/date'
import { debounce } from '@foundrykit/utils/function'

// Or use named imports (tree-shakable)
import { cn, formatDate, debounce } from '@foundrykit/utils'

Browser Compatibility

FoundryKit utils supports modern browsers and provides polyfills where needed:

  • Chrome 90+
  • Firefox 88+
  • Safari 14+
  • Edge 90+

For older browser support, consider using polyfills:

npm install core-js

Performance Considerations

  • Tree Shaking: Import only the utilities you need
  • Memoization: Use built-in memoization for expensive operations
  • Debouncing: Optimize event handlers with debounce/throttle
  • Bundle Size: Monitor your bundle size when adding utilities

Next Steps