@radatek/microserver 2.3.1 → 2.3.3

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 2.3.1
3
+ * @version 2.3.3
4
4
  * @package @radatek/microserver
5
5
  * @copyright Darius Kisonas 2022
6
6
  * @license MIT
@@ -128,6 +128,7 @@ export interface WebSocketOptions {
128
128
  autoPong?: boolean;
129
129
  permessageDeflate?: boolean;
130
130
  maxWindowBits?: number;
131
+ timeout?: number;
131
132
  deflate?: boolean;
132
133
  }
133
134
  /** WebSocket class */
@@ -198,8 +199,8 @@ export declare class WebSocket extends EventEmitter {
198
199
  * ```
199
200
  */
200
201
  export declare class Controller<T extends Model<any> = any> {
201
- protected req: ServerRequest<T>;
202
- protected res: ServerResponse<T>;
202
+ req: ServerRequest<T>;
203
+ res: ServerResponse<T>;
203
204
  get model(): T | undefined;
204
205
  constructor(req: ServerRequest<T>, res: ServerResponse<T>);
205
206
  /** Generate routes for this controller */
@@ -213,12 +214,11 @@ export interface Middleware {
213
214
  plugin?: Plugin;
214
215
  }
215
216
  declare class Waiter {
216
- get busy(): boolean;
217
- startJob(): void;
217
+ isBusy(id?: string): boolean;
218
+ startJob(id?: string): void;
218
219
  endJob(id?: string): void;
219
220
  get nextId(): string;
220
- wait(id: string): Promise<void>;
221
- resolve(id: string): void;
221
+ wait(id?: string): Promise<void>;
222
222
  }
223
223
  /** Router */
224
224
  export declare class Router extends EventEmitter {
@@ -373,7 +373,7 @@ export declare class MicroServer extends EventEmitter {
373
373
  once(name: string, cb: Function): this;
374
374
  /** Add listener and call immediatelly for 'ready' */
375
375
  on(name: string, cb: Function): this;
376
- isReady(): boolean;
376
+ get isReady(): boolean;
377
377
  waitReady(): Promise<void>;
378
378
  waitPlugin(id: string): Promise<void>;
379
379
  /** Listen server, should be used only if config.listen is not set */
@@ -538,6 +538,33 @@ export interface AuthOptions {
538
538
  /** Interal next cache cleanup time */
539
539
  cacheCleanup?: number;
540
540
  }
541
+ export interface AuthOptionsInternal extends AuthOptions {
542
+ /** Authentication token */
543
+ token: Buffer;
544
+ /** Users */
545
+ users: (usr: string, psw?: string) => Promise<UserInfo | undefined>;
546
+ /** Default ACL */
547
+ defaultAcl: {
548
+ [key: string]: boolean;
549
+ };
550
+ /** Expire time in seconds */
551
+ expire: number;
552
+ /** Authentication mode */
553
+ mode: 'cookie' | 'token';
554
+ /** Authentication realm for basic authentication */
555
+ realm: string;
556
+ /** Redirect URL */
557
+ redirect: string;
558
+ /** Authentication cache */
559
+ cache: {
560
+ [key: string]: {
561
+ data: UserInfo;
562
+ time: number;
563
+ };
564
+ };
565
+ /** Interal next cache cleanup time */
566
+ cacheCleanup: number;
567
+ }
541
568
  /** Authentication class */
542
569
  export declare class Auth {
543
570
  /** Server request */
@@ -545,10 +572,8 @@ export declare class Auth {
545
572
  /** Server response */
546
573
  res: ServerResponse | undefined;
547
574
  /** Authentication options */
548
- options: AuthOptions;
549
- /** Get user function */
550
- users: ((usr: string, psw?: string, salt?: string) => Promise<UserInfo | undefined>);
551
- constructor(options?: AuthOptions);
575
+ options: AuthOptionsInternal;
576
+ constructor(options: AuthOptionsInternal, req?: ServerRequest, res?: ServerResponse);
552
577
  /** Decode token */
553
578
  decode(data: string): {
554
579
  data: string;
@@ -708,12 +733,22 @@ export type ModelDocument<T extends ModelSchema> = {
708
733
  export declare interface ModelCollections {
709
734
  collection(name: string): Promise<MicroCollection>;
710
735
  }
736
+ export declare class Models {
737
+ }
711
738
  export declare class Model<TSchema extends ModelSchema> {
712
- static collections?: ModelCollections;
713
- static models: Record<string, Model<any>>;
714
- static schema(schema: ModelSchema): ModelSchema;
739
+ static collections: ModelCollections;
740
+ static models: Models;
741
+ static set db(db: any);
742
+ static get db(): any;
743
+ /** Dynamic model extension */
744
+ static dynamic<T extends Model<any>>(model: T, options: {
745
+ controller?: Controller;
746
+ collection?: MicroCollection<any>;
747
+ req?: ServerRequest;
748
+ } | Controller): T;
749
+ static register<K extends string, T extends Model<any>>(name: K, model: T): typeof Model;
715
750
  /** Define model */
716
- static define<T extends ModelSchema>(name: string, schema: T, options?: {
751
+ static define<K extends string, T extends ModelSchema>(name: K, schema: T, options?: {
717
752
  collection?: MicroCollection | Promise<MicroCollection>;
718
753
  class?: typeof Model;
719
754
  }): Model<T>;
@@ -778,7 +813,7 @@ export declare interface FindOptions {
778
813
  }
779
814
  /** Collection factory */
780
815
  export declare class MicroCollectionStore {
781
- constructor(dataPath?: string);
816
+ constructor(dataPath?: string, storeTimeDelay?: number);
782
817
  /** Get collection */
783
818
  collection(name: string): Promise<MicroCollection>;
784
819
  }
package/microserver.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MicroServer
3
- * @version 2.3.1
3
+ * @version 2.3.3
4
4
  * @package @radatek/microserver
5
5
  * @copyright Darius Kisonas 2022
6
6
  * @license MIT
@@ -457,8 +457,10 @@ export class WebSocket extends EventEmitter {
457
457
  maxPayload: 1024 * 1024,
458
458
  permessageDeflate: false,
459
459
  maxWindowBits: 15,
460
+ timeout: 120000,
460
461
  ...options
461
462
  };
463
+ this._socket.setTimeout(this._options.timeout || 120000);
462
464
  const key = req.headers['sec-websocket-key'];
463
465
  const upgrade = req.headers.upgrade;
464
466
  const version = +(req.headers['sec-websocket-version'] || 0);
@@ -836,10 +838,6 @@ export class Controller {
836
838
  method = keyMatch[1];
837
839
  url = keyMatch[2].startsWith('/') ? keyMatch[2] : ('/' + prefix + keyMatch[2]);
838
840
  }
839
- if (!url && !method) {
840
- method = 'GET';
841
- url = '/' + prefix + key;
842
- }
843
841
  if (!method)
844
842
  return;
845
843
  let autoAcl = method.toLowerCase();
@@ -906,14 +904,7 @@ export class Controller {
906
904
  req.model = modelName instanceof Model ? modelName : Model.models[modelName];
907
905
  if (!obj.model)
908
906
  throw new InvalidData(modelName, 'model');
909
- const modelOptions = { ...obj.model.options, user: req.user, params: req.params };
910
- req.model = new Proxy(req.model, {
911
- get: (target, prop) => {
912
- if (prop === 'options')
913
- return modelOptions;
914
- return target[prop];
915
- }
916
- });
907
+ req.model = Model.dynamic(req.model, { controller: obj });
917
908
  }
918
909
  return func.apply(obj, req.paramsList);
919
910
  });
@@ -922,46 +913,53 @@ export class Controller {
922
913
  return routes;
923
914
  }
924
915
  }
925
- class Waiter {
916
+ class WaiterJob {
926
917
  constructor() {
927
- this._waiters = {};
928
- this._id = 0;
918
+ this._waiters = [];
929
919
  this._busy = 0;
930
920
  }
931
- get busy() {
932
- return this._busy > 0;
933
- }
934
- startJob() {
921
+ start() {
935
922
  this._busy++;
936
923
  }
937
- endJob(id) {
924
+ end() {
938
925
  this._busy--;
926
+ for (const resolve of this._waiters.splice(0))
927
+ resolve();
928
+ }
929
+ async wait() {
939
930
  if (!this._busy)
940
- this.resolve(id || 'ready');
931
+ return;
932
+ return new Promise(resolve => this._waiters.push(resolve));
941
933
  }
942
- get nextId() {
943
- return (++this._id).toString();
934
+ }
935
+ class Waiter {
936
+ constructor() {
937
+ this._id = 0;
938
+ this._waiters = {};
944
939
  }
945
- async wait(id) {
946
- return new Promise(resolve => (this._waiters[id] = this._waiters[id] || []).push(resolve));
940
+ isBusy(id) {
941
+ const waiter = this._waiters[id || 'ready'];
942
+ return !!waiter?._busy;
947
943
  }
948
- resolve(id) {
949
- const resolvers = this._waiters[id];
950
- if (resolvers)
951
- for (const resolve of resolvers)
952
- resolve(undefined);
953
- delete this._waiters[id];
944
+ startJob(id) {
945
+ let waiter = this._waiters[id || 'ready'];
946
+ if (!waiter)
947
+ waiter = this._waiters[id || 'ready'] = new WaiterJob();
948
+ waiter._busy++;
954
949
  }
955
- }
956
- class EmitterWaiter extends Waiter {
957
- constructor(emitter) {
958
- super();
959
- this._emitter = emitter;
950
+ endJob(id) {
951
+ const waiter = this._waiters[id || 'ready'];
952
+ if (waiter)
953
+ waiter.end();
960
954
  }
961
- resolve(id) {
962
- if (id === 'ready')
963
- this._emitter.emit(id);
964
- super.resolve(id);
955
+ get nextId() {
956
+ return (++this._id).toString();
957
+ }
958
+ async wait(id) {
959
+ const waiter = this._waiters[id || 'ready'];
960
+ if (!waiter)
961
+ return;
962
+ return waiter.wait();
965
963
  }
966
964
  }
967
965
  /** Router */
@@ -1361,7 +1359,7 @@ export class Router extends EventEmitter {
1361
1359
  if (plugin.routes)
1362
1360
  await this.use(isFunction(plugin.routes) ? await plugin.routes() : plugin.routes);
1363
1361
  if (added)
1364
- this._waiter.resolve(added);
1362
+ this.emit(added);
1365
1363
  }
1366
1364
  async waitPlugin(id) {
1367
1365
  if (!this.plugins[id])
@@ -1384,12 +1382,9 @@ export class Router extends EventEmitter {
1384
1382
  export class MicroServer extends EventEmitter {
1385
1383
  constructor(config) {
1386
1384
  super();
1387
- this._waiter = new EmitterWaiter(this);
1385
+ this._waiter = new Waiter();
1388
1386
  this._methods = {};
1389
1387
  let promise = Promise.resolve();
1390
- this._init = (f, ...args) => {
1391
- promise = promise.then(() => f.apply(this, args)).catch(e => this.emit('error', e));
1392
- };
1393
1388
  this.config = {
1394
1389
  maxBodySize: defaultMaxBodySize,
1395
1390
  methods: defaultMethods,
@@ -1406,17 +1401,20 @@ export class MicroServer extends EventEmitter {
1406
1401
  if (config[key])
1407
1402
  this.router.use(MicroServer.plugins[key], config[key]);
1408
1403
  }
1409
- if (config.listen)
1410
- this._init(() => {
1411
- this.listen({
1412
- tls: config.tls,
1413
- listen: config.listen || 8080
1414
- });
1404
+ if (config.listen) {
1405
+ this._waiter.startJob();
1406
+ this.listen({
1407
+ tls: config.tls,
1408
+ listen: config.listen || 8080
1409
+ }).then(() => {
1410
+ this._waiter.endJob();
1415
1411
  });
1412
+ }
1413
+ this._waiter.wait().then(() => this.emit('ready'));
1416
1414
  }
1417
1415
  /** Add one time listener or call immediatelly for 'ready' */
1418
1416
  once(name, cb) {
1419
- if (name === 'ready' && this.isReady())
1417
+ if (name === 'ready' && this.isReady)
1420
1418
  cb();
1421
1419
  else
1422
1420
  super.once(name, cb);
@@ -1424,18 +1422,18 @@ export class MicroServer extends EventEmitter {
1424
1422
  }
1425
1423
  /** Add listener and call immediatelly for 'ready' */
1426
1424
  on(name, cb) {
1427
- if (name === 'ready' && this.isReady())
1425
+ if (name === 'ready' && this.isReady)
1428
1426
  cb();
1429
1427
  super.on(name, cb);
1430
1428
  return this;
1431
1429
  }
1432
- isReady() {
1433
- return !this._waiter.busy;
1430
+ get isReady() {
1431
+ return !this._waiter.isBusy();
1434
1432
  }
1435
1433
  async waitReady() {
1436
- if (this.isReady())
1434
+ if (this.isReady)
1437
1435
  return;
1438
- return this._waiter.wait("ready");
1436
+ return this._waiter.wait();
1439
1437
  }
1440
1438
  async waitPlugin(id) {
1441
1439
  await this.router.waitPlugin(id);
@@ -1467,7 +1465,7 @@ export class MicroServer extends EventEmitter {
1467
1465
  }
1468
1466
  const reg = /^((?<proto>\w+):\/\/)?(?<host>(\[[^\]]+\]|[a-z][^:,]+|\d+\.\d+\.\d+\.\d+))?:?(?<port>\d+)?/;
1469
1467
  listen.split(',').forEach(listen => {
1470
- this._waiter.startJob();
1468
+ this._waiter.startJob('listen');
1471
1469
  let { proto, host, port } = reg.exec(listen)?.groups || {};
1472
1470
  let srv;
1473
1471
  switch (proto) {
@@ -1494,20 +1492,20 @@ export class MicroServer extends EventEmitter {
1494
1492
  }
1495
1493
  this.servers.add(srv);
1496
1494
  if (port === '0') // skip listening
1497
- this._waiter.endJob();
1495
+ this._waiter.endJob('listen');
1498
1496
  else {
1499
1497
  srv.listen(parseInt(port), host?.replace(/[\[\]]/g, '') || '0.0.0.0', () => {
1500
1498
  const addr = srv.address();
1501
1499
  this.emit('listen', addr.port, addr.address, srv);
1502
1500
  srv._ready = true;
1503
- this._waiter.endJob();
1501
+ this._waiter.endJob('listen');
1504
1502
  });
1505
1503
  }
1506
1504
  srv.on('error', err => {
1507
1505
  srv.close();
1508
1506
  this.servers.delete(srv);
1509
1507
  if (!srv._ready)
1510
- this._waiter.endJob();
1508
+ this._waiter.endJob('listen');
1511
1509
  this.emit('error', err);
1512
1510
  });
1513
1511
  srv.on('connection', s => {
@@ -1516,7 +1514,7 @@ export class MicroServer extends EventEmitter {
1516
1514
  });
1517
1515
  srv.on('upgrade', this.handlerUpgrade.bind(this));
1518
1516
  });
1519
- return this._waiter.wait('ready');
1517
+ return this._waiter.wait('listen');
1520
1518
  }
1521
1519
  /** Add middleware, routes, etc.. see {router.use} */
1522
1520
  use(...args) {
@@ -1663,7 +1661,6 @@ export class MicroServer extends EventEmitter {
1663
1661
  this.sockets.clear();
1664
1662
  }).then(() => {
1665
1663
  this.emit('close');
1666
- this._waiter.resolve("close");
1667
1664
  });
1668
1665
  }
1669
1666
  /** Add route, alias to `server.router.use(url, ...args)` */
@@ -2006,31 +2003,12 @@ ProxyPlugin.validHeaders = {
2006
2003
  MicroServer.plugins.proxy = ProxyPlugin;
2007
2004
  /** Authentication class */
2008
2005
  export class Auth {
2009
- constructor(options) {
2010
- let token = options?.token || defaultToken;
2011
- if (!token || token.length !== 32)
2012
- token = defaultToken;
2013
- if (token.length !== 32)
2014
- token = crypto.createHash('sha256').update(token).digest();
2015
- if (!(token instanceof Buffer))
2016
- token = Buffer.from(token);
2017
- this.options = {
2018
- token,
2019
- users: options?.users,
2020
- mode: options?.mode || 'cookie',
2021
- defaultAcl: options?.defaultAcl || { '*': false },
2022
- expire: options?.expire || defaultExpire,
2023
- cache: options?.cache || {}
2024
- };
2025
- if (typeof options?.users === 'function')
2026
- this.users = options.users;
2027
- else
2028
- this.users = async (usrid, psw) => {
2029
- const users = this.options.users;
2030
- const usr = users?.[usrid];
2031
- if (usr && (psw === undefined || this.checkPassword(usrid, psw, usr.password || '')))
2032
- return usr;
2033
- };
2006
+ constructor(options, req, res) {
2007
+ this.options = options;
2008
+ this.req = req;
2009
+ this.res = res;
2010
+ if (req)
2011
+ req.auth = this;
2034
2012
  }
2035
2013
  /** Decode token */
2036
2014
  decode(data) {
@@ -2106,7 +2084,7 @@ export class Auth {
2106
2084
  data = JSON.stringify(usr);
2107
2085
  else if (typeof usr === 'string') {
2108
2086
  if (psw !== undefined) {
2109
- const userInfo = await this.users(usr, psw);
2087
+ const userInfo = await this.options.users(usr, psw);
2110
2088
  data = userInfo?.id || userInfo?._id;
2111
2089
  }
2112
2090
  else
@@ -2123,7 +2101,7 @@ export class Auth {
2123
2101
  if (typeof usr === 'object')
2124
2102
  usrInfo = usr;
2125
2103
  if (typeof usr === 'string')
2126
- usrInfo = await this.users(usr, psw, options?.salt);
2104
+ usrInfo = await this.options.users(usr, psw);
2127
2105
  if (usrInfo?.id || usrInfo?._id) {
2128
2106
  const expire = Math.min(34560000, options?.expire || this.options.expire || defaultExpire);
2129
2107
  const expireTime = new Date().getTime() + expire * 1000;
@@ -2289,17 +2267,32 @@ class AuthPlugin extends Plugin {
2289
2267
  ...options,
2290
2268
  cacheCleanup: new Date().getTime()
2291
2269
  };
2292
- if (this.options.token === defaultToken)
2270
+ if (options?.token === defaultToken)
2293
2271
  console.warn('Default token in auth plugin');
2272
+ let token = options?.token || defaultToken;
2273
+ if (!token || token.length !== 32)
2274
+ token = defaultToken;
2275
+ if (token.length !== 32)
2276
+ token = crypto.createHash('sha256').update(token).digest();
2277
+ if (!(token instanceof Buffer))
2278
+ token = Buffer.from(token);
2279
+ this.options.token = token;
2280
+ if (typeof options?.users === 'function')
2281
+ this.options.users = (usr, psw) => options.users(usr, psw);
2282
+ else {
2283
+ this.options.users = async (usrid, psw) => {
2284
+ const users = this.options.users;
2285
+ const usr = users?.[usrid];
2286
+ if (usr && (psw === undefined || router.auth?.checkPassword(usrid, psw, usr.password || '')))
2287
+ return usr;
2288
+ };
2289
+ }
2294
2290
  router.auth = new Auth(this.options);
2295
2291
  }
2296
2292
  /** Authentication middleware */
2297
2293
  async handler(req, res, next) {
2298
2294
  const options = this.options, cache = options.cache;
2299
- const auth = new Auth(options);
2300
- req.auth = auth;
2301
- auth.req = req;
2302
- auth.res = res;
2295
+ const auth = new Auth(this.options, req, res);
2303
2296
  const authorization = req.headers.authorization || '';
2304
2297
  if (authorization.startsWith('Basic ')) {
2305
2298
  let usr = cache?.[authorization];
@@ -2308,7 +2301,7 @@ class AuthPlugin extends Plugin {
2308
2301
  else {
2309
2302
  const usrpsw = Buffer.from(authorization.slice(6), 'base64').toString('utf-8'), pos = usrpsw.indexOf(':'), username = usrpsw.slice(0, pos), psw = usrpsw.slice(pos + 1);
2310
2303
  if (username && psw)
2311
- req.user = await auth.users(username, psw);
2304
+ req.user = await auth.options.users(username, psw);
2312
2305
  if (!req.user)
2313
2306
  return res.error(401);
2314
2307
  if (cache) // 1 hour to expire in cache
@@ -2354,7 +2347,7 @@ class AuthPlugin extends Plugin {
2354
2347
  catch (e) { }
2355
2348
  }
2356
2349
  else {
2357
- req.user = await auth.users(usrData.data);
2350
+ req.user = await auth.options.users(usrData.data);
2358
2351
  if (!req.user) {
2359
2352
  req.auth.logout();
2360
2353
  return new AccessDenied();
@@ -2383,7 +2376,7 @@ export class FileStore {
2383
2376
  this._dir = options?.dir || 'data';
2384
2377
  this._cacheTimeout = options?.cacheTimeout || 2000;
2385
2378
  this._cacheItems = options?.cacheItems || 10;
2386
- this._debounceTimeout = options?.debounceTimeout || 1000;
2379
+ this._debounceTimeout = options?.debounceTimeout ?? 1000;
2387
2380
  this._iter = 0;
2388
2381
  }
2389
2382
  /** cleanup cache */
@@ -2435,16 +2428,16 @@ export class FileStore {
2435
2428
  if (item && new Date().getTime() - item.atime < this._cacheTimeout)
2436
2429
  return item.data;
2437
2430
  try {
2438
- const stat = await fs.promises.lstat(path.join(this._dir, name));
2439
- if (item?.mtime !== stat.mtime.getTime()) {
2440
- let data = JSON.parse(await fs.promises.readFile(path.join(this._dir, name), 'utf8') || '{}');
2431
+ const stat = await fs.promises.lstat(path.join(this._dir, name)).catch(() => null);
2432
+ if (!stat || item?.mtime !== stat.mtime.getTime()) {
2433
+ let data = stat ? JSON.parse(await fs.promises.readFile(path.join(this._dir, name), 'utf8').catch(() => '') || '{}') : {};
2441
2434
  this._iter++;
2442
2435
  this.cleanup();
2443
2436
  if (autosave)
2444
2437
  data = this.observe(data, () => this.save(name, data));
2445
2438
  this._cache[name] = {
2446
2439
  atime: new Date().getTime(),
2447
- mtime: stat.mtime.getTime(),
2440
+ mtime: stat?.mtime.getTime() || new Date().getTime(),
2448
2441
  data: data
2449
2442
  };
2450
2443
  return data;
@@ -2573,19 +2566,64 @@ function newObjectId() {
2573
2566
  break;
2574
2567
  return (new Date().getTime() / 1000 | 0).toString(16) + globalObjectId.toString('hex');
2575
2568
  }
2569
+ class ModelCollectionsInternal {
2570
+ constructor() {
2571
+ this._wait = new Promise(resolve => this._ready = resolve);
2572
+ }
2573
+ set db(db) {
2574
+ Promise.resolve(db).then(db => this._ready(this._db = db));
2575
+ }
2576
+ get db() {
2577
+ return this._db;
2578
+ }
2579
+ async collection(name) {
2580
+ const db = await this._wait;
2581
+ return db.collection(name);
2582
+ }
2583
+ }
2584
+ export class Models {
2585
+ }
2576
2586
  export class Model {
2577
- static schema(schema) {
2578
- return schema;
2587
+ static set db(db) {
2588
+ this.collections.db = db;
2589
+ }
2590
+ static get db() {
2591
+ return this.collections.db;
2592
+ }
2593
+ /** Dynamic model extension */
2594
+ static dynamic(model, options) {
2595
+ if (options instanceof Controller)
2596
+ options = { controller: options };
2597
+ const collection = options?.collection || model.collection;
2598
+ const modelOptions = { ...model.options };
2599
+ const req = options.req || options.controller?.req;
2600
+ if (req) {
2601
+ modelOptions.user = req.user;
2602
+ modelOptions.params = req.params;
2603
+ }
2604
+ return new Proxy(model, {
2605
+ get: (target, prop) => {
2606
+ if (prop === 'collection')
2607
+ return collection;
2608
+ if (prop === 'options')
2609
+ return modelOptions;
2610
+ return target[prop];
2611
+ }
2612
+ });
2613
+ }
2614
+ static register(name, model) {
2615
+ Model.models[name] = model;
2616
+ return this;
2579
2617
  }
2580
2618
  /** Define model */
2581
2619
  static define(name, schema, options) {
2582
2620
  options = options || {};
2583
- if (!options.collection && this.collections)
2621
+ if (!options.collection)
2584
2622
  options.collection = this.collections.collection(name);
2585
2623
  const inst = options?.class
2586
2624
  ? new options.class(schema, { name, ...options })
2587
2625
  : new Model(schema, { name, ...options });
2588
- Model.models[name] = inst;
2626
+ this.register(name, inst);
2589
2627
  return inst;
2590
2628
  }
2591
2629
  /** Create model acording to description */
@@ -2963,13 +3001,16 @@ export class Model {
2963
3001
  }
2964
3002
  }
2965
3003
  }
3004
+ Model.collections = new ModelCollectionsInternal();
2966
3005
  Model.models = {};
2967
3006
  /** Collection factory */
2968
3007
  export class MicroCollectionStore {
2969
- constructor(dataPath) {
3008
+ constructor(dataPath, storeTimeDelay) {
2970
3009
  this._collections = new Map();
2971
3010
  if (dataPath)
2972
- this._store = new FileStore({ dir: dataPath.replace(/^\w+:\/\//, '') });
3011
+ this._store = new FileStore({ dir: dataPath.replace(/^\w+:\/\//, ''), debounceTimeout: storeTimeDelay ?? 1000 });
3012
+ if (!Model.db)
3013
+ Model.db = this;
2973
3014
  }
2974
3015
  /** Get collection */
2975
3016
  async collection(name) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radatek/microserver",
3
- "version": "2.3.1",
3
+ "version": "2.3.3",
4
4
  "description": "HTTP MicroServer",
5
5
  "author": "Darius Kisonas",
6
6
  "license": "MIT",