5htp-core 0.3.0 → 0.3.1-2

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.
Files changed (35) hide show
  1. package/package.json +2 -2
  2. package/src/client/components/Select/index.tsx +4 -2
  3. package/src/client/services/router/index.tsx +22 -1
  4. package/src/client/services/router/response/index.tsx +1 -1
  5. package/src/common/errors/index.ts +20 -0
  6. package/src/common/validation/validators.ts +12 -1
  7. package/src/server/app/commands.ts +21 -1
  8. package/src/server/app/container/config.ts +1 -0
  9. package/src/server/app/container/index.ts +6 -3
  10. package/src/server/app/index.ts +41 -17
  11. package/src/server/app/service/container.ts +13 -24
  12. package/src/server/app/service/index.ts +118 -54
  13. package/src/server/app.tsconfig.json +2 -0
  14. package/src/server/index.ts +2 -1
  15. package/src/server/services/cache/index.ts +6 -4
  16. package/src/server/services/console/index.ts +7 -24
  17. package/src/server/services/cron/index.ts +5 -1
  18. package/src/server/services/database/datatypes.ts +1 -0
  19. package/src/server/services/database/index.ts +67 -41
  20. package/src/server/services/disks/driver.ts +5 -1
  21. package/src/server/services/disks/drivers/s3/index.ts +1 -1
  22. package/src/server/services/disks/index.ts +16 -13
  23. package/src/server/services/email/index.ts +7 -6
  24. package/src/server/services/email/transporter.ts +19 -6
  25. package/src/server/services/fetch/index.ts +3 -5
  26. package/src/server/services/router/http/index.ts +1 -1
  27. package/src/server/services/router/index.ts +10 -11
  28. package/src/server/services/router/response/index.ts +2 -2
  29. package/src/server/services/router/service.ts +9 -3
  30. package/src/server/services/schema/router/index.ts +1 -2
  31. package/src/server/services/security/encrypt/aes/index.ts +9 -6
  32. package/src/server/services/socket/index.ts +16 -17
  33. package/src/server/services/users/index.ts +5 -1
  34. package/src/server/services/users/router/index.ts +2 -2
  35. package/src/server/app/instance.ts +0 -3
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "5htp-core",
3
3
  "description": "Convenient TypeScript framework designed for Performance and Productivity.",
4
- "version": "0.3.0",
4
+ "version": "0.3.1-2",
5
5
  "author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
6
6
  "repository": "git://github.com/gaetanlegac/5htp-core.git",
7
7
  "license": "MIT",
@@ -97,6 +97,6 @@
97
97
  "babel-plugin-glob-import": "^0.0.7"
98
98
  },
99
99
  "peerDependencies": {
100
- "5htp": "0.3.0"
100
+ "5htp": "0.3.1"
101
101
  }
102
102
  }
@@ -6,11 +6,11 @@
6
6
  import React from 'react';
7
7
 
8
8
  // Core
9
- import Dropdown, { TDropdownControl, Props as DropdownProps } from '@client/components/dropdown';
9
+ import { Props as DropdownProps } from '@client/components/dropdown';
10
10
  import Input from '@client/components/inputv3';
11
11
 
12
12
  // Specific
