← Public packages
@kentcdodds/wise
Call Wise profiles, balances, quotes, recipients, and transfers with wiseApiToken.
src/core.ts
78 lines · 2.5 KB · TypeScriptconst wiseApiToken = '{{secret:wiseApiToken|scope=user}}'
const wiseApiBase = 'https://api.wise.com'
export function clean(value: unknown) {
return String(value ?? '').trim()
}
export async function parseJsonResponse(response: Response, label = 'Wise API') {
const text = await response.text()
let body: unknown = null
try {
body = text ? JSON.parse(text) : null
} catch {
body = { raw: text }
}
if (!response.ok) {
throw new Error(
label + ' HTTP ' + response.status + ': ' + JSON.stringify(body).slice(0, 800),
)
}
return body
}
export type WiseRequestInput = {
method?: string
path: string
query?: Record<string, unknown>
body?: unknown
label?: string
}
/**
* Make one authenticated Wise REST request with wiseApiToken.
* Prefer the typed exports (list-profiles, create-quote, …) over calling this directly.
*/
export async function wiseRequest(input: WiseRequestInput) {
const path = clean(input.path)
if (!path.startsWith('/')) throw new Error('path must start with /')
const url = new URL(path, wiseApiBase)
for (const [key, value] of Object.entries(input.query || {})) {
if (value === undefined || value === null || value === '') continue
url.searchParams.set(key, String(value))
}
const method = clean(input.method || 'GET').toUpperCase() || 'GET'
const headers: Record<string, string> = {
Authorization: 'Bearer ' + wiseApiToken,
Accept: 'application/json',
}
let body: string | undefined
if (input.body !== undefined && method !== 'GET' && method !== 'HEAD') {
headers['Content-Type'] = 'application/json'
body = JSON.stringify(input.body)
}
const response = await fetch(url, { method, headers, body })
return await parseJsonResponse(response, input.label || 'Wise API')
}
export type ProfileRef = {
profileId?: number | string
/** When profileId is omitted, pick the first profile of this type (default business). */
profileType?: 'personal' | 'business'
}
export async function resolveProfileId(input: ProfileRef = {}) {
if (input.profileId !== undefined && input.profileId !== null && clean(input.profileId)) {
const id = Number(input.profileId)
if (!Number.isFinite(id)) throw new Error('profileId must be a number')
return id
}
const profiles = (await wiseRequest({
path: '/v1/profiles',
label: 'Wise profiles',
})) as Array<{ id: number; type?: string }>
const wanted = input.profileType || 'business'
const hit = profiles.find((p) => p.type === wanted) || profiles[0]
if (!hit?.id) throw new Error('No Wise profiles available for this token')
return hit.id
}