Api Reference

Types & Configuration

Complete TypeScript reference for all interfaces, types, and configuration options in Nuxt Crouton

Nuxt Crouton provides comprehensive TypeScript types for type-safe collection management, UI configuration, and data operations. This reference documents all core types, interfaces, and configuration patterns.

Core Configuration Types

CollectionConfig

The master configuration interface for registering collections in app.config.ts.

type CollectionKind = 'data' | 'content' | 'media'

interface CollectionConfig {
  // Basic Collection Info
  name?: string
  layer?: string
  componentName?: string
  /** Collection kind: determines behavior and available features. */
  kind?: CollectionKind
  /** Container type for create/update forms. Defaults to 'slideover'. */
  container?: 'slideover' | 'modal' | 'dialog' | 'inline'
  apiPath?: string
  displayName?: string

  // Pagination Settings
  defaultPagination?: {
    currentPage: number
    pageSize: number
    sortBy: string
    sortDirection: 'asc' | 'desc'
  }

  // Relationship Declarations
  references?: Record<string, string>

  // Custom Component Mapping
  dependentFieldComponents?: Record<string, string>

  // Hierarchy (tree-enabled collections)
  hierarchy?: {
    enabled: boolean
    parentField?: string
    pathField?: string
    depthField?: string
    orderField?: string
  }

  // Sortable (flat collections with ordering, without hierarchy)
  sortable?: {
    enabled: boolean
    orderField?: string
  }

  // Admin sidebar navigation
  adminNav?: {
    enabled?: boolean
    icon?: string
    label?: string
    order?: number
  }

  // Display config: maps display roles to field names
  display?: DisplayConfig

  // Runtime field metadata for display components
  fields?: RuntimeFieldMeta[]

  // When true, collection auto-registers as a page type in crouton-pages
  publishable?: boolean

  // Extensible for custom properties
  [key: string]: any
}

Property Details

PropertyTypeDescription
namestringCollection name (usually auto-set by generator)
layerstringNuxt layer this collection belongs to
componentNamestringName of the form component (e.g., 'ShopProductsForm')
kindCollectionKindCollection kind: 'data', 'content', or 'media'. Determines behavior and available features
container'slideover' | 'modal' | 'dialog' | 'inline'Container type for create/update forms (default: 'slideover')
apiPathstringAPI endpoint path (defaults to collection name)
displayNamestringHuman-readable display name for the collection
defaultPaginationobjectDefault pagination settings for this collection
referencesRecord<string, string>Field-to-collection mappings for automatic cache refresh
dependentFieldComponentsRecord<string, string>Custom component mappings for dependent fields
hierarchyobjectHierarchy config for tree-enabled collections (generated by CLI --hierarchy flag)
sortableobjectSortable config for flat collections with drag-drop ordering
adminNavobjectControls how the collection appears in the admin sidebar
displayDisplayConfigMaps display roles (title, subtitle, image, badge, description) to field names
fieldsRuntimeFieldMeta[]Lightweight runtime field metadata: name, type, label, area, displayAs
publishablebooleanWhen true, auto-registers as a page type in crouton-pages

Basic Usage

// app.config.ts
export default defineAppConfig({
  croutonCollections: {
    shopProducts: {
      name: 'shopProducts',
      layer: 'shop',
      componentName: 'ShopProductsForm',
      apiPath: 'products',
      defaultPagination: {
        currentPage: 1,
        pageSize: 25,
        sortBy: 'name',
        sortDirection: 'asc'
      }
    }
  }
})

With References (Cache Invalidation)

When a collection has fields that reference other collections, declare them using references. This enables automatic cache refresh when related items are updated.

// app.config.ts
export default defineAppConfig({
  croutonCollections: {
    bookingsEvents: {
      name: 'bookingsEvents',
      layer: 'bookings',
      componentName: 'BookingsEventsForm',
      references: {
        location: 'bookingsLocations',  // 'location' field references 'bookingsLocations'
        host: 'users',                   // 'host' field references 'users'
        category: 'bookingsCategories'   // 'category' field references 'bookingsCategories'
      }
    }
  }
})

