@kaito-http/core 2.3.9 → 2.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.
@@ -1,8 +1,51 @@
1
- import * as http from 'http';
2
- import fmw from 'find-my-way';
3
- import { TLSSocket } from 'tls';
1
+ import { TLSSocket } from 'node:tls';
4
2
  import { parse } from 'content-type';
5
3
  import getRawBody from 'raw-body';
4
+ import { serialize } from 'cookie';
5
+ import fmw from 'find-my-way';
6
+ import * as http from 'node:http';
7
+
8
+ class WrappedError extends Error {
9
+ static maybe(maybeError) {
10
+ if (maybeError instanceof Error) {
11
+ return maybeError;
12
+ }
13
+
14
+ return WrappedError.from(maybeError);
15
+ }
16
+
17
+ static from(data) {
18
+ return new WrappedError(data);
19
+ }
20
+
21
+ constructor(data) {
22
+ super('Something was thrown, but it was not an instance of Error, so a WrappedError was created.');
23
+ this.data = data;
24
+ }
25
+
26
+ }
27
+ class KaitoError extends Error {
28
+ constructor(status, message) {
29
+ super(message);
30
+ this.status = status;
31
+ }
32
+
33
+ }
34
+
35
+ function _defineProperty(obj, key, value) {
36
+ if (key in obj) {
37
+ Object.defineProperty(obj, key, {
38
+ value: value,
39
+ enumerable: true,
40
+ configurable: true,
41
+ writable: true
42
+ });
43
+ } else {
44
+ obj[key] = value;
45
+ }
46
+
47
+ return obj;
48
+ }
6
49
 
7
50
  function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
8
51
  try {
@@ -40,118 +83,6 @@ function _asyncToGenerator(fn) {
40
83
  };
41
84
  }
42
85
 
43
- function createFMWServer(config) {
44
- var fmw = config.router.toFindMyWay(config);
45
- var server = http.createServer( /*#__PURE__*/function () {
46
- var _ref = _asyncToGenerator(function* (req, res) {
47
- for (var fn of (_config$before = config.before) !== null && _config$before !== void 0 ? _config$before : []) {
48
- var _config$before;
49
-
50
- // Disabled because we need these to run in order!
51
- // eslint-disable-next-line no-await-in-loop
52
- yield fn(req, res);
53
- }
54
-
55
- if (req.method === 'OPTIONS') {
56
- return;
57
- }
58
-
59
- fmw.lookup(req, res);
60
- });
61
-
62
- return function (_x, _x2) {
63
- return _ref.apply(this, arguments);
64
- };
65
- }());
66
- return {
67
- server,
68
- fmw
69
- };
70
- }
71
- function createServer(config) {
72
- return createFMWServer(config).server;
73
- }
74
-
75
- function _defineProperty(obj, key, value) {
76
- if (key in obj) {
77
- Object.defineProperty(obj, key, {
78
- value: value,
79
- enumerable: true,
80
- configurable: true,
81
- writable: true
82
- });
83
- } else {
84
- obj[key] = value;
85
- }
86
-
87
- return obj;
88
- }
89
-
90
- function ownKeys(object, enumerableOnly) {
91
- var keys = Object.keys(object);
92
-
93
- if (Object.getOwnPropertySymbols) {
94
- var symbols = Object.getOwnPropertySymbols(object);
95
-
96
- if (enumerableOnly) {
97
- symbols = symbols.filter(function (sym) {
98
- return Object.getOwnPropertyDescriptor(object, sym).enumerable;
99
- });
100
- }
101
-
102
- keys.push.apply(keys, symbols);
103
- }
104
-
105
- return keys;
106
- }
107
-
108
- function _objectSpread2(target) {
109
- for (var i = 1; i < arguments.length; i++) {
110
- var source = arguments[i] != null ? arguments[i] : {};
111
-
112
- if (i % 2) {
113
- ownKeys(Object(source), true).forEach(function (key) {
114
- _defineProperty(target, key, source[key]);
115
- });
116
- } else if (Object.getOwnPropertyDescriptors) {
117
- Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
118
- } else {
119
- ownKeys(Object(source)).forEach(function (key) {
120
- Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
121
- });
122
- }
123
- }
124
-
125
- return target;
126
- }
127
-
128
- class WrappedError extends Error {
129
- static maybe(maybeError) {
130
- if (maybeError instanceof Error) {
131
- return maybeError;
132
- }
133
-
134
- return WrappedError.from(maybeError);
135
- }
136
-
137
- static from(data) {
138
- return new WrappedError(data);
139
- }
140
-
141
- constructor(data) {
142
- super('Something was thrown, but it was not an instance of Error, so a WrappedError was created.');
143
- this.data = data;
144
- }
145
-
146
- }
147
- class KaitoError extends Error {
148
- constructor(status, message) {
149
- super(message);
150
- this.status = status;
151
- }
152
-
153
- }
154
-
155
86
  function createGetContext(callback) {
156
87
  return callback;
157
88
  }
