npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kodeme-io/next-core-patterns

v0.8.4

Published

Reusable business patterns for Next.js applications

Downloads

12

Readme

@kodeme-io/next-core-patterns

Reusable business patterns for Next.js applications - offline-first, type-safe, and framework-agnostic.

Installation

npm install @kodeme-io/next-core-patterns
# or
pnpm add @kodeme-io/next-core-patterns

Patterns

OfflineSyncQueue

Generic offline-first sync queue with automatic retry and persistence.

import { OfflineSyncQueue } from '@kodeme-io/next-core-patterns'

// Define your sync handler
const syncHandler = async (item) => {
  await fetch(`/api/${item.type}`, {
    method: 'POST',
    body: JSON.stringify(item.data)
  })
}

// Create queue instance
const queue = new OfflineSyncQueue({
  storageKey: 'my-sync-queue',
  syncHandler,
  maxRetries: 3
})

// Add items to queue
queue.enqueue({
  type: 'photo',
  data: { url: '...', metadata: {} }
})

// Subscribe to changes
queue.subscribe((items) => {
  console.log('Queue updated:', items.length, 'pending')
})

PricingCalculator

Offline-first pricing engine with tax calculations.

import { PricingCalculator } from '@kodeme-io/next-core-patterns'

const calculator = new PricingCalculator()

// Load price data
await calculator.loadPriceData({
  priceList: [
    { productId: 1, price: 100 },
    { productId: 2, price: 200 }
  ],
  taxes: [
    { id: 1, name: 'VAT', amount: 10, type: 'percent' }
  ]
})

// Calculate line item
const line = calculator.calculateLine({
  productId: 1,
  quantity: 5,
  taxIds: [1]
})
// Result: { subtotal: 500, tax: 50, total: 550 }

// Calculate order
const order = calculator.calculateOrder([
  { productId: 1, quantity: 5, taxIds: [1] },
  { productId: 2, quantity: 2, taxIds: [1] }
])
// Result: { subtotal: 900, tax: 90, total: 990 }

ScheduleUtils

Date and schedule calculation utilities - 25+ functions for common business date operations.

import {
  getCurrentWeekNumber,
  isOddWeek,
  addBusinessDays,
  getDateRange,
  formatRelativeDate,
  getBusinessDaysBetween,
  getWeekStart,
  getMonthStart,
  getQuarterStart
} from '@kodeme-io/next-core-patterns'

// ISO week calculations
const week = getCurrentWeekNumber() // 42
const isOdd = isOddWeek(week) // true

// Business days (skip weekends)
const deliveryDate = addBusinessDays(new Date(), 5)
const workDays = getBusinessDaysBetween(startDate, endDate)

// Date ranges
const nextWeek = getDateRange(new Date(), 7)
const weekStart = getWeekStart(new Date())
const monthStart = getMonthStart()

// Relative dates
const display = formatRelativeDate(date) // "Today", "Tomorrow", or day name

// Period calculations
const q1Start = getQuarterStart()
const yearEnd = getYearEnd()

Available Functions:

  • Week: getCurrentWeekNumber, isOddWeek, isCurrentWeekOdd, getWeekStart, getWeekEnd
  • Business Days: addBusinessDays, getBusinessDaysBetween, isBusinessDay, isWeekend
  • Date Ranges: getDateRange, getDaysBetween, isSameDay
  • Formatting: formatRelativeDate
  • Next Occurrence: getNextDayOfWeek
  • Period Boundaries: getMonthStart, getMonthEnd, getQuarterStart, getQuarterEnd, getYearStart, getYearEnd

GeofenceUtils

Location-based validation and GPS distance calculations using the Haversine formula.

import {
  calculateDistance,
  validateGeofence,
  formatDistance,
  hasValidGPSCoordinates,
  getCenterPoint,
  getBoundingBox
} from '@kodeme-io/next-core-patterns'

// Calculate distance between two GPS coordinates
const distance = calculateDistance(
  -6.2088, 106.8456,  // Jakarta
  -6.9175, 107.6191   // Bandung
)
// Returns: ~120000 (120km in meters)

// Validate geofence (e.g., check-in at customer location)
const result = validateGeofence(
  userLat, userLon,      // User's current location
  customerLat, customerLon, // Customer location
  100                     // 100m radius
)

