fets 0.4.14 → 0.4.15
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/cjs/Response.js +80 -0
- package/cjs/client/auth/oauth.js +34 -0
- package/cjs/client/createClient.js +133 -0
- package/cjs/client/index.js +6 -0
- package/cjs/client/plugins/useClientCookieStore.js +31 -0
- package/cjs/client/types.js +0 -0
- package/cjs/createRouter.js +299 -0
- package/cjs/index.js +16 -0
- package/cjs/plugins/ajv.js +213 -0
- package/cjs/plugins/openapi.js +171 -0
- package/cjs/plugins/utils.js +31 -0
- package/cjs/swagger-ui-html.js +3 -0
- package/cjs/typed-fetch.js +0 -0
- package/cjs/types.js +0 -0
- package/cjs/utils.js +73 -0
- package/cjs/zod/types.js +7 -0
- package/cjs/zod/zod.js +92 -0
- package/esm/Response.js +74 -0
- package/esm/client/auth/oauth.js +31 -0
- package/esm/client/createClient.js +128 -0
- package/esm/client/index.js +3 -0
- package/esm/client/plugins/useClientCookieStore.js +27 -0
- package/esm/client/types.js +0 -0
- package/esm/createRouter.js +293 -0
- package/esm/index.js +7 -0
- package/esm/plugins/ajv.js +208 -0
- package/esm/plugins/openapi.js +166 -0
- package/esm/plugins/utils.js +27 -0
- package/esm/swagger-ui-html.js +1 -0
- package/esm/typed-fetch.js +0 -0
- package/esm/types.js +0 -0
- package/esm/utils.js +69 -0
- package/esm/zod/types.js +3 -0
- package/esm/zod/zod.js +88 -0
- package/package.json +1 -1
- package/typings/Response.d.cts +28 -0
- package/typings/Response.d.ts +28 -0
- package/typings/client/auth/oauth.d.cts +150 -0
- package/typings/client/auth/oauth.d.ts +150 -0
- package/typings/client/createClient.d.cts +34 -0
- package/typings/client/createClient.d.ts +34 -0
- package/typings/client/index.d.cts +3 -0
- package/typings/client/index.d.ts +3 -0
- package/typings/client/plugins/useClientCookieStore.d.cts +3 -0
- package/typings/client/plugins/useClientCookieStore.d.ts +3 -0
- package/typings/client/types.d.cts +426 -0
- package/typings/client/types.d.ts +426 -0
- package/typings/createRouter.d.cts +6 -0
- package/typings/createRouter.d.ts +6 -0
- package/typings/index.d.cts +7 -0
- package/typings/index.d.ts +7 -0
- package/typings/plugins/ajv.d.cts +4 -0
- package/typings/plugins/ajv.d.ts +4 -0
- package/typings/plugins/openapi.d.cts +33 -0
- package/typings/plugins/openapi.d.ts +33 -0
- package/typings/plugins/utils.d.cts +1 -0
- package/typings/plugins/utils.d.ts +1 -0
- package/typings/swagger-ui-html.d.cts +2 -0
- package/typings/swagger-ui-html.d.ts +2 -0
- package/typings/typed-fetch.d.cts +232 -0
- package/typings/typed-fetch.d.ts +232 -0
- package/typings/types.d.cts +261 -0
- package/typings/types.d.ts +261 -0
- package/typings/utils.d.cts +31 -0
- package/typings/utils.d.ts +31 -0
- package/typings/zod/types.d.cts +39 -0
- package/typings/zod/types.d.ts +39 -0
- package/typings/zod/zod.d.cts +2 -0
- package/typings/zod/zod.d.ts +2 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export var OAuthPathErrorType;
|
|
2
|
+
(function (OAuthPathErrorType) {
|
|
3
|
+
/**
|
|
4
|
+
* The request is missing a parameter so the server can’t proceed with the request.
|
|
5
|
+
* This may also be returned if the request includes an unsupported parameter or repeats a parameter.
|
|
6
|
+
*/
|
|
7
|
+
OAuthPathErrorType["invalid_request"] = "invalid_request";
|
|
8
|
+
/**
|
|
9
|
+
* Client authentication failed, such as if the request contains an invalid client ID or secret.
|
|
10
|
+
* Send an HTTP 401 response in this case.
|
|
11
|
+
*/
|
|
12
|
+
OAuthPathErrorType["invalid_client"] = "invalid_client";
|
|
13
|
+
/**
|
|
14
|
+
* The authorization code (or user’s password for the password grant type) is invalid or expired.
|
|
15
|
+
* This is also the error you would return if the redirect URL given in the authorization grant does not match the URL provided in this access token request.
|
|
16
|
+
*/
|
|
17
|
+
OAuthPathErrorType["invalid_grant"] = "invalid_grant";
|
|
18
|
+
/**
|
|
19
|
+
* For access token requests that include a scope (password or client_credentials grants), this error indicates an invalid scope value in the request.
|
|
20
|
+
*/
|
|
21
|
+
OAuthPathErrorType["invalid_scope"] = "invalid_scope";
|
|
22
|
+
/**
|
|
23
|
+
* This client is not authorized to use the requested grant type. For example, if you restrict which applications can use the Implicit grant, you would return this error for the other apps.
|
|
24
|
+
*/
|
|
25
|
+
OAuthPathErrorType["unauthorized_client"] = "unauthorized_client";
|
|
26
|
+
/**
|
|
27
|
+
* If a grant type is requested that the authorization server doesn’t recognize, use this code.
|
|
28
|
+
* Note that unknown grant types also use this specific error code rather than using the `invalid_request` above.
|
|
29
|
+
*/
|
|
30
|
+
OAuthPathErrorType["unsupported_grant_type"] = "unsupported_grant_type";
|
|
31
|
+
})(OAuthPathErrorType || (OAuthPathErrorType = {}));
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { stringify as qsStringify } from 'qs';
|
|
2
|
+
import { fetch } from '@whatwg-node/fetch';
|
|
3
|
+
const qsOptions = {
|
|
4
|
+
indices: false,
|
|
5
|
+
arrayFormat: 'repeat',
|
|
6
|
+
};
|
|
7
|
+
export class ClientValidationError extends Error {
|
|
8
|
+
constructor(path, method, errors, response) {
|
|
9
|
+
super(`Validation failed for ${method} ${path}`);
|
|
10
|
+
this.path = path;
|
|
11
|
+
this.method = method;
|
|
12
|
+
this.errors = errors;
|
|
13
|
+
this.response = response;
|
|
14
|
+
}
|
|
15
|
+
[Symbol.iterator]() {
|
|
16
|
+
return this.errors[Symbol.iterator]();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function useValidationErrors() {
|
|
20
|
+
return {
|
|
21
|
+
async onResponse({ path, method, response }) {
|
|
22
|
+
if (response.status === 400 && response.headers.get('x-error-type') === 'validation') {
|
|
23
|
+
const resJson = await response.json();
|
|
24
|
+
if (resJson.errors) {
|
|
25
|
+
throw new ClientValidationError(path, method, resJson.errors, response);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function createClient({ endpoint, fetchFn = fetch, plugins = [] }) {
|
|
32
|
+
plugins.unshift(useValidationErrors());
|
|
33
|
+
const onRequestInitHooks = [];
|
|
34
|
+
const onFetchHooks = [];
|
|
35
|
+
const onResponseHooks = [];
|
|
36
|
+
for (const plugin of plugins) {
|
|
37
|
+
if (plugin.onRequestInit) {
|
|
38
|
+
onRequestInitHooks.push(plugin.onRequestInit);
|
|
39
|
+
}
|
|
40
|
+
if (plugin.onFetch) {
|
|
41
|
+
onFetchHooks.push(plugin.onFetch);
|
|
42
|
+
}
|
|
43
|
+
if (plugin.onResponse) {
|
|
44
|
+
onResponseHooks.push(plugin.onResponse);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return new Proxy({}, {
|
|
48
|
+
get(_target, path) {
|
|
49
|
+
return new Proxy({}, {
|
|
50
|
+
get(_target, method) {
|
|
51
|
+
return async function (requestParams = {}) {
|
|
52
|
+
for (const pathParamKey in requestParams?.params || {}) {
|
|
53
|
+
const value = requestParams?.params?.[pathParamKey];
|
|
54
|
+
if (value) {
|
|
55
|
+
path = path.replace(`{${pathParamKey}}`, value).replace(`:${pathParamKey}`, value);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!path.startsWith('/') && !path.startsWith('http')) {
|
|
59
|
+
path = `/${path}`;
|
|
60
|
+
}
|
|
61
|
+
const requestInit = {
|
|
62
|
+
method,
|
|
63
|
+
headers: requestParams?.headers || {},
|
|
64
|
+
};
|
|
65
|
+
if (requestParams?.json) {
|
|
66
|
+
requestInit.body = JSON.stringify(requestParams.json);
|
|
67
|
+
requestInit.headers['Content-Type'] = 'application/json';
|
|
68
|
+
}
|
|
69
|
+
if (requestParams?.formData) {
|
|
70
|
+
requestInit.body = requestParams.formData;
|
|
71
|
+
}
|
|
72
|
+
if (requestParams?.formUrlEncoded) {
|
|
73
|
+
requestInit.body = qsStringify(requestParams.formUrlEncoded, qsOptions);
|
|
74
|
+
requestInit.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
75
|
+
}
|
|
76
|
+
let response;
|
|
77
|
+
for (const onRequestParamsHook of onRequestInitHooks) {
|
|
78
|
+
await onRequestParamsHook({
|
|
79
|
+
path,
|
|
80
|
+
method,
|
|
81
|
+
requestParams,
|
|
82
|
+
requestInit,
|
|
83
|
+
endResponse(res) {
|
|
84
|
+
response = res;
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
let finalUrl = path;
|
|
89
|
+
if (endpoint && !path.startsWith('http')) {
|
|
90
|
+
finalUrl = `${endpoint}${path}`;
|
|
91
|
+
}
|
|
92
|
+
if (requestParams?.query) {
|
|
93
|
+
const searchParams = qsStringify(requestParams.query, qsOptions);
|
|
94
|
+
if (finalUrl.includes('?')) {
|
|
95
|
+
finalUrl += '&' + searchParams;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
finalUrl += '?' + searchParams;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
let currentFetchFn = fetchFn;
|
|
102
|
+
for (const onFetchHook of onFetchHooks) {
|
|
103
|
+
await onFetchHook({
|
|
104
|
+
url: finalUrl,
|
|
105
|
+
init: requestInit,
|
|
106
|
+
fetchFn: currentFetchFn,
|
|
107
|
+
setFetchFn(newFetchFn) {
|
|
108
|
+
currentFetchFn = newFetchFn;
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
response ||= await currentFetchFn(finalUrl, requestInit);
|
|
113
|
+
for (const onResponseHook of onResponseHooks) {
|
|
114
|
+
await onResponseHook({
|
|
115
|
+
path,
|
|
116
|
+
method,
|
|
117
|
+
requestParams,
|
|
118
|
+
requestInit,
|
|
119
|
+
response,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return response;
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { parse } from '@whatwg-node/cookie-store';
|
|
2
|
+
import { Headers } from '@whatwg-node/fetch';
|
|
3
|
+
export function useClientCookieStore(cookieStore) {
|
|
4
|
+
return {
|
|
5
|
+
async onRequestInit({ requestInit }) {
|
|
6
|
+
requestInit.headers = new Headers(requestInit.headers);
|
|
7
|
+
let cookieHeader = requestInit.headers.get('cookie') || '';
|
|
8
|
+
if (cookieHeader) {
|
|
9
|
+
cookieHeader += '; ';
|
|
10
|
+
}
|
|
11
|
+
const cookies = await cookieStore.getAll();
|
|
12
|
+
cookieHeader += cookies.map(cookie => `${cookie.name}=${cookie.value}`).join('; ');
|
|
13
|
+
requestInit.headers.set('cookie', cookieHeader);
|
|
14
|
+
},
|
|
15
|
+
onResponse({ response }) {
|
|
16
|
+
const setCookies = response.headers.getSetCookie?.();
|
|
17
|
+
if (setCookies) {
|
|
18
|
+
for (const setCookie of setCookies) {
|
|
19
|
+
const cookieMap = parse(setCookie);
|
|
20
|
+
for (const [, cookie] of cookieMap) {
|
|
21
|
+
cookieStore.set(cookie);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import * as DefaultFetchAPI from '@whatwg-node/fetch';
|
|
2
|
+
import { createServerAdapter, isPromise } from '@whatwg-node/server';
|
|
3
|
+
import { useOpenAPI } from './plugins/openapi.js';
|
|
4
|
+
import { isLazySerializedResponse } from './Response.js';
|
|
5
|
+
import { addHandlersToMethod } from './utils.js';
|
|
6
|
+
import { useZod } from './zod/zod.js';
|
|
7
|
+
const HTTP_METHODS = [
|
|
8
|
+
'GET',
|
|
9
|
+
'HEAD',
|
|
10
|
+
'POST',
|
|
11
|
+
'PUT',
|
|
12
|
+
'DELETE',
|
|
13
|
+
'CONNECT',
|
|
14
|
+
'OPTIONS',
|
|
15
|
+
'TRACE',
|
|
16
|
+
'PATCH',
|
|
17
|
+
];
|
|
18
|
+
const EMPTY_OBJECT = {};
|
|
19
|
+
const EMPTY_MATCH = { pathname: { groups: {} } };
|
|
20
|
+
export function createRouterBase({ fetchAPI: givenFetchAPI, base: basePath = '/', plugins = [], swaggerUI, } = {}, openAPIDocument) {
|
|
21
|
+
const fetchAPI = {
|
|
22
|
+
...DefaultFetchAPI,
|
|
23
|
+
...givenFetchAPI,
|
|
24
|
+
};
|
|
25
|
+
const __onRouterInitHooks = [];
|
|
26
|
+
const onRouteHooks = [];
|
|
27
|
+
const onSerializeResponseHooks = [];
|
|
28
|
+
for (const plugin of plugins) {
|
|
29
|
+
if (plugin.onRouterInit) {
|
|
30
|
+
__onRouterInitHooks.push(plugin.onRouterInit);
|
|
31
|
+
}
|
|
32
|
+
if (plugin.onRoute) {
|
|
33
|
+
onRouteHooks.push(plugin.onRoute);
|
|
34
|
+
}
|
|
35
|
+
if (plugin.onSerializeResponse) {
|
|
36
|
+
onSerializeResponseHooks.push(plugin.onSerializeResponse);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const handlersByPatternByMethod = new Map();
|
|
40
|
+
const internalPatternsByMethod = new Map();
|
|
41
|
+
// Use this in `handle` for iteration to get better performance
|
|
42
|
+
const patternHandlerObjByMethod = new Map();
|
|
43
|
+
function handleUnhandledRoute() {
|
|
44
|
+
if (swaggerUI?.endpoint) {
|
|
45
|
+
return new fetchAPI.Response(null, {
|
|
46
|
+
status: 302,
|
|
47
|
+
headers: {
|
|
48
|
+
location: swaggerUI.endpoint,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return new fetchAPI.Response(null, { status: 404 });
|
|
53
|
+
}
|
|
54
|
+
function processHandlerResult(handlerResult, opts) {
|
|
55
|
+
if (handlerResult) {
|
|
56
|
+
if (isLazySerializedResponse(handlerResult)) {
|
|
57
|
+
const onSerializeResponseHookPayload = {
|
|
58
|
+
request: opts.routerRequest,
|
|
59
|
+
path: opts.pattern.pathname,
|
|
60
|
+
lazyResponse: handlerResult,
|
|
61
|
+
serverContext: opts.context,
|
|
62
|
+
};
|
|
63
|
+
for (const onSerializeResponseHook of onSerializeResponseHooks) {
|
|
64
|
+
onSerializeResponseHook(onSerializeResponseHookPayload);
|
|
65
|
+
}
|
|
66
|
+
return (handlerResult.actualResponse ||
|
|
67
|
+
fetchAPI.Response.json(handlerResult.jsonObj, handlerResult.init));
|
|
68
|
+
}
|
|
69
|
+
return handlerResult;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function asyncIterationUntilReturn(iterable, callback) {
|
|
73
|
+
const iterator = iterable[Symbol.iterator]();
|
|
74
|
+
function iterate() {
|
|
75
|
+
const { value, done } = iterator.next();
|
|
76
|
+
if (done) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (value) {
|
|
80
|
+
const callbackResult$ = callback(value);
|
|
81
|
+
if (isPromise(callbackResult$)) {
|
|
82
|
+
return callbackResult$.then(callbackResult => {
|
|
83
|
+
if (callbackResult) {
|
|
84
|
+
return callbackResult;
|
|
85
|
+
}
|
|
86
|
+
return iterate();
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (callbackResult$) {
|
|
90
|
+
return callbackResult$;
|
|
91
|
+
}
|
|
92
|
+
return iterate();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return iterate();
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
openAPIDocument,
|
|
99
|
+
handle(request, context) {
|
|
100
|
+
let url = new Proxy(EMPTY_OBJECT, {
|
|
101
|
+
get(_target, prop, _receiver) {
|
|
102
|
+
url = new fetchAPI.URL(request.url, 'http://localhost');
|
|
103
|
+
return Reflect.get(url, prop, url);
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
const methodPatternMaps = patternHandlerObjByMethod.get(request.method);
|
|
107
|
+
if (methodPatternMaps) {
|
|
108
|
+
const queryProxy = new Proxy({}, {
|
|
109
|
+
get(_, prop) {
|
|
110
|
+
if (prop !== 'then' && !url.searchParams.has(prop)) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
const allQueries = url.searchParams.getAll(prop.toString());
|
|
114
|
+
if (allQueries.length === 0) {
|
|
115
|
+
return '';
|
|
116
|
+
}
|
|
117
|
+
return allQueries.length === 1 ? allQueries[0] : allQueries;
|
|
118
|
+
},
|
|
119
|
+
has(_, prop) {
|
|
120
|
+
return url.searchParams.has(prop.toString());
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
const iterationResult$ = asyncIterationUntilReturn(methodPatternMaps, ({ pattern, handlers }) => {
|
|
124
|
+
// Do not parse URL if not needed
|
|
125
|
+
let match = null;
|
|
126
|
+
if (pattern.isPattern) {
|
|
127
|
+
match = pattern.exec(url);
|
|
128
|
+
}
|
|
129
|
+
else if (request.url.endsWith(pattern.pathname) ||
|
|
130
|
+
url.pathname === pattern.pathname) {
|
|
131
|
+
match = EMPTY_MATCH;
|
|
132
|
+
}
|
|
133
|
+
if (match != null) {
|
|
134
|
+
const routerRequest = new Proxy(request, {
|
|
135
|
+
get(target, prop) {
|
|
136
|
+
if (prop === 'parsedUrl') {
|
|
137
|
+
return url;
|
|
138
|
+
}
|
|
139
|
+
if (prop === 'params') {
|
|
140
|
+
return new Proxy(match.pathname.groups, {
|
|
141
|
+
get(_, prop) {
|
|
142
|
+
const value = match.pathname.groups[prop.toString()];
|
|
143
|
+
if (value != null) {
|
|
144
|
+
return decodeURIComponent(value);
|
|
145
|
+
}
|
|
146
|
+
return value;
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
if (prop === 'query') {
|
|
151
|
+
return queryProxy;
|
|
152
|
+
}
|
|
153
|
+
const targetProp = target[prop];
|
|
154
|
+
if (typeof targetProp === 'function') {
|
|
155
|
+
return targetProp.bind(target);
|
|
156
|
+
}
|
|
157
|
+
return targetProp;
|
|
158
|
+
},
|
|
159
|
+
has(target, prop) {
|
|
160
|
+
return (prop in target || prop === 'parsedUrl' || prop === 'params' || prop === 'query');
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
return asyncIterationUntilReturn(handlers, handler => {
|
|
164
|
+
const handlerResult$ = handler(routerRequest, context);
|
|
165
|
+
if (isPromise(handlerResult$)) {
|
|
166
|
+
return handlerResult$.then(handlerResult => {
|
|
167
|
+
if (handlerResult) {
|
|
168
|
+
return processHandlerResult(handlerResult, {
|
|
169
|
+
routerRequest,
|
|
170
|
+
context,
|
|
171
|
+
pattern,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
if (handlerResult$) {
|
|
177
|
+
return processHandlerResult(handlerResult$, {
|
|
178
|
+
routerRequest,
|
|
179
|
+
context,
|
|
180
|
+
pattern,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
if (isPromise(iterationResult$)) {
|
|
187
|
+
return iterationResult$.then(iterationResult => {
|
|
188
|
+
if (iterationResult) {
|
|
189
|
+
return iterationResult;
|
|
190
|
+
}
|
|
191
|
+
return handleUnhandledRoute();
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (iterationResult$) {
|
|
195
|
+
return iterationResult$;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return handleUnhandledRoute();
|
|
199
|
+
},
|
|
200
|
+
route(opts) {
|
|
201
|
+
const { operationId, description, method, path, schemas, tags, internal, handler } = opts;
|
|
202
|
+
const handlers = Array.isArray(handler) ? handler : [handler];
|
|
203
|
+
if (!method) {
|
|
204
|
+
for (const method of HTTP_METHODS) {
|
|
205
|
+
addHandlersToMethod({
|
|
206
|
+
operationId,
|
|
207
|
+
description,
|
|
208
|
+
method,
|
|
209
|
+
path,
|
|
210
|
+
schemas,
|
|
211
|
+
handlers,
|
|
212
|
+
tags,
|
|
213
|
+
internal,
|
|
214
|
+
// Router specific
|
|
215
|
+
onRouteHooks,
|
|
216
|
+
openAPIDocument,
|
|
217
|
+
basePath,
|
|
218
|
+
fetchAPI,
|
|
219
|
+
handlersByPatternByMethod,
|
|
220
|
+
internalPatternsByMethod,
|
|
221
|
+
patternHandlerObjByMethod,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
addHandlersToMethod({
|
|
227
|
+
operationId,
|
|
228
|
+
description,
|
|
229
|
+
method,
|
|
230
|
+
path,
|
|
231
|
+
schemas,
|
|
232
|
+
handlers,
|
|
233
|
+
tags,
|
|
234
|
+
internal,
|
|
235
|
+
// Router specific
|
|
236
|
+
onRouteHooks,
|
|
237
|
+
openAPIDocument,
|
|
238
|
+
basePath,
|
|
239
|
+
fetchAPI,
|
|
240
|
+
handlersByPatternByMethod,
|
|
241
|
+
internalPatternsByMethod,
|
|
242
|
+
patternHandlerObjByMethod,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
return this;
|
|
246
|
+
},
|
|
247
|
+
__client: {},
|
|
248
|
+
__onRouterInitHooks,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
export function createRouter(options = {}) {
|
|
252
|
+
const { openAPI: { endpoint: oasEndpoint = '/openapi.json', ...openAPIDocument } = {}, swaggerUI: { endpoint: swaggerUIEndpoint = '/docs', ...swaggerUIOpts } = {}, plugins: userPlugins = [], base = '/', } = options;
|
|
253
|
+
openAPIDocument.openapi = openAPIDocument.openapi || '3.0.1';
|
|
254
|
+
const oasInfo = (openAPIDocument.info ||= {});
|
|
255
|
+
oasInfo.title ||= 'feTS API';
|
|
256
|
+
oasInfo.description ||= 'An API written with feTS';
|
|
257
|
+
oasInfo.version ||= '1.0.0';
|
|
258
|
+
if (base !== '/') {
|
|
259
|
+
openAPIDocument.servers = openAPIDocument.servers || [
|
|
260
|
+
{
|
|
261
|
+
url: base,
|
|
262
|
+
},
|
|
263
|
+
];
|
|
264
|
+
}
|
|
265
|
+
const plugins = [
|
|
266
|
+
...(oasEndpoint || swaggerUIEndpoint
|
|
267
|
+
? [
|
|
268
|
+
useOpenAPI({
|
|
269
|
+
oasEndpoint,
|
|
270
|
+
swaggerUIEndpoint,
|
|
271
|
+
swaggerUIOpts,
|
|
272
|
+
}),
|
|
273
|
+
]
|
|
274
|
+
: []),
|
|
275
|
+
useZod(),
|
|
276
|
+
...userPlugins,
|
|
277
|
+
];
|
|
278
|
+
const finalOpts = {
|
|
279
|
+
...options,
|
|
280
|
+
swaggerUI: {
|
|
281
|
+
endpoint: swaggerUIEndpoint,
|
|
282
|
+
...swaggerUIOpts,
|
|
283
|
+
},
|
|
284
|
+
base,
|
|
285
|
+
plugins,
|
|
286
|
+
};
|
|
287
|
+
const routerBaseObject = createRouterBase(finalOpts, openAPIDocument);
|
|
288
|
+
const router = createServerAdapter(routerBaseObject, finalOpts);
|
|
289
|
+
for (const onRouterInitHook of routerBaseObject.__onRouterInitHooks) {
|
|
290
|
+
onRouterInitHook(router);
|
|
291
|
+
}
|
|
292
|
+
return router;
|
|
293
|
+
}
|
package/esm/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './types.js';
|
|
2
|
+
export * from './createRouter.js';
|
|
3
|
+
export { URLPattern } from '@whatwg-node/fetch';
|
|
4
|
+
export { useCORS, useErrorHandling, HTTPError } from '@whatwg-node/server';
|
|
5
|
+
export * from './client/index.js';
|
|
6
|
+
export * from './Response.js';
|
|
7
|
+
export { useAjv } from './plugins/ajv.js';
|