import { Client, type Response } from "../../http/mod.ts"; class Modem { constructor(readonly options: Deno.ConnectOptions | Deno.UnixConnectOptions, readonly client = new Client(options)) {} /** * Send a `POST` request to the Docker API. * * @param param.path - Path of the API endpoint. * @param param.query - Query parameters. * @param param.body - Request body. */ async post>({ path, query = {}, body }: RequestOptions): Promise { return getParsedResponse(await this.request({ method: "POST", path, query, body })); } /** * Send a `GET` request to the Docker API. * * @param param.path - Path of the API endpoint. * @param param.query - Query parameters. */ async get>({ path, query }: Omit): Promise { return getParsedResponse(await this.request({ method: "GET", path, query })); } /** * Send a `DELETE` request to the Docker API. * * @param param.path - Path of the API endpoint. * @param param.query - Query parameters. */ async del>({ path, query }: Omit): Promise { return getParsedResponse(await this.request({ method: "DELETE", path, query })); } /** * Send a fetch request to the Docker API. * * Note! When calling this method directly, ensure to call the .close() method on the response * or active connections may remain open causing dirty shutdown of services. Only when accessing * the .stream of the response through an async itterator is the connection automatically closed * when the itterator has completed. * * @param param.method - HTTP method to use. * @param param.path - Path of the API endpoint. * @param param.query - Query parameters. * @param param.body - Request body. _(Ignored for `GET` requests.)_ * @param param.headers - Headers to send with the request. */ async request( { method, path, query = {}, body, headers = {} }: { method: "POST" | "GET" | "DELETE" } & RequestOptions, ): Promise { return this.client.fetch(`http://docker${path}${toSearchParams(query)}`, { method, body, headers, }); } } export const modem = new Modem({ path: "/var/run/docker.sock", transport: "unix", }); /* |-------------------------------------------------------------------------------- | Utilities |-------------------------------------------------------------------------------- */ function getParsedResponse(res: Response): T { res.close(); if (res.status >= 400) { const error = res.json; assertError(error); throw new Error(error.message); } if (res.status === 204 || res.status === 304) { return {} as T; } return res.json as T; } function toSearchParams(query: Record): string { if (Object.keys(query).length === 0) { return ""; } const searchParams = new URLSearchParams(); for (const [key, value] of Object.entries(query)) { searchParams.append(key, String(value)); } return `?${searchParams.toString()}`; } function assertError(error: unknown): asserts error is { message: string } { if (typeof error !== "object" || error === null || !("message" in error)) { throw new Error("Docker Modem > Could not parse error response."); } } /* |-------------------------------------------------------------------------------- | Types |-------------------------------------------------------------------------------- */ type RequestOptions = { path: string; headers?: RequestInit["headers"]; query?: Record; body?: Record; };