@kaito-http/core 2.0.2 → 2.2.1

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,4 +1,6 @@
1
- import fastify from 'fastify';
1
+ import http from 'http';
2
+ import { TLSSocket } from 'tls';
3
+ import getRawBody from 'raw-body';
2
4
 
3
5
  function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
4
6
  try {
@@ -89,35 +91,174 @@ function _objectSpread2(target) {
89
91
  return target;
90
92
  }
91
93
 
92
- var Method;
94
+ class WrappedError extends Error {
95
+ static maybe(maybeError) {
96
+ if (maybeError instanceof Error) {
97
+ return maybeError;
98
+ }
99
+
100
+ return WrappedError.from(maybeError);
101
+ }
102
+
103
+ static from(data) {
104
+ return new WrappedError(data);
105
+ }
106
+
107
+ constructor(data) {
108
+ super('Something was thrown, but it was not an instance of Error, so a WrappedError was created.');
109
+ this.data = data;
110
+ }
111
+
112
+ }
113
+
114
+ function getLastEntryInMultiHeaderValue(headerValue) {
115
+ var normalized = Array.isArray(headerValue) ? headerValue.join(',') : headerValue;
116
+ var lastIndex = normalized.lastIndexOf(',');
117
+ return lastIndex === -1 ? normalized.trim() : normalized.slice(lastIndex + 1).trim();
118
+ }
119
+ function normalizePath(path) {
120
+ var result = path;
121
+
122
+ if (!result.startsWith('/')) {
123
+ result = "/".concat(result);
124
+ }
125
+
126
+ if (result.endsWith('/')) {
127
+ result = result.slice(-1);
128
+ }
129
+
130
+ return result;
131
+ } // Type for import('http').METHODS
132
+
133
+ function getInput(_x) {
134
+ return _getInput.apply(this, arguments);
135
+ }
136
+
137
+ function _getInput() {
138
+ _getInput = _asyncToGenerator(function* (req) {
139
+ if (req.method === 'GET') {
140
+ var input = req.url.searchParams.get('input');
141
+
142
+ if (!input) {
143
+ return null;
144
+ }
145
+
146
+ return JSON.parse(input);
147
+ }
148
+
149
+ var buffer = yield getRawBody(req.raw);
150
+
151
+ switch (req.headers['content-type']) {
152
+ case 'application/json':
153
+ {
154
+ return JSON.parse(buffer.toString());
155
+ }
156
+
157
+ default:
158
+ {
159
+ return null;
160
+ }
161
+ }
162
+ });
163
+ return _getInput.apply(this, arguments);
164
+ }
165
+
166
+ class KaitoRequest {
167
+ constructor(raw) {
168
+ this.raw = raw;
169
+ }
170
+
171
+ get fullURL() {
172
+ var _this$raw$url;
173
+
174
+ return "".concat(this.protocol, "://").concat(this.hostname).concat((_this$raw$url = this.raw.url) !== null && _this$raw$url !== void 0 ? _this$raw$url : '');
175
+ }
176
+
177
+ get url() {
178
+ return new URL(this.fullURL);
179
+ }
180
+
181
+ get method() {
182
+ if (!this.raw.method) {
183
+ throw new Error('Request method is not defined, somehow...');
184
+ }
185
+
186
+ return this.raw.method;
187
+ }
188
+
189
+ get protocol() {
190
+ if (this.raw.socket instanceof TLSSocket) {
191
+ return this.raw.socket.encrypted ? 'https' : 'http';
192
+ }
193
+
194
+ return 'http';
195
+ }
196
+
197
+ get headers() {
198
+ return this.raw.headers;
199
+ }
200
+
201
+ get hostname() {
202
+ var _this$raw$headers$hos, _this$raw$headers$Au;
203
+
204
+ return (_this$raw$headers$hos = this.raw.headers.host) !== null && _this$raw$headers$hos !== void 0 ? _this$raw$headers$hos : getLastEntryInMultiHeaderValue((_this$raw$headers$Au = this.raw.headers[':authority']) !== null && _this$raw$headers$Au !== void 0 ? _this$raw$headers$Au : []);
205
+ }
206
+
207
+ }
93
208
 
94
- (function (Method) {
95
- Method["GET"] = "GET";
96
- Method["POST"] = "POST";
97
- Method["PATCH"] = "PATCH";
98
- Method["DELETE"] = "DELETE";
99
- })(Method || (Method = {}));
209
+ class KaitoResponse {
210
+ constructor(raw) {
211
+ this.raw = raw;
212
+ }
213
+
214
+ header(key, value) {
215
+ this.raw.setHeader(key, value);
216
+ return this;
217
+ }
218
+
219
+ status(code) {
220
+ this.raw.statusCode = code;
221
+ return this;
222
+ }
223
+
224
+ json(data) {
225
+ var json = JSON.stringify(data);
226
+ this.raw.setHeader('Content-Type', 'application/json');
227
+ this.raw.setHeader('Content-Length', Buffer.byteLength(json));
228
+ this.raw.end(json);
229
+ return this;
230
+ }
231
+
232
+ }
100
233
 
101
234
  function createGetContext(getContext) {
102
235
  return getContext;
103
236
  }
104
237
  class Router {
105
238
  constructor(procs) {
106
- _defineProperty(this, "create", method => (name, proc) => {
107
- return new Router(_objectSpread2(_objectSpread2({}, this.procs), {}, {
108
- [name]: _objectSpread2(_objectSpread2({}, proc), {}, {
239
+ _defineProperty(this, "create", method => (path, proc) => {
240
+ var stripped = normalizePath(path);
241
+ var pattern = new RegExp("^".concat(stripped, "/?$"), 'i');
242
+
243
+ var merged = _objectSpread2(_objectSpread2({}, this.procs), {}, {
244
+ [path]: _objectSpread2(_objectSpread2({}, proc), {}, {
109
245
  method,
110
- name
246
+ path,
247
+ pattern
111
248
  })
112
- }));
249
+ });
250
+
251
+ return new Router(merged);
113
252
  });
114
253
 
115
- _defineProperty(this, "merge", (prefix, router) => {
254
+ _defineProperty(this, "merge", (_prefix, router) => {
255
+ var prefix = normalizePath(_prefix);
116
256
  var newProcs = Object.entries(router.getProcs()).reduce((all, entry) => {
117
- var [name, proc] = entry;
257
+ var [_path, proc] = entry;
258
+ var path = normalizePath(_path);
118
259
  return _objectSpread2(_objectSpread2({}, all), {}, {
119
- ["".concat(prefix).concat(name)]: _objectSpread2(_objectSpread2({}, proc), {}, {
120
- name: "".concat(prefix).concat(name)
260
+ ["".concat(prefix).concat(path)]: _objectSpread2(_objectSpread2({}, proc), {}, {
261
+ path: "".concat(prefix).concat(path)
121
262
  })
122
263
  });
123
264
  }, {});
@@ -127,26 +268,55 @@ class Router {
127
268
  return new Router(mergedProcs);
128
269
  });
129
270
 
130
- _defineProperty(this, "get", this.create(Method.GET));
271
+ _defineProperty(this, "get", this.create('GET'));
272
+
273
+ _defineProperty(this, "post", this.create('POST'));
131
274
 
132
- _defineProperty(this, "post", this.create(Method.POST));
275
+ _defineProperty(this, "put", this.create('PUT'));
133
276
 
134
- _defineProperty(this, "patch", this.create(Method.PATCH));
277
+ _defineProperty(this, "patch", this.create('PATCH'));
135
278
 
136
- _defineProperty(this, "delete", this.create(Method.DELETE));
279
+ _defineProperty(this, "delete", this.create('DELETE'));
280
+
281
+ _defineProperty(this, "head", this.create('HEAD'));
282
+
283
+ _defineProperty(this, "options", this.create('OPTIONS'));
284
+
285
+ _defineProperty(this, "connect", this.create('CONNECT'));
286
+
287
+ _defineProperty(this, "trace", this.create('TRACE'));
288
+
289
+ _defineProperty(this, "acl", this.create('ACL'));
290
+
291
+ _defineProperty(this, "bind", this.create('BIND'));
137
292
 
138
293
  this.procs = procs;
294
+ this._procsArray = Object.values(procs);
139
295
  }
140
296
 
141
297
  getProcs() {
142
298
  return this.procs;
143
299
  }
144
300
 
301
+ find(method, url) {
302
+ for (var proc of this._procsArray) {
303
+ if (proc.method !== method) {
304
+ continue;
305
+ }
306
+
307
+ if (proc.pattern.test(url)) {
308
+ return proc;
309
+ }
310
+ }
311
+
312
+ return null;
313
+ }
314
+
145
315
  }
146
316
  class KaitoError extends Error {
147
- constructor(code, message, cause) {
317
+ constructor(status, message, cause) {
148
318
  super(message);
149
- this.code = code;
319
+ this.status = status;
150
320
  this.cause = cause;
151
321
  }
152
322
 
@@ -155,79 +325,76 @@ function createRouter() {
155
325
  return new Router({});
156
326
  }
157
327
  function createServer(config) {
158
- var tree = config.router.getProcs();
159
- var app = fastify();
160
- app.setErrorHandler( /*#__PURE__*/function () {
161
- var _ref = _asyncToGenerator(function* (error, req, res) {
162
- if (error instanceof KaitoError) {
163
- yield res.status(error.code).send({
164
- success: false,
165
- data: null,
166
- message: error.message
167
- });
168
- return;
169
- }
170
-
171
- var {
172
- code,
173
- message
174
- } = yield config.onError({
175
- error,
176
- req,
177
- res
178
- }).catch(() => ({
179
- code: 500,
180
- message: 'Something went wrong'
181
- }));
182
- yield res.status(code).send({
183
- success: false,
184
- data: null,
185
- message
186
- });
187
- });
188
-
189
- return function (_x, _x2, _x3) {
190
- return _ref.apply(this, arguments);
191
- };
192
- }());
193
- app.all('*', /*#__PURE__*/function () {
194
- var _ref2 = _asyncToGenerator(function* (req, res) {
195
- var _handler$input$parse, _handler$input;
196
-
197
- var logMessage = "".concat(req.hostname, " ").concat(req.method, " ").concat(req.url);
328
+ var log = message => {
329
+ if (config.log === undefined) {
330
+ console.log(message);
331
+ } else if (config.log) {
332
+ config.log(message);
333
+ }
334
+ };
198
335
 
199
- if (config.log === undefined) {
200
- console.log(logMessage);
201
- } else if (config.log) {
202
- config.log(logMessage);
203
- }
336
+ return http.createServer( /*#__PURE__*/function () {
337
+ var _ref = _asyncToGenerator(function* (incomingMessage, serverResponse) {
338
+ var start = Date.now();
339
+ var req = new KaitoRequest(incomingMessage);
340
+ var res = new KaitoResponse(serverResponse);
204
341
 
205
- var url = new URL("".concat(req.protocol, "://").concat(req.hostname).concat(req.url));
206
- var handler = tree[url.pathname];
342
+ try {
343
+ var _handler$input, _yield$getInput;
207
344
 
208
- if (!handler) {
209
- throw new KaitoError(404, "Cannot ".concat(req.method, " this route."));
210
- }
345
+ var handler = config.router.find(req.method, req.url.pathname);
211
346
 
212
- var context = yield config.getContext(req, res); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
347
+ if (!handler) {
348
+ throw new KaitoError(404, "Cannot ".concat(req.method, " this route."));
349
+ }
213
350
 
214
- var input = (_handler$input$parse = (_handler$input = handler.input) === null || _handler$input === void 0 ? void 0 : _handler$input.parse(req.method === 'GET' ? req.query : req.body)) !== null && _handler$input$parse !== void 0 ? _handler$input$parse : null;
215
- yield res.send({
216
- success: true,
217
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
218
- data: yield handler.run({
351
+ var input = (_handler$input = handler.input) === null || _handler$input === void 0 ? void 0 : _handler$input.parse((_yield$getInput = yield getInput(req)) !== null && _yield$getInput !== void 0 ? _yield$getInput : undefined);
352
+ var context = yield config.getContext(req, res);
353
+ var data = yield handler.run({
219
354
  ctx: context,
220
355
  input
221
- }),
222
- message: 'OK'
223
- });
356
+ });
357
+ res.json({
358
+ success: true,
359
+ data,
360
+ message: 'OK'
361
+ });
362
+ } catch (error) {
363
+ if (error instanceof KaitoError) {
364
+ res.status(error.status).json({
365
+ success: false,
366
+ data: null,
367
+ message: error.message
368
+ });
369
+ return;
370
+ }
371
+
372
+ var {
373
+ status: _status,
374
+ message: _message
375
+ } = yield config.onError({
376
+ error: WrappedError.maybe(error),
377
+ req,
378
+ res
379
+ }).catch(() => ({
380
+ status: 500,
381
+ message: 'Something went wrong'
382
+ }));
383
+ res.status(_status).json({
384
+ success: false,
385
+ data: null,
386
+ message: _message
387
+ });
388
+ } finally {
389
+ var finish = Date.now();
390
+ log("".concat(req.method, " ").concat(req.fullURL, " ").concat(res.raw.statusCode, " ").concat(finish - start, "ms"));
391
+ }
224
392
  });
225
393
 
226
- return function (_x4, _x5) {
227
- return _ref2.apply(this, arguments);
394
+ return function (_x, _x2) {
395
+ return _ref.apply(this, arguments);
228
396
  };
229
397
  }());
230
- return app;
231
398
  }
232
399
 
233
- export { KaitoError, Method, Router, createGetContext, createRouter, createServer };
400
+ export { KaitoError, Router, createGetContext, createRouter, createServer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaito-http/core",
3
- "version": "2.0.2",
3
+ "version": "2.2.1",
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>",
@@ -11,7 +11,6 @@
11
11
  "devDependencies": {
12
12
  "@types/body-parser": "^1.19.2",
13
13
  "@types/node": "^17.0.24",
14
- "@types/node-fetch": "^2.6.1",
15
14
  "typescript": "4.6",
16
15
  "zod": "^3.14.4"
17
16
  },
@@ -20,10 +19,6 @@
20
19
  "readme.md",
21
20
  "dist"
22
21
  ],
23
- "dependencies": {
24
- "colorette": "^2.0.16",
25
- "fastify": "^3.28.0"
26
- },
27
22
  "bugs": {
28
23
  "url": "https://github.com/kaito-http/kaito/issues"
29
24
  },
@@ -33,8 +28,10 @@
33
28
  },
34
29
  "keywords": [
35
30
  "typescript",
36
- "fastify",
37
31
  "http",
38
32
  "framework"
39
- ]
33
+ ],
34
+ "dependencies": {
35
+ "raw-body": "^2.5.1"
36
+ }
40
37
  }