if (result.isWithinGeofence) {
  console.log(`✅ Within range (${result.distance}m away)`)
} else {
  console.log(`❌ Too far: ${formatDistance(result.distance)}`)
}

// Validate GPS coordinates
if (hasValidGPSCoordinates(lat, lon)) {
  // Coordinates are valid
}

// Get center point of multiple locations
const center = getCenterPoint([
  { lat: -6.2088, lon: 106.8456 },
  { lat: -6.9175, lon: 107.6191 }
])

// Get bounding box for map viewport
const bounds = getBoundingBox(locations)
map.fitBounds([[bounds.minLat, bounds.minLon], [bounds.maxLat, bounds.maxLon]])

Available Functions:

  • calculateDistance - Haversine distance calculation
  • validateGeofence - Check if within radius
  • formatDistance - Format meters/kilometers
  • hasValidGPSCoordinates - Validate lat/lon
  • getCenterPoint - Center of multiple points
  • getBoundingBox - Min/max lat/lon bounds
  • isWithinBounds - Check if point in bounds

MediaUtils

Image compression and storage management for PWA applications.

import {
  compressPhoto,
  checkStorageQuota,
  fileToBase64,
  base64ToBlob,
  batchCompressPhotos
} from '@kodeme-io/next-core-patterns'

// Compress photo from camera
const result = await compressPhoto(photoDataUrl, {
  maxSizeKB: 500,    // Target 500KB
  maxWidth: 1920,    // Max 1920px width
  maxHeight: 1080,   // Max 1080px height
  format: 'image/jpeg'
})

console.log(`Compressed: ${result.originalSizeKB}KB → ${result.compressedSizeKB}KB`)
console.log(`Compression ratio: ${(result.compressionRatio * 100).toFixed(1)}%`)

// Check storage quota
const quota = await checkStorageQuota()
if (quota.percentUsed > 90) {
  alert(`Storage almost full: ${quota.usedMB.toFixed(0)}MB / ${quota.availableMB.toFixed(0)}MB`)
}

// Convert file to base64
const file = event.target.files[0]
const dataUrl = await fileToBase64(file)
const compressed = await compressPhoto(dataUrl)

// Convert back to blob for upload
const blob = base64ToBlob(compressed.dataUrl)
const formData = new FormData()
formData.append('photo', blob, 'photo.jpg')

// Batch compress multiple photos
const results = await batchCompressPhotos(
  photoArray,
  { maxSizeKB: 500 },
  (current, total) => console.log(`${current}/${total}`)
)

Available Functions:

  • compressPhoto - Compress image to size limit
  • getBase64SizeKB - Calculate base64 size
  • checkStorageQuota - Check browser storage
  • fileToBase64 - Convert File to base64
  • base64ToBlob - Convert base64 to Blob
  • downloadImage - Trigger download
  • batchCompressPhotos - Compress multiple

Formatters

Number, currency, phone, and text formatting utilities with Indonesian locale support.

import {
  formatCurrency,
  formatRupiah,
  formatNumber,
  formatCompactNumber,
  formatPhoneIndonesia,
  formatKTP,
  formatPercentage,
  formatFileSize,
  pluralize
} from '@kodeme-io/next-core-patterns'

// Currency formatting
formatRupiah(1500000)  // "Rp1.500.000"
formatRupiah(1500000, true)  // "Rp1.500.000,00"
formatCurrency(1500, { currency: 'USD', locale: 'en-US' })  // "$1,500.00"

// Number formatting
formatNumber(1500000)  // "1.500.000" (Indonesian)
formatCompactNumber(1500000)  // "1.5M"
formatPercentage(0.156)  // "15.6%"
formatFileSize(1536000)  // "1.5 MB"

// Phone & ID formatting
formatPhoneIndonesia('081234567890')  // "0812-3456-7890"
formatPhoneIndonesia('081234567890', true)  // "+62 812-3456-7890"
formatKTP('3216012345678901')  // "3216-012345-678901"

// Text formatting
pluralize(5, 'item')  // "5 items"
truncateText('Long text...', 10)  // "Long te..."

