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
39 changes: 39 additions & 0 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Build and Test

on:
push:
branches: [master]
pull_request:
branches: [master]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: "25.x"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Build project
run: npm run build

test:
runs-on: ubuntu-latest
needs: build

steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: "25.x"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm run test
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.DS_Store
coverage
node_modules
yarn-error.log
yarn-error.log
dist
4 changes: 0 additions & 4 deletions .travis.yml

This file was deleted.

20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Midnight [![Build Status](https://travis-ci.org/msavela/midnight.svg)](https://travis-ci.org/msavela/midnight)
# Midnight [![Build and Test](https://github.com/msavela/midnight/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/msavela/midnight)

Midnight is an open source web framework for node.js without external dependencies.

Expand All @@ -17,6 +17,24 @@ app.start();

> yarn add midnight

## Typescript Support

Midnight provides first-class Typescript support with comprehensive type definitions. These types are automatically available when you install the package in a Typescript project.

## Examples

### Typescript Example

A basic example demonstrating how to use Midnight.js with Typescript is available in the `examples/typescript` directory.

To run the Typescript example:

```bash
cd examples/typescript
npm install
npm run start:dev
```

## Documentation

See [full documentation](http://msavela.github.io/midnight).
2 changes: 2 additions & 0 deletions examples/typescript/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
dist/
39 changes: 39 additions & 0 deletions examples/typescript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# TypeScript Example for Midnight.js

This directory contains an example application demonstrating how to use the Midnight.js framework with TypeScript.

## Getting Started

To run this example, ensure you have Node.js and npm (or yarn) installed.

### Build

First, navigate to the example directory:

```bash
cd examples/typescript
```

Then, install the dependencies and build the TypeScript code:

```bash
npm install
npm run build
```

This will compile `app.ts` into `dist/app.js`.

### Run

After building, you can start the application using the `start:dev` script:

```bash
npm run start:dev
```

This command will rebuild the project and then run the compiled `dist/app.js` using Node.js.

The application will typically start on `http://127.0.0.1:8080`. You can test the routes:
- `http://127.00.1:8080/`
- `http://127.0.0.1:8080/users/123`
- `http://127.0.0.1:8080/search?q=typescript`
36 changes: 36 additions & 0 deletions examples/typescript/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import midnight, { Request, Response, Next } from "midnight";

const app = midnight();

// Configure the application
app.configure({
host: "127.0.0.1",
port: 3000,
env: "development"
});

// Custom middleware
app.use((req: Request, res: Response, next: Next) => {
app.log.info("Custom middleware executed!");
next();
});

// Basic route
app.route("/", (req: Request, res: Response) => {
res.send("Hello from TypeScript example!");
});

// Route with parameters
app.route("/users/:id", (req: Request, res: Response) => {
res.send(`User ID: ${req.params.id}`);
});

// Route with query parameters (uses built-in query middleware)
app.route("/search", (req: Request, res: Response) => {
res.send(`Search query: ${req.query.q}`);
});

// Start the server
app.start(app.config);

app.log.info(`TypeScript example server running on http://${app.config.host}:${app.config.port}`);
47 changes: 47 additions & 0 deletions examples/typescript/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions examples/typescript/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "typescript-example",
"version": "1.0.0",
"description": "TypeScript example for midnight",
"main": "app.js",
"scripts": {
"start": "node app.js",
"build": "tsc",
"start:dev": "npm run build && node dist/app.js"
},
"dependencies": {
"midnight": "file:../.."
},
"devDependencies": {
"typescript": "^5.9.3"
}
}
20 changes: 20 additions & 0 deletions examples/typescript/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist",
"declaration": true,
"baseUrl": ".",
"paths": {
"midnight": ["../../"]
},
"moduleResolution": "node"
},
"include": [
"app.ts"
],
"types": ["../../index"]
}
125 changes: 125 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Type definitions for the 'midnight' package

import { IncomingMessage, ServerResponse } from 'http';