How references work:

  1. When you update an event with { location: 'location-123' }
  2. Crouton invalidates cache for both:
    • collection:bookingsEvents:* (the event collection)
    • collection:bookingsLocations:* (the referenced location collection)
  3. Any UI displaying locations automatically refreshes to show updated data
  4. This keeps CardMini displays in sync across the application

With Custom Dependent Fields

For complex field relationships, map field names to custom components:

// app.config.ts
export default defineAppConfig({
  croutonCollections: {
    bookingsEvents: {
      name: 'bookingsEvents',
      layer: 'bookings',
      componentName: 'BookingsEventsForm',
      dependentFieldComponents: {
        slots: 'SlotSelect',           // Use SlotSelect component for 'slots' field
        recurringPattern: 'RecurringPatternEditor'  // Custom editor for recurring events
      }
    }
  }
})

The FormDependentFieldLoader component uses this mapping to dynamically load the correct component for each field.

Custom Properties

You can extend CollectionConfig with any custom properties your application needs:

export default defineAppConfig({
  croutonCollections: {
    shopProducts: {
      name: 'shopProducts',
      layer: 'shop',
      componentName: 'ShopProductsForm',

      // Custom properties
      features: {
        enableInventoryTracking: true,
        enableVariants: true
      },
      permissions: {
        create: 'product:create',
        update: 'product:update',
        delete: 'product:delete'
      },
      metadata: {
        icon: 'i-lucide-package',
        displayName: 'Products',
        singularName: 'Product'
      }
    }
  }
})

Access custom properties via useCollections():

const collections = useCollections()
const config = collections.getConfig('shopProducts')

if (config?.features?.enableInventoryTracking) {
  // Show inventory fields
}

ExternalCollectionConfig

Configuration for external collections (e.g., users from auth system, third-party APIs).

interface ExternalCollectionConfig {
  name: string
  schema: z.ZodSchema
  apiPath?: string
  fetchStrategy?: 'query' | 'restful'
  readonly?: boolean
  meta?: {
    label?: string
    description?: string
  }
  proxy?: {
    enabled: boolean
    sourceEndpoint: string
    transform: (item: any) => { id: string; title: string; [key: string]: any }
  }
}

Property Details

PropertyTypeDefaultDescription
namestringrequiredCollection name (must match app.config.ts key)
schemaz.ZodSchemarequiredZod schema for validation and types
apiPathstringnameAPI endpoint path
fetchStrategy'query' | 'restful''query'Fetch method: ?ids= vs /{id}
readonlybooleantrueHide edit/delete buttons in CardMini
metaobject{}Display metadata
proxyobjectundefinedProxy configuration for external endpoints

Basic External Collection

// composables/useExternalCollections.ts
import { defineExternalCollection } from '@fyit/crouton'
import { z } from 'zod'

const userSchema = z.object({
  id: z.string(),
  title: z.string(),     // Required for CroutonReferenceSelect display
  email: z.string().optional(),
  avatarUrl: z.string().optional()
})

export const usersConfig = defineExternalCollection({
  name: 'users',
  schema: userSchema,
  apiPath: 'users',
  readonly: true,        // Users managed by auth system
  meta: {
    label: 'Team Members',
    description: 'Users from the authentication system'
  }
})
// app.config.ts
import { usersConfig } from '~/composables/useExternalCollections'

export default defineAppConfig({
  croutonCollections: {
    users: usersConfig,
    // ... other collections
  }
})

With Proxy Transform

Proxy external endpoints and transform data to Crouton format:

import { defineExternalCollection } from '@fyit/crouton'
import { z } from 'zod'

