@kaito-http/core 2.4.0 → 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,9 +269,117 @@ 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
385
  static handle(server, route, options) {
@@ -286,11 +387,11 @@ class Router {
286
387
  try {
287
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
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;
292
393
  var result = yield route.run({
293
- ctx: context,
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,100 +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
- this.routes = routes;
336
- }
446
+ }
337
447
 
338
- toFindMyWay(server) {
339
- var instance = fmw({
340
- ignoreTrailingSlash: true,
448
+ _defineProperty(Router, "create", () => new Router([]));
341
449
 
342
- defaultRoute(req, serverResponse) {
343
- var _req$url;
450
+ function createFMWServer(config) {
451
+ var _config$rawRoutes;
344
452
 
345
- var res = new KaitoResponse(serverResponse);
346
- res.status(404).json({
347
- success: false,
348
- data: null,
349
- message: "Cannot ".concat(req.method, " ").concat((_req$url = req.url) !== null && _req$url !== void 0 ? _req$url : '/')
350
- });
351
- }
453
+ var fmw = config.router.toFindMyWay(config);
454
+ var rawRoutes = (_config$rawRoutes = config.rawRoutes) !== null && _config$rawRoutes !== void 0 ? _config$rawRoutes : {};
352
455
 
353
- });
456
+ for (var method in rawRoutes) {
457
+ if (!Object.prototype.hasOwnProperty.call(rawRoutes, method)) {
458
+ continue;
459
+ }
354
460
 
355
- var _loop = function _loop(route) {
356
- var handler = /*#__PURE__*/function () {
357
- var _ref = _asyncToGenerator(function* (incomingMessage, serverResponse, params) {
358
- var req = new KaitoRequest(incomingMessage);
359
- var res = new KaitoResponse(serverResponse);
360
- yield Router.handle(server, route, {
361
- params,
362
- req,
363
- res
364
- });
365
- });
461
+ var routes = rawRoutes[method];
366
462
 
367
- return function handler(_x, _x2, _x3) {
368
- return _ref.apply(this, arguments);
369
- };
370
- }();
463
+ if (!routes || routes.length === 0) {
464
+ continue;
465
+ }
371
466
 
372
- if (route.method === '*') {
373
- instance.all(route.path, handler);
374
- return "continue";
467
+ for (var route of routes) {
468
+ if (method === '*') {
469
+ fmw.all(route.path, route.handler);
470
+ continue;
375
471
  }
376
472
 
377
- instance.on(route.method, route.path, handler);
378
- };
379
-
380
- for (var route of this.routes) {
381
- var _ret = _loop(route);
382
-
383
- if (_ret === "continue") continue;
473
+ fmw[method.toLowerCase()](route.path, route.handler);
384
474
  }
385
-
386
- return instance;
387
475
  }
388
476
 
389
- add(route) {
390
- return new Router([...this.routes, route]);
391
- }
477
+ var server = http.createServer( /*#__PURE__*/function () {
478
+ var _ref = _asyncToGenerator(function* (req, res) {
479
+ var before;
392
480
 
393
- map() {
394
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
395
- var result = {};
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.
396
486
 
397
- for (var route of this.routes) {
398
- var _result$method;
399
487
 
400
- var method = route.method;
401
- result[method] = _objectSpread2(_objectSpread2({}, (_result$method = result[method]) !== null && _result$method !== void 0 ? _result$method : {}), {}, {
402
- [route.path]: route
403
- });
404
- }
488
+ if (res.headersSent) {
489
+ return;
490
+ }
405
491
 
406
- return result;
407
- }
492
+ var result = yield fmw.lookup(req, res);
408
493
 
409
- merge(prefix, router) {
410
- return this.copyContext([...this.routes, ...router.routes.map(route => _objectSpread2(_objectSpread2({}, route), {}, {
411
- path: prefix + route.path
412
- }))]);
413
- }
414
-
415
- copyContext(routes) {
416
- return new Router(routes);
417
- }
494
+ if ('after' in config && config.after) {
495
+ yield config.after(before, result);
496
+ }
497
+ });
418
498
 
499
+ return function (_x, _x2) {
500
+ return _ref.apply(this, arguments);
501
+ };
502
+ }());
503
+ return {
504
+ server,
505
+ fmw
506
+ };
507
+ }
508
+ function createServer(config) {
509
+ return createFMWServer(config).server;
419
510
  }
420
- /**
421
- * @deprecated Please use Router#create instead
422
- */
423
-
424
- var createRouter = Router.create;
425
511
 
426
- 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.4.0",
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
  }