Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ jobs:
if [ "${{ github.event_name }}" = "pull_request" ]; then
BASE_SHA="${{ github.event.pull_request.base.sha }}"
CHANGED=$(git diff --name-only "${BASE_SHA}"...HEAD)
if echo "$CHANGED" | grep -Eq '^(package.json|pnpm-lock.yaml|pnpm-workspace.yaml|tsconfig.base.json|codecov.yml|sonar-project.properties|\.sonarcloud\.properties|\.github/workflows/ci\.yml|governance/repository-health/|scripts/(repository-health|run-ci-tests|normalize-lcov-paths|sync-service-(rate-limit|edge)-policy)\.(mjs|test\.mjs)$)'; then
if echo "$CHANGED" | grep -Eq '^(package.json|pnpm-lock.yaml|pnpm-workspace.yaml|tsconfig.base.json|codecov.yml|sonar-project.properties|\.sonarcloud\.properties|\.github/workflows/ci\.yml|governance/repository-health/|scripts/(check-package-artifact|repository-health|run-ci-tests|normalize-lcov-paths|sync-service-(rate-limit|edge)-policy)\.(mjs|test\.mjs)$)'; then
FILTER=""
else
FILTER="...[${BASE_SHA}]"
Expand Down Expand Up @@ -150,6 +150,14 @@ jobs:
--filter '!example-paymail' \
run build

- name: Verify changed package artifacts
env:
SCOPE_FILTER: ${{ steps.scope.outputs.filter }}
run: |
EXTRA=""
if [ -n "$SCOPE_FILTER" ]; then EXTRA="--filter $SCOPE_FILTER"; fi
pnpm -r --if-present --filter '!@bsv/ts-stack' $EXTRA run pack:check

- name: Lint changed packages
continue-on-error: true
env:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ jobs:
- name: Verify package license payloads
run: pnpm license:pack-check

- name: Verify package artifacts and clean consumers
run: pnpm -r --if-present --filter '!@bsv/ts-stack' run pack:check

# Resolve release mode + filter. Three shapes:
#
# 1. Tag `packages/<path>/v*` → single-package publish, filter=./packages/<path>
Expand Down
10 changes: 10 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"arrowParens": "avoid",
"endOfLine": "lf",
"printWidth": 100,
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "none",
"useTabs": false
}
13 changes: 10 additions & 3 deletions docs/packages/helpers/amountinator.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ title: "@bsv/amountinator"
kind: package
domain: helpers
version: "2.1.1"
source_repo: "bsv-blockchain/amountinator"
source_repo: "bsv-blockchain/ts-stack"
source_commit: "unknown"
last_updated: "2026-07-24"
last_verified: "2026-07-24"
review_cadence_days: 30
npm: "https://www.npmjs.com/package/@bsv/amountinator"
repo: "https://github.com/bsv-blockchain/amountinator"
repo: "https://github.com/bsv-blockchain/ts-stack/tree/main/packages/helpers/amountinator"
status: stable
tags: [helpers, amounts, satoshis]
---
Expand Down Expand Up @@ -53,6 +53,13 @@ converter.dispose()
- **Formatted output** — Display string with symbol, decimals, and hover text
- **Satoshi rounding** — `convertToSatoshis()` rounds up to ensure sufficient payment

## Runtime and package compatibility

The package root provides matching typed entry points for Node.js ESM and
CommonJS consumers. The published tarball is checked with `publint`, strict
`@arethetypeswrong/core` resolution, and clean installs that import and
require both public exports. Node.js 22 or newer is supported.

## Common patterns

### Initialize with auto-refresh
Expand Down Expand Up @@ -140,5 +147,5 @@ const amount = await staticConverter.convertAmount('100')
## Reference

