← Public packages
@noah/hey-email
HEY.com email integration with Kody: search threads, read messages/attachments, create drafts, and send with explicit confirmation.
src/client.js
90 lines · 2.6 KB · JavaScriptconst BASE = 'https://app.hey.com'
/**
* Resolve a request path against app.hey.com and refuse any other origin.
* Attachment URLs come from untrusted email HTML, so a credentialed fetch must
* never follow them off-site.
*
* @param {string} path
* @returns {string}
*/
function resolveHeyUrl(path) {
const raw = String(path || '')
const url = raw.startsWith('http')
? new URL(raw)
: new URL(`${BASE}${raw.startsWith('/') ? '' : '/'}${raw}`)
if (url.origin !== BASE) {
throw new Error(`Refusing to send HEY credentials to ${url.origin}`)
}
return url.toString()
}
/**
* Authenticated fetch to app.hey.com using the heyToken secret.
* @param {string} path
* @param {RequestInit} [init]
*/
export async function heyFetch(path, init = {}) {
const { data } = await heyRequest(path, init)
return data
}
/**
* Same as heyFetch but keeps status + response headers (Location on draft create).
* @param {string} path
* @param {RequestInit} [init]
* @returns {Promise<{ status: number, headers: Headers, data: any }>}
*/
export async function heyRequest(path, init = {}) {
const url = resolveHeyUrl(path)
const headers = new Headers(init.headers || {})
if (!headers.has('Authorization')) {
headers.set('Authorization', 'Bearer {{secret:heyToken}}')
}
if (!headers.has('Accept')) headers.set('Accept', 'application/json')
const res = await fetch(url, { ...init, headers })
const text = await res.text()
let data = text
try {
data = text ? JSON.parse(text) : null
} catch {
// keep text
}
if (!res.ok) {
const err = new Error(`HEY ${res.status} ${path}`)
err.status = res.status
err.body = typeof data === 'string' ? data.slice(0, 500) : data
throw err
}
return { status: res.status, headers: res.headers, data }
}
/**
* Authenticated binary fetch (PDF attachments). Follows redirects.
* @param {string} path
* @param {RequestInit} [init]
* @returns {Promise<Uint8Array>}
*/
export async function heyFetchBinary(path, init = {}) {
const url = resolveHeyUrl(path)
const headers = new Headers(init.headers || {})
if (!headers.has('Authorization')) {
headers.set('Authorization', 'Bearer {{secret:heyToken}}')
}
if (!headers.has('Accept')) headers.set('Accept', '*/*')
const res = await fetch(url, { ...init, headers, redirect: 'follow' })
if (!res.ok) {
const err = new Error(`HEY binary ${res.status} ${path}`)
err.status = res.status
try {
err.body = (await res.text()).slice(0, 500)
} catch {
err.body = null
}
throw err
}
const buf = await res.arrayBuffer()
return new Uint8Array(buf)
}
export { BASE, resolveHeyUrl }