Configuration
format(value, formatType, options?) accepts an options object whose shape depends on formatType. All fields are optional; sensible defaults cover the common case (space-delimited MX phones, slash-delimited dates, etc.).
import type { FormatOptions, FormatType } from '@samline/formatter'
function format( value: unknown, formatType: FormatType, options?: FormatOptions): FormatterResultInternally FormatOptions is the intersection of cleave-zen’s per-type option interfaces plus a few package-specific extras (country, dateRawPattern, timeRawPattern, prefixMode, rawPrefix, suffix, suffixMode, rawSuffix, tailPrefix). Only the fields relevant to your formatType are read; everything else is ignored.
Common fields
Section titled “Common fields”These apply to most format types.
| Field | Type | Default | Applies to | Notes |
|---|---|---|---|---|
country |
string (ISO 3166-1 alpha-2) |
'MX' for phone, '' otherwise |
phone |
Passed to libphonenumber-js’s AsYouType. |
delimiter |
string |
phone: ' '; others: cleave-zen default |
all | Single-character separator for the formatted display. |
delimiters |
string[] |
[] |
general, numeral |
Additional separators (e.g. [' ', '-']). |
prefix |
string |
'' |
general, numeral |
Prepended to the display. See Prefix & suffix on general for how the formatter manages it (including prefixMode and rawPrefix). |
tailPrefix |
boolean |
false |
general, numeral |
Legacy: when true (and suffix is not provided), prefix is treated as a suffix (stripped from the end). Prefer the new dedicated suffix option for new code. |
general
Section titled “general”Type:
'general'
Block-based masking with custom delimiter / delimiters. Pair with blocks to define group sizes.
| Field | Type | Default | Notes |
|---|---|---|---|
blocks |
number[] |
[] |
Required for sensible output. Defines block sizes (e.g. [4, 4, 4, 4] for credit-card-style groups). |
delimiterLazyShow |
boolean |
false |
Show delimiters only when the next block has content. |
numericOnly |
boolean |
false |
Strip non-digit characters. Applied to both display and (when rawPrefix: true / rawSuffix: true) to the canonical raw mirror — see Prefix & suffix on general. |
uppercase |
boolean |
false |
Force uppercase. |
lowercase |
boolean |
false |
Force lowercase. |
prefixMode |
'lock' | 'passthrough' |
'lock' |
'lock' (default) auto-prepends the configured prefix; 'passthrough' reflects whatever the user has typed of the prefix instead (so E sticks for a configured EASY). See Prefix & suffix on general. |
rawPrefix |
boolean |
false |
When true, the raw mirror includes the configured prefix. Default false (digits-only). |
suffix |
string |
'' |
Tail decoration appended at the end of the display (e.g. ' USD', '-END'). Independent from prefix; can differ from it. |
suffixMode |
'lock' | 'passthrough' |
'lock' |
'lock' (default) auto-appends the configured suffix; 'passthrough' reflects whatever the user has typed of the suffix instead. |
rawSuffix |
boolean |
false |
When true, the raw mirror includes the configured suffix. Default false. |
Prefix & suffix on general
Section titled “Prefix & suffix on general”general formats can decorate the visible value with a head (prefix) and/or a tail (suffix). The formatter manages both entirely on its own — it never delegates prefix handling to cleave-zen (whose internal stripPrefix discards any input that doesn’t already start with the configured prefix, freezing the field at the literal prefix).
prefixMode — how the user interacts with the head decoration
Section titled “prefixMode — how the user interacts with the head decoration”'lock'(default): the user types only the body; the formatter auto-prepends the configuredprefixon every call. If the input happens to start with the prefix (paste case), the prefix is stripped before processing.'passthrough': the user types (or pastes) the prefix themselves. Every keystroke that matches the prefix sticks, soE,EA,EAS,EASYall stay in the field until you type past it.
// Auto-prepend `EASY`, user types only the 9 digitsformat('123456789', 'general', { blocks: [13], prefix: 'EASY' })// => { formatted: 'EASY123456789', raw: '123456789', type: 'general' }
// User types the prefix themselves character by characterformat('EASY1', 'general', { blocks: [13], prefix: 'EASY', prefixMode: 'passthrough'})// => { formatted: 'EASY1', raw: '1', type: 'general' }suffixMode — how the user interacts with the tail decoration
Section titled “suffixMode — how the user interacts with the tail decoration”Mirror image of prefixMode. 'lock' (default) auto-appends the configured suffix; 'passthrough' lets the user type it character by character.
format('123456789', 'general', { blocks: [12], suffix: 'USD' })// => { formatted: '123456789USD', raw: '123456789', type: 'general' }
format('12345US', 'general', { blocks: [12], suffix: 'USD', suffixMode: 'passthrough'})// => { formatted: '12345US', raw: '12345', type: 'general' }rawPrefix / rawSuffix — what the raw mirror contains
Section titled “rawPrefix / rawSuffix — what the raw mirror contains”The default raw value is the digits-only body the user typed. Opt in with rawPrefix: true / rawSuffix: true when the backend needs the canonical value with the decoration included:
// Backend wants the canonical identifierformat('123456789', 'general', { blocks: [13], prefix: 'EASY', rawPrefix: true})// => { formatted: 'EASY123456789', raw: 'EASY123456789', type: 'general' }
// Both ends canonicalformat('12345', 'general', { blocks: [11], prefix: 'PRE-', suffix: '-END', rawPrefix: true, rawSuffix: true})// => { formatted: 'PRE-12345-END', raw: 'PRE-12345-END', type: 'general' }When rawPrefix / rawSuffix is set, the body part of raw is derived from the formatted body (with the display delimiter stripped) rather than from the user’s typed input verbatim. This means numericOnly and the case transforms (uppercase / lowercase) are honoured in the canonical raw too — a user fat-fingering letters into a numericOnly: true field no longer ships a contaminated identifier to the backend.
// Fat-fingered input — display is clean AND the canonical raw is clean.format('1a2b3c4d5e6f7g8h9i', 'general', { blocks: [4, 5], delimiter: ' ', prefix: 'EASY', prefixMode: 'lock', rawPrefix: true, numericOnly: true})// => { formatted: 'EASY1234 56789', raw: 'EASY123456789', type: 'general' }Callers who keep the historical default (rawPrefix: false / rawSuffix: false) see no change: raw continues to mirror the user’s typed input verbatim, including any characters that numericOnly would have stripped from the display.
prefix + suffix combined
Section titled “prefix + suffix combined”The two decorations are independent — different head and tail, different modes, different raw flags:
format('12345', 'general', { blocks: [11], prefix: 'PRE-', suffix: '-END'})// => { formatted: 'PRE-12345-END', raw: '12345', type: 'general' }Backwards compatibility: tailPrefix
Section titled “Backwards compatibility: tailPrefix”The historical prefix + tailPrefix: true shape (where prefix was repurposed as a tail decoration) is preserved for existing callers. When both suffix and prefix + tailPrefix: true are provided, the new suffix wins:
// Legacy shape still worksformat('123456789', 'general', { prefix: 'USD', tailPrefix: true })// => { formatted: '123456789USD', raw: '123456789', type: 'general' }
// `suffix` wins when both are setformat('12345', 'general', { prefix: 'X', tailPrefix: true, suffix: 'OK'})// => { formatted: 'X12345OK', raw: '12345', type: 'general' }numeral
Section titled “numeral”Type:
'numeral'
Thousand separators with optional decimal scaling.
| Field | Type | Default | Notes |
|---|---|---|---|
numeralDecimalMark |
string |
'.' |
Decimal separator. |
numeralThousandsGroupStyle |
'thousand' | 'lakh' | 'wan' | 'none' |
'thousand' |
Grouping style. |
numeralIntegerScale |
number |
0 |
Max integer digits. |
numeralDecimalScale |
number |
2 |
Max decimal digits. |
stripLeadingZeroes |
boolean |
false |
Strip leading 0s. |
numeralPositiveOnly |
boolean |
false |
Disallow negative numbers. |
signBeforePrefix |
boolean |
false |
Place the - sign before the prefix. |
Type:
'date'
Raw Y-m-d (configurable) → display pattern. The package always parses the input as raw digits, then re-emits them in the display pattern order.
| Field | Type | Default | Notes |
|---|---|---|---|
datePattern |
DatePatternType (['d', 'm', 'Y'] | …) |
['d', 'm', 'Y'] |
Display pattern. |
dateRawPattern |
DatePatternType |
['Y', 'm', 'd'] |
Pattern used to derive the raw value from the formatted display. |
dateRawPatternDelimiter |
string |
'-' |
Delimiter used in the raw value. |
dateMin / dateMax |
string |
'' |
Optional bounds ('YYYY-MM-DD'). |
delimiter |
string |
'/' |
Display delimiter (use this to switch to - or .). |
Type:
'time'
Raw h:m (configurable) → display pattern.
| Field | Type | Default | Notes |
|---|---|---|---|
timePattern |
TimePatternType (['h', 'm', 's'] | …) |
['h', 'm', 's'] |
Display pattern. |
timeRawPattern |
TimePatternType |
['h', 'm'] |
Pattern used to derive the raw value from the formatted display. |
timeRawPatternDelimiter |
string |
':' |
Delimiter used in the raw value. |
timeFormat |
'12' | '24' |
'24' |
12-hour or 24-hour clock. |
delimiter |
string |
':' |
Display delimiter. |
creditCard
Section titled “creditCard”Type:
'creditCard'
Brand-aware grouping. The package detects Visa, Mastercard, Amex, Discover, JCB, Diners, UnionPay, Maestro, Mir, Elo, Hiper, and Hipercard.
| Field | Type | Default | Notes |
|---|---|---|---|
creditCardStrictMode |
boolean |
false |
Strict 19-digit padding. |
delimiter |
string |
' ' |
Group separator (set to '-' for dashes). |
creditCardType
Section titled “creditCardType”Type:
'creditCardType'
Returns the card brand name as formatted. The raw is always digits-only.
This type does not consume any extra options — pass the card number and read result.formatted to get the brand.
Type:
'phone'
Country-aware phone formatting via libphonenumber-js. The leading + is preserved in raw so international numbers keep their prefix.
| Field | Type | Default | Notes |
|---|---|---|---|
country |
string |
'MX' |
ISO 3166-1 alpha-2 country code (e.g. 'US', 'GB', 'AR'). |
delimiter |
string |
' ' |
Group separator (set to '-' for dashes, '.' for dots). |
Examples
Section titled “Examples”// US phone with dash delimiterformat('2025551234', 'phone', { country: 'US', delimiter: '-' })// => { formatted: '202-555-1234', raw: '2025551234', type: 'phone' }
// Numeral with comma decimal mark (European style)format('1234,5', 'numeral', { numeralDecimalMark: ',' })// => { formatted: '1.234,5', raw: '1234,5', type: 'numeral' }
// Date with explicit dash delimiter and Y-m-d displayformat('20260512', 'date', { datePattern: ['Y', 'm', 'd'], delimiter: '-'})// => { formatted: '2026-05-12', raw: '2026-05-12', type: 'date' }
// Numeral with prefix and tailPrefix (suffix)format('100', 'numeral', { prefix: '$', tailPrefix: true })// => { formatted: '100$', raw: '100', type: 'numeral' }
// general with auto-prepended prefix and canonical rawformat('123456789', 'general', { blocks: [13], prefix: 'EASY', rawPrefix: true})// => { formatted: 'EASY123456789', raw: 'EASY123456789', type: 'general' }
// general with head + tail decorations (independent)format('12345', 'general', { blocks: [11], prefix: 'PRE-', suffix: '-END', rawPrefix: true, rawSuffix: true})// => { formatted: 'PRE-12345-END', raw: 'PRE-12345-END', type: 'general' }For the underlying option semantics, see cleave-zen’s docs.