- [API reference (TypeDoc)](https://bsv-blockchain.github.io/ts-stack/api/amountinator/)
- [Source on GitHub](https://github.com/bsv-blockchain/amountinator)
- [Source on GitHub](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/helpers/amountinator)
- [npm](https://www.npmjs.com/package/@bsv/amountinator)
29 changes: 28 additions & 1 deletion docs/reference/repository-health.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
id: repository-health
title: "Repository Health Controls"
kind: reference
version: "1.1.0"
version: "1.2.0"
last_updated: "2026-07-26"
last_verified: "2026-07-26"
review_cadence_days: 30
Expand Down Expand Up @@ -99,6 +99,33 @@ Never refresh the baseline merely to make unexplained new debt pass. A new
temporary hold belongs in the exception registry, not in an unreviewed baseline
rewrite.

### Package artifact checks

A publishable package's `pack:check` command validates the built package rather
than its source tree. The shared checker:

1. creates the exact pnpm release tarball with lifecycle scripts disabled;
2. rejects tests, compiler caches, lockfiles, and uncompiled TypeScript in the
payload;
3. runs strict `publint` metadata and entry-point validation;
4. runs the `@arethetypeswrong/core` analyzer across every strict Node and
bundler resolution mode; and
5. installs the tarball into a clean temporary consumer and exercises every
declared runtime module format.

Build before checking an individual package:

```sh
pnpm --filter @bsv/amountinator build
pnpm --filter @bsv/amountinator pack:check
```

CI runs implemented `pack:check` commands for affected packages after the
shared workspace build. The release workflow repeats every implemented check
before resolving or publishing a release plan. Adding a script therefore adds
a blocking release contract; it must not be a placeholder or a source-only
check.

## CI behavior

The `Repository health contract` job runs without installing dependencies. It
Expand Down
26 changes: 1 addition & 25 deletions governance/repository-health/contract-baseline.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"schemaVersion": 1,
"recordedAt": "2026-07-26",
"findingCount": 137,
"findingCount": 131,
"findings": [
{
"id": ".::missing-script::format:check",
Expand Down Expand Up @@ -39,30 +39,6 @@
"id": "docs-site::missing-script::test",
"message": "Profile documentation-site requires script test"
},
{
"id": "packages/helpers/amountinator::missing-exports",
"message": "Profile requires package exports"
},
{
"id": "packages/helpers/amountinator::missing-script::format:check",
"message": "Profile node-library requires script format:check"
},
{
"id": "packages/helpers/amountinator::missing-script::lint",
"message": "Profile node-library requires script lint"
},
{
"id": "packages/helpers/amountinator::missing-script::pack:check",
"message": "Profile node-library requires script pack:check"
},
{
"id": "packages/helpers/amountinator::missing-script::test:coverage",
"message": "Profile node-library requires script test:coverage"
},
{
"id": "packages/helpers/amountinator::missing-script::typecheck",
"message": "Profile node-library requires script typecheck"
},
{
"id": "packages/helpers/bsv-wallet-helper::disabled-quality-script::lint:ci-disabled",
"message": "Remove disabled quality escape hatch lint:ci-disabled"
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
"docs:preview": "pnpm --filter docs-site preview"
},
"devDependencies": {
"@arethetypeswrong/core": "0.18.5",
"prettier": "3.9.6",
"publint": "0.3.22",
"yaml": "^2.9.0"
},
"engines": {
Expand Down
24 changes: 24 additions & 0 deletions packages/helpers/amountinator/jest.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/** @type {import('jest').Config} */
module.exports = {
bail: 1,
moduleFileExtensions: ['ts', 'js'],
modulePathIgnorePatterns: ['out/src', 'out/test', 'dist'],
rootDir: '.',
roots: ['<rootDir>'],
testEnvironment: 'node',
testMatch: ['**/?(*.)+(test).[tj]s'],
testRegex: [],
transform: {
'^.+\\.ts$': [
'ts-jest',
{
rootDir: '.',
tsconfig: {
module: 'commonjs',
moduleResolution: 'bundler'
}
}
]
},
verbose: true
}
35 changes: 0 additions & 35 deletions packages/helpers/amountinator/jest.config.ts

This file was deleted.

29 changes: 25 additions & 4 deletions packages/helpers/amountinator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,24 @@
"description": "Currency amount conversion",
"author": "Peer-to-peer Privacy Systems Research, LLC & BSV Association",
"license": "SEE LICENSE IN LICENSE.txt",
"main": "./out/src/index.js",
"types": "./out/src/index.d.ts",
"type": "commonjs",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.cts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"files": [
"out",
"dist",
"README.md",
"LICENSE.txt"
],
Expand All @@ -23,8 +37,13 @@
},
"scripts": {
"test": "jest",
"test:coverage": "jest --coverage",
"test:watch": "jest --watch",
"build": "tsc --build"
"build": "tsdown src/index.ts --format cjs,esm --dts --sourcemap --clean --out-dir dist --tsconfig tsconfig.build.json",
"format:check": "pnpm --workspace-root exec prettier --check \"packages/helpers/amountinator/{src,tests}/**/*.ts\" \"packages/helpers/amountinator/*.{cjs,json,ts}\"",
"lint": "oxlint src tests --deny-warnings",
"pack:check": "node ../../../scripts/check-package-artifact.mjs . --exports CurrencyConverter,formatAmountWithCurrency",
"typecheck": "tsc --project tsconfig.typecheck.json"
},
"repository": {
"type": "git",
Expand All @@ -39,8 +58,10 @@
"@bsv/sdk": "workspace:^",
"@types/jest": "^30.0.0",
"jest": "^30.4.2",
"oxlint": "^1.75.0",
"ts-jest": "^29.4.12",
"ts-node": "^10.9.2",
"tsdown": "0.22.14",
"typescript": "^6.0.3"
},
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/helpers/amountinator/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { CurrencyConverter } from './utils/currencyConverter'
export { formatAmountWithCurrency } from './utils/amountFormatHelpers'
export { formatAmountWithCurrency } from './utils/amountFormatHelpers'
2 changes: 1 addition & 1 deletion packages/helpers/amountinator/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ export interface FormatOptions {
decimalPlaces?: number
useCommas?: boolean
useUnderscores?: boolean
}
}
22 changes: 13 additions & 9 deletions packages/helpers/amountinator/src/utils/amountFormatHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,35 @@ function trimInsignificantZeros(fixed: string): string {

/**
* Formats a numerical amount with a specified currency and optional formatting options.
*
*
* @param amount - The numerical amount to format.
* @param currency - The currency code ('USD', 'GBP', 'EUR', 'SATS', or 'BSV').
* @param options - Optional formatting configuration.
* @param options.decimalPlaces - The number of decimal places to display. If not provided, it's determined automatically.
* @param options.useCommas - Whether to use commas as thousand separators. Defaults to true.
* @param options.useUnderscores - Whether to use underscores as thousand separators. Takes precedence over useCommas if both are true.
*
*
* @returns An object containing the formatted amount string and optionally a hover text.
* @property {string} formattedAmount - The formatted amount with currency symbol.
* @property {string} [hoverText] - The full formatted amount, present only for very small amounts (< 0.01).
*
*
* @example
* // Returns { formattedAmount: '$1,234.56' }
* formatAmountWithCurrency(1234.56, 'USD')
*
*
* @example
* // Returns { formattedAmount: '1_234.56 BSV' }
* formatAmountWithCurrency(1234.56, 'BSV', { useUnderscores: true })
*
*
* @example
* // Returns { formattedAmount: '< $0.01', hoverText: '$0.001' }
* formatAmountWithCurrency(0.001, 'USD')
*/
export function formatAmountWithCurrency(amount: number, currency: string, options?: { decimalPlaces?: number, useCommas?: boolean, useUnderscores?: boolean }): { formattedAmount: string, hoverText?: string } {
export function formatAmountWithCurrency(
amount: number,
currency: string,
options?: { decimalPlaces?: number; useCommas?: boolean; useUnderscores?: boolean }
): { formattedAmount: string; hoverText?: string } {
const { decimalPlaces, useCommas = true, useUnderscores = false } = options || {}

// Determine the number of decimal places
Expand Down Expand Up @@ -101,15 +105,15 @@ export function formatAmountWithCurrency(amount: number, currency: string, optio
} else if (currency === 'BSV') {
formattedAmount = formattedAmount + ' BSV'
} else {
formattedAmount = (symbols[currency] || (currency + ' ')) + formattedAmount
formattedAmount = (symbols[currency] || currency + ' ') + formattedAmount
}

// build result with hover text
const result: { formattedAmount: string; hoverText?: string } = { formattedAmount }
if (amount < 0.01) {
result.hoverText = formattedAmount
const currencyPrefix = currency === 'BSV' ? '' : (symbols[currency] || (currency + ' '))
const smallAmountLabel = currency === 'BSV' ? '< 0.01 BSV' : ('< ' + currencyPrefix + '0.01')
const currencyPrefix = currency === 'BSV' ? '' : symbols[currency] || currency + ' '
const smallAmountLabel = currency === 'BSV' ? '< 0.01 BSV' : '< ' + currencyPrefix + '0.01'
result.formattedAmount = smallAmountLabel
}

Expand Down
Loading
Loading