🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

h11

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

h11 - npm Package Compare versions

Comparing version
0.0.2
to
0.0.3
+19
dist/bun.js
// src/providers/bun.ts
var bunProviderFactory = ({ h11 }) => {
return async (req, server) => {
const res = await h11.exec({
req,
providers: {
bun: {
req,
server
}
},
data: {}
});
return res;
};
};
export {
bunProviderFactory
};
// src/radix/node/node.ts
class Node {
segment;
handlers = {};
children = [];
wildParent = false;
parent;
constructor({ segment, parent }) {
this.segment = segment;
this.parent = parent;
}
}
var addNodeToChildren = (parent, node) => {
const children = parent.children;
if (children.length === 0) {
children.push(node);
return;
}
if (node.segment === "**") {
children.push(node);
return;
} else if (node.segment[0] === ":") {
for (let i = children.length - 1;i >= 0; i--) {
const compareNode = children[i];
if (compareNode.segment === "**") {
continue;
}
children.splice(i + 1, 0, node);
return;
}
children.unshift(node);
} else {
for (let i = children.length - 1;i >= 0; i--) {
const compareNode = children[i];
if (compareNode.segment === "**" || compareNode.segment[0] === ":") {
continue;
}
children.splice(i + 1, 0, node);
return;
}
children.unshift(node);
}
};
// src/radix/radix.ts
class Radix {
root = new Node({ segment: "" });
find(path, method = "GET") {
const segments = path.split("/");
const params = {};
let currentNode = this.root;
let lastWild = null;
outer:
for (let i = 0;i < segments.length; i++) {
const segment = segments[i];
if (currentNode.wildParent === true) {
const wild = currentNode.children[currentNode.children.length - 1];
const wildHandlerContainer = wild.handlers[method];
if (wildHandlerContainer) {
lastWild = {
node: wild,
handler: wildHandlerContainer,
params: { ...params, wild: segments.slice(i).join("/") }
};
}
}
if (currentNode.segment[0] === ":") {
params[currentNode.segment.slice(1)] = segment;
}
const isLastSegment = i + 1 === segments.length;
if (isLastSegment) {
break;
}
if (currentNode.children.length === 0) {
return lastWild;
}
for (const child of currentNode.children) {
const nextSegment = segments[i + 1];
if (child.segment === nextSegment) {
currentNode = child;
continue outer;
}
if (child.segment[0] === ":" && nextSegment !== "") {
currentNode = child;
continue outer;
}
}
return lastWild;
}
const handlerContainer = currentNode.handlers[method];
if (!handlerContainer) {
return lastWild;
}
return {
node: currentNode,
params,
handler: handlerContainer
};
}
add(pattern, method = "GET", handler) {
const patternSegments = pattern.slice(1).split("/");
let currentNode = this.root;
outer:
for (let i = 0;i < patternSegments.length; i++) {
const segment = patternSegments[i];
for (const child of currentNode.children) {
if (child.segment === segment) {
currentNode = child;
continue outer;
}
}
const newNode = new Node({ segment, parent: currentNode });
addNodeToChildren(currentNode, newNode);
if (segment === "**") {
currentNode.wildParent = true;
}
currentNode = newNode;
}
currentNode.handlers[method] = handler;
return currentNode;
}
}
// src/h11.ts
var FORCE_STOP = Symbol("FORCE_STOP");
var defaultOnNotFound = (req) => {
console.log(`h9: Not found ${req.url}`);
return new Response("Not Found", {
status: 404,
statusText: "Not Found 404",
headers: {
"Content-Type": "text/plain"
}
});
};
var defaultOnError = ({ req, error }) => {
console.error(`h9: Error ${req.url} - ${error.message}`);
return new Response("Error 500", {
status: 500,
statusText: "System error 500",
headers: {
"Content-Type": "text/plain"
}
});
};
class H11 {
types;
radix = new Radix;
onNotFound = defaultOnNotFound;
onError = defaultOnError;
addRoute(pattern, method, handler) {
const preparedHandler = typeof handler === "function" ? { handler, plugins: [] } : handler;
this.radix.add(pattern, method, preparedHandler);
}
get(pattern, handler) {
this.addRoute(pattern, "GET", handler);
return this;
}
post(pattern, handler) {
this.addRoute(pattern, "POST", handler);
return this;
}
async exec({ req, data, providers }) {
const url = new URL(req.url);
const findResult = this.radix.find(url.pathname, req.method);
if (!findResult) {
return this.onNotFound(req);
}
const props = { req, params: findResult.params, data, providers };
try {
for (const plugin of findResult.handler.plugins) {
const response2 = await plugin(props);
if (response2) {
return response2;
}
}
const response = await findResult.handler.handler(props);
return response;
} catch (error) {
return this.onError({ ...props, error });
}
}
}
export {
H11
};
// src/providers/node.ts
import {Readable} from "node:stream";
var joinPaths = (elem1, elem2) => {
if (elem1.at(-1) === "/") {
if (elem2[0] === "/") {
return `${elem1}${elem2.slice(1)}`;
} else {
return elem1 + elem2;
}
} else {
if (elem2[0] === "/") {
return elem1 + elem2;
} else {
return `${elem1}/${elem2}`;
}
}
};
var getAddress = (server) => {
const address = server.address();
if (typeof address !== "object" || address === null) {
return;
}
const protocol = "http";
const port = `:${address.port}`;
let url;
if (address.family === "IPv6") {
url = `[${address.address}]`;
} else {
url = address.address;
}
return `${protocol}://${url}${port}`;
};
var createReqFromNode = (req, origin) => {
const method = req.method;
const headers = new Headers(req.headers);
const body = method === "GET" || method === "HEAD" ? null : Readable.toWeb(req);
return new Request(joinPaths(origin, req.url), {
method,
headers,
body
});
};
var sendNodeRes = (responseNative, res) => {
const headers = {};
responseNative.headers.forEach((value, key) => headers[key] = value);
res.writeHead(responseNative.status, responseNative.statusText, headers);
if (responseNative.body) {
const resStream = Readable.fromWeb(responseNative.body);
resStream.pipe(res);
} else {
res.end();
}
};
var nodeProviderFactory = ({ h11 }) => async ({
req,
res,
origin
}) => {
const preapredOrigin = req.headers["host"] || origin;
const preparedRes = createReqFromNode(req, preapredOrigin);
const execRes = await h11.exec({
req: preparedRes,
data: {},
providers: {
node: {
req,
res
}
}
});
sendNodeRes(execRes, res);
};
export {
nodeProviderFactory,
getAddress
};
import { Radix } from './radix/radix.ts';
export type Method = 'GET' | 'POST' | 'DELET' | 'PUT' | 'PATCH';
type HandlerResponse = Promise<Response> | Response;
export type ExecProps = {
req: Request;
providers: Record<string, any>;
data: Record<any, any>;
};
export type HandlerPure = (props: Props) => HandlerResponse;
export type HandlerContainer = {
handler: HandlerPure;
plugins: Plugin[];
};
type Props = {
req: Request;
params: Record<string, string>;
data: Record<any, any>;
};
type Handler = HandlerPure | HandlerContainer;
export type Plugin = (props: Props) => HandlerResponse;
type NotFoundHandler = (req: Request) => HandlerResponse;
type ErrorHandler = (props: {
error: Error;
} & Props) => HandlerResponse;
export declare class H11<TExecProps extends ExecProps = ExecProps> {
types: TExecProps;
radix: Radix;
onNotFound: NotFoundHandler;
onError: ErrorHandler;
private addRoute;
get(pattern: string, handler: Handler): this;
post(pattern: string, handler: Handler): this;
exec({ req, data, providers }: TExecProps): Promise<Response>;
}
export {};
import type { Server } from 'bun';
import type { H11 } from '../h11.ts';
export declare const bunProviderFactory: ({ h11 }: {
h11: H11;
}) => (req: Request, server: Server) => Promise<Response>;
import type { IncomingMessage, ServerResponse, Server } from 'node:http';
import type { H11 } from '../h11.ts';
export declare const getAddress: (server: Server) => string | undefined;
export declare const nodeProviderFactory: ({ h11 }: {
h11: H11;
}) => ({ req, res, origin, }: {
req: IncomingMessage;
res: ServerResponse<IncomingMessage>;
origin: string;
}) => Promise<void>;
import type { HandlerContainer, Method } from '../../h11.ts';
export declare class Node {
segment: string;
handlers: Partial<Record<Method, HandlerContainer>>;
children: Node[];
wildParent: boolean;
parent?: Node;
constructor({ segment, parent }: {
segment: string;
parent?: Node;
});
}
export declare const addNodeToChildren: (parent: Node, node: Node) => void;
import type { HandlerContainer, Method } from '../h11.ts';
import { Node } from './node/node.ts';
type Params = Record<string, string>;
export declare class Radix {
root: Node;
find(path: string, method?: Method): {
handler: HandlerContainer;
params: Params;
node: Node;
} | null;
add(pattern: string, method: Method | undefined, handler: HandlerContainer): Node;
}
export {};
+5
-4
{
"name": "h11",
"type": "module",
"version": "0.0.2",
"version": "0.0.3",
"scripts": {
"build": "bun run ./build.ts"
},
"files": [
"./dist/**"
],
"exports": {

@@ -24,5 +27,3 @@ ".": {

"@types/bun": "latest",
"typescript": "^5.0.0"
},
"dependencies": {
"typescript": "^5.0.0",
"mitata": "^0.1.14",

@@ -29,0 +30,0 @@ "utftu": "^1.0.26"

{
"singleQuote": true
}
import { $ } from 'bun';
await $`rm -rf ./dist`;
await Promise.all([
Bun.build({
entrypoints: ['./src/h11.ts'],
outdir: './dist',
}),
$`tsc --project tsconfig.types.json`,
Bun.build({
entrypoints: ['./src/providers/bun.ts'],
outdir: './dist',
}),
$`tsc --project tsconfig.types.bun.json`,
Bun.build({
entrypoints: ['./src/providers/node.ts'],
outdir: './dist',
external: ['*'],
}),
$`tsc --project tsconfig.types.node.json`,
]);

Sorry, the diff of this file is not supported yet

import { H11 } from '../src/h11.ts';
import { bunProviderFactory } from '../src/providers/bun.ts';
const h11 = new H11();
h11.get('/hello/world/sasha', () => {
return new Response(`it is a super path`);
});
h11.get('/hello/world/:name', ({ params }) => {
return new Response(`hello world mister ${params.name}`);
});
h11.get('/hello/**', ({ params }) => {
return new Response(`it is a wild path ${params.wild}`);
});
const bunProvider = bunProviderFactory({ h11 });
// const server = Bun.serve({
// // hostname: 'hello.world',
// unix: '\0benchmark-h11-bun',
// // port: '443',
// async fetch(req, server) {
// console.log('-----', 'req.url', req.url);
// // console.log('-----', 'args', args);
// // req.body.
// const res = await bunProvider(req, server);
// return res;
// },
// });
// console.log('-----', 'server', server.url);
Bun.serve({
unix: '/tmp/my-socket.sock', // abstract namespace socket
fetch(req) {
return new Response(`404!`);
},
});
import { createServer } from 'node:http';
import { H11 } from '../src/h11.ts';
import {
getAddress,
nodeProviderFactory,
} from '../src/providers/nodejs-old.ts';
const h11 = new H11();
h11.get('/hello/world/sasha', () => {
return new Response(`it is a super path`);
});
h11.get('/hello/world/:name', ({ params }) => {
return new Response(`hello world mister ${params.name}`);
});
h11.get('/hello/**', ({ params }) => {
return new Response(`it is a wild path ${params.wild}`);
});
h11.get('/**', ({ req }) => {
return new Response(req.url);
});
const nodeProvider = nodeProviderFactory({ h11 });
const server = createServer((req, res) => {
nodeProvider({
req,
res,
origin: getAddress(server) || 'http://localhost:3000',
});
});
server.listen(3000);
import { Readable } from 'stream';
console.log('-----', 'Readable', Readable);
import { Radix } from './radix/radix.ts';
const FORCE_STOP = Symbol('FORCE_STOP');
export type Method = 'GET' | 'POST' | 'DELET' | 'PUT' | 'PATCH';
type HandlerResponse = Promise<Response> | Response;
export type ExecProps = {
req: Request;
providers: Record<string, any>;
data: Record<any, any>;
};
export type HandlerPure = (props: Props) => HandlerResponse;
export type HandlerContainer = {
handler: HandlerPure;
plugins: Plugin[];
};
type Props = {
req: Request;
params: Record<string, string>;
data: Record<any, any>;
};
type Handler = HandlerPure | HandlerContainer;
export type Plugin = (props: Props) => HandlerResponse;
type NotFoundHandler = (req: Request) => HandlerResponse;
type ErrorHandler = (props: { error: Error } & Props) => HandlerResponse;
const defaultOnNotFound: NotFoundHandler = (req) => {
console.log(`h9: Not found ${req.url}`);
return new Response('Not Found', {
status: 404,
statusText: 'Not Found 404',
headers: {
'Content-Type': 'text/plain',
},
});
};
const defaultOnError: ErrorHandler = ({ req, error }) => {
console.error(`h9: Error ${req.url} - ${error.message}`);
return new Response('Error 500', {
status: 500,
statusText: 'System error 500',
headers: {
'Content-Type': 'text/plain',
},
});
};
export class H11<TExecProps extends ExecProps = ExecProps> {
types!: TExecProps;
radix = new Radix();
onNotFound: NotFoundHandler = defaultOnNotFound;
onError: ErrorHandler = defaultOnError;
private addRoute(pattern: string, method: Method, handler: Handler) {
const preparedHandler =
typeof handler === 'function' ? { handler, plugins: [] } : handler;
this.radix.add(pattern, method, preparedHandler);
}
get(pattern: string, handler: Handler) {
this.addRoute(pattern, 'GET', handler);
return this;
}
post(pattern: string, handler: Handler) {
this.addRoute(pattern, 'POST', handler);
return this;
}
async exec({ req, data, providers }: TExecProps) {
const url = new URL(req.url);
const findResult = this.radix.find(url.pathname, req.method as any);
if (!findResult) {
return this.onNotFound(req);
}
const props = { req, params: findResult.params, data, providers };
try {
for (const plugin of findResult.handler.plugins) {
const response = await plugin(props);
if (response) {
return response;
}
}
const response = await findResult.handler.handler(props);
return response;
} catch (error) {
return this.onError({ ...props, error: error as Error });
}
}
}
import type { Server } from 'bun';
import type { H11 } from '../h11.ts';
export const bunProviderFactory = ({ h11 }: { h11: H11 }) => {
return async (req: Request, server: Server) => {
const res = await h11.exec({
req,
providers: {
bun: {
req,
server,
},
},
data: {},
});
return res;
};
};
import type { IncomingMessage, ServerResponse, Server } from 'node:http';
import { Readable } from 'node:stream';
import type { H11 } from '../h11.ts';
const joinPaths = (elem1: string, elem2: string) => {
if (elem1.at(-1) === '/') {
if (elem2[0] === '/') {
return `${elem1}${elem2.slice(1)}`;
} else {
return elem1 + elem2;
}
} else {
if (elem2[0] === '/') {
return elem1 + elem2;
} else {
return `${elem1}/${elem2}`;
}
}
};
export const getAddress = (server: Server) => {
const address = server.address();
if (typeof address !== 'object' || address === null) {
return;
}
const protocol = 'http';
const port = `:${address.port}`;
let url: string;
if (address.family === 'IPv6') {
url = `[${address.address}]`;
} else {
url = address.address;
}
return `${protocol}://${url}${port}`;
};
const createReqFromNode = (req: IncomingMessage, origin: string) => {
const method = req.method;
const headers = new Headers(req.headers as Record<string, string>);
const body =
method === 'GET' || method === 'HEAD'
? null
: (Readable.toWeb(req) as any as ReadableStream);
// Создаем и возвращаем новый Request объект
return new Request(joinPaths(origin, req.url!), {
method,
headers,
body,
});
};
const sendNodeRes = (
responseNative: Response,
res: ServerResponse<IncomingMessage>
) => {
const headers: Record<string, string> = {};
responseNative.headers.forEach((value, key) => (headers[key] = value));
res.writeHead(responseNative.status, responseNative.statusText, headers);
if (responseNative.body) {
const resStream = Readable.fromWeb(responseNative.body as any);
resStream.pipe(res);
} else {
res.end();
}
};
export const nodeProviderFactory =
({ h11 }: { h11: H11 }) =>
async ({
req,
res,
origin,
}: {
req: IncomingMessage;
res: ServerResponse<IncomingMessage>;
origin: string;
}) => {
const preapredOrigin = req.headers['host'] || origin;
const preparedRes = createReqFromNode(req, preapredOrigin);
const execRes = await h11.exec({
req: preparedRes,
data: {},
providers: {
node: {
req,
res,
},
},
});
sendNodeRes(execRes, res);
};
export type ProviderResponse = {
req: Request;
providers: Record<string, any>;
};
type A = Record<string, any>;
type B = { hello: 'world' };
type C = A & B;
const c: C = null as any as C;
import type { HandlerContainer, Method } from '../../h11.ts';
export class Node {
segment: string;
// data
handlers: Partial<Record<Method, HandlerContainer>> = {};
children: Node[] = [];
wildParent: boolean = false;
parent?: Node;
constructor({ segment, parent }: { segment: string; parent?: Node }) {
this.segment = segment;
this.parent = parent;
}
}
export const addNodeToChildren = (parent: Node, node: Node) => {
const children = parent.children;
if (children.length === 0) {
children.push(node);
return;
}
if (node.segment === '**') {
children.push(node);
return;
} else if (node.segment[0] === ':') {
for (let i = children.length - 1; i >= 0; i--) {
const compareNode = children[i];
if (compareNode.segment === '**') {
continue;
}
children.splice(i + 1, 0, node);
return;
}
children.unshift(node);
} else {
for (let i = children.length - 1; i >= 0; i--) {
const compareNode = children[i];
if (compareNode.segment === '**' || compareNode.segment[0] === ':') {
continue;
}
children.splice(i + 1, 0, node);
return;
}
children.unshift(node);
}
};
import { describe, it, expect } from 'bun:test';
import { Radix } from './radix.ts';
const handlerContainer = {
handler: async () => new Response(),
plugins: [],
};
describe('radix', () => {
it('add', () => {
const radix = new Radix();
const node = radix.add('/hello/:name/world', 'GET', handlerContainer);
// node.handlers.push(handler);
expect(radix.root.children.length).toBe(1);
const helloNode = radix.root.children[0];
expect(helloNode.segment).toBe('hello');
expect(helloNode.children.length).toBe(1);
const nameNode = helloNode.children[0];
expect(nameNode.segment).toBe(':name');
expect(nameNode.children.length).toBe(1);
const worldNode = nameNode.children[0];
expect(worldNode.segment).toBe('world');
expect(worldNode.children.length).toBe(0);
expect(node).toBe(worldNode);
radix.add('/hello/:name/world2', 'GET', handlerContainer);
expect(nameNode.children.length).toBe(2);
const world2Node = nameNode.children[1];
expect(world2Node.segment).toBe('world2');
expect(world2Node.children.length).toBe(0);
});
it('find', () => {
const radix = new Radix();
const worldNode = radix.add('/hello/:name/world', 'GET', handlerContainer);
const findResult = radix.find('/hello/aleksey/world');
expect(findResult).not.toBe(null);
expect(findResult!.node).toBe(worldNode);
expect(findResult!.params.name).toBe('aleksey');
const cityNode = radix.add('/hello2/:name/:city', 'GET', handlerContainer);
const findResult2 = radix.find('/hello2/aleksey/london');
expect(findResult2).not.toBe(null);
expect(findResult2!.node).toBe(cityNode);
expect(findResult2!.params.name).toBe('aleksey');
expect(findResult2!.params.city).toBe('london');
const findResult3 = radix.find('/sdsdsdsdsds');
expect(findResult3).toBe(null);
});
it('find wild', () => {
const radix = new Radix();
radix.add('/hello/world/:name', 'GET', handlerContainer);
radix.add('/hello/**', 'GET', handlerContainer);
let result = radix.find('/hello/world/', 'GET')!;
expect(result.node.segment).toBe('**');
result = radix.find('/hello/world/aleksey', 'GET')!;
expect(result.node.segment[0]).toBe(':');
result = radix.find('/hello', 'GET')!;
expect(result.node.segment).toBe('**');
const resultNull = radix.find('/hello123', 'GET');
expect(resultNull).toBe(null);
});
it('find named and regular', () => {
const radix = new Radix();
radix.add('/hello/world/:name', 'GET', handlerContainer);
radix.add('/hello/world/aleksey', 'GET', handlerContainer);
let result = radix.find('/hello/world/aleksey')!;
expect(result.node.segment).toBe('aleksey');
});
it('find named and wild', () => {
const radix = new Radix();
radix.add('/hello/world/**', 'GET', handlerContainer);
radix.add('/hello/world/:name', 'GET', handlerContainer);
let result = radix.find('/hello/world/aleksey')!;
expect(result.node.segment).toBe(':name');
});
});
import type { HandlerContainer, Method } from '../h11.ts';
import { Node, addNodeToChildren } from './node/node.ts';
type Params = Record<string, string>;
// /hello/world/:name/:family
export class Radix {
root = new Node({ segment: '' });
find(path: string, method: Method = 'GET') {
const segments = path.split('/');
const params: Params = {};
let currentNode = this.root;
let lastWild: {
handler: HandlerContainer;
params: Params;
node: Node;
} | null = null;
outer: for (let i = 0; i < segments.length; i++) {
const segment = segments[i];
if (currentNode.wildParent === true) {
const wild = currentNode.children[currentNode.children.length - 1];
const wildHandlerContainer = wild.handlers[method];
if (wildHandlerContainer) {
lastWild = {
node: wild,
handler: wildHandlerContainer,
params: { ...params, wild: segments.slice(i).join('/') },
};
}
}
if (currentNode.segment[0] === ':') {
params[currentNode.segment.slice(1)] = segment;
}
const isLastSegment = i + 1 === segments.length;
if (isLastSegment) {
break;
}
if (currentNode.children.length === 0) {
// no node children in tree
return lastWild;
}
for (const child of currentNode.children) {
const nextSegment = segments[i + 1];
if (child.segment === nextSegment) {
currentNode = child;
continue outer;
}
if (child.segment[0] === ':' && nextSegment !== '') {
currentNode = child;
continue outer;
}
}
// no children node with target name
return lastWild;
}
const handlerContainer = currentNode.handlers[method];
if (!handlerContainer) {
return lastWild;
}
// handle last segment
return {
node: currentNode,
params,
handler: handlerContainer,
};
}
// hello/world/:name
add(pattern: string, method: Method = 'GET', handler: HandlerContainer) {
const patternSegments = pattern.slice(1).split('/');
let currentNode = this.root;
outer: for (let i = 0; i < patternSegments.length; i++) {
const segment = patternSegments[i];
for (const child of currentNode.children) {
if (child.segment === segment) {
currentNode = child;
continue outer;
}
}
const newNode = new Node({ segment, parent: currentNode });
addNodeToChildren(currentNode, newNode);
if (segment === '**') {
currentNode.wildParent = true;
// setWildToChildren(currentNode, method, );
}
currentNode = newNode;
}
currentNode.handlers[method] = handler;
return currentNode;
}
}
{
"compilerOptions": {
// Enable latest features
"lib": ["ESNext", "DOM"],
"target": "ESNext",
"module": "ESNext",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,
// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
}
}
{
"extends": "./tsconfig.types.json",
"include": ["src/providers/bun.ts"]
}
{
"extends": "./tsconfig.json",
"compilerOptions": {
"emitDeclarationOnly": true,
"outDir": "./dist/types",
"declaration": true,
"noEmit": false
},
"include": ["src/h11.ts"]
}
{
"extends": "./tsconfig.types.json",
"include": ["src/providers/node.ts"]
}