-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
executable file
·200 lines (186 loc) · 7.42 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#!/usr/bin/env node
/**
* Falsy Values: false, 0, "", null, undefined, NaN
* Command Line Usage (Local): node [path/to/]index.js
* Command Line Usage (Global): coast
*/
/**
* Walk a file system directory, and callback at every file.
*
* @param dir {String} representing a file system directory.
* @example "/"
* @param callback {Function} with a single 'file' path argument.
* @example function (file) { var name = require('path').basename(file); }
* @return {Undefined}
*/
function walkDirByFileSync(dir, callback) {
if (typeof dir === 'string' && typeof callback === 'function') {
try {
var fs = require('fs'),
path = require('path'),
stat = fs.lstatSync(dir);
if (stat.isFile()) {
callback(dir);
} else if (stat.isDirectory()) {
try {
fs.readdirSync(dir).forEach(function (sub) {
walkDirByFileSync(path.join(dir, sub), callback);
});
} catch (error) {
console.error(new Date().toUTCString() + ' caughtException:', error.message);
console.error(error.stack);
}
}
} catch (error) {
console.error(new Date().toUTCString() + ' caughtException:', error.message);
console.error(error.stack);
}
}
}
/**
* Walk a file system directory, and require file by name.
*
* @param dir {String} representing a file system directory.
* @example "/"
* @param name {String} representing the name of the file.
* @example "name.js"
* @return {Object}
* @example module.exports
* @example null
*/
function requireFileByName(dir, name) {
var handler = null;
if (typeof dir === 'string' && typeof name === 'string') {
walkDirByFileSync(dir, function (file) {
var path = require('path');
if (path.basename(file) === name) {
try {
handler = require(file);
} catch (error) {
handler = null;
}
}
});
}
return handler;
}
/**
* Look for api.json in cwd.
*/
var lostFile = true;
walkDirByFileSync(process.cwd(), function (file) {
var path = require('path');
if (path.basename(file) === 'api.json') {
/**
* Found api.json in cwd.
* Require Hypermedia as a module to use exported middleware in a server.
* Read and minify Hypermedia as a text file to use exported client in a browser.
*/
var fs = require('fs'),
hypermedia = require('hypermedia'),
uglify = require('uglify-js'),
clientPath = require.resolve('hypermedia'),
readClient = fs.readFileSync(clientPath, 'utf-8'),
miniClient = uglify.minify(clientPath).code,
api = hypermedia.Api.createFromFile(file);
lostFile = false;
if (api.protocol === 'http') {
var express = require('express'),
app = express(),
lostMiddleware = [];
/**
* Default API route handler that acknowledges requests with a 200 OK.
*
* @param req {IncomingMessage} object created by http.Server.
* @see http://nodejs.org/api/http.html#http_http_incomingmessage
* @param res {ServerResponse} object created by http.Server.
* @see http://nodejs.org/api/http.html#http_class_http_serverresponse
* @param next {Function} middleware in the pipeline.
* @return {Undefined}
*/
function defaultApi(req, res, next) {
res.statusCode = 200;
return next();
}
/**
* Default middleware handler that does nothing.
*
* @param req {IncomingMessage} object created by http.Server.
* @see http://nodejs.org/api/http.html#http_http_incomingmessage
* @param res {ServerResponse} object created by http.Server.
* @see http://nodejs.org/api/http.html#http_class_http_serverresponse
* @param next {Function} middleware in the pipeline.
* @return {Undefined}
*/
function defaultMid(req, res, next) {
return next();
}
/**
* Create API with middleware/route stack:
* 1. Incoming Message CORS Middleware (Creates req.cors based on http.IncomingMessage.)
* 2. a. Client User Agent API (Returns API client from GET /ua.)
* b. Custom api.json Middleware/APIs
* 3. Outgoing Message Body Middleware (Modifies res.body to String.)
* 4. Outgoing Message Hypermedia Middleware (Modifies res.body based on res.hype.)
* 5. Outgoing Message CORS Middleware (Modifies http.ServerResponse based on req.cors.)
* 6. Outgoing Message Final Middleware (Sends final res.body to client.)
*/
app
.disable('x-powered-by')
.use(function (req, res, next) {
api.incomingMessageCors(req, res);
return next();
})
.get('/ua', function (req, res, next) {
res.statusCode = 200;
res.setHeader('content-type', 'application/javascript');
res.body = api.debug ? readClient : miniClient;
return next();
});
/**
* Look for [routine.id].js in cwd.
* Use custom middleware and APIs when found; default, otherwise.
*/
api.each(function (routine, type) {
var handler = requireFileByName(process.cwd(), routine.id + '.js'),
lostFile = typeof handler !== 'function';
if (lostFile) {
lostMiddleware.push(routine.id);
}
if (type === 'mid') {
app.use(lostFile ? defaultMid : handler);
}
if (type === 'api') {
app[routine.method.toLowerCase()](routine.uri, lostFile ? defaultApi : handler);
}
});
if (lostMiddleware.length > 0) {
console.log('A handler file was not found for ' + lostMiddleware.join(', ') + '.');
}
app
.use(function (req, res, next) {
api.outgoingMessageBody(req, res);
return next();
})
.use(function (req, res, next) {
api.outgoingMessageHypermedia(req, res);
return next();
})
.use(function (req, res, next) {
api.outgoingMessageCors(req, res);
return next();
})
.use(function (req, res) {
api.outgoingMessageFinal(req, res);
})
.listen(api.port, api.hostname, function () {
console.log('A server is accepting connections on ' + api.hostname + ':' + api.port + '.');
});
} else {
console.log('A transfer protocol other than HTTP is not currently supported in api.json.');
}
}
});
if (lostFile) {
console.log('A file called api.json was not found within the current working directory.');
}