npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@plasius/economy

v0.3.2

Published

Provider-neutral Token economy contracts and deterministic ledger invariants for Plasius

Readme

@plasius/economy

Provider-neutral TypeScript contracts and deterministic invariants for the Plasius sitewide Token economy.

This package models exact TokenSubunit amounts, immutable double-entry journal transactions, source lots, family gameplay reservations, early-backer basis, future spend requests, acquisition contracts, projections, and persistence ports. It intentionally contains no HTTP, authentication, provider SDK, database driver, secret, or Azure implementation.

Product boundary

  • 1 public Token = 1,000 TokenSubunits.
  • The nominal product reference is 1 Token = £0.10; it is not a redemption, transfer, investment, or cash-equivalence promise.
  • Amounts use bigint internally and canonical base-10 strings in contracts.
  • Tokens have no cash-redemption value and do not replace PP, ESP, TIS, or DIS.
  • Gameplay conversion, spending, and subscriptions are policy decisions owned by consuming services and are disabled in the baseline product.

Requirements

  • Node.js 24 or later
  • TypeScript with NodeNext-compatible module resolution

Install

npm install @plasius/economy

Exact amounts

import {
  parseTokenSubunits,
  serializeTokenSubunits,
  wholeTokensToSubunits,
} from "@plasius/economy";

const packGrant = wholeTokensToSubunits(50n);
const wireAmount = serializeTokenSubunits(packGrant); // "50000"
const restored = parseTokenSubunits(wireAmount); // 50000n

Non-canonical values such as "01", decimals, exponent notation, and values outside signed PostgreSQL bigint range are rejected. Runtime validators also reject non-string JSON values rather than coercing numbers into wire amounts.

ActivityEntryV1 contains both a stable source (shopify, ayet, bitlabs, subscription, event, competition, or adjustment) and a bounded display-oriented sourceLabel. Filters and reconciliation must use source; localized UI must use sourceLabel.

New read adapters should return the discriminated WalletActivityEntryV1. economic rows may be held, settled, or read-model-derived reversed; workflow rows alone may be pending or failed. The additive EconomicJournalTransactionV1 narrows immutable journal writes to held or settled. A reversed display state is derived from a compensating transaction; the original journal row is never rewritten.

Baseline GBP catalog

BASELINE_GBP_REFERENCE_RATE is versioned product-copy metadata with cashRedemptionAllowed: false. assertFlatTokenCatalog() proves that a catalog uses the declared flat nominal ratio. The initial immutable catalog facts are:

| Pack ID | Price | Grant | |---|---:|---:| | gbp_5_50_v1 | £5 | 50 Tokens | | gbp_10_100_v1 | £10 | 100 Tokens | | gbp_25_250_v1 | £25 | 250 Tokens | | gbp_50_500_v1 | £50 | 500 Tokens |

The default server ceilings are £50 per order and £100 per payer and household over 30 days. A consuming service may apply a lower household control but must not raise these defaults without a separately versioned policy decision.

Balanced journal transactions

import {
  assertBalancedTransaction,
  canonicalTransactionPayload,
  serializeTokenSubunits,
  type LedgerTransactionV1,
} from "@plasius/economy";

const transaction: LedgerTransactionV1 = {
  schemaVersion: "1",
  transactionId: "txn:purchase:1",
  activityType: "purchase",
  status: "settled",
  idempotencyKey: "intent:1:paid",
  effectiveAt: "2026-07-15T10:00:00.000Z",
  recordedAt: "2026-07-15T10:00:01.000Z",
  metadata: { catalogVersion: "gbp-v1" },
  postings: [
    {
      schemaVersion: "1",
      postingId: "post:1",
      transactionId: "txn:purchase:1",
      accountId: "account:purchase-clearing",
      amount: serializeTokenSubunits(-50_000n),
    },
    {
      schemaVersion: "1",
      postingId: "post:2",
      transactionId: "txn:purchase:1",
      accountId: "account:household-treasury",
      lotId: "lot:shopify:1",
      amount: serializeTokenSubunits(50_000n),
    },
  ],
};

assertBalancedTransaction(transaction);
const payloadForApprovedHashAdapter = canonicalTransactionPayload(transaction);

The package produces canonical bytes but deliberately leaves SHA-256/HSM signing to an approved infrastructure adapter. Metadata keys and posting IDs are ordered with locale-independent UTF-16 code-unit comparison. Their validated ASCII alphabet makes that ordering identical to PostgreSQL COLLATE "C"; adapters must hash the exact UTF-8 bytes returned by canonicalTransactionPayload().

Source-lot policy and allocations

selectSourceLots() selects spendable slices in credited-time/lot-ID order and enforces household-allocatable, same-user-only, and non-transferable policies. createGameplayAllocation(), boostGameplayAllocation(), and reclaimGameplayAllocation() require exact whole Tokens and return new immutable states.

Selection is only a deterministic proposal. The persistence adapter must lock the source rows, revalidate them, append the balanced transaction, update the projection, save idempotency evidence, and append the outbox event within one serializable database transaction.

VersionedSourceLotV1 and SourceLotMovementV1 make allocation, boost, reclaim, spend, hold, release, refund, chargeback, and reversal changes explicit. applySourceLotMovement() proves the signed amount deltas, refund-state transition, and exact optimistic-version increment. A V2 persistence adapter must append the movement evidence and compare-and-swap the projection as one operation.

Early backers

evaluateEarlyBacker() calculates provisional pre_utility_backer_v1 status from net retained paid lots. It uses an inclusive public launch and exclusive first-public-spend cutoff. It does not express or promise a reward entitlement.