const memberSchema = z.object({
  id: z.string(),
  title: z.string(),
  role: z.string(),
  joinedAt: z.string()
})

export const membersConfig = defineExternalCollection({
  name: 'members',
  schema: memberSchema,
  proxy: {
    enabled: true,
    sourceEndpoint: 'members',  // → /api/teams/[id]/members
    transform: (item) => ({
      id: item.userId,
      title: `${item.firstName} ${item.lastName}`,  // Transform to required 'title' field
      role: item.memberRole,
      joinedAt: item.createdAt
    })
  }
})

How proxy works:

  1. CroutonReferenceSelect fetches from /api/teams/[teamId]/members
  2. Raw data from external system is transformed using the transform function
  3. Transformed data has id and title fields (required by Crouton)
  4. Data displays correctly in CroutonItemCardMini and CroutonReferenceSelect

RESTful Fetch Strategy

For APIs using /resource/{id} pattern instead of /resource?ids=:

export const customersConfig = defineExternalCollection({
  name: 'customers',
  schema: customerSchema,
  fetchStrategy: 'restful',  // Use /{id} instead of ?ids=
  apiPath: 'customers'
})

This changes how single items are fetched:

  • 'query' (default): GET /api/teams/[id]/customers?ids=customer-123
  • 'restful': GET /api/teams/[id]/customers/customer-123

Layout System Types

LayoutType

Basic layout modes for displaying collections.

type LayoutType = 'table' | 'list' | 'grid' | 'tree' | 'kanban' | 'workspace'
LayoutDescriptionUse Case
tableTraditional data tableDesktop, detailed data with many columns
listCompact list viewMobile, simple data, quick scanning
gridGrid of cards (2-3 columns)Mobile/tablet, visual content
treeHierarchical tree viewParent-child relationships, nested data
kanbanKanban board viewStatus-based workflows, task management
workspaceWorkspace layoutComplex multi-panel interfaces

ResponsiveLayout

Responsive layout configuration with breakpoint support.

interface ResponsiveLayout {
  base: LayoutType
  sm?: LayoutType   // 640px+
  md?: LayoutType   // 768px+
  lg?: LayoutType   // 1024px+
  xl?: LayoutType   // 1280px+
  '2xl'?: LayoutType // 1536px+
}

Basic Responsive Layout

<script setup lang="ts">
const layout = {
  base: 'list',   // Mobile: list
  md: 'grid',     // Tablet: grid
  lg: 'table'     // Desktop: table
}
</script>

<template>
  <CroutonCollection
    :layout="layout"
    :rows="products"
    :columns="columns"
    collection="shopProducts"
  />
</template>

Using Layout Presets

// Built-in presets
const layoutPresets = {
  'responsive': { base: 'list', md: 'grid', lg: 'table' },
  'mobile-friendly': { base: 'list', lg: 'table' },
  'compact': { base: 'list', xl: 'table' },
  'tree-default': { base: 'tree' }
}
<template>
  <!-- Use preset by name -->
  <CroutonCollection
    layout="responsive"
    :rows="products"
    :columns="columns"
    collection="shopProducts"
  />
</template>

Complex Responsive Example

<script setup lang="ts">
import type { ResponsiveLayout } from '@fyit/crouton'

// Fine-tuned for different screen sizes
const layout: ResponsiveLayout = {
  base: 'list',      // Phone (< 640px)
  sm: 'list',        // Large phone (640px+)
  md: 'grid',        // Tablet (768px+)
  lg: 'grid',        // Small laptop (1024px+)
  xl: 'table',       // Desktop (1280px+)
  '2xl': 'table'     // Large desktop (1536px+)
}
</script>

<template>
  <CroutonCollection
    :layout="layout"
    :rows="products"
    :columns="columns"
    collection="shopProducts"
  />
</template>

Table and Column Types

TableColumn

Column definition for table layouts.

