@caboodle-tech/node-simple-server 1.3.1 → 2.0.0

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 (40) hide show
  1. package/.eslintrc.json +35 -23
  2. package/COPYING.txt +7 -0
  3. package/LICENSE.txt +22 -0
  4. package/README.md +73 -40
  5. package/bin/nss.js +1039 -0
  6. package/changelogs/v1.md +7 -0
  7. package/changelogs/v2.md +28 -0
  8. package/examples/README.md +15 -0
  9. package/examples/controllers/website.js +60 -0
  10. package/examples/controllers/websocket.js +83 -0
  11. package/examples/run.js +17 -0
  12. package/examples/www-website/assets/fonts/roboto/LICENSE.txt +202 -0
  13. package/examples/www-website/assets/fonts/roboto/roboto-regular.ttf +0 -0
  14. package/examples/www-website/assets/fonts/roboto/roboto-regular.woff +0 -0
  15. package/examples/www-website/assets/fonts/roboto/roboto-regular.woff2 +0 -0
  16. package/examples/www-website/assets/imgs/logo.png +0 -0
  17. package/examples/www-website/css/main.css +96 -0
  18. package/examples/www-website/css/normalize.css +349 -0
  19. package/examples/www-website/index.html +47 -0
  20. package/examples/www-website/js/main.js +33 -0
  21. package/examples/www-websockets/assets/fonts/roboto/LICENSE.txt +202 -0
  22. package/examples/www-websockets/assets/fonts/roboto/roboto-regular.ttf +0 -0
  23. package/examples/www-websockets/assets/fonts/roboto/roboto-regular.woff +0 -0
  24. package/examples/www-websockets/assets/fonts/roboto/roboto-regular.woff2 +0 -0
  25. package/examples/www-websockets/assets/imgs/logo.png +0 -0
  26. package/examples/www-websockets/css/main.css +148 -0
  27. package/examples/www-websockets/css/normalize.css +349 -0
  28. package/examples/www-websockets/index.html +45 -0
  29. package/examples/www-websockets/js/main.js +63 -0
  30. package/{sources → handlers}/dir-listing.html +68 -5
  31. package/handlers/forbidden.html +43 -0
  32. package/{js → handlers/js}/content-types.js +3 -1
  33. package/{js → handlers/js}/http-status.js +2 -1
  34. package/handlers/live-reloading.html +209 -0
  35. package/handlers/not-found.html +43 -0
  36. package/package.json +13 -10
  37. package/LICENSE +0 -21
  38. package/server.js +0 -866
  39. package/sources/socket.html +0 -204
  40. /package/{sources → handlers}/favicon.ico +0 -0
@@ -10,9 +10,19 @@
10
10
  body {
11
11
  margin: 0;
12
12
  padding: 0;
13
+ height: 100%;
14
+ overflow: hidden;
13
15
  }
14
16
  body {
17
+ display: grid;
18
+ grid-template-areas:
19
+ "listing"
20
+ "content"
21
+ "footer";
22
+ grid-template-columns: 1fr ;
23
+ grid-template-rows: auto 1fr auto;
15
24
  padding: 15px;
25
+ overflow-y: auto;
16
26
  }
17
27
  a {
18
28
  color: #008fcc;
@@ -23,6 +33,20 @@
23
33
  display: flex;
24
34
  flex-direction: column;
25
35
  }