Existing evaluateEarlyBacker() behavior is preserved for V1 consumers. The approved additive evaluateEarlyBackerBySettlementV2() policy treats settledAt as the sole inclusive-launch/exclusive-cutoff qualification event; purchase and credit timestamps remain ordering/provenance facts. It ignores settlements after evaluatedAt. The caller must derive both window timestamps from the entire public cohort and exclude non-production/test lots: staff, closed-beta, and test availability must never open or close the public window.

Acquisition and future contracts

Purchase intents bind payer, receiving household/wallet, pack, catalog, expected GBP price, grant, and an expiry. assertOpenPurchaseIntent() is for checkout creation; a late provider webhook must instead be reconciled against the immutable purchase facts and provider purchase time.

Reward conversion contracts retain signed provider-event IDs, server rate/FX versions, exact payout values, and beneficiary/wallet IDs. ayeT and BitLabs lots are structurally restricted to same-user-only; changing a provider-earned lot to household-allocatable is rejected.

Deterministic paid-acquisition lifecycle

PurchaseIntentLifecycleV1 adds receipts and optimistic versions around the unchanged PurchaseIntentV1. reducePurchaseIntentTransitions() canonicalizes out-of-order checkout, payment, credit, expiry, cancellation, and dispute evidence. Exact retries collapse, conflicting ID reuse fails, and one intent can emit at most one stable intent-ID-scoped credit instruction. The transition name is checkout-bound; the published compatible intent status remains checkout-created.

reserveRollingPurchaseCaps() mirrors one exact GBP-minor-unit reservation into payer and household aggregates. Both expected versions must match and both returned versions must be committed in one serializable transaction. Settlement keeps the amount in the rolling window; release and expiry free it. Exact command replays are no-ops, including after finalization. GBP minor units use canonical non-negative signed-64-bit strings through parseGbpMinorUnits() and serializeGbpMinorUnits().

PaidLotLifecycleV1 separately preserves early-backer retained basis:

  • a dispute hold leaves retained basis unchanged;
  • a dispute win releases only the hold;
  • refunds, lost disputes, direct chargebacks, and the one permitted reversal reduce retained basis exactly;
  • partial outcomes retain their unreversed remainder;
  • createEarlyBackerRetentionFromPaidLot() supplies a current PaidLotRetentionV1 for recalculation.

Retained basis is not a spendable-balance claim. Before a compensation commits, the authoritative service must atomically reclaim allocated-but-unused value, reject value already spent or otherwise unavailable, append balanced journal postings, update source-lot/lifecycle projections, save idempotency evidence, and append the outbox. This package contains none of those provider or persistence adapters.

These contracts do not enable checkout. The site must still enforce economy.tokens.shopify.enabled, payer/household authorization, legal gates, provider-signature verification, and server-derived purchase facts.

BASELINE_MONTHLY_SUBSCRIPTION_PLAN preserves the provider-neutral future £10 monthly/100 Token shape with enabled: false. Spend-request contracts likewise exist for future use but this package does not enable their creation.

Balance, lifetime, and portfolio reads

WalletBalanceProjectionV1 stores exclusive spendable, reserved, and held buckets. createWalletBalanceSummary() splits each wallet's spendable amount into independently usable whole-Token available value and a sub-Token rewardProgress remainder. WalletBalanceDeltaV1 is an atomic add, never an absolute balance replacement.

Lifetime counters are monotonic gross flows. Settled purchase/subscription credits add to bought; rewarded-ad, offerwall, event, and competition credits add to earned; source-wallet allocation/boost debits add to allocated; returns add to reclaimed; spend debits add to spent; and refund, chargeback, or reversal debits add to reversed. A reversal does not subtract from the earlier bought/earned counter.

EconomyQueryPortV1 keeps single-wallet reads and introduces an explicit WalletPortfolioReadScopeV1. A host portfolio may contain both a household treasury and a same-user-only personal reward wallet, and every component keeps its wallet ID, role, beneficiary, summary, and lifetime snapshot. Portfolio totals are display-only column sums: progress is deliberately not promoted across wallets, even if the sum reaches 1,000 TokenSubunits, because source and transfer restrictions may differ. Authorization must happen before the server constructs a portfolio scope.

Activity reads use bounded opaque cursor pagination in stable descending (occurredAt, activityId) order. Each row retains the component walletId whose signed display amount it represents. Cursor contents and signatures are adapter concerns; assertWalletActivityPageForPortfolio() proves that a result never expands the authorized portfolio scope.

Persistence ports

EconomyPersistencePort remains exported unchanged for existing consumers. New authoritative services should implement EconomyPersistencePortV2, whose unit of work deliberately omits V1's whole-projection overwrite. Runtime roles should execute approved posting procedures but must not update or delete journal rows directly. The V2 adapter is responsible for:

  • serializable row locking and optimistic versions;
  • exact owner-constrained wallet access and household/child-constrained allocation compare-and-swap;
  • immutable accepted-command and workflow-event append;
  • actor/subject/command-scoped idempotency plus unique transaction and provider-event constraints;
  • active regional writer-fence validation and locked canonical chain-head extension;
  • atomic wallet balance and monotonic lifetime deltas;
  • atomic source-lot movement append, refund-state update, and version advance;
  • transactional outbox append;
  • managed identity and least-privilege database access; and
  • immutable audit/integrity evidence outside this package.

Development

npm ci
npm run lint
npm run typecheck
npm run build
npm run test:coverage
npm run pack:check

Coverage must remain at least 80%, and every changed source file must appear in LCOV. Generated property tests cover exact arithmetic, double-entry balance, projection rebuilds, idempotency, reversals, lot isolation, and allocation non-negativity. npm publication is performed only by the approved GitHub CD workflow.

Security

Do not include raw payment details, personal data, provider callback bodies, or secrets in contracts, metadata, tests, examples, or logs. Report vulnerabilities privately according to SECURITY.md.