interface TableColumn {
  id?: string
  accessorKey?: string
  header: string | ((props: any) => any)
  cell?: (props: any) => any
  sortable?: boolean
  enableSorting?: boolean
  enableHiding?: boolean
}

Property Details

PropertyTypeDefaultDescription
idstringaccessorKeyUnique column identifier
accessorKeystring-Object property to access (dot notation supported)
headerstring | functionrequiredColumn header text or render function
cellfunction-Custom cell renderer (receives { row, value })
sortablebooleanfalseEnable sorting for this column
enableSortingbooleansortableTanStack Table sorting flag
enableHidingbooleantrueAllow hiding this column

Basic Columns

const columns: TableColumn[] = [
  {
    accessorKey: 'name',
    header: 'Product Name',
    sortable: true
  },
  {
    accessorKey: 'price',
    header: 'Price',
    sortable: true
  },
  {
    accessorKey: 'inStock',
    header: 'In Stock'
  }
]

Custom Cell Renderers

import type { TableColumn } from '@fyit/crouton'

const columns: TableColumn[] = [
  {
    accessorKey: 'price',
    header: 'Price',
    cell: ({ value }) => `$${value.toFixed(2)}`
  },
  {
    accessorKey: 'status',
    header: 'Status',
    cell: ({ row }) => {
      const status = row.original.status
      const color = status === 'active' ? 'green' : 'gray'
      return h('span', { class: `text-${color}-600` }, status)
    }
  }
]

Nested Data Access

const columns: TableColumn[] = [
  {
    accessorKey: 'user.name',    // Dot notation for nested properties
    header: 'User Name'
  },
  {
    accessorKey: 'location.city',
    header: 'City'
  }
]

Custom Header Renderers

const columns: TableColumn[] = [
  {
    accessorKey: 'price',
    header: () => h('div', [
      h('span', 'Price '),
      h('span', { class: 'text-xs text-gray-500' }, '(USD)')
    ])
  }
]

CollectionProps

Props interface for CroutonCollection component.

interface CollectionProps {
  // Layout Configuration
  layout?: LayoutType | ResponsiveLayout | keyof typeof layoutPresets
  card?: 'Card' | 'CardMini' | 'CardSmall' | 'CardTree' | string  // Card variant
  /** Direct card component (skips name resolution, for stateless mode) */
  cardComponent?: any

  // Data
  rows?: any[]
  columns?: TableColumn[]
  collection: string

  // Pagination
  serverPagination?: boolean
  paginationData?: PaginationData | null
  refreshFn?: () => Promise<void> | null

  // UI Options
  create?: boolean
  /** Hierarchy configuration for tree layouts */
  hierarchy?: HierarchyConfig
  /** Enable drag-and-drop row reordering (table layout only) */
  sortable?: boolean | SortableOptions
  /**
   * Grid size for grid layout
   * - compact: 4 columns, tight spacing
   * - comfortable: 3 columns, medium spacing (default)
   * - spacious: 2 columns, generous spacing
   */
  gridSize?: 'compact' | 'comfortable' | 'spacious'
  hideDefaultColumns?: {
    select?: boolean
    createdAt?: boolean
    updatedAt?: boolean
    createdBy?: boolean
    updatedBy?: boolean
    presence?: boolean
    actions?: boolean
  }
  /** Stateless mode: no config lookup, no mutations, just renders data */
  stateless?: boolean
  /** Show collaboration presence badges per row (requires @fyit/crouton-collab) */
  showCollabPresence?: boolean | CollabPresenceConfig
}

The card prop allows specifying which card variant to use:

  • card="CardSmall" resolves to {Collection}CardSmall (e.g., BookingsCardSmall)
  • card="CardTree" resolves to {Collection}CardTree
  • No card prop uses {Collection}Card with the layout prop passed to it

Complete Example

Query Examples: For complete useCollectionQuery patterns, see Querying Data.
<script setup lang="ts">
import type { CollectionProps, TableColumn } from '@fyit/crouton'

