-
Notifications
You must be signed in to change notification settings - Fork 3
/
alexa-app-router.js
170 lines (135 loc) · 4.59 KB
/
alexa-app-router.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
var url = require('url');
var router = {};
router.addRouter = function(app, config, intents, routes) {
if (!app) return;
app.$$launch = app.launch;
app.launch = launchHandler;
app.$$intent = app.intent;
app.intent = intentHandler;
app.$$routeConfig = config || {};
app.$$routeIntents = intents || {};
app.$$routeList = routes || {};
registerIntents();
registerRoutes();
registerDefaultIntents();
function registerIntents() {
if (typeof app.$$routeIntents !== 'object') return;
for (var name in app.$$routeIntents) {
if (app.$$routeIntents[name]) {
app.intent(name, app.$$routeIntents[name], noop);
} else {
app.intent(name, noop);
}
}
}
function registerRoutes() {
if (typeof app.$$routeConfig.pre === 'function') {
app.pre = app.$$routeConfig.pre;
}
if (typeof app.$$routeConfig.post === 'function') {
app.post = app.$$routeConfig.post;
}
if (typeof app.$$routeConfig.launch === 'function') {
app.launch(app.$$routeConfig.launch);
}
}
function registerDefaultIntents() {
var defaultRoutes = app.$$routeConfig && app.$$routeConfig.defaultRoutes || {};
for (var intentName in defaultRoutes) {
if (!app.intents[intentName]) {
app.intent(intentName, noop);
}
}
}
function launchHandler(handler) {
app.$$launch(function(request, response) {
response.route = function(nextRoutes) {
response
.shouldEndSession(false)
.session('route', nextRoutes);
return response;
};
return handler.apply(null, arguments);
});
}
function intentHandler(name, config, handler) {
if (typeof config === 'function') {
handler = config;
config = null;
}
app.$$intent(name, config, function(request, response) {
// Set up route handler on response.
response.route = function(nextRouteName) {
response
.shouldEndSession(false)
.session('route', nextRouteName);
return response;
};
// Get current route set last session, if available
var routes;
routes = request.session('route') || {};
response.session('route', null);
var routeUrl = routes[name] || routes['default'] || getDefaultRoute(name);
request.route = parseRoute(routeUrl, app.$$routeList);
var routeHandler = request.route && app.$$routeList[request.route.route];
if (typeof routeHandler === 'function') {
// Call route handler
return routeHandler.apply(null, arguments);
} else {
throw new Error('No handler for ' + name + ' at route ' + request.route.route);
}
});
}
function getDefaultRoute(name) {
return app.$$routeConfig && app.$$routeConfig.defaultRoutes && app.$$routeConfig.defaultRoutes[name];
}
function parseRoute(route, routes) {
route = route || '';
routes = routes || {};
var parsedUrl = url.parse(route, true);
if (!parsedUrl || typeof parsedUrl.pathname !== 'string') return null;
var parts = parsedUrl.pathname.split('/');
var analyzedRoutes = Object.keys(routes)
.map(function(target) {
var targetParts = target.split('/');
if (targetParts.length !== parts.length) return null;
// Match target parts against route parts
var params = {};
var specificity = 0;
targetParts.forEach(function(part, index) {
// Specificity determines how exact the match is, with a 1 for exact match, 0 for url param.
// The total specificity is a binary number with each entry matching a segment from left to right.
specificity *= 2;
var regex = /^{([a-zA-Z_$][0-9a-zA-Z_$]*)}$/.exec(targetParts[index]);
var urlParam = regex && regex[1];
if (targetParts[index] === parts[index]) {
// A segment was an exact match
specificity++;
} else if (urlParam) {
// A segment was a fuzzy (url parameter) match
params[urlParam] = parts[index];
} else {
// A segment didn't match
return null;
}
});
return {
params: params,
route: target,
specificity: specificity
};
})
.filter(function(r) { return r; })
.sort(function(a, b) { return b.specificity - a.specificity; });
if (!analyzedRoutes.length) return null;
return {
params: analyzedRoutes[0].params,
query: parsedUrl.query,
route: analyzedRoutes[0].route,
url: route
};
}
function noop(request, response) {}
return app;
};
module.exports = router;