@koine/api 1.0.51 → 1.0.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ApiError.d.ts +11 -0
- package/ApiError.js +20 -0
- package/core/index.js +12 -13
- package/createApi.d.ts +8 -0
- package/createApi.js +127 -0
- package/createSwrApi.d.ts +14 -0
- package/createSwrApi.js +83 -0
- package/index.d.ts +4 -3
- package/index.js +4 -3
- package/nextApiResponse.d.ts +3 -0
- package/nextApiResponse.js +5 -0
- package/node/ApiError.js +23 -0
- package/node/{core/index.js → createApi.js} +48 -59
- package/node/{swr/index.js → createSwrApi.js} +7 -6
- package/node/index.js +4 -3
- package/node/{next/index.js → nextApiResponse.js} +1 -0
- package/package.json +3 -3
- package/swr/index.js +1 -1
- package/typings.d.ts +147 -85
package/ApiError.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom `ApiError` class extending `Error` to throw in failed response.
|
|
3
|
+
*
|
|
4
|
+
* @see https://eslint.org/docs/rules/no-throw-literal
|
|
5
|
+
* @see https://github.com/sindresorhus/ky/blob/main/source/errors/HTTPError.ts
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
export declare class ApiError<TResponseFail extends Koine.Api.ResponseFail = unknown> extends Error {
|
|
9
|
+
constructor(result: Koine.Api.ResultFail<TResponseFail>);
|
|
10
|
+
}
|
|
11
|
+
export default ApiError;
|
package/ApiError.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { __extends } from "tslib";
|
|
2
|
+
/**
|
|
3
|
+
* Custom `ApiError` class extending `Error` to throw in failed response.
|
|
4
|
+
*
|
|
5
|
+
* @see https://eslint.org/docs/rules/no-throw-literal
|
|
6
|
+
* @see https://github.com/sindresorhus/ky/blob/main/source/errors/HTTPError.ts
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
var ApiError = /** @class */ (function (_super) {
|
|
10
|
+
__extends(ApiError, _super);
|
|
11
|
+
function ApiError(result) {
|
|
12
|
+
var _this = _super.call(this, "Request failed with ".concat(result.status, " ").concat(result.msg)) || this;
|
|
13
|
+
_this.name = "ApiError";
|
|
14
|
+
Object.assign(_this, result);
|
|
15
|
+
return _this;
|
|
16
|
+
}
|
|
17
|
+
return ApiError;
|
|
18
|
+
}(Error));
|
|
19
|
+
export { ApiError };
|
|
20
|
+
export default ApiError;
|
package/core/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __assign, __awaiter, __extends, __generator } from "tslib";
|
|
2
|
-
import { buildUrlQueryString } from "@koine/utils";
|
|
2
|
+
import { buildUrlQueryString, isFullObject } from "@koine/utils";
|
|
3
3
|
/**
|
|
4
4
|
* Custom `ApiError` class extending `Error` to throw in failed response.
|
|
5
5
|
*
|
|
@@ -25,24 +25,24 @@ export { ApiError };
|
|
|
25
25
|
* @param baseUrl Either relativ eor absolute, it must end without trailing slash
|
|
26
26
|
*/
|
|
27
27
|
export var createApi = function (apiName, baseUrl, options) {
|
|
28
|
-
var _a = options || {},
|
|
28
|
+
var _a = options || {}, _b = _a.headers, headersBase = _b === void 0 ? {} : _b, _c = _a.request, requestBase = _c === void 0 ? {
|
|
29
29
|
credentials: "include",
|
|
30
30
|
referrerPolicy: "no-referrer",
|
|
31
31
|
// mode: "cors",
|
|
32
32
|
// redirect: "follow",
|
|
33
33
|
// cache: "no-cache",
|
|
34
|
-
} :
|
|
34
|
+
} : _c, shouldThrowBase = _a.shouldThrow, _d = _a.timeout, timeoutBase = _d === void 0 ? 10000 : _d, transformRequestBase = _a.transformRequest, transformResponseBase = _a.transformResponse;
|
|
35
35
|
return ["get", "post", "put", "patch", "delete"].reduce(function (api, method) {
|
|
36
36
|
api[method] = function (endpoint, options) { return __awaiter(void 0, void 0, void 0, function () {
|
|
37
|
-
var _a,
|
|
37
|
+
var _a, params, json, query, _b, request, _c, headers, _d, timeout, _e, transformRequest, _f, transformResponse, _g, shouldThrow, requestInit, key, timeoutNumber, controller, timeoutId, url, response, e_1, result, e_2, msg;
|
|
38
38
|
return __generator(this, function (_h) {
|
|
39
39
|
switch (_h.label) {
|
|
40
40
|
case 0:
|
|
41
|
-
_a = options || {},
|
|
41
|
+
_a = options || {}, params = _a.params, json = _a.json, query = _a.query, _b = _a.request, request = _b === void 0 ? requestBase : _b, _c = _a.headers, headers = _c === void 0 ? headersBase : _c, _d = _a.timeout, timeout = _d === void 0 ? timeoutBase : _d, _e = _a.transformRequest, transformRequest = _e === void 0 ? transformRequestBase : _e, _f = _a.transformResponse, transformResponse = _f === void 0 ? transformResponseBase : _f, _g = _a.shouldThrow, shouldThrow = _g === void 0 ? shouldThrowBase : _g;
|
|
42
42
|
requestInit = __assign(__assign({ method: method.toUpperCase() }, request), { headers: __assign({ "content-type": "application/json" }, headers) });
|
|
43
|
-
if (
|
|
44
|
-
for (key in
|
|
45
|
-
endpoint = endpoint.replace("{".concat(key, "}"),
|
|
43
|
+
if (isFullObject(params)) {
|
|
44
|
+
for (key in params) {
|
|
45
|
+
endpoint = endpoint.replace("{".concat(key, "}"), params[key].toString());
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
timeoutNumber = Number(timeout);
|
|
@@ -58,10 +58,9 @@ export var createApi = function (apiName, baseUrl, options) {
|
|
|
58
58
|
if (transformRequest) {
|
|
59
59
|
requestInit = transformRequest(requestInit);
|
|
60
60
|
}
|
|
61
|
-
if (
|
|
61
|
+
if (query) {
|
|
62
62
|
// FIXME: ts-expect-error this assertion is not the best, but nevermind for now
|
|
63
|
-
|
|
64
|
-
url += buildUrlQueryString(params);
|
|
63
|
+
url += buildUrlQueryString(query);
|
|
65
64
|
}
|
|
66
65
|
if (!shouldThrow) return [3 /*break*/, 5];
|
|
67
66
|
_h.label = 1;
|
|
@@ -123,10 +122,10 @@ export var createApi = function (apiName, baseUrl, options) {
|
|
|
123
122
|
if (process.env["NODE_ENV"] !== "production") {
|
|
124
123
|
msg = "".concat(result.status, ": api[").concat(apiName, "] ").concat(method.toUpperCase(), " ").concat(url);
|
|
125
124
|
if (result.ok) {
|
|
126
|
-
console.log("
|
|
125
|
+
console.log("\uD83D\uDFE2 ".concat(msg));
|
|
127
126
|
}
|
|
128
127
|
else {
|
|
129
|
-
console.log("
|
|
128
|
+
console.log("\uD83D\uDD34 ".concat(msg));
|
|
130
129
|
}
|
|
131
130
|
}
|
|
132
131
|
return [2 /*return*/, result];
|
package/createApi.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create api client
|
|
3
|
+
*
|
|
4
|
+
* @param apiName Short name to use in debug logs
|
|
5
|
+
* @param baseUrl Either relativ eor absolute, it must end without trailing slash
|
|
6
|
+
*/
|
|
7
|
+
export declare const createApi: <TEndpoints extends Koine.Api.Endpoints>(apiName: string, baseUrl: string, options?: Koine.Api.ClientOptions) => Koine.Api.Client<TEndpoints>;
|
|
8
|
+
export default createApi;
|
package/createApi.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { __assign, __awaiter, __generator } from "tslib";
|
|
2
|
+
import { buildUrlQueryString, isFullObject } from "@koine/utils";
|
|
3
|
+
/**
|
|
4
|
+
* Create api client
|
|
5
|
+
*
|
|
6
|
+
* @param apiName Short name to use in debug logs
|
|
7
|
+
* @param baseUrl Either relativ eor absolute, it must end without trailing slash
|
|
8
|
+
*/
|
|
9
|
+
export var createApi = function (apiName, baseUrl, options) {
|
|
10
|
+
var _a = options || {}, _b = _a.headers, headersBase = _b === void 0 ? {} : _b, _c = _a.request, requestBase = _c === void 0 ? {
|
|
11
|
+
credentials: "include",
|
|
12
|
+
referrerPolicy: "no-referrer",
|
|
13
|
+
// mode: "cors",
|
|
14
|
+
// redirect: "follow",
|
|
15
|
+
// cache: "no-cache",
|
|
16
|
+
} : _c, exceptionBase = _a.exception, _d = _a.timeout, timeoutBase = _d === void 0 ? 10000 : _d, processReqBase = _a.processReq, processResBase = _a.processRes;
|
|
17
|
+
return ["get", "post", "put", "patch", "delete"].reduce(function (api, method) {
|
|
18
|
+
// @ts-expect-error FIXME: type
|
|
19
|
+
api[method] = function (endpoint, options) { return __awaiter(void 0, void 0, void 0, function () {
|
|
20
|
+
var _a, _b, request, _c, headers, _d, timeout, _e, processReq, _f, processRes, _g, exception, _h, params, json, query, url, requestInit, transformed, key, timeoutNumber, controller, timeoutId, response, e_1, result, e_2, msg;
|
|
21
|
+
return __generator(this, function (_j) {
|
|
22
|
+
switch (_j.label) {
|
|
23
|
+
case 0:
|
|
24
|
+
_a = options || {}, _b = _a.request, request = _b === void 0 ? requestBase : _b, _c = _a.headers, headers = _c === void 0 ? headersBase : _c, _d = _a.timeout, timeout = _d === void 0 ? timeoutBase : _d, _e = _a.processReq, processReq = _e === void 0 ? processReqBase : _e, _f = _a.processRes, processRes = _f === void 0 ? processResBase : _f, _g = _a.exception, exception = _g === void 0 ? exceptionBase : _g;
|
|
25
|
+
_h = options || {}, params = _h.params, json = _h.json, query = _h.query;
|
|
26
|
+
url = "".concat(baseUrl, "/").concat(endpoint + "".replace(/^\/*/, ""));
|
|
27
|
+
requestInit = __assign(__assign({ method: method.toUpperCase() }, request), { headers: __assign({ "content-type": "application/json" }, headers) });
|
|
28
|
+
if (processReq) {
|
|
29
|
+
transformed = processReq(method, url, query, json, params, requestInit);
|
|
30
|
+
url = transformed[0];
|
|
31
|
+
query = transformed[1];
|
|
32
|
+
json = transformed[2];
|
|
33
|
+
params = transformed[3];
|
|
34
|
+
requestInit = transformed[4];
|
|
35
|
+
}
|
|
36
|
+
if (isFullObject(params)) {
|
|
37
|
+
for (key in params) {
|
|
38
|
+
endpoint = endpoint.replace("{".concat(key, "}"), params[key].toString());
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
timeoutNumber = Number(timeout);
|
|
42
|
+
if (method !== "get" && json) {
|
|
43
|
+
requestInit.body = JSON.stringify(json);
|
|
44
|
+
}
|
|
45
|
+
if (timeoutNumber > 0) {
|
|
46
|
+
controller = new AbortController();
|
|
47
|
+
timeoutId = setTimeout(function () { return controller.abort(); }, timeoutNumber);
|
|
48
|
+
requestInit.signal = controller.signal;
|
|
49
|
+
}
|
|
50
|
+
if (query) {
|
|
51
|
+
// FIXME: ts-expect-error this assertion is not the best, but nevermind for now
|
|
52
|
+
url += buildUrlQueryString(query);
|
|
53
|
+
}
|
|
54
|
+
if (!exception) return [3 /*break*/, 5];
|
|
55
|
+
_j.label = 1;
|
|
56
|
+
case 1:
|
|
57
|
+
_j.trys.push([1, 3, , 4]);
|
|
58
|
+
return [4 /*yield*/, fetch(url, requestInit)];
|
|
59
|
+
case 2:
|
|
60
|
+
response = _j.sent();
|
|
61
|
+
return [3 /*break*/, 4];
|
|
62
|
+
case 3:
|
|
63
|
+
e_1 = _j.sent();
|
|
64
|
+
// eslint-disable-next-line no-throw-literal
|
|
65
|
+
throw { e: e_1 };
|
|
66
|
+
case 4: return [3 /*break*/, 7];
|
|
67
|
+
case 5: return [4 /*yield*/, fetch(url, requestInit)];
|
|
68
|
+
case 6:
|
|
69
|
+
response = _j.sent();
|
|
70
|
+
_j.label = 7;
|
|
71
|
+
case 7:
|
|
72
|
+
if (timeoutId) {
|
|
73
|
+
clearTimeout(timeoutId);
|
|
74
|
+
}
|
|
75
|
+
if (!exception) return [3 /*break*/, 15];
|
|
76
|
+
_j.label = 8;
|
|
77
|
+
case 8:
|
|
78
|
+
_j.trys.push([8, 13, , 14]);
|
|
79
|
+
if (!processRes) return [3 /*break*/, 10];
|
|
80
|
+
return [4 /*yield*/, processRes(response, options || {})];
|
|
81
|
+
case 9:
|
|
82
|
+
result = _j.sent();
|
|
83
|
+
return [3 /*break*/, 12];
|
|
84
|
+
case 10: return [4 /*yield*/, response.json()];
|
|
85
|
+
case 11:
|
|
86
|
+
result = _j.sent();
|
|
87
|
+
_j.label = 12;
|
|
88
|
+
case 12: return [3 /*break*/, 14];
|
|
89
|
+
case 13:
|
|
90
|
+
e_2 = _j.sent();
|
|
91
|
+
// eslint-disable-next-line no-throw-literal
|
|
92
|
+
throw { e: e_2 };
|
|
93
|
+
case 14: return [3 /*break*/, 19];
|
|
94
|
+
case 15:
|
|
95
|
+
if (!processRes) return [3 /*break*/, 17];
|
|
96
|
+
return [4 /*yield*/, processRes(response, options || {})];
|
|
97
|
+
case 16:
|
|
98
|
+
result = _j.sent();
|
|
99
|
+
return [3 /*break*/, 19];
|
|
100
|
+
case 17: return [4 /*yield*/, response.json()];
|
|
101
|
+
case 18:
|
|
102
|
+
result = _j.sent();
|
|
103
|
+
_j.label = 19;
|
|
104
|
+
case 19:
|
|
105
|
+
if (exception && result.fail) {
|
|
106
|
+
// throw new ApiError<Failed>(result);
|
|
107
|
+
// I prefer to throw an object literal despite what eslint says
|
|
108
|
+
// eslint-disable-next-line no-throw-literal
|
|
109
|
+
throw result;
|
|
110
|
+
}
|
|
111
|
+
if (process.env["NODE_ENV"] !== "production") {
|
|
112
|
+
msg = "".concat(result.status, ": api[").concat(apiName, "] ").concat(method.toUpperCase(), " ").concat(url);
|
|
113
|
+
if (result.ok) {
|
|
114
|
+
console.log("\uD83D\uDFE2 ".concat(msg));
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
console.log("\uD83D\uDD34 ".concat(msg));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return [2 /*return*/, result];
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}); };
|
|
124
|
+
return api;
|
|
125
|
+
}, {});
|
|
126
|
+
};
|
|
127
|
+
export default createApi;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type SWRConfiguration, type SWRResponse } from "swr";
|
|
2
|
+
import { type SWRMutationConfiguration, type SWRMutationResponse } from "swr/mutation";
|
|
3
|
+
declare type KoineApiMethodHookSWR<THookName extends keyof Koine.Api.HooksMapsByName, TEndpoints extends Koine.Api.Endpoints> = <TEndpoint extends Koine.Api.EndpointUrl<TEndpoints>, TMethod extends Koine.Api.RequestMethod = Koine.Api.HooksMapsByName[THookName]>(endpoint: TEndpoint, options?: Koine.Api.EndpointOptions<TEndpoints, TEndpoint, TMethod>, config?: THookName extends "useGet" ? SWRConfiguration<Koine.Api.EndpointResponseOk<TEndpoints, TEndpoint, TMethod>, Koine.Api.EndpointResponseFail<TEndpoints, TEndpoint, TMethod>> : SWRMutationConfiguration<Koine.Api.EndpointResponseOk<TEndpoints, TEndpoint, TMethod>, Koine.Api.EndpointResponseFail<TEndpoints, TEndpoint, TMethod>, Koine.Api.EndpointOptions<TEndpoints, TEndpoint, TMethod>, TEndpoint>) => THookName extends "useGet" ? SWRResponse<Koine.Api.EndpointResponseOk<TEndpoints, TEndpoint, TMethod>, Koine.Api.EndpointResponseFail<TEndpoints, TEndpoint, TMethod>> : SWRMutationResponse<Koine.Api.EndpointResponseOk<TEndpoints, TEndpoint, TMethod>, Koine.Api.EndpointResponseFail<TEndpoints, TEndpoint, TMethod>, Koine.Api.EndpointOptions<TEndpoints, TEndpoint, TMethod>, TEndpoint>;
|
|
4
|
+
/**
|
|
5
|
+
* It creates an api client extended with auto-generated SWR wrapper hooks
|
|
6
|
+
*/
|
|
7
|
+
export declare const createSwrApi: <TEndpoints extends Koine.Api.Endpoints>(apiName: string, baseUrl: string, options?: Koine.Api.ClientOptions | undefined) => Koine.Api.Client<TEndpoints> & {
|
|
8
|
+
useGet: KoineApiMethodHookSWR<"useGet", TEndpoints>;
|
|
9
|
+
usePost: KoineApiMethodHookSWR<"usePost", TEndpoints>;
|
|
10
|
+
usePut: KoineApiMethodHookSWR<"usePut", TEndpoints>;
|
|
11
|
+
usePatch: KoineApiMethodHookSWR<"usePatch", TEndpoints>;
|
|
12
|
+
useDelete: KoineApiMethodHookSWR<"useDelete", TEndpoints>;
|
|
13
|
+
};
|
|
14
|
+
export default createSwrApi;
|
package/createSwrApi.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { __assign, __awaiter, __generator } from "tslib";
|
|
2
|
+
import useSWR from "swr";
|
|
3
|
+
import useSWRMutation from "swr/mutation";
|
|
4
|
+
import { createApi } from "./createApi";
|
|
5
|
+
function createUseApi(api, method) {
|
|
6
|
+
return function useApi(endpoint, options, _config) {
|
|
7
|
+
var _this = this;
|
|
8
|
+
if (method === "get") {
|
|
9
|
+
// const fetcher = async (_endpoint: TEndpoint) => {
|
|
10
|
+
// try {
|
|
11
|
+
// const { ok, data } = await api[method](_endpoint, {
|
|
12
|
+
// ...(options || {}),
|
|
13
|
+
// exception: true,
|
|
14
|
+
// });
|
|
15
|
+
// if (ok) {
|
|
16
|
+
// return data;
|
|
17
|
+
// }
|
|
18
|
+
// throw new Error() as unknown as Koine.Api.EndpointResponseFail<TEndpoints, TEndpoint, TMethod>;
|
|
19
|
+
// } catch(e) {
|
|
20
|
+
// throw new Error() as unknown as Koine.Api.EndpointResponseFail<TEndpoints, TEndpoint, TMethod>;;
|
|
21
|
+
// }
|
|
22
|
+
// };
|
|
23
|
+
// }
|
|
24
|
+
var fetcher = function () { return __awaiter(_this, void 0, void 0, function () {
|
|
25
|
+
var data;
|
|
26
|
+
return __generator(this, function (_a) {
|
|
27
|
+
switch (_a.label) {
|
|
28
|
+
case 0: return [4 /*yield*/, api[method](endpoint, __assign(__assign({}, (options || {})), { exception: true }))];
|
|
29
|
+
case 1:
|
|
30
|
+
data = (_a.sent()).data;
|
|
31
|
+
return [2 /*return*/, data];
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}); };
|
|
35
|
+
var config_1 = _config;
|
|
36
|
+
// <Data = any, Error = any>(key: Key, config: SWRConfiguration<Data, Error, Fetcher<Data>> | undefined): SWRResponse<Data, Error>;
|
|
37
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
38
|
+
return useSWR(options ? [endpoint, options] : [endpoint], fetcher, config_1);
|
|
39
|
+
}
|
|
40
|
+
var config = _config;
|
|
41
|
+
var sender = function (
|
|
42
|
+
// if the first argument is an array the second tem are the base options
|
|
43
|
+
// defined when calling the usePost/Put/etc. hook, these will be overriden
|
|
44
|
+
// by the _options just here below
|
|
45
|
+
_endpoint,
|
|
46
|
+
// these are the options arriving when calling `trigger({ json, query, etc... })
|
|
47
|
+
_options) { return __awaiter(_this, void 0, void 0, function () {
|
|
48
|
+
var endpoint, options, _a, ok, data;
|
|
49
|
+
return __generator(this, function (_b) {
|
|
50
|
+
switch (_b.label) {
|
|
51
|
+
case 0:
|
|
52
|
+
endpoint = Array.isArray(_endpoint) ? _endpoint[0] : _endpoint;
|
|
53
|
+
options = Array.isArray(_endpoint) ? _endpoint[1] : {};
|
|
54
|
+
return [4 /*yield*/, api[method](endpoint, __assign(__assign(__assign({}, options), (_options.arg || {})), { exception: true }))];
|
|
55
|
+
case 1:
|
|
56
|
+
_a = _b.sent(), ok = _a.ok, data = _a.data;
|
|
57
|
+
return [2 /*return*/, ok ? data : data];
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}); };
|
|
61
|
+
// config.fetcher = sender;
|
|
62
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
63
|
+
return useSWRMutation(
|
|
64
|
+
// @ts-expect-error FIXME: I can't get it...
|
|
65
|
+
options ? [endpoint, options] : endpoint, sender, config);
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* It creates an api client extended with auto-generated SWR wrapper hooks
|
|
70
|
+
*/
|
|
71
|
+
export var createSwrApi = function () {
|
|
72
|
+
var args = [];
|
|
73
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
74
|
+
args[_i] = arguments[_i];
|
|
75
|
+
}
|
|
76
|
+
var api = createApi.apply(void 0, args);
|
|
77
|
+
["get", "post", "put", "patch", "delete"].forEach(function (method) {
|
|
78
|
+
var hookName = "use".concat(method.charAt(0).toUpperCase() + method.slice(1));
|
|
79
|
+
api[hookName] = createUseApi(api, method);
|
|
80
|
+
});
|
|
81
|
+
return api;
|
|
82
|
+
};
|
|
83
|
+
export default createSwrApi;
|
package/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
export * from "./
|
|
3
|
-
export * from "./
|
|
1
|
+
export * from "./ApiError";
|
|
2
|
+
export * from "./createApi";
|
|
3
|
+
export * from "./createSwrApi";
|
|
4
|
+
export * from "./nextApiResponse";
|
package/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
export * from "./
|
|
3
|
-
export * from "./
|
|
1
|
+
export * from "./ApiError";
|
|
2
|
+
export * from "./createApi";
|
|
3
|
+
export * from "./createSwrApi";
|
|
4
|
+
export * from "./nextApiResponse";
|
package/node/ApiError.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ApiError = void 0;
|
|
4
|
+
var tslib_1 = require("tslib");
|
|
5
|
+
/**
|
|
6
|
+
* Custom `ApiError` class extending `Error` to throw in failed response.
|
|
7
|
+
*
|
|
8
|
+
* @see https://eslint.org/docs/rules/no-throw-literal
|
|
9
|
+
* @see https://github.com/sindresorhus/ky/blob/main/source/errors/HTTPError.ts
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
var ApiError = /** @class */ (function (_super) {
|
|
13
|
+
tslib_1.__extends(ApiError, _super);
|
|
14
|
+
function ApiError(result) {
|
|
15
|
+
var _this = _super.call(this, "Request failed with ".concat(result.status, " ").concat(result.msg)) || this;
|
|
16
|
+
_this.name = "ApiError";
|
|
17
|
+
Object.assign(_this, result);
|
|
18
|
+
return _this;
|
|
19
|
+
}
|
|
20
|
+
return ApiError;
|
|
21
|
+
}(Error));
|
|
22
|
+
exports.ApiError = ApiError;
|
|
23
|
+
exports.default = ApiError;
|
|
@@ -1,26 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createApi =
|
|
3
|
+
exports.createApi = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
var utils_1 = require("@koine/utils");
|
|
6
|
-
/**
|
|
7
|
-
* Custom `ApiError` class extending `Error` to throw in failed response.
|
|
8
|
-
*
|
|
9
|
-
* @see https://eslint.org/docs/rules/no-throw-literal
|
|
10
|
-
* @see https://github.com/sindresorhus/ky/blob/main/source/errors/HTTPError.ts
|
|
11
|
-
*
|
|
12
|
-
*/
|
|
13
|
-
var ApiError = /** @class */ (function (_super) {
|
|
14
|
-
tslib_1.__extends(ApiError, _super);
|
|
15
|
-
function ApiError(result) {
|
|
16
|
-
var _this = _super.call(this, "Request failed with ".concat(result.status, " ").concat(result.msg)) || this;
|
|
17
|
-
_this.name = "ApiError";
|
|
18
|
-
Object.assign(_this, result);
|
|
19
|
-
return _this;
|
|
20
|
-
}
|
|
21
|
-
return ApiError;
|
|
22
|
-
}(Error));
|
|
23
|
-
exports.ApiError = ApiError;
|
|
24
6
|
/**
|
|
25
7
|
* Create api client
|
|
26
8
|
*
|
|
@@ -28,28 +10,38 @@ exports.ApiError = ApiError;
|
|
|
28
10
|
* @param baseUrl Either relativ eor absolute, it must end without trailing slash
|
|
29
11
|
*/
|
|
30
12
|
var createApi = function (apiName, baseUrl, options) {
|
|
31
|
-
var _a = options || {},
|
|
13
|
+
var _a = options || {}, _b = _a.headers, headersBase = _b === void 0 ? {} : _b, _c = _a.request, requestBase = _c === void 0 ? {
|
|
32
14
|
credentials: "include",
|
|
33
15
|
referrerPolicy: "no-referrer",
|
|
34
16
|
// mode: "cors",
|
|
35
17
|
// redirect: "follow",
|
|
36
18
|
// cache: "no-cache",
|
|
37
|
-
} :
|
|
19
|
+
} : _c, exceptionBase = _a.exception, _d = _a.timeout, timeoutBase = _d === void 0 ? 10000 : _d, processReqBase = _a.processReq, processResBase = _a.processRes;
|
|
38
20
|
return ["get", "post", "put", "patch", "delete"].reduce(function (api, method) {
|
|
21
|
+
// @ts-expect-error FIXME: type
|
|
39
22
|
api[method] = function (endpoint, options) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
|
|
40
|
-
var _a,
|
|
41
|
-
return tslib_1.__generator(this, function (
|
|
42
|
-
switch (
|
|
23
|
+
var _a, _b, request, _c, headers, _d, timeout, _e, processReq, _f, processRes, _g, exception, _h, params, json, query, url, requestInit, transformed, key, timeoutNumber, controller, timeoutId, response, e_1, result, e_2, msg;
|
|
24
|
+
return tslib_1.__generator(this, function (_j) {
|
|
25
|
+
switch (_j.label) {
|
|
43
26
|
case 0:
|
|
44
|
-
_a = options || {},
|
|
27
|
+
_a = options || {}, _b = _a.request, request = _b === void 0 ? requestBase : _b, _c = _a.headers, headers = _c === void 0 ? headersBase : _c, _d = _a.timeout, timeout = _d === void 0 ? timeoutBase : _d, _e = _a.processReq, processReq = _e === void 0 ? processReqBase : _e, _f = _a.processRes, processRes = _f === void 0 ? processResBase : _f, _g = _a.exception, exception = _g === void 0 ? exceptionBase : _g;
|
|
28
|
+
_h = options || {}, params = _h.params, json = _h.json, query = _h.query;
|
|
29
|
+
url = "".concat(baseUrl, "/").concat(endpoint + "".replace(/^\/*/, ""));
|
|
45
30
|
requestInit = tslib_1.__assign(tslib_1.__assign({ method: method.toUpperCase() }, request), { headers: tslib_1.__assign({ "content-type": "application/json" }, headers) });
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
31
|
+
if (processReq) {
|
|
32
|
+
transformed = processReq(method, url, query, json, params, requestInit);
|
|
33
|
+
url = transformed[0];
|
|
34
|
+
query = transformed[1];
|
|
35
|
+
json = transformed[2];
|
|
36
|
+
params = transformed[3];
|
|
37
|
+
requestInit = transformed[4];
|
|
38
|
+
}
|
|
39
|
+
if ((0, utils_1.isFullObject)(params)) {
|
|
40
|
+
for (key in params) {
|
|
41
|
+
endpoint = endpoint.replace("{".concat(key, "}"), params[key].toString());
|
|
49
42
|
}
|
|
50
43
|
}
|
|
51
44
|
timeoutNumber = Number(timeout);
|
|
52
|
-
url = "".concat(baseUrl, "/").concat(endpoint + "".replace(/^\/*/, ""));
|
|
53
45
|
if (method !== "get" && json) {
|
|
54
46
|
requestInit.body = JSON.stringify(json);
|
|
55
47
|
}
|
|
@@ -58,66 +50,62 @@ var createApi = function (apiName, baseUrl, options) {
|
|
|
58
50
|
timeoutId = setTimeout(function () { return controller.abort(); }, timeoutNumber);
|
|
59
51
|
requestInit.signal = controller.signal;
|
|
60
52
|
}
|
|
61
|
-
if (
|
|
62
|
-
requestInit = transformRequest(requestInit);
|
|
63
|
-
}
|
|
64
|
-
if (params) {
|
|
53
|
+
if (query) {
|
|
65
54
|
// FIXME: ts-expect-error this assertion is not the best, but nevermind for now
|
|
66
|
-
|
|
67
|
-
url += (0, utils_1.buildUrlQueryString)(params);
|
|
55
|
+
url += (0, utils_1.buildUrlQueryString)(query);
|
|
68
56
|
}
|
|
69
|
-
if (!
|
|
70
|
-
|
|
57
|
+
if (!exception) return [3 /*break*/, 5];
|
|
58
|
+
_j.label = 1;
|
|
71
59
|
case 1:
|
|
72
|
-
|
|
60
|
+
_j.trys.push([1, 3, , 4]);
|
|
73
61
|
return [4 /*yield*/, fetch(url, requestInit)];
|
|
74
62
|
case 2:
|
|
75
|
-
response =
|
|
63
|
+
response = _j.sent();
|
|
76
64
|
return [3 /*break*/, 4];
|
|
77
65
|
case 3:
|
|
78
|
-
e_1 =
|
|
66
|
+
e_1 = _j.sent();
|
|
79
67
|
// eslint-disable-next-line no-throw-literal
|
|
80
68
|
throw { e: e_1 };
|
|
81
69
|
case 4: return [3 /*break*/, 7];
|
|
82
70
|
case 5: return [4 /*yield*/, fetch(url, requestInit)];
|
|
83
71
|
case 6:
|
|
84
|
-
response =
|
|
85
|
-
|
|
72
|
+
response = _j.sent();
|
|
73
|
+
_j.label = 7;
|
|
86
74
|
case 7:
|
|
87
75
|
if (timeoutId) {
|
|
88
76
|
clearTimeout(timeoutId);
|
|
89
77
|
}
|
|
90
|
-
if (!
|
|
91
|
-
|
|
78
|
+
if (!exception) return [3 /*break*/, 15];
|
|
79
|
+
_j.label = 8;
|
|
92
80
|
case 8:
|
|
93
|
-
|
|
94
|
-
if (!
|
|
95
|
-
return [4 /*yield*/,
|
|
81
|
+
_j.trys.push([8, 13, , 14]);
|
|
82
|
+
if (!processRes) return [3 /*break*/, 10];
|
|
83
|
+
return [4 /*yield*/, processRes(response, options || {})];
|
|
96
84
|
case 9:
|
|
97
|
-
result =
|
|
85
|
+
result = _j.sent();
|
|
98
86
|
return [3 /*break*/, 12];
|
|
99
87
|
case 10: return [4 /*yield*/, response.json()];
|
|
100
88
|
case 11:
|
|
101
|
-
result =
|
|
102
|
-
|
|
89
|
+
result = _j.sent();
|
|
90
|
+
_j.label = 12;
|
|
103
91
|
case 12: return [3 /*break*/, 14];
|
|
104
92
|
case 13:
|
|
105
|
-
e_2 =
|
|
93
|
+
e_2 = _j.sent();
|
|
106
94
|
// eslint-disable-next-line no-throw-literal
|
|
107
95
|
throw { e: e_2 };
|
|
108
96
|
case 14: return [3 /*break*/, 19];
|
|
109
97
|
case 15:
|
|
110
|
-
if (!
|
|
111
|
-
return [4 /*yield*/,
|
|
98
|
+
if (!processRes) return [3 /*break*/, 17];
|
|
99
|
+
return [4 /*yield*/, processRes(response, options || {})];
|
|
112
100
|
case 16:
|
|
113
|
-
result =
|
|
101
|
+
result = _j.sent();
|
|
114
102
|
return [3 /*break*/, 19];
|
|
115
103
|
case 17: return [4 /*yield*/, response.json()];
|
|
116
104
|
case 18:
|
|
117
|
-
result =
|
|
118
|
-
|
|
105
|
+
result = _j.sent();
|
|
106
|
+
_j.label = 19;
|
|
119
107
|
case 19:
|
|
120
|
-
if (
|
|
108
|
+
if (exception && result.fail) {
|
|
121
109
|
// throw new ApiError<Failed>(result);
|
|
122
110
|
// I prefer to throw an object literal despite what eslint says
|
|
123
111
|
// eslint-disable-next-line no-throw-literal
|
|
@@ -126,10 +114,10 @@ var createApi = function (apiName, baseUrl, options) {
|
|
|
126
114
|
if (process.env["NODE_ENV"] !== "production") {
|
|
127
115
|
msg = "".concat(result.status, ": api[").concat(apiName, "] ").concat(method.toUpperCase(), " ").concat(url);
|
|
128
116
|
if (result.ok) {
|
|
129
|
-
console.log("
|
|
117
|
+
console.log("\uD83D\uDFE2 ".concat(msg));
|
|
130
118
|
}
|
|
131
119
|
else {
|
|
132
|
-
console.log("
|
|
120
|
+
console.log("\uD83D\uDD34 ".concat(msg));
|
|
133
121
|
}
|
|
134
122
|
}
|
|
135
123
|
return [2 /*return*/, result];
|
|
@@ -140,3 +128,4 @@ var createApi = function (apiName, baseUrl, options) {
|
|
|
140
128
|
}, {});
|
|
141
129
|
};
|
|
142
130
|
exports.createApi = createApi;
|
|
131
|
+
exports.default = exports.createApi;
|
|
@@ -4,7 +4,7 @@ exports.createSwrApi = void 0;
|
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
var swr_1 = require("swr");
|
|
6
6
|
var mutation_1 = require("swr/mutation");
|
|
7
|
-
var
|
|
7
|
+
var createApi_1 = require("./createApi");
|
|
8
8
|
function createUseApi(api, method) {
|
|
9
9
|
return function useApi(endpoint, options, _config) {
|
|
10
10
|
var _this = this;
|
|
@@ -13,7 +13,7 @@ function createUseApi(api, method) {
|
|
|
13
13
|
// try {
|
|
14
14
|
// const { ok, data } = await api[method](_endpoint, {
|
|
15
15
|
// ...(options || {}),
|
|
16
|
-
//
|
|
16
|
+
// exception: true,
|
|
17
17
|
// });
|
|
18
18
|
// if (ok) {
|
|
19
19
|
// return data;
|
|
@@ -28,7 +28,7 @@ function createUseApi(api, method) {
|
|
|
28
28
|
var data;
|
|
29
29
|
return tslib_1.__generator(this, function (_a) {
|
|
30
30
|
switch (_a.label) {
|
|
31
|
-
case 0: return [4 /*yield*/, api[method](endpoint, tslib_1.__assign(tslib_1.__assign({}, (options || {})), {
|
|
31
|
+
case 0: return [4 /*yield*/, api[method](endpoint, tslib_1.__assign(tslib_1.__assign({}, (options || {})), { exception: true }))];
|
|
32
32
|
case 1:
|
|
33
33
|
data = (_a.sent()).data;
|
|
34
34
|
return [2 /*return*/, data];
|
|
@@ -46,7 +46,7 @@ function createUseApi(api, method) {
|
|
|
46
46
|
// defined when calling the usePost/Put/etc. hook, these will be overriden
|
|
47
47
|
// by the _options just here below
|
|
48
48
|
_endpoint,
|
|
49
|
-
// these are the options arriving when calling `trigger({ json,
|
|
49
|
+
// these are the options arriving when calling `trigger({ json, query, etc... })
|
|
50
50
|
_options) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
|
51
51
|
var endpoint, options, _a, ok, data;
|
|
52
52
|
return tslib_1.__generator(this, function (_b) {
|
|
@@ -54,7 +54,7 @@ function createUseApi(api, method) {
|
|
|
54
54
|
case 0:
|
|
55
55
|
endpoint = Array.isArray(_endpoint) ? _endpoint[0] : _endpoint;
|
|
56
56
|
options = Array.isArray(_endpoint) ? _endpoint[1] : {};
|
|
57
|
-
return [4 /*yield*/, api[method](endpoint, tslib_1.__assign(tslib_1.__assign(tslib_1.__assign({}, options), (_options.arg || {})), {
|
|
57
|
+
return [4 /*yield*/, api[method](endpoint, tslib_1.__assign(tslib_1.__assign(tslib_1.__assign({}, options), (_options.arg || {})), { exception: true }))];
|
|
58
58
|
case 1:
|
|
59
59
|
_a = _b.sent(), ok = _a.ok, data = _a.data;
|
|
60
60
|
return [2 /*return*/, ok ? data : data];
|
|
@@ -76,7 +76,7 @@ var createSwrApi = function () {
|
|
|
76
76
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
77
77
|
args[_i] = arguments[_i];
|
|
78
78
|
}
|
|
79
|
-
var api =
|
|
79
|
+
var api = createApi_1.createApi.apply(void 0, args);
|
|
80
80
|
["get", "post", "put", "patch", "delete"].forEach(function (method) {
|
|
81
81
|
var hookName = "use".concat(method.charAt(0).toUpperCase() + method.slice(1));
|
|
82
82
|
api[hookName] = createUseApi(api, method);
|
|
@@ -84,3 +84,4 @@ var createSwrApi = function () {
|
|
|
84
84
|
return api;
|
|
85
85
|
};
|
|
86
86
|
exports.createSwrApi = createSwrApi;
|
|
87
|
+
exports.default = exports.createSwrApi;
|
package/node/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
var tslib_1 = require("tslib");
|
|
4
|
-
tslib_1.__exportStar(require("./
|
|
5
|
-
tslib_1.__exportStar(require("./
|
|
6
|
-
tslib_1.__exportStar(require("./
|
|
4
|
+
tslib_1.__exportStar(require("./ApiError"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./createApi"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./createSwrApi"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./nextApiResponse"), exports);
|
package/package.json
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
"typings": "./index.d.ts",
|
|
6
6
|
"dependencies": {},
|
|
7
7
|
"peerDependencies": {
|
|
8
|
-
"@koine/utils": "1.0.
|
|
9
|
-
"next": "^12.2.0",
|
|
8
|
+
"@koine/utils": "1.0.54",
|
|
10
9
|
"swr": "^2.0.0-beta.6",
|
|
10
|
+
"next": "^12.2.0",
|
|
11
11
|
"tslib": "^2.4.0"
|
|
12
12
|
},
|
|
13
|
-
"version": "1.0.
|
|
13
|
+
"version": "1.0.54",
|
|
14
14
|
"module": "./index.js",
|
|
15
15
|
"types": "./index.d.ts"
|
|
16
16
|
}
|
package/swr/index.js
CHANGED
|
@@ -43,7 +43,7 @@ function createUseApi(api, method) {
|
|
|
43
43
|
// defined when calling the usePost/Put/etc. hook, these will be overriden
|
|
44
44
|
// by the _options just here below
|
|
45
45
|
_endpoint,
|
|
46
|
-
// these are the options arriving when calling `trigger({ json,
|
|
46
|
+
// these are the options arriving when calling `trigger({ json, query, etc... })
|
|
47
47
|
_options) { return __awaiter(_this, void 0, void 0, function () {
|
|
48
48
|
var endpoint, options, _a, ok, data;
|
|
49
49
|
return __generator(this, function (_b) {
|
package/typings.d.ts
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
type _Response = Response;
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @borrows [awesome-template-literal-types](https://github.com/ghoullier/awesome-template-literal-types#router-params-parsing)
|
|
7
|
+
*/
|
|
8
|
+
type ExtractEndpointParams<T extends string> = string extends T
|
|
9
|
+
? Record<string, string>
|
|
10
|
+
: // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
11
|
+
T extends `${infer _Start}{${infer Param}}${infer Rest}`
|
|
12
|
+
? { [k in Param | keyof ExtractEndpointParams<Rest>]: string | number }
|
|
13
|
+
: // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
14
|
+
T extends `${infer _Start}{${infer Param}}`
|
|
15
|
+
? { [k in Param]: string | number }
|
|
16
|
+
: never;
|
|
17
|
+
|
|
5
18
|
declare namespace Koine.Api {
|
|
6
19
|
// @see https://stackoverflow.com/a/60702896/1938970
|
|
7
20
|
// import { Exact } from "type-fest";
|
|
@@ -19,6 +32,15 @@ declare namespace Koine.Api {
|
|
|
19
32
|
) => Client<TEndpoints>;
|
|
20
33
|
|
|
21
34
|
type ClientOptions = {
|
|
35
|
+
/**
|
|
36
|
+
* Headers will be merged with
|
|
37
|
+
* ```
|
|
38
|
+
* { "content-type": "application/json" }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @default {}
|
|
42
|
+
*/
|
|
43
|
+
headers?: RequestInit["headers"];
|
|
22
44
|
/**
|
|
23
45
|
* Basic request options to supply to `fetch`
|
|
24
46
|
*
|
|
@@ -27,9 +49,30 @@ declare namespace Koine.Api {
|
|
|
27
49
|
* @default { credentials: "include", referrerPolicy: "no-referrer" }
|
|
28
50
|
*/
|
|
29
51
|
request?: Omit<RequestInit, "body" | "headers" | "method">;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Flag to throw error with try/catch
|
|
54
|
+
*
|
|
55
|
+
* @default false
|
|
56
|
+
*/
|
|
57
|
+
exception?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Timeout in `ms`, if `falsy` there is no timeout
|
|
60
|
+
*
|
|
61
|
+
* @default 10000
|
|
62
|
+
*/
|
|
63
|
+
timeout?: number | false | null;
|
|
64
|
+
/**
|
|
65
|
+
* Process request before actual http call
|
|
66
|
+
*
|
|
67
|
+
* @default undefined
|
|
68
|
+
*/
|
|
69
|
+
processReq?: RequestProcessor;
|
|
70
|
+
/**
|
|
71
|
+
* Process response just after http response
|
|
72
|
+
*
|
|
73
|
+
* @default undefined
|
|
74
|
+
*/
|
|
75
|
+
processRes?: ResponseProcessor;
|
|
33
76
|
};
|
|
34
77
|
|
|
35
78
|
type ClientMethod<
|
|
@@ -37,13 +80,9 @@ declare namespace Koine.Api {
|
|
|
37
80
|
TEndpoints extends Endpoints
|
|
38
81
|
> = <
|
|
39
82
|
TEndpoint extends EndpointUrl<TEndpoints>,
|
|
40
|
-
TOptions extends
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
TEndpoint,
|
|
44
|
-
TMethod
|
|
45
|
-
>,
|
|
46
|
-
TFailed extends Koine.Api.DataFailed = Koine.Api.EndpointResponseFail<
|
|
83
|
+
TOptions extends EndpointOptions<TEndpoints, TEndpoint, TMethod>,
|
|
84
|
+
TOk extends ResponseOk = EndpointResponseOk<TEndpoints, TEndpoint, TMethod>,
|
|
85
|
+
TFail extends ResponseFail = EndpointResponseFail<
|
|
47
86
|
TEndpoints,
|
|
48
87
|
TEndpoint,
|
|
49
88
|
TMethod
|
|
@@ -51,8 +90,8 @@ declare namespace Koine.Api {
|
|
|
51
90
|
>(
|
|
52
91
|
endpoint: TEndpoint,
|
|
53
92
|
options?: TOptions
|
|
54
|
-
|
|
55
|
-
) => Promise<Result<
|
|
93
|
+
) => Promise<EndpointResult<TEndpoints, TEndpoint, TMethod>>;
|
|
94
|
+
// ) => Promise<Result<TOk, TFail>>;
|
|
56
95
|
|
|
57
96
|
/**
|
|
58
97
|
* The `api` interface generated by `createApi`
|
|
@@ -67,14 +106,16 @@ declare namespace Koine.Api {
|
|
|
67
106
|
//
|
|
68
107
|
//////////////////////////////////////////////////////////////////////////////
|
|
69
108
|
|
|
70
|
-
type
|
|
109
|
+
type EndpointOptions<
|
|
71
110
|
TEndpoints extends Endpoints,
|
|
72
111
|
TEndpoint extends EndpointUrl<TEndpoints>,
|
|
73
112
|
TMethod extends RequestMethod
|
|
74
113
|
> = RequestOptions<
|
|
114
|
+
TEndpoints,
|
|
115
|
+
TEndpoint,
|
|
75
116
|
TMethod,
|
|
76
117
|
TEndpoints[TEndpoint][Uppercase<TMethod>]["json"],
|
|
77
|
-
TEndpoints[TEndpoint][Uppercase<TMethod>]["
|
|
118
|
+
TEndpoints[TEndpoint][Uppercase<TMethod>]["query"]
|
|
78
119
|
>;
|
|
79
120
|
|
|
80
121
|
type EndpointResultOk<
|
|
@@ -101,7 +142,7 @@ declare namespace Koine.Api {
|
|
|
101
142
|
TMethod extends RequestMethod
|
|
102
143
|
> = TEndpoints[TEndpoint][Uppercase<TMethod>]["fail"];
|
|
103
144
|
|
|
104
|
-
type
|
|
145
|
+
type EndpointResult<
|
|
105
146
|
TEndpoints extends Endpoints,
|
|
106
147
|
TEndpoint extends EndpointUrl<TEndpoints>,
|
|
107
148
|
TMethod extends RequestMethod
|
|
@@ -155,7 +196,7 @@ declare namespace Koine.Api {
|
|
|
155
196
|
/**
|
|
156
197
|
* The parameters to encode in the URL of the request
|
|
157
198
|
*/
|
|
158
|
-
|
|
199
|
+
query?: RequestQuery;
|
|
159
200
|
/**
|
|
160
201
|
* The JSON response data returned by the request in case of success
|
|
161
202
|
*/
|
|
@@ -173,83 +214,65 @@ declare namespace Koine.Api {
|
|
|
173
214
|
//
|
|
174
215
|
//////////////////////////////////////////////////////////////////////////////
|
|
175
216
|
|
|
217
|
+
type RequestMethod = "get" | "post" | "put" | "patch" | "delete";
|
|
218
|
+
|
|
176
219
|
type RequestJson = undefined | null | Record<string | number, unknown>;
|
|
177
220
|
|
|
178
|
-
type
|
|
221
|
+
type RequestQuery = undefined | null | Record<string | number, unknown>;
|
|
222
|
+
|
|
223
|
+
type RequestParams =
|
|
224
|
+
| undefined
|
|
225
|
+
| null
|
|
226
|
+
| Record<string | number, string | number>;
|
|
179
227
|
|
|
180
228
|
/**
|
|
181
|
-
*
|
|
229
|
+
* Request options
|
|
182
230
|
*
|
|
183
|
-
*
|
|
231
|
+
* `ClientOptions` can be overriden here at the single request level.
|
|
184
232
|
*/
|
|
185
|
-
type
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
233
|
+
type RequestOptions<
|
|
234
|
+
TEndpoints extends Endpoints,
|
|
235
|
+
TEndpoint extends EndpointUrl<TEndpoints>,
|
|
236
|
+
TMethod extends RequestMethod,
|
|
237
|
+
TJson extends RequestJson = RequestJson,
|
|
238
|
+
TQuery extends RequestQuery = RequestQuery
|
|
239
|
+
> = Omit<ClientOptions, "processReq"> & {
|
|
240
|
+
processReq?: EndpointRequestProcessor<TEndpoints, TEndpoint, TMethod>;
|
|
193
241
|
/**
|
|
194
|
-
*
|
|
242
|
+
* A dictionary to dynamically interpolate endpoint url params, e.g.:
|
|
195
243
|
*
|
|
196
|
-
*
|
|
244
|
+
* ```js
|
|
245
|
+
* myapi.get("user/{id}", { path: { id: "12" }})
|
|
246
|
+
* ```
|
|
247
|
+
* results in a call to the endpoint `"user/12"`
|
|
197
248
|
*/
|
|
198
|
-
|
|
249
|
+
params?: ExtractEndpointParams<TEndpoint>;
|
|
199
250
|
/**
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
* @see RequestInit
|
|
203
|
-
*
|
|
204
|
-
* @default { credentials: "include", referrerPolicy: "no-referrer" }
|
|
251
|
+
* Query parameters will be serialized into a string and appended to the URL
|
|
205
252
|
*/
|
|
206
|
-
|
|
253
|
+
query?: TQuery;
|
|
207
254
|
/**
|
|
208
|
-
*
|
|
255
|
+
* JSON request body
|
|
209
256
|
*
|
|
210
|
-
*
|
|
211
|
-
* "user/{id}" endpoint with { path: { id: "12" }} results in a call
|
|
212
|
-
* to the endpoint "user/12"
|
|
213
|
-
* ```
|
|
257
|
+
* FIXME: this should be `undefined` with `get` request mehod
|
|
214
258
|
*/
|
|
215
|
-
|
|
259
|
+
json?: TJson;
|
|
216
260
|
};
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
*/
|
|
233
|
-
json?: TJson;
|
|
234
|
-
/**
|
|
235
|
-
* Params will be serialized into a string and appended to the URL
|
|
236
|
-
*/
|
|
237
|
-
params?: TParams;
|
|
238
|
-
}
|
|
239
|
-
: {
|
|
240
|
-
/**
|
|
241
|
-
* JSON request body
|
|
242
|
-
*
|
|
243
|
-
* @default {}
|
|
244
|
-
*/
|
|
245
|
-
json?: TJson;
|
|
246
|
-
/**
|
|
247
|
-
* Params will be serialized into a string and appended to the URL
|
|
248
|
-
*/
|
|
249
|
-
params?: TParams;
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
type RequestMethod = "get" | "post" | "put" | "patch" | "delete";
|
|
261
|
+
// } & ([TMethod] extends ["get"]
|
|
262
|
+
// ? {
|
|
263
|
+
// /**
|
|
264
|
+
// * JSON request body
|
|
265
|
+
// */
|
|
266
|
+
// json?: TJson;
|
|
267
|
+
// }
|
|
268
|
+
// : {
|
|
269
|
+
// /**
|
|
270
|
+
// * JSON request body
|
|
271
|
+
// *
|
|
272
|
+
// * @default {}
|
|
273
|
+
// */
|
|
274
|
+
// json?: TJson;
|
|
275
|
+
// });
|
|
253
276
|
|
|
254
277
|
//////////////////////////////////////////////////////////////////////////////
|
|
255
278
|
//
|
|
@@ -288,11 +311,6 @@ declare namespace Koine.Api {
|
|
|
288
311
|
TResponseOk extends ResponseOk,
|
|
289
312
|
TResponseFail extends ResponseFail
|
|
290
313
|
> =
|
|
291
|
-
// FIXME: without the type duplication below the following two lines do not
|
|
292
|
-
// work as they do not narrow the type when checking for the boolean values
|
|
293
|
-
// truthiness
|
|
294
|
-
// | ResultOk<TOk>
|
|
295
|
-
// | ResultFail<TOk>;
|
|
296
314
|
| {
|
|
297
315
|
status: _Response["status"];
|
|
298
316
|
msg: _Response["statusText"];
|
|
@@ -308,9 +326,53 @@ declare namespace Koine.Api {
|
|
|
308
326
|
data: TResponseFail;
|
|
309
327
|
};
|
|
310
328
|
|
|
311
|
-
|
|
329
|
+
/**
|
|
330
|
+
* The request processor at the client level, this is meant to apply global
|
|
331
|
+
* transformations to all endpoints requests
|
|
332
|
+
*/
|
|
333
|
+
type RequestProcessor = (
|
|
334
|
+
method: RequestMethod,
|
|
335
|
+
url: string,
|
|
336
|
+
query: any,
|
|
337
|
+
json: any,
|
|
338
|
+
params: any,
|
|
339
|
+
requestInit: RequestInit
|
|
340
|
+
) => [
|
|
341
|
+
string, // url
|
|
342
|
+
RequestQuery, // query
|
|
343
|
+
RequestJson, // json
|
|
344
|
+
RequestParams, // params
|
|
345
|
+
RequestInit // requestInit
|
|
346
|
+
];
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* The request processor at the request level, this is meant to apply
|
|
350
|
+
* transformations to a single endpoint request
|
|
351
|
+
*/
|
|
352
|
+
type EndpointRequestProcessor<
|
|
353
|
+
TEndpoints extends Endpoints,
|
|
354
|
+
TEndpoint extends EndpointUrl<TEndpoints>,
|
|
355
|
+
TMethod extends RequestMethod
|
|
356
|
+
> = (
|
|
357
|
+
method: TMethod,
|
|
358
|
+
url: string,
|
|
359
|
+
query: EndpointOptions<TEndpoints, TEndpoint, TMethod>["query"],
|
|
360
|
+
json: EndpointOptions<TEndpoints, TEndpoint, TMethod>["json"],
|
|
361
|
+
params: EndpointOptions<TEndpoints, TEndpoint, TMethod>["params"],
|
|
362
|
+
requestInit: RequestInit
|
|
363
|
+
) => [
|
|
364
|
+
string, // url
|
|
365
|
+
EndpointOptions<TEndpoints, TEndpoint, TMethod>["query"], // query
|
|
366
|
+
EndpointOptions<TEndpoints, TEndpoint, TMethod>["json"], // json
|
|
367
|
+
EndpointOptions<TEndpoints, TEndpoint, TMethod>["params"], // params
|
|
368
|
+
RequestInit // requestInit
|
|
369
|
+
];
|
|
312
370
|
|
|
313
|
-
|
|
371
|
+
/**
|
|
372
|
+
* The response processor at the request level, this is meant to apply
|
|
373
|
+
* transformations to a single endpoint response
|
|
374
|
+
*/
|
|
375
|
+
type ResponseProcessor = <
|
|
314
376
|
TResponseOk extends ResponseOk = ResponseOk,
|
|
315
377
|
TResponseFail extends ResponseFailed = ResponseFailed
|
|
316
378
|
>(
|