sockress 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +374 -74
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,74 +1,374 @@
1
- # Sockress Server (Core Package)
2
-
3
- Sockress is a socket-first server framework that mirrors Express' API while transparently upgrading requests to WebSockets whenever possible. HTTP-only clients continue to work automatically, so you can ship a single codebase that serves both realtime and REST consumers.
4
-
5
- Created by **Also Coder** · [alsocoder.com](https://alsocoder.com) · [github.com/alsocoders](https://github.com/alsocoders)
6
-
7
- ## Features
8
-
9
- - Express-style routing (pp.use, pp.get, pp.post, etc.)
10
- - Shared middleware pipeline for HTTP and WebSocket transports
11
- - Built-in createUploader() helper (multer-compatible) that works for sockets *and* HTTP
12
- - serveStatic() helper for mounting folders with cache-control headers
13
- - Unified request object with
14
-
15
- ## Installation
16
-
17
- `ash
18
- npm install sockress
19
- `
20
-
21
- ## Quick Start
22
-
23
- ` s
24
- import { sockress } from 'sockress';
25
-
26
- const app = sockress();
27
-
28
- app.use((req, _res, next) => {
29
- console.log([] );
30
- next();
31
- });
32
-
33
- app.get('/ping', (_req, res) => {
34
- res.json({ ok: true, via: 'sockress' });
35
- });
36
-
37
- app.listen(5051, (err, address) => {
38
- if (err) throw err;
39
- console.log(Sockress listening on );
40
- });
41
- `
42
-
43
- ## Uploads + Static Assets
44
-
45
- ` s
46
- import { sockress, createUploader, serveStatic } from 'sockress';
47
- import path from 'path';
48
-
49
- const app = sockress();
50
- const uploads = path.join(process.cwd(), 'uploads');
51
- const uploader = createUploader({ dest: uploads, preserveFilename: true });
52
-
53
- app.use('/uploads', serveStatic(uploads));
54
-
55
- app.post('/avatar', uploader.single('avatar'), (req, res) => {
56
- if (!req.file) return res.status(400).json({ error: 'avatar missing' });
57
- res.json({ path: /uploads/ });
58
- });
59
- `
60
-
61
- ## Working with the Client SDK
62
-
63
- Use [sockress-client](https://www.npmjs.com/package/sockress-client) to consume your API from browsers or Node. It automatically serializes FormData and prefers the socket transport:
64
-
65
- ` s
66
- import { sockressClient } from 'sockress-client';
67
-
68
- const api = sockressClient({ baseUrl: 'http://localhost:5051', preferSocket: true });
69
- const { body } = await api.post('/api/auth/login', { body: { email, password } });
70
- `
71
-
72
- ---
73
-
74
- Need help or want to showcase a project? Reach out via [alsocoder.com](https://alsocoder.com) or [@alsocoders](https://github.com/alsocoders).
1
+ # Sockress Server
2
+
3
+ Sockress is a socket-first Node.js framework that mirrors the Express API while automatically upgrading compatible requests to WebSockets. HTTP clients (Postman, curl, third-party services) continue to work with zero changes, so a single codebase can serve both realtime and REST consumers.
4
+
5
+ **Created by [Also Coder](https://alsocoder.com) · GitHub [@alsocoders](https://github.com/alsocoders)**
6
+
7
+ ---
8
+
9
+ ## Features
10
+
11
+ - Express-style routing (`app.get`, `app.post`, `app.put`, `app.patch`, `app.delete`, `app.head`, `app.options`, `app.all`)
12
+ - Unified middleware pipeline for HTTP and WebSocket transports
13
+ - Automatic CORS handling with configurable origins
14
+ - Cookie parsing and setting via `req.cookies` and `res.cookie()`
15
+ - File uploads via `createUploader()` (multer-compatible) that work on both transports
16
+ - Static file serving via `serveStatic()` helper
17
+ - Request context object (`req.context`) for passing data through middleware
18
+ - Graceful shutdown hooks (automatically closes on `beforeExit`, `SIGINT`, `SIGTERM`)
19
+ - Heartbeat management for long-lived WebSocket connections
20
+
21
+ ---
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install sockress
27
+ ```
28
+
29
+ Sockress supports both ESM and CommonJS:
30
+
31
+ ```ts
32
+ // ESM
33
+ import { sockress, createUploader, serveStatic } from 'sockress';
34
+
35
+ // CommonJS
36
+ const { sockress, createUploader, serveStatic } = require('sockress');
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Quick Start
42
+
43
+ ```ts
44
+ import { sockress } from 'sockress';
45
+
46
+ const app = sockress();
47
+
48
+ app.use((req, res, next) => {
49
+ console.log(`[${req.method}] ${req.path}`);
50
+ next();
51
+ });
52
+
53
+ app.get('/ping', (req, res) => {
54
+ res.json({ ok: true, via: 'sockress' });
55
+ });
56
+
57
+ app.listen(5051, (err, address) => {
58
+ if (err) throw err;
59
+ console.log(`Sockress listening on ${address?.url}`);
60
+ });
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Routing
66
+
67
+ Sockress supports all standard HTTP methods:
68
+
69
+ ```ts
70
+ app.get('/users', (req, res) => {
71
+ res.json({ users: [] });
72
+ });
73
+
74
+ app.post('/users', (req, res) => {
75
+ const { name, email } = req.body;
76
+ res.json({ id: 1, name, email });
77
+ });
78
+
79
+ app.put('/users/:id', (req, res) => {
80
+ const { id } = req.params;
81
+ res.json({ id, updated: true });
82
+ });
83
+
84
+ app.patch('/users/:id', (req, res) => {
85
+ res.json({ patched: true });
86
+ });
87
+
88
+ app.delete('/users/:id', (req, res) => {
89
+ res.status(204).end();
90
+ });
91
+
92
+ app.all('/catch-all', (req, res) => {
93
+ res.json({ method: req.method });
94
+ });
95
+ ```
96
+
97
+ Route parameters are available via `req.params`:
98
+
99
+ ```ts
100
+ app.get('/users/:userId/posts/:postId', (req, res) => {
101
+ const { userId, postId } = req.params;
102
+ res.json({ userId, postId });
103
+ });
104
+ ```
105
+
106
+ ---
107
+
108
+ ## Middleware
109
+
110
+ Middleware works the same way as Express. Use `app.use()` to register global or path-scoped middleware:
111
+
112
+ ```ts
113
+ // Global middleware
114
+ app.use((req, res, next) => {
115
+ console.log(`${req.method} ${req.path}`);
116
+ next();
117
+ });
118
+
119
+ // Path-scoped middleware
120
+ app.use('/api', (req, res, next) => {
121
+ req.context.apiVersion = 'v1';
122
+ next();
123
+ });
124
+
125
+ // Multiple middleware
126
+ app.use('/secure', authMiddleware, validateMiddleware, handler);
127
+ ```
128
+
129
+ ### Error Handling
130
+
131
+ Error handlers have 4 parameters `(err, req, res, next)`:
132
+
133
+ ```ts
134
+ app.use((err, req, res, next) => {
135
+ console.error(err);
136
+ res.status(500).json({ error: err.message });
137
+ });
138
+ ```
139
+
140
+ ---
141
+
142
+ ## File Uploads
143
+
144
+ Use `createUploader()` to handle file uploads. It works for both HTTP and WebSocket transports:
145
+
146
+ ```ts
147
+ import { sockress, createUploader } from 'sockress';
148
+ import path from 'path';
149
+
150
+ const app = sockress();
151
+ const uploadsDir = path.join(process.cwd(), 'uploads');
152
+
153
+ const uploader = createUploader({
154
+ dest: uploadsDir,
155
+ preserveFilename: true,
156
+ limits: { fileSize: 2 * 1024 * 1024 } // 2MB
157
+ });
158
+
159
+ // Single file upload
160
+ app.post('/avatar', uploader.single('avatar'), (req, res) => {
161
+ if (!req.file) {
162
+ return res.status(400).json({ error: 'avatar missing' });
163
+ }
164
+ res.json({ path: req.file.path, name: req.file.name });
165
+ });
166
+
167
+ // Multiple files
168
+ app.post('/gallery', uploader.array('images', 5), (req, res) => {
169
+ if (!req.files || req.files.length === 0) {
170
+ return res.status(400).json({ error: 'images missing' });
171
+ }
172
+ res.json({ count: req.files.length });
173
+ });
174
+
175
+ // Multiple fields
176
+ app.post('/documents', uploader.fields([
177
+ { name: 'avatar', maxCount: 1 },
178
+ { name: 'documents', maxCount: 10 }
179
+ ]), (req, res) => {
180
+ const avatar = req.files?.avatar?.[0];
181
+ const documents = req.files?.documents || [];
182
+ res.json({ avatar, documents: documents.length });
183
+ });
184
+
185
+ // Any files
186
+ app.post('/upload', uploader.any(), (req, res) => {
187
+ const files = req.files || {};
188
+ res.json({ files: Object.keys(files) });
189
+ });
190
+ ```
191
+
192
+ Uploaded files are available via:
193
+ - `req.file` - first uploaded file (when using `single()`)
194
+ - `req.files` - object mapping field names to arrays of files
195
+
196
+ Each file has:
197
+ - `fieldName` - form field name
198
+ - `name` - original filename
199
+ - `type` - MIME type
200
+ - `size` - file size in bytes
201
+ - `buffer` - file contents as Buffer
202
+ - `path` - file path on disk (if `dest` was configured)
203
+ - `lastModified` - file last modified timestamp (if available)
204
+
205
+ ---
206
+
207
+ ## Static File Serving
208
+
209
+ Use `serveStatic()` to serve static files:
210
+
211
+ ```ts
212
+ import { sockress, serveStatic } from 'sockress';
213
+ import path from 'path';
214
+
215
+ const app = sockress();
216
+ const uploadsDir = path.join(process.cwd(), 'uploads');
217
+
218
+ app.use('/uploads', serveStatic(uploadsDir, {
219
+ stripPrefix: '/uploads',
220
+ maxAge: 60_000, // cache for 60 seconds
221
+ index: 'index.html' // default file for directories
222
+ }));
223
+ ```
224
+
225
+ Options:
226
+ - `stripPrefix` - remove this prefix from the request path before resolving files
227
+ - `maxAge` - cache control max age in milliseconds
228
+ - `index` - default file to serve for directories (default: `'index.html'`)
229
+
230
+ You can also use the convenience method:
231
+
232
+ ```ts
233
+ app.useStatic('/uploads', uploadsDir, { maxAge: 60_000 });
234
+ ```
235
+
236
+ ---
237
+
238
+ ## Request Object
239
+
240
+ The `req` object provides:
241
+
242
+ ```ts
243
+ req.id // unique request ID
244
+ req.method // HTTP method (GET, POST, etc.)
245
+ req.path // request path
246
+ req.query // parsed query string (object)
247
+ req.params // route parameters (object)
248
+ req.headers // request headers (object)
249
+ req.body // parsed request body
250
+ req.cookies // parsed cookies (object)
251
+ req.file // first uploaded file (if any)
252
+ req.files // all uploaded files (object mapping field names to arrays)
253
+ req.type // 'http' or 'socket'
254
+ req.ip // client IP address
255
+ req.protocol // 'http', 'https', 'ws', or 'wss'
256
+ req.secure // boolean indicating if connection is secure
257
+ req.context // plain object for passing data through middleware
258
+ req.raw // IncomingMessage (HTTP only)
259
+ req.get(field) // get header value by name
260
+ ```
261
+
262
+ ---
263
+
264
+ ## Response Object
265
+
266
+ The `res` object provides:
267
+
268
+ ```ts
269
+ res.status(code) // set status code
270
+ res.set(field, value) // set header
271
+ res.append(field, value) // append to header
272
+ res.cookie(name, value, opts) // set cookie
273
+ res.clearCookie(name, opts) // clear cookie
274
+ res.json(payload) // send JSON response
275
+ res.send(payload) // send response (auto-detects type)
276
+ res.end() // end response
277
+ res.isSent() // check if response was sent
278
+ res.raw // ServerResponse (HTTP only)
279
+ ```
280
+
281
+ ---
282
+
283
+ ## Configuration
284
+
285
+ Create a Sockress app with custom options:
286
+
287
+ ```ts
288
+ const app = sockress({
289
+ cors: {
290
+ origin: ['http://localhost:3000', 'https://example.com'],
291
+ credentials: true,
292
+ methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
293
+ allowedHeaders: ['Content-Type', 'Authorization'],
294
+ exposedHeaders: ['X-Custom-Header'],
295
+ maxAge: 600
296
+ },
297
+ socket: {
298
+ path: '/sockress', // WebSocket path (default: '/sockress')
299
+ heartbeatInterval: 30_000, // heartbeat interval in ms (default: 30000)
300
+ idleTimeout: 120_000 // idle timeout in ms (default: 120000)
301
+ },
302
+ bodyLimit: 1_000_000 // max body size in bytes (default: 1000000)
303
+ });
304
+ ```
305
+
306
+ ---
307
+
308
+ ## Listening
309
+
310
+ Start the server with `app.listen()`:
311
+
312
+ ```ts
313
+ // Simple
314
+ app.listen(5051);
315
+
316
+ // With callback
317
+ app.listen(5051, (err, address) => {
318
+ if (err) {
319
+ console.error('Failed to start server:', err);
320
+ return;
321
+ }
322
+ console.log(`Server listening on ${address?.url}`);
323
+ });
324
+
325
+ // With host
326
+ app.listen(5051, '0.0.0.0', (err, address) => {
327
+ if (err) throw err;
328
+ console.log(`Server listening on ${address?.url}`);
329
+ });
330
+ ```
331
+
332
+ The callback receives:
333
+ - `err` - Error if server failed to start, or `null`
334
+ - `address` - Server address info with `hostname` and `url` properties
335
+
336
+ Sockress automatically registers shutdown hooks for `beforeExit`, `SIGINT`, and `SIGTERM` to gracefully close the server.
337
+
338
+ ---
339
+
340
+ ## Manual Shutdown
341
+
342
+ You can manually close the server:
343
+
344
+ ```ts
345
+ const server = app.listen(5051);
346
+ // ... later
347
+ await app.close();
348
+ ```
349
+
350
+ ---
351
+
352
+ ## Companion Client
353
+
354
+ Pair Sockress with [`sockress-client`](https://www.npmjs.com/package/sockress-client) to get automatic socket transports, FormData serialization, and seamless HTTP fallback:
355
+
356
+ ```ts
357
+ import { sockressClient } from 'sockress-client';
358
+
359
+ const api = sockressClient({ baseUrl: 'http://localhost:5051' });
360
+ const response = await api.post('/api/auth/login', {
361
+ body: { email: 'user@example.com', password: 'secret' }
362
+ });
363
+ console.log(response.body.token);
364
+ ```
365
+
366
+ ---
367
+
368
+ ## Links
369
+
370
+ - Website: [https://alsocoder.com](https://alsocoder.com)
371
+ - GitHub: [https://github.com/alsocoders/sockress](https://github.com/alsocoders/sockress)
372
+ - Issues: [https://github.com/alsocoders/sockress/issues](https://github.com/alsocoders/sockress/issues)
373
+
374
+ PRs and feedback welcome!
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sockress",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Socket-first Express-compatible server with optional HTTP fallback, built-in WebSocket transport, uploader, and static helpers.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",