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,208 @@
|
|
|
1
|
+
import Ajv from 'ajv';
|
|
2
|
+
import addFormats from 'ajv-formats';
|
|
3
|
+
import jsonSerializerFactory from '@ardatan/fast-json-stringify';
|
|
4
|
+
import { URL } from '@whatwg-node/fetch';
|
|
5
|
+
import { Response } from '../Response.js';
|
|
6
|
+
import { isZodSchema } from '../zod/types.js';
|
|
7
|
+
import { getHeadersObj } from './utils.js';
|
|
8
|
+
export function useAjv({ components = {}, } = {}) {
|
|
9
|
+
const ajv = new Ajv({
|
|
10
|
+
strict: false,
|
|
11
|
+
strictSchema: false,
|
|
12
|
+
validateSchema: false,
|
|
13
|
+
allowUnionTypes: true,
|
|
14
|
+
uriResolver: {
|
|
15
|
+
parse(uri) {
|
|
16
|
+
const url = new URL(uri);
|
|
17
|
+
return {
|
|
18
|
+
scheme: url.protocol,
|
|
19
|
+
userinfo: url.username + (url.password ? ':' + url.password : ''),
|
|
20
|
+
host: url.hostname,
|
|
21
|
+
port: url.port,
|
|
22
|
+
path: url.pathname,
|
|
23
|
+
query: url.search,
|
|
24
|
+
fragment: url.hash,
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
resolve(base, ref) {
|
|
28
|
+
return new URL(ref, base).toString();
|
|
29
|
+
},
|
|
30
|
+
serialize(components) {
|
|
31
|
+
return (components.scheme +
|
|
32
|
+
'://' +
|
|
33
|
+
components.userinfo +
|
|
34
|
+
components.host +
|
|
35
|
+
components.port +
|
|
36
|
+
components.path +
|
|
37
|
+
components.query +
|
|
38
|
+
components.fragment);
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
addFormats(ajv);
|
|
43
|
+
// Required for fast-json-stringify
|
|
44
|
+
ajv.addKeyword({
|
|
45
|
+
keyword: 'fjs_type',
|
|
46
|
+
type: 'object',
|
|
47
|
+
errors: false,
|
|
48
|
+
validate: (_type, date) => {
|
|
49
|
+
return date instanceof Date;
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
const serializersByPath = new Map();
|
|
53
|
+
return {
|
|
54
|
+
onRoute({ path, schemas, handlers }) {
|
|
55
|
+
const validationMiddlewares = new Map();
|
|
56
|
+
if (schemas?.request?.headers && !isZodSchema(schemas.request.headers)) {
|
|
57
|
+
const validateFn = ajv.compile({
|
|
58
|
+
...schemas.request.headers,
|
|
59
|
+
components,
|
|
60
|
+
});
|
|
61
|
+
validationMiddlewares.set('headers', request => {
|
|
62
|
+
const headersObj = getHeadersObj(request.headers);
|
|
63
|
+
const isValid = validateFn(headersObj);
|
|
64
|
+
if (!isValid) {
|
|
65
|
+
return validateFn.errors;
|
|
66
|
+
}
|
|
67
|
+
return [];
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (schemas?.request?.params && !isZodSchema(schemas.request.params)) {
|
|
71
|
+
const validateFn = ajv.compile({
|
|
72
|
+
...schemas.request.params,
|
|
73
|
+
components,
|
|
74
|
+
});
|
|
75
|
+
validationMiddlewares.set('params', request => {
|
|
76
|
+
const isValid = validateFn(request.params);
|
|
77
|
+
if (!isValid) {
|
|
78
|
+
return validateFn.errors;
|
|
79
|
+
}
|
|
80
|
+
return [];
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (schemas?.request?.query && !isZodSchema(schemas.request.query)) {
|
|
84
|
+
const validateFn = ajv.compile({
|
|
85
|
+
...schemas.request.query,
|
|
86
|
+
components,
|
|
87
|
+
});
|
|
88
|
+
validationMiddlewares.set('query', request => {
|
|
89
|
+
const isValid = validateFn(request.query);
|
|
90
|
+
if (!isValid) {
|
|
91
|
+
return validateFn.errors;
|
|
92
|
+
}
|
|
93
|
+
return [];
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (schemas?.request?.json && !isZodSchema(schemas.request.json)) {
|
|
97
|
+
const validateFn = ajv.compile({
|
|
98
|
+
...schemas.request.json,
|
|
99
|
+
components,
|
|
100
|
+
});
|
|
101
|
+
validationMiddlewares.set('json', async (request) => {
|
|
102
|
+
const contentType = request.headers.get('content-type');
|
|
103
|
+
if (contentType?.includes('json')) {
|
|
104
|
+
const jsonObj = await request.json();
|
|
105
|
+
Object.defineProperty(request, 'json', {
|
|
106
|
+
value: async () => jsonObj,
|
|
107
|
+
configurable: true,
|
|
108
|
+
});
|
|
109
|
+
const isValid = validateFn(jsonObj);
|
|
110
|
+
if (!isValid) {
|
|
111
|
+
return validateFn.errors;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return [];
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
if (schemas?.request?.formData && !isZodSchema(schemas.request.formData)) {
|
|
118
|
+
const validateFn = ajv.compile({
|
|
119
|
+
...schemas.request.formData,
|
|
120
|
+
components,
|
|
121
|
+
});
|
|
122
|
+
validationMiddlewares.set('formData', async (request) => {
|
|
123
|
+
const contentType = request.headers.get('content-type');
|
|
124
|
+
if (contentType?.includes('multipart/form-data') ||
|
|
125
|
+
contentType?.includes('application/x-www-form-urlencoded')) {
|
|
126
|
+
const formData = await request.formData();
|
|
127
|
+
const formDataObj = {};
|
|
128
|
+
const jobs = [];
|
|
129
|
+
formData.forEach((value, key) => {
|
|
130
|
+
if (typeof value === 'string') {
|
|
131
|
+
formDataObj[key] = value;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
jobs.push(value.arrayBuffer().then(buffer => {
|
|
135
|
+
const typedArray = new Uint8Array(buffer);
|
|
136
|
+
const binaryStrParts = [];
|
|
137
|
+
typedArray.forEach((byte, index) => {
|
|
138
|
+
binaryStrParts[index] = String.fromCharCode(byte);
|
|
139
|
+
});
|
|
140
|
+
formDataObj[key] = binaryStrParts.join('');
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
await Promise.all(jobs);
|
|
145
|
+
Object.defineProperty(request, 'formData', {
|
|
146
|
+
value: async () => formData,
|
|
147
|
+
configurable: true,
|
|
148
|
+
});
|
|
149
|
+
const isValid = validateFn(formDataObj);
|
|
150
|
+
if (!isValid) {
|
|
151
|
+
return validateFn.errors;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return [];
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
if (jsonSerializerFactory && schemas?.responses) {
|
|
158
|
+
const serializerByStatusCode = new Map();
|
|
159
|
+
for (const statusCode in schemas.responses) {
|
|
160
|
+
const schema = schemas.responses[statusCode];
|
|
161
|
+
if (!isZodSchema(schema)) {
|
|
162
|
+
const serializer = jsonSerializerFactory({
|
|
163
|
+
...schema,
|
|
164
|
+
components,
|
|
165
|
+
}, {
|
|
166
|
+
ajv,
|
|
167
|
+
});
|
|
168
|
+
serializerByStatusCode.set(Number(statusCode), serializer);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
serializersByPath.set(path, serializerByStatusCode);
|
|
172
|
+
}
|
|
173
|
+
if (validationMiddlewares.size > 0) {
|
|
174
|
+
handlers.unshift(async (request) => {
|
|
175
|
+
const validationErrorsNonFlat = await Promise.all([...validationMiddlewares.entries()].map(async ([name, fn]) => {
|
|
176
|
+
const errors = await fn(request);
|
|
177
|
+
if (errors.length > 0) {
|
|
178
|
+
return errors.map(error => ({
|
|
179
|
+
name,
|
|
180
|
+
...error,
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
}));
|
|
184
|
+
const validationErrors = validationErrorsNonFlat.flat().filter(Boolean);
|
|
185
|
+
if (validationErrors.length > 0) {
|
|
186
|
+
return Response.json({
|
|
187
|
+
errors: validationErrors,
|
|
188
|
+
}, {
|
|
189
|
+
status: 400,
|
|
190
|
+
headers: {
|
|
191
|
+
'x-error-type': 'validation',
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
onSerializeResponse({ path, lazyResponse }) {
|
|
199
|
+
const serializers = serializersByPath.get(path);
|
|
200
|
+
if (serializers) {
|
|
201
|
+
const serializer = serializers.get(lazyResponse.init?.status || 200);
|
|
202
|
+
if (serializer) {
|
|
203
|
+
lazyResponse.resolveWithSerializer(serializer);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
2
|
+
import { Response } from '../Response.js';
|
|
3
|
+
import swaggerUiHtml from '../swagger-ui-html.js';
|
|
4
|
+
import { isZodSchema } from '../zod/types.js';
|
|
5
|
+
export function useOpenAPI({ oasEndpoint, swaggerUIEndpoint, swaggerUIOpts, }) {
|
|
6
|
+
let paths;
|
|
7
|
+
return {
|
|
8
|
+
onRouterInit(router) {
|
|
9
|
+
paths = router.openAPIDocument.paths = router.openAPIDocument.paths || {};
|
|
10
|
+
if (oasEndpoint) {
|
|
11
|
+
router.route({
|
|
12
|
+
method: 'GET',
|
|
13
|
+
path: oasEndpoint,
|
|
14
|
+
internal: true,
|
|
15
|
+
handler: () => Response.json(router.openAPIDocument),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
if (swaggerUIEndpoint) {
|
|
19
|
+
router.route({
|
|
20
|
+
method: 'GET',
|
|
21
|
+
path: swaggerUIEndpoint,
|
|
22
|
+
internal: true,
|
|
23
|
+
handler: () => new Response(swaggerUiHtml.replace('__SWAGGER_UI_OPTIONS__', JSON.stringify({
|
|
24
|
+
spec: router.openAPIDocument,
|
|
25
|
+
dom_id: '#swagger-ui',
|
|
26
|
+
displayOperationId: true,
|
|
27
|
+
tryItOutEnabled: true,
|
|
28
|
+
requestSnippetsEnabled: true,
|
|
29
|
+
displayRequestDuration: true,
|
|
30
|
+
defaultModelRendering: 'model',
|
|
31
|
+
defaultModelExpandDepth: 3,
|
|
32
|
+
defaultModelsExpandDepth: 3,
|
|
33
|
+
...swaggerUIOpts,
|
|
34
|
+
})), {
|
|
35
|
+
headers: {
|
|
36
|
+
'Content-Type': 'text/html',
|
|
37
|
+
},
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
onRoute({ method, path, operationId, description, tags, schemas }) {
|
|
43
|
+
if (schemas) {
|
|
44
|
+
let pathForOAS = path.replace(/:([^/]+)/g, '{$1}');
|
|
45
|
+
if (!pathForOAS.startsWith('/')) {
|
|
46
|
+
pathForOAS = `/${pathForOAS}`;
|
|
47
|
+
}
|
|
48
|
+
const pathObj = (paths[pathForOAS] = paths[pathForOAS] || {});
|
|
49
|
+
const lowerCasedMethod = method.toLowerCase();
|
|
50
|
+
pathObj[lowerCasedMethod] = pathObj[lowerCasedMethod] || {};
|
|
51
|
+
const operation = pathObj[lowerCasedMethod];
|
|
52
|
+
operation.operationId = operationId;
|
|
53
|
+
operation.description = description;
|
|
54
|
+
operation.tags = tags;
|
|
55
|
+
if (schemas.responses) {
|
|
56
|
+
for (const statusCode in schemas.responses) {
|
|
57
|
+
let responseSchema = schemas.responses[statusCode];
|
|
58
|
+
if (isZodSchema(responseSchema)) {
|
|
59
|
+
responseSchema = zodToJsonSchema(responseSchema, {
|
|
60
|
+
target: 'openApi3',
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
operation.responses = operation.responses || {};
|
|
64
|
+
operation.responses[statusCode] = {
|
|
65
|
+
description: '',
|
|
66
|
+
content: {
|
|
67
|
+
'application/json': {
|
|
68
|
+
schema: responseSchema,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
operation.responses = {
|
|
76
|
+
default: {
|
|
77
|
+
description: '',
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (schemas.request?.headers) {
|
|
82
|
+
let headersSchema = schemas.request.headers;
|
|
83
|
+
if (isZodSchema(headersSchema)) {
|
|
84
|
+
headersSchema = zodToJsonSchema(headersSchema, {
|
|
85
|
+
target: 'openApi3',
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
for (const headerName in headersSchema.properties) {
|
|
89
|
+
const headerSchema = headersSchema.properties[headerName];
|
|
90
|
+
operation.parameters = operation.parameters || [];
|
|
91
|
+
operation.parameters.push({
|
|
92
|
+
name: headerName,
|
|
93
|
+
in: 'header',
|
|
94
|
+
required: headersSchema.required?.includes(headerName),
|
|
95
|
+
schema: headerSchema,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (schemas.request?.params) {
|
|
100
|
+
let paramsSchema = schemas.request.params;
|
|
101
|
+
if (isZodSchema(paramsSchema)) {
|
|
102
|
+
paramsSchema = zodToJsonSchema(paramsSchema, {
|
|
103
|
+
target: 'openApi3',
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
for (const paramName in paramsSchema.properties) {
|
|
107
|
+
const paramSchema = paramsSchema.properties[paramName];
|
|
108
|
+
operation.parameters = operation.parameters || [];
|
|
109
|
+
operation.parameters.push({
|
|
110
|
+
name: paramName,
|
|
111
|
+
in: 'path',
|
|
112
|
+
required: paramsSchema.required?.includes(paramName),
|
|
113
|
+
schema: paramSchema,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (schemas.request?.query) {
|
|
118
|
+
let queriesSchema = schemas.request.query;
|
|
119
|
+
if (isZodSchema(queriesSchema)) {
|
|
120
|
+
queriesSchema = zodToJsonSchema(queriesSchema, {
|
|
121
|
+
target: 'openApi3',
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
for (const queryName in queriesSchema.properties) {
|
|
125
|
+
const querySchema = queriesSchema.properties[queryName];
|
|
126
|
+
operation.parameters = operation.parameters || [];
|
|
127
|
+
operation.parameters.push({
|
|
128
|
+
name: queryName,
|
|
129
|
+
in: 'query',
|
|
130
|
+
required: queriesSchema.required?.includes(queryName),
|
|
131
|
+
schema: querySchema,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (schemas.request?.json) {
|
|
136
|
+
let requestJsonSchema = schemas.request.json;
|
|
137
|
+
if (isZodSchema(requestJsonSchema)) {
|
|
138
|
+
requestJsonSchema = zodToJsonSchema(requestJsonSchema, {
|
|
139
|
+
target: 'openApi3',
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
const requestBody = (operation.requestBody = (operation.requestBody || {}));
|
|
143
|
+
requestBody.required = true;
|
|
144
|
+
const requestBodyContent = (requestBody.content = (requestBody.content || {}));
|
|
145
|
+
requestBodyContent['application/json'] = {
|
|
146
|
+
schema: requestJsonSchema,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (schemas.request?.formData) {
|
|
150
|
+
const requestBody = (operation.requestBody = (operation.requestBody || {}));
|
|
151
|
+
requestBody.required = true;
|
|
152
|
+
const requestBodyContent = (requestBody.content = (requestBody.content || {}));
|
|
153
|
+
let requestFormDataSchema = schemas.request.formData;
|
|
154
|
+
if (isZodSchema(requestFormDataSchema)) {
|
|
155
|
+
requestFormDataSchema = zodToJsonSchema(requestFormDataSchema, {
|
|
156
|
+
target: 'openApi3',
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
requestBodyContent['multipart/form-data'] = {
|
|
160
|
+
schema: requestFormDataSchema,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function getHeadersObj(headers) {
|
|
2
|
+
return new Proxy({}, {
|
|
3
|
+
get(_target, prop) {
|
|
4
|
+
return headers.get(prop) || undefined;
|
|
5
|
+
},
|
|
6
|
+
set(_target, prop, value) {
|
|
7
|
+
headers.set(prop, value);
|
|
8
|
+
return true;
|
|
9
|
+
},
|
|
10
|
+
has(_target, prop) {
|
|
11
|
+
return headers.has(prop);
|
|
12
|
+
},
|
|
13
|
+
deleteProperty(_target, prop) {
|
|
14
|
+
headers.delete(prop);
|
|
15
|
+
return true;
|
|
16
|
+
},
|
|
17
|
+
ownKeys() {
|
|
18
|
+
return [...headers.keys()];
|
|
19
|
+
},
|
|
20
|
+
getOwnPropertyDescriptor() {
|
|
21
|
+
return {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
configurable: true,
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default "<!doctype html><html lang=en><head><meta charset=utf-8><meta name=viewport content=\"width=device-width,initial-scale=1\"><meta name=description content=SwaggerUI><title>SwaggerUI</title><style>@import url(https://cdn.jsdelivr.net/gh/Itz-fork/Fastapi-Swagger-UI-Dark/assets/swagger_ui_dark.min.css) (prefers-color-scheme: dark);@import url(https://unpkg.com/swagger-ui-dist/swagger-ui.css) (prefers-color-scheme: light);@media (prefers-color-scheme:dark){.swagger-ui textarea{background:hsl(358.8deg 1.05% 1.91% / 80%)!important}.swagger-ui .scheme-container{background:#1e1e2e}.swagger-ui select{background:rgba(0,0,0,.2)}}</style></head><body><div id=swagger-ui></div><script src=https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js crossorigin></script><script>window.onload=()=>{window.ui=SwaggerUIBundle(__SWAGGER_UI_OPTIONS__)}</script></body></html>";
|
|
File without changes
|
package/esm/types.js
ADDED
|
File without changes
|
package/esm/utils.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// This is used on runtime for optimization
|
|
2
|
+
function preparePatternHandlerObjByMethod({ handlersByPatternByMethod, patternHandlerObjByMethod, }) {
|
|
3
|
+
for (const [method, patternHandlersMap] of handlersByPatternByMethod) {
|
|
4
|
+
const patternHandlerObjList = [];
|
|
5
|
+
for (const [pattern, handlers] of patternHandlersMap) {
|
|
6
|
+
patternHandlerObjList.push({
|
|
7
|
+
pattern,
|
|
8
|
+
handlers,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
patternHandlerObjByMethod.set(method, patternHandlerObjList);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function addHandlersToMethod({ operationId, description, tags, method, path, schemas, handlers, internal, onRouteHooks, openAPIDocument, basePath, fetchAPI, handlersByPatternByMethod, internalPatternsByMethod, patternHandlerObjByMethod, }) {
|
|
15
|
+
for (const onRouteHook of onRouteHooks) {
|
|
16
|
+
onRouteHook({
|
|
17
|
+
operationId,
|
|
18
|
+
description,
|
|
19
|
+
tags,
|
|
20
|
+
openAPIDocument,
|
|
21
|
+
method,
|
|
22
|
+
path,
|
|
23
|
+
schemas,
|
|
24
|
+
handlers,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
let methodPatternMaps = handlersByPatternByMethod.get(method);
|
|
28
|
+
if (!methodPatternMaps) {
|
|
29
|
+
methodPatternMaps = new Map();
|
|
30
|
+
handlersByPatternByMethod.set(method, methodPatternMaps);
|
|
31
|
+
}
|
|
32
|
+
let fullPath = '';
|
|
33
|
+
if (basePath === '/') {
|
|
34
|
+
fullPath = path;
|
|
35
|
+
}
|
|
36
|
+
else if (path === '/') {
|
|
37
|
+
fullPath = basePath;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
fullPath = `${basePath}${path}`;
|
|
41
|
+
}
|
|
42
|
+
const pattern = new fetchAPI.URLPattern({ pathname: fullPath });
|
|
43
|
+
pattern.isPattern = fullPath.includes(':') || fullPath.includes('*');
|
|
44
|
+
methodPatternMaps.set(pattern, handlers);
|
|
45
|
+
// TODO: Better logic to make sure internal routes are always last
|
|
46
|
+
let internalPatterns = internalPatternsByMethod.get(method);
|
|
47
|
+
if (internal) {
|
|
48
|
+
if (!internalPatterns) {
|
|
49
|
+
internalPatterns = new Set();
|
|
50
|
+
internalPatternsByMethod.set(method, internalPatterns);
|
|
51
|
+
}
|
|
52
|
+
internalPatterns.add(pattern);
|
|
53
|
+
}
|
|
54
|
+
if (internalPatterns?.size) {
|
|
55
|
+
handlersByPatternByMethod.set(method, new Map([...methodPatternMaps.entries()].sort(([a], [b]) => {
|
|
56
|
+
if (internalPatterns.has(a)) {
|
|
57
|
+
return 1;
|
|
58
|
+
}
|
|
59
|
+
else if (internalPatterns.has(b)) {
|
|
60
|
+
return -1;
|
|
61
|
+
}
|
|
62
|
+
return 0;
|
|
63
|
+
})));
|
|
64
|
+
}
|
|
65
|
+
preparePatternHandlerObjByMethod({
|
|
66
|
+
handlersByPatternByMethod,
|
|
67
|
+
patternHandlerObjByMethod,
|
|
68
|
+
});
|
|
69
|
+
}
|
package/esm/zod/types.js
ADDED
package/esm/zod/zod.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { getHeadersObj } from '../plugins/utils.js';
|
|
2
|
+
import { Response } from '../Response.js';
|
|
3
|
+
import { isZodSchema } from './types.js';
|
|
4
|
+
export function useZod() {
|
|
5
|
+
return {
|
|
6
|
+
onRoute({ schemas, handlers }) {
|
|
7
|
+
if (schemas) {
|
|
8
|
+
const validationMiddlewares = new Map();
|
|
9
|
+
const requestSchemas = schemas.request;
|
|
10
|
+
if (requestSchemas) {
|
|
11
|
+
if (isZodSchema(requestSchemas.headers)) {
|
|
12
|
+
const headersSchema = requestSchemas.headers;
|
|
13
|
+
validationMiddlewares.set('headers', request => {
|
|
14
|
+
const headersObj = getHeadersObj(request.headers);
|
|
15
|
+
const result = headersSchema.safeParse(headersObj);
|
|
16
|
+
if (!result.success) {
|
|
17
|
+
return result.error.issues;
|
|
18
|
+
}
|
|
19
|
+
return [];
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
if (isZodSchema(requestSchemas.params)) {
|
|
23
|
+
const paramsSchema = requestSchemas.params;
|
|
24
|
+
validationMiddlewares.set('params', request => {
|
|
25
|
+
const result = paramsSchema.safeParse(request.params);
|
|
26
|
+
if (!result.success) {
|
|
27
|
+
return result.error.issues;
|
|
28
|
+
}
|
|
29
|
+
return [];
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
if (isZodSchema(requestSchemas.query)) {
|
|
33
|
+
const querySchema = requestSchemas.query;
|
|
34
|
+
validationMiddlewares.set('query', request => {
|
|
35
|
+
const result = querySchema.safeParse(request.query);
|
|
36
|
+
if (!result.success) {
|
|
37
|
+
return result.error.issues;
|
|
38
|
+
}
|
|
39
|
+
return [];
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (isZodSchema(requestSchemas.json)) {
|
|
43
|
+
const jsonSchema = requestSchemas.json;
|
|
44
|
+
validationMiddlewares.set('json', async (request) => {
|
|
45
|
+
const contentType = request.headers.get('content-type');
|
|
46
|
+
if (contentType?.includes('json')) {
|
|
47
|
+
const jsonObj = await request.json();
|
|
48
|
+
Object.defineProperty(request, 'json', {
|
|
49
|
+
value: async () => jsonObj,
|
|
50
|
+
configurable: true,
|
|
51
|
+
});
|
|
52
|
+
const result = jsonSchema.safeParse(jsonObj);
|
|
53
|
+
if (!result.success) {
|
|
54
|
+
return result.error.issues;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return [];
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (validationMiddlewares.size > 0) {
|
|
61
|
+
handlers.unshift(async (request) => {
|
|
62
|
+
const validationErrorsNonFlat = await Promise.all([...validationMiddlewares.entries()].map(async ([name, fn]) => {
|
|
63
|
+
const errors = await fn(request);
|
|
64
|
+
if (errors.length > 0) {
|
|
65
|
+
return errors.map(error => ({
|
|
66
|
+
name,
|
|
67
|
+
...error,
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
}));
|
|
71
|
+
const validationErrors = validationErrorsNonFlat.flat().filter(Boolean);
|
|
72
|
+
if (validationErrors.length > 0) {
|
|
73
|
+
return Response.json({
|
|
74
|
+
errors: validationErrors,
|
|
75
|
+
}, {
|
|
76
|
+
status: 400,
|
|
77
|
+
headers: {
|
|
78
|
+
'x-error-type': 'validation',
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { StatusCode, TypedResponse, TypedResponseCtor } from './typed-fetch.cjs';
|
|
2
|
+
import { JSONSerializer } from './types.cjs';
|
|
3
|
+
export declare const LAZY_SERIALIZED_RESPONSE: unique symbol;
|
|
4
|
+
export declare const defaultSerializer: JSONSerializer;
|
|
5
|
+
export interface LazySerializedResponse {
|
|
6
|
+
[LAZY_SERIALIZED_RESPONSE]: true;
|
|
7
|
+
resolveWithSerializer(serializer: JSONSerializer): void;
|
|
8
|
+
init?: ResponseInit;
|
|
9
|
+
actualResponse: Response;
|
|
10
|
+
jsonObj: any;
|
|
11
|
+
json: () => Promise<any>;
|
|
12
|
+
status: StatusCode;
|
|
13
|
+
headers: Headers;
|
|
14
|
+
}
|
|
15
|
+
export declare function isLazySerializedResponse(response: any): response is LazySerializedResponse;
|
|
16
|
+
export declare function createLazySerializedResponse(jsonObj: any, init?: ResponseInit): LazySerializedResponse;
|
|
17
|
+
/**
|
|
18
|
+
* The Response interface of the Fetch API represents the response to a request.
|
|
19
|
+
* It contains the status of the response, as well as the response headers, and
|
|
20
|
+
* an optional response body.
|
|
21
|
+
*
|
|
22
|
+
* @param body An object defining a body for the response. This can be null (which is the default value), or a Blob, BufferSource, FormData, Node.js Readable stream, URLSearchParams, or USVString object. The USVString is handled as UTF-8.
|
|
23
|
+
* @param options An options object containing any custom settings that you want to apply to the response, or an empty object (which is the default value).
|
|
24
|
+
*
|
|
25
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Response
|
|
26
|
+
*/
|
|
27
|
+
export declare const Response: TypedResponseCtor;
|
|
28
|
+
export type Response<TJSON = any, THeaders extends Record<string, string> = Record<string, string>, TStatusCode extends StatusCode = StatusCode> = TypedResponse<TJSON, THeaders, TStatusCode>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { StatusCode, TypedResponse, TypedResponseCtor } from './typed-fetch.js';
|
|
2
|
+
import { JSONSerializer } from './types.js';
|
|
3
|
+
export declare const LAZY_SERIALIZED_RESPONSE: unique symbol;
|
|
4
|
+
export declare const defaultSerializer: JSONSerializer;
|
|
5
|
+
export interface LazySerializedResponse {
|
|
6
|
+
[LAZY_SERIALIZED_RESPONSE]: true;
|
|
7
|
+
resolveWithSerializer(serializer: JSONSerializer): void;
|
|
8
|
+
init?: ResponseInit;
|
|
9
|
+
actualResponse: Response;
|
|
10
|
+
jsonObj: any;
|
|
11
|
+
json: () => Promise<any>;
|
|
12
|
+
status: StatusCode;
|
|
13
|
+
headers: Headers;
|
|
14
|
+
}
|
|
15
|
+
export declare function isLazySerializedResponse(response: any): response is LazySerializedResponse;
|
|
16
|
+
export declare function createLazySerializedResponse(jsonObj: any, init?: ResponseInit): LazySerializedResponse;
|
|
17
|
+
/**
|
|
18
|
+
* The Response interface of the Fetch API represents the response to a request.
|
|
19
|
+
* It contains the status of the response, as well as the response headers, and
|
|
20
|
+
* an optional response body.
|
|
21
|
+
*
|
|
22
|
+
* @param body An object defining a body for the response. This can be null (which is the default value), or a Blob, BufferSource, FormData, Node.js Readable stream, URLSearchParams, or USVString object. The USVString is handled as UTF-8.
|
|
23
|
+
* @param options An options object containing any custom settings that you want to apply to the response, or an empty object (which is the default value).
|
|
24
|
+
*
|
|
25
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Response
|
|
26
|
+
*/
|
|
27
|
+
export declare const Response: TypedResponseCtor;
|
|
28
|
+
export type Response<TJSON = any, THeaders extends Record<string, string> = Record<string, string>, TStatusCode extends StatusCode = StatusCode> = TypedResponse<TJSON, THeaders, TStatusCode>;
|