next-rest-framework
Advanced tools
Comparing version
@@ -1,57 +0,23 @@ | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (g && (g = 0, op[0] && (_ = 0)), _) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
import { defineCatchAllHandler } from './define-catch-all-handler'; | ||
import { defineEndpoints } from './define-endpoints'; | ||
import merge from 'lodash.merge'; | ||
import { getDefaultConfig, getOpenApiSpecWithPaths, logInitInfo } from './utils'; | ||
export var NextRestFramework = function (_config) { | ||
var config = merge(getDefaultConfig({ config: _config }), _config); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.NextRestFramework = void 0; | ||
const define_catch_all_handler_1 = require("./define-catch-all-handler"); | ||
const define_endpoints_1 = require("./define-endpoints"); | ||
const lodash_merge_1 = __importDefault(require("lodash.merge")); | ||
const utils_1 = require("./utils"); | ||
const NextRestFramework = (_config) => { | ||
const config = (0, lodash_merge_1.default)((0, utils_1.getDefaultConfig)({ config: _config }), _config); | ||
if (!config.suppressInfo) { | ||
logInitInfo({ config: config }); | ||
(0, utils_1.logInitInfo)({ config }); | ||
} | ||
return { | ||
config: config, | ||
getOpenApiSpec: function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getOpenApiSpecWithPaths({ config: config })]; | ||
case 1: return [2 /*return*/, _a.sent()]; | ||
} | ||
}); }); }, | ||
defineCatchAllHandler: defineCatchAllHandler({ config: config }), | ||
defineEndpoints: defineEndpoints({ config: config }) | ||
config, | ||
getOpenApiSpec: async () => await (0, utils_1.getOpenApiSpecWithPaths)({ config }), | ||
defineCatchAllHandler: (0, define_catch_all_handler_1.defineCatchAllHandler)({ config }), | ||
defineEndpoints: (0, define_endpoints_1.defineEndpoints)({ config }) | ||
}; | ||
}; | ||
exports.NextRestFramework = NextRestFramework; |
@@ -22,1 +22,2 @@ export declare const DEFAULT_MESSAGES: { | ||
} | ||
export declare const NEXT_REST_FRAMEWORK_USER_AGENT = "next-rest-framework"; |
@@ -1,6 +0,9 @@ | ||
export var DEFAULT_MESSAGES = { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.NEXT_REST_FRAMEWORK_USER_AGENT = exports.ValidMethod = exports.OPEN_API_VERSION = exports.DEFAULT_ERRORS = exports.DEFAULT_MESSAGES = void 0; | ||
exports.DEFAULT_MESSAGES = { | ||
created: 'Created', | ||
noContent: 'No content' | ||
}; | ||
export var DEFAULT_ERRORS = { | ||
exports.DEFAULT_ERRORS = { | ||
unexpectedError: 'An unknown error occurred, trying again might help.', | ||
@@ -11,4 +14,4 @@ methodNotAllowed: 'Method not allowed.', | ||
}; | ||
export var OPEN_API_VERSION = '3.0.1'; | ||
export var ValidMethod; | ||
exports.OPEN_API_VERSION = '3.0.1'; | ||
var ValidMethod; | ||
(function (ValidMethod) { | ||
@@ -23,2 +26,3 @@ ValidMethod["GET"] = "GET"; | ||
ValidMethod["TRACE"] = "TRACE"; | ||
})(ValidMethod || (ValidMethod = {})); | ||
})(ValidMethod = exports.ValidMethod || (exports.ValidMethod = {})); | ||
exports.NEXT_REST_FRAMEWORK_USER_AGENT = 'next-rest-framework'; |
@@ -1,1 +0,2 @@ | ||
export {}; | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); |
@@ -1,82 +0,56 @@ | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (g && (g = 0, op[0] && (_ = 0)), _) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
import * as zod from 'zod'; | ||
import { DEFAULT_ERRORS, ValidMethod } from './constants'; | ||
import { defineEndpoints } from './define-endpoints'; | ||
export var defineCatchAllHandler = function (_a) { | ||
var config = _a.config; | ||
return function (methodHandlers) { | ||
return defineEndpoints({ config: config, _warnAboutReservedPaths: false })({ | ||
GET: { | ||
responses: [ | ||
{ | ||
description: 'Response for 404 fallback.', | ||
status: 404, | ||
contentType: 'application/json', | ||
schema: zod.object({ | ||
message: zod.string() | ||
}) | ||
} | ||
], | ||
handler: function (_a) { | ||
var req = _a.req, res = _a.res; | ||
return __awaiter(void 0, void 0, void 0, function () { | ||
var methodHandlerExists; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
methodHandlerExists = methodHandlers && | ||
Object.values(ValidMethod).some(function (method) { | ||
return Object.keys(methodHandlers).includes(method); | ||
}); | ||
if (!methodHandlerExists) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, defineEndpoints({ config: config })(methodHandlers)(req, res)]; | ||
case 1: | ||
_b.sent(); | ||
return [3 /*break*/, 3]; | ||
case 2: | ||
res.status(404).json({ message: DEFAULT_ERRORS.notFound }); | ||
_b.label = 3; | ||
case 3: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.defineCatchAllHandler = void 0; | ||
const zod = __importStar(require("zod")); | ||
const constants_1 = require("./constants"); | ||
const define_endpoints_1 = require("./define-endpoints"); | ||
const defineCatchAllHandler = ({ config }) => (methodHandlers) => { | ||
return (0, define_endpoints_1.defineEndpoints)({ config, _warnAboutReservedPaths: false })({ | ||
GET: { | ||
responses: [ | ||
{ | ||
description: 'Response for 404 fallback.', | ||
status: 404, | ||
contentType: 'application/json', | ||
schema: zod.object({ | ||
message: zod.string() | ||
}) | ||
} | ||
], | ||
handler: async ({ req, res }) => { | ||
const methodHandlerExists = methodHandlers && | ||
Object.values(constants_1.ValidMethod).some((method) => Object.keys(methodHandlers).includes(method)); | ||
if (methodHandlerExists) { | ||
await (0, define_endpoints_1.defineEndpoints)({ config })(methodHandlers)(req, res); | ||
} | ||
else { | ||
res.status(404).json({ message: constants_1.DEFAULT_ERRORS.notFound }); | ||
} | ||
} | ||
}); | ||
}; | ||
} | ||
}); | ||
}; | ||
exports.defineCatchAllHandler = defineCatchAllHandler; |
@@ -1,204 +0,138 @@ | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (g && (g = 0, op[0] && (_ = 0)), _) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.defineEndpoints = void 0; | ||
const constants_1 = require("./constants"); | ||
const utils_1 = require("./utils"); | ||
const js_yaml_1 = __importDefault(require("js-yaml")); | ||
const defineEndpoints = ({ config, _warnAboutReservedPaths = true }) => { | ||
return (methodHandlers) => { | ||
return async (req, res) => { | ||
const { method, body, headers, url } = req; | ||
const { openApiJsonPath, openApiYamlPath, swaggerUiPath, exposeOpenApiSpec, suppressInfo } = config; | ||
if (!suppressInfo && !global.reservedPathsLogged) { | ||
(0, utils_1.logReservedPaths)({ config, headers }); | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
import { DEFAULT_ERRORS } from './constants'; | ||
import { handleReservedPaths, isValidMethod, isZodSchema, isYupSchema, isYupValidationError, logReservedPaths, warnAboutReservedPath } from './utils'; | ||
export var defineEndpoints = function (_a) { | ||
var config = _a.config, _b = _a._warnAboutReservedPaths, _warnAboutReservedPaths = _b === void 0 ? true : _b; | ||
return function (methodHandlers) { | ||
return function (req, res) { return __awaiter(void 0, void 0, void 0, function () { | ||
var method, body, headers, url, openApiJsonPath, openApiYamlPath, swaggerUiPath, exposeOpenApiSpec, suppressInfo, reservedPathFound, allowedMethods, returnMethodNotAllowed, returnUnexpectedError, methodHandler, requestBody, handler, _a, errorHandler, schema, contentType, data, e_1, globalMiddlewareParams, routeMiddlewareParams, methodMiddlewareParams, params, error_1; | ||
var _b, _c, _d, _e; | ||
return __generator(this, function (_f) { | ||
switch (_f.label) { | ||
case 0: | ||
// The handler is called without params when generating OpenAPI spec. | ||
if (!req || !res) { | ||
return [2 /*return*/, methodHandlers]; | ||
if ([openApiJsonPath, openApiYamlPath, swaggerUiPath].includes(url) && | ||
exposeOpenApiSpec) { | ||
const spec = await (0, utils_1.getOpenApiSpecWithPaths)({ config }); | ||
if (_warnAboutReservedPaths) { | ||
(0, utils_1.handleReservedPathWarnings)({ url, config }); | ||
} | ||
if (url === openApiJsonPath) { | ||
res.status(200).json(spec); | ||
return; | ||
} | ||
if (url === openApiYamlPath) { | ||
res.setHeader('Content-Type', 'text/plain'); | ||
res.status(200).send(js_yaml_1.default.dump(spec)); | ||
return; | ||
} | ||
if (url === swaggerUiPath) { | ||
const html = (0, utils_1.getHTMLForSwaggerUI)({ headers }); | ||
res.setHeader('Content-Type', 'text/html'); | ||
res.status(200).send(html); | ||
return; | ||
} | ||
} | ||
const allowedMethods = Object.keys(methodHandlers); | ||
const returnMethodNotAllowed = () => { | ||
res.setHeader('Allow', allowedMethods.join(', ')); | ||
res.status(405).json({ message: constants_1.DEFAULT_ERRORS.methodNotAllowed }); | ||
}; | ||
const returnUnexpectedError = () => { | ||
res.status(500).json({ message: constants_1.DEFAULT_ERRORS.unexpectedError }); | ||
}; | ||
if (headers['user-agent'] === constants_1.NEXT_REST_FRAMEWORK_USER_AGENT) { | ||
try { | ||
const paths = (0, utils_1.getPathsFromMethodHandlers)({ | ||
methodHandlers: methodHandlers, | ||
route: url ?? '' | ||
}); | ||
res.status(200).json(paths); | ||
} | ||
catch (error) { | ||
await config.errorHandler?.({ req, res, error }); | ||
returnUnexpectedError(); | ||
} | ||
return; | ||
} | ||
if (!(0, utils_1.isValidMethod)(method)) { | ||
returnMethodNotAllowed(); | ||
return; | ||
} | ||
const methodHandler = methodHandlers[method]; | ||
if (methodHandler == null) { | ||
returnMethodNotAllowed(); | ||
return; | ||
} | ||
const { requestBody, handler, errorHandler = methodHandlers.errorHandler ?? config.errorHandler } = methodHandler; | ||
if (requestBody) { | ||
const { schema, contentType } = requestBody; | ||
if (headers['content-type'] !== contentType) { | ||
res | ||
.status(415) | ||
.json({ message: constants_1.DEFAULT_ERRORS.unsupportedMediaType }); | ||
return; | ||
} | ||
if ((0, utils_1.isZodSchema)(schema)) { | ||
const data = schema.safeParse(body); | ||
if (!data.success) { | ||
res.status(400).json({ message: data.error.issues }); | ||
return; | ||
} | ||
} | ||
else if ((0, utils_1.isYupSchema)(schema)) { | ||
try { | ||
await schema.validate(body); | ||
} | ||
catch (e) { | ||
if ((0, utils_1.isYupValidationError)(e)) { | ||
res.status(400).json({ message: e.errors }); | ||
return; | ||
} | ||
method = req.method, body = req.body, headers = req.headers, url = req.url; | ||
openApiJsonPath = config.openApiJsonPath, openApiYamlPath = config.openApiYamlPath, swaggerUiPath = config.swaggerUiPath, exposeOpenApiSpec = config.exposeOpenApiSpec, suppressInfo = config.suppressInfo; | ||
if (!suppressInfo && !global.reservedPathsLogged) { | ||
logReservedPaths({ config: config, headers: headers }); | ||
} | ||
if (_warnAboutReservedPaths) { | ||
if (url === openApiJsonPath && | ||
!global.reservedOpenApiJsonPathWarningLogged) { | ||
warnAboutReservedPath({ | ||
path: openApiJsonPath, | ||
name: 'OpenAPI JSON spec', | ||
configName: 'openApiJsonPath' | ||
}); | ||
} | ||
if (url === openApiYamlPath && | ||
!global.reservedOpenApiYamlPathWarningLogged) { | ||
warnAboutReservedPath({ | ||
path: openApiYamlPath, | ||
name: 'OpenAPI YAML spec', | ||
configName: 'openApiYamlPath' | ||
}); | ||
} | ||
if (url === swaggerUiPath && | ||
!global.reservedSwaggerUiPathWarningLogged) { | ||
warnAboutReservedPath({ | ||
path: swaggerUiPath, | ||
name: 'Swagger UI', | ||
configName: 'swaggerUiPath' | ||
}); | ||
} | ||
} | ||
if (!([openApiJsonPath, openApiYamlPath, swaggerUiPath].includes(url) && | ||
exposeOpenApiSpec)) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, handleReservedPaths({ | ||
req: req, | ||
res: res, | ||
config: config | ||
})]; | ||
case 1: | ||
reservedPathFound = _f.sent(); | ||
if (reservedPathFound) { | ||
return [2 /*return*/]; | ||
} | ||
_f.label = 2; | ||
case 2: | ||
allowedMethods = Object.keys(methodHandlers); | ||
returnMethodNotAllowed = function () { | ||
res.setHeader('Allow', allowedMethods.join(', ')); | ||
res.status(405).json({ message: DEFAULT_ERRORS.methodNotAllowed }); | ||
}; | ||
returnUnexpectedError = function () { | ||
res.status(500).json({ message: DEFAULT_ERRORS.unexpectedError }); | ||
}; | ||
if (!isValidMethod(method)) { | ||
returnMethodNotAllowed(); | ||
return [2 /*return*/]; | ||
} | ||
methodHandler = methodHandlers[method]; | ||
if (methodHandler == null) { | ||
returnMethodNotAllowed(); | ||
return [2 /*return*/]; | ||
} | ||
requestBody = methodHandler.requestBody, handler = methodHandler.handler, _a = methodHandler.errorHandler, errorHandler = _a === void 0 ? (_b = methodHandlers.errorHandler) !== null && _b !== void 0 ? _b : config.errorHandler : _a; | ||
if (!requestBody) return [3 /*break*/, 7]; | ||
schema = requestBody.schema, contentType = requestBody.contentType; | ||
if (headers['content-type'] !== contentType) { | ||
res | ||
.status(415) | ||
.json({ message: DEFAULT_ERRORS.unsupportedMediaType }); | ||
return [2 /*return*/]; | ||
} | ||
if (!isZodSchema(schema)) return [3 /*break*/, 3]; | ||
data = schema.safeParse(body); | ||
if (!data.success) { | ||
res.status(400).json({ message: data.error.issues }); | ||
return [2 /*return*/]; | ||
} | ||
return [3 /*break*/, 7]; | ||
case 3: | ||
if (!isYupSchema(schema)) return [3 /*break*/, 7]; | ||
_f.label = 4; | ||
case 4: | ||
_f.trys.push([4, 6, , 7]); | ||
return [4 /*yield*/, schema.validate(body)]; | ||
case 5: | ||
_f.sent(); | ||
return [3 /*break*/, 7]; | ||
case 6: | ||
e_1 = _f.sent(); | ||
if (isYupValidationError(e_1)) { | ||
res.status(400).json({ message: e_1.errors }); | ||
return [2 /*return*/]; | ||
} | ||
else { | ||
returnUnexpectedError(); | ||
} | ||
return [3 /*break*/, 7]; | ||
case 7: return [4 /*yield*/, ((_c = config.middleware) === null || _c === void 0 ? void 0 : _c.call(config, { | ||
req: req, | ||
res: res | ||
}))]; | ||
case 8: | ||
globalMiddlewareParams = (_f.sent()); | ||
return [4 /*yield*/, ((_d = methodHandlers.middleware) === null || _d === void 0 ? void 0 : _d.call(methodHandlers, { | ||
req: req, | ||
res: res, | ||
params: globalMiddlewareParams | ||
}))]; | ||
case 9: | ||
routeMiddlewareParams = (_f.sent()); | ||
return [4 /*yield*/, ((_e = methodHandler.middleware) === null || _e === void 0 ? void 0 : _e.call(methodHandler, { | ||
req: req, | ||
res: res, | ||
params: __assign(__assign({}, globalMiddlewareParams), routeMiddlewareParams) | ||
}))]; | ||
case 10: | ||
methodMiddlewareParams = (_f.sent()); | ||
params = __assign(__assign(__assign({}, globalMiddlewareParams), routeMiddlewareParams), methodMiddlewareParams); | ||
_f.label = 11; | ||
case 11: | ||
_f.trys.push([11, 13, , 15]); | ||
return [4 /*yield*/, handler({ | ||
req: req, | ||
res: res, | ||
params: params | ||
})]; | ||
case 12: | ||
_f.sent(); | ||
return [3 /*break*/, 15]; | ||
case 13: | ||
error_1 = _f.sent(); | ||
return [4 /*yield*/, (errorHandler === null || errorHandler === void 0 ? void 0 : errorHandler({ req: req, res: res, error: error_1, params: params }))]; | ||
case 14: | ||
_f.sent(); | ||
returnUnexpectedError(); | ||
return [3 /*break*/, 15]; | ||
case 15: return [2 /*return*/]; | ||
} | ||
} | ||
}); | ||
}); }; | ||
} | ||
const globalMiddlewareParams = (await config.middleware?.({ | ||
req, | ||
res | ||
})); | ||
const routeMiddlewareParams = (await methodHandlers.middleware?.({ | ||
req, | ||
res, | ||
params: globalMiddlewareParams | ||
})); | ||
const methodMiddlewareParams = (await methodHandler.middleware?.({ | ||
req, | ||
res, | ||
params: { | ||
...globalMiddlewareParams, | ||
...routeMiddlewareParams | ||
} | ||
})); | ||
const params = { | ||
...globalMiddlewareParams, | ||
...routeMiddlewareParams, | ||
...methodMiddlewareParams | ||
}; | ||
try { | ||
await handler({ | ||
req, | ||
res, | ||
params | ||
}); | ||
} | ||
catch (error) { | ||
await errorHandler?.({ req, res, error, params }); | ||
returnUnexpectedError(); | ||
} | ||
}; | ||
}; | ||
}; | ||
exports.defineEndpoints = defineEndpoints; |
@@ -1,2 +0,6 @@ | ||
import './polyfills'; | ||
export { NextRestFramework } from './client'; | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.NextRestFramework = void 0; | ||
require("./polyfills"); | ||
var client_1 = require("./client"); | ||
Object.defineProperty(exports, "NextRestFramework", { enumerable: true, get: function () { return client_1.NextRestFramework; } }); |
@@ -12,5 +12,4 @@ "use strict"; | ||
} | ||
var TypedArray = Reflect.getPrototypeOf(Int8Array); | ||
for (var _i = 0, _a = [Array, String, TypedArray]; _i < _a.length; _i++) { | ||
var C = _a[_i]; | ||
const TypedArray = Reflect.getPrototypeOf(Int8Array); | ||
for (const C of [Array, String, TypedArray]) { | ||
Object.defineProperty(C.prototype, 'at', { | ||
@@ -17,0 +16,0 @@ value: at, |
@@ -30,3 +30,3 @@ import { z } from 'zod'; | ||
}>; | ||
export type RequestBodyObject<Body> = Omit<OpenAPIV3_1.RequestBodyObject, 'content'> & Modify<OpenAPIV3_1.MediaTypeObject, { | ||
export type RequestBodyObject<Body = unknown> = Omit<OpenAPIV3_1.RequestBodyObject, 'content'> & Modify<OpenAPIV3_1.MediaTypeObject, { | ||
description?: string; | ||
@@ -84,2 +84,3 @@ required?: boolean; | ||
$ref?: OpenAPIV3_1.ReferenceObject; | ||
summary?: OpenAPIV3_1.PathItemObject['summary']; | ||
description?: OpenAPIV3_1.PathItemObject['description']; | ||
@@ -86,0 +87,0 @@ servers?: OpenAPIV3_1.ServerObject[]; |
@@ -1,1 +0,3 @@ | ||
import { ValidMethod } from './constants'; | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const constants_1 = require("./constants"); |
@@ -1,1 +0,2 @@ | ||
export {}; | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); |
import http from 'http'; | ||
import { ValidMethod } from './constants'; | ||
import { NextRestFrameworkConfig } from './types'; | ||
import { DefineEndpointsParams, NextRestFrameworkConfig } from './types'; | ||
import { z } from 'zod'; | ||
@@ -8,3 +8,2 @@ import * as yup from 'yup'; | ||
import { Modify } from './utility-types'; | ||
import { NextApiRequest, NextApiResponse } from 'next'; | ||
export declare const getDefaultConfig: ({ config }?: { | ||
@@ -27,7 +26,6 @@ config?: NextRestFrameworkConfig<unknown> | undefined; | ||
}) => void; | ||
export declare const handleReservedPaths: ({ req: { url, headers }, res, config }: { | ||
req: NextApiRequest; | ||
res: NextApiResponse; | ||
export declare const handleReservedPathWarnings: ({ url, config: { openApiJsonPath, openApiYamlPath, swaggerUiPath } }: { | ||
url?: string | undefined; | ||
config: NextRestFrameworkConfig; | ||
}) => Promise<boolean>; | ||
}) => void; | ||
export declare const getHTMLForSwaggerUI: ({ headers }: { | ||
@@ -37,4 +35,4 @@ headers: http.IncomingHttpHeaders; | ||
export declare const isValidMethod: (x: unknown) => x is ValidMethod; | ||
export declare const getOpenApiSpecWithPaths: <GlobalMiddlewareResponse>({ config }: { | ||
config: NextRestFrameworkConfig<GlobalMiddlewareResponse>; | ||
export declare const getOpenApiSpecWithPaths: ({ config }: { | ||
config: NextRestFrameworkConfig; | ||
}) => Promise<{ | ||
@@ -57,1 +55,10 @@ openapi: string; | ||
export declare const isYupValidationError: (e: unknown) => e is yup.ValidationError; | ||
export declare const convertSchemaToJsonSchema: (_schema: unknown) => OpenAPIV3_1.SchemaObject; | ||
export declare const defaultResponses: OpenAPIV3_1.ResponsesObject; | ||
export declare const getPathsFromMethodHandlers: ({ methodHandlers, route }: { | ||
methodHandlers: DefineEndpointsParams; | ||
route: string; | ||
}) => OpenAPIV3_1.PathsObject<{}, {}>; | ||
export declare const generatePaths: <GlobalMiddlewareResponse>({ config: { openApiJsonPath, openApiYamlPath, swaggerUiPath, errorHandler } }: { | ||
config: NextRestFrameworkConfig<GlobalMiddlewareResponse>; | ||
}) => Promise<OpenAPIV3_1.PathsObject>; |
@@ -1,89 +0,47 @@ | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (g && (g = 0, op[0] && (_ = 0)), _) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.generatePaths = exports.getPathsFromMethodHandlers = exports.defaultResponses = exports.convertSchemaToJsonSchema = exports.isYupValidationError = exports.isYupSchema = exports.isZodSchema = exports.getOpenApiSpecWithPaths = exports.isValidMethod = exports.getHTMLForSwaggerUI = exports.handleReservedPathWarnings = exports.warnAboutReservedPath = exports.logReservedPaths = exports.logInitInfo = exports.getDefaultConfig = void 0; | ||
const constants_1 = require("./constants"); | ||
const fs_1 = require("fs"); | ||
const path_1 = require("path"); | ||
const zod_1 = require("zod"); | ||
const chalk_1 = __importDefault(require("chalk")); | ||
const lodash_merge_1 = __importDefault(require("lodash.merge")); | ||
const lodash_isequalwith_1 = __importDefault(require("lodash.isequalwith")); | ||
const zod_to_json_schema_1 = __importDefault(require("zod-to-json-schema")); | ||
const yup_to_json_schema_1 = __importDefault(require("@sodaru/yup-to-json-schema")); | ||
const logNextRestFrameworkError = ({ error }) => { | ||
if (process.env.NODE_ENV !== 'production') { | ||
console.error(chalk_1.default.red(`Next REST Framework encountered an error: | ||
${error}`)); | ||
} | ||
else { | ||
console.error(chalk_1.default.red('Next REST Framework encountered an error - suppressed in production mode.')); | ||
} | ||
}; | ||
import { OPEN_API_VERSION, ValidMethod } from './constants'; | ||
import { readFileSync } from 'fs'; | ||
import { join } from 'path'; | ||
import { generatePaths } from './generate-paths'; | ||
import chalk from 'chalk'; | ||
import merge from 'lodash.merge'; | ||
import yaml from 'js-yaml'; | ||
import isEqualWith from 'lodash.isequalwith'; | ||
export var getDefaultConfig = function (_a) { | ||
var _b = _a === void 0 ? {} : _a, config = _b.config; | ||
return ({ | ||
openApiSpec: { | ||
openapi: OPEN_API_VERSION, | ||
info: { | ||
title: 'Next REST Framework', | ||
description: 'This is an autogenerated OpenAPI spec by Next REST Framework.', | ||
// Ignore: We don't want to use promises here to avoid making this an async function. | ||
// eslint-disable-next-line @typescript-eslint/no-var-requires | ||
version: require('../package.json').version | ||
}, | ||
components: {} | ||
const getDefaultConfig = ({ config } = {}) => ({ | ||
openApiSpec: { | ||
openapi: constants_1.OPEN_API_VERSION, | ||
info: { | ||
title: 'Next REST Framework', | ||
description: 'This is an autogenerated OpenAPI spec by Next REST Framework.', | ||
// Ignore: We don't want to use promises here to avoid making this an async function. | ||
// eslint-disable-next-line @typescript-eslint/no-var-requires | ||
version: require('../package.json').version | ||
}, | ||
openApiJsonPath: '/api/openapi.json', | ||
openApiYamlPath: '/api/openapi.yaml', | ||
swaggerUiPath: '/api', | ||
exposeOpenApiSpec: true, | ||
errorHandler: function (_a) { | ||
var error = _a.error; | ||
if (process.env.NODE_ENV !== 'production') { | ||
console.error(chalk.red("Next REST Framework encountered an error:\n".concat(error))); | ||
} | ||
else { | ||
console.error(chalk.red('Next REST Framework encountered an error - suppressed in production mode.')); | ||
} | ||
}, | ||
suppressInfo: false | ||
}); | ||
}; | ||
export var logInitInfo = function (_a) { | ||
var config = _a.config; | ||
var configsEqual = isEqualWith(global.nextRestFrameworkConfig, config, function (val1, val2) { | ||
components: {} | ||
}, | ||
openApiJsonPath: '/api/openapi.json', | ||
openApiYamlPath: '/api/openapi.yaml', | ||
swaggerUiPath: '/api', | ||
exposeOpenApiSpec: true, | ||
errorHandler: logNextRestFrameworkError, | ||
suppressInfo: false | ||
}); | ||
exports.getDefaultConfig = getDefaultConfig; | ||
const logInitInfo = ({ config }) => { | ||
const configsEqual = (0, lodash_isequalwith_1.default)(global.nextRestFrameworkConfig, config, (val1, val2) => { | ||
if (typeof val1 === 'function' && typeof val2 === 'function') { | ||
@@ -95,6 +53,6 @@ return val1.toString() === val2.toString(); | ||
global.nextRestFrameworkConfig = config; | ||
console.info(chalk.green('Next REST Framework initialized! 🚀')); | ||
console.info(chalk_1.default.green('Next REST Framework initialized! 🚀')); | ||
} | ||
else if (!configsEqual) { | ||
console.info(chalk.green('Next REST Framework config changed, re-initializing!')); | ||
console.info(chalk_1.default.green('Next REST Framework config changed, re-initializing!')); | ||
global.nextRestFrameworkConfig = config; | ||
@@ -104,19 +62,20 @@ global.reservedPathsLogged = false; | ||
}; | ||
export var logReservedPaths = function (_a) { | ||
var _b; | ||
var config = _a.config, headers = _a.headers; | ||
var proto = (_b = headers['x-forwarded-proto']) !== null && _b !== void 0 ? _b : 'http'; | ||
var host = headers.host; | ||
var baseUrl = "".concat(proto, "://").concat(host); | ||
exports.logInitInfo = logInitInfo; | ||
const logReservedPaths = ({ config, headers }) => { | ||
const proto = headers['x-forwarded-proto'] ?? 'http'; | ||
const host = headers.host; | ||
const baseUrl = `${proto}://${host}`; | ||
if (config.exposeOpenApiSpec) { | ||
console.info(chalk.yellowBright("Swagger UI: ".concat(baseUrl).concat(config.swaggerUiPath, "\nOpenAPI JSON: ").concat(baseUrl).concat(config.openApiJsonPath, "\nOpenAPI YAML: ").concat(baseUrl).concat(config.openApiYamlPath))); | ||
console.info(chalk_1.default.yellowBright(`Swagger UI: ${baseUrl}${config.swaggerUiPath} | ||
OpenAPI JSON: ${baseUrl}${config.openApiJsonPath} | ||
OpenAPI YAML: ${baseUrl}${config.openApiYamlPath}`)); | ||
} | ||
else { | ||
console.info(chalk.yellowBright("OpenAPI spec is not exposed. To expose it, set ".concat(chalk.bold('exposeOpenApiSpec'), " to ").concat(chalk.bold('true'), " in the Next REST Framework config."))); | ||
console.info(chalk_1.default.yellowBright(`OpenAPI spec is not exposed. To expose it, set ${chalk_1.default.bold('exposeOpenApiSpec')} to ${chalk_1.default.bold('true')} in the Next REST Framework config.`)); | ||
} | ||
global.reservedPathsLogged = true; | ||
}; | ||
export var warnAboutReservedPath = function (_a) { | ||
var path = _a.path, name = _a.name, configName = _a.configName; | ||
console.warn(chalk.yellowBright("Warning: ".concat(chalk.bold(path), " is reserved for ").concat(name, ". Update ").concat(chalk.bold(configName), " in your Next REST Framework config to use this path for other purposes."))); | ||
exports.logReservedPaths = logReservedPaths; | ||
const warnAboutReservedPath = ({ path, name, configName }) => { | ||
console.warn(chalk_1.default.yellowBright(`Warning: ${chalk_1.default.bold(path)} is reserved for ${name}. Update ${chalk_1.default.bold(configName)} in your Next REST Framework config to use this path for other purposes.`)); | ||
switch (configName) { | ||
@@ -137,70 +96,231 @@ case 'openApiJsonPath': { | ||
}; | ||
export var handleReservedPaths = function (_a) { | ||
var _b = _a.req, url = _b.url, headers = _b.headers, res = _a.res, config = _a.config; | ||
return __awaiter(void 0, void 0, void 0, function () { | ||
var openApiJsonPath, openApiYamlPath, swaggerUiPath, spec, html; | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: | ||
openApiJsonPath = config.openApiJsonPath, openApiYamlPath = config.openApiYamlPath, swaggerUiPath = config.swaggerUiPath; | ||
return [4 /*yield*/, getOpenApiSpecWithPaths({ config: config })]; | ||
case 1: | ||
spec = _c.sent(); | ||
if (url === openApiJsonPath) { | ||
res.status(200).json(spec); | ||
return [2 /*return*/, true]; | ||
} | ||
if (url === openApiYamlPath) { | ||
res.setHeader('Content-Type', 'text/plain'); | ||
res.status(200).send(yaml.dump(spec)); | ||
return [2 /*return*/, true]; | ||
} | ||
if (url === swaggerUiPath) { | ||
html = getHTMLForSwaggerUI({ headers: headers }); | ||
res.setHeader('Content-Type', 'text/html'); | ||
res.status(200).send(html); | ||
return [2 /*return*/, true]; | ||
} | ||
return [2 /*return*/, false]; | ||
} | ||
exports.warnAboutReservedPath = warnAboutReservedPath; | ||
const handleReservedPathWarnings = ({ url, config: { openApiJsonPath, openApiYamlPath, swaggerUiPath } }) => { | ||
if (url === openApiJsonPath && !global.reservedOpenApiJsonPathWarningLogged) { | ||
(0, exports.warnAboutReservedPath)({ | ||
path: openApiJsonPath, | ||
name: 'OpenAPI JSON spec', | ||
configName: 'openApiJsonPath' | ||
}); | ||
} | ||
if (url === openApiYamlPath && !global.reservedOpenApiYamlPathWarningLogged) { | ||
(0, exports.warnAboutReservedPath)({ | ||
path: openApiYamlPath, | ||
name: 'OpenAPI YAML spec', | ||
configName: 'openApiYamlPath' | ||
}); | ||
} | ||
if (url === swaggerUiPath && !global.reservedSwaggerUiPathWarningLogged) { | ||
(0, exports.warnAboutReservedPath)({ | ||
path: swaggerUiPath, | ||
name: 'Swagger UI', | ||
configName: 'swaggerUiPath' | ||
}); | ||
} | ||
}; | ||
exports.handleReservedPathWarnings = handleReservedPathWarnings; | ||
const getHTMLForSwaggerUI = ({ headers }) => { | ||
const proto = headers['x-forwarded-proto'] ?? 'http'; | ||
const host = headers.host; | ||
const url = `${proto}://${host}/api/openapi.yaml`; | ||
const css = (0, fs_1.readFileSync)((0, path_1.join)(process.cwd(), 'node_modules/next-rest-framework/dist/swagger-ui/swagger-ui.css')); | ||
const swaggerUiBundle = (0, fs_1.readFileSync)((0, path_1.join)(process.cwd(), 'node_modules/next-rest-framework/dist/swagger-ui/swagger-ui-bundle.js')); | ||
const swaggerUiStandalonePreset = (0, fs_1.readFileSync)((0, path_1.join)(process.cwd(), 'node_modules/next-rest-framework/dist/swagger-ui/swagger-ui-standalone-preset.js')); | ||
return `<!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>Next REST Framework | SwaggerUI</title> | ||
<style>${css}</style> | ||
</head> | ||
<body> | ||
<div id="swagger-ui"></div> | ||
<script>${swaggerUiBundle}</script> | ||
<script>${swaggerUiStandalonePreset}</script> | ||
<script> | ||
window.onload = () => { | ||
window.ui = SwaggerUIBundle({ | ||
url: '${url}', | ||
dom_id: '#swagger-ui', | ||
presets: [ | ||
SwaggerUIBundle.presets.apis, | ||
SwaggerUIStandalonePreset | ||
], | ||
layout: "StandaloneLayout", | ||
}); | ||
}; | ||
</script> | ||
</body> | ||
</html>`; | ||
}; | ||
export var getHTMLForSwaggerUI = function (_a) { | ||
var _b; | ||
var headers = _a.headers; | ||
var proto = (_b = headers['x-forwarded-proto']) !== null && _b !== void 0 ? _b : 'http'; | ||
var host = headers.host; | ||
var url = "".concat(proto, "://").concat(host, "/api/openapi.yaml"); | ||
var css = readFileSync(join(process.cwd(), 'node_modules/next-rest-framework/dist/swagger-ui/swagger-ui.css')); | ||
var swaggerUiBundle = readFileSync(join(process.cwd(), 'node_modules/next-rest-framework/dist/swagger-ui/swagger-ui-bundle.js')); | ||
var swaggerUiStandalonePreset = readFileSync(join(process.cwd(), 'node_modules/next-rest-framework/dist/swagger-ui/swagger-ui-standalone-preset.js')); | ||
return "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <meta\n name=\"description\"\n content=\"SwaggerUI\"\n />\n <title>Next REST Framework | SwaggerUI</title>\n <style>".concat(css, "</style>\n</head>\n<body>\n<div id=\"swagger-ui\"></div>\n<script>").concat(swaggerUiBundle, "</script>\n<script>").concat(swaggerUiStandalonePreset, "</script>\n<script>\n window.onload = () => {\n window.ui = SwaggerUIBundle({\n url: '").concat(url, "',\n dom_id: '#swagger-ui',\n presets: [\n SwaggerUIBundle.presets.apis,\n SwaggerUIStandalonePreset\n ],\n layout: \"StandaloneLayout\",\n });\n };\n</script>\n</body>\n</html>"); | ||
exports.getHTMLForSwaggerUI = getHTMLForSwaggerUI; | ||
const isValidMethod = (x) => Object.values(constants_1.ValidMethod).includes(x); | ||
exports.isValidMethod = isValidMethod; | ||
const getOpenApiSpecWithPaths = async ({ config }) => { | ||
const paths = await (0, exports.generatePaths)({ config }); | ||
const spec = { | ||
...config.openApiSpec, | ||
openapi: constants_1.OPEN_API_VERSION, | ||
paths: (0, lodash_merge_1.default)(config.openApiSpec?.paths, paths) | ||
}; | ||
return spec; | ||
}; | ||
export var isValidMethod = function (x) { | ||
return Object.values(ValidMethod).includes(x); | ||
exports.getOpenApiSpecWithPaths = getOpenApiSpecWithPaths; | ||
const isZodSchema = (obj) => { | ||
return !!obj && typeof obj === 'object' && '_def' in obj; | ||
}; | ||
export var getOpenApiSpecWithPaths = function (_a) { | ||
var config = _a.config; | ||
return __awaiter(void 0, void 0, void 0, function () { | ||
var paths; | ||
var _b; | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: return [4 /*yield*/, generatePaths({ config: config })]; | ||
case 1: | ||
paths = _c.sent(); | ||
return [2 /*return*/, __assign(__assign({}, config.openApiSpec), { openapi: OPEN_API_VERSION, paths: merge((_b = config.openApiSpec) === null || _b === void 0 ? void 0 : _b.paths, paths) })]; | ||
exports.isZodSchema = isZodSchema; | ||
const isYupSchema = (obj) => { | ||
return !!obj && obj.constructor.name === 'ObjectSchema'; | ||
}; | ||
exports.isYupSchema = isYupSchema; | ||
const isYupValidationError = (e) => { | ||
return e instanceof Error && e.name === 'ValidationError'; | ||
}; | ||
exports.isYupValidationError = isYupValidationError; | ||
const convertSchemaToJsonSchema = (_schema) => { | ||
let schema = {}; | ||
if ((0, exports.isZodSchema)(_schema)) { | ||
schema = (0, zod_to_json_schema_1.default)(_schema); | ||
} | ||
else if ((0, exports.isYupSchema)(_schema)) { | ||
schema = (0, yup_to_json_schema_1.default)(_schema); | ||
} | ||
else { | ||
console.warn(chalk_1.default.yellowBright("Warning: Unsupported schema type. Can't convert to JSON Schema.")); | ||
} | ||
return schema; | ||
}; | ||
exports.convertSchemaToJsonSchema = convertSchemaToJsonSchema; | ||
exports.defaultResponses = { | ||
500: { | ||
description: constants_1.DEFAULT_ERRORS.unexpectedError, | ||
content: { | ||
'application/json': { | ||
schema: (0, exports.convertSchemaToJsonSchema)(zod_1.z.object({ message: zod_1.z.string() })) | ||
} | ||
} | ||
} | ||
}; | ||
const getPathsFromMethodHandlers = ({ methodHandlers, route }) => { | ||
const { $ref, summary, description, servers, parameters } = methodHandlers; | ||
const paths = {}; | ||
paths[route] = { | ||
$ref: $ref, | ||
summary, | ||
description, | ||
servers, | ||
parameters | ||
}; | ||
Object.keys(methodHandlers) | ||
.filter(exports.isValidMethod) | ||
.forEach((method) => { | ||
const { tags, summary, description, externalDocs, operationId, parameters, requestBody: _requestBody, responses: _responses, callbacks, deprecated, security, servers } = methodHandlers[method]; | ||
let requestBody; | ||
if (_requestBody) { | ||
const { description, required, contentType, schema: _schema, examples, example, encoding } = _requestBody; | ||
const schema = (0, exports.convertSchemaToJsonSchema)(_schema); | ||
requestBody = { | ||
description, | ||
required, | ||
content: { | ||
[contentType]: { | ||
schema, | ||
examples, | ||
example, | ||
encoding | ||
} | ||
} | ||
}; | ||
} | ||
else { | ||
requestBody = _requestBody; | ||
} | ||
const responses = { | ||
...exports.defaultResponses | ||
}; | ||
_responses.forEach(({ status, contentType, description = 'Auto-generated description by Next REST Framework.', headers, links, schema: _schema, example, examples, encoding }) => { | ||
if (status) { | ||
const schema = (0, exports.convertSchemaToJsonSchema)(_schema); | ||
responses[status.toString()] = { | ||
description, | ||
headers, | ||
links, | ||
content: { | ||
[contentType]: { | ||
schema, | ||
example, | ||
examples, | ||
encoding | ||
} | ||
} | ||
}; | ||
} | ||
}); | ||
paths[route] = { | ||
...paths[route], | ||
[method.toLowerCase()]: { | ||
tags, | ||
summary, | ||
description, | ||
externalDocs, | ||
operationId, | ||
parameters, | ||
requestBody, | ||
responses, | ||
callbacks, | ||
deprecated, | ||
security, | ||
servers | ||
} | ||
}; | ||
}); | ||
return paths; | ||
}; | ||
export var isZodSchema = function (obj) { | ||
return !!obj && typeof obj === 'object' && '_def' in obj; | ||
exports.getPathsFromMethodHandlers = getPathsFromMethodHandlers; | ||
const generatePaths = async ({ config: { openApiJsonPath, openApiYamlPath, swaggerUiPath, errorHandler } }) => { | ||
const filterApiRoutes = (file) => { | ||
const isCatchAllRoute = file.includes('...'); | ||
const isOpenApiJsonRoute = file.endsWith(`${openApiJsonPath?.split('/').at(-1)}.ts`); | ||
const isOpenApiYamlRoute = file.endsWith(`${openApiYamlPath?.split('/').at(-1)}.ts`); | ||
const isSwaggerUiRoute = file.endsWith(`${swaggerUiPath?.split('/').at(-1)}.ts`); | ||
if (isCatchAllRoute || | ||
isOpenApiJsonRoute || | ||
isOpenApiYamlRoute || | ||
isSwaggerUiRoute) { | ||
return false; | ||
} | ||
else { | ||
return true; | ||
} | ||
}; | ||
const mapApiRoutes = (0, fs_1.readdirSync)((0, path_1.join)(process.cwd(), 'pages/api')) | ||
.filter(filterApiRoutes) | ||
.map((file) => `/api/${file}` | ||
.replace('/index', '') | ||
.replace('[', '{') | ||
.replace(']', '}') | ||
.replace('.ts', '')); | ||
let paths = {}; | ||
await Promise.all(mapApiRoutes.map(async (route) => { | ||
try { | ||
const res = await fetch(`http://localhost:3000${route}`, { | ||
headers: { | ||
'User-Agent': constants_1.NEXT_REST_FRAMEWORK_USER_AGENT | ||
} | ||
}); | ||
const data = await res.json(); | ||
paths = { ...paths, ...data }; | ||
} | ||
catch (error) { | ||
logNextRestFrameworkError({ error }); | ||
} | ||
})); | ||
return paths; | ||
}; | ||
export var isYupSchema = function (obj) { | ||
return !!obj && obj.constructor.name === 'ObjectSchema'; | ||
}; | ||
export var isYupValidationError = function (e) { | ||
return e instanceof Error && e.name === 'ValidationError'; | ||
}; | ||
exports.generatePaths = generatePaths; |
{ | ||
"name": "next-rest-framework", | ||
"version": "0.1.0", | ||
"version": "0.1.1", | ||
"description": "Next REST Framework - write type-safe, self-documenting REST APIs in Next.js", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
<p align="center"> | ||
<br/> | ||
<img width="250px" src="./packages/next-rest-framework/logo.svg" /> | ||
<img width="250px" src="https://raw.githubusercontent.com/blomqma/next-rest-framework/d02224b38d07ede85257b22ed50159a947681f99/packages/next-rest-framework/logo.svg" /> | ||
<h2 align="center">Next REST Framework</h3> | ||
@@ -11,4 +11,16 @@ <p align="center">Type-safe, self-documenting REST APIs for Next.js</p> | ||
</a> | ||
<a href="https://codecov.io/gh/blomqma/next-rest-framework" > | ||
<img src="https://codecov.io/gh/blomqma/next-rest-framework/branch/main/graph/badge.svg?token=IUG5ZCVGPV"/> | ||
</a> | ||
<a href="https://github.com/blomqma/next-rest-framework/stargazers"> | ||
<img src="https://img.shields.io/github/stars/blomqma/next-rest-framework" alt="Github Stars" /> | ||
</a> | ||
<a href="https://packagephobia.com/result?p=next-rest-framework"> | ||
<img src="https://packagephobia.com/badge?p=next-rest-framework" alt="Bundle Size"/> | ||
</a> | ||
<a href="https://opensource.org/licenses/ISC" rel="nofollow"> | ||
<img src="https://img.shields.io/badge/License-ISC-blue.svg" alt="License"> | ||
</a> | ||
<img src="https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg" alt="Contributor Covenant 2.1" /> | ||
</p> | ||
</p> | ||
</p> | ||
@@ -120,3 +132,2 @@ | ||
// Can be a `zod` schema as well. | ||
const todoSchema = object({ | ||
@@ -123,0 +134,0 @@ id: string(), |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
371
3.06%2
-33.33%2179817
-9.51%30
-9.09%20536
-6.56%1
Infinity%