Available Functions (13):

  • Currency: formatCurrency, formatRupiah, formatNumber, formatCompactNumber
  • Phone/ID: formatPhoneIndonesia, formatKTP
  • Numbers: formatPercentage, formatFileSize, formatCoordinates
  • Text: truncateText, formatDateIndonesia, formatDateRange, pluralize

Validators

Form validation and data integrity checks with helpful error messages.

import {
  isValidEmail,
  isValidPhoneIndonesia,
  validatePassword,
  validateRequired,
  validateRange,
  isNumeric
} from '@kodeme-io/next-core-patterns'

// Email & phone validation
isValidEmail('[email protected]')  // true
isValidPhoneIndonesia('081234567890')  // true

// Password validation
const result = validatePassword('weak')
// { isValid: false, error: 'Password must be at least 8 characters' }

validatePassword('Password123')
// { isValid: true }

// Field validation
validateRequired('', 'Email')
// { isValid: false, error: 'Email is required' }

validateRange(15, 1, 10)
// { isValid: false, error: 'Value must be between 1 and 10' }

// Simple checks
isNumeric('12345')  // true
isAlphanumeric('abc123')  // true

Available Functions (18):

  • Email/Phone/ID: isValidEmail, isValidPhoneIndonesia, isValidKTP, isValidPostalCode
  • URL/Card: isValidURL, isValidCreditCard
  • Password: validatePassword
  • Range/Length: validateRange, validateMinLength, validateMaxLength
  • Required/Match: validateRequired, validateMatch
  • String Type: isNumeric, isAlphanumeric
  • Date: validateFutureDate, validateDateRange
  • File: validateFileSize, validateFileType

String Utilities

String manipulation and transformation functions.

import {
  slugify,
  titleCase,
  toCamelCase,
  toSnakeCase,
  getInitials,
  maskString,
  randomString
} from '@kodeme-io/next-core-patterns'

// Case conversion
slugify('Hello World!')  // "hello-world"
titleCase('hello world')  // "Hello World"
toCamelCase('hello world')  // "helloWorld"
toSnakeCase('helloWorld')  // "hello_world"
toKebabCase('helloWorld')  // "hello-world"

// Name utilities
getInitials('John Doe')  // "JD"
maskString('1234567890123456', 4, 4)  // "1234********3456"

// Text manipulation
randomString(8)  // "a7B3xY9k"
countWords('hello world')  // 2
contains('Hello World', 'world')  // true (case insensitive)

// HTML safety
escapeHtml('<script>alert("xss")</script>')
// "&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;"

Available Functions (20):

  • Case Conversion: slugify, titleCase, sentenceCase, camelToTitle, snakeToTitle
  • Case Transforms: toCamelCase, toSnakeCase, toKebabCase, capitalize
  • Text Manipulation: removeExtraSpaces, getInitials, maskString, reverse
  • Text Analysis: countWords, contains, stripHtml, escapeHtml
  • Generation: randomString, pad, wordWrap

React Query Helpers

Utilities for @tanstack/react-query - standardized patterns for data fetching.

import {
  createQueryKeys,
  StaleTime,
  createPagination,
  useDebouncedQueryKey
} from '@kodeme-io/next-core-patterns'

// Create query key factory
const customerKeys = createQueryKeys('customers')

useQuery({
  queryKey: customerKeys.list({ active: true }),
  queryFn: () => fetchCustomers({ active: true }),
  staleTime: StaleTime.MEDIUM  // 5 minutes
})

useQuery({
  queryKey: customerKeys.detail(123),
  queryFn: () => fetchCustomer(123),
  staleTime: StaleTime.LONG  // 10 minutes
})

// Pagination
const pagination = createPagination(20)
const { data } = useQuery({
  queryKey: ['items', page],
  queryFn: () => fetchItems(pagination.getOffset(page), pagination.pageSize)
})

// Debounced search
const [search, setSearch] = useState('')
const debouncedKey = useDebouncedQueryKey(['search'], search, 500)
const { data } = useQuery({
  queryKey: debouncedKey,
  queryFn: () => searchItems(search)
})

