intl-formatter 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,471 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createFormatter = createFormatter;
4
+ const DEFAULT_RULES = {
5
+ compactThreshold: 10000,
6
+ minimumFractionDigits: 0,
7
+ maximumFractionDigits: 2,
8
+ currencyDisplay: "narrowSymbol",
9
+ numberFormat: {},
10
+ currencyFormat: {},
11
+ relativeTimeFormat: { style: "long", numeric: "auto" },
12
+ dateFormat: { year: "numeric", month: "short", day: "2-digit" },
13
+ dateTimeFormat: {
14
+ year: "numeric",
15
+ month: "short",
16
+ day: "2-digit",
17
+ hour: "2-digit",
18
+ minute: "2-digit",
19
+ hour12: true,
20
+ },
21
+ fallback: "—",
22
+ numberFallback: "",
23
+ currencyFallback: "",
24
+ percentageFallback: "",
25
+ durationFallback: "",
26
+ dateFallback: "",
27
+ dateTimeFallback: "",
28
+ relativeTimeFallback: "",
29
+ };
30
+ const DEFAULT_DURATION_LABELS = {
31
+ h: "h",
32
+ m: "m",
33
+ s: "s",
34
+ hour: { one: "hour", other: "hours" },
35
+ minute: { one: "minute", other: "minutes" },
36
+ second: { one: "second", other: "seconds" }
37
+ };
38
+ // ─── Module-Level High Performance Global Caches ──────────────────────────────
39
+ const MAX_CACHE_SIZE = 1000;
40
+ const numberFormatters = new Map();
41
+ const dateTimeFormatters = new Map();
42
+ const relativeTimeFormatters = new Map();
43
+ const pluralRulesFormatters = new Map();
44
+ function serializeOptions(locale, opts) {
45
+ const keys = Object.keys(opts);
46
+ if (keys.length === 0)
47
+ return locale;
48
+ keys.sort();
49
+ let parts = [locale];
50
+ for (let i = 0; i < keys.length; i++) {
51
+ const k = keys[i];
52
+ const v = opts[k];
53
+ if (v === undefined)
54
+ continue;
55
+ // escape backslashes, pipes, commas, and colons to prevent cache key collisions
56
+ const safeK = k.replace(/[\\|:,]/g, '\\$&');
57
+ const safeV = String(v).replace(/[\\|:,]/g, '\\$&');
58
+ parts.push(`${safeK}:${safeV}`);
59
+ }
60
+ return parts.join("|");
61
+ }
62
+ function getNumberFormatter(locale, options) {
63
+ const key = serializeOptions(locale, options);
64
+ if (numberFormatters.has(key)) {
65
+ const entry = numberFormatters.get(key);
66
+ numberFormatters.delete(key);
67
+ numberFormatters.set(key, entry);
68
+ return entry;
69
+ }
70
+ const formatter = new Intl.NumberFormat(locale, options);
71
+ if (numberFormatters.size >= MAX_CACHE_SIZE) {
72
+ const oldestKey = numberFormatters.keys().next().value;
73
+ if (oldestKey !== undefined)
74
+ numberFormatters.delete(oldestKey);
75
+ }
76
+ numberFormatters.set(key, formatter);
77
+ return formatter;
78
+ }
79
+ function getDateTimeFormatter(locale, options) {
80
+ const key = serializeOptions(locale, options);
81
+ if (dateTimeFormatters.has(key)) {
82
+ const entry = dateTimeFormatters.get(key);
83
+ dateTimeFormatters.delete(key);
84
+ dateTimeFormatters.set(key, entry);
85
+ return entry;
86
+ }
87
+ const formatter = new Intl.DateTimeFormat(locale, options);
88
+ if (dateTimeFormatters.size >= MAX_CACHE_SIZE) {
89
+ const oldestKey = dateTimeFormatters.keys().next().value;
90
+ if (oldestKey !== undefined)
91
+ dateTimeFormatters.delete(oldestKey);
92
+ }
93
+ dateTimeFormatters.set(key, formatter);
94
+ return formatter;
95
+ }
96
+ function getRelativeTimeFormatter(locale, options) {
97
+ const key = serializeOptions(locale, options);
98
+ if (relativeTimeFormatters.has(key)) {
99
+ const entry = relativeTimeFormatters.get(key);
100
+ relativeTimeFormatters.delete(key);
101
+ relativeTimeFormatters.set(key, entry);
102
+ return entry;
103
+ }
104
+ const formatter = new Intl.RelativeTimeFormat(locale, options);
105
+ if (relativeTimeFormatters.size >= MAX_CACHE_SIZE) {
106
+ const oldestKey = relativeTimeFormatters.keys().next().value;
107
+ if (oldestKey !== undefined)
108
+ relativeTimeFormatters.delete(oldestKey);
109
+ }
110
+ relativeTimeFormatters.set(key, formatter);
111
+ return formatter;
112
+ }
113
+ function getPluralRulesFormatter(locale, options) {
114
+ const key = serializeOptions(locale, options);
115
+ if (pluralRulesFormatters.has(key)) {
116
+ const entry = pluralRulesFormatters.get(key);
117
+ pluralRulesFormatters.delete(key);
118
+ pluralRulesFormatters.set(key, entry);
119
+ return entry;
120
+ }
121
+ const rulesInstance = new Intl.PluralRules(locale, options);
122
+ if (pluralRulesFormatters.size >= MAX_CACHE_SIZE) {
123
+ const oldestKey = pluralRulesFormatters.keys().next().value;
124
+ if (oldestKey !== undefined)
125
+ pluralRulesFormatters.delete(oldestKey);
126
+ }
127
+ pluralRulesFormatters.set(key, rulesInstance);
128
+ return rulesInstance;
129
+ }
130
+ function createFormatter(config = {}) {
131
+ const locale = config.locale ?? "en-US";
132
+ let defaultCurrency = config.currency ?? "USD";
133
+ if (typeof defaultCurrency === "string") {
134
+ defaultCurrency = defaultCurrency.toUpperCase();
135
+ if (!/^[A-Z]{3}$/.test(defaultCurrency)) {
136
+ console.warn(`[intl-formatter] Invalid currency code "${defaultCurrency}". Falling back to "USD".`);
137
+ defaultCurrency = "USD";
138
+ }
139
+ }
140
+ const fallback = config.fallback ?? "—";
141
+ const rules = {
142
+ ...DEFAULT_RULES,
143
+ ...config.rules,
144
+ numberFormat: { ...DEFAULT_RULES.numberFormat, ...config.rules?.numberFormat },
145
+ currencyFormat: { ...DEFAULT_RULES.currencyFormat, ...config.rules?.currencyFormat },
146
+ relativeTimeFormat: { ...DEFAULT_RULES.relativeTimeFormat, ...config.rules?.relativeTimeFormat },
147
+ dateFormat: { ...DEFAULT_RULES.dateFormat, ...config.rules?.dateFormat },
148
+ dateTimeFormat: { ...DEFAULT_RULES.dateTimeFormat, ...config.rules?.dateTimeFormat },
149
+ };
150
+ // Specialized Fallbacks Resolution
151
+ const defaultNumberFallback = rules.numberFallback || rules.fallback || fallback;
152
+ const defaultCurrencyFallback = rules.currencyFallback || rules.fallback || fallback;
153
+ const defaultPercentageFallback = rules.percentageFallback || rules.fallback || fallback;
154
+ const defaultDurationFallback = rules.durationFallback || rules.fallback || fallback;
155
+ const defaultDateFallback = rules.dateFallback || rules.fallback || fallback;
156
+ const defaultDateTimeFallback = rules.dateTimeFallback || rules.fallback || fallback;
157
+ const defaultRelativeTimeFallback = rules.relativeTimeFallback || rules.fallback || fallback;
158
+ function toNumber(value) {
159
+ if (value == null || value === "")
160
+ return null;
161
+ try {
162
+ if (typeof value === "boolean")
163
+ return null;
164
+ if (Array.isArray(value))
165
+ return null;
166
+ const n = typeof value === "number" ? value : Number(value);
167
+ return Number.isNaN(n) ? null : n;
168
+ }
169
+ catch {
170
+ return null;
171
+ }
172
+ }
173
+ function toValidDate(value) {
174
+ if (value == null || value === "")
175
+ return null;
176
+ try {
177
+ if (value instanceof Date) {
178
+ return Number.isNaN(value.getTime()) ? null : value;
179
+ }
180
+ if (typeof value === "number") {
181
+ const d = new Date(value);
182
+ return Number.isNaN(d.getTime()) ? null : d;
183
+ }
184
+ if (typeof value === "string") {
185
+ const isDigitsOnly = /^-?\d+$/.test(value);
186
+ const isCalendarDate = /^(?:19|20)\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])(?:[01]\d|2[0-3])?$/.test(value);
187
+ if (isDigitsOnly && value.length === 10 && !isCalendarDate) {
188
+ // Second UNIX timestamp: convert to milliseconds
189
+ const parsedValue = Number(value) * 1000;
190
+ const d = new Date(parsedValue);
191
+ return Number.isNaN(d.getTime()) ? null : d;
192
+ }
193
+ if (isDigitsOnly && value.length >= 12) {
194
+ // Millisecond UNIX timestamp
195
+ const parsedValue = Number(value);
196
+ const d = new Date(parsedValue);
197
+ return Number.isNaN(d.getTime()) ? null : d;
198
+ }
199
+ const d = new Date(value);
200
+ return Number.isNaN(d.getTime()) ? null : d;
201
+ }
202
+ return null;
203
+ }
204
+ catch {
205
+ return null;
206
+ }
207
+ }
208
+ function isLarge(n) {
209
+ return Math.abs(n) >= rules.compactThreshold;
210
+ }
211
+ function normalizeICU(str, localeStr) {
212
+ // Normalize both narrow no-break space and standard non-breaking space to regular space
213
+ let normalized = str.replace(/[\u202f\u00a0]/g, " ").trim();
214
+ // Only apply English-style compact suffix normalizer if locale is English to avoid breaking other languages
215
+ if (localeStr.startsWith("en")) {
216
+ normalized = normalized.replace(/(\d)\s*([kKmMbBtT])\b/g, (match, p1, p2) => p1 + p2.toUpperCase());
217
+ }
218
+ return normalized;
219
+ }
220
+ function safeFormat(formatterFn, fallbackValue) {
221
+ try {
222
+ return formatterFn();
223
+ }
224
+ catch (err) {
225
+ console.error(`[intl-formatter] Formatting failed. Returning fallback. Error:`, err);
226
+ return fallbackValue;
227
+ }
228
+ }
229
+ function resolvePluralLabel(v, form, localeStr) {
230
+ if (typeof form === "string") {
231
+ // Singular/plural backwards fallback
232
+ return v === 1 ? (form === "hours" ? "hour" : form === "minutes" ? "minute" : form === "seconds" ? "second" : form) : form;
233
+ }
234
+ // Backward compatibility for legacy option keys { singular, plural }
235
+ const legacyForm = form;
236
+ if (legacyForm.singular !== undefined || legacyForm.plural !== undefined) {
237
+ return v === 1 ? (legacyForm.singular ?? legacyForm.other) : (legacyForm.plural ?? legacyForm.other);
238
+ }
239
+ const rulesInstance = getPluralRulesFormatter(localeStr, { type: "cardinal" });
240
+ const category = rulesInstance.select(v);
241
+ return form[category] ?? form.other;
242
+ }
243
+ const formatters = {
244
+ number(value, options = {}) {
245
+ const n = toNumber(value);
246
+ const callFallback = options.fallback ?? defaultNumberFallback;
247
+ if (n == null)
248
+ return callFallback;
249
+ return safeFormat(() => {
250
+ const { fallback: _, ...rest } = options;
251
+ const resolvedOptions = {
252
+ style: "decimal",
253
+ notation: isLarge(n) ? "compact" : "standard",
254
+ minimumFractionDigits: rules.minimumFractionDigits,
255
+ maximumFractionDigits: rules.maximumFractionDigits,
256
+ ...rules.numberFormat,
257
+ ...rest,
258
+ };
259
+ return normalizeICU(getNumberFormatter(locale, resolvedOptions).format(n), locale);
260
+ }, callFallback);
261
+ },
262
+ currency(value, options = {}) {
263
+ const n = toNumber(value);
264
+ const callFallback = options.fallback ?? defaultCurrencyFallback;
265
+ if (n == null)
266
+ return callFallback;
267
+ return safeFormat(() => {
268
+ const { currency: overrideCurrency, currencyDisplay, fallback: _, ...rest } = options;
269
+ let finalCurrency = overrideCurrency ?? defaultCurrency;
270
+ if (typeof finalCurrency === "string") {
271
+ finalCurrency = finalCurrency.toUpperCase();
272
+ if (!/^[A-Z]{3}$/.test(finalCurrency)) {
273
+ console.warn(`[intl-formatter] Invalid override currency code "${finalCurrency}". Falling back to default: "${defaultCurrency}".`);
274
+ finalCurrency = defaultCurrency;
275
+ }
276
+ }
277
+ const resolvedOptions = {
278
+ style: "currency",
279
+ currency: finalCurrency,
280
+ notation: isLarge(n) ? "compact" : "standard",
281
+ minimumFractionDigits: rules.minimumFractionDigits,
282
+ maximumFractionDigits: rules.maximumFractionDigits,
283
+ currencyDisplay: currencyDisplay ?? rules.currencyDisplay,
284
+ ...rules.currencyFormat,
285
+ ...rest,
286
+ };
287
+ return normalizeICU(getNumberFormatter(locale, resolvedOptions).format(n), locale);
288
+ }, callFallback);
289
+ },
290
+ percentage(value, options = {}) {
291
+ const n = toNumber(value);
292
+ const callFallback = options.fallback ?? defaultPercentageFallback;
293
+ if (n == null)
294
+ return callFallback;
295
+ return safeFormat(() => {
296
+ const { inputMode, fallback: _, ...intlOptions } = options;
297
+ const normalized = inputMode === "fraction" ? n : n / 100;
298
+ const checkMaxFraction = intlOptions.maximumFractionDigits ?? rules.maximumFractionDigits;
299
+ const wouldBeZero = Math.abs(normalized) > 0 && parseFloat(Math.abs(normalized).toFixed(checkMaxFraction + 2)) === 0;
300
+ const sigDigits = Math.max(checkMaxFraction, 4);
301
+ const resolvedOptions = {
302
+ style: "percent",
303
+ ...(wouldBeZero
304
+ ? { maximumSignificantDigits: sigDigits }
305
+ : {
306
+ minimumFractionDigits: rules.minimumFractionDigits,
307
+ maximumFractionDigits: rules.maximumFractionDigits,
308
+ }),
309
+ ...intlOptions,
310
+ };
311
+ return normalizeICU(getNumberFormatter(locale, resolvedOptions).format(normalized), locale);
312
+ }, callFallback);
313
+ },
314
+ duration(value, options = {}) {
315
+ const n = toNumber(value);
316
+ const callFallback = options.fallback ?? defaultDurationFallback;
317
+ if (n == null)
318
+ return callFallback;
319
+ return safeFormat(() => {
320
+ const absN = Math.abs(n);
321
+ const fracDigits = options.fractionalDigits ?? 0;
322
+ const roundedAbsN = fracDigits > 0
323
+ ? Math.round(absN * Math.pow(10, fracDigits)) / Math.pow(10, fracDigits)
324
+ : Math.round(absN);
325
+ const totalSec = Math.floor(roundedAbsN);
326
+ const h = Math.floor(totalSec / 3600);
327
+ const m = Math.floor((totalSec % 3600) / 60);
328
+ let s;
329
+ let sVal;
330
+ if (fracDigits > 0) {
331
+ sVal = roundedAbsN % 60;
332
+ s = sVal.toFixed(fracDigits);
333
+ }
334
+ else {
335
+ sVal = totalSec % 60;
336
+ s = String(sVal);
337
+ }
338
+ const sign = n < 0 && absN >= 0.0001 ? "-" : "";
339
+ const userHour = options.labels?.hour;
340
+ const resolvedHour = typeof userHour === "string"
341
+ ? userHour
342
+ : { ...DEFAULT_DURATION_LABELS.hour, ...userHour };
343
+ const userMinute = options.labels?.minute;
344
+ const resolvedMinute = typeof userMinute === "string"
345
+ ? userMinute
346
+ : { ...DEFAULT_DURATION_LABELS.minute, ...userMinute };
347
+ const userSecond = options.labels?.second;
348
+ const resolvedSecond = typeof userSecond === "string"
349
+ ? userSecond
350
+ : { ...DEFAULT_DURATION_LABELS.second, ...userSecond };
351
+ const labels = {
352
+ h: options.labels?.h ?? DEFAULT_DURATION_LABELS.h,
353
+ m: options.labels?.m ?? DEFAULT_DURATION_LABELS.m,
354
+ s: options.labels?.s ?? DEFAULT_DURATION_LABELS.s,
355
+ hour: resolvedHour,
356
+ minute: resolvedMinute,
357
+ second: resolvedSecond,
358
+ };
359
+ if (options.format === "verbose") {
360
+ const parts = [];
361
+ if (h)
362
+ parts.push(`${h} ${resolvePluralLabel(h, labels.hour, locale)}`);
363
+ if (m)
364
+ parts.push(`${m} ${resolvePluralLabel(m, labels.minute, locale)}`);
365
+ if (sVal > 0 || fracDigits > 0 || !parts.length) {
366
+ parts.push(`${s} ${resolvePluralLabel(sVal, labels.second, locale)}`);
367
+ }
368
+ return sign + parts.join(", ");
369
+ }
370
+ if (h > 0)
371
+ return `${sign}${h}${labels.h} ${m}${labels.m}`;
372
+ if (m > 0)
373
+ return `${sign}${m}${labels.m} ${s}${labels.s}`;
374
+ return `${sign}${s}${labels.s}`;
375
+ }, callFallback);
376
+ },
377
+ date(value, options = {}) {
378
+ const date = toValidDate(value);
379
+ const callFallback = options.fallback ?? defaultDateFallback;
380
+ if (!date)
381
+ return callFallback;
382
+ return safeFormat(() => {
383
+ const { fallback: _, ...rest } = options;
384
+ const resolvedOptions = rest.dateStyle
385
+ ? rest
386
+ : { ...rules.dateFormat, ...rest };
387
+ return normalizeICU(getDateTimeFormatter(locale, resolvedOptions).format(date), locale);
388
+ }, callFallback);
389
+ },
390
+ dateTime(value, options = {}) {
391
+ const date = toValidDate(value);
392
+ const callFallback = options.fallback ?? defaultDateTimeFallback;
393
+ if (!date)
394
+ return callFallback;
395
+ return safeFormat(() => {
396
+ const { fallback: _, ...rest } = options;
397
+ const resolvedOptions = rest.dateStyle || rest.timeStyle
398
+ ? rest
399
+ : { ...rules.dateTimeFormat, ...rest };
400
+ return normalizeICU(getDateTimeFormatter(locale, resolvedOptions).format(date), locale);
401
+ }, callFallback);
402
+ },
403
+ relativeTime(value, optionsOrNow) {
404
+ const date = toValidDate(value);
405
+ let opts = {};
406
+ let now = Date.now();
407
+ if (typeof optionsOrNow === "number") {
408
+ now = optionsOrNow;
409
+ }
410
+ else if (optionsOrNow && typeof optionsOrNow === "object") {
411
+ const { now: overrideNow, ...rest } = optionsOrNow;
412
+ opts = rest;
413
+ if (overrideNow !== undefined) {
414
+ now = overrideNow;
415
+ }
416
+ }
417
+ const callFallback = opts.fallback ?? defaultRelativeTimeFallback;
418
+ if (!date)
419
+ return callFallback;
420
+ return safeFormat(() => {
421
+ const diffSec = Math.round((date.getTime() - now) / 1000);
422
+ const abs = Math.abs(diffSec);
423
+ const resolvedOptions = {
424
+ style: opts.style ?? rules.relativeTimeFormat?.style ?? "long",
425
+ numeric: opts.numeric ?? rules.relativeTimeFormat?.numeric ?? "auto",
426
+ };
427
+ const rtfInstance = getRelativeTimeFormatter(locale, resolvedOptions);
428
+ let formatted;
429
+ if (abs < 60) {
430
+ formatted = rtfInstance.format(diffSec, "second");
431
+ }
432
+ else {
433
+ const min = Math.round(diffSec / 60);
434
+ if (Math.abs(min) < 60) {
435
+ formatted = rtfInstance.format(min, "minute");
436
+ }
437
+ else {
438
+ const hr = Math.round(min / 60);
439
+ if (Math.abs(hr) < 24) {
440
+ formatted = rtfInstance.format(hr, "hour");
441
+ }
442
+ else {
443
+ const day = Math.round(hr / 24);
444
+ if (Math.abs(day) < 7) {
445
+ formatted = rtfInstance.format(day, "day");
446
+ }
447
+ else {
448
+ const week = Math.round(day / 7);
449
+ if (Math.abs(week) < 4) {
450
+ formatted = rtfInstance.format(week, "week");
451
+ }
452
+ else {
453
+ const month = Math.round(day / 30);
454
+ if (Math.abs(month) < 12) {
455
+ formatted = rtfInstance.format(month, "month");
456
+ }
457
+ else {
458
+ const year = Math.round(day / 365);
459
+ formatted = rtfInstance.format(year, "year");
460
+ }
461
+ }
462
+ }
463
+ }
464
+ }
465
+ }
466
+ return normalizeICU(formatted, locale);
467
+ }, callFallback);
468
+ },
469
+ };
470
+ return Object.freeze(formatters);
471
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createFormatter = void 0;
4
+ var formatter_js_1 = require("./core/formatter.js");
5
+ Object.defineProperty(exports, "createFormatter", { enumerable: true, get: function () { return formatter_js_1.createFormatter; } });
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mockFormatter = void 0;
4
+ const toMockDateStr = (v, fallback = "—") => {
5
+ if (v == null)
6
+ return fallback;
7
+ try {
8
+ if (typeof v === "boolean" || Array.isArray(v))
9
+ return fallback;
10
+ const isUnixTimestamp = typeof v === "string" && /^-?\d+$/.test(v) && v.length >= 12;
11
+ const parsedValue = isUnixTimestamp ? Number(v) : v;
12
+ const d = new Date(parsedValue);
13
+ return Number.isNaN(d.getTime()) ? fallback : d.toISOString();
14
+ }
15
+ catch {
16
+ return fallback;
17
+ }
18
+ };
19
+ exports.mockFormatter = Object.freeze({
20
+ number: (v, options) => {
21
+ if (v == null || v === "" || typeof v === "boolean" || Array.isArray(v)) {
22
+ return options?.fallback ?? "—";
23
+ }
24
+ return String(v);
25
+ },
26
+ currency: (v, options) => {
27
+ if (v == null || v === "" || typeof v === "boolean" || Array.isArray(v)) {
28
+ return options?.fallback ?? "—";
29
+ }
30
+ return `$${v}`;
31
+ },
32
+ percentage: (v, options) => {
33
+ if (v == null || v === "" || typeof v === "boolean" || Array.isArray(v)) {
34
+ return options?.fallback ?? "—";
35
+ }
36
+ return `${v}%`;
37
+ },
38
+ duration: (v, options) => {
39
+ if (v == null || v === "" || typeof v === "boolean" || Array.isArray(v)) {
40
+ return options?.fallback ?? "—";
41
+ }
42
+ return `${v}s`;
43
+ },
44
+ date: (v, options) => {
45
+ const s = toMockDateStr(v, options?.fallback ?? "—");
46
+ return s === (options?.fallback ?? "—") ? s : (s.split("T")[0] ?? "—");
47
+ },
48
+ dateTime: (v, options) => {
49
+ return toMockDateStr(v, options?.fallback ?? "—");
50
+ },
51
+ relativeTime: (v, optionsOrNow) => {
52
+ let callFallback = "—";
53
+ if (optionsOrNow && typeof optionsOrNow === "object") {
54
+ callFallback = optionsOrNow.fallback ?? "—";
55
+ }
56
+ if (v == null || v === "") {
57
+ return callFallback;
58
+ }
59
+ return "just now";
60
+ },
61
+ });
@@ -0,0 +1,3 @@
1
+ import type { FormatterConfig, Formatter } from "./types.js";
2
+ export declare function createFormatter(config?: FormatterConfig): Formatter;
3
+ //# sourceMappingURL=formatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../../src/core/formatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EAEf,SAAS,EAYV,MAAM,YAAY,CAAC;AAmIpB,wBAAgB,eAAe,CAAC,MAAM,GAAE,eAAoB,GAAG,SAAS,CAqXvE"}