Files
inkreach-official-website/apps/website/.agents/skills/vue/references/utils-client.md
T
yeuimu 6c61a4e871 feat(deploy): production deployment setup and fixes
- Debian-based api image (bookworm-slim), docker/debian mirrors, prisma
  binaryTargets for openssl 3.0
- nginx: admin SPA under /admin, TLS via acme.sh (ZeroSSL) + auto-renewal
  cron, http->https redirect
- prisma: add origin_goods.delisted migration, sync missing schema
  (good_image/tag_font_color/good_tags), fix users.createdAt Timestamptz
- api: CORS wildcard reflection, helmet CORP cross-origin, price
  backfill in persistProductDetail, categoryIcon ancestor fallback,
  mediaByColor per-color gallery in public goods detail
- admin: /admin base path (vite + router)
- import-data.mjs: udt_name casting, serial sequence advance fix
2026-08-26 14:23:09 +08:00

3.5 KiB

Client Utilities

Pure functions for formatting, validation, transformation, and parsing.

Quick Reference

Category Examples
Formatters formatCurrency, formatDate, formatBytes
Validators isValidEmail, isValidUrl, isValidPhone
Transformers slugify, truncate, capitalize
Parsers parseQuery, parseJSON, parseDate

Rules

Pure functions:

  • Same input → same output
  • No side effects
  • No external state mutation
  • No API calls, no refs, no reactive

When NOT to use utils:

  • Stateful logic → use composables
  • Vue-specific → use composables
  • Component logic → keep in component
  • API calls → use queries

Structure

// utils/formatters.ts
export function formatCurrency(amount: number, currency = 'USD'): string {
  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency,
  }).format(amount)
}

export function formatRelativeTime(date: Date): string {
  const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' })
  const diff = date.getTime() - Date.now()
  const days = Math.floor(diff / (1000 * 60 * 60 * 24))
  return rtf.format(days, 'day')
}

Naming: Descriptive verbs (formatCurrency, validateEmail, parseQuery) Organization: Group by category (formatters.ts, validators.ts) Exports: Named exports only

Examples by Category

Formatters:

// utils/formatters.ts
export function formatBytes(bytes: number): string { ... }
export function formatPhone(phone: string): string { ... }

Validators:

// utils/validators.ts
export function isValidEmail(email: string): boolean {
  return /^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/.test(email)
}

export function isValidUrl(url: string): boolean {
  try { new URL(url); return true }
  catch { return false }
}

Transformers:

// utils/transformers.ts
export function slugify(text: string): string {
  return text.toLowerCase()
    .replace(/[^\w\s-]/g, '')
    .replace(/\s+/g, '-')
}

export function truncate(text: string, length: number): string {
  return text.length > length ? `${text.slice(0, length)}...` : text
}

Parsers:

// utils/parsers.ts
export function parseQuery(search: string): Record<string, string> {
  return Object.fromEntries(new URLSearchParams(search))
}

export function parseJSON<T>(json: string, fallback: T): T {
  try { return JSON.parse(json) }
  catch { return fallback }
}

Common Mistakes

Side effects (not pure):

// ❌ Wrong - mutates external state
let count = 0
export function increment() {
  count++
  return count
}

// ✅ Correct - pure
export function add(a: number, b: number): number {
  return a + b
}

Using utils for stateful logic:

// ❌ Wrong - should be composable
export function useCounter() { ... }

// ✅ Correct - pure transformation
export function formatCount(count: number): string { ... }

Organization

Flat for small projects:

utils/
├── formatters.ts
├── validators.ts
└── transformers.ts

Nested for large projects:

utils/
├── formatters/
│   ├── date.ts
│   ├── currency.ts
│   └── index.ts
└── validators/
    ├── email.ts
    └── index.ts