/** * @pwngh/economy-lab * * Copyright (c) Preston Neal * * This source code is licensed under the MIT license found in the * LICENSE.md file in the root directory of this source tree. * * @license MIT */ import type { Amount } from './money.js'; import type { Range } from './ports.js'; /** * Distinguishes money in (a user buying credits) from money out (a payout). The two kinds * reconcile separately, so a buy never matches a payout. */ export type ReconcileKind = 'buy' | 'payout'; /** * Represents one settled event as the processor reports it, meaning money it says cleared. * The reconciler matches it against the ledger's record of the same event. */ export interface ProcessorRecord { kind: ReconcileKind; matchKey: string; amount: Amount; providerRef: string; settledAt: number; } export interface LedgerRecord { kind: ReconcileKind; matchKey: string; amount: Amount; txnId: string; postedAt: number; } /** * Names the ways the two sides can disagree. * * - processor_orphan: the processor cleared money with no matching ledger entry. Real money * moved but nothing is on our books. * - ledger_orphan: a ledger entry the processor never cleared. This is either stuck money * or an entry that should not exist. * - amount_drift: both sides exist for the event but the amounts differ. */ export type DiscrepancyKind = 'processor_orphan' | 'ledger_orphan' | 'amount_drift'; /** * Describes one mismatch found. The amount fields are decimal strings such as * `'CREDIT:12.34'` rather than raw `bigint`, because `JSON.stringify` cannot serialize a * `bigint` but strings round-trip through JSON unchanged. * * Which amount fields are present depends on `kind`. A processor orphan has only * `processorAmount`, a ledger orphan has only `ledgerAmount`, and an amount drift has both. */ export interface Discrepancy { kind: DiscrepancyKind; matchKey: string; recordKind: ReconcileKind; processorAmount?: string; ledgerAmount?: string; } /** * Holds the result of reconciling one window. `discrepancies` lists every mismatch in * stable sorted order, so the same inputs always produce an identical report, and the count * fields summarize that list. `reconciled` is true when no mismatch was found. */ export interface ReconcileReport { window: Range; reconciled: boolean; matched: number; processorCount: number; ledgerCount: number; processorOrphans: number; ledgerOrphans: number; amountDrifts: number; discrepancies: ReadonlyArray; } export interface ReconcileInputs { processor: ReadonlyArray; ledger: ReadonlyArray; } /** * Compares processor records against ledger records for one window and reports mismatches. The * caller may over-supply; only records inside the half-open window count (a record exactly on `to` * belongs to the next window). Two records match on the same kind and matchKey. Every record must be * accounted for exactly once and the reconciler must not drop one, so `report` self-checks * that the counts reconstruct both sides. * * @see {@link https://economy-lab-docs.pages.dev/economy/reference/background-worker/ Background * worker} for how reconciliation runs on a schedule. */ export declare function reconcile(window: Range, inputs: ReconcileInputs): ReconcileReport;