Socket
Socket
Sign inDemoInstall

urgot

Package Overview
Dependencies
40
Maintainers
1
Versions
84
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.5.7 to 1.0.5

dist/bin/options.d.ts

56

dist/bin/main.js
#!/usr/bin/env node
import Path from 'path';
const resolve = (...args) => Path.resolve(process.env.INIT_CWD, ...args);
const [command, target] = process.argv.slice(2);
if (command === '-v') {
console.log('1.0.0');
import fs from 'fs';
import path from 'path';
import url from 'url';
import childProcess from 'child_process';
import options, { files } from './options.js';
const resolve = (...args) => path.resolve(process.cwd(), ...args);
class Base {
static init(name) {
options.name = name ?? 'MyApp';
const packagePath = path.resolve(url.fileURLToPath(import.meta.url), '../../../package.json');
const packageJSON = JSON.parse(fs.readFileSync(packagePath).toString());
options.dependencies.urgot = `^${packageJSON.version}`;
fs.writeFileSync(resolve('package.json'), JSON.stringify(options, null, 2));
const create = (root, value) => {
for (const key in value) {
const item = value[key];
try {
if (item === null) {
fs.mkdirSync(resolve(root, key));
}
if (typeof item === 'string') {
fs.writeFileSync(resolve(root, key), item);
continue;
}
if (typeof item === 'object') {
create(resolve(root, key), item);
}
}
catch (error) { }
}
};
create(resolve(), files);
try {
childProcess.execSync('pnpm i', { cwd: resolve() });
}
catch (error) {
childProcess.execSync('npm i', { cwd: resolve() });
}
console.log('[urgot]', 'completed');
}
static dev() {
childProcess.exec('tsc -w', { cwd: resolve(), windowsHide: true });
childProcess.execSync('node --experimental-loader urgot/system/loader dist/main.js', { cwd: resolve() });
}
}
{
const [command, target] = process.argv.slice(2);
const name = command;
if (name !== 'prototype' && typeof Base[name] === 'function') {
Base[name](target);
}
}

61

dist/core/app.d.ts

@@ -1,36 +0,29 @@

import Context from '../context.js';
import AppContext from './appContext.js';
export interface AppCallback {
onRequest?: (context: AppContext) => Promise<void> | void;
onResponse?: (context: AppContext, value: unknown) => Promise<void> | void;
onError?: (context: AppContext, value: unknown) => Promise<void> | void;
import Context from './context.js';
export declare class App {
protected context: Context;
/**
* 实例化后触发
**/
onCreate(): Promise<void>;
constructor(context: Context);
}
export declare type AppOptions = {
path?: {
include?: string;
app?: string;
};
fileFilter?: {
dir?: string[];
ext?: string[];
};
session?: {
action?: {
get(key: string): Promise<string | null>;
setex(key: string, seconds: number, value: string): Promise<'OK'>;
expire(key: string, seconds: number): Promise<number>;
del(key: string): Promise<number>;
};
expires?: number;
cookieName?: string;
cookie?: () => {
domain?: string;
path?: string;
expires?: string;
maxAge?: number;
httpOnly?: boolean;
};
};
};
declare const _default: (calls?: AppCallback, options?: AppOptions) => Promise<(context: Context, next: () => Promise<void>) => Promise<void>>;
interface CreateApp {
/**
* 发生错误时触发
**/
onError?: (error: unknown, context: Context) => unknown;
/**
* 请求时触发(在实例化应用类之前)
**/
onRequest?: (context: Context) => unknown;
/**
* 应用响应时触发(路由方法返回值时触发)
**/
onResponse?: (value: unknown, context: Context) => unknown;
/**
* 执行结束销毁时触发(无论是否抛出错误始终触发)
**/
onDestroy?: (context: Context) => unknown;
}
declare const _default: (apps: (typeof App)[], options?: CreateApp) => (context: Context, next: Function) => Promise<void>;
export default _default;

@@ -1,110 +0,65 @@

import * as Uuid from 'uuid';
import AppLoader from './appLoader.js';
import { JSONImprove } from '../util.js';
export default async (calls = {}, options = {}) => {
const routes = await AppLoader(options?.path?.include || './lib', options?.path?.app || './lib/app', options.fileFilter || {});
const session = {
name: options.session?.cookieName || 'sessionKey',
expires: options.session?.expires || 60 * 60 * 24 * 30,
action: options.session?.action,
cookie: options.session?.cookie
};
const handler = async (context, apps) => {
const appContext = context;
for (const app of apps) {
try {
let state;
let sessionkey = String(context.request.cookies[session.name]);
appContext.session = {
key: sessionkey,
get state() {
if (state)
return state;
return state = new Promise(async (res, rej) => {
if (!session.action)
return rej(new Error('Did not implement session action.get method'));
const data = await session.action.get(appContext.session.key);
if (!data)
return res({});
res(JSONImprove.parse(data));
});
},
async setState() {
if (!session.action)
throw new Error('Did not implement session action.setex method');
if (!state)
return;
const val = await state;
await session.action.setex(appContext.session.key, session.expires, JSONImprove.stringify(val));
if (context.request.cookies[session.name] !== appContext.session.key) {
context.cookies[session.name] = { ...session.cookie?.(), value: appContext.session.key };
}
},
async destroy() {
if (!session.action)
throw new Error('Did not implement session action.del method');
await session.action.del(appContext.session.key);
},
async refreshExpires() {
if (!session.action)
throw new Error('Did not implement session action.expire method');
await session.action.expire(appContext.session.key, session.expires);
}
};
if (!Uuid.validate(sessionkey)) {
const uuid = Uuid.v4();
sessionkey = uuid;
appContext.session.key = uuid;
export class App {
context;
/**
* 实例化后触发
**/
async onCreate() { }
constructor(context) {
this.context = context;
}
}
export default (apps, options) => {
const appRoutes = {};
for (const item of apps) {
const app = item;
for (const prefix of app.__$routes ?? ['']) {
for (const methodName of Object.getOwnPropertyNames(app.prototype)) {
if (methodName === 'constructor')
continue;
const method = app.prototype[methodName];
if (typeof method !== 'function' || !method.__$routes)
continue;
for (const httpObject of method.__$routes) {
const route = `${prefix}${httpObject.route === '' && prefix === '' ? '/' : httpObject.route}`;
if (!appRoutes[route])
appRoutes[route] = {};
if (appRoutes[route][httpObject.method])
console.warn('[urgot]', '路由定义重复', `${httpObject.method} ${route}`);
appRoutes[route][httpObject.method] = { instance: app, func: methodName };
}
const object = new app(appContext);
if (typeof object.onCreate === 'function')
await object.onCreate();
try {
if (typeof object.all === 'function') {
const all = await object.all();
if (all !== undefined) {
calls.onResponse && await calls.onResponse(appContext, all);
break;
}
}
const method = object[appContext.method.toLocaleLowerCase()];
if (typeof method === 'function') {
const methdsObj = method;
const args = methdsObj.validat ? await methdsObj.validat(appContext) : [];
const called = await methdsObj.apply(object, args);
if (called !== undefined) {
calls.onResponse && await calls.onResponse(appContext, called);
break;
}
}
}
catch (error) {
if (calls.onError)
await calls.onError(appContext, error);
break;
}
finally {
if (typeof object.onDestroy === 'function')
await object.onDestroy();
if (sessionkey !== appContext.session.key) {
if (state !== undefined && session.action)
await session.action.del(sessionkey);
context.cookies[session.name] = { ...session.cookie?.(), value: appContext.session.key };
}
}
}
catch (error) {
if (calls.onError)
await calls.onError(appContext, error);
break;
}
}
};
}
return async (context, next) => {
await next();
const pathname = context.url.pathname;
if (!context.method || !routes[pathname])
return;
await handler(context, routes[pathname]);
try {
const route = appRoutes[context.url.pathname];
if (!route)
return;
const method = context.method?.toLocaleUpperCase();
if (!method || !route[method])
return;
await options?.onRequest?.(context);
const obj = route[method];
const app = new obj.instance(context);
await app.onCreate?.();
const func = app[obj.func];
for (const item of func.__$uses ?? [])
await item(context);
const args = [];
if (func.__$paramers) {
const params = new func.__$paramers(context);
await params.onCreate?.();
args.push(params);
}
const out = await func.apply(app, args);
await options?.onResponse?.(out, context);
}
catch (error) {
await options?.onError?.(error, context);
}
finally {
await options?.onDestroy?.(context);
}
};
};

@@ -1,20 +0,21 @@

import Context, { File } from './context.js';
import AppContext, { App } from './core/appContext.js';
import Server from './server.js';
import { JSONImprove } from './util.js';
import Server from './core/server.js';
import Context, { File } from './core/context.js';
import * as utils from './utils.js';
import { App } from './core/app.js';
declare type Methods = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'TRACE';
declare type RouterDecorator = (target: unknown) => void;
export declare const Router: (path: string | string[]) => RouterDecorator;
declare type ValidatorCallback<T> = (context: AppContext) => T | Promise<T>;
declare type ValidatorDecorator<T extends readonly unknown[]> = (target: unknown, key: string, desc: {
value?: {
(...args: T): unknown;
};
}) => void;
export declare const Validator: <T extends readonly unknown[]>(func: ValidatorCallback<T>) => ValidatorDecorator<T>;
declare type ParamsDecorator<T> = (target: unknown, key: string, desc: {
declare type RouterMethodDecorator = (target: unknown, key: string, desc: PropertyDescriptor) => void;
declare type Router = {
(method: Methods, path?: string): RouterMethodDecorator;
(prefix: string): RouterDecorator;
};
declare const Router: Router;
declare type Use = (target: unknown, key: string, desc: PropertyDescriptor) => void;
declare const Use: (...hooks: ((context: Context) => unknown)[]) => Use;
declare type Paramer<T> = (target: unknown, key: string, desc: {
value?: (params: T) => void;
}) => void;
export declare const Params: <T extends {
onStart: (context: AppContext) => Promise<void>;
}>(dto: new () => T) => ParamsDecorator<T>;
export { Server, App, Context, AppContext, File, JSONImprove };
declare const Paramer: <T extends {
onCreate: () => unknown;
}>(paramsConstructor: new (context: Context) => T) => Paramer<T>;
export { Server, App, Context, File, Use, Router, Paramer, utils };

@@ -1,27 +0,32 @@

import Context, { File } from './context.js';
import { App } from './core/appContext.js';
import Server from './server.js';
import { JSONImprove } from './util.js';
export const Router = (path) => {
return (target) => {
const app = target;
app.routes = Array.isArray(path) ? path : [path];
import Server from './core/server.js';
import Context, { File } from './core/context.js';
import * as utils from './utils.js';
import { App } from './core/app.js';
const Router = (prefix, path) => {
return (target, key, desc) => {
if (!key) {
const newTarget = target;
if (!newTarget.__$routes)
newTarget.__$routes = [];
newTarget.__$routes.push(prefix);
return;
}
const func = desc?.value;
if (!func.__$routes)
func.__$routes = [];
func.__$routes.push({ method: prefix, route: path ?? '' });
};
};
export const Validator = (func) => {
const Use = (...hooks) => {
return (target, key, desc) => {
const value = desc.value;
value.validat = func;
const func = desc?.value;
func.__$uses = hooks;
};
};
export const Params = (dto) => {
return async (target, key, desc) => {
const fn = desc.value;
fn.validat = async (context) => {
const obj = new dto();
await obj.onStart(context);
return [obj];
};
const Paramer = (paramsConstructor) => {
return (target, key, desc) => {
const func = desc?.value;
func.__$paramers = paramsConstructor;
};
};
export { Server, App, Context, File, JSONImprove };
export { Server, App, Context, File, Use, Router, Paramer, utils };
{
"name": "urgot",
"version": "0.5.7",
"version": "1.0.05",
"description": "a Node.js web framework",
"type": "module",
"keywords": [
"web frameworks",
"web",
"framework",
"middleware",
"app"
"web frameworks"
],
"scripts": {
"dev": "nodemon ./dist/main.js",
"test": "nodemon --exec ts-node-esm ./src/test/main.ts",
"test:pm2": "pm2 start dist/test.js"
},
"bin": {

@@ -22,32 +23,18 @@ "urgot": "./dist/bin/main.js"

".": "./dist/main.js",
"./loader": "./dist/core/loader.js",
"./context": "./dist/context.js",
"./server": "./dist/server.js"
"./system/loader": "./dist/system/loader.js"
},
"types": "dist/main.d.ts",
"typesVersions": {
"*": {
"context": [
"dist/context.d.ts"
],
"server": [
"dist/server.d.ts"
]
}
},
"dependencies": {
"@types/busboy": "^0.2.4",
"@types/graceful-fs": "^4.1.5",
"@types/busboy": "^1.5.0",
"@types/json-bigint": "^1.0.1",
"@types/mime-types": "^2.1.1",
"@types/node": "^16.4.8",
"@types/uuid": "^8.3.1",
"busboy": "^0.3.1",
"chokidar": "^3.5.2",
"file-type": "^16.5.2",
"graceful-fs": "^4.2.8",
"@types/node": "^18.7.18",
"@types/uuid": "^8.3.4",
"busboy": "^1.6.0",
"chokidar": "^3.5.3",
"file-type": "^18.0.0",
"json-bigint": "^1.0.0",
"mime-types": "^2.1.32",
"uuid": "^8.3.2"
"mime-types": "^2.1.35",
"uuid": "^9.0.0"
}
}

@@ -1,28 +0,27 @@

# Language
English | [简体中文](./README.cn.md)
Urgot 是一个轻量级高性能的 Node.js 框架,可以用TypeScript构建服务器端应用程序。
Urgot is a Nodejs HTTP middleware framework written in Typescript. It allows you to create and pass down requests in the middleware, then filter and respond in reverse order. It comes with an out-of-the-box application development framework, hot update, Session, etc. It supports both Javascript and Typescript.
# Installation
# 安装
```shell
$ npm install urgot
npm i urgot
```
安装 **typescript** 和 **ts-node**
```shell
npm i -g typescript ts-node
```
# Hello World
```js
//index.js
import { Server } from "urgot"
## Hello World
```typescript
import { Server } from 'urgot'
const server = new Server()
server.use(async (context, next) => {
context.body = "Hello World"
context.body = 'Hello World'
await next()
})
server.run(8080)
server.listen(5611)
```
# Middleware
Middleware is the main function of urgot. Calling the `use` method to register a middleware requires an asynchronous function (Promise) as a parameter.
调用 `use` 方法注册一个中间件,接受一个异步函数,当监听到请求时传入请求上下文(`Context`)和一个`Next`函数用于响应并执行下一个中间件
```js
```typescript
server.use(async (context, next) => {

@@ -32,15 +31,141 @@ const start = Date.now()

const ms = Date.now() - start
context.setHeader("X-Response-Time", `${ms}ms`)
context.body = "Hello World"
context.setHeader('X-Response-Time', `${ms}ms`)
context.body = 'Hello World'
})
```
Tips: Whenever you should call await next() to let the downstream intermediate response execution.
中间件使用洋葱模型,从请求(**next()前**)到响应(**next()后**),每一个中间件都有两次处理时机。
# Context and Next functions
Each middleware will receive a Context object and a `next` asynchronous function. The `Context` object is the encapsulation of `Http.ServerResponse`, of course you can visit `context.response` to access it, but usually we do not recommend it. After processing the middleware, you need to call the `next` function to let the downstream middleware Respond to execution.
# 创建应用
调用 **createApp** 创建您的应用程序并传入您的应用和配置项,
```typescript
import { App } from 'urgot'
# Demo
[https://gitee.com/apprat/urgot-server](https://gitee.com/apprat/urgot-server)
class MyApp extends App {
//实现
}
# License
[MIT](./LICENSE)
const server = new Server()
server.createApp([MyApp])
server.listen(5611)
```
## Router 注解
使用 `Router` 注解去设置路由和HTTP方法
```typescript
import { App, Server, Router } from 'urgot'
class MyApp extends App {
@Router('GET')
find() {
const { searchParams } = this.context.url
return { id: searchParams.get('id') }
}
}
const server = new Server()
server.createApp([MyApp],{ onResponse: (vlaue, context) => context.body = vlaue as object })
server.listen(5611)
```
>访问地址:http://localhost/?id=123456
>页面响应:{ id: 123456 }
`Reouter` 不仅可以注解方法,还可注解控制器类来添加固定的路由前缀
```typescript
import { App, Router } from 'urgot'
@Router('/video')
class MyApp extends App {
@Router('GET','/list')
find() {
const { searchParams } = this.context.url
return { targets: searchParams.getAll('name') }
}
}
```
>访问地址:http://localhost/video/list?name=Yone&name=Yasuo
>页面响应: { tagrets: ["Yone", "Yasuo"] }
PS:`Router` 可以注册多次来定义多对一的路由
## Use 注解
使用 **Use** 注解来注册自定义的拦截器
```typescript
import { App, use, Router, Context } from 'urgot'
const auth = (context: Context) => {
if (!context.url.searchParams.get('authed')) throw 'Please login'
}
@Router('/video')
class MyApp extends App {
@Use(auth)
@Router('GET','/list')
find() {
const { searchParams } = this.context.url
return { targets: searchParams.getAll('name') }
}
}
```
## Paramer 注解
获取请求提交的参数是必不可少的一项,使用 **Paramer** 参数注解获取和转换用户提交的参数
```typescript
import { App, Use, Router, Context } from 'urgot'
class Params {
id!: number
constructor(private context: Context) { }
async onCreate() {
const { searchParams } = this.context.url
this.id = Number(searchParams.get('id'))
}
}
@Router('/video')
class MyApp extends App {
@Paramer(Params)
@Router('GET','/list')
find(params: Params) {
return { upload: params.id }
}
}
```
>所有注解在抛出错误后都会阻止继续执行,并响应该错误值,利用这个特性来拦截一些操作。
# 命令行
可以使用 `urgot` 命令行来使用 **urgot** 的内置命令(局部安装需要使用 `npx` 命令)
| 命令 | 说明 | 示例 |
|---|---|---|
| init | 初始化项目 (会覆盖当前文件夹) | `urtot init MyApp` |
| dev | 运行开发环境 | `urtot dev` |
# 常见问题
- **HTTP Body 解析**
>调用`context.request.body()`解析请求Body,它返回一个`Promise`(注意多次调用仅在第一次解析,后续调用将返回缓存值
- **Session 的使用**
>要使用`Session`需要在实例化`Server`时配置`session`实现参数,否则在调用时将抛出错误
```typescript
const server = new Server({
session: {
handlers: {
//实现方法,建议使用IORedis库,传递client即可
}
}
})
```
- **处理路由函数返回的值**
>你必须在调用 `server.createApp()`时传递 `options`选项来处理返回值
```typescript
import { Server } from 'urgot'
const server = new Server()
server.createApp([],{
//当路由方法返回时触发
onResponse: (vlaue, context) => {
context.body = vlaue as object
}
})
server.listen(5611)
```

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc