instaserve 1.1.1 → 1.1.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.
- package/README.md +32 -1
- package/instaserve +80 -5
- package/package.json +2 -2
- package/public/index.html +0 -209
- /package/{routes.js → routes_example.md} +0 -0
package/README.md
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<h1 style="color: #3b82f6; margin: 10px 0 5px;">Instaserve</h1>
|
|
3
3
|
<p style="color: #6b7280; margin: 0;">Instant web stack for Node.js</p>
|
|
4
|
+
|
|
5
|
+
<p>
|
|
6
|
+
<img src="https://img.shields.io/npm/v/instaserve" alt="npm version" />
|
|
7
|
+
<img src="https://img.shields.io/bundlephobia/minzip/instaserve" alt="bundle size" />
|
|
8
|
+
</p>
|
|
4
9
|
</div>
|
|
5
10
|
|
|
6
11
|
## Usage
|
|
7
12
|
|
|
8
13
|
<div style="background: white; padding: 15px; border-radius: 6px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); margin: 15px 0;">
|
|
9
|
-
<pre style="margin: 0;"><code>npx instaserve [options]
|
|
14
|
+
<pre style="margin: 0;"><code>npx instaserve [options]
|
|
15
|
+
npx instaserve generate-routes</code></pre>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
## Commands
|
|
19
|
+
|
|
20
|
+
<div style="display: flex; gap: 8px; margin: 8px 0;">
|
|
21
|
+
<code style="background: #f3f4f6; padding: 2px 4px; border-radius: 4px; color: #2563eb;">generate-routes</code>
|
|
22
|
+
<span>Create a sample routes.js file in the current directory</span>
|
|
10
23
|
</div>
|
|
11
24
|
|
|
12
25
|
## Options
|
|
@@ -65,6 +78,24 @@ The certificate generation script:
|
|
|
65
78
|
|
|
66
79
|
The routes file (`routes.js` by default) defines your API endpoints. Each route is a function that handles requests to a specific URL path.
|
|
67
80
|
|
|
81
|
+
### Generating a Routes File
|
|
82
|
+
|
|
83
|
+
To create a sample `routes.js` file with example routes and middleware:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npx instaserve generate-routes
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
This creates a `routes.js` file in the current directory with example code. If the file already exists, the command will fail to prevent overwriting.
|
|
90
|
+
|
|
91
|
+
### Routes File Validation
|
|
92
|
+
|
|
93
|
+
Instaserve validates routes files on startup:
|
|
94
|
+
- If `-api` is specified and the file doesn't exist, the server will fail to start
|
|
95
|
+
- The routes file must export a default object
|
|
96
|
+
- All route handlers must be functions
|
|
97
|
+
- Invalid routes files will cause the server to exit with an error message
|
|
98
|
+
|
|
68
99
|
### Basic Route Example
|
|
69
100
|
|
|
70
101
|
```javascript
|
package/instaserve
CHANGED
|
@@ -2,10 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
import chalk from 'chalk'
|
|
4
4
|
import fs from 'node:fs'
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import { pathToFileURL } from 'node:url'
|
|
5
7
|
|
|
6
8
|
console.log(chalk.cyan('\nInstaserve - Instant Web Stack\n'))
|
|
7
9
|
console.log(chalk.yellow('Usage:'))
|
|
8
|
-
console.log(chalk.green(' npx instaserve [options]
|
|
10
|
+
console.log(chalk.green(' npx instaserve [options]'))
|
|
11
|
+
console.log(chalk.green(' npx instaserve generate-routes\n'))
|
|
12
|
+
console.log(chalk.yellow('Commands:'))
|
|
13
|
+
console.log(chalk.green(' generate-routes') + ' Create a sample routes.js file in the current directory\n')
|
|
9
14
|
console.log(chalk.yellow('Options:'))
|
|
10
15
|
console.log(chalk.green(' -port <number>') + ' Port to listen on (default: 3000)')
|
|
11
16
|
console.log(chalk.green(' -ip <address>') + ' IP address to bind to (default: 127.0.0.1)')
|
|
@@ -18,9 +23,53 @@ if (process.argv.includes('-help')) {
|
|
|
18
23
|
process.exit(0)
|
|
19
24
|
}
|
|
20
25
|
|
|
26
|
+
const args = process.argv.slice(2)
|
|
27
|
+
|
|
28
|
+
// Handle generate-routes command
|
|
29
|
+
if (args[0] === 'generate-routes') {
|
|
30
|
+
const routesFile = './routes.js'
|
|
31
|
+
if (fs.existsSync(routesFile)) {
|
|
32
|
+
console.error(chalk.red(`Error: ${routesFile} already exists`))
|
|
33
|
+
process.exit(1)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const sampleRoutes = `export default {
|
|
37
|
+
// Middleware functions (prefixed with _) run on every request
|
|
38
|
+
// Return false to continue processing, or a value to use as response
|
|
39
|
+
|
|
40
|
+
// Example: Log all requests
|
|
41
|
+
_log: (req, res, data) => {
|
|
42
|
+
console.log(\`\${req.method} \${req.url}\`)
|
|
43
|
+
return false // Continue to next middleware or route
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
// Example: Basic authentication (commented out)
|
|
47
|
+
// _auth: (req, res, data) => {
|
|
48
|
+
// if (!data.token) {
|
|
49
|
+
// res.writeHead(401)
|
|
50
|
+
// return 'Unauthorized'
|
|
51
|
+
// }
|
|
52
|
+
// return false // Continue if authorized
|
|
53
|
+
// },
|
|
54
|
+
|
|
55
|
+
// Regular route handlers
|
|
56
|
+
hello: (req, res, data) => {
|
|
57
|
+
return { message: 'Hello World' }
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
api: (req, res, data) => {
|
|
61
|
+
return { message: 'API endpoint', data }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
`
|
|
65
|
+
|
|
66
|
+
fs.writeFileSync(routesFile, sampleRoutes)
|
|
67
|
+
console.log(chalk.green(`✓ Created ${routesFile}`))
|
|
68
|
+
process.exit(0)
|
|
69
|
+
}
|
|
70
|
+
|
|
21
71
|
import server from './module.mjs'
|
|
22
72
|
|
|
23
|
-
const args = process.argv.slice(2)
|
|
24
73
|
const params = {}
|
|
25
74
|
for (let i = 0; i < args.length; i++) {
|
|
26
75
|
const arg = args[i]
|
|
@@ -38,13 +87,39 @@ for (let i = 0; i < args.length; i++) {
|
|
|
38
87
|
|
|
39
88
|
// Load routes file
|
|
40
89
|
let routes = {}
|
|
41
|
-
const
|
|
90
|
+
const routesFileParam = params.api || './routes.js'
|
|
91
|
+
const routesFileSpecified = !!params.api
|
|
92
|
+
|
|
93
|
+
// Resolve to absolute path from current working directory
|
|
94
|
+
const routesFile = path.isAbsolute(routesFileParam)
|
|
95
|
+
? routesFileParam
|
|
96
|
+
: path.resolve(process.cwd(), routesFileParam)
|
|
97
|
+
|
|
98
|
+
if (routesFileSpecified && !fs.existsSync(routesFile)) {
|
|
99
|
+
console.error(chalk.red(`Error: Routes file "${routesFileParam}" does not exist`))
|
|
100
|
+
process.exit(1)
|
|
101
|
+
}
|
|
102
|
+
|
|
42
103
|
if (fs.existsSync(routesFile)) {
|
|
43
104
|
try {
|
|
44
|
-
const
|
|
105
|
+
const routesFileURL = pathToFileURL(routesFile).href
|
|
106
|
+
const imported = await import(routesFileURL)
|
|
45
107
|
routes = imported.default || imported
|
|
108
|
+
|
|
109
|
+
if (!routes || typeof routes !== 'object' || Array.isArray(routes)) {
|
|
110
|
+
console.error(chalk.red(`Error: Routes file "${routesFileParam}" must export a default object`))
|
|
111
|
+
process.exit(1)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
for (const [key, handler] of Object.entries(routes)) {
|
|
115
|
+
if (typeof handler !== 'function') {
|
|
116
|
+
console.error(chalk.red(`Error: Route "${key}" in "${routesFileParam}" must be a function`))
|
|
117
|
+
process.exit(1)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
46
120
|
} catch (e) {
|
|
47
|
-
console.
|
|
121
|
+
console.error(chalk.red(`Error: Could not load routes file "${routesFileParam}": ${e.message}`))
|
|
122
|
+
process.exit(1)
|
|
48
123
|
}
|
|
49
124
|
}
|
|
50
125
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "instaserve",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "Instant web stack",
|
|
5
5
|
"main": "module.mjs",
|
|
6
6
|
"bin": "./instaserve",
|
|
@@ -9,6 +9,6 @@
|
|
|
9
9
|
},
|
|
10
10
|
"type": "module",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"chalk": "^5.
|
|
12
|
+
"chalk": "^5.6.2"
|
|
13
13
|
}
|
|
14
14
|
}
|
package/public/index.html
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<title>Instaserve</title>
|
|
5
|
-
<style>
|
|
6
|
-
:root {
|
|
7
|
-
--primary: #3b82f6;
|
|
8
|
-
--primary-dark: #2563eb;
|
|
9
|
-
--text: #1f2937;
|
|
10
|
-
--text-light: #6b7280;
|
|
11
|
-
--bg: #f9fafb;
|
|
12
|
-
}
|
|
13
|
-
body {
|
|
14
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
15
|
-
line-height: 1.5;
|
|
16
|
-
max-width: 800px;
|
|
17
|
-
margin: 0 auto;
|
|
18
|
-
padding: 20px 15px;
|
|
19
|
-
color: var(--text);
|
|
20
|
-
background: var(--bg);
|
|
21
|
-
}
|
|
22
|
-
.header {
|
|
23
|
-
text-align: center;
|
|
24
|
-
margin-bottom: 20px;
|
|
25
|
-
}
|
|
26
|
-
.orb {
|
|
27
|
-
width: 60px;
|
|
28
|
-
height: 60px;
|
|
29
|
-
background: radial-gradient(circle at 30% 30%, #60a5fa, var(--primary));
|
|
30
|
-
border-radius: 50%;
|
|
31
|
-
margin: 0 auto 10px;
|
|
32
|
-
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
|
33
|
-
}
|
|
34
|
-
h1 {
|
|
35
|
-
color: var(--primary);
|
|
36
|
-
font-size: 2rem;
|
|
37
|
-
margin: 0;
|
|
38
|
-
}
|
|
39
|
-
.subtitle {
|
|
40
|
-
color: var(--text-light);
|
|
41
|
-
font-size: 1rem;
|
|
42
|
-
margin: 5px 0;
|
|
43
|
-
}
|
|
44
|
-
h2 {
|
|
45
|
-
color: var(--text);
|
|
46
|
-
font-size: 1.3rem;
|
|
47
|
-
margin: 20px 0 10px;
|
|
48
|
-
padding-bottom: 5px;
|
|
49
|
-
border-bottom: 2px solid var(--primary);
|
|
50
|
-
}
|
|
51
|
-
h3 {
|
|
52
|
-
color: var(--text);
|
|
53
|
-
font-size: 1.1rem;
|
|
54
|
-
margin: 15px 0 8px;
|
|
55
|
-
}
|
|
56
|
-
code {
|
|
57
|
-
background: #f3f4f6;
|
|
58
|
-
padding: 2px 4px;
|
|
59
|
-
border-radius: 4px;
|
|
60
|
-
font-family: 'SF Mono', Menlo, monospace;
|
|
61
|
-
color: var(--primary-dark);
|
|
62
|
-
}
|
|
63
|
-
pre {
|
|
64
|
-
background: #f3f4f6;
|
|
65
|
-
padding: 10px;
|
|
66
|
-
border-radius: 6px;
|
|
67
|
-
overflow-x: auto;
|
|
68
|
-
border: 1px solid #e5e7eb;
|
|
69
|
-
margin: 10px 0;
|
|
70
|
-
font-size: 0.9rem;
|
|
71
|
-
}
|
|
72
|
-
.option {
|
|
73
|
-
margin: 8px 0;
|
|
74
|
-
padding-left: 0;
|
|
75
|
-
position: relative;
|
|
76
|
-
display: flex;
|
|
77
|
-
gap: 8px;
|
|
78
|
-
}
|
|
79
|
-
.option:before {
|
|
80
|
-
display: none;
|
|
81
|
-
}
|
|
82
|
-
.option code {
|
|
83
|
-
flex-shrink: 0;
|
|
84
|
-
}
|
|
85
|
-
ul {
|
|
86
|
-
padding-left: 15px;
|
|
87
|
-
margin: 8px 0;
|
|
88
|
-
}
|
|
89
|
-
li {
|
|
90
|
-
margin: 5px 0;
|
|
91
|
-
}
|
|
92
|
-
.usage {
|
|
93
|
-
background: white;
|
|
94
|
-
padding: 15px;
|
|
95
|
-
border-radius: 6px;
|
|
96
|
-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
97
|
-
margin: 15px 0;
|
|
98
|
-
}
|
|
99
|
-
p {
|
|
100
|
-
margin: 8px 0;
|
|
101
|
-
}
|
|
102
|
-
</style>
|
|
103
|
-
</head>
|
|
104
|
-
<body>
|
|
105
|
-
<div class="header">
|
|
106
|
-
<div class="orb"></div>
|
|
107
|
-
<h1>Instaserve</h1>
|
|
108
|
-
<p class="subtitle">Instant web stack for Node.js</p>
|
|
109
|
-
</div>
|
|
110
|
-
|
|
111
|
-
<div class="usage">
|
|
112
|
-
<h2>Usage</h2>
|
|
113
|
-
<pre><code>npx instaserve [options]</code></pre>
|
|
114
|
-
</div>
|
|
115
|
-
|
|
116
|
-
<h2>Options</h2>
|
|
117
|
-
<div class="option"><code>-port <number></code> - Port to listen on (default: 3000)</div>
|
|
118
|
-
<div class="option"><code>-ip <address></code> - IP address to bind to (default: 127.0.0.1)</div>
|
|
119
|
-
<div class="option"><code>-public <path></code> - Public directory path (default: ./public)</div>
|
|
120
|
-
<div class="option"><code>-api <file></code> - Path to routes file (default: ./routes.js)</div>
|
|
121
|
-
<div class="option"><code>-secure</code> - Enable HTTPS (requires cert.pem and key.pem - run ./generate-certs.sh)</div>
|
|
122
|
-
<div class="option"><code>-help</code> - Show help message</div>
|
|
123
|
-
|
|
124
|
-
<h2>HTTPS Support</h2>
|
|
125
|
-
<p>Instaserve supports HTTPS with self-signed certificates. To enable HTTPS:</p>
|
|
126
|
-
<ol>
|
|
127
|
-
<li><strong>Generate certificates:</strong> <code>./generate-certs.sh</code></li>
|
|
128
|
-
<li><strong>Run with HTTPS:</strong> <code>npx instaserve -secure</code></li>
|
|
129
|
-
</ol>
|
|
130
|
-
<p>The certificate generation script creates trusted certificates that won't show browser warnings.</p>
|
|
131
|
-
|
|
132
|
-
<h2>Routes</h2>
|
|
133
|
-
<p>The routes file (<code>routes.js</code> by default) defines your API endpoints. Each route is a function that handles requests to a specific URL path.</p>
|
|
134
|
-
|
|
135
|
-
<h3>Basic Route Example</h3>
|
|
136
|
-
<pre><code>export default {
|
|
137
|
-
// Handle GET /hello
|
|
138
|
-
hello: (req, res, data) => {
|
|
139
|
-
return { message: 'Hello World' }
|
|
140
|
-
}
|
|
141
|
-
}</code></pre>
|
|
142
|
-
|
|
143
|
-
<h3>Special Routes (Middleware)</h3>
|
|
144
|
-
<p>Routes starting with <code>_</code> are middleware functions that run on <strong>every request</strong> before the main route handler. They are useful for:</p>
|
|
145
|
-
<ul>
|
|
146
|
-
<li>Logging requests</li>
|
|
147
|
-
<li>Authentication</li>
|
|
148
|
-
<li>Request modification</li>
|
|
149
|
-
<li>Response headers</li>
|
|
150
|
-
</ul>
|
|
151
|
-
<p>Middleware functions can:</p>
|
|
152
|
-
<ul>
|
|
153
|
-
<li>Return <code>false</code> to continue to the next middleware or main route</li>
|
|
154
|
-
<li>Return a truthy value to stop processing and use that as the response</li>
|
|
155
|
-
<li>Modify the request or response objects</li>
|
|
156
|
-
</ul>
|
|
157
|
-
|
|
158
|
-
<h4>Middleware Example</h4>
|
|
159
|
-
<pre><code>export default {
|
|
160
|
-
// Log every request
|
|
161
|
-
_log: (req, res, data) => {
|
|
162
|
-
console.log(`${req.method} ${req.url}`)
|
|
163
|
-
return false // Continue processing
|
|
164
|
-
},
|
|
165
|
-
|
|
166
|
-
// Block unauthorized requests
|
|
167
|
-
_auth: (req, res, data) => {
|
|
168
|
-
if (!data.token) {
|
|
169
|
-
res.writeHead(401)
|
|
170
|
-
return 'Unauthorized'
|
|
171
|
-
}
|
|
172
|
-
return false // Continue if authorized
|
|
173
|
-
}
|
|
174
|
-
}</code></pre>
|
|
175
|
-
|
|
176
|
-
<h3>Route Parameters</h3>
|
|
177
|
-
<p>Each route function receives:</p>
|
|
178
|
-
<ul>
|
|
179
|
-
<li><code>req</code> - The HTTP request object</li>
|
|
180
|
-
<li><code>res</code> - The HTTP response object</li>
|
|
181
|
-
<li><code>data</code> - Combined data from:
|
|
182
|
-
<ul>
|
|
183
|
-
<li>POST body (if JSON)</li>
|
|
184
|
-
<li>URL query parameters</li>
|
|
185
|
-
<li>Form data</li>
|
|
186
|
-
</ul>
|
|
187
|
-
</li>
|
|
188
|
-
</ul>
|
|
189
|
-
|
|
190
|
-
<h3>Example Routes File</h3>
|
|
191
|
-
<pre><code>export default {
|
|
192
|
-
// Middleware example
|
|
193
|
-
_debug: (req, res, data) => {
|
|
194
|
-
console.log('Request:', req.url)
|
|
195
|
-
return false // Continue to next route
|
|
196
|
-
},
|
|
197
|
-
|
|
198
|
-
// API endpoint
|
|
199
|
-
api: (req, res, data) => {
|
|
200
|
-
return { status: 'ok', data }
|
|
201
|
-
},
|
|
202
|
-
|
|
203
|
-
// Error handling
|
|
204
|
-
testerror: () => {
|
|
205
|
-
throw new Error('Test error')
|
|
206
|
-
}
|
|
207
|
-
}</code></pre>
|
|
208
|
-
</body>
|
|
209
|
-
</html>
|
|
File without changes
|