@koine/api 1.0.13
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/README.md +5 -0
- package/core/index.d.ts +17 -0
- package/core/index.js +132 -0
- package/core/package.json +6 -0
- package/index.d.ts +3 -0
- package/index.js +3 -0
- package/next/index.d.ts +2 -0
- package/next/index.js +4 -0
- package/next/package.json +6 -0
- package/node/core/index.js +136 -0
- package/node/index.js +6 -0
- package/node/next/index.js +8 -0
- package/node/swr/index.js +77 -0
- package/package.json +16 -0
- package/swr/index.d.ts +14 -0
- package/swr/index.js +73 -0
- package/swr/package.json +6 -0
- package/typings.d.ts +306 -0
package/README.md
ADDED
package/core/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
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<DataFailed extends Koine.Api.DataFailed = unknown> extends Error {
|
|
9
|
+
constructor(result: Koine.Api.ResponseFailed<DataFailed>);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Create api client
|
|
13
|
+
*
|
|
14
|
+
* @param apiName Short name to use in debug logs
|
|
15
|
+
* @param baseUrl Either relativ eor absolute, it must end without trailing slash
|
|
16
|
+
*/
|
|
17
|
+
export declare const createApi: <TEndpoints extends Koine.Api.Endpoints>(apiName: string, baseUrl: string, options?: Koine.Api.ClientOptions) => Koine.Api.Client<TEndpoints>;
|
package/core/index.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { __assign, __awaiter, __extends, __generator } from "tslib";
|
|
2
|
+
import { buildUrlQueryString } from "@koine/utils";
|
|
3
|
+
/**
|
|
4
|
+
* Custom `ApiError` class extending `Error` to throw in failed response.
|
|
5
|
+
*
|
|
6
|
+
* @see https://eslint.org/docs/rules/no-throw-literal
|
|
7
|
+
* @see https://github.com/sindresorhus/ky/blob/main/source/errors/HTTPError.ts
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
var ApiError = /** @class */ (function (_super) {
|
|
11
|
+
__extends(ApiError, _super);
|
|
12
|
+
function ApiError(result) {
|
|
13
|
+
var _this = _super.call(this, "Request failed with ".concat(result.status, " ").concat(result.msg)) || this;
|
|
14
|
+
_this.name = "ApiError";
|
|
15
|
+
Object.assign(_this, result);
|
|
16
|
+
return _this;
|
|
17
|
+
}
|
|
18
|
+
return ApiError;
|
|
19
|
+
}(Error));
|
|
20
|
+
export { ApiError };
|
|
21
|
+
/**
|
|
22
|
+
* Create api client
|
|
23
|
+
*
|
|
24
|
+
* @param apiName Short name to use in debug logs
|
|
25
|
+
* @param baseUrl Either relativ eor absolute, it must end without trailing slash
|
|
26
|
+
*/
|
|
27
|
+
export var createApi = function (apiName, baseUrl, options) {
|
|
28
|
+
var _a = options || {}, adapterBase = _a.adapter, shouldThrowBase = _a.shouldThrow;
|
|
29
|
+
return ["get", "post", "put", "patch", "delete"].reduce(function (api, method) {
|
|
30
|
+
api[method] = function (endpoint, options) { return __awaiter(void 0, void 0, void 0, function () {
|
|
31
|
+
var _a, json, params, _b, headers, _c, timeout, _d, adapter, _e, shouldThrow, requestInit, timeoutNumber, controller, timeoutId, url, response, e_1, result, e_2, pre;
|
|
32
|
+
return __generator(this, function (_f) {
|
|
33
|
+
switch (_f.label) {
|
|
34
|
+
case 0:
|
|
35
|
+
_a = options || {}, json = _a.json, params = _a.params, _b = _a.headers, headers = _b === void 0 ? {} : _b, _c = _a.timeout, timeout = _c === void 0 ? 10000 : _c, _d = _a.adapter, adapter = _d === void 0 ? adapterBase : _d, _e = _a.shouldThrow, shouldThrow = _e === void 0 ? shouldThrowBase : _e;
|
|
36
|
+
requestInit = {
|
|
37
|
+
method: method.toUpperCase(),
|
|
38
|
+
// mode: "cors",
|
|
39
|
+
// redirect: "follow",
|
|
40
|
+
credentials: "include",
|
|
41
|
+
// cache: "no-cache",
|
|
42
|
+
referrerPolicy: "no-referrer",
|
|
43
|
+
headers: __assign({ "content-type": "application/json" }, headers),
|
|
44
|
+
};
|
|
45
|
+
timeoutNumber = Number(timeout);
|
|
46
|
+
url = "".concat(baseUrl, "/").concat(endpoint + "".replace(/^\/*/, ""));
|
|
47
|
+
if (method !== "get" && json) {
|
|
48
|
+
requestInit.body = JSON.stringify(json);
|
|
49
|
+
}
|
|
50
|
+
if (timeoutNumber > 0) {
|
|
51
|
+
controller = new AbortController();
|
|
52
|
+
timeoutId = setTimeout(function () { return controller.abort(); }, timeoutNumber);
|
|
53
|
+
requestInit.signal = controller.signal;
|
|
54
|
+
}
|
|
55
|
+
if (params) {
|
|
56
|
+
// FIXME: ts-expect-error this assertion is not the best, but nevermind for now
|
|
57
|
+
// url += buildUrlQueryString(params as unknown as Koine.Api.RequestParams);
|
|
58
|
+
url += buildUrlQueryString(params);
|
|
59
|
+
}
|
|
60
|
+
if (!shouldThrow) return [3 /*break*/, 5];
|
|
61
|
+
_f.label = 1;
|
|
62
|
+
case 1:
|
|
63
|
+
_f.trys.push([1, 3, , 4]);
|
|
64
|
+
return [4 /*yield*/, fetch(url, requestInit)];
|
|
65
|
+
case 2:
|
|
66
|
+
response = _f.sent();
|
|
67
|
+
return [3 /*break*/, 4];
|
|
68
|
+
case 3:
|
|
69
|
+
e_1 = _f.sent();
|
|
70
|
+
// eslint-disable-next-line no-throw-literal
|
|
71
|
+
throw { e: e_1 };
|
|
72
|
+
case 4: return [3 /*break*/, 7];
|
|
73
|
+
case 5: return [4 /*yield*/, fetch(url, requestInit)];
|
|
74
|
+
case 6:
|
|
75
|
+
response = _f.sent();
|
|
76
|
+
_f.label = 7;
|
|
77
|
+
case 7:
|
|
78
|
+
if (timeoutId) {
|
|
79
|
+
clearTimeout(timeoutId);
|
|
80
|
+
}
|
|
81
|
+
if (!shouldThrow) return [3 /*break*/, 15];
|
|
82
|
+
_f.label = 8;
|
|
83
|
+
case 8:
|
|
84
|
+
_f.trys.push([8, 13, , 14]);
|
|
85
|
+
if (!adapter) return [3 /*break*/, 10];
|
|
86
|
+
return [4 /*yield*/, adapter(response, options)];
|
|
87
|
+
case 9:
|
|
88
|
+
result = _f.sent();
|
|
89
|
+
return [3 /*break*/, 12];
|
|
90
|
+
case 10: return [4 /*yield*/, response.json()];
|
|
91
|
+
case 11:
|
|
92
|
+
result = _f.sent();
|
|
93
|
+
_f.label = 12;
|
|
94
|
+
case 12: return [3 /*break*/, 14];
|
|
95
|
+
case 13:
|
|
96
|
+
e_2 = _f.sent();
|
|
97
|
+
// eslint-disable-next-line no-throw-literal
|
|
98
|
+
throw { e: e_2 };
|
|
99
|
+
case 14: return [3 /*break*/, 19];
|
|
100
|
+
case 15:
|
|
101
|
+
if (!adapter) return [3 /*break*/, 17];
|
|
102
|
+
return [4 /*yield*/, adapter(response, options)];
|
|
103
|
+
case 16:
|
|
104
|
+
result = _f.sent();
|
|
105
|
+
return [3 /*break*/, 19];
|
|
106
|
+
case 17: return [4 /*yield*/, response.json()];
|
|
107
|
+
case 18:
|
|
108
|
+
result = _f.sent();
|
|
109
|
+
_f.label = 19;
|
|
110
|
+
case 19:
|
|
111
|
+
if (shouldThrow && result.fail) {
|
|
112
|
+
// throw new ApiError<Failed>(result);
|
|
113
|
+
// I prefer to throw an object literal despite what eslint says
|
|
114
|
+
// eslint-disable-next-line no-throw-literal
|
|
115
|
+
throw result;
|
|
116
|
+
}
|
|
117
|
+
if (process.env["NODE_ENV"] !== "production") {
|
|
118
|
+
pre = "api[".concat(apiName, "] ").concat(method.toUpperCase(), " to ").concat(url);
|
|
119
|
+
if (result.ok) {
|
|
120
|
+
console.log("".concat(pre, " success."));
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
console.log("".concat(pre, " failed."));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return [2 /*return*/, result];
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}); };
|
|
130
|
+
return api;
|
|
131
|
+
}, {});
|
|
132
|
+
};
|
package/index.d.ts
ADDED
package/index.js
ADDED
package/next/index.d.ts
ADDED
package/next/index.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createApi = exports.ApiError = void 0;
|
|
4
|
+
var tslib_1 = require("tslib");
|
|
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
|
+
/**
|
|
25
|
+
* Create api client
|
|
26
|
+
*
|
|
27
|
+
* @param apiName Short name to use in debug logs
|
|
28
|
+
* @param baseUrl Either relativ eor absolute, it must end without trailing slash
|
|
29
|
+
*/
|
|
30
|
+
var createApi = function (apiName, baseUrl, options) {
|
|
31
|
+
var _a = options || {}, adapterBase = _a.adapter, shouldThrowBase = _a.shouldThrow;
|
|
32
|
+
return ["get", "post", "put", "patch", "delete"].reduce(function (api, method) {
|
|
33
|
+
api[method] = function (endpoint, options) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
|
|
34
|
+
var _a, json, params, _b, headers, _c, timeout, _d, adapter, _e, shouldThrow, requestInit, timeoutNumber, controller, timeoutId, url, response, e_1, result, e_2, pre;
|
|
35
|
+
return tslib_1.__generator(this, function (_f) {
|
|
36
|
+
switch (_f.label) {
|
|
37
|
+
case 0:
|
|
38
|
+
_a = options || {}, json = _a.json, params = _a.params, _b = _a.headers, headers = _b === void 0 ? {} : _b, _c = _a.timeout, timeout = _c === void 0 ? 10000 : _c, _d = _a.adapter, adapter = _d === void 0 ? adapterBase : _d, _e = _a.shouldThrow, shouldThrow = _e === void 0 ? shouldThrowBase : _e;
|
|
39
|
+
requestInit = {
|
|
40
|
+
method: method.toUpperCase(),
|
|
41
|
+
// mode: "cors",
|
|
42
|
+
// redirect: "follow",
|
|
43
|
+
credentials: "include",
|
|
44
|
+
// cache: "no-cache",
|
|
45
|
+
referrerPolicy: "no-referrer",
|
|
46
|
+
headers: tslib_1.__assign({ "content-type": "application/json" }, headers),
|
|
47
|
+
};
|
|
48
|
+
timeoutNumber = Number(timeout);
|
|
49
|
+
url = "".concat(baseUrl, "/").concat(endpoint + "".replace(/^\/*/, ""));
|
|
50
|
+
if (method !== "get" && json) {
|
|
51
|
+
requestInit.body = JSON.stringify(json);
|
|
52
|
+
}
|
|
53
|
+
if (timeoutNumber > 0) {
|
|
54
|
+
controller = new AbortController();
|
|
55
|
+
timeoutId = setTimeout(function () { return controller.abort(); }, timeoutNumber);
|
|
56
|
+
requestInit.signal = controller.signal;
|
|
57
|
+
}
|
|
58
|
+
if (params) {
|
|
59
|
+
// FIXME: ts-expect-error this assertion is not the best, but nevermind for now
|
|
60
|
+
// url += buildUrlQueryString(params as unknown as Koine.Api.RequestParams);
|
|
61
|
+
url += (0, utils_1.buildUrlQueryString)(params);
|
|
62
|
+
}
|
|
63
|
+
if (!shouldThrow) return [3 /*break*/, 5];
|
|
64
|
+
_f.label = 1;
|
|
65
|
+
case 1:
|
|
66
|
+
_f.trys.push([1, 3, , 4]);
|
|
67
|
+
return [4 /*yield*/, fetch(url, requestInit)];
|
|
68
|
+
case 2:
|
|
69
|
+
response = _f.sent();
|
|
70
|
+
return [3 /*break*/, 4];
|
|
71
|
+
case 3:
|
|
72
|
+
e_1 = _f.sent();
|
|
73
|
+
// eslint-disable-next-line no-throw-literal
|
|
74
|
+
throw { e: e_1 };
|
|
75
|
+
case 4: return [3 /*break*/, 7];
|
|
76
|
+
case 5: return [4 /*yield*/, fetch(url, requestInit)];
|
|
77
|
+
case 6:
|
|
78
|
+
response = _f.sent();
|
|
79
|
+
_f.label = 7;
|
|
80
|
+
case 7:
|
|
81
|
+
if (timeoutId) {
|
|
82
|
+
clearTimeout(timeoutId);
|
|
83
|
+
}
|
|
84
|
+
if (!shouldThrow) return [3 /*break*/, 15];
|
|
85
|
+
_f.label = 8;
|
|
86
|
+
case 8:
|
|
87
|
+
_f.trys.push([8, 13, , 14]);
|
|
88
|
+
if (!adapter) return [3 /*break*/, 10];
|
|
89
|
+
return [4 /*yield*/, adapter(response, options)];
|
|
90
|
+
case 9:
|
|
91
|
+
result = _f.sent();
|
|
92
|
+
return [3 /*break*/, 12];
|
|
93
|
+
case 10: return [4 /*yield*/, response.json()];
|
|
94
|
+
case 11:
|
|
95
|
+
result = _f.sent();
|
|
96
|
+
_f.label = 12;
|
|
97
|
+
case 12: return [3 /*break*/, 14];
|
|
98
|
+
case 13:
|
|
99
|
+
e_2 = _f.sent();
|
|
100
|
+
// eslint-disable-next-line no-throw-literal
|
|
101
|
+
throw { e: e_2 };
|
|
102
|
+
case 14: return [3 /*break*/, 19];
|
|
103
|
+
case 15:
|
|
104
|
+
if (!adapter) return [3 /*break*/, 17];
|
|
105
|
+
return [4 /*yield*/, adapter(response, options)];
|
|
106
|
+
case 16:
|
|
107
|
+
result = _f.sent();
|
|
108
|
+
return [3 /*break*/, 19];
|
|
109
|
+
case 17: return [4 /*yield*/, response.json()];
|
|
110
|
+
case 18:
|
|
111
|
+
result = _f.sent();
|
|
112
|
+
_f.label = 19;
|
|
113
|
+
case 19:
|
|
114
|
+
if (shouldThrow && result.fail) {
|
|
115
|
+
// throw new ApiError<Failed>(result);
|
|
116
|
+
// I prefer to throw an object literal despite what eslint says
|
|
117
|
+
// eslint-disable-next-line no-throw-literal
|
|
118
|
+
throw result;
|
|
119
|
+
}
|
|
120
|
+
if (process.env["NODE_ENV"] !== "production") {
|
|
121
|
+
pre = "api[".concat(apiName, "] ").concat(method.toUpperCase(), " to ").concat(url);
|
|
122
|
+
if (result.ok) {
|
|
123
|
+
console.log("".concat(pre, " success."));
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
console.log("".concat(pre, " failed."));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return [2 /*return*/, result];
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}); };
|
|
133
|
+
return api;
|
|
134
|
+
}, {});
|
|
135
|
+
};
|
|
136
|
+
exports.createApi = createApi;
|
package/node/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.nextApiResponse = void 0;
|
|
4
|
+
var nextApiResponse = function (nextRes, response) {
|
|
5
|
+
// nextRes.status(response.status).json(response.data || response.msg);
|
|
6
|
+
nextRes.status(response.status).json(response);
|
|
7
|
+
};
|
|
8
|
+
exports.nextApiResponse = nextApiResponse;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSwrApi = void 0;
|
|
4
|
+
var tslib_1 = require("tslib");
|
|
5
|
+
var swr_1 = require("swr");
|
|
6
|
+
var mutation_1 = require("swr/mutation");
|
|
7
|
+
var core_1 = require("../core");
|
|
8
|
+
function createUseApi(api, method) {
|
|
9
|
+
return function useApi(endpoint, options, _config) {
|
|
10
|
+
var _this = this;
|
|
11
|
+
if (method === "get") {
|
|
12
|
+
// const fetcher = async (_endpoint: TEndpoint) => {
|
|
13
|
+
// try {
|
|
14
|
+
// const { ok, data } = await api[method](_endpoint, {
|
|
15
|
+
// ...(options || {}),
|
|
16
|
+
// shouldThrow: true,
|
|
17
|
+
// });
|
|
18
|
+
// if (ok) {
|
|
19
|
+
// return data;
|
|
20
|
+
// }
|
|
21
|
+
// throw new Error() as unknown as Koine.Api.EndpointResponseFailed<TEndpoints, TEndpoint, TMethod>;
|
|
22
|
+
// } catch(e) {
|
|
23
|
+
// throw new Error() as unknown as Koine.Api.EndpointResponseFailed<TEndpoints, TEndpoint, TMethod>;;
|
|
24
|
+
// }
|
|
25
|
+
// };
|
|
26
|
+
// }
|
|
27
|
+
var fetcher = function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
|
28
|
+
var data;
|
|
29
|
+
return tslib_1.__generator(this, function (_a) {
|
|
30
|
+
switch (_a.label) {
|
|
31
|
+
case 0: return [4 /*yield*/, api[method](endpoint, tslib_1.__assign(tslib_1.__assign({}, (options || {})), { shouldThrow: true }))];
|
|
32
|
+
case 1:
|
|
33
|
+
data = (_a.sent()).data;
|
|
34
|
+
return [2 /*return*/, data];
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}); };
|
|
38
|
+
var config_1 = _config;
|
|
39
|
+
// <Data = any, Error = any>(key: Key, config: SWRConfiguration<Data, Error, Fetcher<Data>> | undefined): SWRResponse<Data, Error>;
|
|
40
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
41
|
+
return (0, swr_1.default)(options ? [endpoint, options] : [endpoint], fetcher, config_1);
|
|
42
|
+
}
|
|
43
|
+
var config = _config;
|
|
44
|
+
var sender = function (_endpoint, _options) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
|
45
|
+
var _a, ok, data;
|
|
46
|
+
return tslib_1.__generator(this, function (_b) {
|
|
47
|
+
switch (_b.label) {
|
|
48
|
+
case 0: return [4 /*yield*/, api[method](_endpoint, tslib_1.__assign(tslib_1.__assign({}, (_options.arg || {})), { shouldThrow: true }))];
|
|
49
|
+
case 1:
|
|
50
|
+
_a = _b.sent(), ok = _a.ok, data = _a.data;
|
|
51
|
+
return [2 /*return*/, ok ? data : data];
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}); };
|
|
55
|
+
// config.fetcher = sender;
|
|
56
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
57
|
+
return (0, mutation_1.default)(
|
|
58
|
+
// @ts-expect-error FIXME: I can't get it...
|
|
59
|
+
options ? [endpoint, options] : [endpoint], sender, config);
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* It creates an api client extended with auto-generated SWR wrapper hooks
|
|
64
|
+
*/
|
|
65
|
+
var createSwrApi = function () {
|
|
66
|
+
var args = [];
|
|
67
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
68
|
+
args[_i] = arguments[_i];
|
|
69
|
+
}
|
|
70
|
+
var api = core_1.createApi.apply(void 0, args);
|
|
71
|
+
["get", "post", "put", "patch", "delete"].forEach(function (method) {
|
|
72
|
+
var hookName = "use".concat(method.charAt(0).toUpperCase() + method.slice(1));
|
|
73
|
+
api[hookName] = createUseApi(api, method);
|
|
74
|
+
});
|
|
75
|
+
return api;
|
|
76
|
+
};
|
|
77
|
+
exports.createSwrApi = createSwrApi;
|
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@koine/api",
|
|
3
|
+
"version": "1.0.13",
|
|
4
|
+
"sideEffects": false,
|
|
5
|
+
"main": "./node/index.js",
|
|
6
|
+
"typings": "./index.d.ts",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"next": "^12.1.6",
|
|
9
|
+
"swr": "^2.0.0-beta.3",
|
|
10
|
+
"type-fest": "^2.13.0",
|
|
11
|
+
"tslib": "^2.4.0"
|
|
12
|
+
},
|
|
13
|
+
"peerDependencies": {},
|
|
14
|
+
"module": "./index.js",
|
|
15
|
+
"types": "./index.d.ts"
|
|
16
|
+
}
|
package/swr/index.d.ts
ADDED
|
@@ -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.EndpointRequestOptions<TEndpoints, TEndpoint, TMethod>, config?: THookName extends "useGet" ? SWRConfiguration<Koine.Api.EndpointResponseSuccesfull<TEndpoints, TEndpoint, TMethod>, Koine.Api.EndpointResponseFailed<TEndpoints, TEndpoint, TMethod>> : SWRMutationConfiguration<Koine.Api.EndpointResponseSuccesfull<TEndpoints, TEndpoint, TMethod>, Koine.Api.EndpointResponseFailed<TEndpoints, TEndpoint, TMethod>, Koine.Api.EndpointRequestOptions<TEndpoints, TEndpoint, TMethod>, TEndpoint>) => THookName extends "useGet" ? SWRResponse<Koine.Api.EndpointResponseSuccesfull<TEndpoints, TEndpoint, TMethod>, Koine.Api.EndpointResponseFailed<TEndpoints, TEndpoint, TMethod>> : SWRMutationResponse<Koine.Api.EndpointResponseSuccesfull<TEndpoints, TEndpoint, TMethod>, Koine.Api.EndpointResponseFailed<TEndpoints, TEndpoint, TMethod>, Koine.Api.EndpointRequestOptions<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 {};
|
package/swr/index.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { __assign, __awaiter, __generator } from "tslib";
|
|
2
|
+
import useSWR from "swr";
|
|
3
|
+
import useSWRMutation from "swr/mutation";
|
|
4
|
+
import { createApi } from "../core";
|
|
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
|
+
// shouldThrow: true,
|
|
14
|
+
// });
|
|
15
|
+
// if (ok) {
|
|
16
|
+
// return data;
|
|
17
|
+
// }
|
|
18
|
+
// throw new Error() as unknown as Koine.Api.EndpointResponseFailed<TEndpoints, TEndpoint, TMethod>;
|
|
19
|
+
// } catch(e) {
|
|
20
|
+
// throw new Error() as unknown as Koine.Api.EndpointResponseFailed<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 || {})), { shouldThrow: 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 (_endpoint, _options) { return __awaiter(_this, void 0, void 0, function () {
|
|
42
|
+
var _a, ok, data;
|
|
43
|
+
return __generator(this, function (_b) {
|
|
44
|
+
switch (_b.label) {
|
|
45
|
+
case 0: return [4 /*yield*/, api[method](_endpoint, __assign(__assign({}, (_options.arg || {})), { shouldThrow: true }))];
|
|
46
|
+
case 1:
|
|
47
|
+
_a = _b.sent(), ok = _a.ok, data = _a.data;
|
|
48
|
+
return [2 /*return*/, ok ? data : data];
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}); };
|
|
52
|
+
// config.fetcher = sender;
|
|
53
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
54
|
+
return useSWRMutation(
|
|
55
|
+
// @ts-expect-error FIXME: I can't get it...
|
|
56
|
+
options ? [endpoint, options] : [endpoint], sender, config);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* It creates an api client extended with auto-generated SWR wrapper hooks
|
|
61
|
+
*/
|
|
62
|
+
export var createSwrApi = function () {
|
|
63
|
+
var args = [];
|
|
64
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
65
|
+
args[_i] = arguments[_i];
|
|
66
|
+
}
|
|
67
|
+
var api = createApi.apply(void 0, args);
|
|
68
|
+
["get", "post", "put", "patch", "delete"].forEach(function (method) {
|
|
69
|
+
var hookName = "use".concat(method.charAt(0).toUpperCase() + method.slice(1));
|
|
70
|
+
api[hookName] = createUseApi(api, method);
|
|
71
|
+
});
|
|
72
|
+
return api;
|
|
73
|
+
};
|
package/swr/package.json
ADDED
package/typings.d.ts
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
|
|
3
|
+
type _Response = Response;
|
|
4
|
+
|
|
5
|
+
declare namespace Koine.Api {
|
|
6
|
+
// @see https://stackoverflow.com/a/60702896/1938970
|
|
7
|
+
import { Exact } from "type-fest";
|
|
8
|
+
|
|
9
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
10
|
+
//
|
|
11
|
+
// Client
|
|
12
|
+
//
|
|
13
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
14
|
+
|
|
15
|
+
type ClientCreator<TEndpoints extends Endpoints> = (
|
|
16
|
+
apiName: string,
|
|
17
|
+
baseUrl: string,
|
|
18
|
+
options?: ClientOptions
|
|
19
|
+
) => Client<TEndpoints>;
|
|
20
|
+
|
|
21
|
+
type ClientOptions = {
|
|
22
|
+
adapter?: ResponseAdapter;
|
|
23
|
+
shouldThrow?: boolean;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type ClientMethod<
|
|
27
|
+
TMethod extends RequestMethod,
|
|
28
|
+
TEndpoints extends Endpoints
|
|
29
|
+
> = <
|
|
30
|
+
TEndpoint extends EndpointUrl<TEndpoints>,
|
|
31
|
+
TOptions extends EndpointRequestOptions<TEndpoints, TEndpoint, TMethod>,
|
|
32
|
+
TSuccesfull extends Koine.Api.DataSuccesfull = Koine.Api.EndpointResponseSuccesfull<
|
|
33
|
+
TEndpoints,
|
|
34
|
+
TEndpoint,
|
|
35
|
+
TMethod
|
|
36
|
+
>,
|
|
37
|
+
TFailed extends Koine.Api.DataFailed = Koine.Api.EndpointResponseFailed<
|
|
38
|
+
TEndpoints,
|
|
39
|
+
TEndpoint,
|
|
40
|
+
TMethod
|
|
41
|
+
>
|
|
42
|
+
>(
|
|
43
|
+
endpoint: TEndpoint,
|
|
44
|
+
options?: TOptions
|
|
45
|
+
// ) => Promise<EndpointResponse<TEndpoints, TEndpoint, TMethod>>;
|
|
46
|
+
) => Promise<Response<TSuccesfull, TFailed>>;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The `api` interface generated by `createApi`
|
|
50
|
+
*/
|
|
51
|
+
type Client<TEndpoints> = {
|
|
52
|
+
[TMethod in RequestMethod]: ClientMethod<TMethod, TEndpoints>;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
56
|
+
//
|
|
57
|
+
// Endpoints
|
|
58
|
+
//
|
|
59
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
60
|
+
|
|
61
|
+
type EndpointRequestOptions<
|
|
62
|
+
TEndpoints extends Endpoints,
|
|
63
|
+
TEndpoint extends EndpointUrl<TEndpoints>,
|
|
64
|
+
TMethod extends RequestMethod
|
|
65
|
+
> = RequestOptions<
|
|
66
|
+
TMethod,
|
|
67
|
+
TEndpoints[TEndpoint][Uppercase<TMethod>]["request"],
|
|
68
|
+
TEndpoints[TEndpoint][Uppercase<TMethod>]["params"]
|
|
69
|
+
>;
|
|
70
|
+
|
|
71
|
+
type EndpointResponseSuccesfull<
|
|
72
|
+
TEndpoints extends Endpoints,
|
|
73
|
+
TEndpoint extends EndpointUrl<TEndpoints>,
|
|
74
|
+
TMethod extends RequestMethod
|
|
75
|
+
> = TEndpoints[TEndpoint][Uppercase<TMethod>]["response"];
|
|
76
|
+
|
|
77
|
+
type EndpointResponseFailed<
|
|
78
|
+
TEndpoints extends Endpoints,
|
|
79
|
+
TEndpoint extends EndpointUrl<TEndpoints>,
|
|
80
|
+
TMethod extends RequestMethod
|
|
81
|
+
> = TEndpoints[TEndpoint][Uppercase<TMethod>]["error"];
|
|
82
|
+
|
|
83
|
+
type EndpointResponse<
|
|
84
|
+
TEndpoints extends Endpoints,
|
|
85
|
+
TEndpoint extends EndpointUrl<TEndpoints>,
|
|
86
|
+
TMethod extends RequestMethod
|
|
87
|
+
> = Response<
|
|
88
|
+
EndpointResponseSuccesfull<TEndpoints, TEndpoint, TMethod>,
|
|
89
|
+
EndpointResponseFailed<TEndpoints, TEndpoint, TMethod>
|
|
90
|
+
>;
|
|
91
|
+
|
|
92
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
93
|
+
//
|
|
94
|
+
// Defintions
|
|
95
|
+
//
|
|
96
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Validate the `Endpoints` definition against the `Endpoint` shape defined
|
|
100
|
+
* here.
|
|
101
|
+
*
|
|
102
|
+
* FIXME: this does not work yet...constraining the API endpoints definitions
|
|
103
|
+
*
|
|
104
|
+
* @see https://github.com/sindresorhus/type-fest/blob/main/source/exact.d.ts
|
|
105
|
+
* @see https://fettblog.eu/typescript-match-the-exact-object-shape/
|
|
106
|
+
* @see https://stackoverflow.com/a/51496652/1938970
|
|
107
|
+
* @see https://github.com/Microsoft/TypeScript/issues/12936
|
|
108
|
+
*/
|
|
109
|
+
// type DefineEndpoint<T> =
|
|
110
|
+
// T extends Endpoint ?
|
|
111
|
+
// Exclude<keyof T, keyof Endpoint> extends never ?
|
|
112
|
+
// T : "Endpoint must follow `Koine.Api.Endpoint` shape" : never;
|
|
113
|
+
type DefineEndpoint<T extends EndpointShape> = T;
|
|
114
|
+
type DefineEndpoints<T extends Endpoints> = {};
|
|
115
|
+
type EndpointShape = { [TMethod in Uppercase<RequestMethod>]?: DataTypes };
|
|
116
|
+
|
|
117
|
+
type Endpoints = readonly Record<string, Endpoint>;
|
|
118
|
+
type Endpoint = {
|
|
119
|
+
[TMethod in Uppercase<RequestMethod>]?: DataTypes<TMethod>;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
type EndpointUrl<TEndpoints extends Endpoints> = Extract<keyof TEndpoints, string>;
|
|
123
|
+
|
|
124
|
+
type DataTypes<TMethod extends Uppercase<RequestMethod>> =
|
|
125
|
+
TMethod extends "GET"
|
|
126
|
+
? {
|
|
127
|
+
/**
|
|
128
|
+
* The JSON response data returned by the request in case of success
|
|
129
|
+
*/
|
|
130
|
+
response?: null | unknown;
|
|
131
|
+
/**
|
|
132
|
+
* The parameters to encode in the URL of the request
|
|
133
|
+
*/
|
|
134
|
+
params?: RequestParams;
|
|
135
|
+
/**
|
|
136
|
+
* The shape of the error data returned by the request in case of
|
|
137
|
+
* failure
|
|
138
|
+
*/
|
|
139
|
+
error?: null | unknown;
|
|
140
|
+
}
|
|
141
|
+
: {
|
|
142
|
+
/**
|
|
143
|
+
* The request body of a non-GET request
|
|
144
|
+
*/
|
|
145
|
+
request?: null | unknown;
|
|
146
|
+
/**
|
|
147
|
+
* The JSON response data returned by the request in case of success
|
|
148
|
+
*/
|
|
149
|
+
response?: null | unknown;
|
|
150
|
+
/**
|
|
151
|
+
* The parameters to encode in the URL of the request
|
|
152
|
+
*/
|
|
153
|
+
params?: RequestParams;
|
|
154
|
+
/**
|
|
155
|
+
* The shape of the error data returned by the request in case of
|
|
156
|
+
* failure
|
|
157
|
+
*/
|
|
158
|
+
error?: null | unknown;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
162
|
+
//
|
|
163
|
+
// Request
|
|
164
|
+
//
|
|
165
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
166
|
+
|
|
167
|
+
type RequestParams = undefined | null | Record<string | number, unknown>;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Shared request options (for every request method)
|
|
171
|
+
*
|
|
172
|
+
* Client options can be overriden here at request level.
|
|
173
|
+
*/
|
|
174
|
+
type RequestOptionsShared = ClientOptions & {
|
|
175
|
+
/**
|
|
176
|
+
* Headers will be merged with
|
|
177
|
+
* ```
|
|
178
|
+
* { "content-type": "application/json" }
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
headers?: RequestInit["headers"];
|
|
182
|
+
/**
|
|
183
|
+
* Timeout in `ms`, if `falsy` there is no timeout
|
|
184
|
+
*
|
|
185
|
+
* @default 10000
|
|
186
|
+
*/
|
|
187
|
+
timeout?: number | false | null;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Request options
|
|
192
|
+
*
|
|
193
|
+
* Client options can be overriden here at request level.
|
|
194
|
+
*/
|
|
195
|
+
type RequestOptions<
|
|
196
|
+
TMethod extends RequestMethod,
|
|
197
|
+
TJson extends Record<string, unknown> = {},
|
|
198
|
+
TParams extends RequestParams = {}
|
|
199
|
+
> = RequestOptionsShared &
|
|
200
|
+
([TMethod] extends ["get"]
|
|
201
|
+
? {
|
|
202
|
+
/**
|
|
203
|
+
* JSON request body
|
|
204
|
+
*/
|
|
205
|
+
json?: TJson;
|
|
206
|
+
/**
|
|
207
|
+
* Params will be serialized into a string and appended to the URL
|
|
208
|
+
*/
|
|
209
|
+
params?: TParams;
|
|
210
|
+
}
|
|
211
|
+
: {
|
|
212
|
+
/**
|
|
213
|
+
* JSON request body
|
|
214
|
+
*
|
|
215
|
+
* @default {}
|
|
216
|
+
*/
|
|
217
|
+
json?: TJson;
|
|
218
|
+
/**
|
|
219
|
+
* Params will be serialized into a string and appended to the URL
|
|
220
|
+
*/
|
|
221
|
+
params?: TParams;
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
type RequestMethod = "get" | "post" | "put" | "patch" | "delete";
|
|
225
|
+
|
|
226
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
227
|
+
//
|
|
228
|
+
// Response
|
|
229
|
+
//
|
|
230
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
231
|
+
|
|
232
|
+
type DataSuccesfull = unknown;
|
|
233
|
+
|
|
234
|
+
type DataFailed = unknown;
|
|
235
|
+
|
|
236
|
+
type ResponseShared<
|
|
237
|
+
T extends Record<string, unknown> = Record<string, unknown>
|
|
238
|
+
> = T & {
|
|
239
|
+
status: _Response["status"];
|
|
240
|
+
msg: _Response["statusText"];
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
type ResponseSuccesfull<Data extends DataSuccesfull = DataSuccesfull> = {
|
|
244
|
+
status: _Response["status"];
|
|
245
|
+
msg: _Response["statusText"];
|
|
246
|
+
ok: true;
|
|
247
|
+
fail?: false;
|
|
248
|
+
data: Data;
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
type ResponseFailed<Data extends DataFailed = DataFailed> = {
|
|
252
|
+
status: _Response["status"];
|
|
253
|
+
msg: _Response["statusText"];
|
|
254
|
+
ok?: false;
|
|
255
|
+
fail: true;
|
|
256
|
+
data: Data;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
type Response<Succesfull extends DataSuccesfull, Failed extends DataFailed> =
|
|
260
|
+
// FIXME: without the type duplication below the following two lines do not
|
|
261
|
+
// work as they do not narrow the type when checking for the boolean values
|
|
262
|
+
// truthiness
|
|
263
|
+
// | ResponseSuccesfull<Succesfull>
|
|
264
|
+
// | ResponseFailed<Succesfull>;
|
|
265
|
+
| {
|
|
266
|
+
status: _Response["status"];
|
|
267
|
+
msg: _Response["statusText"];
|
|
268
|
+
ok: true;
|
|
269
|
+
fail?: false;
|
|
270
|
+
data: Succesfull;
|
|
271
|
+
}
|
|
272
|
+
| {
|
|
273
|
+
status: _Response["status"];
|
|
274
|
+
msg: _Response["statusText"];
|
|
275
|
+
ok?: false;
|
|
276
|
+
fail: true;
|
|
277
|
+
data: Failed;
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
type ResponseAdapter = <
|
|
281
|
+
Succesfull extends DataSuccesfull = DataSuccesfull,
|
|
282
|
+
Failed extends DataFailed = DataFailed
|
|
283
|
+
>(
|
|
284
|
+
response: _Response,
|
|
285
|
+
options: RequestOptions
|
|
286
|
+
) => Promise<Koine.Api.Response<Succesfull, Failed>>;
|
|
287
|
+
|
|
288
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
289
|
+
//
|
|
290
|
+
// Hooks
|
|
291
|
+
//
|
|
292
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Api hooks map for `react`, each request method has its own `use{Method}`
|
|
296
|
+
* hook.
|
|
297
|
+
*
|
|
298
|
+
* These hooks are implemented with different libraries or, in the future as
|
|
299
|
+
* standalone hooks, see SWR ones to start with.
|
|
300
|
+
*/
|
|
301
|
+
type HooksMaps = readonly {
|
|
302
|
+
[TMethod in RequestMethod]: `use${Capitalize<TMethod>}`;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
type HooksMapsByName = readonly { [K in keyof HooksMaps as HooksMaps[K]]: K };
|
|
306
|
+
}
|