36
+ .row.listing {
37
+ grid-area: listing;
38
+ }
39
+ .row.content {
40
+ grid-area: content;
41
+ }
42
+ .row.footer {
43
+ grid-area: footer;
44
+ }
45
+ .row.footer .col {
46
+ display: flex;
47
+ flex-direction: column;
48
+ justify-content: end;
49
+ }
26
50
  .row .col {
27
51
  flex: 1;
28
52
  margin-bottom: 15px;
@@ -49,6 +73,8 @@
49
73
  @media (min-width: 768px) {
50
74
  .row {
51
75
  flex-direction: row;
76
+ flex-basis: 100%;
77
+ height: auto;
52
78
  }
53
79
  .row .col:nth-of-type(odd) {
54
80
  margin-right: 10px;
@@ -60,19 +86,24 @@
60
86
  </style>
61
87
  </head>
62
88
  <body>
63
- <div class="row">
89
+ <div class="row listing">
64
90
  <div class="col">
65
- <h1>Listing ${path}</h1>
91
+ <h1>
92
+ Listing
93
+ <script type="text/javascript">
94
+ document.write(document.location.pathname);
95
+ </script>
96
+ </h1>
66
97
  </div>
67
98
  </div>
68
- <div class="row">
99
+ <div class="row content">
69
100
  <div class="col">
70
101
  <svg class="icon" width="24" height="24" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd">
71
102
  <path d="M22 24h-20v-24h14l6 6v18zm-7-23h-12v22h18v-16h-6v-6zm3 15v1h-12v-1h12zm0-3v1h-12v-1h12zm0-3v1h-12v-1h12zm-2-4h4.586l-4.586-4.586v4.586z" />
72
103
  </svg>
73
104
  <span class="title">Files</span>
74
105
  <ul>
75
- ${files}
106
+ {{files}}
76
107
  </ul>
77
108
  </div>
78
109
  <div class="col">
@@ -81,9 +112,41 @@
81
112
  </svg>
82
113
  <span class="title">Directories</span>
83
114
  <ul>
84
- ${directories}
115
+ {{directories}}
85
116
  </ul>
86
117
  </div>
87
118
  </div>
119
+ <div class="row footer">
120
+ <div class="col">
121
+ <p>
122
+ <i>
123
+ Node Simple Server (NSS) {{version}} Server
124
+ <script type="text/javascript">
125
+ document.write(`${document.location.protocol}//${document.location.hostname}`);
126
+ </script>
127
+ Port
128
+ <script type="text/javascript">
129
+ document.write(document.location.port);
130
+ </script>
131
+ </i>
132
+ </p>
133
+ </div>
134
+ </div>
135
+ <script>
136
+ (function fixLinks() {
137
+ let base = document.location.href;
138
+ if (base[base.length - 1] == '/') {
139
+ base = base.substring(0, base.length - 1);
140
+ }
141
+ const links = document.querySelectorAll('a');
142
+ links.forEach((link) => {
143
+ const href = link.getAttribute('href');
144
+ if (href.slice(0, 3) === '[b]') {
145
+ link.href = `${base + href.substring(3)}`;
146
+ }
147
+ });
148
+ })();
149
+ </script>
150
+ {{live_reload}}
88
151
  </body>
89
152
  </html>
@@ -0,0 +1,43 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <!-- This is NSS's forbidden page. -->
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>403 Forbidden</title>
8
+ <style>
9
+ html,
10
+ body {
11
+ margin: 0;
12
+ padding: 0;
13
+ }
14
+ body {
15
+ padding: 15px;
16
+ }
17
+ </style>
18
+ </head>
19
+ <body>
20
+ <h1>403 Forbidden</h1>
21
+ <p>
22
+ You don't have permission to access
23
+ <script type="text/javascript">
24
+ document.write(document.location.pathname);
25
+ </script>
26
+ on this server.
27
+ </p>
28
+ <hr>
29
+ <p>
30
+ <i>
31
+ Node Simple Server (NSS) {{version}} Server
32
+ <script type="text/javascript">
33
+ document.write(`${document.location.protocol}//${document.location.hostname}`);
34
+ </script>
35
+ port
36
+ <script type="text/javascript">
37
+ document.write(document.location.port);
38
+ </script>
39
+ </i>
40
+ </p>
41
+ {{live_reload}}
42
+ </body>
43
+ </html>
@@ -4,6 +4,7 @@
4
4
  * what content its serving, its up to the browser to do the rest of the work.
5
5
  */
6
6
  const contentTypes = {
7
+ '': 'application/octet-stream',
7
8
  '.aac': 'audio/aac',
8
9
  '.avi': 'video/x-msvideo',
9
10
  '.bmp': 'image/bmp',
@@ -15,6 +16,7 @@ const contentTypes = {
15
16
  '.epub': 'application/epub+zip',
16
17
  '.gif': 'image/gif',
17
18
  '.gz': 'application/gzip',
19
+ '.htm': 'text/html',
18
20
  '.html': 'text/html',
19
21
  '.ico': 'image/x-icon',
20
22
  '.jpeg': 'image/jpeg',
@@ -60,4 +62,4 @@ const contentTypes = {
60
62
  '.zip': 'application/zip'
61
63
  };
62
64
 
63
- module.exports = contentTypes;
65
+ export default contentTypes;
@@ -4,10 +4,11 @@
4
4
  const HTTPStatus = {
5
5
  continue: 100,
6
6
  ok: 200,
7
+ noContent: 204,
7
8
  found: 302,
8
9
  forbidden: 403,
9
10
  notFound: 404,
10
11
  error: 500
11
12
  };
12
13
 
13
- module.exports = HTTPStatus;
14
+ export default HTTPStatus;
@@ -0,0 +1,209 @@
1
+ <!-- Code injected by Node Simple Server for live reloading. -->
2
+ <style>
3
+ #NSS_WS{display:flex;justify-content:center;align-items:center;position:fixed;right:15px;bottom:15px;z-index:9000;width:80px;height:45px;border-radius:25px;background:#000}#NSS_WS svg{margin-left:5px;fill:#ffffff}#NSS_WS span{display:inline-block;margin-right:5px;font-family:monospace;font-size:22px;color:#fff}
4
+ </style>
5
+ <script type="text/javascript">
6
+ // <![CDATA[
7
+ if ("WebSocket" in window) {
8
+ NSS_WS = (() => {
9
+ /** Handles showing the user the server disconnected and attempts to reconnect */
10
+ const disconnected = () => {
11
+ const icon = '<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd"><path d="M6.817 5.084l-2.057-2.937 1.639-1.147 14.601 20.853-1.638 1.147-1.401-2h-12.461c-3.037 0-5.5-2.463-5.5-5.5 0-2.702 1.951-4.945 4.521-5.408.105-1.965.965-3.73 2.296-5.008zm3.032-1.771c.681-.204 1.403-.313 2.151-.313 4.006 0 7.267 3.141 7.479 7.092 2.57.463 4.521 2.706 4.521 5.408 0 1.898-.962 3.571-2.424 4.56l-1.149-1.64c.947-.627 1.573-1.702 1.573-2.92 0-2.969-2.688-3.766-4.433-3.72.324-3.983-2.114-6.78-5.567-6.78-.317 0-.624.023-.922.068l-1.229-1.755zm-4.349 15.687h11.061l-8.595-12.274c-1.038 1.169-1.618 2.895-1.533 5.054-1.954-.113-4.433.923-4.433 3.72 0 1.93 1.57 3.5 3.5 3.5z"></path></svg>';
12
+ // Create the icon container.
13
+ let div = document.createElement("DIV");
14
+ div.id = "NSS_WS";
15
+ div.innerHTML = '<span id="NSS_WS_RETRY">02</span>' + icon;
16
+ // Add the container to the page.
17
+ if (document.body.firstChild) {
18
+ document.body.insertBefore(div, document.body.firstChild);
19
+ } else {
20
+ document.body.appendChild(div);
21
+ }
22
+ // Keep updating the reconnect timer.
23
+ counterInterval = setInterval(function () {
24
+ var counter = document.getElementById("NSS_WS_RETRY");
25
+ var seconds = parseInt(counter.innerHTML) - 1;
26
+ if (seconds <= 0) {
27
+ seconds = 3000 * restartAttempts * 0.001;
28
+ }
29
+ if (seconds < 10) {
30
+ seconds = `0${seconds}`;
31
+ }
32
+ counter.innerHTML = seconds;
33
+ }, 1000);
34
+ };
35
+
36
+ /** Expose the random page ID that was assigned to this page. */
37
+ const getId = () => {
38
+ return pageId;
39
+ };
40
+
41
+ /** Reload the pages stylesheets. */
42
+ const refreshCSS = () => {
43
+ const sheets = [].slice.call(document.getElementsByTagName("link"));
44
+ const head = document.getElementsByTagName("head")[0];
45
+ for (let i = 0; i < sheets.length; i++) {
46
+ const elem = sheets[i];
47
+ const parent = elem.parentElement || head;
48
+ parent.removeChild(elem);
49
+ const rel = elem.rel;
50
+ if ((elem.href && typeof rel != "string") || rel.length == 0 || rel.toLowerCase() == "stylesheet") {
51
+ const url = elem.href.replace(/(&|\?)_cacheOverride=\d+/, "");
52
+ let newUrl = url + (url.indexOf("?") >= 0 ? "&" : "?");
53
+ newUrl += "_cacheOverride=" + new Date().valueOf();
54
+ elem.href = newUrl;
55
+ }
56
+ parent.appendChild(elem);
57
+ }
58
+ };
59
+
60
+ /** Register a function or functions to call when this page receives a WebSocket message. */
61
+ const registerCallback = (func) => {
62
+ if (whatIs(func) === "function") {
63
+ callbacks.push(func);
64
+ }
65
+ };
66
+
67
+ /** Attempt to reestablish a connection to the WebSocket server; used with disconnected(). */
68
+ const restart = () => {
69
+ const http = new XMLHttpRequest();
70
+ http.onerror = function (e) {
71
+ // Ignore the error in browsers that respect that.
72
+ };
73
+ // Attempt a reconnect.
74
+ http.onreadystatechange = () => {
75
+ if (http.readyState === 4) {
76
+ if (http.status >= 200 && http.status < 400) {
77
+ window.location.reload();
78
+ }
79
+ }
80
+ };
81
+ http.open("GET", window.location.href);
82
+ http.send();
83
+ // Keep attempting to reconnect unless told not to.
84
+ if (autoRestart) {
85
+ if (restartAttempts < 10) {
86
+ restartAttempts += 1;
87
+ }
88
+ clearInterval(restartInterval);
89
+ restartInterval = setInterval(restart, 3000 * restartAttempts);
90
+ }
91
+ };
92
+
93
+ /** Send a WebSocket message to the WebSocket server. */
94
+ const send = (message) => {
95
+ if (ready && socket.readyState === WebSocket.OPEN) {
96
+ socket.send(JSON.stringify({
97
+ data: message,
98
+ type: whatIs(message)
99
+ }));
100
+ return;
101
+ }
102
+ console.warn("Node Simple Server: The WebSocket is not ready or the connection was closed.");
103
+ };
104
+
105
+ /** Generate a random unique ID for this page; will be registered in the back-end. */
106
+ const uid = () => {
107
+ return Math.random().toString(16).slice(2);
108
+ };
109
+
110
+ /** Remove a callback function previously registered with registerCallback(). */
111
+ const unregisterCallback = (func) => {
112
+ for (let i = 0; i < callbacks.length; i++) {
113
+ if (callbacks[i] == func) {
114
+ callbacks.splice(i, 1);
115
+ }
116
+ }
117
+ };
118
+
119
+ /**
120
+ * The fastest way to get the actual type of anything in JavaScript.
121
+ *
122
+ * {@link https://jsbench.me/ruks9jljcu/2 | See benchmarks}.
123
+ *
124
+ * @param {*} unknown Anything you wish to check the type of.
125
+ * @return {string|undefined} The type in lowercase of the unknown value passed in or undefined.
126
+ */
127
+ const whatIs = (unknown) => {
128
+ try {
129
+ return ({}).toString.call(unknown).match(/\s([^\]]+)/)[1].toLowerCase();
130
+ } catch (e) { return undefined; }
131
+ };
132
+
133
+ // NSS_WS internal global variables.
134
+ let autoRestart = true;
135
+ const callbacks = [];
136
+ let counterInterval = null;
137
+ const pageId = uid();
138
+ let ready = false;
139
+ let restartAttempts = 0;
140
+ let restartInterval = null;
141
+
142
+ // Socket specific variables.
143
+ const protocol = window.location.protocol === "http:" ? "ws://" : "wss://";
144
+ const address = protocol + window.location.host + window.location.pathname + "/ws?id=" + pageId;
145
+ const socket = new WebSocket(address);
146
+
147
+ // Respond to messages the socket receives.
148
+ socket.onmessage = (evt) => {
149
+ const msgObj = JSON.parse(evt.data); // NSS uses a standard messaging object.
150
+ switch (msgObj.message) {
151
+ case "close":
152
+ ready = false;
153
+ break;
154
+ case "disableAutoRestart":
155
+ autoRestart = false;
156
+ clearInterval(restartInterval);
157
+ clearInterval(counterInterval);
158
+ console.log("Node Simple Server: Auto restart disabled, manually refresh page if server disconnects.");
159
+ break;
160
+ case 'ping':
161
+ send('pong');
162
+ break;
163
+ case "refreshCSS":
164
+ refreshCSS();
165
+ break;
166
+ case "reload":
167
+ window.location.reload();
168
+ break;
169
+ default:
170
+ if (callbacks.length > 0) {
171
+ for (let i = 0; i < callbacks.length; i++) {
172
+ callbacks[i](msgObj);
173
+ }
174
+ return;
175
+ }
176
+ console.log(`Received from WebSocket: ${msgObj.data}`);
177
+ }
178
+ };
179
+
180
+ // Mark the script as not ready when the websocket connection is closed.
181
+ socket.addEventListener("close", () => {
182
+ ready = false;
183
+ console.warn("Node Simple Server: Connection closed, live reload disabled.");
184
+ if (autoRestart) {
185
+ disconnected();
186
+ restartInterval = setInterval(restart, 3000);
187
+ }
188
+ });
189
+
190
+ // Mark the script as ready when a websocket connection is established.
191
+ socket.addEventListener("open", () => {
192
+ console.log("Node Simple Server: Live reload enabled.");
193
+ ready = true;
194
+ });
195
+
196
+ // Expose some of NSS_WS methods.
197
+ return {
198
+ getId,
199
+ registerCallback,
200
+ send,
201
+ unregisterCallback,
202
+ whatIs
203
+ };
204
+ })();
205
+ } else {
206
+ console.error("Node Simple Server: This Browser does NOT support WebSocket for live reloading.");
207
+ }
208
+ // ]]>
209
+ </script>
@@ -0,0 +1,43 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <!-- This is NSS's 404 page. -->
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>404 Not Found</title>
8
+ <style>
9
+ html,
10
+ body {
11
+ margin: 0;
12
+ padding: 0;
13
+ }
14
+ body {
15
+ padding: 15px;
16
+ }
17
+ </style>
18
+ </head>
19
+ <body>
20
+ <h1>404 Not Found</h1>
21
+ <p>
22
+ The requested URL
23
+ <script type="text/javascript">
24
+ document.write(document.location.pathname);
25
+ </script>
26
+ was not found on this server.
27
+ </p>
28
+ <hr>
29
+ <p>
30
+ <i>
31
+ Node Simple Server (NSS) {{version}} Server
32
+ <script type="text/javascript">
33
+ document.write(`${document.location.protocol}//${document.location.hostname}`);
34
+ </script>
35
+ Port
36
+ <script type="text/javascript">
37
+ document.write(document.location.port);
38
+ </script>
39
+ </i>
40
+ </p>
41
+ {{live_reload}}
42
+ </body>
43
+ </html>
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "@caboodle-tech/node-simple-server",
3
- "version": "1.3.1",
4
- "description": "Node Simple Server (NSS): A small but effective node based server for development sites and self controlled live reloading.",
5
- "main": "server.js",
3
+ "version": "2.0.0",
4
+ "description": "Node Simple Server (NSS): A small but effective node based server for development sites, customizable live reloading, and websocket support built-in.",
5
+ "main": "bin/nss.js",
6
6
  "scripts": {
7
7
  "test": "echo \"Error: no test specified\" && exit 1",
8
- "start": "node server.js"
8
+ "example": "node ./examples/run.js",
9
+ "example:website": "node ./examples/run.js website",
10
+ "example:websocket": "node ./examples/run.js websocket"
9
11
  },
12
+ "type": "module",
10
13
  "repository": {
11
14
  "type": "git",
12
15
  "url": "git+https://github.com/caboodle-tech/node-simple-server.git"
@@ -25,18 +28,18 @@
25
28
  "contributors": [
26
29
  "Caboodle Tech Inc (https://github.com/caboodle-tech)"
27
30
  ],
28
- "license": "MIT",
31
+ "license": "Common Clause with MIT",
29
32
  "bugs": {
30
33
  "url": "https://github.com/caboodle-tech/node-simple-server/issues"
31
34
  },
32
35
  "homepage": "https://github.com/caboodle-tech/node-simple-server#readme",
33
36
  "dependencies": {
34
- "chokidar": "^3.5.2",
35
- "ws": "^8.2.3"
37
+ "chokidar": "^3.5.3",
38
+ "ws": "^8.13.0"
36
39
  },
37
40
  "devDependencies": {
38
- "eslint": "^7.32.0",
39
- "eslint-config-airbnb-base": "^14.2.1",
41
+ "eslint": "^8.2.0",
42
+ "eslint-config-airbnb-base": "^15.0.0",
40
43
  "eslint-plugin-import": "^2.25.2"
41
44
  }
42
- }
45
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2021 Caboodle Tech Inc
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.