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.
Files changed (69) hide show
  1. package/cjs/Response.js +80 -0
  2. package/cjs/client/auth/oauth.js +34 -0
  3. package/cjs/client/createClient.js +133 -0
  4. package/cjs/client/index.js +6 -0
  5. package/cjs/client/plugins/useClientCookieStore.js +31 -0
  6. package/cjs/client/types.js +0 -0
  7. package/cjs/createRouter.js +299 -0
  8. package/cjs/index.js +16 -0
  9. package/cjs/plugins/ajv.js +213 -0
  10. package/cjs/plugins/openapi.js +171 -0
  11. package/cjs/plugins/utils.js +31 -0
  12. package/cjs/swagger-ui-html.js +3 -0
  13. package/cjs/typed-fetch.js +0 -0
  14. package/cjs/types.js +0 -0
  15. package/cjs/utils.js +73 -0
  16. package/cjs/zod/types.js +7 -0
  17. package/cjs/zod/zod.js +92 -0
  18. package/esm/Response.js +74 -0
  19. package/esm/client/auth/oauth.js +31 -0
  20. package/esm/client/createClient.js +128 -0
  21. package/esm/client/index.js +3 -0
  22. package/esm/client/plugins/useClientCookieStore.js +27 -0
  23. package/esm/client/types.js +0 -0
  24. package/esm/createRouter.js +293 -0
  25. package/esm/index.js +7 -0
  26. package/esm/plugins/ajv.js +208 -0
  27. package/esm/plugins/openapi.js +166 -0
  28. package/esm/plugins/utils.js +27 -0
  29. package/esm/swagger-ui-html.js +1 -0
  30. package/esm/typed-fetch.js +0 -0
  31. package/esm/types.js +0 -0
  32. package/esm/utils.js +69 -0
  33. package/esm/zod/types.js +3 -0
  34. package/esm/zod/zod.js +88 -0
  35. package/package.json +1 -1
  36. package/typings/Response.d.cts +28 -0
  37. package/typings/Response.d.ts +28 -0
  38. package/typings/client/auth/oauth.d.cts +150 -0
  39. package/typings/client/auth/oauth.d.ts +150 -0
  40. package/typings/client/createClient.d.cts +34 -0
  41. package/typings/client/createClient.d.ts +34 -0
  42. package/typings/client/index.d.cts +3 -0
  43. package/typings/client/index.d.ts +3 -0
  44. package/typings/client/plugins/useClientCookieStore.d.cts +3 -0
  45. package/typings/client/plugins/useClientCookieStore.d.ts +3 -0
  46. package/typings/client/types.d.cts +426 -0
  47. package/typings/client/types.d.ts +426 -0
  48. package/typings/createRouter.d.cts +6 -0
  49. package/typings/createRouter.d.ts +6 -0
  50. package/typings/index.d.cts +7 -0
  51. package/typings/index.d.ts +7 -0
  52. package/typings/plugins/ajv.d.cts +4 -0
  53. package/typings/plugins/ajv.d.ts +4 -0
  54. package/typings/plugins/openapi.d.cts +33 -0
  55. package/typings/plugins/openapi.d.ts +33 -0
  56. package/typings/plugins/utils.d.cts +1 -0
  57. package/typings/plugins/utils.d.ts +1 -0
  58. package/typings/swagger-ui-html.d.cts +2 -0
  59. package/typings/swagger-ui-html.d.ts +2 -0
  60. package/typings/typed-fetch.d.cts +232 -0
  61. package/typings/typed-fetch.d.ts +232 -0
  62. package/typings/types.d.cts +261 -0
  63. package/typings/types.d.ts +261 -0
  64. package/typings/utils.d.cts +31 -0
  65. package/typings/utils.d.ts +31 -0
  66. package/typings/zod/types.d.cts +39 -0
  67. package/typings/zod/types.d.ts +39 -0
  68. package/typings/zod/zod.d.cts +2 -0
  69. package/typings/zod/zod.d.ts +2 -0
