@xrystal/core 3.20.5 → 3.20.9

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "author": "Yusuf Yasir KAYGUSUZ",
3
3
  "name": "@xrystal/core",
4
- "version": "3.20.5",
4
+ "version": "3.20.9",
5
5
  "description": "Project core for xrystal",
6
6
  "publishConfig": {
7
7
  "access": "public",
@@ -37,34 +37,27 @@
37
37
  "start": "bun --env-file=../infrastructer/x/environments/.global.env --env-file=../infrastructer/x/environments/.dev.env source/index.js"
38
38
  },
39
39
  "dependencies": {
40
- "@types/lodash": "^4.17.23",
41
- "@types/yaml": "^1.9.7",
40
+ "lodash": "^4.17.23",
42
41
  "awilix": "^12.0.5",
43
- "chalk": "^5.6.2",
44
- "commander": "^13.0.0",
45
- "ejs": "^3.1.9",
42
+ "yaml": "^2.5.0",
46
43
  "handlebars": "^4.7.8",
47
- "i": "^0.3.7",
44
+ "winston": "^3.19.0",
45
+ "winston-daily-rotate-file": "^5.0.0",
46
+ "winston-mongodb": "^7.0.1",
47
+ "kafkajs": "^2.2.4",
48
48
  "i18next": "^25.6.3",
49
49
  "i18next-fs-backend": "^2.6.1",
50
50
  "i18next-http-middleware": "^3.8.2",
51
- "kafkajs": "^2.2.4",
52
- "lodash": "^4.17.23",
53
- "moment-timezone": "^0.6.0",
54
- "npm": "^11.7.0",
55
51
  "ora": "^9.0.0",
56
- "picocolors": "^1.1.1",
57
52
  "qs": "^6.14.1",
58
53
  "soap": "^1.6.3",
59
- "winston": "^3.19.0",
60
- "winston-daily-rotate-file": "^5.0.0",
61
- "winston-mongodb": "^7.0.1",
62
- "yaml": "^2.5.0"
54
+ "chalk": "^5.6.2",
55
+ "commander": "^13.0.0",
56
+ "picocolors": "^1.1.1"
63
57
  },
64
58
  "devDependencies": {
65
- "@types/minimist": "^1.2.5",
66
- "@types/bun": "^1.3.5",
67
- "@types/node": "^25.0.6",
68
- "typescript": "^5.5.3"
59
+ "@types/lodash": "^4.17.23",
60
+ "@types/yaml": "^1.9.7",
61
+ "@types/bun": "^1.3.5"
69
62
  }
70
63
  }