// See /fundamentals/querying for query patterns
const { items, pending, refresh } = await useCollectionQuery('shopProducts')

const columns: TableColumn[] = [
  { accessorKey: 'name', header: 'Name', sortable: true },
  { accessorKey: 'price', header: 'Price', sortable: true }
]

const paginationData = computed(() => ({
  currentPage: page.value,
  pageSize: 25,
  totalItems: totalItems.value
}))
</script>

<template>
  <CroutonCollection
    layout="responsive"
    :rows="items"
    :columns="columns"
    collection="shopProducts"
    :server-pagination="true"
    :pagination-data="paginationData"
    :refresh-fn="refresh"
    create
    :hide-default-columns="{
      createdBy: true,
      updatedBy: true
    }"
  />
</template>

Pagination Types

PaginationData

Pagination metadata for server-side pagination.

interface PaginationData {
  currentPage: number
  pageSize: number
  totalItems: number
  totalPages?: number
  sortBy?: string
  sortDirection?: 'asc' | 'desc'
}

Property Details

PropertyTypeRequiredDescription
currentPagenumberYesCurrent page number (1-indexed)
pageSizenumberYesItems per page
totalItemsnumberYesTotal number of items across all pages
totalPagesnumberNoTotal pages (auto-calculated if omitted)
sortBystringNoCurrent sort column
sortDirection'asc' | 'desc'NoCurrent sort direction

Pagination Usage

Pagination Examples: For complete pagination and sorting patterns with useCollectionQuery, see Querying Data.
<script setup lang="ts">
// See /fundamentals/querying for query patterns
const { items, data } = await useCollectionQuery('shopProducts')

const paginationData = computed(() => ({
  currentPage: page.value,
  pageSize: 25,
  totalItems: data.value?.pagination?.totalItems || 0
}))
</script>

<template>
  <CroutonCollection
    :rows="items"
    :columns="columns"
    collection="shopProducts"
    :server-pagination="true"
    :pagination-data="paginationData"
  />
</template>

Server Response Format

Your API should return data in this format:

// GET /api/teams/[id]/products?page=1&pageSize=25
{
  items: [
    { id: '1', name: 'Product 1', price: 29.99 },
    // ... more items
  ],
  pagination: {
    currentPage: 1,
    pageSize: 25,
    totalItems: 156,
    totalPages: 7,
    sortBy: 'name',
    sortDirection: 'asc'
  }
}

PaginationState (Internal)

Internal pagination state (used within composables).

interface PaginationState {
  currentPage: number
  pageSize: number
  sortBy: string
  sortDirection: 'asc' | 'desc'
  totalItems?: number
  totalPages?: number
}

Used internally by useCrouton() to manage pagination across multiple collections.

Composable Return Types

CollectionQueryReturn

Return type of useCollectionQuery().

interface CollectionQueryReturn<T = any> {
  items: ComputedRef<T[]>
  data: Ref<any>
  refresh: () => Promise<void>
  pending: Ref<boolean>
  error: Ref<any>
}

Usage with Types

import type { ShopProduct } from '~/layers/shop/types/products'

const {
  items,      // ComputedRef<ShopProduct[]>
  pending,    // Ref<boolean>
  error,      // Ref<any>
  refresh     // () => Promise<void>
} = await useCollectionQuery<ShopProduct>('shopProducts')

CollectionQueryOptions

Options for useCollectionQuery().

interface CollectionQueryOptions {
  query?: ComputedRef<Record<string, any>> | Ref<Record<string, any>>
  watch?: boolean
}

Property Details

PropertyTypeDefaultDescription
queryComputedRef | Ref{}Reactive query parameters
watchbooleantrueAuto-refetch when query changes

Basic Query Options

const page = ref(1)
const search = ref('')

const { items } = await useCollectionQuery('shopProducts', {
  query: computed(() => ({
    page: page.value,
    search: search.value
  })),
  watch: true  // Auto-refetch when page or search changes
})

