proteum 2.2.7 → 2.2.8

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.
@@ -120,12 +120,14 @@ export const generateControllerArtifacts = async () => {
120
120
  const connectedManifestControllers: TProteumManifestController[] = [];
121
121
 
122
122
  connectedProjectContracts.forEach(({ namespace, cachedContractFilepath, contract, sourceKind, sourceValue, typeImportModuleSpecifier, typingMode }) => {
123
+ let connectedTypeName: string | null = null;
124
+
123
125
  if (typingMode === 'local-typed' && typeImportModuleSpecifier) {
124
- const typeName = `ConnectedControllers_${namespace.replace(/[^A-Za-z0-9_$]+/g, '_')}`;
126
+ connectedTypeName = `ConnectedControllers_${namespace.replace(/[^A-Za-z0-9_$]+/g, '_')}`;
125
127
  connectedControllerTypeImports.push(
126
- `import type { TConnectedControllers as ${typeName} } from ${JSON.stringify(typeImportModuleSpecifier)};`,
128
+ `import type { TConnectedControllers as ${connectedTypeName} } from ${JSON.stringify(typeImportModuleSpecifier)};`,
127
129
  );
128
- typeTree[namespace] = JSON.stringify({ rawType: typeName });
130
+ typeTree[namespace] = JSON.stringify({ rawType: connectedTypeName });
129
131
  } else {
130
132
  typeTree[namespace] = JSON.stringify({ runtimeOnly: true });
131
133
  }
@@ -164,7 +166,9 @@ export const generateControllerArtifacts = async () => {
164
166
  hasInput: controller.hasInput,
165
167
  httpPath: controller.httpPath,
166
168
  methodName: controller.methodName,
167
- resultType: 'unknown',
169
+ resultType: connectedTypeName
170
+ ? `TConnectedControllerResult<${connectedTypeName}, ${JSON.stringify(controller.clientAccessor)}>`
171
+ : 'unknown',
168
172
  }),
169
173
  );
170
174
  });
@@ -228,6 +232,28 @@ type TControllerResult<TController, TMethod extends keyof TController> =
228
232
 
229
233
  type TControllerFetcher<TController, TMethod extends keyof TController> = TFetcher<TControllerResult<TController, TMethod>>;
230
234
 
