| 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 | 1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
2×
1×
1×
2×
2×
1×
2×
2×
2×
2×
8×
2×
1×
11×
2×
6×
6×
1×
6×
1×
6×
6×
6×
1×
1×
2×
2×
1×
1×
2×
2×
1×
| const d = require("describe-property");
const RoutingProperties = require("../utils/RoutingProperties");
const createMapper = require("./mapper");
const createRouter = require("./router");
function mapperCreator(mappings) {
return function (app) {
app = createMapper(app);
for (let i = 0, len = mappings.length; i < len; ++i) {
app.map.apply(app, mappings[i]);
}
return app;
};
}
function routerCreator(routes) {
return function (app) {
app = createRouter(app);
for (let i = 0, len = routes.length; i < len; ++i) {
app.route.apply(app, routes[i]);
}
return app;
};
}
/**
* A middleware that aids in building complex apps that are fronted by other
* middleware in a "middleware stack". Also provides several other useful methods
* for request mapping and routing that make this middleware a good choice when
* working with mach at a high level or when getting started for the first time.
*
* Middleware are placed into the stack by calling the `use` method which passes
* along any additional arguments that it receives directly on to the middleware
* when the stack is compiled.
*
* Other stacks can be "mounted" easily at various locations using the `map`
* method. Routes can be added using `route`, `get`, `post`, etc. When a request
* is received, all middleware, mappings, and routes run in the order they are
* defined in the stack, top to bottom.
*
* let app = mach.stack();
*
* app.use(mach.gzip);
* app.use(mach.file, __dirname + '/public');
*
* // Use an image server to serve requests that begin
* // with /images out of /public/img.
* app.map('/images', mach.file('/public/img'));
*
* // Since this call is *after* the call to map, this middleware
* // will not run when requests begin with "/images".
* app.use(mach.params);
*
* app.get('/', function (request) {
* return "The params are: " + JSON.stringify(request.params);
* });
*
* app.post('/posts/:post_id/messages', function (request) {
* // ...
* });
*
* mach.serve(app);
*
* Note: A stack is compiled the first time it is called. When a stack is
* compiled, all middleware is invoked with the downstream app plus any
* additional arguments that were passed to the call to stack.use. As long as
* the stack doesn't change between requests, this happens only once.
*/
function createStack(app) {
let layers = [], mappings = [], routes = [];
let compiledApp;
function compile(app) {
Iif (routes.length) {
app = routerCreator(routes)(app);
}
Iif (mappings.length) {
app = mapperCreator(mappings)(app);
}
let index = layers.length;
while (index) {
app = layers[--index].call(this, app);
}
return app;
}
function stack(conn) {
return conn.call(compiledApp || (compiledApp = compile(app)));
}
Object.defineProperties(stack, {
/**
* Declares that the given `middleware` should be used at the current point
* in the stack. Any additional arguments to this function are passed along
* to the middleware with the downstream app as the first argument when the
* stack is compiled.
*/
use: d(function (middleware) {
const args = Array.prototype.slice.call(arguments, 1);
if (mappings.length) {
layers.push(mapperCreator(mappings.splice(0, mappings.length)));
}
if (routes.length) {
layers.push(routerCreator(routes.splice(0, routes.length)));
}
layers.push(function (app) {
return middleware.apply(this, [app].concat(args));
});
compiledApp = null;
}),
/**
* Uses a mapper to map a URL path to an app.
*/
map: d(function (location, app) {
mappings.push([location, app]);
compiledApp = null;
}),
/**
* Uses a router to route URLs that match a pattern/method to an app.
*/
route: d(function (pattern, methods, app) {
routes.push([pattern, methods, app]);
compiledApp = null;
}),
/**
* Sets the given app as the default for this stack.
*/
run: d(function (downstreamApp) {
app = downstreamApp;
compiledApp = null;
})
});
Object.defineProperties(stack, RoutingProperties);
return stack;
}
module.exports = createStack;
|