@eggjs/core 6.5.0 → 6.6.0-beta.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 (87) hide show
  1. package/README.md +1 -5
  2. package/dist/base_context_class.d.ts +21 -0
  3. package/dist/base_context_class.js +40 -0
  4. package/dist/egg.d.ts +248 -0
  5. package/dist/egg.js +358 -0
  6. package/dist/index.d.ts +12 -0
  7. package/dist/index.js +12 -0
  8. package/dist/lifecycle.d.ts +84 -0
  9. package/dist/lifecycle.js +280 -0
  10. package/dist/loader/context_loader.d.ts +39 -0
  11. package/dist/loader/context_loader.js +79 -0
  12. package/dist/loader/egg_loader.d.ts +374 -0
  13. package/dist/loader/egg_loader.js +1184 -0
  14. package/dist/loader/file_loader.d.ts +105 -0
  15. package/dist/loader/file_loader.js +198 -0
  16. package/dist/singleton.d.ts +33 -0
  17. package/dist/singleton.js +107 -0
  18. package/{src/types.ts → dist/types.d.ts} +7 -7
  19. package/dist/utils/index.d.ts +19 -0
  20. package/dist/utils/index.js +103 -0
  21. package/dist/utils/sequencify.d.ts +16 -0
  22. package/dist/utils/sequencify.js +46 -0
  23. package/dist/utils/timing.d.ts +24 -0
  24. package/dist/utils/timing.js +85 -0
  25. package/package.json +34 -72
  26. package/dist/commonjs/base_context_class.d.ts +0 -16
  27. package/dist/commonjs/base_context_class.js +0 -41
  28. package/dist/commonjs/egg.d.ts +0 -246
  29. package/dist/commonjs/egg.js +0 -401
  30. package/dist/commonjs/index.d.ts +0 -12
  31. package/dist/commonjs/index.js +0 -32
  32. package/dist/commonjs/lifecycle.d.ts +0 -78
  33. package/dist/commonjs/lifecycle.js +0 -315
  34. package/dist/commonjs/loader/context_loader.d.ts +0 -35
  35. package/dist/commonjs/loader/context_loader.js +0 -110
  36. package/dist/commonjs/loader/egg_loader.d.ts +0 -369
  37. package/dist/commonjs/loader/egg_loader.js +0 -1558
  38. package/dist/commonjs/loader/file_loader.d.ts +0 -100
  39. package/dist/commonjs/loader/file_loader.js +0 -260
  40. package/dist/commonjs/package.json +0 -3
  41. package/dist/commonjs/singleton.d.ts +0 -29
  42. package/dist/commonjs/singleton.js +0 -124
  43. package/dist/commonjs/types.d.ts +0 -53
  44. package/dist/commonjs/types.js +0 -3
  45. package/dist/commonjs/utils/index.d.ts +0 -17
  46. package/dist/commonjs/utils/index.js +0 -117
  47. package/dist/commonjs/utils/sequencify.d.ts +0 -13
  48. package/dist/commonjs/utils/sequencify.js +0 -64
  49. package/dist/commonjs/utils/timing.d.ts +0 -21
  50. package/dist/commonjs/utils/timing.js +0 -106
  51. package/dist/esm/base_context_class.d.ts +0 -16
  52. package/dist/esm/base_context_class.js +0 -37
  53. package/dist/esm/egg.d.ts +0 -246
  54. package/dist/esm/egg.js +0 -388
  55. package/dist/esm/index.d.ts +0 -12
  56. package/dist/esm/index.js +0 -12
  57. package/dist/esm/lifecycle.d.ts +0 -78
  58. package/dist/esm/lifecycle.js +0 -308
  59. package/dist/esm/loader/context_loader.d.ts +0 -35
  60. package/dist/esm/loader/context_loader.js +0 -102
  61. package/dist/esm/loader/egg_loader.d.ts +0 -369
  62. package/dist/esm/loader/egg_loader.js +0 -1551
  63. package/dist/esm/loader/file_loader.d.ts +0 -100
  64. package/dist/esm/loader/file_loader.js +0 -253
  65. package/dist/esm/package.json +0 -3
  66. package/dist/esm/singleton.d.ts +0 -29
  67. package/dist/esm/singleton.js +0 -117
  68. package/dist/esm/types.d.ts +0 -53
  69. package/dist/esm/types.js +0 -2
  70. package/dist/esm/utils/index.d.ts +0 -17
  71. package/dist/esm/utils/index.js +0 -112
  72. package/dist/esm/utils/sequencify.d.ts +0 -13
  73. package/dist/esm/utils/sequencify.js +0 -61
  74. package/dist/esm/utils/timing.d.ts +0 -21
  75. package/dist/esm/utils/timing.js +0 -99
  76. package/dist/package.json +0 -4
  77. package/src/base_context_class.ts +0 -39
  78. package/src/egg.ts +0 -617
  79. package/src/index.ts +0 -14
  80. package/src/lifecycle.ts +0 -438
  81. package/src/loader/context_loader.ts +0 -123
  82. package/src/loader/egg_loader.ts +0 -1984
  83. package/src/loader/file_loader.ts +0 -349
  84. package/src/singleton.ts +0 -187
  85. package/src/utils/index.ts +0 -132
  86. package/src/utils/sequencify.ts +0 -105
  87. package/src/utils/timing.ts +0 -128