Available Functions (10):

  • Keys: createQueryKeys - Type-safe query key factory
  • Cache: StaleTime, RefetchInterval - Standard timing presets
  • Updates: createOptimisticUpdate - Optimistic update handlers
  • Pagination: createPagination, createInfiniteQueryHelpers
  • Search: useDebouncedQueryKey - Debounced keys for search
  • Errors: getQueryErrorMessage, isQueryLoading - Error handling

Odoo Helpers

Utilities for working with Odoo ERP data structures and APIs.

import {
  extractMany2oneId,
  extractMany2oneName,
  toOdooDatetime,
  toOdooDate,
  buildDomain,
  formatOdooMonetary
} from '@kodeme-io/next-core-patterns'

// Many2one fields
const userId = extractMany2oneId(partner.user_id)  // [5, "John"] → 5
const userName = extractMany2oneName(partner.user_id)  // [5, "John"] → "John"

// Date/datetime conversion
toOdooDatetime(new Date())  // "2024-12-25 10:30:00"
toOdooDate(new Date())  // "2024-12-25"

// Domain builder (type-safe)
const domain = buildDomain()
  .equals('active', true)
  .ilike('name', 'john')
  .greaterThan('age', 18)
  .build()
// [['active', '=', true], ['name', 'ilike', 'john'], ['age', '>', 18]]

// OR conditions
const domain = buildDomain()
  .or()
  .equals('state', 'draft')
  .equals('state', 'pending')
  .build()
// ['|', ['state', '=', 'draft'], ['state', '=', 'pending']]

// Format monetary
formatOdooMonetary(1500000)  // "Rp 1.500.000"

Available Functions (12):

  • Many2one: extractMany2oneId, extractMany2oneName
  • Dates: toOdooDatetime, toOdooDate, fromOdooDatetime, fromOdooDate
  • Domains: buildDomain - Type-safe domain builder
  • Formatting: formatOdooMonetary, parseOdooSelection
  • Records: isOdooRecordActive, buildOdooContext, normalizeOdooRecord

Array Utilities

Common array operations for filtering, grouping, sorting, and analysis.

import {
  groupBy,
  unique,
  sortBy,
  chunk,
  sum,
  average,
  partition
} from '@kodeme-io/next-core-patterns'

// Group by key
const customers = [
  { id: 1, city: 'Jakarta', total: 100 },
  { id: 2, city: 'Jakarta', total: 200 },
  { id: 3, city: 'Bandung', total: 150 }
]

const byCity = groupBy(customers, 'city')
// Map { 'Jakarta' => [{...}, {...}], 'Bandung' => [{...}] }

// Remove duplicates
unique([1, 2, 2, 3, 3, 3])  // [1, 2, 3]
unique(items, 'id')  // Remove by id

// Sort
sortBy(customers, 'total', 'desc')  // Highest first

// Chunk for pagination
chunk([1, 2, 3, 4, 5], 2)  // [[1, 2], [3, 4], [5]]

// Math operations
sum(orders, 'total')  // Sum all totals
average(scores, 'value')  // Average score
min(products, 'price')  // Lowest price
max(products, 'price')  // Highest price

// Partition by condition
const [active, inactive] = partition(users, (u) => u.active)

Available Functions (23):

  • Grouping: groupBy, unique, filterBy, findBy
  • Sorting: sortBy, shuffle
  • Chunking: chunk, take, takeLast, partition
  • Math: sum, average, min, max
  • Manipulation: flatten, flattenDeep, compact
  • Generation: range
  • Checks: isEmpty

Features

  • Offline-First: All patterns work offline with automatic sync when online
  • Type-Safe: Full TypeScript support
  • Framework-Agnostic: Works with any backend or API
  • Persistent: Uses localStorage for data persistence
  • Automatic Retry: Failed operations retry automatically
  • Zero Dependencies: Pure TypeScript implementation
  • GPS & Geofencing: Haversine distance calculations
  • Image Compression: Canvas-based photo compression
  • Storage Management: Quota checking and monitoring

Use Cases

  • Field Workers: Offline sync, geofencing, photo compression
  • E-commerce: Pricing calculations, order totals
  • Logistics: Delivery schedules, route validation
  • Sales: Visit schedules, customer check-ins
  • PWA Apps: Storage management, media optimization

License

MIT © ABC Food