@@ -194,6 +125,11 @@ function _getInput() {
194
125
 
195
126
  default:
196
127
  {
128
+ if (process.env.NODE_ENV === 'development') {
129
+ console.warn('[kaito] Unsupported content type:', type);
130
+ console.warn('[kaito] This message is only shown in development mode.');
131
+ }
132
+
197
133
  return null;
198
134
  }
199
135
  }
@@ -207,12 +143,21 @@ class KaitoRequest {
207
143
 
208
144
  this.raw = raw;
209
145
  }
146
+ /**
147
+ * The full URL of the request, including the protocol, hostname, and path.
148
+ * Note: does not include the query string or hash
149
+ */
150
+
210
151
 
211
152
  get fullURL() {
212
153
  var _this$raw$url;
213
154
 
214
155
  return "".concat(this.protocol, "://").concat(this.hostname).concat((_this$raw$url = this.raw.url) !== null && _this$raw$url !== void 0 ? _this$raw$url : '');
215
156
  }
157
+ /**
158
+ * A new URL instance for the full URL of the request.
159
+ */
160
+
216
161
 
217
162
  get url() {
218
163
  if (this._url) {
@@ -222,6 +167,10 @@ class KaitoRequest {
222
167
  this._url = new URL(this.fullURL);
223
168
  return this._url;
224
169
  }
170
+ /**
171
+ * The HTTP method of the request.
172
+ */
173
+
225
174
 
226
175
  get method() {
227
176
  if (!this.raw.method) {
@@ -230,6 +179,10 @@ class KaitoRequest {
230
179
 
231
180
  return this.raw.method;
232
181
  }
182
+ /**
183
+ * The protocol of the request, either `http` or `https`.
184
+ */
185
+
233
186
 
234
187
  get protocol() {
235
188
  if (this.raw.socket instanceof TLSSocket) {
@@ -238,10 +191,18 @@ class KaitoRequest {
238
191
 
239
192
  return 'http';
240
193
  }
194
+ /**
195
+ * The request headers
196
+ */
197
+
241
198
 
242
199
  get headers() {
243
200
  return this.raw.headers;
244
201
  }
202
+ /**
203
+ * The hostname of the request.
204
+ */
205
+
245
206
 
246
207
  get hostname() {
247
208
  var _this$raw$headers$hos, _this$raw$headers$Au;
@@ -255,16 +216,48 @@ class KaitoResponse {
255
216
  constructor(raw) {
256
217
  this.raw = raw;
257
218
  }
219
+ /**
220
+ * Send a response
221
+ * @param key The key of the header
222
+ * @param value The value of the header
223
+ * @returns The response object
224
+ */
225
+
258
226
 
259
227
  header(key, value) {
260
228
  this.raw.setHeader(key, value);
261
229
  return this;
262
230
  }
231
+ /**
232
+ * Set the status code of the response
233
+ * @param code The status code
234
+ * @returns The response object
235
+ */
236
+
263
237
 
264
238
  status(code) {
265
239
  this.raw.statusCode = code;
266
240
  return this;
267
241
  }
242
+ /**
243
+ * Set a cookie
244
+ * @param name The name of the cookie
245
+ * @param value The value of the cookie
246
+ * @param options The options for the cookie
247
+ * @returns The response object
248
+ */
249
+
250
+
251
+ cookie(name, value, options) {
252
+ this.raw.setHeader('Set-Cookie', serialize(name, value, options));
253
+ return this;
254
+ }
255
+ /**
256
+ * Send a JSON APIResponse body
257
+ * @param data The data to send
258
+ * @returns The response object
259
+ */
260
+
268
261
 
269
262
  json(data) {
270
263
  var json = JSON.stringify(data);
@@ -276,21 +269,129 @@ class KaitoResponse {
276
269
 
277
270
  }
278
271
 
272
+ function ownKeys(object, enumerableOnly) {
273
+ var keys = Object.keys(object);
274
+
275
+ if (Object.getOwnPropertySymbols) {
276
+ var symbols = Object.getOwnPropertySymbols(object);
277
+
278
+ if (enumerableOnly) {
279
+ symbols = symbols.filter(function (sym) {
280
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
281
+ });
282
+ }
283
+
284
+ keys.push.apply(keys, symbols);
285
+ }
286
+
287
+ return keys;
288
+ }
289
+
290
+ function _objectSpread2(target) {
291
+ for (var i = 1; i < arguments.length; i++) {
292
+ var source = arguments[i] != null ? arguments[i] : {};
293
+
294
+ if (i % 2) {
295
+ ownKeys(Object(source), true).forEach(function (key) {
296
+ _defineProperty(target, key, source[key]);
297
+ });
298
+ } else if (Object.getOwnPropertyDescriptors) {
299
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
300
+ } else {
301
+ ownKeys(Object(source)).forEach(function (key) {
302
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
303
+ });
304
+ }
305
+ }
306
+
307
+ return target;
308
+ }
309
+
279
310
  class Router {
280
- static create() {
281
- return new Router({});
311
+ constructor(routes) {
312
+ _defineProperty(this, "add", route => new Router([...this.routes, route]));
313
+
314
+ _defineProperty(this, "merge", (pathPrefix, other) => {
315
+ var newRoutes = other.routes.map(route => _objectSpread2(_objectSpread2({}, route), {}, {
316
+ path: "".concat(pathPrefix).concat(route.path)
317
+ }));
318
+ return new Router([...this.routes, ...newRoutes]);
319
+ });
320
+
321
+ _defineProperty(this, "toFindMyWay", server => {
322
+ var instance = fmw({
323
+ ignoreTrailingSlash: true,
324
+
325
+ defaultRoute(req, serverResponse) {
326
+ return _asyncToGenerator(function* () {
327
+ var _req$url;
328
+
329
+ var res = new KaitoResponse(serverResponse);
330
+ var message = "Cannot ".concat(req.method, " ").concat((_req$url = req.url) !== null && _req$url !== void 0 ? _req$url : '/');
331
+ res.status(404).json({
332
+ success: false,
333
+ data: null,
334
+ message
335
+ });
336
+ return {
337
+ success: false,
338
+ data: {
339
+ status: 404,
340
+ message
341
+ }
342
+ };
343
+ })();
344
+ }
345
+
346
+ });
347
+
348
+ var _loop = function _loop(route) {
349
+ var handler = /*#__PURE__*/function () {
350
+ var _ref = _asyncToGenerator(function* (incomingMessage, serverResponse, params) {
351
+ var req = new KaitoRequest(incomingMessage);
352
+ var res = new KaitoResponse(serverResponse);
353
+ return Router.handle(server, route, {
354
+ params,
355
+ req,
356
+ res
357
+ });
358
+ });
359
+
360
+ return function handler(_x, _x2, _x3) {
361
+ return _ref.apply(this, arguments);
362
+ };
363
+ }();
364
+
365
+ if (route.method === '*') {
366
+ instance.all(route.path, handler);
367
+ return "continue";
368
+ }
369
+
370
+ instance.on(route.method, route.path, handler);
371
+ };
372
+
373
+ for (var route of this.routes) {
374
+ var _ret = _loop(route);
375
+
376
+ if (_ret === "continue") continue;
377
+ }
378
+
379
+ return instance;
380
+ });
381
+
382
+ this.routes = routes;
282
383
  }
283
384
 
284
- static handle(server, options) {
385
+ static handle(server, route, options) {
285
386
  return _asyncToGenerator(function* () {
286
387
  try {
287
- var _options$route$input$, _options$route$input;
388
+ var _route$input$parse, _route$input;
288
389
 
289
- var context = yield server.getContext(options.req, options.res);
390
+ var ctx = yield server.getContext(options.req, options.res);
290
391
  var body = yield getInput(options.req);
291
- var input = (_options$route$input$ = (_options$route$input = options.route.input) === null || _options$route$input === void 0 ? void 0 : _options$route$input.parse(body)) !== null && _options$route$input$ !== void 0 ? _options$route$input$ : undefined;
292
- var result = yield options.route.run({
293
- ctx: context,
392
+ var input = (_route$input$parse = (_route$input = route.input) === null || _route$input === void 0 ? void 0 : _route$input.parse(body)) !== null && _route$input$parse !== void 0 ? _route$input$parse : undefined;
393
+ var result = yield route.run({
394
+ ctx,
294
395
  input,
295
396
  params: options.params
296
397
  });
@@ -299,6 +400,10 @@ class Router {
299
400
  data: result,
300
401
  message: 'OK'
301
402
  });
403
+ return {
404
+ success: true,
405
+ data: result
406
+ };
302
407
  } catch (e) {
303
408
  var error = WrappedError.maybe(e);
304
409
 
@@ -327,162 +432,81 @@ class Router {
327
432
  data: null,
328
433
  message
329
434
  });
435
+ return {
436
+ success: false,
437
+ data: {
438
+ status,
439
+ message
440
+ }
441
+ };
330
442
  }
331
443
  })();
332
444
  }
333
445
 
334
- constructor(routes) {
335
- _defineProperty(this, 'acl', this.make('ACL'));
336
-
337
- _defineProperty(this, 'bind', this.make('BIND'));
338
-
339
- _defineProperty(this, 'checkout', this.make('CHECKOUT'));
340
-
341
- _defineProperty(this, 'connect', this.make('CONNECT'));
342
-
343
- _defineProperty(this, 'copy', this.make('COPY'));
344
-
345
- _defineProperty(this, 'delete', this.make('DELETE'));
346
-
347
- _defineProperty(this, 'get', this.make('GET'));
348
-
349
- _defineProperty(this, 'head', this.make('HEAD'));
350
-
351
- _defineProperty(this, 'link', this.make('LINK'));
352
-
353
- _defineProperty(this, 'lock', this.make('LOCK'));
354
-
355
- _defineProperty(this, 'm_search', this.make('M-SEARCH'));
356
-
357
- _defineProperty(this, 'mkactivity', this.make('MKACTIVITY'));
358
-
359
- _defineProperty(this, 'mkcalendar', this.make('MKCALENDAR'));
360
-
361
- _defineProperty(this, 'mkcol', this.make('MKCOL'));
362
-
363
- _defineProperty(this, 'move', this.make('MOVE'));
364
-
365
- _defineProperty(this, 'notify', this.make('NOTIFY'));
366
-
367
- _defineProperty(this, 'patch', this.make('PATCH'));
368
-
369
- _defineProperty(this, 'post', this.make('POST'));
370
-
371
- _defineProperty(this, 'propfind', this.make('PROPFIND'));
372
-
373
- _defineProperty(this, 'proppatch', this.make('PROPPATCH'));
374
-
375
- _defineProperty(this, 'purge', this.make('PURGE'));
376
-
377
- _defineProperty(this, 'put', this.make('PUT'));
378
-
379
- _defineProperty(this, 'rebind', this.make('REBIND'));
380
-
381
- _defineProperty(this, 'report', this.make('REPORT'));
382
-
383
- _defineProperty(this, 'search', this.make('SEARCH'));
446
+ }
384
447
 
385
- _defineProperty(this, 'source', this.make('SOURCE'));
448
+ _defineProperty(Router, "create", () => new Router([]));
386
449
 
387
- _defineProperty(this, 'subscribe', this.make('SUBSCRIBE'));
450
+ function createFMWServer(config) {
451
+ var _config$rawRoutes;
388
452
 
389
- _defineProperty(this, 'trace', this.make('TRACE'));
453
+ var fmw = config.router.toFindMyWay(config);
454
+ var rawRoutes = (_config$rawRoutes = config.rawRoutes) !== null && _config$rawRoutes !== void 0 ? _config$rawRoutes : {};
390
455
 
391
- _defineProperty(this, 'unbind', this.make('UNBIND'));
456
+ for (var method in rawRoutes) {
457
+ if (!Object.prototype.hasOwnProperty.call(rawRoutes, method)) {
458
+ continue;
459
+ }
392
460
 
393
- _defineProperty(this, 'unlink', this.make('UNLINK'));
461
+ var routes = rawRoutes[method];
394
462
 
395
- _defineProperty(this, 'unlock', this.make('UNLOCK'));
463
+ if (!routes || routes.length === 0) {
464
+ continue;
465
+ }
396
466
 
397
- _defineProperty(this, 'unsubscribe', this.make('UNSUBSCRIBE'));
467
+ for (var route of routes) {
468
+ if (method === '*') {
469
+ fmw.all(route.path, route.handler);
470
+ continue;
471
+ }
398
472
 
399
- this.routes = routes;
473
+ fmw[method.toLowerCase()](route.path, route.handler);
474
+ }
400
475
  }
401
476
 
402
- merge(prefix, router) {
403
- var newRoutes = Object.fromEntries(Object.entries(router.routes).map(_ref => {
404
- var [k, v] = _ref;
405
- return ["".concat(prefix).concat(k), v];
406
- }));
407
-
408
- var merged = _objectSpread2(_objectSpread2({}, this.routes), newRoutes);
477
+ var server = http.createServer( /*#__PURE__*/function () {
478
+ var _ref = _asyncToGenerator(function* (req, res) {
479
+ var before;
409
480
 
410
- return this._copy(merged);
411
- }
481
+ if (config.before) {
482
+ before = yield config.before(req, res);
483
+ } else {
484
+ before = null;
485
+ } // If the user has sent a response (e.g. replying to CORS), we don't want to do anything else.
412
486
 
413
- toFindMyWay(server) {
414
- var _this = this;
415
487
 
416
- var instance = fmw({
417
- ignoreTrailingSlash: true,
488
+ if (res.headersSent) {
489
+ return;
490
+ }
418
491
 
419
- defaultRoute(req, serverResponse) {
420
- var _req$url;
492
+ var result = yield fmw.lookup(req, res);
421
493
 
422
- var res = new KaitoResponse(serverResponse);
423
- res.status(404).json({
424
- success: false,
425
- data: null,
426
- message: "Cannot ".concat(req.method, " ").concat((_req$url = req.url) !== null && _req$url !== void 0 ? _req$url : '/')
427
- });
494
+ if ('after' in config && config.after) {
495
+ yield config.after(before, result);
428
496
  }
429
-
430
497
  });
431
- var paths = Object.keys(this.routes);
432
-
433
- var _loop = function _loop(path) {
434
- var route = _this.routes[path];
435
- instance.on(route.method, path, /*#__PURE__*/function () {
436
- var _ref2 = _asyncToGenerator(function* (incomingMessage, serverResponse, params) {
437
- var req = new KaitoRequest(incomingMessage);
438
- var res = new KaitoResponse(serverResponse);
439
- yield Router.handle(server, {
440
- route,
441
- params,
442
- req,
443
- res
444
- });
445
- });
446
498
 
447
- return function (_x, _x2, _x3) {
448
- return _ref2.apply(this, arguments);
449
- };
450
- }());
451
- };
452
-
453
- for (var path of paths) {
454
- _loop(path);
455
- }
456
-
457
- return instance;
458
- }
459
-
460
- _copy(routes) {
461
- return new Router(routes);
462
- }
463
-
464
- make(method) {
465
- return (path, route) => {
466
- var addedRoute = _objectSpread2(_objectSpread2({}, route), {}, {
467
- method
468
- }); // `as unknown` is required because otherwise
469
- // this type just gets massive and too slow,
470
- // so we have to write it out specifically
471
-
472
-
473
- var merged = _objectSpread2(_objectSpread2({}, this.routes), {}, {
474
- [path]: addedRoute
475
- });
476
-
477
- return this._copy(merged);
499
+ return function (_x, _x2) {
500
+ return _ref.apply(this, arguments);
478
501
  };
479
- }
480
-
502
+ }());
503
+ return {
504
+ server,
505
+ fmw
506
+ };
507
+ }
508
+ function createServer(config) {
509
+ return createFMWServer(config).server;
481
510
  }
482
- /**
483
- * @deprecated Please use Router#create instead
484
- */
485
-
486
- var createRouter = Router.create;
487
511
 
488
- export { KaitoError, KaitoRequest, KaitoResponse, Router, WrappedError, createFMWServer, createGetContext, createRouter, createServer, getInput, getLastEntryInMultiHeaderValue };
512
+ export { KaitoError, KaitoRequest, KaitoResponse, Router, WrappedError, createFMWServer, createGetContext, createServer, getInput, getLastEntryInMultiHeaderValue };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaito-http/core",
3
- "version": "2.3.9",
3
+ "version": "2.5.0",
4
4
  "description": "Functional HTTP Framework for TypeScript",
5
5
  "repository": "https://github.com/kaito-http/kaito",
6
6
  "author": "Alistair Smith <hi@alistair.sh>",
@@ -10,9 +10,10 @@
10
10
  "types": "dist/kaito-http-core.cjs.d.ts",
11
11
  "devDependencies": {
12
12
  "@types/content-type": "^1.1.5",
13
- "@types/node": "^17.0.25",
14
- "typescript": "4.6",
15
- "zod": "^3.14.4"
13
+ "@types/cookie": "^0.5.1",
14
+ "@types/node": "^18.7.18",
15
+ "typescript": "4.8",
16
+ "zod": "^3.19.1"
16
17
  },
17
18
  "files": [
18
19
  "package.json",
@@ -33,7 +34,8 @@
33
34
  ],
34
35
  "dependencies": {
35
36
  "content-type": "^1.0.4",
36
- "find-my-way": "^5.5.0",
37
+ "cookie": "^0.5.0",
38
+ "find-my-way": "^7.1.0",
37
39
  "raw-body": "^2.5.1"
38
40
  }
39
41
  }