import {z} from 'zod' export type GetApiTransactionsArgs = {accountId?: string | undefined, baseType?: string | undefined, categoryId?: string | undefined, categoryType?: string | undefined, container?: string | undefined, detailCategoryId?: string | undefined, fromDate?: string | undefined, highLevelCategoryId?: string | undefined, keyword?: string | undefined, skip?: number | undefined, toDate?: string | undefined, top?: number | undefined, type?: string | undefined}; export type GetApiTransactionsOptions = {baseUrl: string, fetch?: typeof fetch | undefined}; export const description = z.object({security: z.string().optional(), original: z.string().optional(), simple: z.string().optional(), consumer: z.string().optional()}); export const money = z.object({amount: z.number(), currency: z.enum(["AUD", "BRL", "CAD", "EUR", "GBP", "HKD", "IDR", "INR", "JPY", "NZD", "SGD", "USD", "ZAR", "CNY", "VND", "MYR", "CHF"])}); export const accountAddress = z.object({zip: z.string().optional(), country: z.string().optional(), address3: z.string().optional(), address2: z.string().optional(), city: z.string().optional(), sourceType: z.string().optional(), address1: z.string().optional(), street: z.string().optional(), state: z.string().optional(), type: z.enum(["HOME", "BUSINESS", "POBOX", "RETAIL", "OFFICE", "SMALL_BUSINESS", "COMMUNICATION", "PERMANENT", "STATEMENT_ADDRESS", "PAYMENT", "PAYOFF", "UNKNOWN"]).optional()}); export const contact = z.object({phone: z.string().optional(), email: z.string().optional()}); export const coordinates = z.object({latitude: z.number().optional(), longitude: z.number().optional()}); export const merchant = z.object({website: z.string().optional(), address: accountAddress.optional(), contact: contact.optional(), categoryLabel: z.array(z.string()).optional(), coordinates: coordinates.optional(), name: z.string().optional(), id: z.string().optional(), source: z.enum(["YODLEE", "FACTUAL"]).optional()}); export const transaction = z.object({date: z.string().optional(), sourceId: z.string().optional(), symbol: z.string().optional(), cusipNumber: z.string().optional(), highLevelCategoryId: z.number().int().optional(), detailCategoryId: z.number().int().optional(), description: description.optional(), memo: z.string().optional(), settleDate: z.string().optional(), type: z.string().optional(), baseType: z.enum(["CREDIT", "DEBIT"]).optional(), categorySource: z.enum(["SYSTEM", "USER"]).optional(), principal: money.optional(), lastUpdated: z.string().optional(), interest: money.optional(), price: money.optional(), commission: money.optional(), id: z.number().int().optional(), amount: money.optional(), checkNumber: z.string().optional(), quantity: z.number().optional(), valoren: z.string().optional(), isManual: z.boolean().optional(), merchant: merchant.optional(), sedol: z.string().optional(), transactionDate: z.string().optional(), categoryType: z.enum(["TRANSFER", "DEFERRED_COMPENSATION", "UNCATEGORIZE", "INCOME", "EXPENSE"]).optional(), accountId: z.number().int().optional(), createdDate: z.string().optional(), sourceType: z.enum(["AGGREGATED", "MANUAL"]).optional(), CONTAINER: z.enum(["bank", "creditCard", "investment", "insurance", "loan", "reward", "realEstate", "otherAssets", "otherLiabilities"]).optional(), postDate: z.string().optional(), parentCategoryId: z.number().int().optional(), subType: z.enum(["AUTH_HOLD", "AUTH_REQUEST", "OVERDRAFT_CHARGE", "CREDIT_ADJUSTMENT", "PIN_DEBIT", "BANK_DIRECT_DEPOSIT", "DIVIDEND_DEPOSIT", "INTEREST_SAVINGS", "INTEREST_ADJUSTMENT", "ONLINE_PURCHASE", "PURCHASED_WITH_CHECK", "RECURRING_BILLING", "TAX_PAYMENT", "PAYMENT_BY_CHECK", "PAYMENT_PLAN", "FEE_REFUND", "WIRE_TRANSFER_CHARGE", "ACCOUNT_TO_ACCOUNT_TRANSFER", "BANK_TO_BANK_TRANSACTION", "BANK_TO_NON_BANK_ACCOUNT_TRANSFER", "CASH_WITHDRAWAL_AT_FI", "ATM_CASH_WITHDRAWAL", "AUTH_PROCESSING", "AUTH_RELEASE", "PRE_AUTH", "AUTH_COMPLETE", "AUTH_VOID", "BALANCE_ENQUIRY", "ACCOUNT_VERIFICATION", "PRE_AUTH_COMPLETION", "SERVICE_CHARGE", "SERVICE_CHARGE_FEE_REFUND", "RETURNED_CHECK_CHARGE", "RETURNED_CHECK_REIMBURSEMENT", "CASH_ADVANCE", "BILL_PAY_CHARGE", "CHECK_IMAGE_SERVICE_CHARGE", "OVERDRAFT_PROTECTION_CHARGE", "STOP_PAYMENT_CHARGE", "CHECKS_ORDERING_CHARGE", "MONTHLY_MAINTENANCE_CHARGE", "DEBIT_CARD_FEE", "CONVENIENCE_FEE", "PERSONAL_LOAN_CREDIT", "CREDIT_CARD_CREDIT", "AUTO_LOAN", "HOME_LOAN_MORTGAGE", "SHORT_TERM_CREDIT", "SIGNATURE_DEBIT", "CONTACT_LESS_DEBIT", "DEFERRED_DEPOSIT", "DEFERRED_BILL_PAY", "INSTALLMENT_PAYMENT", "RECURRING_SUBSCRIPTION_PAYMENT", "HOLD_CHECK_PAYMENT", "CAPITAL_GAINS_DISTIBUTION", "CG_LONG_TERM_DEPOSIT", "OPEN_SALE_DEPOSIT", "INTEREST__CHECK", "PURCHASE_VOID", "PURCHASE_WITH_CREDIT_CARD", "PURCHASE_WITH_DEBIT_CARD", "CHARGE_A_REPEAT_CUSTOMER", "DOWN_PAYMENT_OR_ANNUITY_PAYMENT_OR_DIRECT_PAYMENT", "FEE_PAYMENT", "FINANCE_CHARGE_REFUND", "TRANSACTION_VOID", "FEE_VOID", "DEBIT_CARD_WITHDRAWAL_AT_STORE", "ELECTRONIC_PAYMENT", "ACH_DEBIT", "ATM_TELLER_DEPOSIT", "POS_DEBIT", "BANK_ADJUSTMENT", "CHARGES_FEES", "INTEREST", "DEPOSITS_CREDITS", "PAYMENT", "PURCHASE", "REFUND", "TRANSFER", "WITHDRAWAL", "OTHER_DEPOSITS", "OTHER_WITHDRAWALS", "ADJUSTMENT", "FINANCE_CHARGE", "OTHER_CHARGES_FEES", "ANNUAL_FEE", "DEPOSIT", "DIRECT_DEPOSIT_SALARY", "INVESTMENT_INCOME_CASH", "SSA", "REWARDS", "CHECK_DEPOSIT", "MOBILE_REMOTE_DEPOSIT", "TELLER_DEPOSIT", "TAX_REFUND", "CREDIT_CARD_PAYMENT", "INSURANCE_PAYMENT", "UTILITIES_PAYMENT", "CHILD_SUPPORT", "LOAN", "PERSONAL_LOAN", "STUDENT_LOAN", "SALES_TAX", "REIMBURSEMENT", "BALANCE_TRANSFER", "WIRE_TRANSFER", "OVERDRAFT_PROTECTION", "DEBIT", "CREDIT", "NSF_FEES"]).optional(), category: z.string().optional(), runningBalance: money.optional(), categoryId: z.number().int().optional(), holdingDescription: z.string().optional(), isin: z.string().optional(), status: z.enum(["POSTED", "PENDING", "SCHEDULED", "FAILED", "CLEARED"]).optional()}); export const transactionResponse = z.object({transaction: z.array(transaction).optional()}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Get Transactions * * GET /transactions */ export const getApiTransactions = async (args: GetApiTransactionsArgs, options: GetApiTransactionsOptions) => { const {baseUrl, fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json;charset=UTF-8") if (args.accountId != null) query.append("accountId", String(args.accountId)) if (args.baseType != null) query.append("baseType", String(args.baseType)) if (args.categoryId != null) query.append("categoryId", String(args.categoryId)) if (args.categoryType != null) query.append("categoryType", String(args.categoryType)) if (args.container != null) query.append("container", String(args.container)) if (args.detailCategoryId != null) query.append("detailCategoryId", String(args.detailCategoryId)) if (args.fromDate != null) query.append("fromDate", String(args.fromDate)) if (args.highLevelCategoryId != null) query.append("highLevelCategoryId", String(args.highLevelCategoryId)) if (args.keyword != null) query.append("keyword", String(args.keyword)) if (args.skip != null) query.append("skip", String(args.skip)) if (args.toDate != null) query.append("toDate", String(args.toDate)) if (args.top != null) query.append("top", String(args.top)) if (args.type != null) query.append("type", String(args.type)) const url = `${baseUrl.replace(/\/+$/, '')}/transactions${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "GET", headers}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(transactionResponse, await res.json()) };