@eggjs/core 6.3.0-beta.0 → 6.3.0

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 (56) hide show
  1. package/dist/commonjs/base_context_class.d.ts +3 -3
  2. package/dist/commonjs/base_context_class.js +1 -1
  3. package/dist/commonjs/egg.d.ts +17 -10
  4. package/dist/commonjs/egg.js +30 -4
  5. package/dist/commonjs/index.d.ts +2 -0
  6. package/dist/commonjs/index.js +2 -1
  7. package/dist/commonjs/lifecycle.d.ts +5 -2
  8. package/dist/commonjs/lifecycle.js +35 -15
  9. package/dist/commonjs/loader/context_loader.d.ts +3 -3
  10. package/dist/commonjs/loader/context_loader.js +1 -1
  11. package/dist/commonjs/loader/egg_loader.d.ts +4 -40
  12. package/dist/commonjs/loader/egg_loader.js +56 -39
  13. package/dist/commonjs/loader/file_loader.d.ts +5 -1
  14. package/dist/commonjs/loader/file_loader.js +18 -5
  15. package/dist/commonjs/singleton.d.ts +29 -0
  16. package/dist/commonjs/singleton.js +125 -0
  17. package/dist/commonjs/types.d.ts +53 -0
  18. package/dist/commonjs/types.js +3 -0
  19. package/dist/commonjs/utils/index.js +10 -5
  20. package/dist/commonjs/utils/sequencify.js +1 -1
  21. package/dist/commonjs/utils/timing.js +1 -1
  22. package/dist/esm/base_context_class.d.ts +3 -3
  23. package/dist/esm/base_context_class.js +1 -1
  24. package/dist/esm/egg.d.ts +17 -10
  25. package/dist/esm/egg.js +30 -4
  26. package/dist/esm/index.d.ts +2 -0
  27. package/dist/esm/index.js +2 -1
  28. package/dist/esm/lifecycle.d.ts +5 -2
  29. package/dist/esm/lifecycle.js +35 -15
  30. package/dist/esm/loader/context_loader.d.ts +3 -3
  31. package/dist/esm/loader/context_loader.js +1 -1
  32. package/dist/esm/loader/egg_loader.d.ts +4 -40
  33. package/dist/esm/loader/egg_loader.js +60 -43
  34. package/dist/esm/loader/file_loader.d.ts +5 -1
  35. package/dist/esm/loader/file_loader.js +17 -4
  36. package/dist/esm/singleton.d.ts +29 -0
  37. package/dist/esm/singleton.js +118 -0
  38. package/dist/esm/types.d.ts +53 -0
  39. package/dist/esm/types.js +2 -0
  40. package/dist/esm/utils/index.js +10 -5
  41. package/dist/esm/utils/sequencify.js +1 -1
  42. package/dist/esm/utils/timing.js +1 -1
  43. package/dist/package.json +1 -1
  44. package/package.json +16 -15
  45. package/src/base_context_class.ts +3 -3
  46. package/src/egg.ts +40 -12
  47. package/src/index.ts +2 -0
  48. package/src/lifecycle.ts +40 -15
  49. package/src/loader/context_loader.ts +4 -4
  50. package/src/loader/egg_loader.ts +76 -90
  51. package/src/loader/file_loader.ts +16 -4
  52. package/src/singleton.ts +149 -0
  53. package/src/types.ts +56 -0
  54. package/src/utils/index.ts +9 -4
  55. package/src/utils/sequencify.ts +1 -1
  56. package/src/utils/timing.ts +1 -1
