OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-note / lib / cli / client.js

90 lines · javascript ref:
/**
 * 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
  }
}