3
3
/**
4
4
* Lightweight web framework for your serverless applications
5
5
* @author Jeremy Daly <[email protected] >
6
- * @version 0.3 .0
6
+ * @version 0.5 .0
7
7
* @license MIT
8
8
*/
9
9
10
- const REQUEST = require ( './request.js' ) // Response object
11
- const RESPONSE = require ( './response.js' ) // Response object
12
- const Promise = require ( 'bluebird' ) // Promise library
10
+ const REQUEST = require ( './lib/request.js' ) // Response object
11
+ const RESPONSE = require ( './lib/response.js' ) // Response object
13
12
14
13
// Create the API class
15
14
class API {
@@ -51,47 +50,24 @@ class API {
51
50
// Executed after the callback
52
51
this . _finally = ( ) => { }
53
52
54
- // Promise placeholder for final route promise resolution
55
- this . _promise = function ( ) { console . log ( 'no promise to resolve' ) }
56
- this . _reject = function ( ) { console . log ( 'no promise to reject' ) }
57
-
58
53
// Global error status
59
54
this . _errorStatus = 500
60
55
61
- // Testing flag
56
+ // Testing flag (disables logging)
62
57
this . _test = false
63
58
64
59
} // end constructor
65
60
66
- // GET: convenience method
67
- get ( path , handler ) {
68
- this . METHOD ( 'GET' , path , handler )
69
- }
70
61
71
- // POST: convenience method
72
- post ( path , handler ) {
73
- this . METHOD ( 'POST' , path , handler )
74
- }
75
62
76
- // PUT: convenience method
77
- put ( path , handler ) {
78
- this . METHOD ( 'PUT' , path , handler )
79
- }
63
+ // Convenience methods (path, handler)
64
+ get ( p , h ) { this . METHOD ( 'GET' , p , h ) }
65
+ post ( p , h ) { this . METHOD ( 'POST' , p , h ) }
66
+ put ( p , h ) { this . METHOD ( 'PUT' , p , h ) }
67
+ patch ( p , h ) { this . METHOD ( 'PATCH' , p , h ) }
68
+ delete ( p , h ) { this . METHOD ( 'DELETE' , p , h ) }
69
+ options ( p , h ) { this . METHOD ( 'OPTIONS' , p , h ) }
80
70
81
- // PATCH: convenience method
82
- patch ( path , handler ) {
83
- this . METHOD ( 'PATCH' , path , handler )
84
- }
85
-
86
- // DELETE: convenience method
87
- delete ( path , handler ) {
88
- this . METHOD ( 'DELETE' , path , handler )
89
- }
90
-
91
- // OPTIONS: convenience method
92
- options ( path , handler ) {
93
- this . METHOD ( 'OPTIONS' , path , handler )
94
- }
95
71
96
72
// METHOD: Adds method and handler to routes
97
73
METHOD ( method , path , handler ) {
@@ -124,112 +100,88 @@ class API {
124
100
this . _routes ,
125
101
( i === route . length - 1 ? { [ '__' + method . toUpperCase ( ) ] : { vars : pathVars , handler : handler , route : '/' + parsedPath . join ( '/' ) } } : { } ) ,
126
102
route . slice ( 0 , i + 1 )
127
- ) ;
103
+ )
128
104
129
105
} // end for loop
130
106
131
107
} // end main METHOD function
132
108
133
109
134
- // RUN: This runs the routes
135
- run ( event , context , cb ) { // TODO: Make this dynamic
136
-
137
- this . startTimer ( 'total' )
138
110
139
- this . _done = false
111
+ // RUN: This runs the routes
112
+ async run ( event , context , cb ) {
140
113
141
114
// Set the event, context and callback
142
115
this . _event = event
143
116
this . _context = context
144
117
this . _cb = cb
145
118
146
- // Initalize response object
147
- let response = new RESPONSE ( this )
148
- let request = { }
119
+ try {
120
+ // Initalize response and request objects
121
+ this . response = new RESPONSE ( this )
122
+ this . request = new REQUEST ( this )
149
123
150
- Promise . try ( ( ) => { // Start a promise
124
+ // Loop through the middleware and await response
125
+ for ( const mw of this . _middleware ) {
126
+ await new Promise ( r => { mw ( this . request , this . response , ( ) => { r ( ) } ) } )
127
+ } // end for
151
128
152
- // Initalize the request object
153
- request = new REQUEST ( this )
129
+ // Execute the primary handler
130
+ await this . handler ( this . request , this . response )
154
131
155
- // Execute the request
156
- return this . execute ( request , response )
132
+ } catch ( e ) {
133
+ this . catchErrors ( e )
134
+ }
157
135
158
- } ) . catch ( ( e ) => {
136
+ } // end run function
159
137
160
- // Error messages should never be base64 encoded
161
- response . _isBase64 = false
162
138
163
- // Strip the headers (TODO: find a better way to handle this)
164
- response . _headers = { }
165
139
166
- let message ;
140
+ // Catch all async/sync errors
141
+ async catchErrors ( e ) {
167
142
168
- if ( e instanceof Error ) {
169
- response . status ( this . _errorStatus )
170
- message = e . message
171
- ! this . _test && console . log ( e )
172
- } else {
173
- message = e
174
- ! this . _test && console . log ( 'API Error:' , e )
175
- }
143
+ // Error messages should never be base64 encoded
144
+ this . response . _isBase64 = false
176
145
177
- // Execute error middleware
178
- if ( this . _errors . length > 0 ) {
179
-
180
- // Init stack queue
181
- let queue = [ ]
182
-
183
- // Loop through the middleware and queue promises
184
- for ( let i in this . _errors ) {
185
- queue . push ( ( ) => {
186
- return new Promise ( ( resolve , reject ) => {
187
- this . _promise = ( ) => { resolve ( ) } // keep track of the last resolve()
188
- this . _reject = ( e ) => { reject ( e ) } // keep track of the last reject()
189
- this . _errors [ i ] ( e , request , response , ( ) => { resolve ( ) } ) // execute the errors with the resolve callback
190
- } ) // end promise
191
- } ) // end queue
192
- } // end for
193
-
194
- // Return Promise.each serialially
195
- return Promise . each ( queue , function ( queue_item ) {
196
- return queue_item ( )
197
- } ) . then ( ( ) => {
198
- response . json ( { 'error' :message } )
199
- } )
146
+ // Strip the headers (TODO: find a better way to handle this)
147
+ this . response . _headers = { }
200
148
201
- } else {
202
- response . json ( { 'error' :message } )
203
- }
149
+ let message ;
204
150
205
- } ) . finally ( ( ) => {
206
- this . _finally ( request , response )
207
- } )
208
- } // end run function
151
+ if ( e instanceof Error ) {
152
+ this . response . status ( this . _errorStatus )
153
+ message = e . message
154
+ ! this . _test && console . log ( e )
155
+ } else {
156
+ message = e
157
+ ! this . _test && console . log ( 'API Error:' , e )
158
+ }
209
159
160
+ // Execute error middleware
161
+ for ( const err of this . _errors ) {
162
+ // Promisify error middleware
163
+ await new Promise ( r => { err ( e , this . request , this . response , ( ) => { r ( ) } ) } )
164
+ } // end for
210
165
211
- // Custom callback
212
- _callback ( err , res ) {
166
+ this . response . json ( { 'error' :message } )
213
167
214
- // Resolve any outstanding promise
215
- this . _promise ( )
168
+ } // end catch
216
169
217
- this . _done = true
218
170
219
- this . endTimer ( 'total' )
220
171
221
- if ( res ) {
222
- if ( this . _debug ) {
223
- console . log ( this . _procTimes )
224
- }
225
- }
172
+ // Custom callback
173
+ async _callback ( err , res ) {
174
+
175
+ // Execute finally
176
+ await this . _finally ( this . request , this . response )
226
177
227
178
// Execute the primary callback
228
179
this . _cb ( err , res )
229
180
230
181
} // end _callback
231
182
232
183
184
+
233
185
// Middleware handler
234
186
use ( fn ) {
235
187
if ( fn . length === 3 ) {
@@ -241,79 +193,12 @@ class API {
241
193
}
242
194
} // end use
243
195
244
- // Finally function
196
+
197
+ // Finally handler
245
198
finally ( fn ) {
246
199
this . _finally = fn
247
200
}
248
201
249
- // Process
250
- execute ( req , res ) {
251
-
252
- // Init stack queue
253
- let queue = [ ]
254
-
255
- // If execute is called after the app is done, just return out
256
- if ( this . _done ) { return ; }
257
-
258
- // If there is middleware
259
- if ( this . _middleware . length > 0 ) {
260
- // Loop through the middleware and queue promises
261
- for ( let i in this . _middleware ) {
262
- queue . push ( ( ) => {
263
- return new Promise ( ( resolve , reject ) => {
264
- this . _promise = ( ) => { resolve ( ) } // keep track of the last resolve()
265
- this . _reject = ( e ) => { reject ( e ) } // keep track of the last reject()
266
- this . _middleware [ i ] ( req , res , ( ) => { resolve ( ) } ) // execute the middleware with the resolve callback
267
- } ) // end promise
268
- } ) // end queue
269
- } // end for
270
- } // end if
271
-
272
- // Push the main execution path to the queue stack
273
- queue . push ( ( ) => {
274
- return new Promise ( ( resolve , reject ) => {
275
- this . _promise = ( ) => { resolve ( ) } // keep track of the last resolve()
276
- this . _reject = ( e ) => { reject ( e ) } // keep track of the last reject()
277
- this . handler ( req , res ) // execute the handler with no callback
278
- } )
279
- } )
280
-
281
- // Return Promise.each serialially
282
- return Promise . each ( queue , function ( queue_item ) {
283
- return queue_item ( )
284
- } )
285
-
286
- } // end execute
287
-
288
-
289
-
290
- //-------------------------------------------------------------------------//
291
- // TIMER FUNCTIONS
292
- //-------------------------------------------------------------------------//
293
-
294
- // Returns the calculated processing times from all stopped timers
295
- getTimers ( timer ) {
296
- if ( timer ) {
297
- return this . _procTimes [ timer ]
298
- } else {
299
- return this . _procTimes
300
- }
301
- } // end getTimers
302
-
303
- // Starts a timer for debugging purposes
304
- startTimer ( name ) {
305
- this . _timers [ name ] = Date . now ( )
306
- } // end startTimer
307
-
308
- // Ends a timer and calculates the total processing time
309
- endTimer ( name ) {
310
- try {
311
- this . _procTimes [ name ] = ( Date . now ( ) - this . _timers [ name ] ) + ' ms'
312
- delete this . _timers [ name ]
313
- } catch ( e ) {
314
- console . error ( 'Could not end timer: ' + name )
315
- }
316
- } // end endTimer
317
202
318
203
319
204
//-------------------------------------------------------------------------//
@@ -324,20 +209,20 @@ class API {
324
209
return path . trim ( ) . replace ( / ^ \/ ( .* ?) ( \/ ) * $ / , '$1' ) . split ( '/' ) . filter ( x => x . trim ( ) !== '' )
325
210
}
326
211
327
-
212
+ // Recursive function to create routes object
328
213
setRoute ( obj , value , path ) {
329
214
if ( typeof path === "string" ) {
330
215
let path = path . split ( '.' )
331
216
}
332
217
333
218
if ( path . length > 1 ) {
334
219
let p = path . shift ( )
335
- if ( obj [ p ] === null ) { // || typeof obj[p] !== 'object') {
220
+ if ( obj [ p ] === null ) {
336
221
obj [ p ] = { }
337
222
}
338
223
this . setRoute ( obj [ p ] , value , path )
339
224
} else {
340
- if ( obj [ path [ 0 ] ] === null ) { // || typeof obj[path[0]] !== 'object') {
225
+ if ( obj [ path [ 0 ] ] === null ) {
341
226
obj [ path [ 0 ] ] = value
342
227
} else {
343
228
obj [ path [ 0 ] ] = Object . assign ( value , obj [ path [ 0 ] ] )
@@ -386,7 +271,9 @@ class API {
386
271
387
272
} // end register
388
273
274
+
389
275
} // end API class
390
276
391
- // Export the API class
277
+
278
+ // Export the API class as a new instance
392
279
module . exports = opts => new API ( opts )
0 commit comments