@momsfriendlydevco/cowboy 1.4.0 → 1.5.0

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/README.md CHANGED
@@ -10,6 +10,7 @@ Features:
10
10
  * Built in middleware + request validation via [Joi](https://joi.dev)
11
11
  * Built-in debug support for testkits + Wrangler
12
12
  * Built-in JSON / Multipart (or FormData) / Plain text decoding and population of `req.body`
13
+ * Scheduled tasks can return promises and they are automatically awaited (no need to do `ctx.waitUntil()`)
13
14
 
14
15
 
15
16
  Examples
package/lib/cowboy.js CHANGED
@@ -25,10 +25,11 @@ export class Cowboy {
25
25
  * General settings for this Cowboy instance
26
26
  *
27
27
  * @type {Object}
28
+ * @property {Boolean|'VAR'} scheduler Add a generic `/__scheduled` endpoint to execute any queued scheduler. If value is `'VAR'` this will only apply if the env `COWBOY_SCHEDULER` is set and truthy
28
29
  * @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
29
30
  */
30
31
  settings = {
31
- patchAxios: true,
32
+ scheduler: 'VAR',
32
33
  pathTidy(path) {
33
34
  return path
34
35
  .replace(/^\/api\/\w+/, '/')
@@ -62,7 +63,7 @@ export class Cowboy {
62
63
  * Queue up a middleware path
63
64
  * All given middleware is called in sequence, if middleware
64
65
  *
65
- * @param {String|Array<String>} methods A method matcher or array of available methods
66
+ * @param {String|Array<String>} methods A method matcher or array of available methods in upper-case
66
67
  * @param {String|RegExp|Array<String|RegExp>} paths A prefix path to match
67
68
  * @param {CowboyMiddleware...} middleware Middleware to call in sequence
68
69
  *
@@ -118,6 +119,60 @@ export class Cowboy {
118
119
  if (env.COWBOY_DEBUG)
119
120
  debug.enabled = true;
120
121
 
122
+ if ( // Setup scheduler endpoint if this is the first fetch() hit were we can access the `env` state
123
+ !this._schedulerSetup
124
+ && (
125
+ this.settings.scheduler === true
126
+ || (
127
+ this.settings.scheduler == 'VAR'
128
+ && env.COWBOY_SCHEDULER
129
+ )
130
+ )
131
+ ) {
132
+ console.log('Scheduler setup against /__scheduled');
133
+ this._schedulerSetup = true;
134
+
135
+ // Find first GET operation
136
+ let routeIndex = this.routes.findIndex(r => r.methods.some(m => m == 'GET'));
137
+ if (routeIndex < 0) routeIndex = this.routes.length; // If no GETS, assume end position
138
+
139
+ // Splice into position before first GET
140
+ let matcher = compileRoutePaths('/__scheduled');
141
+ this.routes.splice(routeIndex, 0, {
142
+ methods: ['GET'],
143
+ paths: matcher.paths,
144
+ matcher,
145
+ middleware: [
146
+ async (req, res, env) => {
147
+ debug('Executing schedule handler');
148
+ if (!this.schedule.handler) return res.status(404).send('No scheduler installed');
149
+
150
+ try {
151
+ let result = await this.schedule.handler.call(
152
+ this,
153
+ { // Faked Cloudflare `controller` context
154
+ cron: 'FAKE',
155
+ type: 'scheduled',
156
+ scheduledTime: (new Date()).toISOString(),
157
+ },
158
+ env,
159
+ { // Faked Cloudflare `ctx` context - we provide a fake waitUntil here
160
+ waitUntil() {
161
+ throw new Error('ctx.waitUntil() functionality is provided natively by Cowboy.schedule(cb:Function) - just return a promise instead of using it');
162
+ },
163
+ },
164
+ );
165
+ debug('Got scheduler response', result);
166
+ return res.send(result);
167
+ } catch (e) {
168
+ return res.status(400).send(`Scheduler threw error: ${e.toString()}`);
169
+ }
170
+ },
171
+ ]
172
+ });
173
+ }
174
+
175
+
121
176
  // Create basic [req]uest / [res]ponse objects
122
177
  let req = new CowboyRequest(cfReq, {
123
178
  router: this,
@@ -167,23 +222,6 @@ export class Cowboy {
167
222
  }
168
223
 
169
224
 
170
- /**
171
- * Set up Cloudflare response to "scheduled" call
172
- * This is really just a map to the last handler we installed to .schedule(cb) - for now
173
- *
174
- * @param {CloudflareEvent} event The Cloudflare event context passed
175
- * @param {Object} env Environment variables
176
- * @param {CloudflareContext} ctx The Cloudflare context to respond to
177
- *
178
- * @returns {Cowboy} This chainable Cowboy router instance
179
- */
180
- scheduled(event, env, ctx) {
181
- if (!this.schedule.handler) throw new Error('Attemped to access Cowboy.scheduled without first calling .schedule() to set something up!');
182
- this.schedule.handler.call(this, event, env, ctx);
183
- return this;
184
- }
185
-
186
-
187
225
  /**
188
226
  * Call a router function as if it were invoked directly
189
227
  * This function exists as an easier way to remap body contents without
@@ -295,12 +333,43 @@ export class Cowboy {
295
333
  * @returns {Cowboy} This chainable Cowboy router instance
296
334
  */
297
335
  schedule(handler) {
298
- console.info('Installed schedule event handler. Access via http://localhost:8787/__scheduled');
336
+ debug('Installed schedule event handler');
299
337
  this.schedule.handler = handler;
300
338
  return this;
301
339
  }
302
340
 
303
341
 
342
+ /**
343
+ * Set up Cloudflare response to "scheduled" call
344
+ * This is really just a map to the last handler we installed to .schedule(cb) - for now
345
+ *
346
+ * @param {CloudflareEvent} event The Cloudflare event context passed
347
+ * @param {Object} env Environment variables
348
+ * @param {CloudflareContext} ctx The Cloudflare context to respond to
349
+ *
350
+ * @returns {Cowboy} This chainable Cowboy router instance
351
+ */
352
+ scheduled(event, env, ctx) {
353
+ if (!this.schedule.handler) throw new Error('Attemped to access Cowboy.scheduled without first calling .schedule() to set something up!');
354
+
355
+ // Wrap all scheduler calls in ctx.waitUntil() so promises are always waited on
356
+ ctx.waitUntil(
357
+ this.schedule.handler.call(
358
+ this,
359
+ event,
360
+ env,
361
+ {
362
+ waitUntil() {
363
+ throw new Error('ctx.waitUntil() functionality is provided natively by Cowboy.schedule(cb:Function) - just return a promise instead of using it');
364
+ },
365
+ },
366
+ )
367
+ );
368
+
369
+ return this;
370
+ }
371
+
372
+
304
373
  /**
305
374
  * Generial Init() sequence
306
375
  * This will be run automatically on setup or the first fetch()
@@ -310,10 +379,6 @@ export class Cowboy {
310
379
  if (this.doneInit) return this; // Already completed init
311
380
  debug('INIT!');
312
381
 
313
- if (this.settings.patchAxios) {
314
- // TODO: Patch Axios somehow
315
- // axios.defaults.adapter = axiosFetchAdapter;
316
- }
317
382
  return this;
318
383
  }
319
384
  }
package/lib/testkit.js CHANGED
@@ -19,6 +19,7 @@ export let worker;
19
19
  * @param {Axios} [options.axios] Axios instance to mutate with the base URL, if specified
20
20
  * @param {Boolean} [options.server=true] Initialize a local server - disable this if you're running your own
21
21
  * @param {Boolean} [options.debug=false] Force debug as if `DEBUG=cowboy` was set
22
+ * @param {Boolean} [options.scheduler=false] Bind any scedhuler callback to `/__scheduled`
22
23
  * @param {Function} [options.logOutput] Function to wrap STDOUT output. Called as `(line:String)`
23
24
  * @param {Function} [options.logOutputErr] Function to wrap STDERR output. Called as `(line:String)`
24
25
  * @param {String} [options.host='127.0.0.1'] Host to run Wrangler on
@@ -32,6 +33,7 @@ export function start(options) {
32
33
  axios: null,
33
34
  server: true,
34
35
  debug: false,
36
+ scheduler: false,
35
37
  logOutput: output => console.log('WRANGLER>', output),
36
38
  logOutputErr: output => console.log('WRANGLER!', output),
37
39
  host: '127.0.0.1',
@@ -70,6 +72,9 @@ export function start(options) {
70
72
  ...(debug.enabled ? [
71
73
  '--var=COWBOY_DEBUG:1'
72
74
  ]: []),
75
+ ...(settings.scheduler ? [
76
+ '--var=COWBOY_SCHEDULER:1'
77
+ ]: []),
73
78
  ]);
74
79
 
75
80
  worker.stdout.on('data', data => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momsfriendlydevco/cowboy",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Wrapper around Cloudflare Wrangler to provide a more Express-like experience",
5
5
  "scripts": {
6
6
  "lint": "eslint"