Socket
Socket
Sign inDemoInstall

@elysiajs/swagger

Package Overview
Dependencies
Maintainers
1
Versions
56
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@elysiajs/swagger - npm Package Compare versions

Comparing version 0.5.2 to 0.6.0-beta.0

13

dist/cjs/index.d.ts
import { type Elysia } from 'elysia';
import type { ElysiaSwaggerConfig } from './types';
export declare const swagger: <Path extends string = "/swagger">({ documentation, version, excludeStaticFile, path, exclude }?: ElysiaSwaggerConfig<Path>) => (app: Elysia) => Elysia<import("elysia").ElysiaInstance>;
export declare const swagger: <Path extends string = "/swagger">({ documentation, version, excludeStaticFile, path, exclude }?: ElysiaSwaggerConfig<Path>) => (app: Elysia) => Elysia<{
path: "";
store: {};
request: {};
schema: {};
error: {};
meta: {
schema: {};
defs: {};
exposed: {};
};
}>;
export default swagger;

57

dist/cjs/index.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.swagger = void 0;
const elysia_1 = require("elysia");
const utils_1 = require("./utils");

@@ -13,2 +12,4 @@ const swagger = ({ documentation = {}, version = '4.18.2', excludeStaticFile = true, path = '/swagger', exclude = [] } = {

}) => (app) => {
const schema = {};
let totalRoutes = 0;
const info = {

@@ -20,3 +21,3 @@ title: 'Elysia Documentation',

};
app.get(path, (context) => {
app.get(path, () => {
return new Response(`<!DOCTYPE html>

@@ -55,21 +56,37 @@ <html lang="en">

});
}).route('GET', `${path}/json`, (context) => ({
openapi: '3.0.3',
...{
...documentation,
info: {
title: 'Elysia Documentation',
description: 'Developement documentation',
version: '0.0.0',
...documentation.info
}).route('GET', `${path}/json`, () => {
const routes = app.routes;
if (routes.length !== totalRoutes) {
totalRoutes = routes.length;
routes.forEach((route) => {
(0, utils_1.registerSchemaPath)({
schema,
hook: route.hooks,
method: route.method,
path: route.path,
models: app.meta.defs,
contentType: route.hooks.type
});
});
}
return {
openapi: '3.0.3',
...{
...documentation,
info: {
title: 'Elysia Documentation',
description: 'Developement documentation',
version: '0.0.0',
...documentation.info
}
},
paths: (0, utils_1.filterPaths)(schema, {
excludeStaticFile,
exclude: Array.isArray(exclude) ? exclude : [exclude]
}),
components: {
schemas: app.meta.defs
}
},
paths: (0, utils_1.filterPaths)(context[elysia_1.SCHEMA], {
excludeStaticFile,
exclude: Array.isArray(exclude) ? exclude : [exclude]
}),
components: {
schemas: context[elysia_1.DEFS]
}
}), {
};
}, {
config: {

@@ -76,0 +93,0 @@ allowMeta: true

@@ -0,1 +1,16 @@

import type { HTTPMethod, LocalHook } from 'elysia';
import { type TSchema } from '@sinclair/typebox';
import type { OpenAPIV3 } from 'openapi-types';
export declare const toOpenAPIPath: (path: string) => string;
export declare const mapProperties: (name: string, schema: TSchema | string | undefined, models: Record<string, TSchema>) => any[];
export declare const capitalize: (word: string) => string;
export declare const generateOperationId: (method: string, paths: string) => string;
export declare const registerSchemaPath: ({ schema, path, method, hook, models }: {
schema: Partial<OpenAPIV3.PathsObject>;
contentType?: string | string[] | undefined;
path: string;
method: HTTPMethod;
hook?: LocalHook<any, any> | undefined;
models: Record<string, TSchema>;
}) => void;
export declare const filterPaths: (paths: Record<string, any>, { excludeStaticFile, exclude }: {

@@ -2,0 +17,0 @@ excludeStaticFile: boolean;

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.filterPaths = void 0;
exports.filterPaths = exports.registerSchemaPath = exports.generateOperationId = exports.capitalize = exports.mapProperties = exports.toOpenAPIPath = void 0;
const typebox_1 = require("@sinclair/typebox");
const lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep"));
const toOpenAPIPath = (path) => path
.split('/')
.map((x) => (x.startsWith(':') ? `{${x.slice(1, x.length)}}` : x))
.join('/');
exports.toOpenAPIPath = toOpenAPIPath;
const mapProperties = (name, schema, models) => {
if (schema === undefined)
return [];
if (typeof schema === 'string')
if (schema in models)
schema = models[schema];
else
throw new Error(`Can't find model ${schema}`);
return Object.entries(schema?.properties ?? []).map(([key, value]) => ({
...value,
in: name,
name: key,
type: value?.type,
required: schema.required?.includes(key) ?? false
}));
};
exports.mapProperties = mapProperties;
const mapTypesResponse = (types, schema) => {
const responses = {};
for (const type of types)
responses[type] = {
schema: typeof schema === 'string'
? {
$ref: `#/components/schemas/${schema}`
}
: { ...schema }
};
return responses;
};
const capitalize = (word) => word.charAt(0).toUpperCase() + word.slice(1);
exports.capitalize = capitalize;
const generateOperationId = (method, paths) => {
let operationId = method.toLowerCase();
if (paths === '/')
return operationId + 'Index';
for (const path of paths.split('/')) {
if (path.charCodeAt(0) === 123) {
operationId += 'By' + (0, exports.capitalize)(path.slice(1, -1));
}
else {
operationId += (0, exports.capitalize)(path);
}
}
return operationId;
};
exports.generateOperationId = generateOperationId;
const registerSchemaPath = ({ schema, path, method, hook, models }) => {
if (hook)
hook = (0, lodash_clonedeep_1.default)(hook);
const contentType = hook?.type ?? [
'application/json',
'multipart/form-data',
'text/plain'
];
path = (0, exports.toOpenAPIPath)(path);
const contentTypes = typeof contentType === 'string'
? [contentType]
: contentType ?? ['application/json'];
const bodySchema = hook?.body;
const paramsSchema = hook?.params;
const headerSchema = hook?.headers;
const querySchema = hook?.query;
let responseSchema = hook?.response;
if (typeof responseSchema === 'object') {
if (typebox_1.Kind in responseSchema) {
const { type, properties, required, ...rest } = responseSchema;
responseSchema = {
'200': {
...rest,
description: rest.description,
content: mapTypesResponse(contentTypes, type === 'object' || type === 'array'
? {
type,
properties,
required
}
: responseSchema)
}
};
}
else {
Object.entries(responseSchema).forEach(([key, value]) => {
if (typeof value === 'string') {
const { type, properties, required, ...rest } = models[value];
responseSchema[key] = {
...rest,
description: rest.description,
content: mapTypesResponse(contentTypes, value)
};
}
else {
const { type, properties, required, ...rest } = value;
responseSchema[key] = {
...rest,
description: rest.description,
content: mapTypesResponse(contentTypes, {
type,
properties,
required
})
};
}
});
}
}
else if (typeof responseSchema === 'string') {
const { type, properties, required, ...rest } = models[responseSchema];
responseSchema = {
'200': {
...rest,
content: mapTypesResponse(contentTypes, responseSchema)
}
};
}
const parameters = [
...(0, exports.mapProperties)('header', headerSchema, models),
...(0, exports.mapProperties)('path', paramsSchema, models),
...(0, exports.mapProperties)('query', querySchema, models)
];
schema[path] = {
...(schema[path] ? schema[path] : {}),
[method.toLowerCase()]: {
...((headerSchema || paramsSchema || querySchema || bodySchema
? { parameters }
: {})),
...(responseSchema
? {
responses: responseSchema
}
: {}),
operationId: hook?.detail?.operationId ?? (0, exports.generateOperationId)(method, path),
...hook?.detail,
...(bodySchema
? {
requestBody: {
content: mapTypesResponse(contentTypes, typeof bodySchema === 'string'
? {
$ref: `#/components/schemas/${bodySchema}`
}
: bodySchema)
}
}
: null)
}
};
};
exports.registerSchemaPath = registerSchemaPath;
const filterPaths = (paths, { excludeStaticFile = true, exclude = [] }) => {

@@ -5,0 +162,0 @@ const newPaths = {};

import { type Elysia } from 'elysia';
import type { ElysiaSwaggerConfig } from './types';
export declare const swagger: <Path extends string = "/swagger">({ documentation, version, excludeStaticFile, path, exclude }?: ElysiaSwaggerConfig<Path>) => (app: Elysia) => Elysia<import("elysia").ElysiaInstance>;
export declare const swagger: <Path extends string = "/swagger">({ documentation, version, excludeStaticFile, path, exclude }?: ElysiaSwaggerConfig<Path>) => (app: Elysia) => Elysia<{
path: "";
store: {};
request: {};
schema: {};
error: {};
meta: {
schema: {};
defs: {};
exposed: {};
};
}>;
export default swagger;

@@ -1,3 +0,2 @@

import { SCHEMA, DEFS } from 'elysia';
import { filterPaths } from './utils';
import { filterPaths, registerSchemaPath } from './utils';
export const swagger = ({ documentation = {}, version = '4.18.2', excludeStaticFile = true, path = '/swagger', exclude = [] } = {

@@ -10,2 +9,4 @@ documentation: {},

}) => (app) => {
const schema = {};
let totalRoutes = 0;
const info = {

@@ -17,3 +18,3 @@ title: 'Elysia Documentation',

};
app.get(path, (context) => {
app.get(path, () => {
return new Response(`<!DOCTYPE html>

@@ -52,21 +53,37 @@ <html lang="en">

});
}).route('GET', `${path}/json`, (context) => ({
openapi: '3.0.3',
...{
...documentation,
info: {
title: 'Elysia Documentation',
description: 'Developement documentation',
version: '0.0.0',
...documentation.info
}).route('GET', `${path}/json`, () => {
const routes = app.routes;
if (routes.length !== totalRoutes) {
totalRoutes = routes.length;
routes.forEach((route) => {
registerSchemaPath({
schema,
hook: route.hooks,
method: route.method,
path: route.path,
models: app.meta.defs,
contentType: route.hooks.type
});
});
}
return {
openapi: '3.0.3',
...{
...documentation,
info: {
title: 'Elysia Documentation',
description: 'Developement documentation',
version: '0.0.0',
...documentation.info
}
},
paths: filterPaths(schema, {
excludeStaticFile,
exclude: Array.isArray(exclude) ? exclude : [exclude]
}),
components: {
schemas: app.meta.defs
}
},
paths: filterPaths(context[SCHEMA], {
excludeStaticFile,
exclude: Array.isArray(exclude) ? exclude : [exclude]
}),
components: {
schemas: context[DEFS]
}
}), {
};
}, {
config: {

@@ -73,0 +90,0 @@ allowMeta: true

@@ -0,1 +1,16 @@

import type { HTTPMethod, LocalHook } from 'elysia';
import { type TSchema } from '@sinclair/typebox';
import type { OpenAPIV3 } from 'openapi-types';
export declare const toOpenAPIPath: (path: string) => string;
export declare const mapProperties: (name: string, schema: TSchema | string | undefined, models: Record<string, TSchema>) => any[];
export declare const capitalize: (word: string) => string;
export declare const generateOperationId: (method: string, paths: string) => string;
export declare const registerSchemaPath: ({ schema, path, method, hook, models }: {
schema: Partial<OpenAPIV3.PathsObject>;
contentType?: string | string[] | undefined;
path: string;
method: HTTPMethod;
hook?: LocalHook<any, any> | undefined;
models: Record<string, TSchema>;
}) => void;
export declare const filterPaths: (paths: Record<string, any>, { excludeStaticFile, exclude }: {

@@ -2,0 +17,0 @@ excludeStaticFile: boolean;

@@ -0,1 +1,150 @@

import { Kind } from '@sinclair/typebox';
import deepClone from 'lodash.clonedeep';
export const toOpenAPIPath = (path) => path
.split('/')
.map((x) => (x.startsWith(':') ? `{${x.slice(1, x.length)}}` : x))
.join('/');
export const mapProperties = (name, schema, models) => {
if (schema === undefined)
return [];
if (typeof schema === 'string')
if (schema in models)
schema = models[schema];
else
throw new Error(`Can't find model ${schema}`);
return Object.entries(schema?.properties ?? []).map(([key, value]) => ({
...value,
in: name,
name: key,
type: value?.type,
required: schema.required?.includes(key) ?? false
}));
};
const mapTypesResponse = (types, schema) => {
const responses = {};
for (const type of types)
responses[type] = {
schema: typeof schema === 'string'
? {
$ref: `#/components/schemas/${schema}`
}
: { ...schema }
};
return responses;
};
export const capitalize = (word) => word.charAt(0).toUpperCase() + word.slice(1);
export const generateOperationId = (method, paths) => {
let operationId = method.toLowerCase();
if (paths === '/')
return operationId + 'Index';
for (const path of paths.split('/')) {
if (path.charCodeAt(0) === 123) {
operationId += 'By' + capitalize(path.slice(1, -1));
}
else {
operationId += capitalize(path);
}
}
return operationId;
};
export const registerSchemaPath = ({ schema, path, method, hook, models }) => {
if (hook)
hook = deepClone(hook);
const contentType = hook?.type ?? [
'application/json',
'multipart/form-data',
'text/plain'
];
path = toOpenAPIPath(path);
const contentTypes = typeof contentType === 'string'
? [contentType]
: contentType ?? ['application/json'];
const bodySchema = hook?.body;
const paramsSchema = hook?.params;
const headerSchema = hook?.headers;
const querySchema = hook?.query;
let responseSchema = hook?.response;
if (typeof responseSchema === 'object') {
if (Kind in responseSchema) {
const { type, properties, required, ...rest } = responseSchema;
responseSchema = {
'200': {
...rest,
description: rest.description,
content: mapTypesResponse(contentTypes, type === 'object' || type === 'array'
? {
type,
properties,
required
}
: responseSchema)
}
};
}
else {
Object.entries(responseSchema).forEach(([key, value]) => {
if (typeof value === 'string') {
const { type, properties, required, ...rest } = models[value];
responseSchema[key] = {
...rest,
description: rest.description,
content: mapTypesResponse(contentTypes, value)
};
}
else {
const { type, properties, required, ...rest } = value;
responseSchema[key] = {
...rest,
description: rest.description,
content: mapTypesResponse(contentTypes, {
type,
properties,
required
})
};
}
});
}
}
else if (typeof responseSchema === 'string') {
const { type, properties, required, ...rest } = models[responseSchema];
responseSchema = {
'200': {
...rest,
content: mapTypesResponse(contentTypes, responseSchema)
}
};
}
const parameters = [
...mapProperties('header', headerSchema, models),
...mapProperties('path', paramsSchema, models),
...mapProperties('query', querySchema, models)
];
schema[path] = {
...(schema[path] ? schema[path] : {}),
[method.toLowerCase()]: {
...((headerSchema || paramsSchema || querySchema || bodySchema
? { parameters }
: {})),
...(responseSchema
? {
responses: responseSchema
}
: {}),
operationId: hook?.detail?.operationId ?? generateOperationId(method, path),
...hook?.detail,
...(bodySchema
? {
requestBody: {
content: mapTypesResponse(contentTypes, typeof bodySchema === 'string'
? {
$ref: `#/components/schemas/${bodySchema}`
}
: bodySchema)
}
}
: null)
}
};
};
export const filterPaths = (paths, { excludeStaticFile = true, exclude = [] }) => {

@@ -2,0 +151,0 @@ const newPaths = {};

{
"name": "@elysiajs/swagger",
"version": "0.5.2",
"version": "0.6.0-beta.0",
"description": "Plugin for Elysia to auto-generate Swagger page",

@@ -38,12 +38,15 @@ "author": {

"peerDependencies": {
"elysia": ">= 0.5.12"
"elysia": ">= 0.6.0-alpha.3"
},
"devDependencies": {
"@types/node": "^20.1.4",
"bun-types": "^0.5.8",
"elysia": "0.5.12",
"bun-types": "^0.7.0",
"elysia": "^0.6.0-alpha.4",
"eslint": "^8.40.0",
"rimraf": "4.3",
"typescript": "^5.0.4"
},
"dependencies": {
"lodash.clonedeep": "^4.5.0"
}
}

@@ -1,4 +0,4 @@

import { type Elysia, SCHEMA, DEFS } from 'elysia'
import { type Elysia, type InternalRoute } from 'elysia'
import { filterPaths } from './utils'
import { filterPaths, registerSchemaPath } from './utils'

@@ -30,2 +30,5 @@ import type { OpenAPIV3 } from 'openapi-types'

(app: Elysia) => {
const schema = {}
let totalRoutes = 0
const info = {

@@ -38,3 +41,3 @@ title: 'Elysia Documentation',

app.get(path, (context) => {
app.get(path, () => {
return new Response(

@@ -79,4 +82,23 @@ `<!DOCTYPE html>

`${path}/json`,
(context) =>
({
() => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const routes = app.routes as InternalRoute[]
if (routes.length !== totalRoutes) {
totalRoutes = routes.length
routes.forEach((route: InternalRoute<any>) => {
registerSchemaPath({
schema,
hook: route.hooks,
method: route.method,
path: route.path,
models: app.meta.defs,
contentType: route.hooks.type
})
})
}
return {
openapi: '3.0.3',

@@ -92,3 +114,3 @@ ...{

},
paths: filterPaths(context[SCHEMA]!, {
paths: filterPaths(schema, {
excludeStaticFile,

@@ -98,5 +120,6 @@ exclude: Array.isArray(exclude) ? exclude : [exclude]

components: {
schemas: context[DEFS]
schemas: app.meta.defs
}
} satisfies OpenAPIV3.Document),
} satisfies OpenAPIV3.Document
},
{

@@ -103,0 +126,0 @@ config: {

@@ -0,1 +1,237 @@

import type { HTTPMethod, LocalHook } from 'elysia'
import { Kind, type TSchema } from '@sinclair/typebox'
import type { OpenAPIV3 } from 'openapi-types'
import deepClone from 'lodash.clonedeep'
export const toOpenAPIPath = (path: string) =>
path
.split('/')
.map((x) => (x.startsWith(':') ? `{${x.slice(1, x.length)}}` : x))
.join('/')
export const mapProperties = (
name: string,
schema: TSchema | string | undefined,
models: Record<string, TSchema>
) => {
if (schema === undefined) return []
if (typeof schema === 'string')
if (schema in models) schema = models[schema]
else throw new Error(`Can't find model ${schema}`)
return Object.entries(schema?.properties ?? []).map(([key, value]) => ({
// @ts-ignore
...value,
in: name,
name: key,
// @ts-ignore
type: value?.type,
// @ts-ignore
required: schema!.required?.includes(key) ?? false
}))
}
const mapTypesResponse = (
types: string[],
schema:
| string
| {
type: string
properties: Object
required: string[]
}
) => {
const responses: Record<string, OpenAPIV3.MediaTypeObject> = {}
for (const type of types)
responses[type] = {
schema:
typeof schema === 'string'
? {
$ref: `#/components/schemas/${schema}`
}
: { ...(schema as any) }
}
return responses
}
export const capitalize = (word: string) =>
word.charAt(0).toUpperCase() + word.slice(1)
export const generateOperationId = (method: string, paths: string) => {
let operationId = method.toLowerCase()
if (paths === '/') return operationId + 'Index'
for (const path of paths.split('/')) {
if (path.charCodeAt(0) === 123) {
operationId += 'By' + capitalize(path.slice(1, -1))
} else {
operationId += capitalize(path)
}
}
return operationId
}
export const registerSchemaPath = ({
schema,
path,
method,
hook,
models
}: {
schema: Partial<OpenAPIV3.PathsObject>
contentType?: string | string[]
path: string
method: HTTPMethod
hook?: LocalHook<any, any>
models: Record<string, TSchema>
}) => {
if (hook) hook = deepClone(hook)
const contentType = hook?.type ?? [
'application/json',
'multipart/form-data',
'text/plain'
]
path = toOpenAPIPath(path)
const contentTypes =
typeof contentType === 'string'
? [contentType]
: contentType ?? ['application/json']
const bodySchema = hook?.body
const paramsSchema = hook?.params
const headerSchema = hook?.headers
const querySchema = hook?.query
let responseSchema = hook?.response as unknown as OpenAPIV3.ResponsesObject
if (typeof responseSchema === 'object') {
if (Kind in responseSchema) {
const { type, properties, required, ...rest } =
responseSchema as typeof responseSchema & {
type: string
properties: Object
required: string[]
}
responseSchema = {
'200': {
...rest,
description: rest.description as any,
content: mapTypesResponse(
contentTypes,
type === 'object' || type === 'array'
? ({
type,
properties,
required
} as any)
: responseSchema
)
}
}
} else {
Object.entries(responseSchema as Record<string, TSchema>).forEach(
([key, value]) => {
if (typeof value === 'string') {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { type, properties, required, ...rest } = models[
value
] as TSchema & {
type: string
properties: Object
required: string[]
}
responseSchema[key] = {
...rest,
description: rest.description as any,
content: mapTypesResponse(contentTypes, value)
}
} else {
const { type, properties, required, ...rest } =
value as typeof value & {
type: string
properties: Object
required: string[]
}
responseSchema[key] = {
...rest,
description: rest.description as any,
content: mapTypesResponse(contentTypes, {
type,
properties,
required
})
}
}
}
)
}
} else if (typeof responseSchema === 'string') {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { type, properties, required, ...rest } = models[
responseSchema
] as TSchema & {
type: string
properties: Object
required: string[]
}
responseSchema = {
// @ts-ignore
'200': {
...rest,
content: mapTypesResponse(contentTypes, responseSchema)
}
}
}
const parameters = [
...mapProperties('header', headerSchema, models),
...mapProperties('path', paramsSchema, models),
...mapProperties('query', querySchema, models)
]
schema[path] = {
...(schema[path] ? schema[path] : {}),
[method.toLowerCase()]: {
...((headerSchema || paramsSchema || querySchema || bodySchema
? ({ parameters } as any)
: {}) satisfies OpenAPIV3.ParameterObject),
...(responseSchema
? {
responses: responseSchema
}
: {}),
operationId:
hook?.detail?.operationId ?? generateOperationId(method, path),
...hook?.detail,
...(bodySchema
? {
requestBody: {
content: mapTypesResponse(
contentTypes,
typeof bodySchema === 'string'
? {
$ref: `#/components/schemas/${bodySchema}`
}
: (bodySchema as any)
)
}
}
: null)
} satisfies OpenAPIV3.OperationObject
}
}
export const filterPaths = (

@@ -2,0 +238,0 @@ paths: Record<string, any>,

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc