import { businessAppError, type RequestBusinessBinding, requestBusinessBinding, toBusinessError, } from "@conusai-kit/business" import type { Container, ProcedureKind } from "@conusai-kit/core" import { type Account, type Balance, type Entry, ledgerCreateAccount, ledgerGetAccount, ledgerGetBalances, ledgerGetTransaction, ledgerListAccounts, ledgerListEntries, ledgerListTransactions, ledgerPostTransaction, ledgerRecordTransaction, ledgerReverseTransaction, ledgerUpdateAccount, ledgerVerify, ledgerVoidTransaction, type Transaction, } from "@conusai-sdk/ledger" import { createClient } from "@conusai-sdk/ledger/client" import { accountView, balanceView, entryView, transactionView, verificationView } from "../contract/index.js" import { LEDGER_SERVICE, type LedgerReads, type LedgerService, type Page, readsOf } from "../core/ports.js" async function answer( operation: Promise<{ data?: T; error?: unknown; response?: Response }>, ): Promise> { let result: Awaited try { result = await operation } catch (error) { throw businessAppError(toBusinessError(error, undefined)) } if (result.error !== undefined || result.data === undefined) throw businessAppError(toBusinessError(result.error, result.response)) return result.data as Exclude } const account = (row: Account) => accountView.parse({ accountId: row.id, code: row.code, name: row.name, currency: row.currency, type: row.type, normalBalance: row.normal_balance, allowNegative: row.allow_negative, status: row.status, revision: row.revision.toString(), }) const balance = (row: Balance) => balanceView.parse({ accountId: row.account_id, currency: row.currency, posted: row.posted.toString(), pending: row.pending.toString(), available: row.available.toString(), }) const entry = (row: Entry) => entryView.parse({ entryId: row.id, transactionId: row.transaction_id, accountId: row.account_id, currency: row.currency, direction: row.direction, amountMinor: row.amount_minor.toString(), }) const transaction = (row: Transaction) => transactionView.parse({ transactionId: row.id, revision: row.revision.toString(), effectiveAt: row.effective_at, description: row.description, reference: row.reference ?? null, reverses: row.reverses ?? null, status: row.status, entries: row.entries.map(entry), }) const page = (result: { data: A[]; next_after?: string | null }, view: (row: A) => B): Page => ({ rows: result.data.map(view), ...(result.next_after ? { after: result.next_after } : {}), }) const query = (after?: string) => ({ limit: 50, ...(after ? { after } : {}) }) export function sdkLedgerService(binding: RequestBusinessBinding): LedgerService { const client = binding.client(createClient) const org = { org_slug: binding.orgSlug } return { async accounts(after) { return page(await answer(ledgerListAccounts({ client, path: org, query: query(after) })), account) }, async account(id) { return account(await answer(ledgerGetAccount({ client, path: { ...org, account_id: id } }))) }, async balances(id, after) { return page( await answer( ledgerGetBalances({ client, path: org, query: { ...query(after), ...(id ? { account_id: id } : {}) } }), ), balance, ) }, async entries(id, after) { return page( await answer(ledgerListEntries({ client, path: { ...org, account_id: id }, query: query(after) })), entry, ) }, async transactions(after) { return page(await answer(ledgerListTransactions({ client, path: org, query: query(after) })), transaction) }, async transaction(id) { return transaction(await answer(ledgerGetTransaction({ client, path: { ...org, transaction_id: id } }))) }, async verify() { const result = await answer(ledgerVerify({ client, path: org })) return verificationView.parse({ checkedAccounts: result.checked_accounts, drift: result.drift }) }, async createAccount(input, key) { return account( await answer( ledgerCreateAccount({ client, path: org, body: { code: input.code, name: input.name, currency: input.currency, type: input.type, normal_balance: input.normalBalance, allow_negative: input.allowNegative, idempotency_key: key, }, }), ), ) }, async updateAccount(id, input, revision, key) { return account( await answer( ledgerUpdateAccount({ client, path: { ...org, account_id: id }, body: { name: input.name, status: input.status, allow_negative: input.allowNegative, revision: BigInt(revision), idempotency_key: key, }, }), ), ) }, async record(terms, key) { return transaction( await answer( ledgerRecordTransaction({ client, path: org, body: { effective_at: terms.effectiveAt, description: terms.description, reference: terms.reference ?? null, status: terms.status, entries: terms.entries.map((row) => ({ account_id: row.accountId, direction: row.direction, amount_minor: BigInt(row.amountMinor), })), idempotency_key: key, }, }), ), ) }, async transition(action, id, revision, key) { const method = action === "post" ? ledgerPostTransaction : action === "void" ? ledgerVoidTransaction : ledgerReverseTransaction return transaction( await answer( method({ client, path: { ...org, transaction_id: id }, body: { revision: BigInt(revision), idempotency_key: key }, }), ), ) }, } } export function ledgerServiceOf(container: Container, kind: "effect"): LedgerService export function ledgerServiceOf(container: Container, kind: ProcedureKind): LedgerReads export function ledgerServiceOf(container: Container, kind: ProcedureKind): LedgerReads { const service = container.has(LEDGER_SERVICE) ? container.resolve(LEDGER_SERVICE) : sdkLedgerService(requestBusinessBinding(container)) return kind === "effect" ? service : readsOf(service) }