instaserve 1.1.6 → 1.1.9

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 (3) hide show
  1. package/module.mjs +165 -158
  2. package/package.json +13 -13
  3. package/test.js +0 -28
package/module.mjs CHANGED
@@ -6,183 +6,190 @@ import path from 'node:path'
6
6
  const args = process.argv.slice(2)
7
7
  const params = {}
8
8
  for (let i = 0; i < args.length; i++) {
9
- const arg = args[i]
10
- if (arg.startsWith('-')) {
11
- const key = arg.slice(1)
12
- const nextArg = args[i + 1]
13
- if (nextArg && !nextArg.startsWith('-')) {
14
- params[key] = nextArg
15
- i++ // Skip the next argument since we used it
16
- } else {
17
- params[key] = true // Boolean flag
18
- }
9
+ const arg = args[i]
10
+ if (arg.startsWith('-')) {
11
+ const key = arg.slice(1)
12
+ const nextArg = args[i + 1]
13
+ if (nextArg && !nextArg.startsWith('-')) {
14
+ params[key] = nextArg
15
+ i++ // Skip the next argument since we used it
16
+ } else {
17
+ params[key] = true // Boolean flag
19
18
  }
19
+ }
20
20
  }
21
21
 
22
22
  function public_file(r, s, publicDir) {
23
- if (r.url == '/') r.url = '/index.html'
24
- const fn = path.resolve(publicDir, r.url.replace(/\.\./g, '').replace(/^\//, ''))
25
- if (fs.existsSync(fn)) {
26
- const content = fs.readFileSync(fn, 'utf-8')
27
- if (fn.match(/.js$/)) {
28
- s.writeHead(200, { 'Content-Type': 'application/javascript' })
29
- } else {
30
- s.writeHead(200)
31
- }
32
- s.end(content)
33
- return true // Indicate file was served
23
+ if (r.url == '/') r.url = '/index.html'
24
+ const fn = path.resolve(publicDir, r.url.replace(/\.\./g, '').replace(/^\//, ''))
25
+ if (fs.existsSync(fn)) {
26
+ const content = fs.readFileSync(fn, 'utf-8')
27
+ if (fn.match(/.js$/)) {
28
+ s.writeHead(200, { 'Content-Type': 'application/javascript' })
29
+ } else {
30
+ s.writeHead(200)
34
31
  }
35
- return false // Indicate no file was served
32
+ s.end(content)
33
+ return true // Indicate file was served
34
+ }
35
+ return false // Indicate no file was served
36
36
  }
37
37
 
38
38
  export default async function (routes, port = params.port || 3000, ip = params.ip || '127.0.0.1', routesFilePath = null) {
39
- const publicDirParam = params.public || './public'
40
- const publicDir = path.isAbsolute(publicDirParam)
41
- ? publicDirParam
42
- : path.resolve(process.cwd(), publicDirParam)
43
- if (publicDir.includes('..')) {
44
- throw new Error('Public directory path cannot contain ".."')
39
+ const publicDirParam = params.public || './public'
40
+ const publicDir = path.isAbsolute(publicDirParam)
41
+ ? publicDirParam
42
+ : path.resolve(process.cwd(), publicDirParam)
43
+ if (publicDir.includes('..')) {
44
+ throw new Error('Public directory path cannot contain ".."')
45
+ }
46
+ if (!fs.existsSync(publicDir)) {
47
+ throw new Error(`Public directory "${publicDir}" does not exist`)
48
+ }
49
+
50
+ const requestHandler = async (r, s) => {
51
+ let sdata = '', rrurl = r.url || ''
52
+ let responseSent = false
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
45
57
  }
46
- if (!fs.existsSync(publicDir)) {
47
- throw new Error(`Public directory "${publicDir}" does not exist`)
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
+ if (result != "SSE") {
66
+ s.end(typeof result === 'string' ? result : JSON.stringify(result))
67
+ }
68
+ }
48
69
  }
49
70
 
50
- const requestHandler = async (r, s) => {
51
- let sdata = '', rrurl = r.url || ''
52
- let responseSent = false
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
71
+ r.on('data', (s) => sdata += s.toString().trim())
72
+ r.on('end', async (x) => {
73
+ try {
74
+ // Compose data object
75
+ const data = sdata ? JSON.parse(sdata) : {}
76
+ const qs = rrurl.split('?')
77
+ if (qs && qs[1]) {
78
+ const o = JSON.parse('{"' + decodeURI(qs[1].replace(/&/g, "\",\"").replace(/=/g, "\":\"")) + '"}')
79
+ Object.assign(data, o)
57
80
  }
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))
81
+
82
+ const midwareKeys = Object.keys(routes).filter((k) => k.startsWith('_'))
83
+ for (const k of midwareKeys) {
84
+ let result = routes[k](r, s, data)
85
+ if (result instanceof Promise) result = await result
86
+
87
+ if (result) {
88
+ if (!responseSent) {
89
+ responseSent = true
90
+ sendResponse(result)
66
91
  }
92
+ break
93
+ }
67
94
  }
68
-
69
- r.on('data', (s) => sdata += s.toString().trim())
70
- r.on('end', (x) => {
71
- try {
72
- // Compose data object
73
- const data = sdata ? JSON.parse(sdata) : {}
74
- const qs = rrurl.split('?')
75
- if(qs && qs[1]) {
76
- const o = JSON.parse('{"' + decodeURI(qs[1].replace(/&/g, "\",\"").replace(/=/g, "\":\"")) + '"}')
77
- Object.assign(data, o)
78
- }
79
-
80
- const midware = Object.keys(routes)
81
- .filter((k) => k.startsWith('_'))
82
- .find((k) => {
83
- const result = routes[k](r, s, data)
84
- if (result && !responseSent) {
85
- responseSent = true
86
- sendResponse(result)
87
- }
88
- return result
89
- })
90
-
91
- // Response closed by middleware
92
- if(responseSent || s.writableEnded) return
93
-
94
- // Try to serve public file
95
- if(public_file(r, s, publicDir)) {
96
- responseSent = true
97
- return
98
- }
99
-
100
- const urlParts = rrurl.split('/')
101
- const url = urlParts.length > 1 ? urlParts[1].split('?')[0] : ''
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)
133
- if (!responseSent && !s.writableEnded) {
134
- responseSent = true
135
- sendResponse(resp)
136
- }
137
- return
138
- }
139
-
140
- if (!responseSent && !s.writableEnded) {
141
- responseSent = true
142
- s.writeHead(404);
143
- s.end();
144
- }
145
- } catch (e) {
146
- console.error(e.stack)
147
- if (!responseSent && !s.writableEnded) {
148
- responseSent = true
149
- s.writeHead(500).end()
150
- }
95
+
96
+ // Response closed by middleware
97
+ if (responseSent || s.writableEnded) return
98
+
99
+ // Try to serve public file
100
+ if (public_file(r, s, publicDir)) {
101
+ responseSent = true
102
+ return
103
+ }
104
+
105
+ const urlParts = rrurl.split('/')
106
+ const url = urlParts.length > 1 ? urlParts[1].split('?')[0] : ''
107
+ const method = (r.method || 'GET').toUpperCase()
108
+
109
+ // Try method-specific route first (e.g., "POST /endp", "GET /")
110
+ const methodRoute = url ? `${method} /${url}` : `${method} /`
111
+ let routeHandler = routes[methodRoute]
112
+
113
+ // If no exact match, check if any method-specific route exists for this path
114
+ if (!routeHandler) {
115
+ const methods = ['GET', 'POST', 'PUT', 'DELETE']
116
+ const pathRoute = url ? `/${url}` : `/`
117
+ const hasMethodSpecificRoute = methods.some(m => {
118
+ const checkRoute = url ? `${m} /${url}` : `${m} /`
119
+ return routes[checkRoute] !== undefined
120
+ })
121
+
122
+ // If method-specific route exists but for different method, return 405
123
+ if (hasMethodSpecificRoute) {
124
+ if (!responseSent && !s.writableEnded) {
125
+ responseSent = true
126
+ s.writeHead(405)
127
+ s.end()
151
128
  }
152
- })
153
- }
129
+ return
130
+ }
131
+
132
+ // Fall back to path-only route (backward compatible)
133
+ routeHandler = routes[url]
134
+ }
135
+
136
+ if (routeHandler) {
137
+ let resp = routeHandler(r, s, data)
138
+ if (resp instanceof Promise) resp = await resp
154
139
 
155
- let server
156
- if (params.secure) {
157
- const certPath = path.resolve(process.cwd(), './cert.pem')
158
- const keyPath = path.resolve(process.cwd(), './key.pem')
159
-
160
- if (!fs.existsSync(certPath) || !fs.existsSync(keyPath)) {
161
- throw new Error('Certificate files not found. Run ./generate-certs.sh first.')
140
+ if (!responseSent && !s.writableEnded) {
141
+ responseSent = true
142
+ sendResponse(resp)
143
+ }
144
+ return
162
145
  }
163
-
164
- const options = {
165
- cert: fs.readFileSync(certPath),
166
- key: fs.readFileSync(keyPath)
146
+
147
+ if (!responseSent && !s.writableEnded) {
148
+ responseSent = true
149
+ s.writeHead(404);
150
+ s.end();
167
151
  }
168
-
169
- server = https.createServer(options, requestHandler)
170
- } else {
171
- server = http.createServer(requestHandler)
152
+ } catch (e) {
153
+ console.error(e.stack)
154
+ if (!responseSent && !s.writableEnded) {
155
+ responseSent = true
156
+ s.writeHead(500).end()
157
+ }
158
+ }
159
+ })
160
+ }
161
+
162
+ let server
163
+ if (params.secure) {
164
+ const certPath = path.resolve(process.cwd(), './cert.pem')
165
+ const keyPath = path.resolve(process.cwd(), './key.pem')
166
+
167
+ if (!fs.existsSync(certPath) || !fs.existsSync(keyPath)) {
168
+ throw new Error('Certificate files not found. Run ./generate-certs.sh first.')
172
169
  }
173
170
 
174
- server.listen(port || 3000, ip || '')
175
-
176
- const protocol = params.secure ? 'https' : 'http'
177
- const routesInfo = Object.keys(routes).length > 0
178
- ? (routesFilePath ? `using routes: ${Object.keys(routes)} (${routesFilePath})` : `using routes: ${Object.keys(routes)}`)
179
- : 'not using routes'
180
- console.log(`started on: ${protocol}://${(process.env.ip || ip)}:${(process.env.port || port)}, public: ${publicDir}, ${routesInfo}`)
181
-
182
- return {
183
- routes: routes,
184
- port: port,
185
- server: server,
186
- stop: () => { server.close(); return true }
171
+ const options = {
172
+ cert: fs.readFileSync(certPath),
173
+ key: fs.readFileSync(keyPath)
187
174
  }
175
+
176
+ server = https.createServer(options, requestHandler)
177
+ } else {
178
+ server = http.createServer(requestHandler)
179
+ }
180
+
181
+ server.listen(port || 3000, ip || '')
182
+
183
+ const protocol = params.secure ? 'https' : 'http'
184
+ const routesInfo = Object.keys(routes).length > 0
185
+ ? (routesFilePath ? `using routes: ${Object.keys(routes)} (${routesFilePath})` : `using routes: ${Object.keys(routes)}`)
186
+ : 'not using routes'
187
+ console.log(`started on: ${protocol}://${(process.env.ip || ip)}:${(process.env.port || port)}, public: ${publicDir}, ${routesInfo}`)
188
+
189
+ return {
190
+ routes: routes,
191
+ port: port,
192
+ server: server,
193
+ stop: () => { server.close(); return true }
194
+ }
188
195
  }
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
- "name": "instaserve",
3
- "version": "1.1.6",
4
- "description": "Instant web stack",
5
- "main": "module.mjs",
6
- "bin": "./instaserve",
7
- "scripts": {
8
- "start": "node instaserve"
9
- },
10
- "type": "module",
11
- "dependencies": {
12
- "chalk": "^5.6.2"
13
- }
14
- }
2
+ "name": "instaserve",
3
+ "version": "1.1.9",
4
+ "description": "Instant web stack",
5
+ "main": "module.mjs",
6
+ "bin": "./instaserve",
7
+ "scripts": {
8
+ "start": "node instaserve"
9
+ },
10
+ "type": "module",
11
+ "dependencies": {
12
+ "chalk": "^5.6.2"
13
+ }
14
+ }
package/test.js DELETED
@@ -1,28 +0,0 @@
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
- }