Initial commit
This commit is contained in:
commit
bfeb01484c
5
.eslintrc.json
Normal file
5
.eslintrc.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/no-explicit-any": "off"
|
||||||
|
}
|
||||||
|
}
|
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
node_modules/
|
||||||
|
build/
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
keys/
|
7
README.md
Normal file
7
README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Awesome Project Build with TypeORM
|
||||||
|
|
||||||
|
Steps to run this project:
|
||||||
|
|
||||||
|
1. Run `npm i` command
|
||||||
|
2. Setup database settings inside `data-source.ts` file
|
||||||
|
3. Run `npm start` command
|
21
package.json
Normal file
21
package.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "clonegur",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.ts",
|
||||||
|
"license": "MIT",
|
||||||
|
"type": "commonjs",
|
||||||
|
"dependencies": {
|
||||||
|
"mongodb": "^5.2.0",
|
||||||
|
"reflect-metadata": "^0.1.13"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/express": "^4.17.14",
|
||||||
|
"@types/node": "^16.11.10",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
|
"typescript": "^5.1.3"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "ts-node src/index.ts",
|
||||||
|
"typeorm": "typeorm-ts-node-commonjs"
|
||||||
|
}
|
||||||
|
}
|
30
src/index.ts
Normal file
30
src/index.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { Collection, MongoClient } from "https://deno.land/x/mongo@v0.31.2/mod.ts";
|
||||||
|
import UserRouter from "./routers/UserRouter.ts";
|
||||||
|
import { Router, rest, route } from "./server/Server.ts";
|
||||||
|
import User from "./models/User.ts";
|
||||||
|
import Response from "./server/Response.ts";
|
||||||
|
|
||||||
|
class RootRouter extends Router {
|
||||||
|
@route('users/*') users: UserRouter;
|
||||||
|
|
||||||
|
@rest('*', '*')
|
||||||
|
default() {
|
||||||
|
return new Response().body(new Blob(['Page not found :/'])).status(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(salt: string, users: Collection<User>) {
|
||||||
|
super();
|
||||||
|
this.users = new UserRouter(salt, users);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function clonegur() {
|
||||||
|
const salt = new TextDecoder().decode(await Deno.readFile('keys/salt.txt'));
|
||||||
|
const db = await new MongoClient().connect({
|
||||||
|
db: 'clonegur',
|
||||||
|
servers: [ { host: '127.0.0.1', port: 27017 } ]
|
||||||
|
});
|
||||||
|
await new RootRouter(salt, db.collection('users')).attach(Deno.listen({ port: 4000, hostname: 'localhost' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
clonegur();
|
2
src/models/User.js
Normal file
2
src/models/User.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
5
src/models/User.ts
Normal file
5
src/models/User.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export default interface User {
|
||||||
|
_id: string;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
}
|
8
src/routers/AppRouter.ts
Normal file
8
src/routers/AppRouter.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { HttpError, RestRequest, RestResponse, Router } from "../server/Server.ts";
|
||||||
|
|
||||||
|
export default class AppRouter extends Router {
|
||||||
|
public onError(_req: RestRequest, error: unknown): RestResponse | HttpError | Promise<RestResponse | HttpError> {
|
||||||
|
if (error instanceof HttpError) return new HttpError({ error: error.body }, error.status);
|
||||||
|
return super.onError(_req, error);
|
||||||
|
}
|
||||||
|
}
|
46
src/routers/UserRouter.ts
Normal file
46
src/routers/UserRouter.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { Collection } from "https://deno.land/x/mongo@v0.31.2/mod.ts";
|
||||||
|
import { HttpError, rest } from "../server/Server.ts";
|
||||||
|
import User from "../models/User.ts";
|
||||||
|
import { body, schema } from "../server/Router.ts";
|
||||||
|
import AppRouter from "./AppRouter.ts";
|
||||||
|
import * as bcrypt from "https://deno.land/x/bcrypt@v0.4.1/mod.ts";
|
||||||
|
|
||||||
|
interface SignupRequest {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class UserRouter extends AppRouter {
|
||||||
|
@rest('GET', '/')
|
||||||
|
async get(@schema('string') username: string) {
|
||||||
|
const res = await this.users.findOne({ username });
|
||||||
|
|
||||||
|
if (res === undefined) throw new HttpError('User not found.');
|
||||||
|
|
||||||
|
return { username: res.username };
|
||||||
|
}
|
||||||
|
@rest('POST', '/signup')
|
||||||
|
async signup(
|
||||||
|
@schema({
|
||||||
|
username: 'string', password: 'string',
|
||||||
|
}) @body() body: SignupRequest
|
||||||
|
) {
|
||||||
|
if (await this.users.countDocuments({ username: body.username }) > 0) {
|
||||||
|
throw new HttpError('User with the same username already exists.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const password = await bcrypt.hash(body.password, this.salt);
|
||||||
|
|
||||||
|
await this.users.insertOne({
|
||||||
|
username: body.username,
|
||||||
|
password: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor(private salt: string, private users: Collection<User>) {
|
||||||
|
super();
|
||||||
|
users.createIndexes({ indexes: [ { key: { username: 1 }, name: 'Username Index' } ] });
|
||||||
|
}
|
||||||
|
}
|
122
src/server/Request.ts
Normal file
122
src/server/Request.ts
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
export type ParamDict = Record<string, string>;
|
||||||
|
export type Headers = Record<string, string | string[] | undefined>;
|
||||||
|
|
||||||
|
function splitUrl(url: string) {
|
||||||
|
return url.split('/').map(v => v.trim()).filter(v => v !== '');
|
||||||
|
}
|
||||||
|
function sanitizeUrl(url: string, forceAbsolute = true) {
|
||||||
|
url = url.trim();
|
||||||
|
if (forceAbsolute || url.startsWith('/')) {
|
||||||
|
return '/' + splitUrl(url).join('/');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return '/' + splitUrl(url).join('/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FetchRequest = Request;
|
||||||
|
|
||||||
|
export default class RestRequest {
|
||||||
|
public readonly body: unknown;
|
||||||
|
public readonly method: string;
|
||||||
|
public readonly url: string;
|
||||||
|
public readonly pathParams: ParamDict;
|
||||||
|
public readonly queryParams: ParamDict;
|
||||||
|
public readonly headers: Headers;
|
||||||
|
|
||||||
|
public get params() {
|
||||||
|
return { ...this.queryParams, ...this.pathParams };
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
body: unknown,
|
||||||
|
headers: Headers,
|
||||||
|
method: string,
|
||||||
|
url: string,
|
||||||
|
pathParams: ParamDict = {},
|
||||||
|
queryParams: ParamDict = {}
|
||||||
|
) {
|
||||||
|
this.body = body;
|
||||||
|
this.headers = headers;
|
||||||
|
this.pathParams = { ...pathParams };
|
||||||
|
this.queryParams = { ...queryParams };
|
||||||
|
this.method = method.toLowerCase();
|
||||||
|
this.url = sanitizeUrl(url);
|
||||||
|
|
||||||
|
if (this.url.includes('?')) {
|
||||||
|
const questionIndex = this.url.indexOf('?');
|
||||||
|
this.url = this.url.substring(0, questionIndex);
|
||||||
|
const params = this.url
|
||||||
|
.substring(questionIndex + 1)
|
||||||
|
.split('&')
|
||||||
|
.map(v => v.trim())
|
||||||
|
.filter(v => v !== '');
|
||||||
|
|
||||||
|
for (const rawParam of params) {
|
||||||
|
const i = rawParam.indexOf('=');
|
||||||
|
if (i < 0) continue;
|
||||||
|
|
||||||
|
const name = rawParam.substring(0, i);
|
||||||
|
const val = rawParam.substring(i + 1);
|
||||||
|
|
||||||
|
if (name === '') continue;
|
||||||
|
this.queryParams[name] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public match(predicate: string) {
|
||||||
|
const urlSegments = splitUrl(this.url);
|
||||||
|
const predSegments = splitUrl(predicate);
|
||||||
|
const wildcardIndex = predSegments.indexOf('*');
|
||||||
|
const hasWildcard = wildcardIndex >= 0;
|
||||||
|
const pathParams: ParamDict = { ...this.pathParams };
|
||||||
|
|
||||||
|
if (wildcardIndex >= 0) {
|
||||||
|
if (predSegments.includes('*', wildcardIndex + 1)) throw new Error("A path predicate may not have more than one wildcard.");
|
||||||
|
if (predSegments.splice(wildcardIndex).length > 1) throw new Error("A path predicate must be the last segment.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const predSeg of predSegments) {
|
||||||
|
const urlSeg = urlSegments.shift();
|
||||||
|
if (urlSeg === undefined) return undefined;
|
||||||
|
else if (predSeg.startsWith(':')) {
|
||||||
|
const name = predSeg.substring(1);
|
||||||
|
if (name.length === 0) throw new Error('Invalid path predicate - a segment may not be ":".');
|
||||||
|
pathParams[name] = decodeURI(urlSeg);
|
||||||
|
}
|
||||||
|
else if (predSeg === urlSeg) continue;
|
||||||
|
else return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasWildcard && urlSegments.length > 0) return undefined;
|
||||||
|
|
||||||
|
return new RestRequest(
|
||||||
|
this.body, this.headers, this.method, '/' + urlSegments.join('/'),
|
||||||
|
{ ...this.pathParams, ...pathParams }, this.queryParams
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async fromMessage(msg: Deno.RequestEvent) {
|
||||||
|
const raw = msg.request.body;
|
||||||
|
const headers = {} as Headers;
|
||||||
|
|
||||||
|
for (const entry of msg.request.headers.entries()) {
|
||||||
|
headers[entry[0]] = entry[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts: Uint8Array[] = [];
|
||||||
|
|
||||||
|
if (raw !== null) {
|
||||||
|
for await (const part of raw) {
|
||||||
|
parts.push(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = new URL(msg.request.url);
|
||||||
|
const params = {} as ParamDict;
|
||||||
|
for (const entry of url.searchParams.entries()) params[entry[0]] = entry[1];
|
||||||
|
|
||||||
|
return new RestRequest(new Blob(parts), headers, msg.request.method, url.pathname, {}, params);
|
||||||
|
}
|
||||||
|
}
|
51
src/server/Response.ts
Normal file
51
src/server/Response.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { Headers } from "./Request.ts";
|
||||||
|
|
||||||
|
export const FetchResponse = Response;
|
||||||
|
|
||||||
|
export default class RestResponse {
|
||||||
|
#status = 200;
|
||||||
|
#statusMsg = '';
|
||||||
|
#body?: Blob;
|
||||||
|
|
||||||
|
headers: Headers = {};
|
||||||
|
|
||||||
|
public constructor() { }
|
||||||
|
|
||||||
|
public get statusCode() { return this.#status; }
|
||||||
|
public get statusMessage() { return this.#statusMsg; }
|
||||||
|
public get content() { return this.#body; }
|
||||||
|
|
||||||
|
public header(name: string, val: string | string[]) {
|
||||||
|
this.headers[name] = val;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public status(val: number, message = '') {
|
||||||
|
this.#status = val;
|
||||||
|
this.#statusMsg = message;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public body(val: string | Blob) {
|
||||||
|
if (typeof val === 'string') val = new Blob([val]);
|
||||||
|
this.#body = val;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toFetchResponse(): Response {
|
||||||
|
const headers: string[][] = [];
|
||||||
|
for (const key in this.headers) {
|
||||||
|
const val = this.headers[key];
|
||||||
|
if (typeof val === 'string') {
|
||||||
|
headers.push([key, val]);
|
||||||
|
}
|
||||||
|
else if (val instanceof Array) {
|
||||||
|
headers.push([key, ...val]);
|
||||||
|
}
|
||||||
|
else headers.push([key]);
|
||||||
|
}
|
||||||
|
return new Response(this.#body, {
|
||||||
|
headers: headers,
|
||||||
|
status: this.#status,
|
||||||
|
statusText: this.#statusMsg,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
249
src/server/Router.ts
Normal file
249
src/server/Router.ts
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
// deno-lint-ignore-file no-explicit-any ban-types
|
||||||
|
import { Reflect } from "https://deno.land/x/reflect_metadata@v0.1.12/mod.ts";
|
||||||
|
import { HttpError, RestRequest, RestResponse, serialize } from "./Server.ts";
|
||||||
|
|
||||||
|
export type HandlerRes = Promise<RestResponse | undefined> | RestResponse | undefined;
|
||||||
|
|
||||||
|
export type HttpMethod = '*' | 'GET' | 'POST' | 'CHANGE' | 'DELETE' | 'PUT' | 'UPDATE';
|
||||||
|
export type BodyType = 'raw' | 'json';
|
||||||
|
export type AuthType = 'raw' | 'jwt';
|
||||||
|
|
||||||
|
export type PrimitiveSchema = 'string' | 'number' | 'boolean' | 'object';
|
||||||
|
export type OptionalSchema = `${PrimitiveSchema}?`;
|
||||||
|
export type Schema = 'any' | PrimitiveSchema | OptionalSchema | Schema[] | ({ $optional?: boolean; } & { [key: string]: Schema });
|
||||||
|
|
||||||
|
export interface Handler {
|
||||||
|
handle(req: RestRequest): HandlerRes;
|
||||||
|
}
|
||||||
|
export interface RestOptions {
|
||||||
|
route?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProcessFunc = (req: RestRequest, arg: unknown, name: string) => Promise<unknown> | unknown;
|
||||||
|
type Base<KeyT extends string> = Router & { [X in KeyT]: Function; };
|
||||||
|
|
||||||
|
interface RouterHandler {
|
||||||
|
path: string;
|
||||||
|
handler: Handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeParameterModifier(func: ProcessFunc) {
|
||||||
|
return (target: Router, key: string, index: number) => {
|
||||||
|
let res = Reflect.getOwnMetadata('router:params', target, key);
|
||||||
|
|
||||||
|
if (res === undefined) Reflect.defineMetadata('router:params', res = [], target, key);
|
||||||
|
(res[index] ??= []).push(func);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function schema(desc: Schema) {
|
||||||
|
function stringify(desc: Schema): string {
|
||||||
|
if (typeof desc === 'string') return desc;
|
||||||
|
if (desc instanceof Array) return desc.map(stringify).join(' | ');
|
||||||
|
|
||||||
|
let res = '{ ';
|
||||||
|
|
||||||
|
for (const key in desc) {
|
||||||
|
if (key === '$optional') continue;
|
||||||
|
if (res != '{ ') res += ', ';
|
||||||
|
res += key + ': ';
|
||||||
|
res += stringify(desc[key]);
|
||||||
|
}
|
||||||
|
res += '}';
|
||||||
|
if (desc.$optional) res += '?';
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
function test(path: string[], val: unknown, desc: Schema) {
|
||||||
|
if (desc === 'any') return;
|
||||||
|
if (typeof desc === 'string') {
|
||||||
|
let type: string = desc;
|
||||||
|
const opt = desc.endsWith('?');
|
||||||
|
|
||||||
|
if (opt) type = type.substring(0, desc.length - 1);
|
||||||
|
if (opt && val === undefined) return;
|
||||||
|
|
||||||
|
if (typeof val as any !== type) throw new HttpError(`${path.join('.')}: Expected a ${type}, got ${typeof val} instead.`);
|
||||||
|
}
|
||||||
|
else if (desc instanceof Array) {
|
||||||
|
for (const type of desc) {
|
||||||
|
try {
|
||||||
|
test(path, val, type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch { /**/ }
|
||||||
|
}
|
||||||
|
throw new HttpError(`${path.join('.')}: Expected a ${stringify(desc)}, got ${typeof val} instead.`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (desc.$optional && val === undefined) return;
|
||||||
|
if (typeof val !== 'object' || val === null) throw new HttpError(`${path.join('.')}: Expected an object, got ${typeof val} instead.`);
|
||||||
|
|
||||||
|
for (const key in desc) {
|
||||||
|
if (key === '$optional') continue;
|
||||||
|
test([ ...path, key ], (val as any)[key], desc[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return makeParameterModifier((_req, val, name) => (test([name], val, desc), val));
|
||||||
|
}
|
||||||
|
export function body(type: BodyType = 'json') {
|
||||||
|
return makeParameterModifier(async req => {
|
||||||
|
let body = req.body;
|
||||||
|
if (type === 'json') {
|
||||||
|
try {
|
||||||
|
if (body instanceof Blob) body = await body.text();
|
||||||
|
if (typeof body === 'string') body = JSON.parse(body.toString());
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
if (e instanceof SyntaxError) throw new HttpError('Body syntax error: ' + e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return body;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export function auth(type: AuthType = 'jwt') {
|
||||||
|
return makeParameterModifier(req => {
|
||||||
|
let res = req.headers.authorization;
|
||||||
|
if (typeof res !== 'string') return undefined;
|
||||||
|
if (res.startsWith('Bearer')) res = res.substring(6).trimStart();
|
||||||
|
|
||||||
|
if (type === 'jwt') throw new Error('JWT is not supported.');
|
||||||
|
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addMetaQuery(target: any, ...handlers: ((r: Router) => RouterHandler)[]) {
|
||||||
|
let props = Reflect.getOwnMetadata('router:queries', target);
|
||||||
|
if (props === undefined) Reflect.defineMetadata('router:queries', props = [], target);
|
||||||
|
|
||||||
|
props.push(...handlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function rest<KeyT extends keyof T & string, T extends Base<KeyT>>(method: HttpMethod, route?: string) {
|
||||||
|
return (target: T, key: KeyT) => {
|
||||||
|
const path = route ?? key;
|
||||||
|
|
||||||
|
addMetaQuery(target, (r: any) => ({
|
||||||
|
path, handler: {
|
||||||
|
async handle(req) {
|
||||||
|
if (method !== '*' && req.method.toUpperCase() !== method) return undefined;
|
||||||
|
|
||||||
|
const params: string[] = [];
|
||||||
|
const func = (r as T)[key];
|
||||||
|
const args: any[] = [];
|
||||||
|
const allMods: ([ProcessFunc, ...ProcessFunc[]] | undefined)[] = [];
|
||||||
|
|
||||||
|
let signature = (target[key] as Function).toString();
|
||||||
|
signature = signature.substring(signature.indexOf('(') + 1, signature.indexOf(')'));
|
||||||
|
params.push(...signature.split(',').map(v => v.trim()).filter(v => v !== ''));
|
||||||
|
|
||||||
|
for (let proto = target; proto instanceof Router; proto = Object.getPrototypeOf(proto)) {
|
||||||
|
const data = Reflect.getOwnMetadata('router:params', proto, key);
|
||||||
|
if (!data) continue;
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
allMods[i] ??= [] as any;
|
||||||
|
if (data[i]) allMods[i]!.push(...data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < params.length; i++) {
|
||||||
|
const param = params[i];
|
||||||
|
|
||||||
|
let arg: any = req.params[param];
|
||||||
|
|
||||||
|
for (const mod of allMods[i] ?? []) {
|
||||||
|
arg = await mod(req, arg, params[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
args.push(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = func.apply(r, args);
|
||||||
|
if (res instanceof RestResponse) return res;
|
||||||
|
return new RestResponse().body(await serialize(res));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function route<KeyT extends keyof T & string, T extends Router & { [x in KeyT]: Handler }>(path?: string) {
|
||||||
|
return (target: T, key: KeyT) => {
|
||||||
|
addMetaQuery(target, (r: any) => ({
|
||||||
|
handler: r[key],
|
||||||
|
path: path ?? key,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Router {
|
||||||
|
private _handlers?: RouterHandler[];
|
||||||
|
public defaultHandler?: Handler;
|
||||||
|
|
||||||
|
private _init() {
|
||||||
|
if (this._handlers === undefined) {
|
||||||
|
this._handlers = [];
|
||||||
|
|
||||||
|
// why the actual fuck not
|
||||||
|
// deno-lint-ignore no-this-alias
|
||||||
|
for (let proto = this; proto instanceof Router; proto = Object.getPrototypeOf(proto)) {
|
||||||
|
const props = Reflect.getOwnMetadata('router:queries', proto);
|
||||||
|
for (const handler of props ?? []) {
|
||||||
|
this._handlers.push(handler(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this._handlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async handle(req: RestRequest) {
|
||||||
|
for (const hnd of this._init()) {
|
||||||
|
const _req = req.match(hnd.path);
|
||||||
|
if (_req) {
|
||||||
|
try {
|
||||||
|
const res = await hnd.handler.handle(_req);
|
||||||
|
if (res) return res;
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
const res = await this.onError(_req, e);
|
||||||
|
if (res instanceof HttpError) return new RestResponse()
|
||||||
|
.body(await serialize(res.body))
|
||||||
|
.status(res.status);
|
||||||
|
else return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.defaultHandler?.handle(req);
|
||||||
|
}
|
||||||
|
public addHandler(path: string, handler: Handler) {
|
||||||
|
this._init().push({ path, handler });
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public onError(_req: RestRequest, error: unknown): Promise<RestResponse | HttpError> | RestResponse | HttpError {
|
||||||
|
if (error instanceof HttpError) return error;
|
||||||
|
else {
|
||||||
|
console.error(error);
|
||||||
|
try {
|
||||||
|
return new HttpError(`Internal error: ${error}\nSee logs for details`, 500);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return new HttpError('Internal error.\nSee logs for details', 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async attach(server: Deno.Listener) {
|
||||||
|
for await (const conn of server) {
|
||||||
|
for await (const req of Deno.serveHttp(conn)) {
|
||||||
|
const r = await this.handle(await RestRequest.fromMessage(req));
|
||||||
|
if (r) req.respondWith(r.toFetchResponse());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
src/server/Server.ts
Normal file
38
src/server/Server.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import Router, { rest, route } from "./Router.ts";
|
||||||
|
import RestRequest from "./Request.ts";
|
||||||
|
import RestResponse from "./Response.ts";
|
||||||
|
|
||||||
|
export { RestRequest, RestResponse, Router, rest, route };
|
||||||
|
|
||||||
|
export class HttpError extends Error {
|
||||||
|
constructor(public readonly body: unknown, public readonly status = 400) {
|
||||||
|
super();
|
||||||
|
serialize(body).then(v => this.message = v.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const undefinedBuff = new Blob([new TextEncoder().encode('undefined')]);
|
||||||
|
const nullBuff = new Blob([new TextEncoder().encode('null')]);
|
||||||
|
|
||||||
|
export async function serialize(val: unknown, depth = 16): Promise<Blob> {
|
||||||
|
while (true) {
|
||||||
|
if (depth <= 0) throw new Error("Call depth exceeded limit.");
|
||||||
|
if (val instanceof Promise) val = await val;
|
||||||
|
else if (val instanceof Function) {
|
||||||
|
if (val.length !== 0) throw new Error('Can\'t serialize an argument-accepting function');
|
||||||
|
val = val();
|
||||||
|
}
|
||||||
|
else break;
|
||||||
|
depth--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val === undefined) return undefinedBuff;
|
||||||
|
if (val === null) return nullBuff;
|
||||||
|
if (val instanceof Blob) return val;
|
||||||
|
|
||||||
|
while (typeof val !== 'string' && val && val.toString !== Object.prototype.toString && val.toString instanceof Function) {
|
||||||
|
val = val.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Blob([new TextEncoder().encode(JSON.stringify(val))]);
|
||||||
|
}
|
11
tsconfig.json
Normal file
11
tsconfig.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"outDir": "./dst",
|
||||||
|
"sourceMap": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"noImplicitAny": false
|
||||||
|
// "module": "ESNext"
|
||||||
|
}
|
||||||
|
}
|
292
yarn.lock
Normal file
292
yarn.lock
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@cspotcode/source-map-support@^0.8.0":
|
||||||
|
version "0.8.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
|
||||||
|
integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/trace-mapping" "0.3.9"
|
||||||
|
|
||||||
|
"@jridgewell/resolve-uri@^3.0.3":
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721"
|
||||||
|
integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==
|
||||||
|
|
||||||
|
"@jridgewell/sourcemap-codec@^1.4.10":
|
||||||
|
version "1.4.15"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
|
||||||
|
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
|
||||||
|
|
||||||
|
"@jridgewell/trace-mapping@0.3.9":
|
||||||
|
version "0.3.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
|
||||||
|
integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/resolve-uri" "^3.0.3"
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
|
|
||||||
|
"@tsconfig/node10@^1.0.7":
|
||||||
|
version "1.0.9"
|
||||||
|
resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz"
|
||||||
|
integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==
|
||||||
|
|
||||||
|
"@tsconfig/node12@^1.0.7":
|
||||||
|
version "1.0.11"
|
||||||
|
resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz"
|
||||||
|
integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
|
||||||
|
|
||||||
|
"@tsconfig/node14@^1.0.0":
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz"
|
||||||
|
integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
|
||||||
|
|
||||||
|
"@tsconfig/node16@^1.0.2":
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz"
|
||||||
|
integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==
|
||||||
|
|
||||||
|
"@types/body-parser@*":
|
||||||
|
version "1.19.2"
|
||||||
|
resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz"
|
||||||
|
integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==
|
||||||
|
dependencies:
|
||||||
|
"@types/connect" "*"
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/connect@*":
|
||||||
|
version "3.4.35"
|
||||||
|
resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz"
|
||||||
|
integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/express-serve-static-core@^4.17.33":
|
||||||
|
version "4.17.35"
|
||||||
|
resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz"
|
||||||
|
integrity sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
"@types/qs" "*"
|
||||||
|
"@types/range-parser" "*"
|
||||||
|
"@types/send" "*"
|
||||||
|
|
||||||
|
"@types/express@^4.17.14":
|
||||||
|
version "4.17.17"
|
||||||
|
resolved "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz"
|
||||||
|
integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/body-parser" "*"
|
||||||
|
"@types/express-serve-static-core" "^4.17.33"
|
||||||
|
"@types/qs" "*"
|
||||||
|
"@types/serve-static" "*"
|
||||||
|
|
||||||
|
"@types/mime@*":
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz"
|
||||||
|
integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==
|
||||||
|
|
||||||
|
"@types/mime@^1":
|
||||||
|
version "1.3.2"
|
||||||
|
resolved "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz"
|
||||||
|
integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
|
||||||
|
|
||||||
|
"@types/node@*", "@types/node@^16.11.10":
|
||||||
|
version "16.18.36"
|
||||||
|
resolved "https://registry.npmjs.org/@types/node/-/node-16.18.36.tgz"
|
||||||
|
integrity sha512-8egDX8dE50XyXWH6C6PRCNkTP106DuUrvdrednFouDSmCi7IOvrqr0frznfZaHifHH/3aq/7a7v9N4wdXMqhBQ==
|
||||||
|
|
||||||
|
"@types/qs@*":
|
||||||
|
version "6.9.7"
|
||||||
|
resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz"
|
||||||
|
integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
|
||||||
|
|
||||||
|
"@types/range-parser@*":
|
||||||
|
version "1.2.4"
|
||||||
|
resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz"
|
||||||
|
integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
|
||||||
|
|
||||||
|
"@types/send@*":
|
||||||
|
version "0.17.1"
|
||||||
|
resolved "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz"
|
||||||
|
integrity sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/mime" "^1"
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/serve-static@*":
|
||||||
|
version "1.15.1"
|
||||||
|
resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz"
|
||||||
|
integrity sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/mime" "*"
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/webidl-conversions@*":
|
||||||
|
version "7.0.0"
|
||||||
|
resolved "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz"
|
||||||
|
integrity sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==
|
||||||
|
|
||||||
|
"@types/whatwg-url@^8.2.1":
|
||||||
|
version "8.2.2"
|
||||||
|
resolved "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz"
|
||||||
|
integrity sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
"@types/webidl-conversions" "*"
|
||||||
|
|
||||||
|
acorn-walk@^8.1.1:
|
||||||
|
version "8.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz"
|
||||||
|
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
|
||||||
|
|
||||||
|
acorn@^8.4.1:
|
||||||
|
version "8.9.0"
|
||||||
|
resolved "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz"
|
||||||
|
integrity sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==
|
||||||
|
|
||||||
|
arg@^4.1.0:
|
||||||
|
version "4.1.3"
|
||||||
|
resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz"
|
||||||
|
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
|
||||||
|
|
||||||
|
bson@^5.3.0:
|
||||||
|
version "5.3.0"
|
||||||
|
resolved "https://registry.npmjs.org/bson/-/bson-5.3.0.tgz"
|
||||||
|
integrity sha512-ukmCZMneMlaC5ebPHXIkP8YJzNl5DC41N5MAIvKDqLggdao342t4McltoJBQfQya/nHBWAcSsYRqlXPoQkTJag==
|
||||||
|
|
||||||
|
create-require@^1.1.0:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz"
|
||||||
|
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
|
||||||
|
|
||||||
|
diff@^4.0.1:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz"
|
||||||
|
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
|
||||||
|
|
||||||
|
ip@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz"
|
||||||
|
integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==
|
||||||
|
|
||||||
|
make-error@^1.1.1:
|
||||||
|
version "1.3.6"
|
||||||
|
resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz"
|
||||||
|
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
|
||||||
|
|
||||||
|
memory-pager@^1.0.2:
|
||||||
|
version "1.5.0"
|
||||||
|
resolved "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz"
|
||||||
|
integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==
|
||||||
|
|
||||||
|
mongodb-connection-string-url@^2.6.0:
|
||||||
|
version "2.6.0"
|
||||||
|
resolved "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz"
|
||||||
|
integrity sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/whatwg-url" "^8.2.1"
|
||||||
|
whatwg-url "^11.0.0"
|
||||||
|
|
||||||
|
mongodb@^5.2.0:
|
||||||
|
version "5.6.0"
|
||||||
|
resolved "https://registry.npmjs.org/mongodb/-/mongodb-5.6.0.tgz"
|
||||||
|
integrity sha512-z8qVs9NfobHJm6uzK56XBZF8XwM9H294iRnB7wNjF0SnY93si5HPziIJn+qqvUR5QOff/4L0gCD6SShdR/GtVQ==
|
||||||
|
dependencies:
|
||||||
|
bson "^5.3.0"
|
||||||
|
mongodb-connection-string-url "^2.6.0"
|
||||||
|
socks "^2.7.1"
|
||||||
|
optionalDependencies:
|
||||||
|
saslprep "^1.0.3"
|
||||||
|
|
||||||
|
punycode@^2.1.1:
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz"
|
||||||
|
integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
|
||||||
|
|
||||||
|
reflect-metadata@^0.1.13:
|
||||||
|
version "0.1.13"
|
||||||
|
resolved "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz"
|
||||||
|
integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==
|
||||||
|
|
||||||
|
saslprep@^1.0.3:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz"
|
||||||
|
integrity sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==
|
||||||
|
dependencies:
|
||||||
|
sparse-bitfield "^3.0.3"
|
||||||
|
|
||||||
|
smart-buffer@^4.2.0:
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz"
|
||||||
|
integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
|
||||||
|
|
||||||
|
socks@^2.7.1:
|
||||||
|
version "2.7.1"
|
||||||
|
resolved "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz"
|
||||||
|
integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==
|
||||||
|
dependencies:
|
||||||
|
ip "^2.0.0"
|
||||||
|
smart-buffer "^4.2.0"
|
||||||
|
|
||||||
|
sparse-bitfield@^3.0.3:
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz"
|
||||||
|
integrity sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==
|
||||||
|
dependencies:
|
||||||
|
memory-pager "^1.0.2"
|
||||||
|
|
||||||
|
tr46@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz"
|
||||||
|
integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==
|
||||||
|
dependencies:
|
||||||
|
punycode "^2.1.1"
|
||||||
|
|
||||||
|
ts-node@^10.9.1:
|
||||||
|
version "10.9.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b"
|
||||||
|
integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==
|
||||||
|
dependencies:
|
||||||
|
"@cspotcode/source-map-support" "^0.8.0"
|
||||||
|
"@tsconfig/node10" "^1.0.7"
|
||||||
|
"@tsconfig/node12" "^1.0.7"
|
||||||
|
"@tsconfig/node14" "^1.0.0"
|
||||||
|
"@tsconfig/node16" "^1.0.2"
|
||||||
|
acorn "^8.4.1"
|
||||||
|
acorn-walk "^8.1.1"
|
||||||
|
arg "^4.1.0"
|
||||||
|
create-require "^1.1.0"
|
||||||
|
diff "^4.0.1"
|
||||||
|
make-error "^1.1.1"
|
||||||
|
v8-compile-cache-lib "^3.0.1"
|
||||||
|
yn "3.1.1"
|
||||||
|
|
||||||
|
typescript@^5.1.3:
|
||||||
|
version "5.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.3.tgz#8d84219244a6b40b6fb2b33cc1c062f715b9e826"
|
||||||
|
integrity sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==
|
||||||
|
|
||||||
|
v8-compile-cache-lib@^3.0.1:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
|
||||||
|
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
|
||||||
|
|
||||||
|
webidl-conversions@^7.0.0:
|
||||||
|
version "7.0.0"
|
||||||
|
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz"
|
||||||
|
integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
|
||||||
|
|
||||||
|
whatwg-url@^11.0.0:
|
||||||
|
version "11.0.0"
|
||||||
|
resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz"
|
||||||
|
integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==
|
||||||
|
dependencies:
|
||||||
|
tr46 "^3.0.0"
|
||||||
|
webidl-conversions "^7.0.0"
|
||||||
|
|
||||||
|
yn@3.1.1:
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz"
|
||||||
|
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
|
Loading…
Reference in New Issue
Block a user