instaserve 1.1.4 → 1.1.6

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.
package/README.md CHANGED
@@ -107,6 +107,39 @@ export default {
107
107
  }
108
108
  ```
109
109
 
110
+ ### Method-Specific Routes
111
+
112
+ Routes can be defined with HTTP method prefixes to handle different methods on the same path. Supported methods: `GET`, `POST`, `PUT`, `DELETE`.
113
+
114
+ ```javascript
115
+ export default {
116
+ // Method-specific routes
117
+ 'POST /users': (req, res, data) => {
118
+ return { message: 'Create user', data }
119
+ },
120
+
121
+ 'GET /users': (req, res, data) => {
122
+ return { message: 'Get users' }
123
+ },
124
+
125
+ 'PUT /users': (req, res, data) => {
126
+ return { message: 'Update user', data }
127
+ },
128
+
129
+ 'DELETE /users': (req, res, data) => {
130
+ return { message: 'Delete user', data }
131
+ },
132
+
133
+ // Path-only routes still work (backward compatible)
134
+ // These match any HTTP method
135
+ hello: (req, res, data) => {
136
+ return { message: 'Hello World' }
137
+ }
138
+ }
139
+ ```
140
+
141
+ Method-specific routes take precedence over path-only routes. If no method-specific route matches, the server falls back to path-only route matching.
142
+
110
143
  ### Special Routes (Middleware)
111
144
 
112
145
  Routes starting with `_` are middleware functions that run on **every request** before the main route handler. They are useful for:
@@ -152,6 +185,25 @@ Each route function receives:
152
185
  - URL query parameters
153
186
  - Form data
154
187
 
188
+ ### Returning Status Codes
189
+
190
+ Routes can return a 3-digit number (100-999) to set the HTTP status code with an empty response body:
191
+
192
+ ```javascript
193
+ export default {
194
+ 'GET /notfound': () => 404,
195
+ 'GET /unauthorized': () => 401,
196
+ 'GET /forbidden': () => 403,
197
+ 'GET /teapot': () => 418, // I'm a teapot
198
+ 'GET /created': () => 201
199
+ }
200
+ ```
201
+
202
+ Routes can also return:
203
+ - **Strings** - Sent as plain text response
204
+ - **Objects** - Automatically serialized as JSON
205
+ - **Status codes** - 3-digit numbers (100-999) set HTTP status with empty body
206
+
155
207
  ### Example Routes File
156
208
 
157
209
  ```javascript
@@ -163,7 +215,19 @@ export default {
163
215
  return false // Continue to next route
164
216
  },
165
217
 
166
- // API endpoint
218
+ // Method-specific routes
219
+ 'POST /api/users': (req, res, data) => {
220
+ return { status: 'created', data }
221
+ },
222
+
223
+ 'GET /api/users': (req, res, data) => {
224
+ return { status: 'ok', users: [] }
225
+ },
226
+
227
+ 'GET /api/notfound': () => 404,
228
+ 'GET /api/unauthorized': () => 401,
229
+
230
+ // Path-only route (matches any method)
167
231
  api: (req, res, data) => {
168
232
  return { status: 'ok', data }
169
233
  },
package/instaserve CHANGED
@@ -59,6 +59,15 @@ if (args[0] === 'generate-routes') {
59
59
 
60
60
  api: (req, res, data) => {
61
61
  return { message: 'API endpoint', data }
62
+ },
63
+
64
+ // Method-specific routes (POST, GET, PUT, DELETE)
65
+ 'POST /users': (req, res, data) => {
66
+ return { message: 'Create user', data }
67
+ },
68
+
69
+ 'GET /users': (req, res, data) => {
70
+ return { message: 'Get users' }
62
71
  }
63
72
  }
