Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 0 additions & 47 deletions .eslintrc.js

This file was deleted.

12 changes: 8 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@ jobs:
release:
name: Release
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
pull-requests: write
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v6.0.1
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v1
uses: actions/setup-node@v6.1.0
with:
node-version: 14.17.0
node-version: v24.12.0
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Run tests
Expand All @@ -25,5 +30,4 @@ jobs:
- name: Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: yarn release
6 changes: 3 additions & 3 deletions .github/workflows/test_branch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v6.0.1
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v1
uses: actions/setup-node@v6.1.0
with:
node-version: 14.17.0
node-version: v24.12.0
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Lint code
Expand Down
59 changes: 59 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import jest from 'eslint-plugin-jest';
import importPlugin from 'eslint-plugin-import';
import tsPlugin from '@typescript-eslint/eslint-plugin';

export default [
{
ignores: ['src/examples/**', 'src/bench/**'],
},
...tsPlugin.configs['flat/recommended'],
{
plugins: {
import: importPlugin,
},
settings: importPlugin.configs.typescript.settings,
rules: {
...importPlugin.configs.errors.rules,
...importPlugin.configs.warnings.rules,
...importPlugin.configs.typescript.rules,
},
},
{
plugins: {
jest,
},
rules: {
'@typescript-eslint/no-var-requires': 0,
'@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/no-empty-function': 0,
'@typescript-eslint/explicit-function-return-type': 0,
'@typescript-eslint/explicit-module-boundary-types': 0,
'@typescript-eslint/prefer-namespace-keyword': 0,
'@typescript-eslint/no-non-null-assertion': 0,
'@typescript-eslint/no-use-before-define': 0,
'max-classes-per-file': ['error', 1],
'import/prefer-default-export': 0,
'import/no-dynamic-require': 0,
'import/named': 2,
'import/namespace': 2,
'import/default': 2,
'import/export': 2,
'import/no-unresolved': 0,
'import/order': [
'error',
{
'newlines-between': 'always',
groups: ['external', 'internal', 'index', 'sibling', 'parent', 'builtin'],
},
],
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: true,
optionalDependencies: false,
peerDependencies: false,
},
],
},
},
];
8 changes: 7 additions & 1 deletion jest.config.js → jest.config.cjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
extensionsToTreatAsEsm: ['.ts'],
transform: {
'^.+\\.ts$': ['ts-jest', { useESM: true, tsconfig: 'tsconfig.json' }],
},
moduleNameMapper: {
'^callsites$': '<rootDir>/src/__tests__/__mocks__/callsites.cjs',
},
collectCoverageFrom: ['<rootDir>/src/**/*.ts'],
coveragePathIgnorePatterns: ['<rootDir>/src/examples'],
testMatch: ['<rootDir>/src/**/**.test.ts'],
Expand Down
81 changes: 46 additions & 35 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,24 @@
"version": "0.6.0",
"description": "Lightweight and fast Node.js web server",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"exports": {
".": {
"types": "./lib/index.d.ts",
"import": "./lib/index.js"
}
},
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib"
],
"type": "module",
"engines": {
"node": ">=22"
},
"scripts": {
"build": "rm -rf ./lib && tsc -p tsconfig.build.json",
"build:dev": "rm -rf ./dev-lib && tsc -p tsconfig.dev.json",
Expand All @@ -33,46 +44,46 @@
},
"license": "MIT",
"dependencies": {
"ajv": "^8.11.0",
"callsites": "^3.1.0",
"cookie": "^0.5.0",
"cookie-signature": "^1.2.0",
"find-my-way": "^5.6.0",
"hyperid": "^2.3.1",
"ajv": "^8.17.1",
"callsites": "^4.2.0",
"cookie": "^1.1.1",
"cookie-signature": "^1.2.2",
"find-my-way": "^9.3.0",
"hyperid": "^3.3.0",
"lodash": "^4.17.21",
"pino": "^7.11.0",
"pino-pretty": "^7.6.1",
"ts-morph": "^14.0.0",
"ws": "^8.6.0"
"pino": "^10.1.0",
"pino-pretty": "^13.1.3",
"ts-morph": "^27.0.2",
"ws": "^8.18.3"
},
"devDependencies": {
"@semantic-release/git": "^10.0.1",
"@semantic-release/github": "^8.0.2",
"@ts-morph/bootstrap": "^0.12.2",
"@types/cookie": "^0.4.1",
"@types/cookie-signature": "^1.0.3",
"@types/jest": "^27.0.3",
"@types/node": "^14.17.0",
"@types/pino": "^7.0.5",
"@types/ws": "^8.2.1",
"@typescript-eslint/eslint-plugin": "^5.5.0",
"@typescript-eslint/parser": "^5.5.0",
"axios": "^0.21.1",
"eslint": "^8.3.0",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-jest": "^25.3.0",
"express": "^4.17.1",
"fastify": "^3.24.1",
"jest": "^27.4.3",
"prettier": "^2.5.1",
"semantic-release": "^18.0.1",
"supertest": "^6.1.3",
"ts-jest": "^27.0.7",
"ts-node-dev": "^1.1.6",
"typescript": "^4.6.4"
"@semantic-release/github": "^12.0.2",
"@ts-morph/bootstrap": "^0.28.1",
"@types/cookie": "^0.6.0",
"@types/cookie-signature": "^1.1.2",
"@types/jest": "^30.0.0",
"@types/node": "^25.0.3",
"@types/pino": "^7.0.4",
"@types/ws": "^8.18.1",
"@typescript-eslint/eslint-plugin": "^8.50.1",
"@typescript-eslint/parser": "^8.50.1",
"axios": "^1.13.2",
"eslint": "^9.39.2",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jest": "^29.11.0",
"express": "^5.2.1",
"fastify": "^5.6.2",
"jest": "^30.2.0",
"prettier": "^3.7.4",
"semantic-release": "^25.0.2",
"supertest": "^7.1.4",
"ts-jest": "^29.4.6",
"ts-node-dev": "^2.0.0",
"typescript": "^5.9.3"
},
"optionalDependencies": {
"bufferutil": "^4.0.3",
"utf-8-validate": "^5.0.5"
"bufferutil": "^4.1.0",
"utf-8-validate": "^6.0.6"
}
}
20 changes: 20 additions & 0 deletions src/__tests__/__mocks__/callsites.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
function mockCallsite() {
return {
getFileName() {
return 'mock';
},
getLineNumber() {
return 1;
},
getColumnNumber() {
return 1;
},
getFunctionName() {
return 'mock';
},
};
}

