import {
accessTokenForPreview,
accessTokenSetupUrl,
mutationPreview,
normalizeItem,
plaidRequest,
requireString,
resolveAccessTokenSecretName,
type MutationGuardInput,
type PlaidAuthInput,
} from './plaid-core.ts'
export type ItemInput = PlaidAuthInput
/**
* Retrieve Item status (`/item/get`). Needs an Item access token, never a
* bank password.
*/
export async function getItem(input: ItemInput = {}) {
const data = await plaidRequest({
...input,
path: '/item/get',
includeAccessToken: true,
})
return {
item: normalizeItem(data?.item),
status: data?.status ?? null,
request_id: data?.request_id ?? null,
}
}
export type ExchangePublicTokenInput = MutationGuardInput & {
publicToken?: string
}
/**
* Exchange a short-lived `public_token` from Link (or sandbox create) for an
* `access_token`. Defaults to dry-run; live exchange needs `confirm: true`.
* Save the returned access_token as `plaidAccessToken` — do not paste bank
* passwords into chat.
*/
export async function exchangePublicToken(input: ExchangePublicTokenInput = {}) {
const publicToken = input.confirm === true && input.dryRun !== true ? requireString(input.publicToken, 'publicToken') : (input.publicToken ?? '<public_token>')
const preview = mutationPreview(input, {
action: 'exchange public token',
method: 'POST',
path: '/item/public_token/exchange',
body: { public_token: publicToken },
})
if (preview) return preview
const data = await plaidRequest({
...input,
path: '/item/public_token/exchange',
body: { public_token: requireString(input.publicToken, 'publicToken') },
})
return {
access_token: data?.access_token ?? null,
item_id: data?.item_id ?? null,
request_id: data?.request_id ?? null,
saveAccessToken: accessTokenSetupUrl(resolveAccessTokenSecretName(input)),
note: 'Save access_token as a Kody secret. Never store a bank login password.',
}
}
export type RemoveItemInput = MutationGuardInput
/** Remove an Item (`/item/remove`). Defaults to dry-run; live needs confirm. */
export async function removeItem(input: RemoveItemInput = {}) {
const preview = mutationPreview(input, {
action: 'remove item',
method: 'POST',
path: '/item/remove',
body: { access_token: accessTokenForPreview(input) },
})
if (preview) return preview
const data = await plaidRequest({
...input,
path: '/item/remove',
includeAccessToken: true,
})
return {
removed: Boolean(data?.removed ?? true),
request_id: data?.request_id ?? null,
}
}
export type UpdateItemWebhookInput = MutationGuardInput & {
webhook?: string
}
/** Update the Item webhook URL. Defaults to dry-run; live needs confirm. */
export async function updateItemWebhook(input: UpdateItemWebhookInput = {}) {
const webhook =
input.confirm === true && input.dryRun !== true
? requireString(input.webhook, 'webhook')
: (input.webhook ?? '<webhook_url>')
const preview = mutationPreview(input, {
action: 'update item webhook',
method: 'POST',
path: '/item/webhook/update',
body: { access_token: accessTokenForPreview(input), webhook },
})
if (preview) return preview
const data = await plaidRequest({
...input,
path: '/item/webhook/update',
includeAccessToken: true,
body: { webhook: requireString(input.webhook, 'webhook') },
})
return {
item: normalizeItem(data?.item),
request_id: data?.request_id ?? null,
}
}
export default getItem