@songjin-ai/sttool
v1.0.1
Published
TypeScript utility library with high-precision calculation and date formatting
Maintainers
Readme
sttool
TypeScript utility library with high-precision calculation, date formatting, and tree structure conversion.
Install
npm install @songjin-ai/sttoolModules
- Decimal — High-precision decimal arithmetic
- DateTime — Chainable date manipulation
- Tree — List ↔ Tree structure conversion
- Object — Object property and structure utilities
Decimal
High-precision decimal arithmetic using string-based calculation. Solves IEEE 754 floating-point precision issues like 0.1 + 0.2 !== 0.3.
Import
import { Decimal } from '@songjin-ai/sttool';Static Methods
| Method | Description |
|--------|-------------|
| Decimal.add(a, b) | Add two numbers |
| Decimal.subtract(a, b) | Subtract b from a |
| Decimal.multiply(a, b) | Multiply two numbers |
| Decimal.divide(a, b, precision?) | Divide a by b (default precision: 20) |
| Decimal.mod(a, b) | Modulo operation |
Instance Methods
| Method | Description |
|--------|-------------|
| .add(other) | Add to current value |
| .subtract(other) | Subtract from current value |
| .multiply(other) | Multiply by other value |
| .divide(other, precision?) | Divide by other value |
| .mod(other) | Modulo operation |
| .negate() | Negate the value |
| .abs() | Absolute value |
| .equals(other) | Check equality |
| .compareTo(other) | Compare (-1, 0, 1) |
| .toNumber() | Convert to JavaScript number |
| .toString() | Convert to string |
| .toFixed(decimals) | Format with fixed decimals |
Examples
// Precision arithmetic
const a = Decimal.add('0.1', '0.2');
const b = Decimal.from('0.3');
a.equals(b); // true (unlike JavaScript: 0.1 + 0.2 !== 0.3)
// Chaining
const result = Decimal.from('10')
.subtract('3.5')
.multiply('2')
.divide('5');
// Fixed precision for display
Decimal.divide('1', '3', 4).toString(); // '0.3333'
Decimal.divide('1', '3').toFixed(4); // '0.3333'
// Financial calculations
const price = Decimal.from('19.99');
const tax = Decimal.multiply(price, '0.08');
const total = price.add(tax);
total.toString(); // '21.5892'DateTime
Chainable date manipulation with fluent API and template-based formatting.
Import
import { DateTime, format, parseDate, isValidDate } from '@songjin-ai/sttool';Creating DateTime
| Method | Description |
|--------|-------------|
| DateTime.now() | Current date/time |
| DateTime.from(value) | From string, Date, or DateTime |
| DateTime.create(year, month, day, hour?, minute?, second?) | From components |
Chainable Methods
| Method | Description |
|--------|-------------|
| .add(value, unit) | Add to unit |
| .subtract(value, unit) | Subtract from unit |
| .set(unit, value) | Set unit to value |
| .startOf(unit) | Set to start of unit |
| .endOf(unit) | Set to end of unit |
| .get(unit) | Get unit value |
Comparison Methods
| Method | Description |
|--------|-------------|
| .isBefore(other) | Check if before |
| .isAfter(other) | Check if after |
| .isSame(other) | Check if same |
| .compareTo(other) | Compare (-1, 0, 1) |
| .diffDays(other) | Difference in days |
Output Methods
| Method | Description |
|--------|-------------|
| .format(template) | Format with template |
| .toDate() | Get native Date |
| .toMillis() | Get timestamp (ms) |
| .toSeconds() | Get timestamp (s) |
Format Tokens
| Token | Output | Example |
|-------|--------|---------|
| YYYY | 4-digit year | 2026 |
| YY | 2-digit year | 26 |
| MMMM | Full month name | January |
| MMM | Short month name | Jan |
| MM | 2-digit month | 01 |
| M | Month number | 1 |
| DD | 2-digit day | 05 |
| D | Day number | 5 |
| HH | 2-digit hour (24h) | 14 |
| H | Hour (24h) | 14 |
| mm | 2-digit minute | 30 |
| m | Minute | 30 |
| ss | 2-digit second | 05 |
| s | Second | 5 |
Examples
// Chainable manipulation
DateTime.now()
.add(1, 'month')
.startOf('day')
.format('YYYY-MM-DD'); // '2026-05-01'
// Date comparison
const a = DateTime.from('2026-01-15');
const b = DateTime.from('2026-02-20');
a.isBefore(b); // true
a.diffDays(b); // 36
// Formatting
DateTime.now().format('YYYY-MM-DD HH:mm:ss');
// '2026-04-03 14:30:00'
// Parse and validate
parseDate('2026-04-03'); // returns Date
isValidDate('invalid'); // falseTree
Convert between flat lists and nested tree structures.
Import
import { listToTree, treeToList, TreeNode } from '@songjin-ai/sttool';Types
interface TreeNode<T = unknown> {
id: string | number;
parentId: string | number | null;
children?: T[];
[key: string]: unknown;
}listToTree
Convert a flat list with parentId references into a nested tree structure.
const list = [
{ id: 1, parentId: null, name: 'Root' },
{ id: 2, parentId: 1, name: 'Child 1' },
{ id: 3, parentId: 1, name: 'Child 2' },
{ id: 4, parentId: 2, name: 'Grandchild' },
];
const tree = listToTree(list);
// Result:
// [
// {
// id: 1, parentId: null, name: 'Root', children: [
// { id: 2, parentId: 1, name: 'Child 1', children: [
// { id: 4, parentId: 2, name: 'Grandchild', children: [] }
// ]},
// { id: 3, parentId: 1, name: 'Child 2', children: [] }
// ]
// }
// ]treeToList
Convert a nested tree structure back to a flat list.
const flat = treeToList(tree);
// [
// { id: 1, parentId: null, name: 'Root' },
// { id: 2, parentId: 1, name: 'Child 1' },
// { id: 3, parentId: 1, name: 'Child 2' },
// { id: 4, parentId: 2, name: 'Grandchild' }
// ]
// With depth information
const withDepth = treeToList(tree, { includeDepth: true });
// [
// { id: 1, parentId: null, name: 'Root', depth: 0 },
// { id: 2, parentId: 1, name: 'Child 1', depth: 1 },
// ...
// ]Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| includeDepth | boolean | false | Include depth level in output |
| childrenKey | string | 'children' | Key name for children array |
Use Cases
- Organizational structures — Convert database records to tree for UI
- File systems — Convert flat file list to nested directories
- Menus/Navigation — Build nested menu from flat menu items
- Category trees — E-commerce product categories
Object
Object property and structure manipulation utilities.
Import
import { deepGet, deepMerge, pick, omit, flatten, unflatten, orderBy, sortBy } from '@songjin-ai/sttool';deepGet
Safely get nested values with dot notation path and default fallback.
deepGet({ a: { b: { c: 1 } } }, 'a.b.c'); // 1
deepGet({ a: { b: [1, 2, 3] } }, 'a.b.1'); // 2
deepGet({ a: {} }, 'a.b.c', 'default'); // 'default'
deepGet(null, 'a.b.c', 'default'); // 'default'deepMerge
Deep merge two objects. Arrays are replaced, not concatenated.
deepMerge({ a: { b: 1 } }, { a: { c: 2 } }); // { a: { b: 1, c: 2 } }
deepMerge({ a: [1, 2] }, { a: [3] }); // { a: [3] }
deepMerge({ a: 1 }, null); // { a: 1 }pick / omit
Select or exclude object properties.
const obj = { a: 1, b: 2, c: 3 };
pick(obj, ['a', 'c']); // { a: 1, c: 3 }
pick(obj, 'a'); // { a: 1 } (string overload)
omit(obj, ['a', 'c']); // { b: 2 }
omit(obj, 'a'); // { b: 2, c: 3 } (string overload)flatten / unflatten
Convert between nested and flat object structures with custom delimiter support.
// Flatten nested object
flatten({ a: { b: { c: 1 }, d: 2 } });
// { 'a.b.c': 1, 'a.d': 2 }
// Custom delimiter
flatten({ a: { b: 1 } }, { delimiter: '-' });
// { 'a-b': 1 }
// Unflatten back to nested
unflatten({ 'a.b.c': 1, 'a.d': 2 });
// { a: { b: { c: 1 }, d: 2 } }
// Arrays are preserved
flatten({ a: [1, 2, { b: 3 }] });
// { 'a.0': 1, 'a.1': 2, 'a.2.b': 3 }orderBy / sortBy
Multi-key sorting for object arrays. Stable sort — preserves original order for equal keys.
// Sort by score descending, then chinese ascending
orderBy(users, ['score', 'chinese'], ['desc', 'asc']);
// Single key sort
sortBy(users, 'score', 'desc');
// Default ascending
sortBy(users, 'name');License
MIT
