@secundus-studio/stift-format 0.0.0-stage → 0.4.2
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.
- package/CHANGELOG.md +50 -0
- package/LICENSE +41 -0
- package/README.md +43 -2
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +112 -0
- package/dist/index.d.ts +112 -0
- package/dist/index.js +1 -0
- package/package.json +51 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# @secundus-studio/stift-format
|
|
2
|
+
|
|
3
|
+
## 0.4.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Republish 0.4.1 as 0.4.2 with installable tarballs: the 0.4.1 artifacts carried literal `workspace:*` dependency ranges and could not be installed by any package manager. No code changes.
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @secundus-studio/stift-core@0.4.2
|
|
10
|
+
|
|
11
|
+
## 0.4.1
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [ec4a6c1]
|
|
16
|
+
- @secundus-studio/stift-core@0.4.1
|
|
17
|
+
|
|
18
|
+
## 0.4.0
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [d60faf6]
|
|
23
|
+
- @secundus-studio/stift-core@0.4.0
|
|
24
|
+
|
|
25
|
+
## 0.3.0
|
|
26
|
+
|
|
27
|
+
### Minor Changes
|
|
28
|
+
|
|
29
|
+
- 71e677d: Rename the library from l10n to Stift. Packages move to `@secundus-studio/stift-*`, the config file is `stift.config.ts`, the CLI is `stift`, and the API follows (`createStift`, `Stift`, `StiftProvider`, `StiftConfig`, `virtual:stift/runtime`). BREAKING (pre-1.0): update imports, the config filename, and pack tooling; old `.dat` packs (I18NKIT magic) are rejected by the new decoder and re-fetched, and renamed storage keys, cookie names, and devtools routes start fresh.
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- Updated dependencies [71e677d]
|
|
34
|
+
- @secundus-studio/stift-core@0.3.0
|
|
35
|
+
|
|
36
|
+
## 0.2.0
|
|
37
|
+
|
|
38
|
+
### Patch Changes
|
|
39
|
+
|
|
40
|
+
- Updated dependencies [13824b6]
|
|
41
|
+
- Updated dependencies [1770b34]
|
|
42
|
+
- @secundus-studio/l10n-core@0.2.0
|
|
43
|
+
|
|
44
|
+
## 0.1.0
|
|
45
|
+
|
|
46
|
+
### Patch Changes
|
|
47
|
+
|
|
48
|
+
- Updated dependencies [275184d]
|
|
49
|
+
- Updated dependencies [275184d]
|
|
50
|
+
- @secundus-studio/l10n-core@0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
Copyright (c) 2026 Ali Alkhateeb. All rights reserved.
|
|
2
|
+
|
|
3
|
+
SECUNDUS L10N — PROPRIETARY LICENSE
|
|
4
|
+
|
|
5
|
+
The compiled software packages distributed under the @secundus-studio scope on
|
|
6
|
+
the public npm registry (the "Software") are proprietary to Ali Alkhateeb. They
|
|
7
|
+
are licensed, not sold.
|
|
8
|
+
|
|
9
|
+
1. Grant. Subject to this license, Ali Alkhateeb grants you a non-exclusive,
|
|
10
|
+
non-transferable, royalty-free right to install and use the Software, in
|
|
11
|
+
object-code form, as a dependency of your own applications, including
|
|
12
|
+
commercial and closed-source applications.
|
|
13
|
+
|
|
14
|
+
2. Restrictions. You may not:
|
|
15
|
+
(a) copy, publish, distribute, sublicense, or sell the Software, or make
|
|
16
|
+
it available to any third party, except as embedded in your own
|
|
17
|
+
application;
|
|
18
|
+
(b) modify, translate, reverse engineer, decompile, or disassemble the
|
|
19
|
+
Software, or attempt to derive its source code;
|
|
20
|
+
(c) remove or alter any copyright, license, or proprietary notices.
|
|
21
|
+
|
|
22
|
+
3. No source code. This license grants no rights to the source code of the
|
|
23
|
+
Software. The source is confidential, is not distributed, and remains the
|
|
24
|
+
property of Ali Alkhateeb.
|
|
25
|
+
|
|
26
|
+
4. Contributions. Any feedback, bug report, or suggestion you provide may be
|
|
27
|
+
used by Ali Alkhateeb without restriction or obligation.
|
|
28
|
+
|
|
29
|
+
5. Termination. This license terminates automatically if you breach any of its
|
|
30
|
+
terms. On termination you must stop using the Software and delete all
|
|
31
|
+
copies in your possession or control.
|
|
32
|
+
|
|
33
|
+
6. Disclaimer. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
34
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
35
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
36
|
+
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
37
|
+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
38
|
+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
39
|
+
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
40
|
+
|
|
41
|
+
For licensing inquiries, contact hello@secundus.studio.
|
package/README.md
CHANGED
|
@@ -1,3 +1,44 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @secundus-studio/stift-format
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Tree-shakable `Intl` formatting helpers: numbers, currency, dates, lists, and units. No state, no React.
|
|
4
|
+
|
|
5
|
+
## What it is
|
|
6
|
+
|
|
7
|
+
The optional formatting companion to `@secundus-studio/stift-core`. Core owns messages (ICU select/plural in translations, locale state, detection); this package adds standalone, locale-aware formatting functions for everything around the message: currency amounts, dates and relative times, list joining, and unit conversion plus rendering. Every function takes `{ locale }` explicitly (pass the active locale to stay in sync), each module is standalone, and the package ships side-effect-free so bundlers drop what you don't import. Currency, unit, calendar, timezone, and numbering-system arguments are narrowed to your registered config sets when the typegen augmentation is present.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @secundus-studio/stift-format
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { formatCurrency, formatDate, formatList, formatUnit } from '@secundus-studio/stift-format'
|
|
17
|
+
|
|
18
|
+
formatCurrency(9.99, 'EUR', { locale: 'en' }) // "€9.99"
|
|
19
|
+
formatDate(new Date(), { locale: 'en', dateStyle: 'medium' }) // e.g. "Oct 9, 2026"
|
|
20
|
+
formatList(['apples', 'bread', 'cheese'], { locale: 'en' }) // "apples, bread, and cheese"
|
|
21
|
+
formatUnit(12000, { locale: 'en', unit: 'kilometer' }) // "12 km"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## API
|
|
25
|
+
|
|
26
|
+
- `formatNumber(value, options)` — locale-aware number via `Intl.NumberFormat`.
|
|
27
|
+
- `formatCurrency(value, currency, options)` — currency amount (`currency` second, style forced to `currency`).
|
|
28
|
+
- `formatPercent(value, options)` — percent (e.g. `0.42` → `"42%"`).
|
|
29
|
+
- `pluralCategory(value, locale)` — CLDR plural category (`"one"`/`"few"`/…) via `Intl.PluralRules`.
|
|
30
|
+
- `formatDate(value, options)` / `formatTime(value, options)` / `formatDateTime(value, options)` — `Date | number | string` via `Intl.DateTimeFormat`; options pass through calendar, timezone, and numbering system.
|
|
31
|
+
- `formatRelativeTime(value, unit, options)` — `"2 days ago"` via `Intl.RelativeTimeFormat`.
|
|
32
|
+
- `formatList(list, options)` — localized joining (`"a, b, and c"`) via `Intl.ListFormat`.
|
|
33
|
+
- `formatUnit(value, options)` — localized quantity; `value` is in the category base unit, target `unit` rides in options; falls back to a plain number (never throws) when the engine lacks the unit.
|
|
34
|
+
- `convertUnit(value, from, to)` — pure base-aware conversion between named units.
|
|
35
|
+
- `fromBase(value, unit)` / `toBase(value, unit)` — the two halves of a conversion, for custom pipelines.
|
|
36
|
+
- `isEngineSupportedUnit(unit)` — whether the running engine can format `unit` (guards `formatUnit`).
|
|
37
|
+
- `NumberFormatOptions`, `DateTimeFormatOptions`, `ListFormatOptions`, `UnitNumberFormatOptions` — option types (type-only).
|
|
38
|
+
|
|
39
|
+
## Docs
|
|
40
|
+
|
|
41
|
+
- Formatting reference — https://stift.secundus.studio/docs/latest/vanilla/api/format
|
|
42
|
+
- Units (vanilla) — https://stift.secundus.studio/docs/latest/vanilla/units
|
|
43
|
+
- Units (React) — https://stift.secundus.studio/docs/latest/react/units
|
|
44
|
+
- `useFormatter` hook (wraps these functions) — https://stift.secundus.studio/docs/latest/react/api/use-formatter
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
'use strict';var F=Object.defineProperty;var o=(t,e)=>F(t,"name",{value:e,configurable:true});function c(t,e,r){let n=r?{...r}:{};return e&&(n.numberingSystem=e),new Intl.NumberFormat(t,n)}o(c,"number");function T(t,e){let{locale:r,numberingSystem:n,...i}=e;return c(r,n,i).format(t)}o(T,"formatNumber");function O(t,e,r){let{locale:n,numberingSystem:i,...a}=r;return c(n,i,{style:"currency",currency:e,...a}).format(t)}o(O,"formatCurrency");function S(t,e){let{locale:r,numberingSystem:n,...i}=e;return c(r,n,{style:"percent",...i}).format(t)}o(S,"formatPercent");function D(t,e){return new Intl.PluralRules(e).select(t)}o(D,"pluralCategory");function f(t){let{locale:e,numberingSystem:r,timeZone:n,calendar:i,...a}=t,m={...a};for(let s of Object.keys(m))m[s]===void 0&&delete m[s];return r&&(m.numberingSystem=r),n&&(m.timeZone=n),i&&(m.calendar=i),new Intl.DateTimeFormat(e,m)}o(f,"dateTime");function l(t){return t instanceof Date?t:typeof t=="number"?new Date(t):new Date(t)}o(l,"toDate");function I(t,e){return f(e).format(l(t))}o(I,"formatDate");function N(t,e){let r={...e,...e.timeStyle==null?{timeStyle:"short"}:{}};return f(r).format(l(t))}o(N,"formatTime");function x(t,e){let r={...e,dateStyle:e.dateStyle??"medium",timeStyle:e.timeStyle??"short"};return f(r).format(l(t))}o(x,"formatDateTime");function R(t,e,r){let{locale:n,numeric:i,style:a}=r;return new Intl.RelativeTimeFormat(n,{...i?{numeric:i}:{},...a?{style:a}:{}}).format(t,e)}o(R,"formatRelativeTime");function L(t,e){let{locale:r,type:n,style:i}=e;return new Intl.ListFormat(r,{...n?{type:n}:{},...i?{style:i}:{}}).format(Array.from(t))}o(L,"formatList");var p={millimeter:.001,centimeter:.01,meter:1,kilometer:1e3,inch:.0254,foot:.3048,yard:.9144,mile:1609.344,"mile-scandinavian":1e4,gram:.001,kilogram:1,ounce:.028349523125,pound:.45359237,stone:6.35029318,acre:.40468564224,hectare:1,celsius:1,bit:.125,byte:1,kilobit:125,kilobyte:1e3,megabit:125e3,megabyte:1e6,gigabit:125e6,gigabyte:1e9,terabit:125e9,terabyte:1e12,petabyte:1e15};function g(t,e){if(e==="fahrenheit")return t*(9/5)+32;let r=p[e];return r!==void 0?t/r:t}o(g,"fromBase");function d(t,e){if(e==="fahrenheit")return (t-32)*(5/9);let r=p[e];return r!==void 0?t*r:t}o(d,"toBase");function h(t,e,r){return e===r?t:g(d(t,e),r)}o(h,"convertUnit");var w=new Set([...Object.keys(p),"fahrenheit"]),b=(()=>{if(!(typeof Intl>"u"||typeof Intl.supportedValuesOf!="function"))try{return new Set(Intl.supportedValuesOf("unit"))}catch{return}})();function y(t){return b?b.has(t):w.has(t)}o(y,"isEngineSupportedUnit");function U(t,e){let{locale:r,numberingSystem:n,unit:i,unitDisplay:a,...m}=e;if(!i)return new Intl.NumberFormat(r,{...m,...n?{numberingSystem:n}:{}}).format(t);let s=g(t,i),u={...m,...n?{numberingSystem:n}:{}};return y(i)&&(u.style="unit",u.unit=i,a&&(u.unitDisplay=a)),new Intl.NumberFormat(r,u).format(s)}o(U,"formatUnit");exports.convertUnit=h;exports.formatCurrency=O;exports.formatDate=I;exports.formatDateTime=x;exports.formatList=L;exports.formatNumber=T;exports.formatPercent=S;exports.formatRelativeTime=R;exports.formatTime=N;exports.formatUnit=U;exports.fromBase=g;exports.isEngineSupportedUnit=y;exports.pluralCategory=D;exports.toBase=d;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { LocaleTag, RegisteredNumberingSystems, RegisteredCurrencies, RegisteredCalendars, RegisteredTimezones, RegisteredUnit } from '@secundus-studio/stift-core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Numeric formatting helpers (docs/04-execution-plan.md Phase 3). Each is a
|
|
5
|
+
* thin, tree-shakable wrapper over `Intl.NumberFormat` with locale +
|
|
6
|
+
* numbering-system (and unit/currency) pass-through.
|
|
7
|
+
*/
|
|
8
|
+
interface NumberFormatOptions extends Intl.NumberFormatOptions {
|
|
9
|
+
/** BCP 47 locale tag of the active locale. */
|
|
10
|
+
locale: LocaleTag;
|
|
11
|
+
/** Override the numbering system (e.g. "arab", "latn") — narrowed to the
|
|
12
|
+
* app's registered set when the typegen augmentation is present. */
|
|
13
|
+
numberingSystem?: RegisteredNumberingSystems;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Format a number using the locale's CLDR rules. */
|
|
17
|
+
declare function formatNumber(value: number, options: NumberFormatOptions): string;
|
|
18
|
+
/** Format a currency amount. */
|
|
19
|
+
declare function formatCurrency(value: number, currency: RegisteredCurrencies, options: NumberFormatOptions): string;
|
|
20
|
+
/** Format a percent value (e.g. 0.42 -> "42%"). */
|
|
21
|
+
declare function formatPercent(value: number, options: NumberFormatOptions): string;
|
|
22
|
+
/** Get the CLDR plural category for a value ("one"/"few"/"many"/…). */
|
|
23
|
+
declare function pluralCategory(value: number, locale: LocaleTag): string;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Date / time formatting helpers (docs/04-execution-plan.md Phase 3), thin
|
|
27
|
+
* `Intl.DateTimeFormat` wrappers with locale + timezone + numbering-system
|
|
28
|
+
* pass-through.
|
|
29
|
+
*/
|
|
30
|
+
interface DateTimeFormatOptions extends Intl.DateTimeFormatOptions {
|
|
31
|
+
/** BCP 47 locale tag of the active locale. */
|
|
32
|
+
locale: LocaleTag;
|
|
33
|
+
/** Override the numeric value's numbering system — narrowed to the app's
|
|
34
|
+
* registered set when the typegen augmentation is present. */
|
|
35
|
+
numberingSystem?: RegisteredNumberingSystems;
|
|
36
|
+
/** Calendar to format in (e.g. "gregory", "islamic") — narrowed to the
|
|
37
|
+
* app's registered set when the typegen augmentation is present. */
|
|
38
|
+
calendar?: RegisteredCalendars;
|
|
39
|
+
/** IANA time zone identifier, e.g. "Asia/Damascus". */
|
|
40
|
+
timeZone?: RegisteredTimezones;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Format a date (uses `dateStyle`/`year`… from options). */
|
|
44
|
+
declare function formatDate(value: Date | number | string, options: DateTimeFormatOptions): string;
|
|
45
|
+
/** Format a time-of-day value. */
|
|
46
|
+
declare function formatTime(value: Date | number | string, options: DateTimeFormatOptions): string;
|
|
47
|
+
/** Format a full date+time. */
|
|
48
|
+
declare function formatDateTime(value: Date | number | string, options: DateTimeFormatOptions): string;
|
|
49
|
+
/** Relative time via `Intl.RelativeTimeFormat` ("2 days ago"). */
|
|
50
|
+
declare function formatRelativeTime(value: number, unit: Intl.RelativeTimeFormatUnit, options: {
|
|
51
|
+
locale: LocaleTag;
|
|
52
|
+
numeric?: Intl.RelativeTimeFormatNumeric;
|
|
53
|
+
style?: Intl.RelativeTimeFormatStyle;
|
|
54
|
+
}): string;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* List formatting (docs/04-execution-plan.md Phase 3) — `Intl.ListFormat`
|
|
58
|
+
* wrapper, locale-aware conjunction/disjunction.
|
|
59
|
+
*/
|
|
60
|
+
interface ListFormatOptions {
|
|
61
|
+
locale: LocaleTag;
|
|
62
|
+
type?: Intl.ListFormatType;
|
|
63
|
+
style?: Intl.ListFormatStyle;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Join a list of strings localized ("a, b, and c"). */
|
|
67
|
+
declare function formatList(list: Iterable<string>, options: ListFormatOptions): string;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Unit formatting helpers (docs/17-units.md). Tree-shakable, standalone —
|
|
71
|
+
* the sole dependency is `@secundus-studio/stift-core`'s types.
|
|
72
|
+
*
|
|
73
|
+
* Value contract: every `formatUnit`/`convertUnit` argument is expressed in
|
|
74
|
+
* the category's canonical base unit (length→meter, mass→kilogram,
|
|
75
|
+
* area→hectare, temperature→celsius, digital→byte). The helpers convert to
|
|
76
|
+
* the active `unit` and format with `Intl.NumberFormat`.
|
|
77
|
+
*
|
|
78
|
+
* Engine guard: the catalog is the ECMA-402-sanctioned set (stable across
|
|
79
|
+
* conforming engines), but an end-user engine may still lack a unit. When the
|
|
80
|
+
* active unit isn't supported by the running engine, `formatUnit` degrades to
|
|
81
|
+
* a plain number instead of throwing — the fallback is never a crash.
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
interface UnitNumberFormatOptions extends Intl.NumberFormatOptions {
|
|
85
|
+
/** BCP 47 locale tag of the active locale. */
|
|
86
|
+
locale: LocaleTag;
|
|
87
|
+
/** Override the numbering system (e.g. "arab", "latn") — narrowed to the
|
|
88
|
+
* app's registered set when the typegen augmentation is present. */
|
|
89
|
+
numberingSystem?: RegisteredNumberingSystems;
|
|
90
|
+
/** The active unit — the category's current unit (docs/17). */
|
|
91
|
+
unit?: RegisteredUnit;
|
|
92
|
+
/** How the unit is displayed. */
|
|
93
|
+
unitDisplay?: 'long' | 'short' | 'narrow';
|
|
94
|
+
}
|
|
95
|
+
/** Convert a value from its base unit into a target unit. */
|
|
96
|
+
declare function fromBase(value: number, unit: string): number;
|
|
97
|
+
/** Convert a value from a target unit into its base unit. */
|
|
98
|
+
declare function toBase(value: number, unit: string): number;
|
|
99
|
+
/** Convert `value` expressed in `from` to `to` (base-aware; both units must
|
|
100
|
+
* share a base, or math is undefined). Identity for equal units. */
|
|
101
|
+
declare function convertUnit(value: number, from: string, to: string): number;
|
|
102
|
+
/** Whether the running engine can format `unit` (guards `formatUnit`). */
|
|
103
|
+
declare function isEngineSupportedUnit(unit: string): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Format `value` (expressed in the category base unit of `unit`) as a
|
|
106
|
+
* localized quantity, e.g. `formatUnit(12000, { locale: 'en', unit: 'kilometer' })`
|
|
107
|
+
* → "12 km". Falls back to a plain localized number when the engine lacks the
|
|
108
|
+
* unit.
|
|
109
|
+
*/
|
|
110
|
+
declare function formatUnit(value: number, options: UnitNumberFormatOptions): string;
|
|
111
|
+
|
|
112
|
+
export { type DateTimeFormatOptions, type ListFormatOptions, type NumberFormatOptions, type UnitNumberFormatOptions, convertUnit, formatCurrency, formatDate, formatDateTime, formatList, formatNumber, formatPercent, formatRelativeTime, formatTime, formatUnit, fromBase, isEngineSupportedUnit, pluralCategory, toBase };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { LocaleTag, RegisteredNumberingSystems, RegisteredCurrencies, RegisteredCalendars, RegisteredTimezones, RegisteredUnit } from '@secundus-studio/stift-core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Numeric formatting helpers (docs/04-execution-plan.md Phase 3). Each is a
|
|
5
|
+
* thin, tree-shakable wrapper over `Intl.NumberFormat` with locale +
|
|
6
|
+
* numbering-system (and unit/currency) pass-through.
|
|
7
|
+
*/
|
|
8
|
+
interface NumberFormatOptions extends Intl.NumberFormatOptions {
|
|
9
|
+
/** BCP 47 locale tag of the active locale. */
|
|
10
|
+
locale: LocaleTag;
|
|
11
|
+
/** Override the numbering system (e.g. "arab", "latn") — narrowed to the
|
|
12
|
+
* app's registered set when the typegen augmentation is present. */
|
|
13
|
+
numberingSystem?: RegisteredNumberingSystems;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Format a number using the locale's CLDR rules. */
|
|
17
|
+
declare function formatNumber(value: number, options: NumberFormatOptions): string;
|
|
18
|
+
/** Format a currency amount. */
|
|
19
|
+
declare function formatCurrency(value: number, currency: RegisteredCurrencies, options: NumberFormatOptions): string;
|
|
20
|
+
/** Format a percent value (e.g. 0.42 -> "42%"). */
|
|
21
|
+
declare function formatPercent(value: number, options: NumberFormatOptions): string;
|
|
22
|
+
/** Get the CLDR plural category for a value ("one"/"few"/"many"/…). */
|
|
23
|
+
declare function pluralCategory(value: number, locale: LocaleTag): string;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Date / time formatting helpers (docs/04-execution-plan.md Phase 3), thin
|
|
27
|
+
* `Intl.DateTimeFormat` wrappers with locale + timezone + numbering-system
|
|
28
|
+
* pass-through.
|
|
29
|
+
*/
|
|
30
|
+
interface DateTimeFormatOptions extends Intl.DateTimeFormatOptions {
|
|
31
|
+
/** BCP 47 locale tag of the active locale. */
|
|
32
|
+
locale: LocaleTag;
|
|
33
|
+
/** Override the numeric value's numbering system — narrowed to the app's
|
|
34
|
+
* registered set when the typegen augmentation is present. */
|
|
35
|
+
numberingSystem?: RegisteredNumberingSystems;
|
|
36
|
+
/** Calendar to format in (e.g. "gregory", "islamic") — narrowed to the
|
|
37
|
+
* app's registered set when the typegen augmentation is present. */
|
|
38
|
+
calendar?: RegisteredCalendars;
|
|
39
|
+
/** IANA time zone identifier, e.g. "Asia/Damascus". */
|
|
40
|
+
timeZone?: RegisteredTimezones;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Format a date (uses `dateStyle`/`year`… from options). */
|
|
44
|
+
declare function formatDate(value: Date | number | string, options: DateTimeFormatOptions): string;
|
|
45
|
+
/** Format a time-of-day value. */
|
|
46
|
+
declare function formatTime(value: Date | number | string, options: DateTimeFormatOptions): string;
|
|
47
|
+
/** Format a full date+time. */
|
|
48
|
+
declare function formatDateTime(value: Date | number | string, options: DateTimeFormatOptions): string;
|
|
49
|
+
/** Relative time via `Intl.RelativeTimeFormat` ("2 days ago"). */
|
|
50
|
+
declare function formatRelativeTime(value: number, unit: Intl.RelativeTimeFormatUnit, options: {
|
|
51
|
+
locale: LocaleTag;
|
|
52
|
+
numeric?: Intl.RelativeTimeFormatNumeric;
|
|
53
|
+
style?: Intl.RelativeTimeFormatStyle;
|
|
54
|
+
}): string;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* List formatting (docs/04-execution-plan.md Phase 3) — `Intl.ListFormat`
|
|
58
|
+
* wrapper, locale-aware conjunction/disjunction.
|
|
59
|
+
*/
|
|
60
|
+
interface ListFormatOptions {
|
|
61
|
+
locale: LocaleTag;
|
|
62
|
+
type?: Intl.ListFormatType;
|
|
63
|
+
style?: Intl.ListFormatStyle;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Join a list of strings localized ("a, b, and c"). */
|
|
67
|
+
declare function formatList(list: Iterable<string>, options: ListFormatOptions): string;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Unit formatting helpers (docs/17-units.md). Tree-shakable, standalone —
|
|
71
|
+
* the sole dependency is `@secundus-studio/stift-core`'s types.
|
|
72
|
+
*
|
|
73
|
+
* Value contract: every `formatUnit`/`convertUnit` argument is expressed in
|
|
74
|
+
* the category's canonical base unit (length→meter, mass→kilogram,
|
|
75
|
+
* area→hectare, temperature→celsius, digital→byte). The helpers convert to
|
|
76
|
+
* the active `unit` and format with `Intl.NumberFormat`.
|
|
77
|
+
*
|
|
78
|
+
* Engine guard: the catalog is the ECMA-402-sanctioned set (stable across
|
|
79
|
+
* conforming engines), but an end-user engine may still lack a unit. When the
|
|
80
|
+
* active unit isn't supported by the running engine, `formatUnit` degrades to
|
|
81
|
+
* a plain number instead of throwing — the fallback is never a crash.
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
interface UnitNumberFormatOptions extends Intl.NumberFormatOptions {
|
|
85
|
+
/** BCP 47 locale tag of the active locale. */
|
|
86
|
+
locale: LocaleTag;
|
|
87
|
+
/** Override the numbering system (e.g. "arab", "latn") — narrowed to the
|
|
88
|
+
* app's registered set when the typegen augmentation is present. */
|
|
89
|
+
numberingSystem?: RegisteredNumberingSystems;
|
|
90
|
+
/** The active unit — the category's current unit (docs/17). */
|
|
91
|
+
unit?: RegisteredUnit;
|
|
92
|
+
/** How the unit is displayed. */
|
|
93
|
+
unitDisplay?: 'long' | 'short' | 'narrow';
|
|
94
|
+
}
|
|
95
|
+
/** Convert a value from its base unit into a target unit. */
|
|
96
|
+
declare function fromBase(value: number, unit: string): number;
|
|
97
|
+
/** Convert a value from a target unit into its base unit. */
|
|
98
|
+
declare function toBase(value: number, unit: string): number;
|
|
99
|
+
/** Convert `value` expressed in `from` to `to` (base-aware; both units must
|
|
100
|
+
* share a base, or math is undefined). Identity for equal units. */
|
|
101
|
+
declare function convertUnit(value: number, from: string, to: string): number;
|
|
102
|
+
/** Whether the running engine can format `unit` (guards `formatUnit`). */
|
|
103
|
+
declare function isEngineSupportedUnit(unit: string): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Format `value` (expressed in the category base unit of `unit`) as a
|
|
106
|
+
* localized quantity, e.g. `formatUnit(12000, { locale: 'en', unit: 'kilometer' })`
|
|
107
|
+
* → "12 km". Falls back to a plain localized number when the engine lacks the
|
|
108
|
+
* unit.
|
|
109
|
+
*/
|
|
110
|
+
declare function formatUnit(value: number, options: UnitNumberFormatOptions): string;
|
|
111
|
+
|
|
112
|
+
export { type DateTimeFormatOptions, type ListFormatOptions, type NumberFormatOptions, type UnitNumberFormatOptions, convertUnit, formatCurrency, formatDate, formatDateTime, formatList, formatNumber, formatPercent, formatRelativeTime, formatTime, formatUnit, fromBase, isEngineSupportedUnit, pluralCategory, toBase };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var F=Object.defineProperty;var o=(t,e)=>F(t,"name",{value:e,configurable:true});function c(t,e,r){let n=r?{...r}:{};return e&&(n.numberingSystem=e),new Intl.NumberFormat(t,n)}o(c,"number");function T(t,e){let{locale:r,numberingSystem:n,...i}=e;return c(r,n,i).format(t)}o(T,"formatNumber");function O(t,e,r){let{locale:n,numberingSystem:i,...a}=r;return c(n,i,{style:"currency",currency:e,...a}).format(t)}o(O,"formatCurrency");function S(t,e){let{locale:r,numberingSystem:n,...i}=e;return c(r,n,{style:"percent",...i}).format(t)}o(S,"formatPercent");function D(t,e){return new Intl.PluralRules(e).select(t)}o(D,"pluralCategory");function f(t){let{locale:e,numberingSystem:r,timeZone:n,calendar:i,...a}=t,m={...a};for(let s of Object.keys(m))m[s]===void 0&&delete m[s];return r&&(m.numberingSystem=r),n&&(m.timeZone=n),i&&(m.calendar=i),new Intl.DateTimeFormat(e,m)}o(f,"dateTime");function l(t){return t instanceof Date?t:typeof t=="number"?new Date(t):new Date(t)}o(l,"toDate");function I(t,e){return f(e).format(l(t))}o(I,"formatDate");function N(t,e){let r={...e,...e.timeStyle==null?{timeStyle:"short"}:{}};return f(r).format(l(t))}o(N,"formatTime");function x(t,e){let r={...e,dateStyle:e.dateStyle??"medium",timeStyle:e.timeStyle??"short"};return f(r).format(l(t))}o(x,"formatDateTime");function R(t,e,r){let{locale:n,numeric:i,style:a}=r;return new Intl.RelativeTimeFormat(n,{...i?{numeric:i}:{},...a?{style:a}:{}}).format(t,e)}o(R,"formatRelativeTime");function L(t,e){let{locale:r,type:n,style:i}=e;return new Intl.ListFormat(r,{...n?{type:n}:{},...i?{style:i}:{}}).format(Array.from(t))}o(L,"formatList");var p={millimeter:.001,centimeter:.01,meter:1,kilometer:1e3,inch:.0254,foot:.3048,yard:.9144,mile:1609.344,"mile-scandinavian":1e4,gram:.001,kilogram:1,ounce:.028349523125,pound:.45359237,stone:6.35029318,acre:.40468564224,hectare:1,celsius:1,bit:.125,byte:1,kilobit:125,kilobyte:1e3,megabit:125e3,megabyte:1e6,gigabit:125e6,gigabyte:1e9,terabit:125e9,terabyte:1e12,petabyte:1e15};function g(t,e){if(e==="fahrenheit")return t*(9/5)+32;let r=p[e];return r!==void 0?t/r:t}o(g,"fromBase");function d(t,e){if(e==="fahrenheit")return (t-32)*(5/9);let r=p[e];return r!==void 0?t*r:t}o(d,"toBase");function h(t,e,r){return e===r?t:g(d(t,e),r)}o(h,"convertUnit");var w=new Set([...Object.keys(p),"fahrenheit"]),b=(()=>{if(!(typeof Intl>"u"||typeof Intl.supportedValuesOf!="function"))try{return new Set(Intl.supportedValuesOf("unit"))}catch{return}})();function y(t){return b?b.has(t):w.has(t)}o(y,"isEngineSupportedUnit");function U(t,e){let{locale:r,numberingSystem:n,unit:i,unitDisplay:a,...m}=e;if(!i)return new Intl.NumberFormat(r,{...m,...n?{numberingSystem:n}:{}}).format(t);let s=g(t,i),u={...m,...n?{numberingSystem:n}:{}};return y(i)&&(u.style="unit",u.unit=i,a&&(u.unitDisplay=a)),new Intl.NumberFormat(r,u).format(s)}o(U,"formatUnit");export{h as convertUnit,O as formatCurrency,I as formatDate,x as formatDateTime,L as formatList,T as formatNumber,S as formatPercent,R as formatRelativeTime,N as formatTime,U as formatUnit,g as fromBase,y as isEngineSupportedUnit,D as pluralCategory,d as toBase};
|
package/package.json
CHANGED
|
@@ -1,6 +1,54 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@secundus-studio/stift-format",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
3
|
+
"version": "0.4.2",
|
|
4
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
5
|
+
"description": "Optional tree-shakable formatting: currency, units, ordinals, collation, calendars, segmentation, rich text, input parsing.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"stift",
|
|
8
|
+
"format",
|
|
9
|
+
"tanstack-intent"
|
|
10
|
+
],
|
|
11
|
+
"sideEffects": false,
|
|
12
|
+
"type": "module",
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=20"
|
|
15
|
+
},
|
|
16
|
+
"main": "./dist/index.cjs",
|
|
17
|
+
"module": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"import": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"default": "./dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
"require": {
|
|
26
|
+
"types": "./dist/index.d.cts",
|
|
27
|
+
"default": "./dist/index.cjs"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"CHANGELOG.md"
|
|
35
|
+
],
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@secundus-studio/stift-core": "0.4.2"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"tsup": "^8.5.1",
|
|
44
|
+
"typescript": "^5.7.2",
|
|
45
|
+
"vitest": "^4.1.10"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://stift.secundus.studio",
|
|
48
|
+
"author": "Ali Alkhateeb",
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsup",
|
|
51
|
+
"test": "vitest run --passWithNoTests",
|
|
52
|
+
"typecheck": "tsc --noEmit"
|
|
53
|
+
}
|
|
6
54
|
}
|