@volverjs/data
Version:
Repository pattern implementation with a tiny HttpClient based on Fetch API.
223 lines (222 loc) • 8.92 kB
JavaScript
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
};