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

@songjin-ai/sttool

v1.0.1

Published

TypeScript utility library with high-precision calculation and date formatting

Readme

sttool

TypeScript utility library with high-precision calculation, date formatting, and tree structure conversion.

Install

npm install @songjin-ai/sttool

Modules

  • 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');      // false

Tree

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