@@ -0,0 +1,213 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useAjv = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const ajv_1 = tslib_1.__importDefault(require("ajv"));
6
+ const ajv_formats_1 = tslib_1.__importDefault(require("ajv-formats"));
7
+ const fast_json_stringify_1 = tslib_1.__importDefault(require("@ardatan/fast-json-stringify"));
8
+ const fetch_1 = require("@whatwg-node/fetch");
9
+ const Response_js_1 = require("../Response.js");
10
+ const types_js_1 = require("../zod/types.js");
11
+ const utils_js_1 = require("./utils.js");
12
+ function useAjv({ components = {}, } = {}) {
13
+ const ajv = new ajv_1.default({
14
+ strict: false,
15
+ strictSchema: false,
16
+ validateSchema: false,
17
+ allowUnionTypes: true,
18
+ uriResolver: {
19
+ parse(uri) {
20
+ const url = new fetch_1.URL(uri);
21
+ return {
22
+ scheme: url.protocol,
23
+ userinfo: url.username + (url.password ? ':' + url.password : ''),
24
+ host: url.hostname,
25
+ port: url.port,
26
+ path: url.pathname,
27
+ query: url.search,
28
+ fragment: url.hash,
29
+ };
30
+ },
31
+ resolve(base, ref) {
32
+ return new fetch_1.URL(ref, base).toString();
33
+ },
34
+ serialize(components) {
35
+ return (components.scheme +
36
+ '://' +
37
+ components.userinfo +
38
+ components.host +
39
+ components.port +
40
+ components.path +
41
+ components.query +
42
+ components.fragment);
43
+ },
44
+ },
45
+ });
46
+ (0, ajv_formats_1.default)(ajv);
47
+ // Required for fast-json-stringify
48
+ ajv.addKeyword({
49
+ keyword: 'fjs_type',
50
+ type: 'object',
51
+ errors: false,
52
+ validate: (_type, date) => {
53
+ return date instanceof Date;
54
+ },
55
+ });
56
+ const serializersByPath = new Map();
57
+ return {
58
+ onRoute({ path, schemas, handlers }) {
59
+ const validationMiddlewares = new Map();
60
+ if (schemas?.request?.headers && !(0, types_js_1.isZodSchema)(schemas.request.headers)) {
61
+ const validateFn = ajv.compile({
62
+ ...schemas.request.headers,
63
+ components,
64
+ });
65
+ validationMiddlewares.set('headers', request => {
66
+ const headersObj = (0, utils_js_1.getHeadersObj)(request.headers);
67
+ const isValid = validateFn(headersObj);
68
+ if (!isValid) {
69
+ return validateFn.errors;
70
+ }
71
+ return [];
72
+ });
73
+ }
74
+ if (schemas?.request?.params && !(0, types_js_1.isZodSchema)(schemas.request.params)) {
75
+ const validateFn = ajv.compile({
76
+ ...schemas.request.params,
77
+ components,
78
+ });
79
+ validationMiddlewares.set('params', request => {
80
+ const isValid = validateFn(request.params);
81
+ if (!isValid) {
82
+ return validateFn.errors;
83
+ }
84
+ return [];
85
+ });
86
+ }
87
+ if (schemas?.request?.query && !(0, types_js_1.isZodSchema)(schemas.request.query)) {
88
+ const validateFn = ajv.compile({
89
+ ...schemas.request.query,
90
+ components,
91
+ });
92
+ validationMiddlewares.set('query', request => {
93
+ const isValid = validateFn(request.query);
94
+ if (!isValid) {
95
+ return validateFn.errors;
96
+ }
97
+ return [];
98
+ });
99
+ }
100
+ if (schemas?.request?.json && !(0, types_js_1.isZodSchema)(schemas.request.json)) {
101
+ const validateFn = ajv.compile({
102
+ ...schemas.request.json,
103
+ components,
104
+ });
105
+ validationMiddlewares.set('json', async (request) => {
106
+ const contentType = request.headers.get('content-type');
107
+ if (contentType?.includes('json')) {
108
+ const jsonObj = await request.json();
109
+ Object.defineProperty(request, 'json', {
110
+ value: async () => jsonObj,
111
+ configurable: true,
112
+ });
113
+ const isValid = validateFn(jsonObj);
114
+ if (!isValid) {
115
+ return validateFn.errors;
116
+ }
117
+ }
118
+ return [];
119
+ });
120
+ }
121
+ if (schemas?.request?.formData && !(0, types_js_1.isZodSchema)(schemas.request.formData)) {
122
+ const validateFn = ajv.compile({
123
+ ...schemas.request.formData,
124
+ components,
125
+ });
126
+ validationMiddlewares.set('formData', async (request) => {
127
+ const contentType = request.headers.get('content-type');
128
+ if (contentType?.includes('multipart/form-data') ||
129
+ contentType?.includes('application/x-www-form-urlencoded')) {
130
+ const formData = await request.formData();
131
+ const formDataObj = {};
132
+ const jobs = [];
133
+ formData.forEach((value, key) => {
134
+ if (typeof value === 'string') {
135
+ formDataObj[key] = value;
136
+ }
137
+ else {
138
+ jobs.push(value.arrayBuffer().then(buffer => {
139
+ const typedArray = new Uint8Array(buffer);
140
+ const binaryStrParts = [];
141
+ typedArray.forEach((byte, index) => {
142
+ binaryStrParts[index] = String.fromCharCode(byte);
143
+ });
144
+ formDataObj[key] = binaryStrParts.join('');
145
+ }));
146
+ }
147
+ });
148
+ await Promise.all(jobs);
149
+ Object.defineProperty(request, 'formData', {
150
+ value: async () => formData,
151
+ configurable: true,
152
+ });
153
+ const isValid = validateFn(formDataObj);
154
+ if (!isValid) {
155
+ return validateFn.errors;
156
+ }
157
+ }
158
+ return [];
159
+ });
160
+ }
161
+ if (fast_json_stringify_1.default && schemas?.responses) {
162
+ const serializerByStatusCode = new Map();
163
+ for (const statusCode in schemas.responses) {
164
+ const schema = schemas.responses[statusCode];
165
+ if (!(0, types_js_1.isZodSchema)(schema)) {
166
+ const serializer = (0, fast_json_stringify_1.default)({
167
+ ...schema,
168
+ components,
169
+ }, {
170
+ ajv,
171
+ });
172
+ serializerByStatusCode.set(Number(statusCode), serializer);
173
+ }
174
+ }
175
+ serializersByPath.set(path, serializerByStatusCode);
176
+ }
177
+ if (validationMiddlewares.size > 0) {
178
+ handlers.unshift(async (request) => {
179
+ const validationErrorsNonFlat = await Promise.all([...validationMiddlewares.entries()].map(async ([name, fn]) => {
180
+ const errors = await fn(request);
181
+ if (errors.length > 0) {
182
+ return errors.map(error => ({
183
+ name,
184
+ ...error,
185
+ }));
186
+ }
187
+ }));
188
+ const validationErrors = validationErrorsNonFlat.flat().filter(Boolean);
189
+ if (validationErrors.length > 0) {
190
+ return Response_js_1.Response.json({
191
+ errors: validationErrors,
192
+ }, {
193
+ status: 400,
194
+ headers: {
195
+ 'x-error-type': 'validation',
196
+ },
197
+ });
198
+ }
199
+ });
200
+ }
201
+ },
202
+ onSerializeResponse({ path, lazyResponse }) {
203
+ const serializers = serializersByPath.get(path);
204
+ if (serializers) {
205
+ const serializer = serializers.get(lazyResponse.init?.status || 200);
206
+ if (serializer) {
207
+ lazyResponse.resolveWithSerializer(serializer);
208
+ }
209
+ }
210
+ },
211
+ };
212
+ }
213
+ exports.useAjv = useAjv;
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useOpenAPI = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const zod_to_json_schema_1 = require("zod-to-json-schema");
6
+ const Response_js_1 = require("../Response.js");
7
+ const swagger_ui_html_js_1 = tslib_1.__importDefault(require("../swagger-ui-html.js"));
8
+ const types_js_1 = require("../zod/types.js");
9
+ function useOpenAPI({ oasEndpoint, swaggerUIEndpoint, swaggerUIOpts, }) {
10
+ let paths;
11
+ return {
12
+ onRouterInit(router) {
13
+ paths = router.openAPIDocument.paths = router.openAPIDocument.paths || {};
14
+ if (oasEndpoint) {
15
+ router.route({
16
+ method: 'GET',
17
+ path: oasEndpoint,
18
+ internal: true,
19
+ handler: () => Response_js_1.Response.json(router.openAPIDocument),
20
+ });
21
+ }
22
+ if (swaggerUIEndpoint) {
23
+ router.route({
24
+ method: 'GET',
25
+ path: swaggerUIEndpoint,
26
+ internal: true,
27
+ handler: () => new Response_js_1.Response(swagger_ui_html_js_1.default.replace('__SWAGGER_UI_OPTIONS__', JSON.stringify({
28
+ spec: router.openAPIDocument,
29
+ dom_id: '#swagger-ui',
30
+ displayOperationId: true,
31
+ tryItOutEnabled: true,
32
+ requestSnippetsEnabled: true,
33
+ displayRequestDuration: true,
34
+ defaultModelRendering: 'model',
35
+ defaultModelExpandDepth: 3,
36
+ defaultModelsExpandDepth: 3,
37
+ ...swaggerUIOpts,
38
+ })), {
39
+ headers: {
40
+ 'Content-Type': 'text/html',
41
+ },
42
+ }),
43
+ });
44
+ }
45
+ },
46
+ onRoute({ method, path, operationId, description, tags, schemas }) {
47
+ if (schemas) {
48
+ let pathForOAS = path.replace(/:([^/]+)/g, '{$1}');
49
+ if (!pathForOAS.startsWith('/')) {
50
+ pathForOAS = `/${pathForOAS}`;
51
+ }
52
+ const pathObj = (paths[pathForOAS] = paths[pathForOAS] || {});
53
+ const lowerCasedMethod = method.toLowerCase();
54
+ pathObj[lowerCasedMethod] = pathObj[lowerCasedMethod] || {};
55
+ const operation = pathObj[lowerCasedMethod];
56
+ operation.operationId = operationId;
57
+ operation.description = description;
58
+ operation.tags = tags;
59
+ if (schemas.responses) {
60
+ for (const statusCode in schemas.responses) {
61
+ let responseSchema = schemas.responses[statusCode];
62
+ if ((0, types_js_1.isZodSchema)(responseSchema)) {
63
+ responseSchema = (0, zod_to_json_schema_1.zodToJsonSchema)(responseSchema, {
64
+ target: 'openApi3',
65
+ });
66
+ }
67
+ operation.responses = operation.responses || {};
68
+ operation.responses[statusCode] = {
69
+ description: '',
70
+ content: {
71
+ 'application/json': {
72
+ schema: responseSchema,
73
+ },
74
+ },
75
+ };
76
+ }
77
+ }
78
+ else {
79
+ operation.responses = {
80
+ default: {
81
+ description: '',
82
+ },
83
+ };
84
+ }
85
+ if (schemas.request?.headers) {
86
+ let headersSchema = schemas.request.headers;
87
+ if ((0, types_js_1.isZodSchema)(headersSchema)) {
88
+ headersSchema = (0, zod_to_json_schema_1.zodToJsonSchema)(headersSchema, {
89
+ target: 'openApi3',
90
+ });
91
+ }
92
+ for (const headerName in headersSchema.properties) {
93
+ const headerSchema = headersSchema.properties[headerName];
94
+ operation.parameters = operation.parameters || [];
95
+ operation.parameters.push({
96
+ name: headerName,
97
+ in: 'header',
98
+ required: headersSchema.required?.includes(headerName),
99
+ schema: headerSchema,
100
+ });
101
+ }
102
+ }
103
+ if (schemas.request?.params) {
104
+ let paramsSchema = schemas.request.params;
105
+ if ((0, types_js_1.isZodSchema)(paramsSchema)) {
106
+ paramsSchema = (0, zod_to_json_schema_1.zodToJsonSchema)(paramsSchema, {
107
+ target: 'openApi3',
108
+ });
109
+ }
110
+ for (const paramName in paramsSchema.properties) {
111
+ const paramSchema = paramsSchema.properties[paramName];
112
+ operation.parameters = operation.parameters || [];
113
+ operation.parameters.push({
114
+ name: paramName,
115
+ in: 'path',
116
+ required: paramsSchema.required?.includes(paramName),
117
+ schema: paramSchema,
118
+ });
119
+ }
120
+ }
121
+ if (schemas.request?.query) {
122
+ let queriesSchema = schemas.request.query;
123
+ if ((0, types_js_1.isZodSchema)(queriesSchema)) {
124
+ queriesSchema = (0, zod_to_json_schema_1.zodToJsonSchema)(queriesSchema, {
125
+ target: 'openApi3',
126
+ });
127
+ }
128
+ for (const queryName in queriesSchema.properties) {
129
+ const querySchema = queriesSchema.properties[queryName];
130
+ operation.parameters = operation.parameters || [];
131
+ operation.parameters.push({
132
+ name: queryName,
133
+ in: 'query',
134
+ required: queriesSchema.required?.includes(queryName),
135
+ schema: querySchema,
136
+ });
137
+ }
138
+ }
139
+ if (schemas.request?.json) {
140
+ let requestJsonSchema = schemas.request.json;
141
+ if ((0, types_js_1.isZodSchema)(requestJsonSchema)) {
142
+ requestJsonSchema = (0, zod_to_json_schema_1.zodToJsonSchema)(requestJsonSchema, {
143
+ target: 'openApi3',
144
+ });
145
+ }
146
+ const requestBody = (operation.requestBody = (operation.requestBody || {}));
147
+ requestBody.required = true;
148
+ const requestBodyContent = (requestBody.content = (requestBody.content || {}));
149
+ requestBodyContent['application/json'] = {
150
+ schema: requestJsonSchema,
151
+ };
152
+ }
153
+ if (schemas.request?.formData) {
154
+ const requestBody = (operation.requestBody = (operation.requestBody || {}));
155
+ requestBody.required = true;
156
+ const requestBodyContent = (requestBody.content = (requestBody.content || {}));
157
+ let requestFormDataSchema = schemas.request.formData;
158
+ if ((0, types_js_1.isZodSchema)(requestFormDataSchema)) {
159
+ requestFormDataSchema = (0, zod_to_json_schema_1.zodToJsonSchema)(requestFormDataSchema, {
160
+ target: 'openApi3',
161
+ });
162
+ }
163
+ requestBodyContent['multipart/form-data'] = {
164
+ schema: requestFormDataSchema,
165
+ };
166
+ }
167
+ }
168
+ },
169
+ };
170
+ }
171
+ exports.useOpenAPI = useOpenAPI;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getHeadersObj = void 0;
4
+ function getHeadersObj(headers) {
5
+ return new Proxy({}, {
6
+ get(_target, prop) {
7
+ return headers.get(prop) || undefined;
8
+ },
9
+ set(_target, prop, value) {
10
+ headers.set(prop, value);
11
+ return true;
12
+ },
13
+ has(_target, prop) {
14
+ return headers.has(prop);
15
+ },
16
+ deleteProperty(_target, prop) {
17
+ headers.delete(prop);
18
+ return true;
19
+ },
20
+ ownKeys() {
21
+ return [...headers.keys()];
22
+ },
23
+ getOwnPropertyDescriptor() {
24
+ return {
25
+ enumerable: true,
26
+ configurable: true,
27
+ };
28
+ },
29
+ });
30
+ }
31
+ exports.getHeadersObj = getHeadersObj;
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.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/cjs/types.js ADDED
File without changes
package/cjs/utils.js ADDED
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addHandlersToMethod = void 0;
4
+ // This is used on runtime for optimization
5
+ function preparePatternHandlerObjByMethod({ handlersByPatternByMethod, patternHandlerObjByMethod, }) {
6
+ for (const [method, patternHandlersMap] of handlersByPatternByMethod) {
7
+ const patternHandlerObjList = [];
8
+ for (const [pattern, handlers] of patternHandlersMap) {
9
+ patternHandlerObjList.push({
10
+ pattern,
11
+ handlers,
12
+ });
13
+ }
14
+ patternHandlerObjByMethod.set(method, patternHandlerObjList);
15
+ }
16
+ }
17
+ function addHandlersToMethod({ operationId, description, tags, method, path, schemas, handlers, internal, onRouteHooks, openAPIDocument, basePath, fetchAPI, handlersByPatternByMethod, internalPatternsByMethod, patternHandlerObjByMethod, }) {
18
+ for (const onRouteHook of onRouteHooks) {
19
+ onRouteHook({
20
+ operationId,
21
+ description,
22
+ tags,
23
+ openAPIDocument,
24
+ method,
25
+ path,
26
+ schemas,
27
+ handlers,
28
+ });
29
+ }
30
+ let methodPatternMaps = handlersByPatternByMethod.get(method);
31
+ if (!methodPatternMaps) {
32
+ methodPatternMaps = new Map();
33
+ handlersByPatternByMethod.set(method, methodPatternMaps);
34
+ }
35
+ let fullPath = '';
36
+ if (basePath === '/') {
37
+ fullPath = path;
38
+ }
39
+ else if (path === '/') {
40
+ fullPath = basePath;
41
+ }
42
+ else {
43
+ fullPath = `${basePath}${path}`;
44
+ }
45
+ const pattern = new fetchAPI.URLPattern({ pathname: fullPath });
46
+ pattern.isPattern = fullPath.includes(':') || fullPath.includes('*');
47
+ methodPatternMaps.set(pattern, handlers);
48
+ // TODO: Better logic to make sure internal routes are always last
49
+ let internalPatterns = internalPatternsByMethod.get(method);
50
+ if (internal) {
51
+ if (!internalPatterns) {
52
+ internalPatterns = new Set();
53
+ internalPatternsByMethod.set(method, internalPatterns);
54
+ }
55
+ internalPatterns.add(pattern);
56
+ }
57
+ if (internalPatterns?.size) {
58
+ handlersByPatternByMethod.set(method, new Map([...methodPatternMaps.entries()].sort(([a], [b]) => {
59
+ if (internalPatterns.has(a)) {
60
+ return 1;
61
+ }
62
+ else if (internalPatterns.has(b)) {
63
+ return -1;
64
+ }
65
+ return 0;
66
+ })));
67
+ }
68
+ preparePatternHandlerObjByMethod({
69
+ handlersByPatternByMethod,
70
+ patternHandlerObjByMethod,
71
+ });
72
+ }
73
+ exports.addHandlersToMethod = addHandlersToMethod;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isZodSchema = void 0;
4
+ function isZodSchema(value) {
5
+ return value && typeof value === 'object' && typeof value.safeParse === 'function';
6
+ }
7
+ exports.isZodSchema = isZodSchema;
package/cjs/zod/zod.js ADDED
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useZod = void 0;
4
+ const utils_js_1 = require("../plugins/utils.js");
5
+ const Response_js_1 = require("../Response.js");
6
+ const types_js_1 = require("./types.js");
7
+ function useZod() {
8
+ return {
9
+ onRoute({ schemas, handlers }) {
10
+ if (schemas) {
11
+ const validationMiddlewares = new Map();
12
+ const requestSchemas = schemas.request;
13
+ if (requestSchemas) {
14
+ if ((0, types_js_1.isZodSchema)(requestSchemas.headers)) {
15
+ const headersSchema = requestSchemas.headers;
16
+ validationMiddlewares.set('headers', request => {
17
+ const headersObj = (0, utils_js_1.getHeadersObj)(request.headers);
18
+ const result = headersSchema.safeParse(headersObj);
19
+ if (!result.success) {
20
+ return result.error.issues;
21
+ }
22
+ return [];
23
+ });
24
+ }
25
+ if ((0, types_js_1.isZodSchema)(requestSchemas.params)) {
26
+ const paramsSchema = requestSchemas.params;
27
+ validationMiddlewares.set('params', request => {
28
+ const result = paramsSchema.safeParse(request.params);
29
+ if (!result.success) {
30
+ return result.error.issues;
31
+ }
32
+ return [];
33
+ });
34
+ }
35
+ if ((0, types_js_1.isZodSchema)(requestSchemas.query)) {
36
+ const querySchema = requestSchemas.query;
37
+ validationMiddlewares.set('query', request => {
38
+ const result = querySchema.safeParse(request.query);
39
+ if (!result.success) {
40
+ return result.error.issues;
41
+ }
42
+ return [];
43
+ });
44
+ }
45
+ if ((0, types_js_1.isZodSchema)(requestSchemas.json)) {
46
+ const jsonSchema = requestSchemas.json;
47
+ validationMiddlewares.set('json', async (request) => {
48
+ const contentType = request.headers.get('content-type');
49
+ if (contentType?.includes('json')) {
50
+ const jsonObj = await request.json();
51
+ Object.defineProperty(request, 'json', {
52
+ value: async () => jsonObj,
53
+ configurable: true,
54
+ });
55
+ const result = jsonSchema.safeParse(jsonObj);
56
+ if (!result.success) {
57
+ return result.error.issues;
58
+ }
59
+ }
60
+ return [];
61
+ });
62
+ }
63
+ if (validationMiddlewares.size > 0) {
64
+ handlers.unshift(async (request) => {
65
+ const validationErrorsNonFlat = await Promise.all([...validationMiddlewares.entries()].map(async ([name, fn]) => {
66
+ const errors = await fn(request);
67
+ if (errors.length > 0) {
68
+ return errors.map(error => ({
69
+ name,
70
+ ...error,
71
+ }));
72
+ }
73
+ }));
74
+ const validationErrors = validationErrorsNonFlat.flat().filter(Boolean);
75
+ if (validationErrors.length > 0) {
76
+ return Response_js_1.Response.json({
77
+ errors: validationErrors,
78
+ }, {
79
+ status: 400,
80
+ headers: {
81
+ 'x-error-type': 'validation',
82
+ },
83
+ });
84
+ }
85
+ });
86
+ }
87
+ }
88
+ }
89
+ },
90
+ };
91
+ }
92
+ exports.useZod = useZod;
@@ -0,0 +1,74 @@
1
+ import { Headers, Response as OriginalResponse } from '@whatwg-node/fetch';
2
+ export const LAZY_SERIALIZED_RESPONSE = Symbol('LAZY_SERIALIZED_RESPONSE');
3
+ export const defaultSerializer = obj => JSON.stringify(obj);
4
+ export function isLazySerializedResponse(response) {
5
+ return response[LAZY_SERIALIZED_RESPONSE];
6
+ }
7
+ function isHeadersLike(headers) {
8
+ return headers?.get && headers?.forEach;
9
+ }
10
+ const JSON_CONTENT_TYPE = 'application/json; charset=utf-8';
11
+ function getHeadersFromHeadersInit(init) {
12
+ let headers;
13
+ if (isHeadersLike(init)) {
14
+ headers = init;
15
+ }
16
+ else {
17
+ headers = new Headers(init);
18
+ }
19
+ if (!headers.has('content-type')) {
20
+ headers.set('content-type', JSON_CONTENT_TYPE);
21
+ }
22
+ return headers;
23
+ }
24
+ export function createLazySerializedResponse(jsonObj, init = {}) {
25
+ let actualResponse;
26
+ let headers;
27
+ function getHeaders() {
28
+ if (headers == null) {
29
+ headers = getHeadersFromHeadersInit(init.headers);
30
+ }
31
+ return headers;
32
+ }
33
+ return {
34
+ jsonObj,
35
+ get actualResponse() {
36
+ return actualResponse;
37
+ },
38
+ [LAZY_SERIALIZED_RESPONSE]: true,
39
+ init,
40
+ resolveWithSerializer(serializer) {
41
+ const serialized = serializer(jsonObj);
42
+ init.headers = getHeaders();
43
+ actualResponse = new OriginalResponse(serialized, init);
44
+ },
45
+ json() {
46
+ return Promise.resolve(jsonObj);
47
+ },
48
+ get status() {
49
+ return (init?.status || 200);
50
+ },
51
+ get headers() {
52
+ return getHeaders();
53
+ },
54
+ };
55
+ }
56
+ // This allows us to hook into serialization of the response body
57
+ /**
58
+ * The Response interface of the Fetch API represents the response to a request.
59
+ * It contains the status of the response, as well as the response headers, and
60
+ * an optional response body.
61
+ *
62
+ * @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.
63
+ * @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).
64
+ *
65
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Response
66
+ */
67
+ export const Response = new Proxy(OriginalResponse, {
68
+ get(OriginalResponse, prop, receiver) {
69
+ if (prop === 'json') {
70
+ return createLazySerializedResponse;
71
+ }
72
+ return Reflect.get(OriginalResponse, prop, receiver);
73
+ },
74
+ });