Skip to content

Commit a800a29

Browse files
committed
feat!: single entry point and add examples
- Remove subpath exports from package.json, keeping only the root @panmdaa/server entry - Update all docs and README imports to use the root entry - Restrict tsup to the root entry - Add self-contained, runnable examples grouped by directory
1 parent 74d5b67 commit a800a29

22 files changed

Lines changed: 434 additions & 75 deletions

‎CONTRIBUTING.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Thank you for considering contributing to `@panmdaa/server`.
1212
- `docs/` — architecture, usage, and subsystem documentation.
1313
- `.github/workflows/` — CI and release automation.
1414

15-
Public entry points use subpath exports — `@panmdaa/server/http`, `@panmdaa/server/router`, `@panmdaa/server/ws`, `@panmdaa/server/middleware`, `@panmdaa/server/error` — with the root `@panmdaa/server` re-exporting everything.
15+
The package exposes a single entry point — `@panmdaa/server` — which re-exports everything (`error`, `http`, `middleware`, `router`, `ws`).
1616

1717
## Ways To Contribute
1818

@@ -30,7 +30,7 @@ For larger changes, open an issue first so we can align on scope, API design, an
3030

3131
Changes that should usually be discussed first:
3232

33-
- new public exports or subpath imports
33+
- new public exports
3434
- changes to core internal algorithms or data structures
3535
- changes to public function signatures or return types
3636
- adding runtime dependencies

‎README.md‎

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,9 @@ npm install @panmdaa/server
3232
## Quick look
3333

3434
```ts
35-
import { Server } from "@panmdaa/server/http";
36-
import { Router } from "@panmdaa/server/router";
37-
import { cors, securityHeaders } from "@panmdaa/server/middleware";
38-
import { NotFound } from "@panmdaa/server/error";
35+
import {
36+
Server, Router, cors, securityHeaders, NotFound,
37+
} from "@panmdaa/server";
3938

4039
// Compose routers, mount them on a server
4140
const users = new Router();
@@ -68,14 +67,14 @@ server.ws("/live", ({ socket }) => {
6867
server.listen(3000);
6968
```
7069

71-
> **Modular imports**: each domain has its own subpath — `@panmdaa/server/http`, `@panmdaa/server/router`, `@panmdaa/server/middleware`, `@panmdaa/server/error`, `@panmdaa/server/ws` — and the root `@panmdaa/server` re-exports everything. See [Development](docs/development/tooling.md).
70+
> **Single entry point**: everything — `Server`, `Router`, middleware, errors, WebSocket helpers — is exported from `@panmdaa/server`. See [Development](docs/development/tooling.md).
7271
7372
## Routing
7473

7574
Verbs, params, wildcards and composition — all type-checked at compile time:
7675

7776
```ts
78-
import { Server } from "@panmdaa/server/http";
77+
import { Server } from "@panmdaa/server";
7978

8079
const server = new Server();
8180

@@ -117,8 +116,7 @@ Optional segments are expanded into concrete routes at registration; matching ne
117116
Middleware is compiled into a single function per route — conditional `await`, shared `next`, short-circuit. It's baked into each route **at registration time**:
118117

119118
```ts
120-
import { Server } from "@panmdaa/server/http";
121-
import { cors, securityHeaders } from "@panmdaa/server/middleware";
119+
import { Server, cors, securityHeaders } from "@panmdaa/server";
122120

123121
const server = new Server();
124122

