@momsfriendlydevco/cowboy 1.0.4 → 1.0.6

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.
package/lib/cowboy.js CHANGED
@@ -27,6 +27,7 @@ export class Cowboy {
27
27
  * @property {Function} pathTidy Additional tidyup for server request paths, useful if the API does not live at the server root. Defaults to removing a "/api/:worker/" prefix
28
28
  */
29
29
  settings = {
30
+ patchAxios: true,
30
31
  pathTidy(path) {
31
32
  return path.replace(/^\/api\/\w+/, '/');
32
33
  },
@@ -47,6 +48,13 @@ export class Cowboy {
47
48
  routes = [];
48
49
 
49
50
 
51
+ /**
52
+ * Has completed one init() cycle
53
+ * @type {Boolean}
54
+ */
55
+ doneInit = false;
56
+
57
+
50
58
  /**
51
59
  * Queue up a middleware path
52
60
  * All given middleware is called in sequence, if middleware
@@ -70,6 +78,7 @@ export class Cowboy {
70
78
  /**
71
79
  * Prepend middleware which will be used for all routes
72
80
  * @param {CowboyMiddleware} middleware Middleware to use
81
+ * @returns {Cowboy} This chainable Cowboy router instance
73
82
  */
74
83
  use(...middleware) {
75
84
  this.earlyMiddleware.push(middleware);
@@ -101,27 +110,32 @@ export class Cowboy {
101
110
  * @returns {Promise<CowboyResponse>} A promise which will eventually resolve when all middleware completes
102
111
  */
103
112
  async fetch(cfReq, env) {
104
- if (env.COWBOY_DEBUG) {
113
+ this.init();
114
+
115
+ if (env.COWBOY_DEBUG)
105
116
  debug.enabled = true;
106
- debug('Cowboy Worker debugging is enabled');
107
- }
108
117
 
109
118
  // Create basic [req]uest / [res]ponse objects
110
119
  let req = new CowboyRequest(cfReq, {
111
120
  router: this,
112
121
  pathTidy: this.settings.pathTidy,
113
122
  });
123
+ if (cfReq.body) req.body = await cfReq.json();
124
+
114
125
  let res = new CowboyResponse();
126
+ debug('Incoming request:', req.toString());
115
127
 
116
128
  // Exec all earlyMiddleware - every time
117
- debug('Run middleware');
118
- await this.execMiddleware({req, res, middleware: this.earlyMiddleware});
129
+ await this.execMiddleware({
130
+ req, res,
131
+ middleware: this.earlyMiddleware,
132
+ });
119
133
 
120
134
  // Find matching route
121
135
  let route = this.resolve(req);
122
136
  if (!route) {
123
137
  if (debug.enabled) {
124
- debug(`No matching route for "${req.method} ${req.path}"`);
138
+ debug(`No matching route for "${req.toString()}"`);
125
139
  this.routes.forEach((r, i) =>
126
140
  debug(
127
141
  `Route #${i}`,
@@ -134,7 +148,10 @@ export class Cowboy {
134
148
  }
135
149
 
136
150
  // Exec route middleware
137
- let response = await this.execMiddleware({req, res, middleware: route.middleware});
151
+ let response = await this.execMiddleware({
152
+ req, res,
153
+ middleware: route.middleware,
154
+ });
138
155
 
139
156
  if (!response) throw new Error('Middleware chain ended without returning a response!');
140
157
  if (!response.toCloudflareResponse) throw new Error('Eventual middleware chain output should have a .toCloudflareResponse() method');
@@ -143,12 +160,12 @@ export class Cowboy {
143
160
 
144
161
 
145
162
  async execMiddleware({middleware, req, res}) {
146
- let middlewareStack = [...middleware] // Shallow copy middleware stack to execute
163
+ let middlewareStack = middleware
147
164
  .map(m => {
148
165
  let mFunc =
149
166
  typeof m == 'function' ? m // Already a function
150
- : typeof m == 'string' ? CowboyMiddleware[m]() // Lookup from middleware with defaults
151
- : Array.isArray(m) ? CowboyMiddleware[m[0]](...m.slice(1)) // Lookup from middleware with options
167
+ : typeof m == 'string' ? CowboyMiddleware[m].apply(this) // Lookup from middleware with defaults
168
+ : Array.isArray(m) ? CowboyMiddleware[m[0]].apply(this, m.slice(1)) // Lookup from middleware with options
152
169
  : (()=> { throw new Error(`Unknown middleware type "${typeof m}"`) })()
153
170
 
154
171
  if (!mFunc) throw new Error('Cowboy Middleware must be a function, string or Record(name, options)');
@@ -156,7 +173,6 @@ export class Cowboy {
156
173
  });
157
174
 
158
175
  let response; // Response to eventually send
159
- debug('Run middleware stack', middlewareStack);
160
176
  while (middlewareStack.length > 0) {
161
177
  let middleware = middlewareStack.shift();
162
178
  try {
@@ -168,9 +184,19 @@ export class Cowboy {
168
184
  response = res.end(response);
169
185
  }
170
186
  } catch (e) {
171
- debug('Error trace', e.trace);
172
- res.status(500).send(e.toString());
173
- response = null;
187
+ let errorText = typeof e == 'string' ? e : e.toString();
188
+
189
+ debug('Error thrown', errorText);
190
+
191
+ // Form: '404: Not found'
192
+ if (/^(\d{3}):/.test(errorText)) {
193
+ let errorBits = /^(?<status>\d{3}):?(?<text>.*)$/.exec(errorText).groups;
194
+ res.status(errorBits.status).send(errorBits.text);
195
+ } else { // Generic error - assume 400
196
+ res.status(400).send(e.toString());
197
+ }
198
+
199
+ response = res;
174
200
  break;
175
201
  }
176
202
  }
@@ -185,6 +211,23 @@ export class Cowboy {
185
211
  post(path, ...middleware) { return this.route('POST', path, ...middleware) }
186
212
  put(path, ...middleware) { return this.route('PUT', path, ...middleware) }
187
213
  options(path, ...middleware) { return this.route('OPTIONS', path, ...middleware) }
214
+
215
+
216
+ /**
217
+ * Generial Init() sequence
218
+ * This will be run automatically on setup or the first fetch()
219
+ * @returns {Cowboy} This chainable Cowboy router instance
220
+ */
221
+ init() {
222
+ debug('INIT!');
223
+ if (this.doneInit) return this; // Already completed init
224
+
225
+ if (this.settings.patchAxios) {
226
+ // TODO: Patch Axios somehow
227
+ // axios.defaults.adapter = axiosFetchAdapter;
228
+ }
229
+ return this;
230
+ }
188
231
  }
189
232
 
190
233
 
@@ -200,5 +243,7 @@ export default function cowboy(options) {
200
243
  fetch: cowboyInstance.fetch.bind(cowboyInstance),
201
244
  });
202
245
 
246
+ cowboyInstance.init();
247
+
203
248
  return cowboyInstance;
204
249
  }
package/lib/request.js CHANGED
@@ -36,4 +36,13 @@ export default class CowboyRequest {
36
36
  this.path = this.pathTidy(url.pathname);
37
37
  this.hostname = url.hostname;
38
38
  }
39
+
40
+
41
+ /**
42
+ * Utility function to simplify an incoming request
43
+ * @returns {String} Human readable string
44
+ */
45
+ toString() {
46
+ return `${this.method} ${this.path}`;
47
+ }
39
48
  }
package/lib/response.js CHANGED
@@ -99,7 +99,7 @@ export default class CowboyResponse {
99
99
  status: this.code,
100
100
  headers: this.headers,
101
101
  };
102
- console.log('Build response', JSON.stringify(cfOptions));
102
+ console.log('Build response', JSON.stringify({...cfOptions, body: this.body}, null, '\t'));
103
103
  return new this.CloudflareResponse(this.body, cfOptions);
104
104
  }
105
105
  }
package/lib/testkit.js CHANGED
@@ -17,6 +17,8 @@ export let worker;
17
17
  *
18
18
  * @param {Object} [options] Additional options to mutate behaviour
19
19
  * @param {Axios} [options.axios] Axios instance to mutate with the base URL, if specified
20
+ * @param {Boolean} [options.server=true] Initialize a local server - disable this if you're running your own
21
+ * @param {Boolean} [options.debug=false] Force debug as if `DEBUG=cowboy` was set
20
22
  * @param {Function} [options.logOutput] Function to wrap STDOUT output. Called as `(line:String)`
21
23
  * @param {Function} [options.logOutputErr] Function to wrap STDERR output. Called as `(line:String)`
22
24
  * @param {String} [options.host='127.0.0.1'] Host to run Wrangler on
@@ -28,6 +30,8 @@ export let worker;
28
30
  export function start(options) {
29
31
  let settings = {
30
32
  axios: null,
33
+ server: true,
34
+ debug: false,
31
35
  logOutput: output => console.log('WRANGLER>', output),
32
36
  logOutputErr: output => console.log('WRANGLER!', output),
33
37
  host: '127.0.0.1',
@@ -36,6 +40,7 @@ export function start(options) {
36
40
  ...options,
37
41
  };
38
42
 
43
+ if (settings.debug) Debug.enable('cowboy');
39
44
  debug('Start cowboy testkit');
40
45
  let wranglerConfig; // Eventual wrangler config
41
46
 
@@ -51,6 +56,7 @@ export function start(options) {
51
56
  // }}}
52
57
  // Launch worker {{{
53
58
  .then(()=> {
59
+ if (!settings.server) return;
54
60
  debug('Running Wrangler against script', wranglerConfig.main);
55
61
 
56
62
  let isRunning = false;
@@ -90,7 +96,6 @@ export function start(options) {
90
96
  });
91
97
  })
92
98
  // }}}
93
- // .then(()=> new Promise(resolve => setTimeout(resolve, 10 * 1000)))
94
99
  // Mutate axios if provided {{{
95
100
  .then(()=> {
96
101
  if (settings.axios) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momsfriendlydevco/cowboy",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Wrapper around Cloudflare Wrangler to provide a more Express-like experience",
5
5
  "scripts": {
6
6
  "lint": "eslint ."