// --- Request and Response Interfaces ---
interface MidnightRequest extends IncomingMessage {
get(key: string): string | undefined;
content(): string | undefined;
params: { [key: string]: string | undefined }; // Parameters can be undefined
query: { [key: string]: string | string[] | undefined }; // Query params can be string or string[]
app: MidnightApp;
originalUrl: string;
}

interface MidnightResponse extends ServerResponse {
app: MidnightApp;
status(code: number): this;
set(field: string, value: string): this;
set(fields: Record<string, string>): this;
content(type: string): this;
content(): string | undefined;
get(field: string): string | undefined;
redirect(url: string): this;
encoding(type: string): this;
render(view: string, locals?: object, options?: object): void;
send(body: any): void;
}

type MidnightNext = (err?: any) => void;
type MidnightMiddleware = (req: MidnightRequest, res: MidnightResponse, next: MidnightNext) => void;

// --- Config Interface ---
interface MidnightConfig {
host: string;
port: number;
views: string;
root: string;
env: string;
version: string;
}

// --- Logger Interface ---
interface MidnightLogger {
trace: (...args: any[]) => void;
debug: (...args: any[]) => void;
info: (...args: any[]) => void;
warn: (...args: any[]) => void;
error: (...args: any[]) => void;
fatal: (...args: any[]) => void;
level: (level: number) => void;
}

// --- Route Interface ---
interface MidnightRoute {
app: MidnightApp;
pattern: string;
handler: (req: MidnightRequest, res: MidnightResponse) => void;
methods: string[];
stack: MidnightMiddleware[];
children: boolean;
re: RegExp;
keys: string[];
method(method: string | string[]): MidnightRoute;
get(): MidnightRoute;
post(): MidnightRoute;
put(): MidnightRoute;
delete(): MidnightRoute;
use(fn: MidnightMiddleware): MidnightRoute;
route(pattern: string, handler: (req: MidnightRequest, res: MidnightResponse) => void): MidnightRoute;
match(path: string, method: string): { route: MidnightRoute, params: { [key: string]: string }, splats: string[] } | undefined;
}

// --- Plugin Interface ---
interface MidnightPlugin {
name: string;
attach: (app: MidnightApp, options: object) => void;
init: (app: MidnightApp, next: () => void) => void;
}

// --- App Interface ---
interface MidnightApp {
stack: MidnightMiddleware[];
routes: MidnightRoute[];
plugins: MidnightPlugin[];
cache: object;
config: Partial<MidnightConfig>;
globals: { title: string };
log: MidnightLogger;
configure(object: Partial<MidnightConfig>): MidnightApp;
start(config: Partial<MidnightConfig>): MidnightApp;
route(pattern: string, handler: (req: MidnightRequest, res: MidnightResponse) => void): MidnightRoute;
utils: {
merge: (obj1: object, obj2: object) => object;
path: { normalize: (pattern: string, keys: string[]) => { re: RegExp, keys: string[] } };
};
use(fn: MidnightMiddleware): MidnightApp;
plugin(plugin: MidnightPlugin, options: object): MidnightApp;
}

// Declare the main factory function for the 'midnight' package
declare function createMidnightApp(): MidnightApp;

// Augment the createMidnightApp function's namespace with named types
declare namespace createMidnightApp {
export type Request = MidnightRequest;
export type Response = MidnightResponse;
export type Next = MidnightNext;
export type Middleware = MidnightMiddleware;
export type App = MidnightApp;
export type Logger = MidnightLogger;
export type Route = MidnightRoute;
export type Plugin = MidnightPlugin;
}

// This makes createMidnightApp the CommonJS default export
export = createMidnightApp;

// Also augment the global NodeJS namespace for IncomingMessage and ServerResponse
// if midnight extends them directly (which it does in index.js app.use)
declare global {
namespace NodeJS {
interface IncomingMessage extends Partial<MidnightRequest> {}
interface ServerResponse extends Partial<MidnightResponse> {}
}
}
Loading