CollectionMutation

Return type of useCollectionMutation().

interface CollectionMutation {
  create: (data: any) => Promise<any>
  update: (id: string, data: any) => Promise<any>
  deleteItems: (ids: string[]) => Promise<void>
}

Usage

See Mutation Composables API for usage examples.

Component Prop Types

CardProps

Props for custom card components.

interface CardProps {
  item: any
  layout: 'list' | 'grid' | 'tree' | 'kanban' | 'workspace'
  collection: string
  pending?: boolean
  error?: any
}

Custom Card Component

<script setup lang="ts">
import type { CardProps } from '@fyit/crouton'

const props = defineProps<CardProps>()
</script>

<template>
  <div :class="layout === 'list' ? 'py-2' : 'p-4 border rounded'">
    <h3>{{ item.name }}</h3>
    <p v-if="layout !== 'list'">{{ item.description }}</p>
  </div>
</template>

TableSearchProps

Props for CroutonTableSearch component.

interface TableSearchProps {
  modelValue: string
  placeholder?: string
  debounceMs?: number
}

TablePaginationProps

Props for CroutonTablePagination component.

interface TablePaginationProps {
  page: number
  pageCount: number
  totalItems: number
  loading?: boolean
  pageSizes?: number[]
}

TableActionsProps

Props for CroutonTableActions component.

interface TableActionsProps {
  selectedRows: any[]
  collection: string
  table?: any
  onDelete?: (ids: string[]) => void
  onColumnVisibilityChange?: (column: string, visible: boolean) => void
}

Hook System Types

crouton:mutation Hook

Nuxt hook emitted after successful mutations. Use for event tracking, analytics, or custom cache invalidation.

// Hook payload type (defined as CroutonMutationEvent in crouton-hooks.d.ts)
interface CroutonMutationEvent {
  operation: 'create' | 'update' | 'delete' | 'move' | 'reorder'
  collection: string
  itemId?: string
  itemIds?: string[]
  data?: Record<string, unknown>
  updates?: Record<string, unknown>
  /** The item data before the mutation (for update operations, enables change tracking) */
  beforeData?: Record<string, unknown>
  result?: unknown
  /** Correlation ID for linking related operations and events */
  correlationId?: string
  /** Timestamp when the mutation was initiated */
  timestamp?: number
}

Registering Hook Listeners

// plugins/crouton-events.ts
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.hooks.hook('crouton:mutation', async (payload) => {
    const { operation, collection, itemId, data, result } = payload

    // Track analytics
    if (operation === 'create') {
      console.log(`Created ${collection} item:`, itemId)
      await $fetch('/api/analytics/track', {
        method: 'POST',
        body: {
          event: `${collection}_created`,
          properties: { itemId }
        }
      })
    }

    // Custom cache invalidation
    if (collection === 'shopProducts' && operation === 'update') {
      // Invalidate related caches
      await clearNuxtData('product-analytics')
      await clearNuxtData('trending-products')
    }
  })
})

Hook Payload Examples

Create operation:

{
  operation: 'create',
  collection: 'shopProducts',
  itemId: 'product-123',
  data: {
    name: 'New Product',
    price: 49.99
  },
  result: {
    id: 'product-123',
    name: 'New Product',
    price: 49.99,
    createdAt: '2025-01-15T10:30:00Z'
  },
  correlationId: 'abc-123',
  timestamp: 1705312200000
}

Update operation:

{
  operation: 'update',
  collection: 'shopProducts',
  itemId: 'product-123',
  updates: {
    price: 39.99
  },
  beforeData: {
    id: 'product-123',
    name: 'New Product',
    price: 49.99
  },
  result: {
    id: 'product-123',
    name: 'New Product',
    price: 39.99,
    updatedAt: '2025-01-15T11:00:00Z'
  },
  correlationId: 'abc-456',
  timestamp: 1705314000000
}