235
+ type TConnectedFallbackValue =
236
+ | string
237
+ | number
238
+ | boolean
239
+ | null
240
+ | TConnectedFallbackValue[]
241
+ | { [key: string]: TConnectedFallbackValue | undefined };
242
+
243
+ type TConnectedControllerLeaf<TControllerTree, TAccessor extends string> =
244
+ TAccessor extends \`${'${infer THead}.${infer TTail}'}\`
245
+ ? THead extends keyof TControllerTree
246
+ ? TConnectedControllerLeaf<TControllerTree[THead], TTail>
247
+ : undefined
248
+ : TAccessor extends keyof TControllerTree
249
+ ? TControllerTree[TAccessor]
250
+ : undefined;
251
+
252
+ type TConnectedControllerResult<TControllerTree, TAccessor extends string> =
253
+ TConnectedControllerLeaf<TControllerTree, TAccessor> extends (...args: infer TArgs) => TFetcher<infer TResult>
254
+ ? Awaited<TResult>
255
+ : TConnectedFallbackValue;
256
+
231
257
  export type TControllers = ${printControllerTree(typeTree, typeLeaf)};
232
258
 
233
259
  export const createControllers = (
@@ -711,6 +711,68 @@ ${classMembers.join('\n')}
711
711
  };
712
712
  };
713
713
 
714
+ const isNamespaceImportDeclaration = (statement: ts.ImportDeclaration) =>
715
+ statement.importClause?.namedBindings !== undefined && ts.isNamespaceImport(statement.importClause.namedBindings);
716
+
717
+ const isClientServerStubSupportStatement = (statement: ts.Statement, appClassIdentifier: string) => {
718
+ if (ts.isTypeAliasDeclaration(statement)) {
719
+ return !new Set([
720
+ `${appClassIdentifier}Disks`,
721
+ `${appClassIdentifier}Router`,
722
+ `${appClassIdentifier}RouterPlugins`,
723
+ ]).has(statement.name.text);
724
+ }
725
+
726
+ return ts.isInterfaceDeclaration(statement) || ts.isModuleDeclaration(statement);
727
+ };
728
+
729
+ const createClientServerIndexDeclaration = (rootServices: TParsedService[]) => {
730
+ const sourceFile = createSourceFile(getAppServerEntryFilepath());
731
+ const appClass = getDefaultExportClassDeclaration(sourceFile);
732
+ const serviceTypesByRegisteredName = new Map(
733
+ rootServices.map((service) => [service.registeredName, `import("${service.importPath}").default`] as const),
734
+ );
735
+
736
+ const imports = sourceFile.statements
737
+ .filter((statement): statement is ts.ImportDeclaration => ts.isImportDeclaration(statement))
738
+ .filter((statement) => !isNamespaceImportDeclaration(statement))
739
+ .map((statement) => statement.getText(sourceFile));
740
+ const supportStatements = sourceFile.statements
741
+ .slice(0, sourceFile.statements.indexOf(appClass))
742
+ .filter((statement) => isClientServerStubSupportStatement(statement, app.identity.identifier))
743
+ .map((statement) => statement.getText(sourceFile));
744
+ const heritageClauses = appClass.heritageClauses?.map((clause) => clause.getText(sourceFile)).join(' ');
745
+ const properties = appClass.members
746
+ .filter((member): member is ts.PropertyDeclaration => ts.isPropertyDeclaration(member))
747
+ .filter((member) => !isPrivateOrProtectedInstanceMember(member))
748
+ .map((property) => {
749
+ const propertyName = getPropertyNameText(property.name);
750
+ if (!propertyName) return undefined;
751
+
752
+ const propertyType =
753
+ propertyName === 'Disks'
754
+ ? 'object'
755
+ : propertyName === 'Router'
756
+ ? `${app.identity.identifier}Router`
757
+ : property.type?.getText(sourceFile) || serviceTypesByRegisteredName.get(propertyName);
758
+ if (!propertyType) return undefined;
759
+
760
+ return ` public ${propertyName}: ${propertyType};`;
761
+ })
762
+ .filter((property): property is string => property !== undefined);
763
+
764
+ return `${imports.join('\n')}
765
+
766
+ ${supportStatements.join('\n\n')}
767
+
768
+ type ${app.identity.identifier}Router = Router<${app.identity.identifier}>;
769
+
770
+ export default class ${app.identity.identifier}${heritageClauses ? ` ${heritageClauses}` : ''} {
771
+ ${properties.join('\n')}
772
+ }
773
+ `;
774
+ };
775
+
714
776
  const resolveManifestService = (service: TParsedService, parent: string): TProteumManifestService => ({
715
777
  kind: 'service',
716
778
  registeredName: service.registeredName,
@@ -730,6 +792,11 @@ export const generateServiceArtifacts = () => {
730
792
  const routerPluginServices = routerPlugins.map((service) => resolveManifestService(service, 'Router.plugins'));
731
793
  const commandServiceStubs = createCommandServiceStubDeclarations(rootServices);
732
794
 
795
+ writeIfChanged(
796
+ path.join(app.paths.client.generated, 'server-index.d.ts'),
797
+ createClientServerIndexDeclaration(rootServices),
798
+ );
799
+
733
800
  writeIfChanged(
734
801
  path.join(app.paths.client.generated, 'services.d.ts'),
735
802
  `declare type ${appClassIdentifier} = import("@/server/index").default;