64
73
  `
package/module.mjs CHANGED
@@ -51,6 +51,21 @@ export default async function (routes, port = params.port || 3000, ip = params.i
51
51
  let sdata = '', rrurl = r.url || ''
52
52
  let responseSent = false
53
53
 
54
+ // Helper to check if value is a 3-digit HTTP status code
55
+ const isStatusCode = (val) => {
56
+ return typeof val === 'number' && val >= 100 && val <= 999 && Math.floor(val) === val
57
+ }
58
+
59
+ // Helper to send response, handling status codes
60
+ const sendResponse = (result) => {
61
+ if (isStatusCode(result)) {
62
+ s.writeHead(result)
63
+ s.end()
64
+ } else {
65
+ s.end(typeof result === 'string' ? result : JSON.stringify(result))
66
+ }
67
+ }
68
+
54
69
  r.on('data', (s) => sdata += s.toString().trim())
55
70
  r.on('end', (x) => {
56
71
  try {
@@ -68,7 +83,7 @@ export default async function (routes, port = params.port || 3000, ip = params.i
68
83
  const result = routes[k](r, s, data)
69
84
  if (result && !responseSent) {
70
85
  responseSent = true
71
- s.end(typeof result === 'string' ? result : JSON.stringify(result))
86
+ sendResponse(result)
72
87
  }
73
88
  return result
74
89
  })
@@ -84,11 +99,40 @@ export default async function (routes, port = params.port || 3000, ip = params.i
84
99
 
85
100
  const urlParts = rrurl.split('/')
86
101
  const url = urlParts.length > 1 ? urlParts[1].split('?')[0] : ''
87
- if (routes[url]) {
88
- const resp = routes[url](r, s, data)
102
+ const method = (r.method || 'GET').toUpperCase()
103
+
104
+ // Try method-specific route first (e.g., "POST /endp", "GET /")
105
+ const methodRoute = url ? `${method} /${url}` : `${method} /`
106
+ let routeHandler = routes[methodRoute]
107
+
108
+ // If no exact match, check if any method-specific route exists for this path
109
+ if (!routeHandler) {
110
+ const methods = ['GET', 'POST', 'PUT', 'DELETE']
111
+ const pathRoute = url ? `/${url}` : `/`
112
+ const hasMethodSpecificRoute = methods.some(m => {
113
+ const checkRoute = url ? `${m} /${url}` : `${m} /`
114
+ return routes[checkRoute] !== undefined
115
+ })
116
+
117
+ // If method-specific route exists but for different method, return 405
118
+ if (hasMethodSpecificRoute) {
119
+ if (!responseSent && !s.writableEnded) {
120
+ responseSent = true
121
+ s.writeHead(405)
122
+ s.end()
123
+ }
124
+ return
125
+ }
126
+
127
+ // Fall back to path-only route (backward compatible)
128
+ routeHandler = routes[url]
129
+ }
130
+
131
+ if (routeHandler) {
132
+ const resp = routeHandler(r, s, data)
89
133
  if (!responseSent && !s.writableEnded) {
90
134
  responseSent = true
91
- s.end(typeof resp === 'string' ? resp:JSON.stringify(resp))
135
+ sendResponse(resp)
92
136
  }
93
137
  return
94
138
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instaserve",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "Instant web stack",
5
5
  "main": "module.mjs",
6
6
  "bin": "./instaserve",
package/test.js ADDED
@@ -0,0 +1,28 @@
1
+ export default {
2
+ // Middleware functions (prefixed with _) run on every request
3
+ // Return false to continue processing, or a value to use as response
4
+
5
+ // Example: Log all requests
6
+ _log: (req, res, data) => {
7
+ console.log(`${req.method} ${req.url}`)
8
+ return false // Continue to next middleware or route
9
+ },
10
+
11
+ // Example: Basic authentication (commented out)
12
+ // _auth: (req, res, data) => {
13
+ // if (!data.token) {
14
+ // res.writeHead(401)
15
+ // return 'Unauthorized'
16
+ // }
17
+ // return false // Continue if authorized
18
+ // },
19
+
20
+ // Regular route handlers
21
+ hello: (req, res, data) => {
22
+ return { message: 'Hello World' }
23
+ },
24
+
25
+ api: (req, res, data) => {
26
+ return { message: 'API endpoint', data }
27
+ }
28
+ }