package/src/lifecycle.ts DELETED
@@ -1,438 +0,0 @@
1
- import assert from 'node:assert';
2
- import { EventEmitter } from 'node:events';
3
- import { debuglog, format } from 'node:util';
4
-
5
- import { isClass } from 'is-type-of';
6
- import { Ready as ReadyObject, type ReadyFunctionArg } from 'get-ready';
7
- import { Ready } from 'ready-callback';
8
- import { EggConsoleLogger } from 'egg-logger';
9
-
10
- import utils from './utils/index.js';
11
- import type { Fun } from './utils/index.js';
12
- import type { EggCore } from './egg.js';
13
-
14
- const debug = debuglog('@eggjs/core/lifecycle');
15
-
16
- export interface ILifecycleBoot {
17
- // loader auto set 'fullPath' property on boot class
18
- fullPath?: string;
19
- /**
20
- * Ready to call configDidLoad,
21
- * Config, plugin files are referred,
22
- * this is the last chance to modify the config.
23
- */
24
- configWillLoad?(): void;
25
-
26
- /**
27
- * Config, plugin files have loaded
28
- */
29
- configDidLoad?(): void;
30
-
31
- /**
32
- * All files have loaded, start plugin here
33
- */
34
- didLoad?(): Promise<void>;
35
-
36
- /**
37
- * All plugins have started, can do some thing before app ready
38
- */
39
- willReady?(): Promise<void>;
40
-
41
- /**
42
- * Worker is ready, can do some things,
43
- * don't need to block the app boot
44
- */
45
- didReady?(err?: Error): Promise<void>;
46
-
47
- /**
48
- * Server is listening
49
- */
50
- serverDidReady?(): Promise<void>;
51
-
52
- /**
53
- * Do some thing before app close
54
- */
55
- beforeClose?(): Promise<void>;
56
- }
57
-
58
- export type BootImplClass<T = ILifecycleBoot> = new (...args: any[]) => T;
59
-
60
- export interface LifecycleOptions {
61
- baseDir: string;
62
- app: EggCore;
63
- logger: EggConsoleLogger;
64
- }
65
-
66
- export type FunWithFullPath = Fun & { fullPath?: string };
67
-
68
- export class Lifecycle extends EventEmitter {
69
- #init: boolean;
70
- #readyObject: ReadyObject;
71
- #bootHooks: (BootImplClass | ILifecycleBoot)[];
72
- #boots: ILifecycleBoot[];
73
- #isClosed: boolean;
74
- #closeFunctionSet: Set<FunWithFullPath>;
75
- loadReady: Ready;
76
- bootReady: Ready;
77
- options: LifecycleOptions;
78
- readyTimeout: number;
79
-
80
- constructor(options: Partial<LifecycleOptions>) {
81
- super();
82
- options.logger = options.logger ?? new EggConsoleLogger();
83
- this.options = options as LifecycleOptions;
84
- this.#readyObject = new ReadyObject();
85
- this.#bootHooks = [];
86
- this.#boots = [];
87
- this.#closeFunctionSet = new Set();
88
- this.#isClosed = false;
89
- this.#init = false;
90
-
91
- this.timing.start(`${this.options.app.type} Start`);
92
- // get app timeout from env or use default timeout 10 second
93
- const eggReadyTimeoutEnv = Number.parseInt(
94
- process.env.EGG_READY_TIMEOUT_ENV || '10000'
95
- );
96
- assert(
97
- Number.isInteger(eggReadyTimeoutEnv),
98
- `process.env.EGG_READY_TIMEOUT_ENV ${process.env.EGG_READY_TIMEOUT_ENV} should be able to parseInt.`
99
- );
100
- this.readyTimeout = eggReadyTimeoutEnv;
101
-
102
- this.#initReady();
103
- this.on('ready_stat', data => {
104
- this.logger.info(
105
- '[@eggjs/core/lifecycle:ready_stat] end ready task %s, remain %j',
106
- data.id,
107
- data.remain
108
- );
109
- }).on('ready_timeout', id => {
110
- this.logger.warn(
111
- '[@eggjs/core/lifecycle:ready_timeout] %s seconds later %s was still unable to finish.',
112
- this.readyTimeout / 1000,
113
- id
114
- );
115
- });
116
-
117
- this.ready(err => {
118
- this.triggerDidReady(err);
119
- debug('app ready');
120
- this.timing.end(`${this.options.app.type} Start`);
121
- });
122
- }
123
-
124
- ready(): Promise<void>;
125
- ready(flagOrFunction: ReadyFunctionArg): void;
126
- ready(flagOrFunction?: ReadyFunctionArg) {
127
- if (flagOrFunction === undefined) {
128
- return this.#readyObject.ready();
129
- }
130
- return this.#readyObject.ready(flagOrFunction);
131
- }
132
-
133
- get app() {
134
- return this.options.app;
135
- }
136
-
137
- get logger() {
138
- return this.options.logger;
139
- }
140
-
141
- get timing() {
142
- return this.app.timing;
143
- }
144
-
145
- legacyReadyCallback(name: string, opt?: object) {
146
- const timingKeyPrefix = 'readyCallback';
147
- const timing = this.timing;
148
- const cb = this.loadReady.readyCallback(name, opt);
149
- const timingKey =
150
- `${timingKeyPrefix} in ` +
151
- utils.getResolvedFilename(name, this.app.baseDir);
152
- this.timing.start(timingKey);
153
- debug('register legacyReadyCallback');
154
- return function legacyReadyCallback(...args: unknown[]) {
155
- timing.end(timingKey);
156
- debug('end legacyReadyCallback');
157
- cb(...args);
158
- };
159
- }
160
-
161
- addBootHook(bootHootOrBootClass: BootImplClass | ILifecycleBoot) {
162
- assert(
163
- this.#init === false,
164
- 'do not add hook when lifecycle has been initialized'
165
- );
166
- this.#bootHooks.push(bootHootOrBootClass);
167
- }
168
-
169
- addFunctionAsBootHook<T = EggCore>(
170
- hook: (app: T) => void,
171
- fullPath?: string
172
- ) {
173
- assert(
174
- this.#init === false,
175
- 'do not add hook when lifecycle has been initialized'
176
- );
177
- // app.js is exported as a function
178
- // call this function in configDidLoad
179
- class Boot implements ILifecycleBoot {
180
- static fullPath?: string;
181
- app: T;
182
- constructor(app: T) {
183
- this.app = app;
184
- }
185
- configDidLoad() {
186
- hook(this.app);
187
- }
188
- }
189
- Boot.fullPath = fullPath;
190
- this.#bootHooks.push(Boot);
191
- }
192
-
193
- /**
194
- * init boots and trigger config did config
195
- */
196
- init() {
197
- debug('%s init lifecycle', this.app.type);
198
- assert(this.#init === false, 'lifecycle have been init');
199
- this.#init = true;
200
- this.#boots = this.#bootHooks.map(BootHootOrBootClass => {
201
- let instance = BootHootOrBootClass as ILifecycleBoot;
202
- if (isClass(BootHootOrBootClass)) {
203
- instance = new BootHootOrBootClass(this.app);
204
- if (!instance.fullPath && 'fullPath' in BootHootOrBootClass) {
205
- instance.fullPath = BootHootOrBootClass.fullPath as string;
206
- }
207
- }
208
- debug('[init] add boot instance: %o', instance.fullPath);
209
- return instance;
210
- });
211
- }
212
-
213
- registerBeforeStart(scope: Fun, name: string) {
214
- debug('%s add registerBeforeStart, name: %o', this.options.app.type, name);
215
- this.#registerReadyCallback({
216
- scope,
217
- ready: this.loadReady,
218
- timingKeyPrefix: 'Before Start',
219
- scopeFullName: name,
220
- });
221
- }
222
-
223
- registerBeforeClose(fn: FunWithFullPath, fullPath?: string) {
224
- assert(typeof fn === 'function', 'argument should be function');
225
- assert(this.#isClosed === false, 'app has been closed');
226
- if (fullPath) {
227
- fn.fullPath = fullPath;
228
- }
229
- this.#closeFunctionSet.add(fn);
230
- debug(
231
- '%s register beforeClose at %o, count: %d',
232
- this.app.type,
233
- fullPath,
234
- this.#closeFunctionSet.size
235
- );
236
- }
237
-
238
- async close() {
239
- // close in reverse order: first created, last closed
240
- const closeFns = Array.from(this.#closeFunctionSet);
241
- debug(
242
- '%s start trigger %d beforeClose functions',
243
- this.app.type,
244
- closeFns.length
245
- );
246
- for (const fn of closeFns.reverse()) {
247
- debug('%s trigger beforeClose at %o', this.app.type, fn.fullPath);
248
- await utils.callFn(fn);
249
- this.#closeFunctionSet.delete(fn);
250
- }
251
- // Be called after other close callbacks
252
- this.app.emit('close');
253
- this.removeAllListeners();
254
- this.app.removeAllListeners();
255
- this.#isClosed = true;
256
- debug('%s closed', this.app.type);
257
- }
258
-
259
- triggerConfigWillLoad() {
260
- debug('trigger configWillLoad start');
261
- for (const boot of this.#boots) {
262
- if (typeof boot.configWillLoad === 'function') {
263
- debug('trigger configWillLoad at %o', boot.fullPath);
264
- boot.configWillLoad();
265
- }
266
- }
267
- debug('trigger configWillLoad end');
268
- this.triggerConfigDidLoad();
269
- }
270
-
271
- triggerConfigDidLoad() {
272
- debug('trigger configDidLoad start');
273
- for (const boot of this.#boots) {
274
- if (typeof boot.configDidLoad === 'function') {
275
- debug('trigger configDidLoad at %o', boot.fullPath);
276
- boot.configDidLoad();
277
- }
278
- // function boot hook register after configDidLoad trigger
279
- if (typeof boot.beforeClose === 'function') {
280
- const beforeClose = boot.beforeClose.bind(boot);
281
- this.registerBeforeClose(beforeClose, boot.fullPath);
282
- }
283
- }
284
- debug('trigger configDidLoad end');
285
- this.triggerDidLoad();
286
- }
287
-
288
- triggerDidLoad() {
289
- debug('trigger didLoad start');
290
- debug('loadReady start');
291
- this.loadReady.start();
292
- for (const boot of this.#boots) {
293
- if (typeof boot.didLoad === 'function') {
294
- const didLoad = boot.didLoad.bind(boot);
295
- this.#registerReadyCallback({
296
- scope: didLoad,
297
- ready: this.loadReady,
298
- timingKeyPrefix: 'Did Load',
299
- scopeFullName: boot.fullPath + ':didLoad',
300
- });
301
- }
302
- }
303
- }
304
-
305
- triggerWillReady() {
306
- debug('trigger willReady start');
307
- debug('bootReady start');
308
- this.bootReady.start();
309
- for (const boot of this.#boots) {
310
- if (typeof boot.willReady === 'function') {
311
- const willReady = boot.willReady.bind(boot);
312
- this.#registerReadyCallback({
313
- scope: willReady,
314
- ready: this.bootReady,
315
- timingKeyPrefix: 'Will Ready',
316
- scopeFullName: boot.fullPath + ':willReady',
317
- });
318
- }
319
- }
320
- }
321
-
322
- triggerDidReady(err?: Error) {
323
- debug('trigger didReady start');
324
- return (async () => {
325
- for (const boot of this.#boots) {
326
- if (typeof boot.didReady === 'function') {
327
- debug('trigger didReady at %o', boot.fullPath);
328
- try {
329
- await boot.didReady(err);
330
- } catch (err) {
331
- debug(
332
- 'trigger didReady error at %o, error: %s',
333
- boot.fullPath,
334
- err
335
- );
336
- this.emit('error', err);
337
- }
338
- }
339
- }
340
- debug('trigger didReady end');
341
- })();
342
- }
343
-
344
- triggerServerDidReady() {
345
- debug('trigger serverDidReady start');
346
- return (async () => {
347
- for (const boot of this.#boots) {
348
- if (typeof boot.serverDidReady !== 'function') {
349
- continue;
350
- }
351
- debug('trigger serverDidReady at %o', boot.fullPath);
352
- try {
353
- await boot.serverDidReady();
354
- } catch (err) {
355
- debug(
356
- 'trigger serverDidReady error at %o, error: %s',
357
- boot.fullPath,
358
- err
359
- );
360
- this.emit('error', err);
361
- }
362
- }
363
- debug('trigger serverDidReady end');
364
- })();
365
- }
366
-
367
- #initReady() {
368
- debug('loadReady init');
369
- this.loadReady = new Ready({ timeout: this.readyTimeout, lazyStart: true });
370
- this.#delegateReadyEvent(this.loadReady);
371
- this.loadReady.ready((err?: Error) => {
372
- debug('loadReady end, err: %o', err);
373
- debug('trigger didLoad end');
374
- if (err) {
375
- this.ready(err);
376
- } else {
377
- this.triggerWillReady();
378
- }
379
- });
380
-
381
- debug('bootReady init');
382
- this.bootReady = new Ready({ timeout: this.readyTimeout, lazyStart: true });
383
- this.#delegateReadyEvent(this.bootReady);
384
- this.bootReady.ready((err?: Error) => {
385
- debug('bootReady end, err: %o', err);
386
- debug('trigger willReady end');
387
- this.ready(err || true);
388
- });
389
- }
390
-
391
- #delegateReadyEvent(ready: Ready) {
392
- ready.once('error', (err?: Error) => ready.ready(err));
393
- ready.on('ready_timeout', (id: unknown) => this.emit('ready_timeout', id));
394
- ready.on('ready_stat', (data: unknown) => this.emit('ready_stat', data));
395
- ready.on('error', (err?: Error) => this.emit('error', err));
396
- }
397
-
398
- #registerReadyCallback(args: {
399
- scope: Fun;
400
- ready: Ready;
401
- timingKeyPrefix: string;
402
- scopeFullName?: string;
403
- }) {
404
- const { scope, ready, timingKeyPrefix, scopeFullName } = args;
405
- if (typeof scope !== 'function') {
406
- throw new TypeError('boot only support function');
407
- }
408
-
409
- // get filename from stack if scopeFullName is undefined
410
- const name = scopeFullName || utils.getCalleeFromStack(true, 4);
411
- const timingKey =
412
- `${timingKeyPrefix} in ` +
413
- utils.getResolvedFilename(name, this.app.baseDir);
414
-
415
- this.timing.start(timingKey);
416
-
417
- debug('[registerReadyCallback] start name: %o', name);
418
- const done = ready.readyCallback(name);
419
-
420
- // ensure scope executes after load completed
421
- process.nextTick(async () => {
422
- try {
423
- await utils.callFn(scope);
424
- debug('[registerReadyCallback] end name: %o', name);
425
- done();
426
- this.timing.end(timingKey);
427
- } catch (e) {
428
- let err = e as Error;
429
- // avoid non-stringify error: TypeError: Cannot convert object to primitive value
430
- if (!(err instanceof Error)) {
431
- err = new Error(format('%s', err));
432
- }
433
- done(err);
434
- this.timing.end(timingKey);
435
- }
436
- });
437
- }
438
- }
@@ -1,123 +0,0 @@
1
- import assert from 'node:assert';
2
- import { isClass, isPrimitive } from 'is-type-of';
3
- import { FileLoader, EXPORTS, type FileLoaderOptions } from './file_loader.js';
4
- import type { Context } from '../egg.js';
5
-
6
- const CLASS_LOADER = Symbol('classLoader');
7
-
8
- export interface ClassLoaderOptions {
9
- ctx: Context;
10
- properties: any;
11
- }
12
-
13
- export class ClassLoader {
14
- readonly _cache = new Map();
15
- _ctx: Context;
16
-
17
- constructor(options: ClassLoaderOptions) {
18
- assert(options.ctx, 'options.ctx is required');
19
- const properties = options.properties;
20
- this._ctx = options.ctx;
21
-
22
- for (const property in properties) {
23
- this.#defineProperty(property, properties[property]);
24
- }
25
- }
26
-
27
- #defineProperty(property: string, values: any) {
28
- Object.defineProperty(this, property, {
29
- get() {
30
- let instance: any = this._cache.get(property);
31
- if (!instance) {
32
- instance = getInstance(values, this._ctx);
33
- this._cache.set(property, instance);
34
- }
35
- return instance;
36
- },
37
- });
38
- }
39
- }
40
-
41
- export interface ContextLoaderOptions
42
- extends Omit<FileLoaderOptions, 'target'> {
43
- /** required inject */
44
- inject: Record<string, any>;
45
- /** property name defined to target */
46
- property: string | symbol;
47
- /** determine the field name of inject object. */
48
- fieldClass?: string;
49
- }
50
-
51
- /**
52
- * Same as {@link FileLoader}, but it will attach file to `inject[fieldClass]`.
53
- * The exports will be lazy loaded, such as `ctx.group.repository`.
54
- * @augments FileLoader
55
- * @since 1.0.0
56
- */
57
- export class ContextLoader extends FileLoader {
58
- readonly #inject: Record<string, any>;
59
- /**
60
- * @class
61
- * @param {Object} options - options same as {@link FileLoader}
62
- * @param {String} options.fieldClass - determine the field name of inject object.
63
- */
64
- constructor(options: ContextLoaderOptions) {
65
- assert(options.property, 'options.property is required');
66
- assert(options.inject, 'options.inject is required');
67
- const target = {};
68
- if (options.fieldClass) {
69
- options.inject[options.fieldClass] = target;
70
- }
71
- super({
72
- ...options,
73
- target,
74
- });
75
- this.#inject = this.options.inject as Record<string, any>;
76
-
77
- const app = this.#inject;
78
- const property = options.property;
79
- // define ctx.service
80
- Object.defineProperty(app.context, property, {
81
- get() {
82
- // oxlint-disable-next-line unicorn/no-this-assignment, typescript/no-this-alias
83
- const ctx = this;
84
- // distinguish property cache,
85
- // cache's lifecycle is the same with this context instance
86
- // e.x. ctx.service1 and ctx.service2 have different cache
87
- if (!ctx[CLASS_LOADER]) {
88
- ctx[CLASS_LOADER] = new Map();
89
- }
90
- const classLoader: Map<string | symbol, ClassLoader> =
91
- ctx[CLASS_LOADER];
92
- let instance = classLoader.get(property);
93
- if (!instance) {
94
- instance = getInstance(target, ctx);
95
- classLoader.set(property, instance as ClassLoader);
96
- }
97
- return instance;
98
- },
99
- });
100
- }
101
- }
102
-
103
- function getInstance(values: any, ctx: Context) {
104
- // it's a directory when it has no exports
105
- // then use ClassLoader
106
- const Class = values[EXPORTS] ? values : null;
107
- let instance;
108
- if (Class) {
109
- if (isClass(Class)) {
110
- instance = new Class(ctx);
111
- } else {
112
- // it's just an object
113
- instance = Class;
114
- }
115
- // Can't set property to primitive, so check again
116
- // e.x. module.exports = 1;
117
- } else if (isPrimitive(values)) {
118
- instance = values;
119
- } else {
120
- instance = new ClassLoader({ ctx, properties: values });
121
- }
122
- return instance;
123
- }