13
- import ChoiceSelector, {
13
+ import {
14
14
  Props as SelectorProps,
15
15
  Choice,
16
16
  } from './ChoiceSelector';
@@ -24,6 +24,8 @@ export type Props = DropdownProps & SelectorProps & {
24
24
  errors?: string[],
25
25
  }
26
26
 
27
+ export type { Choice } from './ChoiceSelector';
28
+
27
29
  const ChoiceElement = ({ choice, currentList, onChange, multiple, includeCurrent }: {
28
30
  choice: Choice,
29
31
  currentList: Choice[],
@@ -40,7 +40,7 @@ import * as appRoutes from '@/client/pages/**/*.tsx';
40
40
  - CONFIG
41
41
  ----------------------------------*/
42
42
 
43
- const debug = true;
43
+ const debug = false;
44
44
  const LogPrefix = '[router]'
45
45
 
46
46
  /*----------------------------------
@@ -61,6 +61,8 @@ export type Response = ClientResponse<ClientRouter> | ServerResponse<ServerRoute
61
61
  - TYPES: ROUTES LOADING
62
62
  ----------------------------------*/
63
63
 
64
+ // WARN: To be updated with the mplemenations list of Router.page
65
+ // (both server and client side)
64
66
  export type TRegisterPageArgs<TProvidedData extends TFetcherList = {}, TRouter extends Router = Router> = ([
65
67
  path: string,
66
68
  controller: TDataProvider<TProvidedData> | null,
@@ -219,6 +221,25 @@ export default class ClientRouter<
219
221
  return currentRoute;
220
222
  }
221
223
 
224
+ public page(
225
+ path: string,
226
+ controller: TDataProvider<{}> | null,
227
+ renderer: TFrontRenderer<{}>
228
+ ): TRoute;
229
+
230
+ public page(
231
+ path: string,
232
+ options: Partial<TRoute["options"]>,
233
+ renderer: TFrontRenderer<{}>
234
+ ): TRoute;
235
+
236
+ public page(
237
+ path: string,
238
+ options: Partial<TRoute["options"]>,
239
+ controller: TDataProvider<{}> | null,
240
+ renderer: TFrontRenderer<{}>
241
+ ): TRoute;
242
+
222
243
  public page(...args: TRegisterPageArgs): TRoute {
223
244
 
224
245
  const { path, options, controller, renderer, layout } = getRegisterPageArgs(...args);
@@ -86,7 +86,7 @@ export default class ClientPageResponse<
86
86
  route: this.route,
87
87
  api: this.request.api,
88
88
 
89
- ...this.request.router.config.context()
89
+ ...this.request.router.config.context( this.request.router )
90
90
  }
91
91
 
92
92
  context.context = context;
@@ -16,6 +16,26 @@ type TDetailsErreur = {
16
16
  urlRequete?: string,
17
17
  }
18
18
 
19
+ /*----------------------------------
20
+ - TYPES: BUG REPORT
21
+ ----------------------------------*/
22
+
23
+ export type ServerBug = {
24
+ // Context
25
+ hash: string,
26
+ date: Date, // Timestamp
27
+ channelType?: string,
28
+ channelId?: string,
29
+
30
+ user: string | null | undefined,
31
+ ip: string | null | undefined,
32
+
33
+ // Error
34
+ error: Error,
35
+ stacktrace: string,
36
+ logs: string,
37
+ }
38
+
19
39
  /*----------------------------------
20
40
  - ERREURS
21
41
  ----------------------------------*/
@@ -10,6 +10,8 @@ import {
10
10
  isURL
11
11
  } from 'validator';
12
12
 
13
+ import normalizeUrl, { Options as NormalizeUrlOptions } from 'normalize-url';
14
+
13
15
  // Core
14
16
  import { InputError } from '@common/errors';
15
17
  import FileToUpload from '@client/components/inputv3/file/FileToUpload';
@@ -157,16 +159,25 @@ export default class SchemaValidators {
157
159
 
158
160
  }, opts)
159
161
 
160
- public url = (opts: TValidator<string> & {} = {}) =>
162
+ public url = (opts: TValidator<string> & {
163
+ normalize?: NormalizeUrlOptions
164
+ } = {}) =>
161
165
  new Validator<string>('url', (inputVal, input, output, corriger?) => {
162
166
 
163
167
  let val = this.string(opts).validate(inputVal, input, output, corriger);
164
168
 
169
+ // Check if URL
165
170
  if (!isURL(val, {
166
171
  // https://www.npmjs.com/package/validator
167
172
  }))
168
173
  throw new InputError(`Please provide a valid URL.`);
169
174
 
175
+ // Normalize
176
+ if (opts.normalize !== undefined)
177
+ val = normalizeUrl(val, opts.normalize);
178
+
179
+ console.log("@@@@@@@@@@@@@NORMALISZE URL", opts.normalize, val);
180
+
170
181
  return val;
171
182
  }, opts)
172
183
 
@@ -41,15 +41,35 @@ export type Hooks = {
41
41
 
42
42
  }
43
43
 
44
+ export type Services = {
45
+
46
+ }
47
+
44
48
  /*----------------------------------
45
49
  - SERVICE
46
50
  ----------------------------------*/
47
- export default class CommandsManager extends Service<Config, Hooks, Application> {
51
+ export default class CommandsManager extends Service<Config, Hooks, Application, Services> {
48
52
 
49
53
  public priority = 2 as 2;
50
54
 
51
55
  public commandsIndex: CommandsList = {}
52
56
 
57
+ /*----------------------------------
58
+ - LIFECYCLE
59
+ ----------------------------------*/
60
+
61
+ protected async start() {
62
+
63
+ }
64
+
65
+ protected async ready() {
66
+
67
+ }
68
+
69
+ protected async shutdown() {
70
+
71
+ }
72
+
53
73
  /*----------------------------------
54
74
  - DEFINITIONS
55
75
  ----------------------------------*/
@@ -35,6 +35,7 @@ export type TEnvConfig = {
35
35
  type AppIdentityConfig = {
36
36
 
37
37
  name: string,
38
+ identifier: string,
38
39
  description: string,
39
40
  author: {
40
41
  name: string,
@@ -8,19 +8,22 @@ import './patch';
8
8
  import path from 'path';
9
9
 
10
10
  // Core
11
- import Services from '../service/container';
11
+ import type { StartedServicesIndex } from '../service';
12
+ import Services, { ServicesContainer } from '../service/container';
12
13
  import ConfigParser, { TEnvConfig } from './config';
13
14
 
14
15
  /*----------------------------------
15
16
  - CLASS
16
17
  ----------------------------------*/
17
- export class ApplicationContainer {
18
+ export class ApplicationContainer<
19
+ TServicesIndex extends StartedServicesIndex = StartedServicesIndex
20
+ > {
18
21
 
19
22
  /*----------------------------------
20
23
  - INIT
21
24
  ----------------------------------*/
22
25
 
23
- public Services = Services;
26
+ public Services = Services as ServicesContainer<TServicesIndex>;
24
27
  public Environment: TEnvConfig;
25
28
  public Identity: Config.Identity;
26
29
 
@@ -4,9 +4,14 @@
4
4
 
5
5
  // Core
6
6
  import AppContainer from './container';
7
- import ApplicationService, { AnyService } from './service';
7
+ import ApplicationService, { StartedServicesIndex } from './service';
8
8
  import CommandsManager from './commands';
9
- import ServicesContainer, { TRegisteredService, TServiceMetas } from './service/container';
9
+ import ServicesContainer, {
10
+ ServicesContainer as ServicesContainerClass,
11
+ TRegisteredService,
12
+ TServiceMetas
13
+ } from './service/container';
14
+ import type { ServerBug } from '../services/console';
10
15
 
11
16
  // Built-in
12
17
  import type { default as Router, Request as ServerRequest } from '@server/services/router';
@@ -48,7 +53,12 @@ export const Service = ServicesContainer;
48
53
  /*----------------------------------
49
54
  - FUNCTIONS
50
55
  ----------------------------------*/
51
- export class Application extends ApplicationService<Config, Hooks, /* TODO: this ? */Application> {
56
+ export class Application<
57
+ TServicesContainer extends ServicesContainerClass = ServicesContainerClass
58
+ > extends ApplicationService<Config, Hooks, /* TODO: this ? */Application, {}> {
59
+
60
+ public app!: this;
61
+ public servicesContainer!: TServicesContainer;
52
62
 
53
63
  /*----------------------------------
54
64
  - PROPERTIES
@@ -72,8 +82,8 @@ export class Application extends ApplicationService<Config, Hooks, /* TODO: this
72
82
  public debug: boolean = false;
73
83
  public launched: boolean = false;
74
84
 
75
- // All service instances by service id
76
- public allServices: {[serviceId: string]: AnyService} = {}
85
+ // Mandatory services
86
+ public Console = this.use('Core/Console');
77
87
 
78
88
  /*----------------------------------
79
89
  - INIT
@@ -81,10 +91,14 @@ export class Application extends ApplicationService<Config, Hooks, /* TODO: this
81
91
 
82
92
  public constructor() {
83
93
 
94
+ const self = 'self' as unknown as Application;
95
+
84
96
  // Application itself doesnt have configuration
85
97
  // Configuration must be handled by application services
86
- super({}, {}, {}, {});
98
+ super(self, {}, {}, self);
87
99
 
100
+ // We can't pass this in super so we assign here
101
+ this.parent = this;
88
102
  this.app = this;
89
103
 
90
104
  // Gestion crash
@@ -113,9 +127,9 @@ export class Application extends ApplicationService<Config, Hooks, /* TODO: this
113
127
  console.log(`5HTP Core`, process.env.npm_package_version);
114
128
 
115
129
  // Handle errors & crashs
116
- this.on('error', this.error.bind(this))
130
+ this.on('error', e => this.Console.createBugReport(e))
117
131
 
118
- this.debug && console.info(`[boot] Start services`);
132
+ console.info(`[boot] Start services`);
119
133
  await this.startServices();
120
134
 
121
135
  this.debug && console.info(`[boot] App ready`);
@@ -132,8 +146,10 @@ export class Application extends ApplicationService<Config, Hooks, /* TODO: this
132
146
  }
133
147
 
134
148
  // Default error handler
135
- public async error( e: Error ) {
136
- console.error( e );
149
+ public async reportBug( bug: ServerBug ) {
150
+
151
+ console.error( bug.error );
152
+
137
153
  }
138
154
 
139
155
  public async shutdown() {
@@ -150,30 +166,38 @@ export class Application extends ApplicationService<Config, Hooks, /* TODO: this
150
166
 
151
167
  // Don't check services prop as it will trigger an error (it's a proxy)
152
168
  // TODO: exclude all properties coming from the Service class itself
153
- if (propName === 'services' || typeof this[ propName ] !== 'object')
169
+ if (propName === 'services' || this[ propName ] === undefined)
154
170
  continue;
155
171
 
156
172
  // Check if this property is a service registration
157
- const registered = this[ propName ] as TRegisteredService;
158
- if (registered?.type !== 'service')
173
+ const service = this[ propName ] as TRegisteredService;
174
+ const isService = (
175
+ service instanceof ApplicationService
176
+ ||
177
+ (
178
+ typeof service === 'function'
179
+ &&
180
+ service.serviceInstance instanceof ApplicationService
181
+ )
182
+ )
183
+ if (!isService)
159
184
  continue;
160
185
 
161
186
  // Instanciate the service
162
- const service = this.registerService( propName, registered );
163
- this[ propName ] = service;
187
+ this.bindService( propName, service );
164
188
 
165
189
  // Register commands
166
190
  if (service.commands)
167
191
  this.commandsManager.fromList( service.commands );
168
192
 
169
193
  // Start service
170
- await this.startService( service );
194
+ await this.startService( propName, service );
171
195
  }
172
196
 
173
197
  // Check if any setup service has not been used
174
198
  const unused: string[] = []
175
199
  for (const serviceNS in ServicesContainer.registered)
176
- if (this.allServices[ serviceNS ] === undefined)
200
+ if (ServicesContainer.allServices[ serviceNS ] === undefined)
177
201
  unused.push(serviceNS);
178
202
 
179
203
  if (unused.length !== 0)
@@ -4,7 +4,7 @@
4
4
 
5
5
  // Specific
6
6
  import type {
7
- AnyService,
7
+ AnyService, StartedServicesIndex,
8
8
  // Hooks
9
9
  THookCallback, THooksIndex
10
10
  } from ".";
@@ -46,13 +46,18 @@ const LogPrefix = '[service]';
46
46
  /*----------------------------------
47
47
  - CLASS
48
48
  ----------------------------------*/
49
- export class ServicesContainer {
49
+ export class ServicesContainer<
50
+ TServicesIndex extends StartedServicesIndex = StartedServicesIndex
51
+ > {
50
52
 
51
53
  public registered: TRegisteredServicesIndex = {}
52
54
 
53
- public setup(
54
- serviceId: string,
55
- serviceConfig: {}
55
+ // All service instances by service id
56
+ public allServices: TServicesIndex = {} as TServicesIndex
57
+
58
+ public setup<TServiceId extends keyof TServicesIndex>(
59
+ serviceId: keyof TServicesIndex,
60
+ serviceConfig: TServicesIndex[TServiceId]["config"]
56
61
  ): TRegisteredService {
57
62
 
58
63
  // Check if the service is installed & has been indexed
@@ -86,25 +91,6 @@ export class ServicesContainer {
86
91
  return service;
87
92
  }
88
93
 
89
- public use(
90
- serviceId: string,
91
- // TODO: Only subservices types supported by the parent service
92
- subServices: TRegisteredServicesIndex = {}
93
- ): TRegisteredService {
94
-
95
- // Check of the service has been configurated
96
- const registered = this.registered[ serviceId ];
97
- if (registered === undefined)
98
- throw new Error(`Unable to use service "${serviceId}": This one hasn't been setup.`);
99
-
100
- // Bind subservices
101
- registered.subServices = subServices;
102
-
103
- // Return the service metas
104
- // The parent service will take care of instanciating & starting it
105
- return registered;
106
- }
107
-
108
94
  public callableInstance = <TInstance extends object, TCallableName extends keyof TInstance>(
109
95
  instance: TInstance,
110
96
  funcName: TCallableName
@@ -129,6 +115,9 @@ export class ServicesContainer {
129
115
  ? instance[ method ].bind( instance )
130
116
  : instance[ method ];
131
117
 
118
+ // Allow us to recognize a callable as a service
119
+ callable.serviceInstance = instance;
120
+
132
121
  return callable as TInstance[TCallableName] & TInstance;
133
122
  }
134
123
  }
@@ -6,12 +6,14 @@
6
6
  import type { Application } from "..";
7
7
  import type { Command } from "../commands";
8
8
  import type { TServiceMetas, TRegisteredServicesIndex, TRegisteredService } from './container';
9
+ import ServicesContainer from './container';
9
10
 
10
11
  /*----------------------------------
11
12
  - TYPES: OPTIONS
12
13
  ----------------------------------*/
13
14
 
14
- export type AnyService = Service<{}, {}, Application, TStartedServicesIndex>
15
+ export type AnyService<TSubServices extends StartedServicesIndex = StartedServicesIndex> =
16
+ Service<{}, {}, Application, TSubServices>
15
17
 
16
18
  type TServiceConfig = {
17
19
  debug?: boolean
@@ -39,6 +41,12 @@ export type StartedServicesIndex = {
39
41
  [serviceId: string]: AnyService
40
42
  }
41
43
 
44
+ type ClassType<T = {}> = new (...args: any[]) => T;
45
+
46
+ type ServiceWithSubservices<TService extends ClassType<AnyService>, TSubservices extends StartedServicesIndex> = {
47
+ new (...args: any[]): TService & { services: TSubservices }
48
+ };
49
+
42
50
  /*----------------------------------
43
51
  - CONFIG
44
52
  ----------------------------------*/
@@ -52,7 +60,7 @@ export default abstract class Service<
52
60
  TConfig extends TServiceConfig,
53
61
  THooks extends THooksList,
54
62
  TApplication extends Application,
55
- TServicesIndex extends StartedServicesIndex = StartedServicesIndex
63
+ TServicesIndex extends StartedServicesIndex
56
64
  > {
57
65
 
58
66
  public started?: Promise<void>;
@@ -62,26 +70,34 @@ export default abstract class Service<
62
70
  public metas!: TServiceMetas;
63
71
  public bindings: string[] = []
64
72
 
65
- public static createInstance?: (
66
- parent: AnyService,
67
- config: TServiceConfig,
68
- services: TRegisteredServicesIndex,
69
- app: Application
70
- ) => Service<TServiceConfig, THooksList, Application, StartedServicesIndex>;
73
+ public app: TApplication;
71
74
 
72
75
  public constructor(
73
- public parent: AnyService,
76
+ public parent: AnyService | 'self',
74
77
  public config: TConfig,
75
- services: TRegisteredServicesIndex,
76
- public app: TApplication
78
+ // Make this argument appear as instanciated sercices index
79
+ // But actually, Setup.use returns a registered service, not yet launched
80
+ services: TServicesIndex,
81
+ app: TApplication | 'self'
77
82
  ) {
78
83
 
84
+ if (this.parent === 'self')
85
+ this.parent = this;
86
+
87
+ this.app = app === 'self'
88
+ ? this as unknown as TApplication
89
+ : app
90
+
79
91
  // Instanciate subservices
80
92
  for (const localName in services)
81
- this.registerService( localName, services[localName] );
93
+ this.bindService( localName, services[localName] as unknown as TRegisteredService );
82
94
 
83
95
  }
84
96
 
97
+ public getServiceInstance() {
98
+ return this;
99
+ }
100
+
85
101
  /*----------------------------------
86
102
  - LIFECYCLE
87
103
  ----------------------------------*/
@@ -90,7 +106,7 @@ export default abstract class Service<
90
106
 
91
107
  // Instanciate subservices
92
108
  for (const localName in this.services)
93
- await this.startService( this.services[localName] );
109
+ await this.startService( localName, this.services[localName] );
94
110
 
95
111
  // Start service
96
112
  if (this.start)
@@ -112,66 +128,114 @@ export default abstract class Service<
112
128
  - SUBSERVICES
113
129
  ----------------------------------*/
114
130
 
115
- public services: TRegisteredServicesIndex = {} as TRegisteredServicesIndex/*new Proxy({}, {
131
+ public registered: TRegisteredServicesIndex = {} as TRegisteredServicesIndex;
132
+
133
+ public services: TServicesIndex = {} as TServicesIndex;
134
+
135
+ /*new Proxy({}, {
116
136
  get: (target, prop, recever) => {
117
137
  if (!( prop in target )) {
118
138
 
119
- throw new Error(`You made reference to the "${prop}" service, but this one hasn't been loaded yet. Loaded services: ` + Object.keys(this.services).join(', '));
139
+ throw new Error(`You made reference to the "${prop}" service, but this one hasn't been loaded or started yet.
140
+ Registered services: ${Object.keys(this.services).join(', ')} ;
141
+ Loaded services: ${Object.keys(this.services).join(', ')}
142
+ `);
120
143
  }
121
144
  }
122
145
  }) as TRegisteredServicesIndex*/
123
146
 
124
- protected registerService( localName: string, registered: TRegisteredService ): AnyService {
147
+ // this.use immediatly instanciate the subservice for few reasons:
148
+ // - The subservice instance can be accesses from another service in the constructor, no matter the order of loading of the services
149
+ // - Consistency: the subserviuce proprties shouldn't be assogned to two different values according depending on the app lifecycle
150
+ public use<
151
+ TInstalledServices extends this["app"]["servicesContainer"]["allServices"],
152
+ TServiceId extends keyof TInstalledServices,
153
+ TServiceClass extends TInstalledServices[TServiceId],
154
+ TSubServices extends TServiceClass["services"],
155
+ >(
156
+ serviceId: TServiceId,
157
+ subServices?: TSubServices
158
+ ): (
159
+ {
160
+ new (...args: any[]): TServiceClass & { services: TSubServices }
161
+ }
162
+ /*Omit<TServiceClass, 'services'> & {
163
+ services: TSubServices
164
+ }*/
165
+ ) {
125
166
 
126
- // Service already instabciates on the app scope
127
- let service = this.app.allServices[ registered.metas.id ];
167
+ // Check of the service has been configurated
168
+ const registered = ServicesContainer.registered[ serviceId ];
169
+ if (registered === undefined)
170
+ throw new Error(`Unable to use service "${serviceId}": This one hasn't been setup.`);
128
171
 
129
- // Service not yet instanciated
130
- if (service === undefined) {
172
+ // Bind subservices
173
+ registered.subServices = subServices || {};
131
174
 
132
- // Instanciate
133
- console.log(`[app] Load service`, registered.metas.id);
134
- const ServiceClass = registered.metas.class().default;
135
- // Create class instance
136
- service = ServiceClass.createInstance !== undefined
137
- ? ServiceClass.createInstance(this, registered.config, registered.subServices, this.app)
138
- : new ServiceClass(this, registered.config, registered.subServices, this.app);
175
+ // Check if not already instanciated
176
+ const existing = ServicesContainer.allServices[ serviceId ];
177
+ if (existing !== undefined) {
178
+ console.info("Service", serviceId, "already instanciated through another service.");
179
+ return existing;
180
+ }
139
181
 
140
- service.metas = registered.metas;
182
+ // Instanciate
183
+ console.log(`[app] Load service`, registered.metas.id);
184
+ const ServiceClass = registered.metas.class().default;
141
185
 
142
- } else {
143
- console.warn(`[app] Service`, registered.metas.id, 'already instanciated in this app.');
144
- }
186
+ // Create class instance
187
+ const service = new ServiceClass(this, registered.config, registered.subServices, this.app || this)
188
+ .getServiceInstance()
145
189
 
146
- // Bind to app
147
- this.services[ localName ] = service;
148
- service.bindings.push(this.constructor.name + '.' + localName);
149
- this.app.allServices[ registered.metas.id ] = service;
190
+ // Bind his own metas
191
+ service.metas = registered.metas;
192
+ ServicesContainer.allServices[ registered.metas.id ] = service;
150
193
 
151
194
  return service;
152
-
153
195
  }
154
196
 
155
- protected async startService( service: AnyService ) {
197
+ public bindService( localName: string, service: AnyService ) {
156
198
 
157
- // Service already started
158
- if (service.started)
159
- return;
160
-
161
- // Start servuce & eventually his subservices
162
- console.log(`[app] Start service`, service.metas.id);
163
- service.status = 'starting';
164
- service.started = service.launch();
165
- await service.started.catch(e => {
166
- console.error("Catched error while starting service " + service.metas.id, e);
167
- if (this.app.env.profile === 'prod')
168
- process.exit();
169
- else
170
- throw e;
171
- })
199
+ const serviceScope = this.constructor.name + '.' + localName;
200
+
201
+ // Fix the parent service (the app could be provided as parent because the dev called this.use() in the app class definition)
202
+ service.parent = this;
203
+
204
+ // Bind subservice to service
205
+ //console.log(`Binding service ${serviceScope}`);
206
+ service.bindings.push(serviceScope);
207
+
208
+ // Since now we have the localname, we can bind the service in this.service too
209
+ this.services[localName] = service;
172
210
 
173
- console.log(`[app] Service`, service.metas.id, 'started (bound to:', service.bindings.join(', '),')');
174
- service.status = 'running';
211
+ // For serices that have been passed through this.use
212
+ if ((localName in this) && this[localName] === undefined)
213
+ this[ localName ] = service;
214
+
215
+ }
216
+
217
+ public async startService( localName: string, service: AnyService ) {
218
+ // Service already started
219
+ if (!service.started) {
220
+
221
+ const serviceScope = this.constructor.name + '.' + localName;
222
+
223
+ // Start servuce & eventually his subservices
224
+ console.log(`[app] Start service`, serviceScope);
225
+ service.status = 'starting';
226
+ service.started = service.launch();
227
+ await service.started.catch(e => {
228
+ console.error("Catched error while starting service " + serviceScope, e);
229
+ if (this.app.env.profile === 'prod')
230
+ process.exit();
231
+ else
232
+ throw e;
233
+ })
234
+
235
+ // Bind to app
236
+ console.log(`[app] Service`, serviceScope, 'started (bound to:', service.bindings.join(', '),')');
237
+ service.status = 'running';
238
+ }
175
239
  }
176
240
 
177
241
  /*----------------------------------