@radatek/microserver 3.0.6 → 3.0.7

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/microserver.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MicroServer
3
- * @version 3.0.6
3
+ * @version 3.0.7
4
4
  * @package @radatek/microserver
5
5
  * @copyright Darius Kisonas 2022
6
6
  * @license MIT
@@ -44,7 +44,6 @@ export declare abstract class Plugin {
44
44
  priority?: number;
45
45
  handler?(req: ServerRequest, res: ServerResponse, next: Function): Promise<string | object | void> | string | object | void;
46
46
  routes?(): Promise<RoutesSet | void> | RoutesSet | void;
47
- constructor();
48
47
  }
49
48
  interface PluginClass {
50
49
  new (options: any, server: MicroServer): Plugin;
@@ -86,6 +85,7 @@ export declare class ServerRequest<T = any> extends http.IncomingMessage {
86
85
  rawBody: Buffer[];
87
86
  /** Request raw body size */
88
87
  rawBodySize: number;
88
+ protected constructor(res: http.ServerResponse, server: MicroServer);
89
89
  /** Extend http.IncomingMessage */
90
90
  static extend(req: http.IncomingMessage, res: http.ServerResponse, server: MicroServer): ServerRequest;
91
91
  /** Check if request is ready */
@@ -114,6 +114,7 @@ export declare class ServerResponse<T = any> extends http.ServerResponse {
114
114
  readonly req: ServerRequest<T>;
115
115
  /** Should response be json */
116
116
  isJson: boolean;
117
+ protected constructor(server: MicroServer);
117
118
  /** Extends http.ServerResponse */
118
119
  static extend(res: http.ServerResponse): void;
119
120
  /** Send error reponse */
@@ -162,7 +163,7 @@ export interface MicroServerConfig extends ListenConfig {
162
163
  /** extra options for plugins */
163
164
  [key: string]: any;
164
165
  }
165
- interface MicroServerEvents {
166
+ export interface MicroServerEvents {
166
167
  ready: () => void;
167
168
  close: () => void;
168
169
  listen: (port: number, address: string, server: http.Server) => void;
@@ -258,6 +259,7 @@ export interface CorsOptions {
258
259
  /** Max age */
259
260
  maxAge?: number;
260
261
  }
262
+ /** CORS plugin. Config may be: true - allow all, string - allow specific origin, or CorsOptions */
261
263
  export declare class CorsPlugin extends Plugin {
262
264
  priority: number;
263
265
  name: string;
@@ -265,6 +267,7 @@ export declare class CorsPlugin extends Plugin {
265
267
  constructor(options?: CorsOptions | string | true);
266
268
  handler?(req: ServerRequest, res: ServerResponse, next: Function): Promise<string | object | void> | string | object | void;
267
269
  }
270
+ /** Methods plugin to support OPTIONS method, and restrict allowed methods. Configuration is comma separated string */
268
271
  export declare class MethodsPlugin extends Plugin {
269
272
  priority: number;
270
273
  name: string;
@@ -274,6 +277,7 @@ export declare class MethodsPlugin extends Plugin {
274
277
  export interface BodyOptions {
275
278
  maxBodySize?: number;
276
279
  }
280
+ /** Body parser plugin */
277
281
  export declare class BodyPlugin extends Plugin {
278
282
  priority: number;
279
283
  name: string;
@@ -291,6 +295,7 @@ export interface UploadFile {
291
295
  size: number;
292
296
  filePath?: string;
293
297
  }
298
+ /** Upload plugin, At least uploadDir option is required */
294
299
  export declare class UploadPlugin extends Plugin {
295
300
  priority: number;
296
301
  name: string;
@@ -321,7 +326,7 @@ interface WebSocketEvents {
321
326
  message: (data: string | Buffer | ArrayBuffer | Blob) => void;
322
327
  open: () => void;
323
328
  }
324
- /** WebSocket class */
329
+ /** WebSocket class used in upgrade handler */
325
330
  export declare class WebSocket extends EventEmitter {
326
331
  ready: boolean;
327
332
  constructor(req: ServerRequest, options?: WebSocketOptions);
@@ -339,6 +344,7 @@ export declare class WebSocket extends EventEmitter {
339
344
  off<K extends keyof WebSocketEvents>(event: K, listener: WebSocketEvents[K]): this;
340
345
  removeListener<K extends keyof WebSocketEvents>(event: K, listener: WebSocketEvents[K]): this;
341
346
  }
347
+ /** WebSocket plugin to support `WEBSOCKET /url` routes */
342
348
  export declare class WebSocketPlugin extends Plugin {
343
349
  name: string;
344
350
  constructor(options?: any, server?: MicroServer);
@@ -385,6 +391,8 @@ export interface StaticFilesOptions {
385
391
  maxAge?: number;
386
392
  /** static errors file for status code, '*' - default */
387
393
  errors?: Record<string, string>;
394
+ /** check precompressed file */
395
+ precompressedGzip?: boolean;
388
396
  }
389
397
  export interface ServeFileOptions {
390
398
  /** path */
@@ -406,11 +414,7 @@ export interface ServeFileOptions {
406
414
  /** stat */
407
415
  stats?: fs.Stats;
408
416
  }
409
- /**
410
- * Static files middleware plugin
411
- * Usage: server.use('static', '/public')
412
- * Usage: server.use('static', { root: 'public', path: '/static' })
413
- */
417
+ /** Static files plugin. At least must be path to public folder as string or StaticFilesOptions */
414
418
  export declare class StaticFilesPlugin extends Plugin {
415
419
  priority: number;
416
420
  /** Default mime types */
@@ -439,6 +443,7 @@ export declare class StaticFilesPlugin extends Plugin {
439
443
  maxAge?: number;
440
444
  prefix: string;
441
445
  errors?: Record<string, string>;
446
+ checkPrecompressedGzip?: boolean;
442
447
  constructor(options?: StaticFilesOptions | string, server?: MicroServer);
443
448
  /** Default static files handler */
444
449
  handler(req: ServerRequest, res: ServerResponse, next: Function): any;
@@ -462,9 +467,9 @@ export interface ProxyOptions {
462
467
  [key: string]: boolean;
463
468
  };
464
469
  }
470
+ /** Reverse proxy plugin, At least remote url as string or in ProxyOptions is required */
465
471
  export declare class ProxyPlugin extends Plugin {
466
472
  priority: number;
467
- name: string;
468
473
  /** Default valid headers */
469
474
  static validHeaders: {
470
475
  [key: string]: boolean;
@@ -610,6 +615,7 @@ export declare class AuthPlugin extends Plugin {
610
615
  /** Authentication middleware */
611
616
  handler(req: ServerRequest, res: ServerResponse, next: Function): Promise<any>;
612
617
  }
618
+ /** Load standard plugins with predefined configs: MethodsPlugin, CorsPlugin, TrustProxyPlugin, BodyPlugin, AuthPlugin, StaticFilesPlugin */
613
619
  export declare class StandardPlugins extends Plugin {
614
620
  constructor(options?: any, server?: MicroServer);
615
621
  }
@@ -706,7 +712,7 @@ export declare class FileStore {
706
712
  observe(data: object, cb: (data: object, key: string, value: any) => void): object;
707
713
  }
708
714
  /** Model validation options */
709
- interface ModelContextOptions {
715
+ export interface ModelContextOptions {
710
716
  /** User info */
711
717
  user?: UserInfo;
712
718
  /** Request params */
@@ -725,12 +731,12 @@ interface ModelContextOptions {
725
731
  projection?: Record<string, 0 | 1 | true | false>;
726
732
  }
727
733
  /** Model field validation options */
728
- interface ModelValidateFieldOptions extends ModelContextOptions {
734
+ export interface ModelValidateFieldOptions extends ModelContextOptions {
729
735
  name: string;
730
736
  field: ResolvedFieldSchema;
731
737
  model: Model<any>;
732
738
  }
733
- export interface ModelCallbackFunc {
739
+ interface ModelCallbackFunc {
734
740
  (options: any): any;
735
741
  }
736
742
  type ModelBasicCtorType = typeof String | typeof Number | typeof Boolean | typeof Date;
@@ -764,7 +770,7 @@ export interface ModelFieldSchema {
764
770
  /** Regex validation or 'email', 'url', 'date', 'time', 'date-time' */
765
771
  format?: string;
766
772
  }
767
- interface ResolvedFieldSchema {
773
+ export interface ResolvedFieldSchema {
768
774
  type: string;
769
775
  model?: Model<any>;
770
776
  primaryKey?: boolean;
package/microserver.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MicroServer
3
- * @version 3.0.6
3
+ * @version 3.0.7
4
4
  * @package @radatek/microserver
5
5
  * @copyright Darius Kisonas 2022
6
6
  * @license MIT
@@ -77,7 +77,6 @@ function deferPromise(cb) {
77
77
  export class Plugin {
78
78
  name;
79
79
  priority;
80
- constructor() { }
81
80
  }
82
81
  // #region ServerRequest/ServerResponse
83
82
  /** Extended http.IncomingMessage */
@@ -120,7 +119,6 @@ export class ServerRequest extends http.IncomingMessage {
120
119
  _body;
121
120
  // @internal
122
121
  _isReady;
123
- // @internal
124
122
  constructor(res, server) {
125
123
  super(new net.Socket());
126
124
  ServerRequest.extend(this, res, server);
@@ -215,7 +213,6 @@ export class ServerRequest extends http.IncomingMessage {
215
213
  export class ServerResponse extends http.ServerResponse {
216
214
  /** Should response be json */
217
215
  isJson;
218
- // @internal
219
216
  constructor(server) {
220
217
  super(new http.IncomingMessage(new net.Socket()));
221
218
  ServerRequest.extend(this.req, this, server);
@@ -816,6 +813,7 @@ class RouterItem {
816
813
  // @internal
817
814
  _last;
818
815
  }
816
+ /** Router plugin */
819
817
  class RouterPlugin extends Plugin {
820
818
  priority = 100;
821
819
  name = 'router';
@@ -947,6 +945,7 @@ class RouterPlugin extends Plugin {
947
945
  this.walk(this._getStack(req.pathname, ['hook', method, '*']), req, res, next);
948
946
  }
949
947
  }
948
+ /** CORS plugin. Config may be: true - allow all, string - allow specific origin, or CorsOptions */
950
949
  export class CorsPlugin extends Plugin {
951
950
  priority = -100;
952
951
  name = 'cors';
@@ -980,6 +979,7 @@ export class CorsPlugin extends Plugin {
980
979
  }
981
980
  // #enregion CorsPlugin
982
981
  // #region MethodsPlugin
982
+ /** Methods plugin to support OPTIONS method, and restrict allowed methods. Configuration is comma separated string */
983
983
  export class MethodsPlugin extends Plugin {
984
984
  priority = -90;
985
985
  name = 'methods';
@@ -1006,6 +1006,7 @@ export class MethodsPlugin extends Plugin {
1006
1006
  return next();
1007
1007
  }
1008
1008
  }
1009
+ /** Body parser plugin */
1009
1010
  export class BodyPlugin extends Plugin {
1010
1011
  priority = -80;
1011
1012
  name = 'body';
@@ -1061,6 +1062,7 @@ export class BodyPlugin extends Plugin {
1061
1062
  });
1062
1063
  }
1063
1064
  }
1065
+ /** Upload plugin, At least uploadDir option is required */
1064
1066
  export class UploadPlugin extends Plugin {
1065
1067
  priority = -70;
1066
1068
  name = 'upload';
@@ -1194,7 +1196,7 @@ export class UploadPlugin extends Plugin {
1194
1196
  }
1195
1197
  const EMPTY_BUFFER = Buffer.alloc(0);
1196
1198
  const DEFLATE_TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]);
1197
- /** WebSocket class */
1199
+ /** WebSocket class used in upgrade handler */
1198
1200
  export class WebSocket extends EventEmitter {
1199
1201
  // @internal
1200
1202
  _socket;
@@ -1464,6 +1466,7 @@ export class WebSocket extends EventEmitter {
1464
1466
  this._socket.write(frame, () => this._socket.write(data, cb));
1465
1467
  }
1466
1468
  }
1469
+ /** WebSocket plugin to support `WEBSOCKET /url` routes */
1467
1470
  export class WebSocketPlugin extends Plugin {
1468
1471
  name = 'websocket';
1469
1472
  // @internal
@@ -1483,8 +1486,6 @@ export class WebSocketPlugin extends Plugin {
1483
1486
  }
1484
1487
  upgradeHandler(server, req, socket, head) {
1485
1488
  const host = req.headers.host || '';
1486
- const vhostPlugin = server.getPlugin('vhost');
1487
- const vserver = vhostPlugin?.vhosts?.[host] || server;
1488
1489
  const res = {
1489
1490
  req,
1490
1491
  get headersSent() {
@@ -1539,7 +1540,7 @@ export class WebSocketPlugin extends Plugin {
1539
1540
  if (req.method !== 'GET' || req.headers.upgrade?.toLowerCase() !== 'websocket')
1540
1541
  return res.error(400);
1541
1542
  req.method = 'WEBSOCKET';
1542
- vserver.handler(req, res);
1543
+ server.handler(req, res);
1543
1544
  }
1544
1545
  }
1545
1546
  // #endregion WebSocket
@@ -1618,13 +1619,7 @@ export class VHostPlugin extends Plugin {
1618
1619
  }
1619
1620
  }
1620
1621
  const etagPrefix = crypto.randomBytes(4).toString('hex');
1621
- //TODO: add precompressed .gz support
1622
- //TODO: add unknown file extension handler
1623
- /**
1624
- * Static files middleware plugin
1625
- * Usage: server.use('static', '/public')
1626
- * Usage: server.use('static', { root: 'public', path: '/static' })
1627
- */
1622
+ /** Static files plugin. At least must be path to public folder as string or StaticFilesOptions */
1628
1623
  export class StaticFilesPlugin extends Plugin {
1629
1624
  priority = 110;
1630
1625
  /** Default mime types */
@@ -1668,6 +1663,7 @@ export class StaticFilesPlugin extends Plugin {
1668
1663
  maxAge;
1669
1664
  prefix;
1670
1665
  errors;
1666
+ checkPrecompressedGzip;
1671
1667
  constructor(options, server) {
1672
1668
  super();
1673
1669
  if (!options)
@@ -1683,8 +1679,8 @@ export class StaticFilesPlugin extends Plugin {
1683
1679
  this.etag = options.etag !== false;
1684
1680
  this.maxAge = options.maxAge;
1685
1681
  this.errors = options.errors;
1682
+ this.checkPrecompressedGzip = options.precompressedGzip;
1686
1683
  this.prefix = ('/' + (options.path?.replace(/^[.\/]*/, '') || '').replace(/\/$/, '')).replace(/\/$/, '');
1687
- const defSend = ServerResponse.prototype.send;
1688
1684
  if (server && !server.getPlugin('static')) {
1689
1685
  this.name = 'static'; // only first plugin instance is registered as
1690
1686
  const defSend = ServerResponse.prototype.send;
@@ -1744,11 +1740,27 @@ export class StaticFilesPlugin extends Plugin {
1744
1740
  req.filename = filename;
1745
1741
  return handler.call(this, req, res, next);
1746
1742
  }
1747
- this.serveFile(req, res, {
1748
- path: filename,
1749
- mimeType,
1750
- stats
1751
- });
1743
+ if (this.checkPrecompressedGzip && (req.headers['accept-encoding'] || '').includes('gzip')) {
1744
+ const gzipped = filename + '.gz';
1745
+ fs.stat(gzipped, (err, statsGz) => {
1746
+ if (!err && statsGz.isFile()) {
1747
+ res.setHeader('Content-Encoding', 'gzip');
1748
+ filename = gzipped;
1749
+ stats = statsGz;
1750
+ }
1751
+ this.serveFile(req, res, {
1752
+ path: filename,
1753
+ mimeType,
1754
+ stats
1755
+ });
1756
+ });
1757
+ }
1758
+ else
1759
+ this.serveFile(req, res, {
1760
+ path: filename,
1761
+ mimeType,
1762
+ stats
1763
+ });
1752
1764
  });
1753
1765
  }
1754
1766
  /** Send static file */
@@ -1802,9 +1814,9 @@ export class StaticFilesPlugin extends Plugin {
1802
1814
  statRes(null, options.stats);
1803
1815
  }
1804
1816
  }
1817
+ /** Reverse proxy plugin, At least remote url as string or in ProxyOptions is required */
1805
1818
  export class ProxyPlugin extends Plugin {
1806
1819
  priority = 120;
1807
- name = 'proxy';
1808
1820
  /** Default valid headers */
1809
1821
  static validHeaders = {
1810
1822
  authorization: true,
@@ -1838,6 +1850,8 @@ export class ProxyPlugin extends Plugin {
1838
1850
  options = { remote: options };
1839
1851
  if (!options.remote)
1840
1852
  throw new Error('Invalid param');
1853
+ if (server && !server.getPlugin('proxy'))
1854
+ this.name = 'proxy';
1841
1855
  this.remoteUrl = new URL(options.remote);
1842
1856
  this.regex = options.match ? new RegExp(options.match) : undefined;
1843
1857
  this.headers = options.headers;
@@ -2278,6 +2292,7 @@ export class AuthPlugin extends Plugin {
2278
2292
  }
2279
2293
  }
2280
2294
  // #endregion AuthPlugin
2295
+ /** Load standard plugins with predefined configs: MethodsPlugin, CorsPlugin, TrustProxyPlugin, BodyPlugin, AuthPlugin, StaticFilesPlugin */
2281
2296
  export class StandardPlugins extends Plugin {
2282
2297
  constructor(options, server) {
2283
2298
  super();
@@ -2293,9 +2308,9 @@ export class StandardPlugins extends Plugin {
2293
2308
  use(MethodsPlugin);
2294
2309
  use(CorsPlugin, 'cors');
2295
2310
  use(TrustProxyPlugin, 'trustproxy');
2311
+ use(AuthPlugin, 'auth');
2296
2312
  use(BodyPlugin);
2297
2313
  use(StaticFilesPlugin, 'static');
2298
- use(AuthPlugin, 'auth');
2299
2314
  }
2300
2315
  }
2301
2316
  /**
@@ -2763,7 +2778,7 @@ export class Models {
2763
2778
  }
2764
2779
  export class Model {
2765
2780
  static collections = new ModelCollectionsInternal();
2766
- static models = {};
2781
+ static models = new Models();
2767
2782
  static set db(db) {
2768
2783
  this.collections.db = db;
2769
2784
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radatek/microserver",
3
- "version": "3.0.6",
3
+ "version": "3.0.7",
4
4
  "description": "HTTP MicroServer",
5
5
  "author": "Darius Kisonas",
6
6
  "license": "MIT",