UNPKG

@volverjs/data

Version:

Repository pattern implementation with a tiny HttpClient based on Fetch API.

223 lines (222 loc) 8.92 kB
var g = Object.defineProperty; var m = (d, t, s) => t in d ? g(d, t, { enumerable: !0, configurable: !0, writable: !0, value: s }) : d[t] = s; var n = (d, t, s) => m(d, typeof t != "symbol" ? t + "" : t, s); import { Hash as f } from "./Hash.js"; import { HTTPError as _ } from "ky"; import "qs"; class b { /** * @param client The HTTP client to use. * @param template The URL template to use for requests. * @param options The options to use. */ constructor(t, s, e) { n(this, "_client"); n(this, "_template"); n(this, "_responseAdapter", (t) => Array.isArray(t) ? t : [t]); n(this, "_requestAdapter", (t) => t); n(this, "_metadataAdapter", (t) => { let s; return t.headers.has("Content-Language") && (s = { contentLanguage: t.headers.get("Content-Language") }), t.headers.has("Accept-Language") && (s = { acceptLanguage: t.headers.get("Accept-Language") }), t.headers.has("X-Total-Count") && (s = { ...s, total: t.headers.get("X-Total-Count") }), s; }); n(this, "_hashFunction", f.cyrb53); n(this, "_readPendingRequests", /* @__PURE__ */ new Map()); n(this, "_httpClientOptions"); /** * @params params - The parameters to use in the request template URL or query. * @params options - The HTTP Client request options. * @returns A an object with the response promise and a function to abort the request. * @example * ```typescript * const repository = new RepositoryHttp(client, 'users/:type') * const { response, abort } = repository.read({ type: 'admin', page: 1 }) * const { data, item, metadata, ok } = await response * //=> GET /users/admin?page=1 * ``` */ n(this, "read", (t = {}, s = {}) => { const { key: e, ...o } = s; let r = e; if (r !== !1 && ((!r || typeof r == "boolean") && (r = this._hashFunction(JSON.stringify(t))), this._hasRepositoryHttpReadPendingRequest(r))) return this._cloneRepositoryHttpReadPendingRequest(r); const { responsePromise: a, abort: u, signal: i } = this._client.request( "get", this._requestUrl(t), this._requestOptions(o) ), c = (async () => { try { const h = await a, p = await h.json(), l = this._responseAdapter(p), R = this._metadataAdapter(h); return r !== !1 && this._deleteRepositoryHttpReadPendingRequest(r), { data: l, item: l == null ? void 0 : l[0], metadata: R, ok: h.ok }; } catch (h) { if (r !== !1 && this._deleteRepositoryHttpReadPendingRequest(r), !i.aborted && h instanceof _) throw h; return { ok: !1, aborted: !0, abortReason: i.reason }; } })(); return r === !1 ? { abort: u, responsePromise: c, signal: i } : this._setRepositoryHttpReadPendingRequest(r, { abort: u, responsePromise: c }); }); /** * @params payload - The payload to use in the request body. * @params params - The parameters to use in the request template URL or query. * @params options - The HTTP Client request options. * @returns A an object with the response promise and a function to abort the request. * @example * ```typescript * const repository = new RepositoryHttp(client, 'users/:type') * const payload = { name: 'John' } * const { response, abort } = repository.create(payload, { type: 'admin' }) * const { data, item, metadata, ok } = await response * //=> POST /users/admin * ``` */ n(this, "create", (t, s, e) => { const { responsePromise: o, abort: r, signal: a } = this._client.request( "post", this._requestUrl(s), this._requestOptions(e, t) ), u = (async () => { try { const i = await o, c = await i.json(), h = this._responseAdapter(c), p = this._metadataAdapter(i); return { data: h, item: h == null ? void 0 : h[0], metadata: p, ok: i.ok }; } catch (i) { if (!a.aborted) throw i; return { ok: !1, aborted: !0, abortReason: a.reason }; } })(); return { abort: r, responsePromise: u, signal: a }; }); /** * @params payload - The payload to use in the request body. * @params params - The parameters to use in the request template URL or query. * @params options - The HTTP Client request options. * @returns A an object with the response promise and a function to abort the request. * @example * ```typescript * const repository = new RepositoryHttp(client, 'users/:type/?:id') * const payload = { id: 1, name: 'John' } * const { response, abort } = repository.update(payload, { type: 'admin', id: 1 }) * const { data, item, metadata, ok } = await response * //=> PUT /users/admin/1 * ``` */ n(this, "update", (t, s, e) => { const { responsePromise: o, abort: r, signal: a } = this._client.request( "put", this._requestUrl(s), this._requestOptions(e, t) ), u = (async () => { try { const i = await o, c = await i.json(), h = this._responseAdapter(c), p = this._metadataAdapter(i); return { data: h, item: h == null ? void 0 : h[0], metadata: p, ok: i.ok }; } catch (i) { if (!a.aborted) throw i; return { ok: !1, aborted: !0, abortReason: a.reason }; } })(); return { abort: r, responsePromise: u, signal: a }; }); /** * @params params - The parameters to use in the request template URL or query. * @params options - The HTTP Client request options. * @returns A an object with the response promise and a function to abort the request. * @example * ```typescript * const repository = new RepositoryHttp(client, 'users/:type/?:id') * const { response, abort } = repository.delete({ type: 'admin', id: 1 }) * const { ok } = await response * //=> DELETE /users/admin/1 * ``` */ n(this, "remove", (t, s) => { const e = this._requestOptions(s), { responsePromise: o, abort: r, signal: a } = this._client.request( "delete", this._requestUrl(t), e ), u = (async () => { try { return { ok: (await o).ok }; } catch (i) { if (!a.aborted) throw i; return { ok: !1, aborted: !0, abortReason: a.reason }; } })(); return { abort: r, responsePromise: u, signal: a }; }); n(this, "_hasRepositoryHttpReadPendingRequest", (t) => t && this._readPendingRequests.has(t)); n(this, "_deleteRepositoryHttpReadPendingRequest", (t) => this._readPendingRequests.delete(t)); n(this, "_cloneRepositoryHttpReadPendingRequest", (t) => { const s = new AbortController(), e = this._readPendingRequests.get( t ); return e.count++, { responsePromise: new Promise((o, r) => { s.signal.addEventListener("abort", () => { e.count--, e.count === 0 && e.abort(s.signal.reason), o({ ok: !1, aborted: !0, abortReason: s.signal.reason }); }), e.responsePromise.then((...a) => { o(...a); }).catch((a) => { a instanceof _ && r(a); }); }), abort: (o) => s.abort(o), signal: s.signal }; }); n(this, "_setRepositoryHttpReadPendingRequest", (t, { abort: s, responsePromise: e }) => (this._readPendingRequests.set(t, { abort: s, responsePromise: e, count: 0 }), this._cloneRepositoryHttpReadPendingRequest(t))); n(this, "_requestUrl", (t = {}) => typeof this._template == "string" ? { template: this._template, params: t } : { ...this._template, params: { ...this._template.params, ...t } }); n(this, "_requestOptions", (t, s) => { const e = { ...this._httpClientOptions, ...t }; return s ? Array.isArray(s) ? (e.json = s.map((o) => this._requestAdapter(o)), e) : (e.json = this._requestAdapter(s), e) : e; }); if (this._client = t, this._template = s, e != null && e.httpClientOptions && (this._httpClientOptions = e.httpClientOptions), e != null && e.class && !(e != null && e.responseAdapter)) { const o = e.class; this._responseAdapter = (r) => Array.isArray(r) ? r.map((a) => new o(a)) : [new o(r)]; } e != null && e.responseAdapter && (this._responseAdapter = e.responseAdapter), e != null && e.requestAdapter && (this._requestAdapter = e.requestAdapter), e != null && e.metadataAdapter && (this._metadataAdapter = e.metadataAdapter), e != null && e.hashFunction && (this._hashFunction = e.hashFunction); } } export { b as RepositoryHttp };