Delete operation:

{
  operation: 'delete',
  collection: 'shopProducts',
  itemIds: ['product-123', 'product-456'],
  result: undefined,
  correlationId: 'abc-789',
  timestamp: 1705315800000
}

Move operation (tree/hierarchy):

{
  operation: 'move',
  collection: 'shopCategories',
  itemId: 'category-5',
  data: { parentId: 'category-2', order: 3 },
  correlationId: 'abc-012',
  timestamp: 1705317600000
}

Reorder operation (sortable):

{
  operation: 'reorder',
  collection: 'shopProducts',
  itemIds: ['product-1', 'product-3', 'product-2'],
  correlationId: 'abc-345',
  timestamp: 1705319400000
}

Use Cases for Hooks

1. Event Tracking / Analytics

nuxtApp.hooks.hook('crouton:mutation', async ({ operation, collection, itemId }) => {
  await $fetch('/api/analytics/events', {
    method: 'POST',
    body: {
      event: `${collection}.${operation}`,
      userId: user.value?.id,
      timestamp: new Date().toISOString(),
      metadata: { itemId }
    }
  })
})

2. Audit Logging

nuxtApp.hooks.hook('crouton:mutation', async (payload) => {
  await $fetch('/api/audit-log', {
    method: 'POST',
    body: {
      action: payload.operation,
      resource: payload.collection,
      resourceId: payload.itemId,
      changes: payload.data,
      performedBy: user.value?.id,
      timestamp: new Date()
    }
  })
})

3. Custom Cache Invalidation

nuxtApp.hooks.hook('crouton:mutation', async ({ collection, operation }) => {
  // When products change, refresh dashboard stats
  if (collection === 'shopProducts') {
    await clearNuxtData('dashboard-stats')
    await clearNuxtData('inventory-summary')
  }

  // When orders change, refresh customer data
  if (collection === 'shopOrders') {
    await clearNuxtData('customer-orders')
    await clearNuxtData('revenue-stats')
  }
})

4. Webhook Notifications

nuxtApp.hooks.hook('crouton:mutation', async (payload) => {
  // Notify external systems of changes
  if (payload.operation === 'create' && payload.collection === 'shopOrders') {
    await $fetch('/api/webhooks/order-created', {
      method: 'POST',
      body: {
        orderId: payload.itemId,
        order: payload.result
      }
    })
  }
})

5. Real-time Updates (WebSocket)

nuxtApp.hooks.hook('crouton:mutation', async (payload) => {
  // Broadcast changes to connected clients
  websocket.broadcast({
    type: 'collection:mutation',
    collection: payload.collection,
    operation: payload.operation,
    itemId: payload.itemId
  })
})

State Management Types

CroutonState (Internal)

Internal state for modal/form management (used by useCrouton()).

interface CroutonState {
  id: string
  action: CroutonAction
  collection: string | null
  activeItem: any
  items: any[]
  loading: LoadingState
  isOpen: boolean
  containerType: 'slideover' | 'modal' | 'dialog' | 'inline'
}

type CroutonAction = 'create' | 'update' | 'delete' | 'view' | undefined

type LoadingState =
  | 'notLoading'
  | 'create_send' | 'update_send' | 'delete_send' | 'view_send'
  | 'create_open' | 'update_open' | 'delete_open' | 'view_open'

Utility Types

ProxyConfig (Internal)

Configuration for proxying external collections.

interface ProxyConfig {
  enabled: boolean
  sourceEndpoint: string
  transform: (item: any) => { id: string; title: string; [key: string]: any }
}

ConfigsMap (Internal)

Type for collection configuration registry.

type ConfigsMap = {
  [K in CollectionName]?: CollectionConfig
}

Type Guards and Helpers

Checking Collection Config

const collections = useCollections()
const config = collections.getConfig('shopProducts')