@@ -19,7 +19,7 @@ export interface IConfig {
19
19
  systemLoggerLayer: string;
20
20
  isKafkaPassive: boolean;
21
21
  kafkaBrokers: string;
22
- kafkaTopics: string;
22
+ kafkaTopics: string[];
23
23
  kafkaLogsTopic: string;
24
24
  baseApiUri: string;
25
25
  port: number;
@@ -44,7 +44,7 @@ export default class ConfigsService {
44
44
  systemLoggerLayer: process.env.SYSTEM_LOGGER_LAYER,
45
45
  isKafkaPassive: process.env.IS_KAFKA_PASSIVE === 'true' ? true : false,
46
46
  kafkaBrokers: process.env?.KAFKA_BROKERS,
47
- kafkaTopics: process.env?.KAFKA_TOPICS,
47
+ kafkaTopics: [],
48
48
  kafkaLogsTopic: this.kafkaLogsTopic,
49
49
  baseApiUri: process.env.HTTPS === 'true' ? process.env.SYSTEM_HTTPS_BASE_API_URI : process.env.SYSTEM_BASE_API_URI,
50
50
  port: Number(process.env.PORT),
@@ -26,7 +26,7 @@ export default class LoggerService implements IService<any> {
26
26
  systemService: SystemService;
27
27
  configsService: ConfigsService;
28
28
  });
29
- load: ({}: {}) => Promise<void>;
29
+ load: () => Promise<void>;
30
30
  winstonLoader: ({ loadPath, loggerLevel }: {
31
31
  loadPath: string;
32
32
  loggerLevel: string;
@@ -43,18 +43,18 @@ export default class LoggerService {
43
43
  constructor({ systemService, configsService }) {
44
44
  this.#systemService = systemService;
45
45
  this.#configsService = configsService;
46
- this.kafkaLogsTopic = this.#configsService?.all.kafkaLogsTopic;
46
+ this.serviceName = this.#systemService?.tmp?.configs?.service;
47
+ this.kafkaLogsTopic = this.#configsService?.all?.kafkaLogsTopic;
48
+ }
49
+ load = async () => {
47
50
  winston.addColors(customColors);
48
51
  this.winston = winston.createLogger({
49
- level: this.#configsService.all.systemLoggerLayer || 'info',
52
+ level: this.#configsService?.all?.systemLoggerLayer || 'info',
50
53
  levels: customLevels,
51
54
  format: this.getConsoleFormat(),
52
55
  transports: [new winston.transports.Console()]
53
56
  });
54
- }
55
- load = async ({}) => {
56
- const loggersConfigs = this.#systemService.tmp.configs.loaders.loggers;
57
- this.serviceName = this.#systemService.tmp.configs.service;
57
+ const loggersConfigs = this.#systemService?.tmp?.configs?.loaders?.loggers;
58
58
  const { kafkaBrokers, isKafkaPassive } = this.#configsService.all;
59
59
  const brokers = kafkaBrokers ? String(kafkaBrokers).split(",").map((b) => b.trim()) : [];
60
60
  const isKafkaEnabled = isKafkaPassive === false && brokers.length > 0;
@@ -76,9 +76,11 @@ export default class LoggerService {
76
76
  this.isKafkaReady = false;
77
77
  }
78
78
  }
79
+ const logPath = loggersConfigs?.loadPath || "logs";
80
+ const loggerLevel = loggersConfigs?.loggerLevel || "info";
79
81
  this.winstonLoader({
80
- loadPath: path.join(this.#configsService.all.rootFolderPath, loggersConfigs?.loadPath),
81
- loggerLevel: loggersConfigs?.loggerLevel
82
+ loadPath: path.join(this.#configsService.all.rootFolderPath, logPath),
83
+ loggerLevel: loggerLevel
82
84
  });
83
85
  };
84
86
  winstonLoader = ({ loadPath, loggerLevel }) => {
@@ -16,7 +16,9 @@ const coreLoader = async ({}) => {
16
16
  await (await x.load([
17
17
  path.join(__dirname, '..', 'loader', '**/*.{ts,js}'),
18
18
  ], {
19
- exclude: [path.join(__dirname, '..', 'utils', '**/class.x.{ts,js}')]
19
+ exclude: [
20
+ path.join(__dirname, '..', 'utils', '**/class.x.{ts,js}'),
21
+ ]
20
22
  }))
21
23
  .initialize([
22
24
  {
@@ -1,19 +1,22 @@
1
1
  import { AwilixContainer, LifetimeType } from 'awilix';
2
+ type Constructor<T> = new (...args: any[]) => T;
3
+ type AbstractConstructor<T> = abstract new (...args: any[]) => T;
2
4
  declare abstract class XHelper {
3
5
  protected checkRegistration(container: AwilixContainer, name: string): boolean;
4
6
  }
5
7
  declare class X extends XHelper {
6
8
  private container;
7
9
  private initializedNames;
10
+ private loadedPaths;
11
+ private registeredClasses;
12
+ private isInitializing;
8
13
  constructor();
9
- private getSource;
10
14
  load(patterns: string | string[], options?: {
11
15
  verbose?: boolean;
12
16
  exclude?: string | Function | (string | Function)[];
13
17
  lifetime?: LifetimeType;
14
18
  }): Promise<this>;
15
19
  register(Dependency: any, lifetime?: LifetimeType): this;
16
- registerAll(dependencies: any[], lifetime?: LifetimeType): this;
17
20
  registerInstance(name: string, instance: any): this;
18
21
  initialize<T = any>(input?: {
19
22
  service: any;
@@ -22,8 +25,8 @@ declare class X extends XHelper {
22
25
  service: any;
23
26
  props?: T;
24
27
  }[], verbose?: boolean): Promise<this>;
25
- shutdown(verbose?: boolean): Promise<void>;
26
- get<T>(target: (new (...args: any[]) => T) | string): T;
28
+ get<T>(target: Constructor<T> | AbstractConstructor<T> | string): T;
29
+ shutdown(): Promise<void>;
27
30
  get cradle(): any;
28
31
  }
29
32
  declare const _default: X;
@@ -3,12 +3,15 @@ import path from 'node:path';
3
3
  import { pathToFileURL } from 'node:url';
4
4
  class XHelper {
5
5
  checkRegistration(container, name) {
6
- return !!container.registrations[name] && container.registrations[name].resolve !== undefined;
6
+ return !!container.registrations[name];
7
7
  }
8
8
  }
9
9
  class X extends XHelper {
10
10
  container;
11
11
  initializedNames = new Set();
12
+ loadedPaths = new Set();
13
+ registeredClasses = new Set();
14
+ isInitializing = false;
12
15
  constructor() {
13
16
  super();
14
17
  this.container = createContainer({
@@ -16,18 +19,13 @@ class X extends XHelper {
16
19
  strict: true
17
20
  });
18
21
  }
19
- getSource(filePath) {
20
- const projectRoot = process.cwd().replace(/\\/g, '/');
21
- const normalizedPath = filePath.replace(/\\/g, '/');
22
- return normalizedPath.includes('node_modules') || !normalizedPath.startsWith(projectRoot) ? 'LIB' : 'APP';
23
- }
24
22
  async load(patterns, options = {}) {
25
23
  const { verbose = false, exclude = [], lifetime = Lifetime.SINGLETON } = options;
26
24
  const cwd = process.cwd();
27
25
  const excludeList = Array.isArray(exclude) ? exclude : [exclude];
28
26
  const resolvedPatterns = (Array.isArray(patterns) ? patterns : [patterns]).map(p => {
29
27
  const resolved = path.isAbsolute(p) ? p : path.resolve(cwd, p);
30
- return resolved.replace(/\\/g, '/');
28
+ return resolved.replace(/\\/g, '/').toLowerCase();
31
29
  });
32
30
  let modules = [];
33
31
  try {
@@ -37,18 +35,10 @@ class X extends XHelper {
37
35
  return this;
38
36
  }
39
37
  for (const m of modules) {
40
- const source = this.getSource(m.path);
41
- const normalizedMPath = m.path.replace(/\\/g, '/');
42
- if (normalizedMPath === __filename.replace(/\\/g, '/') || normalizedMPath.endsWith('.d.ts'))
38
+ const normalizedMPath = m.path.replace(/\\/g, '/').toLowerCase();
39
+ if (this.loadedPaths.has(normalizedMPath))
43
40
  continue;
44
- const isPathExcluded = excludeList.some(ex => {
45
- if (typeof ex === 'string') {
46
- const normalizedEx = ex.replace(/\\/g, '/');
47
- return normalizedMPath.includes(normalizedEx) || m.name === ex;
48
- }
49
- return false;
50
- });
51
- if (isPathExcluded)
41
+ if (normalizedMPath === __filename.replace(/\\/g, '/').toLowerCase() || normalizedMPath.endsWith('.d.ts'))
52
42
  continue;
53
43
  try {
54
44
  const fileUrl = pathToFileURL(m.path).href;
@@ -57,116 +47,98 @@ class X extends XHelper {
57
47
  if (!dependency) {
58
48
  dependency = Object.values(loaded).find(val => typeof val === 'function' && !!val.prototype && !!val.name);
59
49
  }
60
- const isClassExcluded = excludeList.some(ex => typeof ex === 'function' && dependency === ex);
61
- if (isClassExcluded)
62
- continue;
63
- const isClass = typeof dependency === 'function' && !!dependency.prototype && !!dependency.name;
64
- if (isClass) {
65
- const className = dependency.name;
66
- const name = className.charAt(0).toLowerCase() + className.slice(1);
50
+ if (typeof dependency === 'function' && !!dependency.prototype && !!dependency.name) {
51
+ if (this.registeredClasses.has(dependency))
52
+ continue;
53
+ const name = dependency.name.charAt(0).toLowerCase() + dependency.name.slice(1);
67
54
  if (!this.checkRegistration(this.container, name)) {
68
55
  this.container.register({
69
- [name]: asClass(dependency).setLifetime(lifetime)
56
+ [name]: asClass(dependency).singleton()
70
57
  });
58
+ this.loadedPaths.add(normalizedMPath);
59
+ this.registeredClasses.add(dependency);
71
60
  }
72
61
  }
73
62
  }
74
63
  catch (err) {
75
64
  if (verbose)
76
- console.error(`[DI][${source}] Load Error:`, err.message);
65
+ console.error(`[DI] Load Error:`, err.message);
77
66
  }
78
67
  }
79
68
  return this;
80
69
  }
81
70
  register(Dependency, lifetime = Lifetime.SINGLETON) {
82
- if (!Dependency?.name)
71
+ if (!Dependency?.name || this.registeredClasses.has(Dependency))
83
72
  return this;
84
73
  const name = Dependency.name.charAt(0).toLowerCase() + Dependency.name.slice(1);
85
74
  if (this.checkRegistration(this.container, name))
86
75
  return this;
87
- this.container.register({ [name]: asClass(Dependency).setLifetime(lifetime) });
88
- return this;
89
- }
90
- registerAll(dependencies, lifetime = Lifetime.SINGLETON) {
91
- if (Array.isArray(dependencies))
92
- dependencies.forEach(dep => this.register(dep, lifetime));
76
+ this.container.register({ [name]: asClass(Dependency).singleton() });
77
+ this.registeredClasses.add(Dependency);
93
78
  return this;
94
79
  }
95
80
  registerInstance(name, instance) {
96
81
  if (!name)
97
82
  return this;
98
83
  const formattedName = name.charAt(0).toLowerCase() + name.slice(1);
99
- if (this.checkRegistration(this.container, formattedName))
100
- return this;
101
84
  this.container.register({ [formattedName]: asValue(instance) });
102
85
  return this;
103
86
  }
104
87
  async initialize(input, verbose = false) {
105
- const cradle = this.container.cradle;
106
- const inputList = input ? (Array.isArray(input) ? input : [input]) : [];
107
- const propsMap = new Map();
108
- for (const item of inputList) {
109
- if (!item?.service)
110
- continue;
111
- const name = typeof item.service === 'function' ? item.service.name.charAt(0).toLowerCase() + item.service.name.slice(1) : item.service;
112
- if (name)
113
- propsMap.set(name, item.props);
114
- }
115
- const registrations = this.container.registrations;
116
- const allKeys = new Set([...propsMap.keys(), ...Object.keys(registrations)]);
117
- for (const key of allKeys) {
118
- if (this.initializedNames.has(key))
119
- continue;
120
- const reg = registrations[key];
121
- if (!reg || reg.lifetime !== Lifetime.SINGLETON)
122
- continue;
123
- const instance = cradle[key];
124
- if (instance && typeof instance.load === 'function') {
88
+ if (this.isInitializing)
89
+ return this;
90
+ this.isInitializing = true;
91
+ try {
92
+ const cradle = this.container.cradle;
93
+ const inputList = input ? (Array.isArray(input) ? input : [input]) : [];
94
+ const propsMap = new Map();
95
+ for (const item of inputList) {
96
+ if (!item?.service)
97
+ continue;
98
+ const name = typeof item.service === 'function' ? item.service.name.charAt(0).toLowerCase() + item.service.name.slice(1) : item.service;
99
+ if (name)
100
+ propsMap.set(name, item.props);
101
+ }
102
+ const keys = Object.keys(this.container.registrations);
103
+ for (const key of keys) {
104
+ if (this.initializedNames.has(key))
105
+ continue;
106
+ const reg = this.container.registrations[key];
107
+ if (reg.lifetime !== Lifetime.SINGLETON)
108
+ continue;
125
109
  try {
126
- const props = propsMap.get(key) || {};
127
- await instance.load(props);
110
+ const instance = cradle[key];
111
+ if (instance && typeof instance.load === 'function') {
112
+ const props = propsMap.get(key) || {};
113
+ await instance.load(props);
114
+ }
128
115
  this.initializedNames.add(key);
129
116
  }
130
117
  catch (err) {
131
118
  if (verbose)
132
- console.error(`[DI] Initialization Failed: ${key} ->`, err.message);
119
+ console.error(`[DI] Init Failed (${key}):`, err.message);
133
120
  }
134
121
  }
135
122
  }
136
- return this;
137
- }
138
- async shutdown(verbose = false) {
139
- const registrations = this.container.registrations;
140
- for (const key in registrations) {
141
- const instance = this.container.cradle[key];
142
- const shutdownMethod = instance?.dispose || instance?.destroy;
143
- if (instance && typeof shutdownMethod === 'function') {
144
- try {
145
- await shutdownMethod.call(instance);
146
- if (verbose)
147
- console.log(`[DI] Shutdown: ${key}`);
148
- }
149
- catch (err) {
150
- console.error(`[DI] Shutdown Error (${key}):`, err.message);
151
- }
152
- }
123
+ finally {
124
+ this.isInitializing = false;
153
125
  }
154
- await this.container.dispose();
126
+ return this;
155
127
  }
156
128
  get(target) {
157
- try {
158
- const resolveName = typeof target === 'function'
159
- ? target.name.charAt(0).toLowerCase() + target.name.slice(1)
160
- : target;
161
- return this.container.resolve(resolveName);
162
- }
163
- catch (err) {
164
- if (err.message.includes('Cyclic dependencies')) {
165
- console.error(`\nāŒ [DI][CRITICAL] Cyclic dependency detected!\nšŸ” Path: ${err.resolutionStack}`);
166
- }
167
- throw err;
168
- }
129
+ const resolveName = typeof target === 'function'
130
+ ? target.name.charAt(0).toLowerCase() + target.name.slice(1)
131
+ : target;
132
+ return this.container.resolve(resolveName);
133
+ }
134
+ async shutdown() {
135
+ await this.container.dispose();
169
136
  }
170
137
  get cradle() { return this.container.cradle; }
171
138
  }
172
- export default new X();
139
+ const X_SYMBOL = Symbol.for('X');
140
+ const globalObj = global;
141
+ if (!globalObj[X_SYMBOL]) {
142
+ globalObj[X_SYMBOL] = new X();
143
+ }
144
+ export default globalObj[X_SYMBOL];