@@ -146,8 +144,7 @@ Two contracts make the chain work (see [handler compilation](docs/http/handler-c
146144
Path-only matching in a separate tree — `server.ws("/*")` never shadows HTTP routes. Zero-alloc parsing, zero-copy writes, `cork`'d coalescing:
147145

148146
```ts
149-
import { Server } from "@panmdaa/server/http";
150-
import { createWebSocketHeartbeat, CloseCode } from "@panmdaa/server/ws";
147+
import { Server, createWebSocketHeartbeat, CloseCode } from "@panmdaa/server";
151148

152149
const heartbeat = createWebSocketHeartbeat({ intervalMs: 30_000, timeoutMs: 10_000 });
153150

@@ -178,7 +175,7 @@ One code path for HTTP/1, HTTP/2 and WebSocket-over-HTTP/2:
178175

179176
```ts
180177
import { readFileSync } from "node:fs";
181-
import { Server } from "@panmdaa/server/http";
178+
import { Server } from "@panmdaa/server";
182179

183180
const server = new Server({
184181
http2: true, // h2c, or with tls → ALPN
@@ -198,7 +195,7 @@ Throw typed errors, get mapped responses — unknown errors become 500 automatic
198195
import {
199196
HttpError, NotFound, BadRequest,
200197
MethodNotAllowed, PayloadTooLarge, UnsupportedMediaType,
201-
} from "@panmdaa/server/error";
198+
} from "@panmdaa/server";
202199

203200
server.get("/user/:id", ({ params }) => {
204201
throw new NotFound();
@@ -230,7 +227,7 @@ server.post("/api/echo", async ({ body, response }) => {
230227

231228
## API
232229

233-
> **Modular imports**: `Server`, contexts and handler compilation live in `@panmdaa/server/http`; `Router`/`RadixTree` in `@panmdaa/server/router`; the WebSocket stack in `@panmdaa/server/ws`; errors in `@panmdaa/server/error`; middleware in `@panmdaa/server/middleware`. The root `@panmdaa/server` re-exports everything.
230+
> **Single entry point**: everything — `Server`, contexts, handler compilation, `Router`/`RadixTree`, the WebSocket stack, errors, middleware — is exported from `@panmdaa/server`.
234231
235232
| Member | Description |
236233
|--------|-------------|

‎docs/architecture/overview.md‎

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -79,21 +79,14 @@ Both engines follow the same allocation discipline:
7979
- No logging, no cluster, no static file server (files are streamed with `pipeline`, but there is no middleware doing directory listing).
8080
- No room/channel management for WebSockets — that is app-level.
8181

82-
## Public entry points
82+
## Public entry point
8383

84-
The package exposes **modular subpath imports** (`package.json` `exports`): each domain is importable on its own, and the root barrel re-exports everything.
85-
86-
| Import | Surface |
87-
|--------|---------|
88-
| `@panmdaa/server` | everything (root barrel, `src/index.ts`) |
89-
| `@panmdaa/server/error` | `HttpError`, `isHttpError`, status classes |
90-
| `@panmdaa/server/router` | `Router`, `RadixTree`, `Path` types |
91-
| `@panmdaa/server/http` | `Server`, contexts, `compileHandler`, `ResponseContext` |
92-
| `@panmdaa/server/ws` | `WebSocketConnection`, handshake, frame, compression, heartbeat |
93-
| `@panmdaa/server/middleware` | `cors`, `securityHeaders` |
84+
The package exposes a **single entry point** (`package.json` `exports`): `@panmdaa/server`, a root barrel (`src/index.ts`) re-exporting every domain.
9485

9586
```ts
96-
import { Server } from "@panmdaa/server/http";
97-
import { cors } from "@panmdaa/server/middleware";
98-
import { NotFound } from "@panmdaa/server/error";
87+
import {
88+
Server, Router, cors, NotFound,
89+
} from "@panmdaa/server";
9990
```
91+
92+
The `error`, `http`, `middleware`, `router` and `ws` barrels are internal and reachable only through the root export.

‎docs/development/tooling.md‎

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,19 @@ Run it via `npm run build` (or `node scripts/run-all.mjs`).
2525

2626
## Build
2727

28-
- `tsup.config.ts`: entry `src/**/*.ts`, `esnext`, **ESM only**, `minify: true`, `bundle: true`, `treeshake: true`, `clean: true`. tsup preserves the `src/` directory structure in `dist/`, which is what the subpath exports rely on.
28+
- `tsup.config.ts`: entry `src/**/*.ts`, `esnext`, **ESM only**, `minify: true`, `bundle: true`, `treeshake: true`, `clean: true`. tsup preserves the `src/` directory structure in `dist/`.
2929
- `tsconfig.build.json`: `emitDeclarationOnly`, `outDir: dist`, `rootDir: src` — tsup emits JS, tsc emits `.d.ts` with the same structure.
3030

31-
## Public entry points (modular imports)
31+
## Public entry point
3232

33-
`package.json` `exports` defines the public surface. Each domain is a subpath; the root re-exports everything:
33+
`package.json` `exports` defines the public surface — a **single entry point** that re-exports every domain:
3434

35-
| Subpath | `dist/` target | Surface |
36-
|---------|----------------|---------|
35+
| Export | `dist/` target | Surface |
36+
|--------|----------------|---------|
3737
| `.` | `index.js` / `index.d.ts` | root barrel (`export *` from all sub-barrels) |
38-
| `./error` | `error/index.js` | errors |
39-
| `./http` | `http/index.js` | contexts, handler, server |
40-
| `./middleware` | `middleware/index.js` | cors, securityHeaders |
41-
| `./router` | `router/index.js` | router, radix-tree |
42-
| `./ws` | `ws/index.js` | WebSocket stack |
4338
| `./package.json` | `package.json` | tooling convenience |
4439

45-
Adding a new public module means: (1) create its `src/<name>/index.ts` barrel, (2) add a matching `exports` entry in `package.json`, (3) `export *` it from `src/index.ts` if it should also be in the root barrel.
40+
Adding a new public module means: (1) create its `src/<name>/index.ts` barrel, (2) `export *` it from `src/index.ts`.
4641

4742
> `src/index.ts` is a clean barrel — it must **never** contain executable code (no `new Server()`, no `listen()`), or importing the package would boot a server.
4843

‎docs/usage/README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ For the internals (why things work this way, what runs when), see the [architect
1919
## Key concepts in one minute
2020

2121
```ts
22-
import { Server } from "@panmdaa/server/http";
22+
import { Server } from "@panmdaa/server";
2323

2424
const server = new Server();
2525

@@ -37,4 +37,4 @@ server.listen(3000);
3737
- `ctx.body` (JSON, form, raw, stream) is available only on body-capable routes (`POST/PUT/PATCH/DELETE/QUERY`).
3838
- WebSockets live in their own tree: `server.ws("/path", handler)`.
3939

40-
**Imports are modular**: each domain has its own subpath — `@panmdaa/server/http` (Server, contexts), `@panmdaa/server/router`, `@panmdaa/server/ws`, `@panmdaa/server/middleware`, `@panmdaa/server/error` — and the root `@panmdaa/server` re-exports everything. See [Development](../development/tooling.md).
40+
**One entry point**: everything — `Server`, `Router`, middleware, WebSockets, errors — is exported from `@panmdaa/server`. See [Development](../development/tooling.md).

‎docs/usage/best-practices.md‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,19 @@ How to structure a real app with `@panmdaa/server` the way it was designed to be
66

77
```ts
88
// routes/users.ts
9-
import { Router } from "@panmdaa/server/router";
9+
import { Router } from "@panmdaa/server";
1010
export const users = new Router();
1111
users.get("/", listUsers);
1212
users.get("/:id", getUser);
1313
users.post("/", createUser);
1414

1515
// routes/chat.ts
16-
import { Router } from "@panmdaa/server/router";
16+
import { Router } from "@panmdaa/server";
1717
export const chat = new Router();
1818
chat.ws("/live", chatHandler);
1919

2020
// app.ts
21-
import { Server } from "@panmdaa/server/http";
21+
import { Server } from "@panmdaa/server";
2222
import { users } from "./routes/users";
2323
import { chat } from "./routes/chat";
2424

@@ -66,7 +66,7 @@ server.get("/me", ({ state, response }) => {
6666
## 5. Throw typed errors, don't hand-build 500s
6767

6868
```ts
69-
import { NotFound, BadRequest } from "@panmdaa/server/error";
69+
import { NotFound, BadRequest } from "@panmdaa/server";
7070

7171
server.get("/user/:id", async ({ params, response }) => {
7272
const user = await db.findUser(params.id);
@@ -100,7 +100,7 @@ Don't parse the body in middleware for routes that don't need it — parsing is
100100
## 7. WebSocket: track, broadcast, close
101101

102102
```ts
103-
import { createWebSocketHeartbeat } from "@panmdaa/server/ws";
103+
import { createWebSocketHeartbeat } from "@panmdaa/server";
104104

105105
const clients = new Set();
106106
const heartbeat = createWebSocketHeartbeat();

‎docs/usage/getting-started.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Requires Node.js >= 18 (`package.json` `engines`). Pure ESM — use `import`, no
1313
## Your first server
1414

1515
```ts
16-
import { Server } from "@panmdaa/server/http";
16+
import { Server } from "@panmdaa/server";
1717

1818
const server = new Server();
1919

@@ -76,7 +76,7 @@ WebSocket routes match by **path only** and live in a separate tree — `/*` won
7676
Throw `HttpError` subclasses (or build your own) and the server maps them:
7777

7878
```ts
79-
import { NotFound } from "@panmdaa/server/error";
79+
import { NotFound } from "@panmdaa/server";
8080

8181
server.get("/teapot", () => {
8282
throw new NotFound();

‎docs/usage/http.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ server.get("/old", ({ response }) => {
165165
## Errors
166166

167167
```ts
168-
import { NotFound, BadRequest } from "@panmdaa/server/error";
168+
import { NotFound, BadRequest } from "@panmdaa/server";
169169

170170
server.get("/nope", () => {
171171
throw new NotFound();

‎docs/usage/http2.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
```ts
66
import { readFileSync } from "node:fs";
7-
import { Server } from "@panmdaa/server/http";
7+
import { Server } from "@panmdaa/server";
88

99
const server = new Server({
1010
tls: {

‎docs/usage/websockets.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ const server = new Server({
8686
Dead connections (dropped network, powered-off clients) never send a close frame. Track sockets to close them after a timeout:
8787

8888
```ts
89-
import { createWebSocketHeartbeat } from "@panmdaa/server/ws";
89+
import { createWebSocketHeartbeat } from "@panmdaa/server";
9090

9191
const heartbeat = createWebSocketHeartbeat({ intervalMs: 30_000, timeoutMs: 10_000 });
9292

0 commit comments

Comments
 (0)