if (!config) {
  throw new Error('Collection not found')
}

// Access config properties
const apiPath = config.apiPath || 'products'
const references = config.references || {}

Type-Safe Query Building

import type { PaginationData } from '@fyit/crouton'

function buildPaginationData(
  page: number,
  pageSize: number,
  total: number
): PaginationData {
  return {
    currentPage: page,
    pageSize,
    totalItems: total,
    totalPages: Math.ceil(total / pageSize)
  }
}

Type-Safe Column Definitions

import type { TableColumn } from '@fyit/crouton'
import type { ShopProduct } from '~/layers/shop/types/products'

function defineProductColumns(): TableColumn[] {
  return [
    {
      accessorKey: 'name',
      header: 'Product Name',
      sortable: true
    },
    {
      accessorKey: 'price',
      header: 'Price',
      cell: ({ row }: { row: { original: ShopProduct } }) =>
        `$${row.original.price.toFixed(2)}`
    }
  ]
}

Common Patterns

Pattern 1: Type-Safe Collection Setup

// 1. Define your data type
import type { ShopProduct } from '~/layers/shop/types/products'

// 2. Register collection config
// app.config.ts
export default defineAppConfig({
  croutonCollections: {
    shopProducts: {
      name: 'shopProducts',
      layer: 'shop',
      componentName: 'ShopProductsForm'
    }
  }
})

// 3. Use with type parameter
const { items, pending } = await useCollectionQuery<ShopProduct>('shopProducts')
// items is ComputedRef<ShopProduct[]>

Pattern 2: External Collection with Transform

// 1. Define external collection
import { defineExternalCollection } from '@fyit/crouton'

export const membersConfig = defineExternalCollection({
  name: 'members',
  schema: z.object({
    id: z.string(),
    title: z.string()
  }),
  proxy: {
    enabled: true,
    sourceEndpoint: 'members',
    transform: (item) => ({
      id: item.userId,
      title: `${item.firstName} ${item.lastName}`
    })
  }
})

// 2. Register in app.config.ts
export default defineAppConfig({
  croutonCollections: {
    members: membersConfig
  }
})

// 3. Use in components
const { items } = await useCollectionQuery('members')

Pattern 3: Responsive Layout with Type Safety

import type { ResponsiveLayout, TableColumn } from '@fyit/crouton'

const layout: ResponsiveLayout = {
  base: 'list',
  md: 'grid',
  lg: 'table'
}

const columns: TableColumn[] = [
  { accessorKey: 'name', header: 'Name' }
]

Pattern 4: Server Pagination with Types

import type { PaginationData } from '@fyit/crouton'

const page = ref(1)

const { items, data } = await useCollectionQuery('shopProducts', {
  query: computed(() => ({ page: page.value }))
})

const paginationData = computed<PaginationData>(() => ({
  currentPage: page.value,
  pageSize: 25,
  totalItems: data.value?.pagination?.totalItems || 0
}))

Pattern 5: Hook Integration

// Plugin for mutation tracking
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.hooks.hook('crouton:mutation', async (payload) => {
    // Type-safe payload access
    const { operation, collection, itemId, data, result } = payload

    console.log(`[${operation}] ${collection}:`, itemId)
  })
})

TypeScript Configuration

{
  "extends": "./.nuxt/tsconfig.json",
  "compilerOptions": {
    "strict": true,
    "types": ["@nuxt/types", "@fyit/crouton"]
  }
}

Module Augmentation

Extend Crouton types for your app:

// types/crouton.d.ts
declare module '@fyit/crouton' {
  interface CollectionConfig {
    // Add custom properties
    permissions?: {
      create?: string
      update?: string
      delete?: string
    }
    metadata?: {
      icon?: string
      displayName?: string
    }
  }
}

Type Checking

Always run type checking after making changes:

# Type check your application
npx nuxt typecheck

# Watch mode for development
npx nuxt typecheck --watch