@@ -0,0 +1,149 @@
1
+ import assert from 'node:assert';
2
+ import { isAsyncFunction } from 'is-type-of';
3
+ import type { EggCore } from './egg.js';
4
+
5
+ export type SingletonCreateMethod =
6
+ (config: Record<string, any>, app: EggCore, clientName: string) => unknown | Promise<unknown>;
7
+
8
+ export interface SingletonOptions {
9
+ name: string;
10
+ app: EggCore;
11
+ create: SingletonCreateMethod;
12
+ }
13
+
14
+ export class Singleton<T = any> {
15
+ readonly clients = new Map<string, T>();
16
+ readonly app: EggCore;
17
+ readonly create: SingletonCreateMethod;
18
+ readonly name: string;
19
+ readonly options: Record<string, any>;
20
+
21
+ constructor(options: SingletonOptions) {
22
+ assert(options.name, '[@eggjs/core/singleton] Singleton#constructor options.name is required');
23
+ assert(options.app, '[@eggjs/core/singleton] Singleton#constructor options.app is required');
24
+ assert(options.create, '[@eggjs/core/singleton] Singleton#constructor options.create is required');
25
+ assert(!(options.name in options.app), `[@eggjs/core/singleton] ${options.name} is already exists in app`);
26
+ this.app = options.app;
27
+ this.name = options.name;
28
+ this.create = options.create;
29
+ this.options = options.app.config[this.name] ?? {};
30
+ }
31
+
32
+ init() {
33
+ return isAsyncFunction(this.create) ? this.initAsync() : this.initSync();
34
+ }
35
+
36
+ initSync() {
37
+ const options = this.options;
38
+ assert(!(options.client && options.clients),
39
+ `[@eggjs/core/singleton] ${this.name} can not set options.client and options.clients both`);
40
+
41
+ // alias app[name] as client, but still support createInstance method
42
+ if (options.client) {
43
+ const client = this.createInstance(options.client, options.name);
44
+ this.#setClientToApp(client);
45
+ this.#extendDynamicMethods(client);
46
+ return;
47
+ }
48
+
49
+ // multi client, use app[name].getSingletonInstance(id)
50
+ if (options.clients) {
51
+ Object.keys(options.clients).forEach(id => {
52
+ const client = this.createInstance(options.clients[id], id);
53
+ this.clients.set(id, client);
54
+ });
55
+ this.#setClientToApp(this);
56
+ return;
57
+ }
58
+
59
+ // no config.clients and config.client
60
+ this.#setClientToApp(this);
61
+ }
62
+
63
+ async initAsync() {
64
+ const options = this.options;
65
+ assert(!(options.client && options.clients),
66
+ `[@eggjs/core/singleton] ${this.name} can not set options.client and options.clients both`);
67
+
68
+ // alias app[name] as client, but still support createInstance method
69
+ if (options.client) {
70
+ const client = await this.createInstanceAsync(options.client, options.name);
71
+ this.#setClientToApp(client);
72
+ this.#extendDynamicMethods(client);
73
+ return;
74
+ }
75
+
76
+ // multi client, use app[name].getInstance(id)
77
+ if (options.clients) {
78
+ await Promise.all(Object.keys(options.clients).map((id: string) => {
79
+ return this.createInstanceAsync(options.clients[id], id)
80
+ .then(client => this.clients.set(id, client));
81
+ }));
82
+ this.#setClientToApp(this);
83
+ return;
84
+ }
85
+
86
+ // no config.clients and config.client
87
+ this.#setClientToApp(this);
88
+ }
89
+
90
+ #setClientToApp(client: unknown) {
91
+ Reflect.set(this.app, this.name, client);
92
+ }
93
+
94
+ /**
95
+ * @deprecated please use `getSingletonInstance(id)` instead
96
+ */
97
+ get(id: string) {
98
+ return this.clients.get(id)!;
99
+ }
100
+
101
+ /**
102
+ * Get singleton instance by id
103
+ */
104
+ getSingletonInstance(id: string) {
105
+ return this.clients.get(id)!;
106
+ }
107
+
108
+ createInstance(config: Record<string, any>, clientName: string) {
109
+ // async creator only support createInstanceAsync
110
+ assert(!isAsyncFunction(this.create),
111
+ `[@eggjs/core/singleton] ${this.name} only support synchronous creation, please use createInstanceAsync`);
112
+ // options.default will be merge in to options.clients[id]
113
+ config = {
114
+ ...this.options.default,
115
+ ...config,
116
+ };
117
+ return (this.create as SingletonCreateMethod)(config, this.app, clientName) as T;
118
+ }
119
+
120
+ async createInstanceAsync(config: Record<string, any>, clientName: string) {
121
+ // options.default will be merge in to options.clients[id]
122
+ config = {
123
+ ...this.options.default,
124
+ ...config,
125
+ };
126
+ return await this.create(config, this.app, clientName) as T;
127
+ }
128
+
129
+ #extendDynamicMethods(client: any) {
130
+ assert(!client.createInstance, '[@eggjs/core/singleton] singleton instance should not have createInstance method');
131
+ assert(!client.createInstanceAsync, '[@eggjs/core/singleton] singleton instance should not have createInstanceAsync method');
132
+
133
+ try {
134
+ let extendable = client;
135
+ // Object.preventExtensions() or Object.freeze()
136
+ if (!Object.isExtensible(client) || Object.isFrozen(client)) {
137
+ // eslint-disable-next-line no-proto
138
+ extendable = client.__proto__ || client;
139
+ }
140
+ extendable.createInstance = this.createInstance.bind(this);
141
+ extendable.createInstanceAsync = this.createInstanceAsync.bind(this);
142
+ } catch (err) {
143
+ this.app.coreLogger.warn(
144
+ '[@eggjs/core/singleton] %s dynamic create is disabled because of client is un-extendable',
145
+ this.name);
146
+ this.app.coreLogger.warn(err);
147
+ }
148
+ }
149
+ }
package/src/types.ts ADDED
@@ -0,0 +1,56 @@
1
+ export interface EggAppInfo {
2
+ /** package.json */
3
+ pkg: Record<string, any>;
4
+ /** the application name from package.json */
5
+ name: string;
6
+ /** current directory of application */
7
+ baseDir: string;
8
+ /** equals to serverEnv */
9
+ env: string;
10
+ /** equals to serverScope */
11
+ scope: string;
12
+ /** home directory of the OS */
13
+ HOME: string;
14
+ /** baseDir when local and unittest, HOME when other environment */
15
+ root: string;
16
+ }
17
+
18
+ export interface EggPluginInfo {
19
+ /** the plugin name, it can be used in `dep` */
20
+ name: string;
21
+ /** the package name of plugin */
22
+ package?: string;
23
+ version?: string;
24
+ /** whether enabled */
25
+ enable: boolean;
26
+ implicitEnable?: boolean;
27
+ /** the directory of the plugin package */
28
+ path?: string;
29
+ /** the dependent plugins, you can use the plugin name */
30
+ dependencies: string[];
31
+ /** the optional dependent plugins. */
32
+ optionalDependencies: string[];
33
+ dependents?: string[];
34
+ /** specify the serverEnv that only enable the plugin in it */
35
+ env: string[];
36
+ /** the file plugin config in. */
37
+ from: string;
38
+ }
39
+
40
+ export interface CustomLoaderConfigItem {
41
+ /** the directory of the custom loader */
42
+ directory: string;
43
+ /** the inject object, it can be app or ctx */
44
+ inject: string;
45
+ /** whether load unit files */
46
+ loadunit?: boolean;
47
+ }
48
+
49
+ export interface EggAppConfig extends Record<string, any> {
50
+ coreMiddleware: string[];
51
+ middleware: string[];
52
+ customLoader?: Record<string, CustomLoaderConfigItem>;
53
+ controller?: {
54
+ supportParams?: boolean;
55
+ };
56
+ }
@@ -5,7 +5,7 @@ import { stat } from 'node:fs/promises';
5
5
  import BuiltinModule from 'node:module';