@@ -737,18 +804,9 @@ export const generateServiceArtifacts = () => {
737
804
  declare module "@app" {
738
805
 
739
806
  import { ${appClassIdentifier} as ${appClassIdentifier}Client } from "@/client";
740
- import ${appClassIdentifier}Server from "@/server/index";
741
807
 
742
808
  export const Router: ${appClassIdentifier}Client['Router'];
743
809
 
744
- ${rootServices
745
- .map((service) =>
746
- service.registeredName !== 'Router'
747
- ? `export const ${service.registeredName}: ${appClassIdentifier}Server["${service.registeredName}"];`
748
- : '',
749
- )
750
- .join('\n')}
751
-
752
810
  }
753
811
 
754
812
  declare module '@models/types' {
@@ -894,26 +952,6 @@ declare module "@app" {
894
952
  export = ServerServices
895
953
  }
896
954
 
897
- declare module '@server/app' {
898
-
899
- import { Application } from "@server/app";
900
- import { Environment } from "@server/app";
901
- import { ServicesContainer } from "@server/app/service/container";
902
-
903
- abstract class ApplicationWithServices extends Application<
904
- ServicesContainer<InstalledServices>
905
- > {}
906
-
907
- export interface Exported {
908
- Application: typeof ApplicationWithServices,
909
- Environment: Environment,
910
- }
911
-
912
- const foo: Exported;
913
-
914
- export = foo;
915
- }
916
-
917
955
  declare module '@common/errors' {
918
956
 
919
957
  export * from '@common/errors/index';
@@ -191,6 +191,7 @@ export const createClientTsconfigTemplate = (paths: TTsconfigTemplatePaths) => `
191
191
  "@server/*": [${JSON.stringify(paths.frameworkServer)}],
192
192
 
193
193
  "@/client/context": ["./.proteum/client/context.ts"],
194
+ "@/server/index": ["./.proteum/client/server-index.d.ts"],
194
195
  "@generated/client/*": ["./.proteum/client/*"],
195
196
  "@generated/common/*": ["./.proteum/common/*"],
196
197
  "@generated/server/*": ["./.proteum/server/*"],
@@ -207,8 +208,7 @@ export const createClientTsconfigTemplate = (paths: TTsconfigTemplatePaths) => `
207
208
  ".",
208
209
  "../var/typings",
209
210
  ${JSON.stringify(paths.frameworkTypesGlobal)},
210
- "../.proteum/client/services.d.ts",
211
- "../server/index.ts"
211
+ "../.proteum/client/services.d.ts"
212
212
  ]
213
213
  }
214
214
  `;
@@ -228,7 +228,6 @@ export const createServerTsconfigTemplate = (paths: TTsconfigTemplatePaths) => `
228
228
  "@common/*": [${JSON.stringify(paths.frameworkCommon)}],
229
229
  "@server/*": [${JSON.stringify(paths.frameworkServer)}],
230
230
 
231
- "@/client/context": ["./.proteum/client/context.ts"],
232
231
  "@generated/client/*": ["./.proteum/client/*"],
233
232
  "@generated/common/*": ["./.proteum/common/*"],
234
233
  "@generated/server/*": ["./.proteum/server/*"],
@@ -18,7 +18,7 @@ export default function ApexChart({
18
18
  if (!target || !options) return;
19
19
 
20
20
  let disposed = false;
21
- let chart: { destroy: () => void } | undefined;
21
+ let chart: { destroy: () => void; render: () => Promise<unknown> | void } | undefined;
22
22
 
23
23
  target.innerHTML = '';
24
24
  setErrorMessage(undefined);
@@ -29,8 +29,9 @@ export default function ApexChart({
29
29
  if (disposed || !mountRef.current) return;
30
30
 
31
31
  const ApexCharts = module.default;
32
- chart = new ApexCharts(mountRef.current, options);
33
- await chart.render();
32
+ const nextChart = new ApexCharts(mountRef.current, options);
33
+ chart = nextChart;
34
+ await nextChart.render();
34
35
  } catch (error) {
35
36
  if (!disposed) setErrorMessage(readErrorMessage(error));
36
37
  }
@@ -27,33 +27,34 @@ export type TServerReadyMessage = {
27
27
  connectedProjects?: TServerReadyConnectedProject[];
28
28
  };
29
29
 
30
+ const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null;
31
+
30
32
  export const isServerHotReloadRequest = (value: unknown): value is TServerHotReloadRequest =>
31
- typeof value === 'object' &&
32
- value !== null &&
33
- (value as TServerHotReloadRequest).type === serverHotReloadMessageType.request &&
34
- Array.isArray((value as TServerHotReloadRequest).changedFiles);
33
+ isRecord(value) &&
34
+ value.type === serverHotReloadMessageType.request &&
35
+ Array.isArray(value.changedFiles);
35
36
 
36
37
  export const isServerHotReloadResult = (value: unknown): value is TServerHotReloadResult =>
37
- typeof value === 'object' &&
38
- value !== null &&
39
- ((value as TServerHotReloadResult).type === serverHotReloadMessageType.succeeded ||
40
- (value as TServerHotReloadResult).type === serverHotReloadMessageType.failed) &&
41
- Array.isArray((value as TServerHotReloadResult).changedFiles);
38
+ isRecord(value) &&
39
+ (value.type === serverHotReloadMessageType.succeeded || value.type === serverHotReloadMessageType.failed) &&
40
+ Array.isArray(value.changedFiles);
42
41
 
43
42
  const isServerReadyConnectedProject = (value: unknown): value is TServerReadyConnectedProject =>
44
- typeof value === 'object' &&
45
- value !== null &&
46
- typeof (value as TServerReadyConnectedProject).namespace === 'string' &&
47
- typeof (value as TServerReadyConnectedProject).identifier === 'string' &&
48
- typeof (value as TServerReadyConnectedProject).name === 'string' &&
49
- typeof (value as TServerReadyConnectedProject).urlInternal === 'string' &&
50
- typeof (value as TServerReadyConnectedProject).healthUrl === 'string';
51
-
52
- export const isServerReadyMessage = (value: unknown): value is TServerReadyMessage =>
53
- typeof value === 'object' &&
54
- value !== null &&
55
- (value as TServerReadyMessage).type === serverHotReloadMessageType.ready &&
56
- typeof (value as TServerReadyMessage).publicUrl === 'string' &&
57
- ((value as TServerReadyMessage).connectedProjects === undefined ||
58
- (Array.isArray((value as TServerReadyMessage).connectedProjects) &&
59
- (value as TServerReadyMessage).connectedProjects.every(isServerReadyConnectedProject)));
43
+ isRecord(value) &&
44
+ typeof value.namespace === 'string' &&
45
+ typeof value.identifier === 'string' &&
46
+ typeof value.name === 'string' &&
47
+ typeof value.urlInternal === 'string' &&
48
+ typeof value.healthUrl === 'string';
49
+
50
+ export const isServerReadyMessage = (value: unknown): value is TServerReadyMessage => {
51
+ if (!isRecord(value)) return false;
52
+
53
+ const connectedProjects = value.connectedProjects;
54
+ return (
55
+ value.type === serverHotReloadMessageType.ready &&
56
+ typeof value.publicUrl === 'string' &&
57
+ (connectedProjects === undefined ||
58
+ (Array.isArray(connectedProjects) && connectedProjects.every(isServerReadyConnectedProject)))
59
+ );
60
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "proteum",
3
3
  "description": "LLM-first Opinionated Typescript Framework for web applications.",
4
- "version": "2.2.7",
4
+ "version": "2.2.8",
5
5
  "author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
6
6
  "repository": "git://github.com/gaetanlegac/proteum.git",
7
7
  "license": "MIT",
@@ -3,26 +3,25 @@
3
3
  ----------------------------------*/
4
4
 
5
5
  import { Command as ClipanionCommand, Option, UsageError } from 'clipanion';
6
- import type CurrentCommandApplication from '@/server/index';
7
6
 
8
7
  /*----------------------------------
9
8
  - TYPES
10
9
  ----------------------------------*/
11
10
 
12
11
  export type TCommandApplication = {
13
- env: {
12
+ env?: {
14
13
  profile?: string;
15
14
  name?: string;
16
15
  [key: string]: unknown;
17
16
  };
18
- identity: {
17
+ identity?: {
19
18
  identifier?: string;
20
19
  [key: string]: unknown;
21
20
  };
22
- getRootServices: () => Record<string, unknown>;
21
+ getRootServices?: () => Record<string, unknown>;
23
22
  findService?: (serviceId: string) => unknown;
24
- models?: { client?: any };
25
- Models?: { client?: any };
23
+ models?: { client?: object };
24
+ Models?: { client?: object };
26
25
  };
27
26
 
28
27
  export type TCommandService = {
@@ -33,23 +32,19 @@ export type TCommandService = {
33
32
  - COMMAND CLASSES
34
33
  ----------------------------------*/
35
34
 
36
- export abstract class Commands<TApplication extends TCommandApplication = CurrentCommandApplication> {
37
- public app: CurrentCommandApplication;
35
+ export abstract class Commands<TApplication extends TCommandApplication = TCommandApplication> {
36
+ public app: TApplication;
38
37
 
39
- public constructor(app: CurrentCommandApplication) {
38
+ public constructor(app: TApplication) {
40
39
  this.app = app;
41
40
  }
42
41
 
43
- public get services(): CurrentCommandApplication {
42
+ public get services(): TApplication {
44
43
  return this.app;
45
44
  }
46
45
 
47
- public get models(): any {
48
- const app = this.app as {
49
- models?: { client?: any };
50
- Models?: { client?: any };
51
- };
52
- const models = app.models?.client ?? app.Models?.client;
46
+ public get models(): object {
47
+ const models = this.app.models?.client ?? this.app.Models?.client;
53
48
 
54
49
  if (!models)
55
50
  throw new Error(`${this.constructor.name} tried to access models but no Models service is registered.`);
@@ -197,13 +197,17 @@ const parseCommandOptionTokens = (tokens: string[]) => {
197
197
  - SERVICE
198
198
  ----------------------------------*/
199
199
 
200
- export default class CommandsManager extends Service<Config, Hooks, Application> {
200
+ export default class CommandsManager extends Service<Config, Hooks, Application, object> {
201
201
  public priority = 2 as 2;
202
202
 
203
203
  public commandsIndex: CommandsList = {};
204
204
 
205
205
  private runtimeCli?: Cli;
206
206
 
207
+ public constructor(parent: object | 'self', config: Config | null | undefined, app: Application | 'self') {
208
+ super(parent, config, app);
209
+ }
210
+
207
211
  public command<TArgs extends any[]>(
208
212
  ...args:
209
213
  | [name: string, description: string, childrens: RuntimeCommand[]]
@@ -7,39 +7,88 @@ import zod from 'zod';
7
7
 
8
8
  // Core
9
9
  import context from '@server/context';
10
- import type { Application } from '../index';
11
- import type { TServiceModelsClient } from '../service';
12
- import type { TRouterContext, TAnyRouter } from '@server/services/router';
13
10
  import {
14
11
  toValidationSchema,
15
12
  type TValidationSchema,
16
13
  type TValidationShape,
17
14
  } from '@server/services/router/request/validation/zod';
15
+ import type {
16
+ Request as ServerRequest,
17
+ Response as ServerResponse,
18
+ TAnyRouter,
19
+ TRouterContextServices,
20
+ } from '@server/services/router';
18
21
 
19
22
  export { schema } from '@server/services/router/request/validation/zod';
20
- export type { z } from '@server/services/router/request/validation/zod';
23
+ export type {
24
+ z,
25
+ TInferValidationSchema,
26
+ TTypedValidationSchema,
27
+ TValidationSchema,
28
+ TValidationShape,
29
+ } from '@server/services/router/request/validation/zod';
21
30
 
22
31
  /*----------------------------------
23
32
  - TYPES
24
33
  ----------------------------------*/
25
34
 
26
- type TControllerRouter<TApplication extends Application = Application> = TApplication extends { Router: infer TRouter }
27
- ? TRouter extends TAnyRouter
28
- ? TRouter
29
- : TAnyRouter
35
+ type TControllerModelsClient<TApplication extends object = object> = TApplication extends {
36
+ Models: { client: infer TModels };
37
+ }
38
+ ? TModels
39
+ : TApplication extends {
40
+ models: { client: infer TModels };
41
+ }
42
+ ? TModels
43
+ : object;
44
+
45
+ type TControllerRouter<TRouter> = TRouter extends TAnyRouter ? TRouter : TAnyRouter;
46
+ type TControllerApplicationRouter<TApplication extends object> = TApplication extends { Router: infer TRouter }
47
+ ? TControllerRouter<TRouter>
30
48
  : TAnyRouter;
31
49
 
32
- export type TControllerRequestContext<TApplication extends Application = Application> = TRouterContext<
33
- TControllerRouter<TApplication>
34
- >;
50
+ export type TControllerRequestContext<
51
+ TApplication extends object = object,
52
+ TRouter extends object = object,
53
+ TRequestServices extends object = {},
54
+ > = {
55
+ app: TApplication;
56
+ context: object;
57
+ request: ServerRequest<TControllerRouter<TRouter>>;
58
+ api: ServerRequest<TControllerRouter<TRouter>>['api'];
59
+ response: ServerResponse<TControllerRouter<TRouter>>;
60
+ route: object;
61
+ page?: object;
62
+ Router: TControllerRouter<TRouter>;
63
+ } & (TRouter extends TAnyRouter ? TRouterContextServices<TControllerRouter<TRouter>> : {}) &
64
+ TRequestServices;
65
+
66
+ type TControllerBaseContext<TApplication extends object> = {
67
+ app: TApplication;
68
+ request: { data: TObjetDonnees };
69
+ };
70
+
71
+ type TControllerDefaultContext<TApplication extends object, TRequestServices extends object> = {
72
+ app: TApplication;
73
+ context: object;
74
+ request: ServerRequest<TControllerApplicationRouter<TApplication>>;
75
+ api: ServerRequest<TControllerApplicationRouter<TApplication>>['api'];
76
+ response: ServerResponse<TControllerApplicationRouter<TApplication>>;
77
+ route: object;
78
+ page?: object;
79
+ Router: TControllerApplicationRouter<TApplication>;
80
+ } & TRouterContextServices<TControllerApplicationRouter<TApplication>> &
81
+ TRequestServices;
35
82
 
36
83
  /*----------------------------------
37
84
  - CLASS
38
85
  ----------------------------------*/
39
86
 
40
87
  export default abstract class Controller<
41
- TApplication extends Application = Application,
42
- TContext extends TControllerRequestContext<TApplication> = TControllerRequestContext<TApplication>,
88
+ TApplication extends object = object,
89
+ TRouter extends object = object,
90
+ TRequestServices extends object = {},
91
+ TContext extends TControllerBaseContext<TApplication> = TControllerDefaultContext<TApplication, TRequestServices>,
43
92
  > {
44
93
  public constructor(public request: TContext) {}
45
94
 
@@ -51,9 +100,12 @@ export default abstract class Controller<
51
100
  return this.app;
52
101
  }
53
102
 
54
- public get models(): TServiceModelsClient<TApplication> {
55
- const app = this.app as { models?: { client?: unknown }; Models?: { client?: unknown } };
56
- return (app.models?.client ?? app.Models?.client) as TServiceModelsClient<TApplication>;
103
+ public get models(): TControllerModelsClient<TApplication> {
104
+ const app = this.app as {
105
+ models?: { client?: TControllerModelsClient<TApplication> };
106
+ Models?: { client?: TControllerModelsClient<TApplication> };
107
+ };
108
+ return (app.models?.client ?? app.Models?.client) as TControllerModelsClient<TApplication>;
57
109
  }
58
110
 
59
111
  public input<TSchema extends TValidationSchema>(schema: TSchema): zod.output<TSchema>;
@@ -112,10 +112,10 @@ const loadGeneratedCommandDefinitions = () =>
112
112
  (a, b) => a.path.localeCompare(b.path),
113
113
  );
114
114
 
115
- export default class DevCommandsRegistry<TApplication extends Application = Application> {
115
+ export default class DevCommandsRegistry {
116
116
  private definitions = loadGeneratedCommandDefinitions();
117
117
 
118
- public constructor(private app: TApplication) {}
118
+ public constructor(private app: Application) {}
119
119
 
120
120
  public list() {
121
121
  return this.definitions.map((definition) => ({
@@ -150,7 +150,7 @@ export default class DevCommandsRegistry<TApplication extends Application = Appl
150
150
 
151
151
  try {
152
152
  const instance = new definition.Command(this.app);
153
- const method = (instance as Record<string, unknown>)[definition.methodName];
153
+ const method = Reflect.get(instance, definition.methodName);
154
154
 
155
155
  if (typeof method !== 'function') {
156
156
  throw new Error(
@@ -36,8 +36,8 @@ const isExplainSectionName = (value: string): value is TExplainSectionName =>
36
36
  const isConsoleLogLevel = (value: string): value is TDevConsoleLogLevel =>
37
37
  ['silly', 'log', 'info', 'warn', 'error'].includes(value);
38
38
 
39
- export default class DevDiagnosticsRegistry<TApplication extends Application = Application> {
40
- public constructor(private app: TApplication) {}
39
+ export default class DevDiagnosticsRegistry {
40
+ public constructor(private app: Application) {}
41
41
 
42
42
  private getManifestFilepath() {
43
43
  return path.join(this.app.container.path.root, '.proteum', 'manifest.json');
@@ -44,8 +44,13 @@ export const Service = ServicesContainer;
44
44
  type Prettify<T> = { [K in keyof T]: T[K] } & {};
45
45
 
46
46
  export type ApplicationProperties = Prettify<keyof Application>;
47
- export type RootServicesOf<TApplication extends Application = Application> = Prettify<{
48
- [TKey in Exclude<keyof TApplication, ApplicationProperties> as TApplication[TKey] extends AnyService ? TKey : never]: TApplication[TKey];
47
+ type RootServiceCandidate = {
48
+ runHook: object;
49
+ getServiceInstance: object;
50
+ status: string | undefined;
51
+ };
52
+ export type RootServicesOf<TApplication extends object = object> = Prettify<{
53
+ [TKey in Exclude<keyof TApplication, ApplicationProperties> as TApplication[TKey] extends RootServiceCandidate ? TKey : never]: TApplication[TKey];
49
54
  }>;
50
55
 
51
56
  const isServiceInstance = (value: unknown): value is AnyService => {
@@ -56,19 +61,24 @@ const isServiceInstance = (value: unknown): value is AnyService => {
56
61
  return typeof service.runHook === 'function' && typeof service.getServiceInstance === 'function' && service.status !== undefined;
57
62
  };
58
63
 
64
+ const createCommandsManager = (app: Application) => new CommandsManager(app, { debug: true }, app);
65
+ const createDevCommandsRegistry = (app: Application) => new DevCommandsRegistry(app);
66
+ const createDevDiagnosticsRegistry = (app: Application) => new DevDiagnosticsRegistry(app);
67
+
59
68
  /*----------------------------------
60
69
  - FUNCTIONS
61
70
  ----------------------------------*/
62
71
  export abstract class Application<
63
72
  TServicesContainer extends ServicesContainerClass = ServicesContainerClass,
64
73
  TUser extends TBasicUser = TBasicUser,
65
- > extends ApplicationService<Config, Hooks, Application, Application> {
74
+ > extends ApplicationService<Config, Hooks, object, object> {
66
75
  public static identity = ConfigApplication.identity;
67
76
  public static setup = ConfigApplication.setup;
68
77
 
69
78
  public app!: this;
70
79
  public servicesContainer!: TServicesContainer;
71
80
  public userType!: TUser;
81
+ public declare Router: object;
72
82
 
73
83
  /*----------------------------------
74
84
  - PROPERTIES
@@ -122,21 +132,22 @@ export abstract class Application<
122
132
  - COMMANDS
123
133
  ----------------------------------*/
124
134
 
125
- private commandsManager = new CommandsManager(this, { debug: true }, this);
126
- private devCommandsRegistry?: DevCommandsRegistry<this>;
127
- private devDiagnosticsRegistry?: DevDiagnosticsRegistry<this>;
135
+ private commandsManager?: CommandsManager;
136
+ private devCommandsRegistry?: DevCommandsRegistry;
137
+ private devDiagnosticsRegistry?: DevDiagnosticsRegistry;
128
138
 
129
139
  public command(...args: Parameters<CommandsManager['command']>) {
140
+ this.commandsManager ??= createCommandsManager(this as Application);
130
141
  return this.commandsManager.command(...args);
131
142
  }
132
143
 
133
144
  public getDevCommands() {
134
- this.devCommandsRegistry ??= new DevCommandsRegistry(this);
145
+ this.devCommandsRegistry ??= createDevCommandsRegistry(this as Application);
135
146
  return this.devCommandsRegistry;
136
147
  }
137
148
 
138
149
  public getDevDiagnostics() {
139
- this.devDiagnosticsRegistry ??= new DevDiagnosticsRegistry(this);
150
+ this.devDiagnosticsRegistry ??= createDevDiagnosticsRegistry(this as Application);
140
151
  return this.devDiagnosticsRegistry;
141
152
  }
142
153