diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5838194d0b..bf107d2f10 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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}]" @@ -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: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index afef94d3dc..ae8745ca73 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -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//v*` → single-package publish, filter=./packages/ diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000000..2c9b0c9f58 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,10 @@ +{ + "arrowParens": "avoid", + "endOfLine": "lf", + "printWidth": 100, + "semi": false, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "none", + "useTabs": false +} diff --git a/docs/packages/helpers/amountinator.md b/docs/packages/helpers/amountinator.md index 2749e8c9be..be6392d058 100644 --- a/docs/packages/helpers/amountinator.md +++ b/docs/packages/helpers/amountinator.md @@ -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] --- @@ -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 @@ -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) diff --git a/docs/reference/repository-health.md b/docs/reference/repository-health.md index 0eef750995..66fe60717d 100644 --- a/docs/reference/repository-health.md +++ b/docs/reference/repository-health.md @@ -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 @@ -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 diff --git a/governance/repository-health/contract-baseline.json b/governance/repository-health/contract-baseline.json index 000ce4be78..d27aa325d3 100644 --- a/governance/repository-health/contract-baseline.json +++ b/governance/repository-health/contract-baseline.json @@ -1,7 +1,7 @@ { "schemaVersion": 1, "recordedAt": "2026-07-26", - "findingCount": 137, + "findingCount": 131, "findings": [ { "id": ".::missing-script::format:check", @@ -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" diff --git a/package.json b/package.json index d59c8ae972..ff3761f1c3 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/packages/helpers/amountinator/jest.config.cjs b/packages/helpers/amountinator/jest.config.cjs new file mode 100644 index 0000000000..8cdf5ab850 --- /dev/null +++ b/packages/helpers/amountinator/jest.config.cjs @@ -0,0 +1,24 @@ +/** @type {import('jest').Config} */ +module.exports = { + bail: 1, + moduleFileExtensions: ['ts', 'js'], + modulePathIgnorePatterns: ['out/src', 'out/test', 'dist'], + rootDir: '.', + roots: [''], + testEnvironment: 'node', + testMatch: ['**/?(*.)+(test).[tj]s'], + testRegex: [], + transform: { + '^.+\\.ts$': [ + 'ts-jest', + { + rootDir: '.', + tsconfig: { + module: 'commonjs', + moduleResolution: 'bundler' + } + } + ] + }, + verbose: true +} diff --git a/packages/helpers/amountinator/jest.config.ts b/packages/helpers/amountinator/jest.config.ts deleted file mode 100644 index 214c1c6a9a..0000000000 --- a/packages/helpers/amountinator/jest.config.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { Config } from 'jest'; -//import { defaults } from 'jest-config' - -async function jestConfig(): Promise { - //console.log(defaults) - return { - bail: 1, - verbose: true, - // default is '.' - rootDir: '.', - // Must include source and test folders: default is [''] - roots: [""], - // Speed up by restricting to module (source files) extensions used. - moduleFileExtensions: ['ts', 'js'], - // excluded source files... - modulePathIgnorePatterns: ['out/src', 'out/test'], - // Default is 'node' - testEnvironment: 'node', - // default [ '**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)' ] - testMatch: ['**/?(*.)+(test).[tj]s'], - // default [] - testRegex: [], - transform: { - '^.+\\.ts$': ['ts-jest', { - rootDir: '.', - tsconfig: { - module: 'commonjs', - moduleResolution: 'bundler' - } - }] - }, - } -} - -export default jestConfig diff --git a/packages/helpers/amountinator/package.json b/packages/helpers/amountinator/package.json index f676bd433c..f1237b18cc 100644 --- a/packages/helpers/amountinator/package.json +++ b/packages/helpers/amountinator/package.json @@ -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" ], @@ -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", @@ -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": { diff --git a/packages/helpers/amountinator/src/index.ts b/packages/helpers/amountinator/src/index.ts index 9c90f2f0c0..25bfa1a253 100644 --- a/packages/helpers/amountinator/src/index.ts +++ b/packages/helpers/amountinator/src/index.ts @@ -1,2 +1,2 @@ export { CurrencyConverter } from './utils/currencyConverter' -export { formatAmountWithCurrency } from './utils/amountFormatHelpers' \ No newline at end of file +export { formatAmountWithCurrency } from './utils/amountFormatHelpers' diff --git a/packages/helpers/amountinator/src/types/index.ts b/packages/helpers/amountinator/src/types/index.ts index f1277d5106..b8bfaa70ee 100644 --- a/packages/helpers/amountinator/src/types/index.ts +++ b/packages/helpers/amountinator/src/types/index.ts @@ -29,4 +29,4 @@ export interface FormatOptions { decimalPlaces?: number useCommas?: boolean useUnderscores?: boolean -} \ No newline at end of file +} diff --git a/packages/helpers/amountinator/src/utils/amountFormatHelpers.ts b/packages/helpers/amountinator/src/utils/amountFormatHelpers.ts index 203c0a1fdd..c179e2a82f 100644 --- a/packages/helpers/amountinator/src/utils/amountFormatHelpers.ts +++ b/packages/helpers/amountinator/src/utils/amountFormatHelpers.ts @@ -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 @@ -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 } diff --git a/packages/helpers/amountinator/src/utils/currencyConverter.ts b/packages/helpers/amountinator/src/utils/currencyConverter.ts index 9fc7b1339b..4bcfedbc04 100644 --- a/packages/helpers/amountinator/src/utils/currencyConverter.ts +++ b/packages/helpers/amountinator/src/utils/currencyConverter.ts @@ -27,10 +27,13 @@ export class CurrencyConverter { private refreshTimer?: ReturnType - /** + /** * @param refreshInterval How often to pull new rates (ms), if 0, it never auto-refreshes */ - constructor(refreshInterval: number = DEFAULT_REFRESH_INTERVAL, settingsManager: WalletSettingsManager | undefined = undefined) { + constructor( + refreshInterval: number = DEFAULT_REFRESH_INTERVAL, + settingsManager: WalletSettingsManager | undefined = undefined + ) { this.refreshInterval = Math.max(0, refreshInterval) this.services = new Services('main') this.exchangeRates = { @@ -55,7 +58,7 @@ export class CurrencyConverter { // only start timer when refreshInterval > 0 if (this.refreshInterval > 0) { - this.refreshTimer = setInterval( () => this.fetchExchangeRates(true), this.refreshInterval) + this.refreshTimer = setInterval(() => this.fetchExchangeRates(true), this.refreshInterval) } } @@ -135,7 +138,7 @@ export class CurrencyConverter { return this.preferredCurrency } if (this.currencyPromise) return this.currencyPromise - + this.currencyPromise = (async () => { const settingsManager = this.settingsManager @@ -146,9 +149,9 @@ export class CurrencyConverter { this.currencyPromise = null return this.preferredCurrency })() - + return this.currencyPromise - } + } private normalizeSupportedCurrencyCode(currency: string): SupportedCurrencyCode { const upper = currency.toUpperCase() @@ -162,15 +165,15 @@ export class CurrencyConverter { /** * Converts currency amount based on user's preferences * @param {number | string} amount - the currency to convert - * @param {FormatOptions} formatOptions - * @returns + * @param {FormatOptions} formatOptions + * @returns */ async convertAmount(amount: number | string, formatOptions?: FormatOptions) { await this.refreshPreferredCurrency() const amountAsString = amount.toString() - let parsedAmount = Number.parseFloat(amountAsString.replace(/[^0-9.-]+/g, "")) + let parsedAmount = Number.parseFloat(amountAsString.replace(/[^0-9.-]+/g, '')) let inputCurrency = amountAsString.replace(/[\d.,\s]+/g, '').trim() - inputCurrency ||= (amountAsString.includes('.') ? 'BSV' : 'SATS') + inputCurrency ||= amountAsString.includes('.') ? 'BSV' : 'SATS' // Use convertCurrency to directly convert from the input currency to the preferred currency let finalAmount = this.convertCurrency(parsedAmount, inputCurrency, this.preferredCurrency) @@ -182,8 +185,8 @@ export class CurrencyConverter { /** * Convert an amount given in any of the supported currencies to an amount in satoshis - * @param amount - * @returns + * @param amount + * @returns */ async convertToSatoshis(amount: number): Promise { // Directly convert the amount from the preferred currency to SATS diff --git a/packages/helpers/amountinator/tests/currencyConverterCache.test.ts b/packages/helpers/amountinator/tests/currencyConverterCache.test.ts index a60da74aa4..d068a9f390 100644 --- a/packages/helpers/amountinator/tests/currencyConverterCache.test.ts +++ b/packages/helpers/amountinator/tests/currencyConverterCache.test.ts @@ -4,86 +4,84 @@ const getBsvExchangeRateMock = jest.fn() const getFiatExchangeRatesMock = jest.fn() jest.mock('@bsv/wallet-toolbox-client', () => ({ - Services: jest.fn().mockImplementation(() => ({ - getBsvExchangeRate: getBsvExchangeRateMock, - getFiatExchangeRates: getFiatExchangeRatesMock - })), - WalletSettingsManager: jest.fn().mockImplementation(() => ({ - get: jest.fn().mockResolvedValue({ currency: 'USD' }) - })) + Services: jest.fn().mockImplementation(() => ({ + getBsvExchangeRate: getBsvExchangeRateMock, + getFiatExchangeRates: getFiatExchangeRatesMock + })), + WalletSettingsManager: jest.fn().mockImplementation(() => ({ + get: jest.fn().mockResolvedValue({ currency: 'USD' }) + })) })) describe('CurrencyConverter cache behaviour', () => { - let cc: CurrencyConverter - - beforeEach(() => { - jest.useFakeTimers() - jest.setSystemTime(new Date('2024-01-01T00:00:00.000Z')) - jest.clearAllMocks() - - // first call returns 62 USD/BSV, later calls would return 100 - getBsvExchangeRateMock - .mockResolvedValueOnce(62) - .mockResolvedValue(100) - - // fiat rates are fine staying constant for this test - getFiatExchangeRatesMock.mockResolvedValue({ - timestamp: new Date(), - base: 'USD', - rates: {} - }) - }) + let cc: CurrencyConverter + + beforeEach(() => { + jest.useFakeTimers() + jest.setSystemTime(new Date('2024-01-01T00:00:00.000Z')) + jest.clearAllMocks() - afterEach(() => { - cc?.dispose?.() - jest.clearAllTimers() - jest.useRealTimers() + // first call returns 62 USD/BSV, later calls would return 100 + getBsvExchangeRateMock.mockResolvedValueOnce(62).mockResolvedValue(100) + + // fiat rates are fine staying constant for this test + getFiatExchangeRatesMock.mockResolvedValue({ + timestamp: new Date(), + base: 'USD', + rates: {} }) + }) - test('uses cached exchange rate inside 5-minute window', async () => { - cc = new CurrencyConverter() - await cc.initialize() + afterEach(() => { + cc?.dispose?.() + jest.clearAllTimers() + jest.useRealTimers() + }) - const first = await cc.convertAmount('10000') + test('uses cached exchange rate inside 5-minute window', async () => { + cc = new CurrencyConverter() + await cc.initialize() - jest.advanceTimersByTime(1000) + const first = await cc.convertAmount('10000') - const second = await cc.convertAmount('10000') + jest.advanceTimersByTime(1000) - expect(first.hoverText).toBe('$0.0062') - expect(second.hoverText).toBe('$0.0062') - expect(getBsvExchangeRateMock).toHaveBeenCalledTimes(1) - }) + const second = await cc.convertAmount('10000') - test('checks if the cache is set to 0 if it refreshes or not', async () => { - cc = new CurrencyConverter(0) - await cc.initialize() + expect(first.hoverText).toBe('$0.0062') + expect(second.hoverText).toBe('$0.0062') + expect(getBsvExchangeRateMock).toHaveBeenCalledTimes(1) + }) - const first = await cc.convertAmount('10000') + test('checks if the cache is set to 0 if it refreshes or not', async () => { + cc = new CurrencyConverter(0) + await cc.initialize() - jest.advanceTimersByTime(1000) + const first = await cc.convertAmount('10000') - const second = await cc.convertAmount('10000') + jest.advanceTimersByTime(1000) - expect(first.hoverText).toBe('$0.0062') - expect(second.hoverText).toBe('$0.0062') - expect(getBsvExchangeRateMock).toHaveBeenCalledTimes(1) - }) + const second = await cc.convertAmount('10000') - test('checks if the cache will properly reset itself', async () => { - cc = new CurrencyConverter(500) - await cc.initialize() + expect(first.hoverText).toBe('$0.0062') + expect(second.hoverText).toBe('$0.0062') + expect(getBsvExchangeRateMock).toHaveBeenCalledTimes(1) + }) - const first = await cc.convertAmount('10000') - const second = await cc.convertAmount('5000') + test('checks if the cache will properly reset itself', async () => { + cc = new CurrencyConverter(500) + await cc.initialize() - await jest.advanceTimersByTimeAsync(500) + const first = await cc.convertAmount('10000') + const second = await cc.convertAmount('5000') - const third = await cc.convertAmount('10000') + await jest.advanceTimersByTimeAsync(500) - expect(first.hoverText).toBe('$0.0062') - expect(second.hoverText).toBe('$0.0031') - expect(third.hoverText).not.toBe('$0.0062') - expect(getBsvExchangeRateMock).toHaveBeenCalledTimes(2) - }) + const third = await cc.convertAmount('10000') + + expect(first.hoverText).toBe('$0.0062') + expect(second.hoverText).toBe('$0.0031') + expect(third.hoverText).not.toBe('$0.0062') + expect(getBsvExchangeRateMock).toHaveBeenCalledTimes(2) + }) }) diff --git a/packages/helpers/amountinator/tests/formatAmountWithCurrency.test.ts b/packages/helpers/amountinator/tests/formatAmountWithCurrency.test.ts index f4f49cb348..b7793498b3 100644 --- a/packages/helpers/amountinator/tests/formatAmountWithCurrency.test.ts +++ b/packages/helpers/amountinator/tests/formatAmountWithCurrency.test.ts @@ -2,39 +2,64 @@ import { formatAmountWithCurrency } from '../src/utils/amountFormatHelpers' import { CurrencyConverter } from '../src/utils/currencyConverter' describe('formatAmountWithCurrency', () => { + test('infers unitless satoshi and decimal BSV amounts using the wallet currency', async () => { + const settingsManager = { + get: jest.fn().mockResolvedValue({ currency: 'USD' }) + } as unknown as NonNullable[1]> + const converter = new CurrencyConverter(0, settingsManager) + converter.exchangeRates.usdPerBsv = 62 - // I'm skipping this beacuse I'm not 100% sure what it is trying to do - test.skip('generic test', async () => { - const c = new CurrencyConverter() - await c.initialize() - let amount = await c.convertAmount('10000') - console.log('a', amount) - expect(amount.formattedAmount).toBe('$0.0062') - }) + const satoshiAmount = await converter.convertAmount('10000') + const bsvAmount = await converter.convertAmount('0.5') + expect(satoshiAmount.formattedAmount).toBe('< $0.01') + expect(satoshiAmount.hoverText).toBe('$0.0062') + expect(bsvAmount.formattedAmount).toBe('$31') + expect(settingsManager.get).toHaveBeenCalledTimes(2) + }) test('formats USD with default settings', () => { expect(formatAmountWithCurrency(1234.56, 'USD').formattedAmount).toBe('$1,234.56') }) test('formats EUR with no decimals', () => { - expect(formatAmountWithCurrency(1234, 'EUR', { decimalPlaces: 0 }).formattedAmount).toBe('€1,234') + expect(formatAmountWithCurrency(1234, 'EUR', { decimalPlaces: 0 }).formattedAmount).toBe( + '€1,234' + ) }) test('formats GBP with underscores', () => { - expect(formatAmountWithCurrency(1234567.89, 'GBP', { useUnderscores: true }).formattedAmount).toBe('£1_234_567.89') + expect( + formatAmountWithCurrency(1234567.89, 'GBP', { useUnderscores: true }).formattedAmount + ).toBe('£1_234_567.89') }) test('formats SATS with many decimals', () => { - expect(formatAmountWithCurrency(0.000012345, 'SATS', { decimalPlaces: 9 }).hoverText).toBe('0.000012345 satoshis') + expect(formatAmountWithCurrency(0.000012345, 'SATS', { decimalPlaces: 9 }).hoverText).toBe( + '0.000012345 satoshis' + ) }) test('formats BSV without commas', () => { - expect(formatAmountWithCurrency(1000, 'BSV', { useCommas: false }).formattedAmount).toBe('1000 BSV') + expect(formatAmountWithCurrency(1000, 'BSV', { useCommas: false }).formattedAmount).toBe( + '1000 BSV' + ) }) - test('handles very small amounts correctly', () => { - expect(formatAmountWithCurrency(0.00000012345, 'USD', { decimalPlaces: 10 }).hoverText).toBe('$0.0000001235') + expect(formatAmountWithCurrency(0.00000012345, 'USD', { decimalPlaces: 10 }).hoverText).toBe( + '$0.0000001235' + ) + }) + + test('formats very small amounts for BSV and currencies without a known symbol', () => { + expect(formatAmountWithCurrency(0.001, 'BSV')).toEqual({ + formattedAmount: '< 0.01 BSV', + hoverText: '0.001 BSV' + }) + expect(formatAmountWithCurrency(0.001, 'XYZ')).toEqual({ + formattedAmount: '< XYZ 0.01', + hoverText: 'XYZ 0.001' + }) }) test('formats negative amounts correctly', () => { diff --git a/packages/helpers/amountinator/tsconfig.build.json b/packages/helpers/amountinator/tsconfig.build.json new file mode 100644 index 0000000000..5604ff088e --- /dev/null +++ b/packages/helpers/amountinator/tsconfig.build.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "composite": false, + "declaration": false, + "declarationMap": false, + "incremental": false, + "ignoreDeprecations": "6.0" + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/helpers/amountinator/tsconfig.json b/packages/helpers/amountinator/tsconfig.json index 466a81bb5b..8e82080d07 100644 --- a/packages/helpers/amountinator/tsconfig.json +++ b/packages/helpers/amountinator/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "es2019", - "jsx": "react-jsx", + "jsx": "react-jsx", "module": "ESNext", "moduleResolution": "bundler", "sourceMap": true, @@ -21,6 +21,5 @@ }, "include": ["src/**/*", "test/**/*"], "exclude": ["node_modules", "jest.config.ts"], - "references": [ - ] -} \ No newline at end of file + "references": [] +} diff --git a/packages/helpers/amountinator/tsconfig.typecheck.json b/packages/helpers/amountinator/tsconfig.typecheck.json new file mode 100644 index 0000000000..2f21d0c0d5 --- /dev/null +++ b/packages/helpers/amountinator/tsconfig.typecheck.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "composite": false, + "declaration": false, + "declarationMap": false, + "ignoreDeprecations": "6.0", + "incremental": false, + "noEmit": true + }, + "include": ["src/**/*.ts", "tests/**/*.ts"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ef30457d57..2a43e5a924 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,15 @@ importers: .: devDependencies: + '@arethetypeswrong/core': + specifier: 0.18.5 + version: 0.18.5 + prettier: + specifier: 3.9.6 + version: 3.9.6 + publint: + specifier: 0.3.22 + version: 0.3.22 yaml: specifier: ^2.9.0 version: 2.9.0 @@ -128,12 +137,18 @@ importers: jest: specifier: ^30.4.2 version: 30.4.2(@types/node@26.1.1)(ts-node@10.9.2(@types/node@26.1.1)(typescript@6.0.3)) + oxlint: + specifier: ^1.75.0 + version: 1.75.0 ts-jest: specifier: ^29.4.12 version: 29.4.12(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(esbuild@0.28.1)(jest-util@30.4.1)(jest@30.4.2(@types/node@26.1.1)(ts-node@10.9.2(@types/node@26.1.1)(typescript@6.0.3)))(typescript@6.0.3) ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@26.1.1)(typescript@6.0.3) + tsdown: + specifier: 0.22.14 + version: 0.22.14(@arethetypeswrong/core@0.18.5)(publint@0.3.22)(tsx@4.23.1)(typescript@6.0.3) typescript: specifier: ^6.0.3 version: 6.0.3 @@ -1444,9 +1459,16 @@ packages: '@adobe/css-tools@4.5.0': resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==} + '@andrewbranch/untar.js@1.0.3': + resolution: {integrity: sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw==} + '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + '@arethetypeswrong/core@0.18.5': + resolution: {integrity: sha512-9ytjzGwxjm9Uz7I9avfbt5vlQt6uk9uRRESzJjqrznl6WKvI6dwYTo+vJ3U02Wrq/mR3iql/PzhvHhKdJIAjDQ==} + engines: {node: '>=20'} + '@asamuzakjp/css-color@3.2.0': resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} @@ -1619,6 +1641,9 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + '@braidai/lang@1.1.2': + resolution: {integrity: sha512-qBcknbBufNHlui137Hft8xauQMTZDKdophmLFv05r2eNmdIv/MlPuP4TdUknHG68UdWLgVZwgxVe735HzJNIwA==} + '@braintree/sanitize-url@7.1.2': resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==} @@ -2113,6 +2138,9 @@ packages: '@libp2p/utils@7.3.0': resolution: {integrity: sha512-7mDQQVutmz6LsrehO9W12RI0Hwnx/zQzqlsj1DB0/2vHf8As/4jEntoEM8ko6RAOaDQ7Y5OrJ2Fgd7O9gyVRkA==} + '@loaderkit/resolve@1.0.6': + resolution: {integrity: sha512-G8FdIoF5CypfwmD9rl8BXod5HDn8JqB0CCNBXDTaRZ+yRYhARrrSToX1zg1zy9jX3zLqigsELwhT4gNtkdQAUg==} + '@mdx-js/mdx@3.1.1': resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} @@ -2170,6 +2198,9 @@ packages: '@oxc-project/types@0.139.0': resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==} + '@oxc-project/types@0.140.0': + resolution: {integrity: sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==} + '@oxlint/binding-android-arm-eabi@1.75.0': resolution: {integrity: sha512-lutovtFzJqlRaqpZrCqSSGaHZzl9nIxxpjLzhSRLunN6dCLylj0uzlCyQGaQDIys7rrv8kVXiFO+R4Zpn0bX7g==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2341,6 +2372,10 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@publint/pack@0.1.6': + resolution: {integrity: sha512-3uVNyGcVplhPZSLVyeIpL7+cIRn1YCSNHLG/rUIlBQMVH8YuN9++YF+5+UDIIO9RW98dujiUoTltO7RDB5bFJA==} + engines: {node: '>=18'} + '@puppeteer/browsers@3.0.6': resolution: {integrity: sha512-B/gKoqlFkzhvzsI6jo9K1cZz9o5ypviVv/xu8CwA4grZzyVwN+XfkT+tu8T1zrauuEXv6VhS2oGX+6NL95WcKA==} engines: {node: '>=22.12.0'} @@ -2354,36 +2389,69 @@ packages: yauzl: optional: true + '@quansync/fs@1.0.0': + resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} + '@rolldown/binding-android-arm64@1.1.5': resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] + '@rolldown/binding-android-arm64@1.2.0': + resolution: {integrity: sha512-9yB1l95IrJuNGDFdOYe79vdApdz6WWBCObE+rQ2LUliYUlcyFwSYIb2xb5/Ifw7dAtMy2ZqNyd8QTSOc7duAKw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + '@rolldown/binding-darwin-arm64@1.1.5': resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] + '@rolldown/binding-darwin-arm64@1.2.0': + resolution: {integrity: sha512-pexNaW9ACLUOaBITOpU6qVu4VrsOFIjTv6bzgu0YUATo4eUJx0V605PxwZfndpPOn0ilqGqvGQ0M8UW0IE24jg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + '@rolldown/binding-darwin-x64@1.1.5': resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] + '@rolldown/binding-darwin-x64@1.2.0': + resolution: {integrity: sha512-NqKYaq0355ZmNMG4QGpxtEDxsc7tGDhjhCm4PpE0cwnBW+5Il95LJyq414niEiaKLVjnVHBEjSo1wngKxJNiFw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + '@rolldown/binding-freebsd-x64@1.1.5': resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] + '@rolldown/binding-freebsd-x64@1.2.0': + resolution: {integrity: sha512-3vPoHzh6eBTz9IbB0/qZdSr0Qeks2echn+I4cHu2joV74VriPDdldswksEDzrl1mBB+oPRi+67+3Ib59paxIPQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + '@rolldown/binding-linux-arm-gnueabihf@1.1.5': resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] + '@rolldown/binding-linux-arm-gnueabihf@1.2.0': + resolution: {integrity: sha512-E6NNefZ1bUVmKJq2tJkf45J4Zyczj7qm9rUT7NY+Xo2474Y13qWAwc2tvBt0BAVbmtXR1llkxXg0Ou1jbDf2SQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + '@rolldown/binding-linux-arm64-gnu@1.1.5': resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2391,6 +2459,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-arm64-gnu@1.2.0': + resolution: {integrity: sha512-D+TgkdgM1vu+7/Fpf8+v0ARW+RXEP9Ccazgm8zQ4JFFd9Q7SrYQ2TakU5S5ihazQDgpKyAgZDOcIFsvoHmTZ8w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-arm64-musl@1.1.5': resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2398,6 +2473,13 @@ packages: os: [linux] libc: [musl] + '@rolldown/binding-linux-arm64-musl@1.2.0': + resolution: {integrity: sha512-wUqdwJBbAv0APN87GecstdMUtLjjNTs0hBALpxETD73mccFxdmt/XeizXDtN5RAlBwNKmI+Tg+blect2G+8IeQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + '@rolldown/binding-linux-ppc64-gnu@1.1.5': resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2405,6 +2487,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-ppc64-gnu@1.2.0': + resolution: {integrity: sha512-9DtF35qR9/NrfhM4oxLplCzVVjE+KKm8Pjemi0i/sdhAWkUasjmSo8WTTubNJClhSHCfyk2yeyoXDQEDPtDAAw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-s390x-gnu@1.1.5': resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2412,6 +2501,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-s390x-gnu@1.2.0': + resolution: {integrity: sha512-RzuHrBh8X8Hntd2N4VR02QGEciq/9JhcZoTpR/Cee6otRrlILGCf3cg2ygHuih+ZebUnWmMrDX6ITI85btO6rQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-x64-gnu@1.1.5': resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2419,6 +2515,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-x64-gnu@1.2.0': + resolution: {integrity: sha512-MK7L0018jjh1jR3mh21G2j1zAVcpscJBlPo2z19pRjv2XOYGRhaV4LyiD8HO6nCDdZln9IFgCMIV5yt4E3klGQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-x64-musl@1.1.5': resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2426,29 +2529,59 @@ packages: os: [linux] libc: [musl] + '@rolldown/binding-linux-x64-musl@1.2.0': + resolution: {integrity: sha512-gyrxLQ9NfGb/9LoVnC4kb9miUghw1mghnkfYvNHSnVIXriabnfgGPUP4RLcJm87q3KgYz4FYUG8IDiWUT+CpSw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + '@rolldown/binding-openharmony-arm64@1.1.5': resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] + '@rolldown/binding-openharmony-arm64@1.2.0': + resolution: {integrity: sha512-/6VFMQGRmrhP77KXDC+StIxGzcNp5JOIyYtw0CQ8gPlzhpiIRucYfoM5FaFamHd5BJYIdH86yfP46l1p3WdrFA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + '@rolldown/binding-wasm32-wasi@1.1.5': resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] + '@rolldown/binding-wasm32-wasi@1.2.0': + resolution: {integrity: sha512-rwdbUL465kisF24WEJLvP3JrEG6E5GRuIHt5wpMwHGERtHe4Wm2CIvtf5gTBgr2tGOHKh5NdKEAFS2VkOPE91g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + '@rolldown/binding-win32-arm64-msvc@1.1.5': resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] + '@rolldown/binding-win32-arm64-msvc@1.2.0': + resolution: {integrity: sha512-+5suHwRiKGmhwyUaNT8a5QbrBvLFh2DbO910TEmGRH1aSxwrCezodvGQnulv4uiWEIv1Kq4ypRsJ5+O+ry1DiA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + '@rolldown/binding-win32-x64-msvc@1.1.5': resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] + '@rolldown/binding-win32-x64-msvc@1.2.0': + resolution: {integrity: sha512-WfFv6/qGufotqBSBzBYwgpCkJBk8Nj7697LL9vTz/XWc67e0r3oewu8iMRwQj3AUL45GVD7wVsPjCsAAtW66Wg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} @@ -3256,6 +3389,131 @@ packages: '@xtuc/long@4.2.2': resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + '@yuku-codegen/binding-darwin-arm64@0.8.0': + resolution: {integrity: sha512-7cSJH6PaKLRBdCfiB4pM6EukvgOk5xV4tyuLOIOEqrHsbnV7brtyff7CjhZbeGozdIHoOnKOi5R7rrmCWN3QSw==} + cpu: [arm64] + os: [darwin] + + '@yuku-codegen/binding-darwin-x64@0.8.0': + resolution: {integrity: sha512-mhooLL+L5ytMxgz4ueXCIirU796X2xj97d4KSQW1HxZGzX6h8wOk5bIAhGqcmOL2bqAmMZ+UkBTbPC9VpzKb/g==} + cpu: [x64] + os: [darwin] + + '@yuku-codegen/binding-freebsd-x64@0.8.0': + resolution: {integrity: sha512-MHLOAlgGhdOh0ZfnWWnno4ljlFB04/Lox/7MIGYIvISMKFvejfC9Atb1t9G7pTVBvy+l5uqxzHGBSJUsDOOkTA==} + cpu: [x64] + os: [freebsd] + + '@yuku-codegen/binding-linux-arm-gnu@0.8.0': + resolution: {integrity: sha512-Ur3Awo45Sc5/Fglr8WN5XIf4IwAsq8wLd917Du8ow6mStxsBTHqFiH+tT7d5jV0FqcJnwy8EHVczmgde0zTo1A==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@yuku-codegen/binding-linux-arm-musl@0.8.0': + resolution: {integrity: sha512-FIy7Ttx8oeUCd/8Y6IjnOsu+lRc6En+V/H67BlVphOeCySZAo5LU8VWrb4tv0DvjaSOzdm3DmdmRzmzN7NCqWQ==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@yuku-codegen/binding-linux-arm64-gnu@0.8.0': + resolution: {integrity: sha512-s+25wl1TLvf+7LzasPEi1RR1sDfVAU0i1QH601mn3vj+HudFYBYNZtUBKFaZvl645QR6vcaDaGHnOaMZhBR3ig==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@yuku-codegen/binding-linux-arm64-musl@0.8.0': + resolution: {integrity: sha512-pRha3Cjm4AnA5wEuhpg+8XXoGfwz5X61/9a/VNxeau47+kH6xjJspkEloIy/HbHgUnvVRqVHoEHczdGZT2J9NA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@yuku-codegen/binding-linux-x64-gnu@0.8.0': + resolution: {integrity: sha512-GySXmiw5Dw99Ba3GMV2ExQVckihuangnrIpSJagPx8RFUNtwfmzpr2ibf8k31eEAE4YUofV7mEfJN5tN6+POnQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@yuku-codegen/binding-linux-x64-musl@0.8.0': + resolution: {integrity: sha512-8YFfcPZz44v8YyekWVKkNz/cD4t6DW62/dr03OVtMfwUtHfmo/8wpby0JKMleAOXbgWItSGw81LtwqM4I9oS0A==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@yuku-codegen/binding-win32-arm64@0.8.0': + resolution: {integrity: sha512-YhENbgkuzjsil+zDNV35oU3PQMDg2RXh8BPt915WCNAbIIHcqgYupHJF3564206+DbPkZtcDvcoa34Wb5B8tbQ==} + cpu: [arm64] + os: [win32] + + '@yuku-codegen/binding-win32-x64@0.8.0': + resolution: {integrity: sha512-qvzSRABXe6/ndubx+RNwgbFVbs7Pqroz/q/UR6vm++xsmfcpkMF43B23jgNl2xi2IUrEt8C/L1bBslXp+LtgnA==} + cpu: [x64] + os: [win32] + + '@yuku-parser/binding-darwin-arm64@0.8.0': + resolution: {integrity: sha512-04AakSJhI4mPrqhZzXdFyaEDh0YkfeqbnyYY3aCrmxeWfR/Xr8+kFn5sh+wZYN/5HatPniELKHixJuUCUyfBvg==} + cpu: [arm64] + os: [darwin] + + '@yuku-parser/binding-darwin-x64@0.8.0': + resolution: {integrity: sha512-BQJGI9bDeyb/X2rwhtXoBTQ9EtbkJtteX6C7cZ9jow0pqqmoOufgHPP2+m65GLk2eVNCBcfl3xlTGpJ7RFcviw==} + cpu: [x64] + os: [darwin] + + '@yuku-parser/binding-freebsd-x64@0.8.0': + resolution: {integrity: sha512-04hmgnU152wya88raI+RQhxZPgwXcHbfdNZLu3x5ggKnJVHLD8xZZLcWIKSs59CiEXL6PKVX1/cx8GoTYlaCaQ==} + cpu: [x64] + os: [freebsd] + + '@yuku-parser/binding-linux-arm-gnu@0.8.0': + resolution: {integrity: sha512-+cuJWUK13lwce721XGRjz7izSr2Q1U0RlHkDzdkohWpRWlttTqRdxNnaW13nDc47GBDrNsXMHx+KLcebeOeeGg==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@yuku-parser/binding-linux-arm-musl@0.8.0': + resolution: {integrity: sha512-rOVPRqt9cm1YP/wPV+yyZh0FYr6UR/wm/BXslvLuge0LDewVvDrm/AxcDrwWuPAu61Nzdg2rMVfcNnrplCbC1w==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@yuku-parser/binding-linux-arm64-gnu@0.8.0': + resolution: {integrity: sha512-pAoIozKr6E+ptpaQz4CZv1O7cay2f4m7kbd+DSQug5MKdUBTZZ18GdWSPvq0fwQYraH9hZlyLqrMaeP5W/ncrA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@yuku-parser/binding-linux-arm64-musl@0.8.0': + resolution: {integrity: sha512-JCAg50ahXuYlrsIi1jmymX9X/9B0JYBRroAHnYttN44tAvCo1PFqukHrw1up6HvEOoLA0OjlM0Zwh60u3Gc/Zg==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@yuku-parser/binding-linux-x64-gnu@0.8.0': + resolution: {integrity: sha512-tEeVQ14etp7lpUqXzq+X5AlQzFH+m3TVDiCKIq17zxnxJ117DOVvoveWSkSMt/lj68z48SvZUMHe7xLvqtO1lg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@yuku-parser/binding-linux-x64-musl@0.8.0': + resolution: {integrity: sha512-+UGRYnF37nnbZNMsMjSGDXKUTIxYUmbbk3Lzib88sLK7kg2y80vjchYYoYsDUK9kAgqPWyA9hKGURHy/Kk9Few==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@yuku-parser/binding-win32-arm64@0.8.0': + resolution: {integrity: sha512-l+7Va9/sX1ccRjzjJj6MR0arKsvHB5b419+pKbwzn+/18A/xfccWqmxXrn0C0NyVLlbEb3AV9Is+AbF72O85yA==} + cpu: [arm64] + os: [win32] + + '@yuku-parser/binding-win32-x64@0.8.0': + resolution: {integrity: sha512-dolKDTJv2xrWowDBEkvSaDvCsVFZOA7DCUuD+7DatglS68WbuxS4LudU5bzSeAOL5vgPpyGm82469cMLnL+6vQ==} + cpu: [x64] + os: [win32] + + '@yuku-toolchain/types@0.8.0': + resolution: {integrity: sha512-hL/raFM5V9UT2lVE/lIWDTWvANqSB8TvMVp+PgICehBa96KhTl/UpGm370JXaacukR+QnaHM2Yv6O/UcrzAFGg==} + abbrev@4.0.0: resolution: {integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==} engines: {node: ^20.17.0 || >=22.9.0} @@ -3332,6 +3590,10 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} + ansis@4.3.1: + resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==} + engines: {node: '>=14'} + any-signal@4.2.0: resolution: {integrity: sha512-LndMvYuAPf4rC195lk7oSFuHOYFpOszIYrNYv0gHAvz+aEhE9qPZLhmrIz5pXP2BSsPOXvsuHDXEGaiQhIh9wA==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} @@ -3507,6 +3769,10 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -3586,6 +3852,9 @@ packages: resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} + cjs-module-lexer@1.4.3: + resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + cjs-module-lexer@2.2.0: resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} @@ -3932,6 +4201,9 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + delaunator@5.1.0: resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==} @@ -3996,6 +4268,15 @@ packages: resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} engines: {node: '>=12'} + dts-resolver@3.0.0: + resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} + engines: {node: ^22.18.0 || >=24.0.0} + peerDependencies: + oxc-resolver: '>=11.0.0' + peerDependenciesMeta: + oxc-resolver: + optional: true + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -4027,6 +4308,10 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + empathic@2.0.1: + resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} + engines: {node: '>=14'} + encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} @@ -4262,6 +4547,9 @@ packages: picomatch: optional: true + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} @@ -4366,6 +4654,10 @@ packages: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} + get-tsconfig@5.0.0-beta.5: + resolution: {integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==} + engines: {node: '>=20.20.0'} + getopts@2.3.0: resolution: {integrity: sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==} @@ -4446,6 +4738,9 @@ packages: hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + hookable@6.1.1: + resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} + html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -4497,6 +4792,10 @@ packages: import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + import-without-cache@0.4.0: + resolution: {integrity: sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ==} + engines: {node: ^22.18.0 || >=24.0.0} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -5060,6 +5359,10 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.5.2: + resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -5433,6 +5736,10 @@ packages: mortice@3.3.1: resolution: {integrity: sha512-t3oESfijIPGsmsdLEKjF+grHfrbnKSXflJtgb1wY14cjxZpS6GnhHRXTxxzCAoCCnq1YYfpEPwY3gjiCPhOufQ==} + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + mrmime@2.0.1: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} @@ -5763,6 +6070,11 @@ packages: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} + publint@0.3.22: + resolution: {integrity: sha512-6Z/scsr5CA7APdwyF35EY88CqgDj1textWuY788DVTJYPCWVv/Wn9G6KmLnrVRnStgYcahqN4wCDLZGSbQJ69w==} + engines: {node: '>=18'} + hasBin: true + pump@3.0.4: resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} @@ -5786,6 +6098,9 @@ packages: resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} engines: {node: '>=0.6'} + quansync@1.0.0: + resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} + race-event@1.6.1: resolution: {integrity: sha512-vi7WH5g5KoTFpu2mme/HqZiWH14XSOtg5rfp6raBskBHl7wnmy3F/biAIyY5MsK+BHWhoPhxtZ1Y2R7OHHaWyQ==} @@ -5923,6 +6238,9 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve-tsconfig@1.3.0: resolution: {integrity: sha512-Ba5mo3soshb2CnIcNFz75F/80H/2eMVxrlmdgoSDNH7Lr6UAoT3BvxNtc7+VXqKSBlC0SJk2qSXOTcy0/p7cFw==} engines: {node: '>=16', pnpm: '>=7'} @@ -5937,11 +6255,35 @@ packages: robust-predicates@3.0.3: resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} + rolldown-plugin-dts@0.27.14: + resolution: {integrity: sha512-ZvuDDwoIpRK9RPxDXratCpklFO9QZZWndf/sd0VBFb4LEj0jj07UcHK9OCh7V4XiFz2Z89ziyBC2K6tJiDjrbw==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@typescript/native-preview': '*' + '@volar/typescript': ~2.4.0 + rolldown: ^1.0.0 + typescript: ^5.0.0 || ^6.0.0 || ~7.0.0 + vue-tsc: ~3.2.0 || ~3.3.0 + peerDependenciesMeta: + '@typescript/native-preview': + optional: true + '@volar/typescript': + optional: true + typescript: + optional: true + vue-tsc: + optional: true + rolldown@1.1.5: resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rolldown@1.2.0: + resolution: {integrity: sha512-u7tgm5l4Yw1iTqUL4EcYOAt7fFvCgQMLeidrnD4GALlC6aOznCjezYajgxeyKw27u0Q5N7fwgCzjVyPIWzwuBA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + rollup@4.62.2: resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -5960,6 +6302,10 @@ packages: rw@1.3.3: resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -6312,6 +6658,10 @@ packages: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -6385,6 +6735,40 @@ packages: peerDependencies: typescript: '>=4.0.0' + tsdown@0.22.14: + resolution: {integrity: sha512-ule7Y+fsAN2iZbLDoo7C4KYljFJNJJ+fLshyn+9gozeTspVersWHxwdGB+Dm2hzA38s6muFnUTl0jK3vJm9ifQ==} + engines: {node: ^22.18.0 || >=24.11.0} + hasBin: true + peerDependencies: + '@arethetypeswrong/core': ^0.18.1 + '@tsdown/css': 0.22.14 + '@tsdown/exe': 0.22.14 + '@vitejs/devtools': '*' + publint: ^0.3.8 + tsx: '*' + typescript: ^5.0.0 || ^6.0.0 || ^7.0.0 + unplugin-unused: ^0.5.0 + unrun: '*' + peerDependenciesMeta: + '@arethetypeswrong/core': + optional: true + '@tsdown/css': + optional: true + '@tsdown/exe': + optional: true + '@vitejs/devtools': + optional: true + publint: + optional: true + tsx: + optional: true + typescript: + optional: true + unplugin-unused: + optional: true + unrun: + optional: true + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -6415,6 +6799,11 @@ packages: typed-query-selector@2.12.2: resolution: {integrity: sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==} + typescript@5.6.1-rc: + resolution: {integrity: sha512-E3b2+1zEFu84jB0YQi9BORDjz9+jGbwwy1Zi3G0LUNw7a7cePUrHMRNy8aPh53nXpkFGVHSxIZo5vKTfYaFiBQ==} + engines: {node: '>=14.17'} + hasBin: true + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -6448,6 +6837,9 @@ packages: uint8arrays@6.1.1: resolution: {integrity: sha512-iz7JN0XCSZYA111lhFG2Ui9EhFvTNekqSRHw3lvMHq+dzwWy1OQftxFQREEh4rffU0oSoXdQHsk2TiHKVm4fsA==} + unconfig-core@7.5.0: + resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} + undici-types@8.3.0: resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} @@ -6517,10 +6909,18 @@ packages: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} + validate-npm-package-name@5.0.1: + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + verkit@0.3.0: + resolution: {integrity: sha512-Njrh4U8UODGajoZ44QS2C/BsoEM9DTI/aCqY5swsizb+/ap0FamvnCMcZAxrR5+aoC0ZqkawEfpC/N2SBc+xeA==} + engines: {node: '>=18.12.0'} + vfile-message@4.0.3: resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} @@ -6828,6 +7228,15 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yuku-ast@0.8.0: + resolution: {integrity: sha512-trBzFsSa6k32vzNUCH6pFhAoTzWD/NifSYOIQ/6v14vXKh7TRhd2vDNIwRguGdXvcyfBNEEGcsfxFHrnJbS+FQ==} + + yuku-codegen@0.8.0: + resolution: {integrity: sha512-f82SDo8moLRymtdYN7/cz2yRbWE6Pmbmph+mLj24QkIR8ASG/c2nETdHzBhV/3rR/r8K+qmy7+JqQV3thHAm8g==} + + yuku-parser@0.8.0: + resolution: {integrity: sha512-obrazyE8Cyh79xTQS9wv44khnhvkXG1CDSSy0bg+Pjjla+iXkKXK2b6+LTYXSN3qvVfQxc0MN5qhNKYr9sl3Zw==} + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -6838,11 +7247,24 @@ snapshots: '@adobe/css-tools@4.5.0': {} + '@andrewbranch/untar.js@1.0.3': {} + '@antfu/install-pkg@1.1.0': dependencies: package-manager-detector: 1.8.0 tinyexec: 1.2.4 + '@arethetypeswrong/core@0.18.5': + dependencies: + '@andrewbranch/untar.js': 1.0.3 + '@loaderkit/resolve': 1.0.6 + cjs-module-lexer: 1.4.3 + fflate: 0.8.3 + lru-cache: 11.5.2 + semver: 7.8.5 + typescript: 5.6.1-rc + validate-npm-package-name: 5.0.1 + '@asamuzakjp/css-color@3.2.0': dependencies: '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) @@ -7042,6 +7464,8 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} + '@braidai/lang@1.1.2': {} + '@braintree/sanitize-url@7.1.2': {} '@chainsafe/as-chacha20poly1305@0.1.0': {} @@ -7830,6 +8254,10 @@ snapshots: uint8arraylist: 3.0.2 uint8arrays: 6.1.1 + '@loaderkit/resolve@1.0.6': + dependencies: + '@braidai/lang': 1.1.2 + '@mdx-js/mdx@3.1.1': dependencies: '@types/estree': 1.0.9 @@ -7947,6 +8375,8 @@ snapshots: '@oxc-project/types@0.139.0': {} + '@oxc-project/types@0.140.0': {} + '@oxlint/binding-android-arm-eabi@1.75.0': optional: true @@ -8036,6 +8466,10 @@ snapshots: '@polka/url@1.0.0-next.29': {} + '@publint/pack@0.1.6': + dependencies: + tinyexec: 1.2.4 + '@puppeteer/browsers@3.0.6(yauzl@3.4.0)': dependencies: modern-tar: 0.7.7 @@ -8043,42 +8477,82 @@ snapshots: optionalDependencies: yauzl: 3.4.0 + '@quansync/fs@1.0.0': + dependencies: + quansync: 1.0.0 + '@rolldown/binding-android-arm64@1.1.5': optional: true + '@rolldown/binding-android-arm64@1.2.0': + optional: true + '@rolldown/binding-darwin-arm64@1.1.5': optional: true + '@rolldown/binding-darwin-arm64@1.2.0': + optional: true + '@rolldown/binding-darwin-x64@1.1.5': optional: true + '@rolldown/binding-darwin-x64@1.2.0': + optional: true + '@rolldown/binding-freebsd-x64@1.1.5': optional: true + '@rolldown/binding-freebsd-x64@1.2.0': + optional: true + '@rolldown/binding-linux-arm-gnueabihf@1.1.5': optional: true + '@rolldown/binding-linux-arm-gnueabihf@1.2.0': + optional: true + '@rolldown/binding-linux-arm64-gnu@1.1.5': optional: true + '@rolldown/binding-linux-arm64-gnu@1.2.0': + optional: true + '@rolldown/binding-linux-arm64-musl@1.1.5': optional: true + '@rolldown/binding-linux-arm64-musl@1.2.0': + optional: true + '@rolldown/binding-linux-ppc64-gnu@1.1.5': optional: true + '@rolldown/binding-linux-ppc64-gnu@1.2.0': + optional: true + '@rolldown/binding-linux-s390x-gnu@1.1.5': optional: true + '@rolldown/binding-linux-s390x-gnu@1.2.0': + optional: true + '@rolldown/binding-linux-x64-gnu@1.1.5': optional: true + '@rolldown/binding-linux-x64-gnu@1.2.0': + optional: true + '@rolldown/binding-linux-x64-musl@1.1.5': optional: true + '@rolldown/binding-linux-x64-musl@1.2.0': + optional: true + '@rolldown/binding-openharmony-arm64@1.1.5': optional: true + '@rolldown/binding-openharmony-arm64@1.2.0': + optional: true + '@rolldown/binding-wasm32-wasi@1.1.5': dependencies: '@emnapi/core': 1.11.1 @@ -8086,12 +8560,25 @@ snapshots: '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) optional: true + '@rolldown/binding-wasm32-wasi@1.2.0': + dependencies: + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + optional: true + '@rolldown/binding-win32-arm64-msvc@1.1.5': optional: true + '@rolldown/binding-win32-arm64-msvc@1.2.0': + optional: true + '@rolldown/binding-win32-x64-msvc@1.1.5': optional: true + '@rolldown/binding-win32-x64-msvc@1.2.0': + optional: true + '@rolldown/pluginutils@1.0.1': {} '@rollup/pluginutils@5.4.0(rollup@4.62.2)': @@ -8862,6 +9349,74 @@ snapshots: '@xtuc/long@4.2.2': {} + '@yuku-codegen/binding-darwin-arm64@0.8.0': + optional: true + + '@yuku-codegen/binding-darwin-x64@0.8.0': + optional: true + + '@yuku-codegen/binding-freebsd-x64@0.8.0': + optional: true + + '@yuku-codegen/binding-linux-arm-gnu@0.8.0': + optional: true + + '@yuku-codegen/binding-linux-arm-musl@0.8.0': + optional: true + + '@yuku-codegen/binding-linux-arm64-gnu@0.8.0': + optional: true + + '@yuku-codegen/binding-linux-arm64-musl@0.8.0': + optional: true + + '@yuku-codegen/binding-linux-x64-gnu@0.8.0': + optional: true + + '@yuku-codegen/binding-linux-x64-musl@0.8.0': + optional: true + + '@yuku-codegen/binding-win32-arm64@0.8.0': + optional: true + + '@yuku-codegen/binding-win32-x64@0.8.0': + optional: true + + '@yuku-parser/binding-darwin-arm64@0.8.0': + optional: true + + '@yuku-parser/binding-darwin-x64@0.8.0': + optional: true + + '@yuku-parser/binding-freebsd-x64@0.8.0': + optional: true + + '@yuku-parser/binding-linux-arm-gnu@0.8.0': + optional: true + + '@yuku-parser/binding-linux-arm-musl@0.8.0': + optional: true + + '@yuku-parser/binding-linux-arm64-gnu@0.8.0': + optional: true + + '@yuku-parser/binding-linux-arm64-musl@0.8.0': + optional: true + + '@yuku-parser/binding-linux-x64-gnu@0.8.0': + optional: true + + '@yuku-parser/binding-linux-x64-musl@0.8.0': + optional: true + + '@yuku-parser/binding-win32-arm64@0.8.0': + optional: true + + '@yuku-parser/binding-win32-x64@0.8.0': + optional: true + + '@yuku-toolchain/types@0.8.0': {} + abbrev@4.0.0: optional: true @@ -8925,6 +9480,8 @@ snapshots: ansi-styles@6.2.3: {} + ansis@4.3.1: {} + any-signal@4.2.0: {} anymatch@3.1.3: @@ -9110,6 +9667,8 @@ snapshots: bytes@3.1.2: {} + cac@7.0.0: {} + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -9165,6 +9724,8 @@ snapshots: ci-info@4.4.0: {} + cjs-module-lexer@1.4.3: {} + cjs-module-lexer@2.2.0: {} cliui@6.0.0: @@ -9514,6 +10075,8 @@ snapshots: deepmerge@4.3.1: {} + defu@6.1.7: {} + delaunator@5.1.0: dependencies: robust-predicates: 3.0.3 @@ -9562,6 +10125,8 @@ snapshots: dotenv@17.4.2: {} + dts-resolver@3.0.0: {} + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -9584,6 +10149,8 @@ snapshots: emoji-regex@9.2.2: {} + empathic@2.0.1: {} + encodeurl@2.0.0: {} end-of-stream@1.4.5: @@ -9886,6 +10453,8 @@ snapshots: optionalDependencies: picomatch: 4.0.5 + fflate@0.8.3: {} + file-uri-to-path@1.0.0: {} finalhandler@2.1.1: @@ -9990,6 +10559,10 @@ snapshots: get-stream@6.0.1: {} + get-tsconfig@5.0.0-beta.5: + dependencies: + resolve-pkg-maps: 1.0.0 + getopts@2.3.0: {} github-from-package@0.0.0: {} @@ -10123,6 +10696,8 @@ snapshots: dependencies: '@types/hast': 3.0.5 + hookable@6.1.1: {} + html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 @@ -10176,6 +10751,8 @@ snapshots: import-meta-resolve@4.2.0: {} + import-without-cache@0.4.0: {} + imurmurhash@0.1.4: {} indent-string@4.0.0: {} @@ -10928,6 +11505,8 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@11.5.2: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -11564,6 +12143,8 @@ snapshots: it-queue: 1.1.4 main-event: 1.0.4 + mri@1.2.0: {} + mrmime@2.0.1: {} ms@2.1.2: {} @@ -11884,6 +12465,13 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 + publint@0.3.22: + dependencies: + '@publint/pack': 0.1.6 + package-manager-detector: 1.8.0 + picocolors: 1.1.1 + sade: 1.8.1 + pump@3.0.4: dependencies: end-of-stream: 1.4.5 @@ -11918,6 +12506,8 @@ snapshots: es-define-property: 1.0.1 side-channel: 1.1.1 + quansync@1.0.0: {} + race-event@1.6.1: dependencies: abort-error: 1.0.2 @@ -12114,6 +12704,8 @@ snapshots: resolve-from@5.0.0: {} + resolve-pkg-maps@1.0.0: {} + resolve-tsconfig@1.3.0(typescript@6.0.3): dependencies: typescript: 6.0.3 @@ -12127,6 +12719,20 @@ snapshots: robust-predicates@3.0.3: {} + rolldown-plugin-dts@0.27.14(rolldown@1.2.0)(typescript@6.0.3): + dependencies: + dts-resolver: 3.0.0 + get-tsconfig: 5.0.0-beta.5 + obug: 2.1.4 + rolldown: 1.2.0 + yuku-ast: 0.8.0 + yuku-codegen: 0.8.0 + yuku-parser: 0.8.0 + optionalDependencies: + typescript: 6.0.3 + transitivePeerDependencies: + - oxc-resolver + rolldown@1.1.5: dependencies: '@oxc-project/types': 0.139.0 @@ -12148,6 +12754,27 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.1.5 '@rolldown/binding-win32-x64-msvc': 1.1.5 + rolldown@1.2.0: + dependencies: + '@oxc-project/types': 0.140.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.2.0 + '@rolldown/binding-darwin-arm64': 1.2.0 + '@rolldown/binding-darwin-x64': 1.2.0 + '@rolldown/binding-freebsd-x64': 1.2.0 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.0 + '@rolldown/binding-linux-arm64-gnu': 1.2.0 + '@rolldown/binding-linux-arm64-musl': 1.2.0 + '@rolldown/binding-linux-ppc64-gnu': 1.2.0 + '@rolldown/binding-linux-s390x-gnu': 1.2.0 + '@rolldown/binding-linux-x64-gnu': 1.2.0 + '@rolldown/binding-linux-x64-musl': 1.2.0 + '@rolldown/binding-openharmony-arm64': 1.2.0 + '@rolldown/binding-wasm32-wasi': 1.2.0 + '@rolldown/binding-win32-arm64-msvc': 1.2.0 + '@rolldown/binding-win32-x64-msvc': 1.2.0 + rollup@4.62.2: dependencies: '@types/estree': 1.0.9 @@ -12200,6 +12827,10 @@ snapshots: rw@1.3.3: {} + sade@1.8.1: + dependencies: + mri: 1.2.0 + safe-buffer@5.2.1: {} safer-buffer@2.1.2: {} @@ -12628,6 +13259,8 @@ snapshots: dependencies: punycode: 2.3.1 + tree-kill@1.2.2: {} + trim-lines@3.0.1: {} trough@2.2.0: {} @@ -12699,6 +13332,34 @@ snapshots: resolve-tsconfig: 1.3.0(typescript@6.0.3) typescript: 6.0.3 + tsdown@0.22.14(@arethetypeswrong/core@0.18.5)(publint@0.3.22)(tsx@4.23.1)(typescript@6.0.3): + dependencies: + ansis: 4.3.1 + cac: 7.0.0 + defu: 6.1.7 + empathic: 2.0.1 + hookable: 6.1.1 + import-without-cache: 0.4.0 + obug: 2.1.4 + picomatch: 4.0.5 + rolldown: 1.2.0 + rolldown-plugin-dts: 0.27.14(rolldown@1.2.0)(typescript@6.0.3) + tinyexec: 1.2.4 + tinyglobby: 0.2.17 + tree-kill: 1.2.2 + unconfig-core: 7.5.0 + verkit: 0.3.0 + optionalDependencies: + '@arethetypeswrong/core': 0.18.5 + publint: 0.3.22 + tsx: 4.23.1 + typescript: 6.0.3 + transitivePeerDependencies: + - '@typescript/native-preview' + - '@volar/typescript' + - oxc-resolver + - vue-tsc + tslib@2.8.1: {} tsx@4.23.1: @@ -12725,6 +13386,8 @@ snapshots: typed-query-selector@2.12.2: {} + typescript@5.6.1-rc: {} + typescript@5.9.3: {} typescript@6.0.3: {} @@ -12758,6 +13421,11 @@ snapshots: dependencies: multiformats: 14.0.5 + unconfig-core@7.5.0: + dependencies: + '@quansync/fs': 1.0.0 + quansync: 1.0.0 + undici-types@8.3.0: {} undici@6.28.0: @@ -12863,8 +13531,12 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 + validate-npm-package-name@5.0.1: {} + vary@1.1.2: {} + verkit@0.3.0: {} + vfile-message@4.0.3: dependencies: '@types/unist': 3.0.3 @@ -13207,6 +13879,43 @@ snapshots: yocto-queue@0.1.0: {} + yuku-ast@0.8.0: + dependencies: + '@yuku-toolchain/types': 0.8.0 + + yuku-codegen@0.8.0: + dependencies: + '@yuku-toolchain/types': 0.8.0 + optionalDependencies: + '@yuku-codegen/binding-darwin-arm64': 0.8.0 + '@yuku-codegen/binding-darwin-x64': 0.8.0 + '@yuku-codegen/binding-freebsd-x64': 0.8.0 + '@yuku-codegen/binding-linux-arm-gnu': 0.8.0 + '@yuku-codegen/binding-linux-arm-musl': 0.8.0 + '@yuku-codegen/binding-linux-arm64-gnu': 0.8.0 + '@yuku-codegen/binding-linux-arm64-musl': 0.8.0 + '@yuku-codegen/binding-linux-x64-gnu': 0.8.0 + '@yuku-codegen/binding-linux-x64-musl': 0.8.0 + '@yuku-codegen/binding-win32-arm64': 0.8.0 + '@yuku-codegen/binding-win32-x64': 0.8.0 + + yuku-parser@0.8.0: + dependencies: + '@yuku-toolchain/types': 0.8.0 + yuku-ast: 0.8.0 + optionalDependencies: + '@yuku-parser/binding-darwin-arm64': 0.8.0 + '@yuku-parser/binding-darwin-x64': 0.8.0 + '@yuku-parser/binding-freebsd-x64': 0.8.0 + '@yuku-parser/binding-linux-arm-gnu': 0.8.0 + '@yuku-parser/binding-linux-arm-musl': 0.8.0 + '@yuku-parser/binding-linux-arm64-gnu': 0.8.0 + '@yuku-parser/binding-linux-arm64-musl': 0.8.0 + '@yuku-parser/binding-linux-x64-gnu': 0.8.0 + '@yuku-parser/binding-linux-x64-musl': 0.8.0 + '@yuku-parser/binding-win32-arm64': 0.8.0 + '@yuku-parser/binding-win32-x64': 0.8.0 + zod@3.25.76: {} zwitch@2.0.4: {} diff --git a/scripts/check-package-artifact.mjs b/scripts/check-package-artifact.mjs new file mode 100644 index 0000000000..34bce6d35e --- /dev/null +++ b/scripts/check-package-artifact.mjs @@ -0,0 +1,275 @@ +#!/usr/bin/env node + +import { execFile } from 'node:child_process' +import fs from 'node:fs/promises' +import os from 'node:os' +import path from 'node:path' +import process from 'node:process' +import { promisify } from 'node:util' +import { pathToFileURL } from 'node:url' + +const execFileAsync = promisify(execFile) +const COMMAND_TIMEOUT_MS = 180_000 +const MAX_BUFFER_BYTES = 20 * 1024 * 1024 + +function optionValue(arguments_, name, fallback = '') { + const index = arguments_.indexOf(name) + return index === -1 ? fallback : (arguments_[index + 1] ?? fallback) +} + +function csvOption(arguments_, name, fallback) { + return optionValue(arguments_, name, fallback) + .split(',') + .map(value => value.trim()) + .filter(Boolean) +} + +function commandError(error) { + const details = [error.stdout, error.stderr] + .map(value => value?.toString().trim()) + .filter(Boolean) + .join('\n') + return details || error.message +} + +async function run(command, arguments_, options = {}) { + try { + return await execFileAsync(command, arguments_, { + encoding: 'utf8', + maxBuffer: MAX_BUFFER_BYTES, + timeout: COMMAND_TIMEOUT_MS, + ...options + }) + } catch (error) { + throw new Error(`${command} ${arguments_.join(' ')} failed:\n${commandError(error)}`) + } +} + +function isDeclarationFile(file) { + return /\.d\.[cm]?ts$/i.test(file) +} + +function requiredFileErrors(files) { + return ['package.json', 'LICENSE.txt'].flatMap(requiredFile => + files.filter(file => file === requiredFile).length === 1 + ? [] + : [`tarball must contain exactly one root ${requiredFile}`] + ) +} + +function readmeErrors(files) { + return files.some(file => /^readme(?:\.[^.]+)?$/i.test(file)) + ? [] + : ['tarball must contain a root README'] +} + +function packedFileErrors(file) { + const errors = [] + if ( + /(^|\/)(?:__tests__|tests?|coverage)(?:\/|$)/i.test(file) || + /\.(?:spec|test)\.[cm]?[jt]sx?$/i.test(file) + ) { + errors.push(`tarball contains test artifact ${file}`) + } + if (/\.tsbuildinfo$/i.test(file)) { + errors.push(`tarball contains compiler cache ${file}`) + } + if (/\.[cm]?tsx?$/i.test(file) && !isDeclarationFile(file)) { + errors.push(`tarball contains uncompiled TypeScript source ${file}`) + } + if (/(^|\/)(?:package-lock|pnpm-lock|yarn\.lock)(?:\.json|\.yaml)?$/i.test(file)) { + errors.push(`tarball contains a package-manager lockfile ${file}`) + } + return errors +} + +function identityErrors(packResult, manifest) { + const errors = [] + if (packResult.name !== manifest.name) { + errors.push( + `tarball name ${JSON.stringify(packResult.name)} does not match ` + + JSON.stringify(manifest.name) + ) + } + if (packResult.version !== manifest.version) { + errors.push( + `tarball version ${JSON.stringify(packResult.version)} does not match ` + + JSON.stringify(manifest.version) + ) + } + return errors +} + +export function validatePackedFiles(packResult, manifest) { + const files = (packResult.files ?? []).map(file => file.path) + return [ + ...requiredFileErrors(files), + ...readmeErrors(files), + ...files.flatMap(packedFileErrors), + ...identityErrors(packResult, manifest) + ] +} + +async function packPackage(packageDirectory) { + const temporaryDirectory = await fs.mkdtemp(path.join(os.tmpdir(), 'ts-stack-package-artifact-')) + const { stdout } = await run( + 'pnpm', + ['pack', '--json', '--pack-destination', temporaryDirectory], + { + cwd: packageDirectory, + env: { + ...process.env, + npm_config_ignore_scripts: 'true' + } + } + ) + const result = JSON.parse(stdout) + const tarballPath = path.resolve(result.filename) + if (path.dirname(tarballPath) !== temporaryDirectory) { + throw new Error(`pnpm pack wrote outside its temporary directory: ${tarballPath}`) + } + return { result, tarballPath, temporaryDirectory } +} + +async function checkPublint(tarballPath) { + const [{ publint }, { formatMessage }] = await Promise.all([ + import('publint'), + import('publint/utils') + ]) + const buffer = await fs.readFile(tarballPath) + const tarball = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength) + const result = await publint({ + level: 'suggestion', + pack: { tarball }, + strict: true + }) + return result.messages + .filter(message => message.type === 'error') + .map(message => formatMessage(message, result.pkg, { color: false })) +} + +async function checkTypes(tarballPath) { + const [{ checkPackage, createPackageFromTarballData }, { problemKindInfo }] = await Promise.all([ + import('@arethetypeswrong/core'), + import('@arethetypeswrong/core/problems') + ]) + const tarball = new Uint8Array(await fs.readFile(tarballPath)) + const result = await checkPackage(createPackageFromTarballData(tarball)) + if (!result.types) { + throw new Error('@arethetypeswrong/core found no package types') + } + if (result.problems.length > 0) { + const problems = result.problems.map(problem => { + const title = problemKindInfo[problem.kind]?.title ?? problem.kind + return `${title}: ${JSON.stringify(problem)}` + }) + throw new Error( + `@arethetypeswrong/core found ${problems.length} strict type problem(s):\n` + + problems.join('\n') + ) + } +} + +function exportValidation(expectedExports) { + return [ + `const expected = ${JSON.stringify(expectedExports)};`, + 'for (const name of expected) {', + ' if (!(name in loaded) || loaded[name] === undefined) {', + ' throw new Error(`Missing public export ${name}`);', + ' }', + '}' + ].join('\n') +} + +async function checkConsumer(tarballPath, manifest, modes, expectedExports) { + const consumerDirectory = await fs.mkdtemp(path.join(os.tmpdir(), 'ts-stack-package-consumer-')) + try { + await fs.writeFile( + path.join(consumerDirectory, 'package.json'), + `${JSON.stringify({ private: true, type: 'module' }, null, 2)}\n` + ) + await run( + 'npm', + [ + 'install', + '--ignore-scripts', + '--no-audit', + '--no-fund', + '--package-lock=false', + '--omit=dev', + tarballPath + ], + { cwd: consumerDirectory } + ) + + const validation = exportValidation(expectedExports) + if (modes.includes('esm')) { + await run( + 'node', + [ + '--input-type=module', + '--eval', + `const loaded = await import(${JSON.stringify(manifest.name)});\n${validation}` + ], + { cwd: consumerDirectory } + ) + } + if (modes.includes('cjs')) { + await run( + 'node', + ['--eval', `const loaded = require(${JSON.stringify(manifest.name)});\n${validation}`], + { cwd: consumerDirectory } + ) + } + } finally { + await fs.rm(consumerDirectory, { recursive: true, force: true }) + } +} + +export async function checkPackageArtifact({ + packageDirectory, + modes = ['esm', 'cjs'], + expectedExports = [] +}) { + const manifestPath = path.join(packageDirectory, 'package.json') + const manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8')) + const packed = await packPackage(packageDirectory) + try { + const payloadErrors = validatePackedFiles(packed.result, manifest) + const publintErrors = await checkPublint(packed.tarballPath) + const errors = [...payloadErrors, ...publintErrors] + if (errors.length > 0) { + throw new Error(errors.join('\n')) + } + await checkTypes(packed.tarballPath) + await checkConsumer(packed.tarballPath, manifest, modes, expectedExports) + return manifest + } finally { + await fs.rm(packed.temporaryDirectory, { recursive: true, force: true }) + } +} + +async function main(arguments_) { + const target = arguments_[0] && !arguments_[0].startsWith('-') ? arguments_[0] : '.' + const packageDirectory = path.resolve(process.cwd(), target) + const modes = csvOption(arguments_, '--modes', 'esm,cjs') + const expectedExports = csvOption(arguments_, '--exports', '') + const manifest = await checkPackageArtifact({ + packageDirectory, + modes, + expectedExports + }) + console.log( + `Verified ${manifest.name}@${manifest.version}: packed payload, publint, ` + + `strict type resolution, and ${modes.join('/')} clean consumers.` + ) +} + +if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) { + try { + await main(process.argv.slice(2)) + } catch (error) { + console.error(error.message) + process.exitCode = 1 + } +} diff --git a/scripts/check-package-artifact.test.mjs b/scripts/check-package-artifact.test.mjs new file mode 100644 index 0000000000..efadf7247e --- /dev/null +++ b/scripts/check-package-artifact.test.mjs @@ -0,0 +1,57 @@ +import assert from 'node:assert/strict' +import test from 'node:test' + +import { validatePackedFiles } from './check-package-artifact.mjs' + +const manifest = { + name: '@bsv/example', + version: '1.2.3' +} + +test('packed artifact policy accepts a minimal compiled package', () => { + assert.deepEqual( + validatePackedFiles( + { + name: manifest.name, + version: manifest.version, + files: [ + { path: 'LICENSE.txt' }, + { path: 'README.md' }, + { path: 'dist/index.js' }, + { path: 'dist/index.d.ts' }, + { path: 'package.json' } + ] + }, + manifest + ), + [] + ) +}) + +test('packed artifact policy rejects source, tests, caches, locks, and identity drift', () => { + const errors = validatePackedFiles( + { + name: '@bsv/wrong', + version: '9.9.9', + files: [ + { path: 'LICENSE.txt' }, + { path: 'LICENSE.txt' }, + { path: 'src/index.ts' }, + { path: 'dist/index.test.js' }, + { path: 'dist/tsconfig.tsbuildinfo' }, + { path: 'package-lock.json' }, + { path: 'package.json' } + ] + }, + manifest + ) + + assert.ok(errors.some(error => error.includes('exactly one root LICENSE.txt'))) + assert.ok(errors.some(error => error.includes('root README'))) + assert.ok(errors.some(error => error.includes('uncompiled TypeScript'))) + assert.ok(errors.some(error => error.includes('test artifact'))) + assert.ok(errors.some(error => error.includes('compiler cache'))) + assert.ok(errors.some(error => error.includes('package-manager lockfile'))) + assert.ok(errors.some(error => error.includes('tarball name'))) + assert.ok(errors.some(error => error.includes('tarball version'))) +})