6
6
  import { importResolve, importModule } from '@eggjs/utils';
7
7
 
8
- const debug = debuglog('@eggjs/core:utils');
8
+ const debug = debuglog('@eggjs/core/utils');
9
9
 
10
10
  export type Fun = (...args: any[]) => any;
11
11
 
@@ -55,10 +55,10 @@ function getCalleeFromStack(withLine?: boolean, stackIndex?: number) {
55
55
  export default {
56
56
  deprecated(message: string) {
57
57
  if (debug.enabled) {
58
- console.trace('[@eggjs/core:deprecated] %s', message);
58
+ console.trace('[@eggjs/core/deprecated] %s', message);
59
59
  } else {
60
- console.warn('[@eggjs/core:deprecated] %s', message);
61
- console.warn('[@eggjs/core:deprecated] set NODE_DEBUG=@eggjs/core:utils can show call stack');
60
+ console.log('[@eggjs/core/deprecated] %s', message);
61
+ console.log('[@eggjs/core/deprecated] set NODE_DEBUG=@eggjs/core/utils can show call stack');
62
62
  }
63
63
  },
64
64
 
@@ -84,6 +84,11 @@ export default {
84
84
  const obj = await importModule(filepath, { importDefaultOnly: true });
85
85
  return obj;
86
86
  } catch (e: any) {
87
+ if (!e.message && typeof e !== 'string') {
88
+ // ts error: test/fixtures/apps/app-ts/app/extend/context.ts(5,17): error TS2339: Property 'url' does not exist on type 'Context'
89
+ console.trace(e);
90
+ throw e;
91
+ }
87
92
  const err = new Error(`[@eggjs/core] load file: ${filepath}, error: ${e.message}`);
88
93
  err.cause = e;
89
94
  debug('[loadFile] handle %s error: %s', filepath, e);
@@ -1,6 +1,6 @@
1
1
  import { debuglog } from 'node:util';
2
2
 
3
- const debug = debuglog('@eggjs/core:utils:sequencify');
3
+ const debug = debuglog('@eggjs/core/utils/sequencify');
4
4
 
5
5
  export interface SequencifyResult {
6
6
  sequence: string[];
@@ -2,7 +2,7 @@ import { EOL } from 'node:os';
2
2
  import { debuglog } from 'node:util';
3
3
  import assert from 'node:assert';
4
4
 
5
- const debug = debuglog('@eggjs/core:utils:timing');
5
+ const debug = debuglog('@eggjs/core/utils/timing');
6
6
 
7
7
  export interface TimingItem {
8
8
  name: string;