module.exports = function callsites() {
return [mockCallsite(), mockCallsite(), mockCallsite()];
};
7 changes: 4 additions & 3 deletions src/__tests__/server.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,10 @@ test('Enables cookies attachment in the settings', async () => {
const { headers } = await axios.get(`http://localhost:${app.getServerPort()}/test`);
await app.stop();

expect(headers['set-cookie'][0]).toContain(
'important=cookie; Domain=example.com; Path=/; Expires=',
);
const setCookie = headers['set-cookie'];
const firstCookie = Array.isArray(setCookie) ? setCookie[0] : setCookie;

expect(firstCookie).toContain('important=cookie; Domain=example.com; Path=/; Expires=');
});

test('Sets x-processing-time to milliseconds', async () => {
Expand Down
2 changes: 1 addition & 1 deletion src/examples/bare-http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ app.route.declare({
handler: function multipleDeclarationRoute(flow) {
flow.params.param;
},
methods: ['get', 'post'],
methods: ['post'],
});

const _wait = () => new Promise((resolve) => setTimeout(resolve, 5000));
Expand Down
1 change: 0 additions & 1 deletion src/logger/serializers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import callsites from 'callsites';
import { context } from '../context';

import util from 'util';

import type { IncomingMessage, ServerResponse } from 'http';

export function parseError(e: any, meta: any) {
Expand Down
27 changes: 17 additions & 10 deletions src/middlewares/cookies/cookie-manager.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import cookie from 'cookie';
import { serialize, type ParseOptions, type SerializeOptions } from 'cookie';

import { secretsOperator } from './signer';

import { logMe } from '../../logger';

import type { BareRequest } from '../../request';

export type CookiesManagerOptions = cookie.CookieSerializeOptions & {
export type CookiesManagerOptions = Omit<SerializeOptions, 'expires'> & {
expires?: Date | number;
signed?: boolean;
parseOptions?: cookie.CookieParseOptions;
parseOptions?: ParseOptions;
secret?: string | string[];
};

export class CookiesManager {
signer: null | ReturnType<typeof secretsOperator>;

constructor(private options: CookiesManagerOptions = {}, private flow: BareRequest) {
constructor(
private options: CookiesManagerOptions = {},
private flow: BareRequest,
) {
const secret = this.options.secret || '';
const enableRotation = Array.isArray(secret);
this.signer = typeof secret === 'string' || enableRotation ? secretsOperator(secret) : null;
Expand All @@ -29,15 +32,19 @@ export class CookiesManager {
) {
const localSigner = signer || this.signer;
const opts = options || this.options;
if (opts.expires && Number.isInteger(opts.expires)) {
opts.expires = new Date(opts.expires);
}
const { signed, ...rest } = opts;
const normalizedExpires =
typeof rest.expires === 'number' ? new Date(rest.expires) : rest.expires;
const serializeOptions: SerializeOptions = {
...rest,
expires: normalizedExpires,
};

if (opts.signed && localSigner) {
if (signed && localSigner) {
value = localSigner.sign(value);
}

const serialized = cookie.serialize(name, value, opts);
const serialized = serialize(name, value, serializeOptions);
let setCookie = this.flow.getHeader('Set-Cookie');

if (!setCookie) {
Expand Down
4 changes: 2 additions & 2 deletions src/middlewares/cors/cors.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use strict';

import { IncomingMessage } from 'http';

import type { HttpMethodsUnionUppercase } from '../../utils';
import type { BareRequest } from '../../request';

import type { IncomingMessage } from 'http';

export type CorsOptions = {
origin?: string | RegExp;
methods?: Array<HttpMethodsUnionUppercase>;
Expand Down
Loading