/**
* hn HTTP client
*
* Wraps fetch() with auth headers and error handling
*/
import { getServer, getToken } from './config.js'
export class HnClient {
constructor (flags = {}) {
this.server = getServer(flags)
this.token = getToken(flags)
}
_headers (extra = {}) {
const headers = { 'Content-Type': 'application/json', ...extra }
if (this.token) {
headers.Authorization = `Bearer ${this.token}`
}
return headers
}
async _request (method, path, body = null, extra = {}) {
const url = this.server + path
const opts = { method, headers: this._headers(extra) }
if (body) opts.body = JSON.stringify(body)
let res
try {
res = await fetch(url, opts)
} catch (err) {
const isConnect = err.message.includes('ECONNREFUSED') || err.message.includes('connect')
if (isConnect) {
console.error(`Error: Could not connect to ${this.server}`)
console.error(` Is the server running? Start with: bare hypernote-server.js serve`)
console.error(` Or set server URL: hn config set server http://<host>:<port>`)
} else {
console.error(`Error: Network request failed — ${err.message}`)
}
process.exit(1)
}
if (!res.ok) {
let msg = res.statusText
try {
const data = await res.json()
if (data.error) msg = data.error
} catch {}
if (res.status === 401) {
console.error(`Error: Authentication required`)
console.error(` Set a token: hn config set token <your-token>`)
} else if (res.status === 403) {
console.error(`Error: Permission denied — ${msg}`)
} else if (res.status === 404) {
console.error(`Error: Not found — ${msg}`)
} else {
console.error(`Error (${res.status}): ${msg}`)
}
process.exit(1)
}
return res
}
async get (path, extra = {}) {
const res = await this._request('GET', path, null, extra)
return res.json()
}
async post (path, body, extra = {}) {
const res = await this._request('POST', path, body, extra)
return res.json()
}
async patch (path, body, extra = {}) {
const res = await this._request('PATCH', path, body, extra)
return res.json()
}
async del (path, extra = {}) {
const res = await this._request('DELETE', path, null, extra)
return res.json()
}
async stream (path) {
const res = await this._request('GET', path)
return res.body
}
}