instaserve 1.1.6 → 1.1.8

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/module.mjs +163 -158
  2. package/package.json +13 -13
package/module.mjs CHANGED
@@ -6,183 +6,188 @@ 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
+ s.end(typeof result === 'string' ? result : JSON.stringify(result))
66
+ }
48
67
  }
49
68
 
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
69
+ r.on('data', (s) => sdata += s.toString().trim())
70
+ r.on('end', async (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)
57
78
  }
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))
79
+
80
+ const midwareKeys = Object.keys(routes).filter((k) => k.startsWith('_'))
81
+ for (const k of midwareKeys) {
82
+ let result = routes[k](r, s, data)
83
+ if (result instanceof Promise) result = await result
84
+
85
+ if (result) {
86
+ if (!responseSent) {
87
+ responseSent = true
88
+ sendResponse(result)
66
89
  }
90
+ break
91
+ }
92
+ }
93
+
94
+ // Response closed by middleware
95
+ if (responseSent || s.writableEnded) return
96
+
97
+ // Try to serve public file
98
+ if (public_file(r, s, publicDir)) {
99
+ responseSent = true
100
+ return
67
101
  }
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
- }
102
+
103
+ const urlParts = rrurl.split('/')
104
+ const url = urlParts.length > 1 ? urlParts[1].split('?')[0] : ''
105
+ const method = (r.method || 'GET').toUpperCase()
106
+
107
+ // Try method-specific route first (e.g., "POST /endp", "GET /")
108
+ const methodRoute = url ? `${method} /${url}` : `${method} /`
109
+ let routeHandler = routes[methodRoute]
110
+
111
+ // If no exact match, check if any method-specific route exists for this path
112
+ if (!routeHandler) {
113
+ const methods = ['GET', 'POST', 'PUT', 'DELETE']
114
+ const pathRoute = url ? `/${url}` : `/`
115
+ const hasMethodSpecificRoute = methods.some(m => {
116
+ const checkRoute = url ? `${m} /${url}` : `${m} /`
117
+ return routes[checkRoute] !== undefined
118
+ })
119
+
120
+ // If method-specific route exists but for different method, return 405
121
+ if (hasMethodSpecificRoute) {
122
+ if (!responseSent && !s.writableEnded) {
123
+ responseSent = true
124
+ s.writeHead(405)
125
+ s.end()
151
126
  }
152
- })
153
- }
127
+ return
128
+ }
154
129
 
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.')
130
+ // Fall back to path-only route (backward compatible)
131
+ routeHandler = routes[url]
162
132
  }
163
-
164
- const options = {
165
- cert: fs.readFileSync(certPath),
166
- key: fs.readFileSync(keyPath)
133
+
134
+ if (routeHandler) {
135
+ let resp = routeHandler(r, s, data)
136
+ if (resp instanceof Promise) resp = await resp
137
+
138
+ if (!responseSent && !s.writableEnded) {
139
+ responseSent = true
140
+ sendResponse(resp)
141
+ }
142
+ return
167
143
  }
168
-
169
- server = https.createServer(options, requestHandler)
170
- } else {
171
- server = http.createServer(requestHandler)
144
+
145
+ if (!responseSent && !s.writableEnded) {
146
+ responseSent = true
147
+ s.writeHead(404);
148
+ s.end();
149
+ }
150
+ } catch (e) {
151
+ console.error(e.stack)
152
+ if (!responseSent && !s.writableEnded) {
153
+ responseSent = true
154
+ s.writeHead(500).end()
155
+ }
156
+ }
157
+ })
158
+ }
159
+
160
+ let server
161
+ if (params.secure) {
162
+ const certPath = path.resolve(process.cwd(), './cert.pem')
163
+ const keyPath = path.resolve(process.cwd(), './key.pem')
164
+
165
+ if (!fs.existsSync(certPath) || !fs.existsSync(keyPath)) {
166
+ throw new Error('Certificate files not found. Run ./generate-certs.sh first.')
172
167
  }
173
168
 
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 }
169
+ const options = {
170
+ cert: fs.readFileSync(certPath),
171
+ key: fs.readFileSync(keyPath)
187
172
  }
173
+
174
+ server = https.createServer(options, requestHandler)
175
+ } else {
176
+ server = http.createServer(requestHandler)
177
+ }
178
+
179
+ server.listen(port || 3000, ip || '')
180
+
181
+ const protocol = params.secure ? 'https' : 'http'
182
+ const routesInfo = Object.keys(routes).length > 0
183
+ ? (routesFilePath ? `using routes: ${Object.keys(routes)} (${routesFilePath})` : `using routes: ${Object.keys(routes)}`)
184
+ : 'not using routes'
185
+ console.log(`started on: ${protocol}://${(process.env.ip || ip)}:${(process.env.port || port)}, public: ${publicDir}, ${routesInfo}`)
186
+
187
+ return {
188
+ routes: routes,
189
+ port: port,
190
+ server: server,
191
+ stop: () => { server.close(); return true }
192
+ }
188
193
  }
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.8",
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
+ }