@tramvai/module-common 3.21.0 → 3.23.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.
@@ -90,6 +90,7 @@ CommonModule = __decorate([
90
90
  }),
91
91
  provide({
92
92
  provide: EXECUTION_CONTEXT_MANAGER_TOKEN,
93
+ scope: Scope.SINGLETON,
93
94
  useClass: ExecutionContextManager,
94
95
  }),
95
96
  ...providers,
@@ -90,6 +90,7 @@ CommonModule = __decorate([
90
90
  }),
91
91
  provide({
92
92
  provide: EXECUTION_CONTEXT_MANAGER_TOKEN,
93
+ scope: Scope.SINGLETON,
93
94
  useClass: ExecutionContextManager,
94
95
  }),
95
96
  ...providers,
@@ -98,6 +98,7 @@ exports.CommonModule = tslib.__decorate([
98
98
  }),
99
99
  core.provide({
100
100
  provide: tokensCommon.EXECUTION_CONTEXT_MANAGER_TOKEN,
101
+ scope: core.Scope.SINGLETON,
101
102
  useClass: executionContextManager.ExecutionContextManager,
102
103
  }),
103
104
  ...serverProviders.providers,
@@ -1,5 +1,5 @@
1
1
  import { ACTION_PARAMETERS, isTramvaiAction } from '@tramvai/core';
2
- import { isSilentError } from '@tinkoff/errors';
2
+ import { ExecutionAbortError, isSilentError } from '@tinkoff/errors';
3
3
  import { actionServerStateEvent } from './actionTramvaiReducer.es.js';
4
4
  import { generateDeferredResolve, generateDeferredReject } from './deferred/clientScriptsUtils.es.js';
5
5
 
@@ -17,7 +17,18 @@ class ActionPageRunner {
17
17
  return this.deps.executionContextManager.withContext(this.deps.commandLineExecutionContext(), { name: 'pageActions', values: { pageActions: true } }, (executionContext, abortController) => {
18
18
  return new Promise((resolve, reject) => {
19
19
  const timeoutMarker = setTimeout(() => {
20
- this.log.warn(`page actions has exceeded timeout of ${this.deps.limitTime}ms, ignore some results of execution`);
20
+ const unfinishedActions = [];
21
+ this.deps.actionExecution.execution.forEach((value, key) => {
22
+ if (value.status === 'pending') {
23
+ unfinishedActions.push(key);
24
+ }
25
+ });
26
+ this.log.warn({
27
+ event: `actions-execution-timeout`,
28
+ message: `Page actions has exceeded timeout of ${this.deps.limitTime}ms, ignore some results of execution.
29
+ You can find more detailed information from "action-execution-error" logs, and find relative logs by using the same "x-request-id" header`,
30
+ unfinishedActions,
31
+ });
21
32
  abortController.abort();
22
33
  // eslint-disable-next-line @typescript-eslint/no-use-before-define
23
34
  endChecker();
@@ -68,19 +79,39 @@ class ActionPageRunner {
68
79
  });
69
80
  }
70
81
  return promise;
82
+ })
83
+ .then((payload) => {
84
+ var _a;
85
+ if (executionContext.abortSignal.aborted) {
86
+ const parameters = isTramvaiAction(action) ? action : action[ACTION_PARAMETERS];
87
+ const actionName = (_a = parameters === null || parameters === void 0 ? void 0 : parameters.name) !== null && _a !== void 0 ? _a : 'unknown';
88
+ const contextName = `${executionContext.name}.${actionName}`;
89
+ this.log.warn({
90
+ error: new ExecutionAbortError({
91
+ message: `Execution aborted in context "${contextName}"`,
92
+ contextName,
93
+ }),
94
+ event: `action-execution-error`,
95
+ message: `${actionName} has exceeded timeout of ${this.deps.limitTime}ms, execution results will be ignored.
96
+ This action will be automatically executed on client - https://tramvai.dev/docs/features/data-fetching/action#synchronizing-between-server-and-client
97
+ If the request in this action takes too long, you can move it to the client using "onlyBrowser" condition or use Deferred Actions.
98
+ Also, the necessary network accesses may not be present.`,
99
+ });
100
+ }
101
+ return payload;
71
102
  })
72
103
  .catch((error) => {
73
104
  var _a;
74
105
  const isCriticalError = stopRunAtError(error);
75
106
  if (!isSilentError(error)) {
76
107
  const parameters = isTramvaiAction(action) ? action : action[ACTION_PARAMETERS];
77
- this.log.error({
108
+ this.log.warn({
78
109
  error,
79
110
  event: `action-execution-error`,
80
111
  message: `${(_a = parameters === null || parameters === void 0 ? void 0 : parameters.name) !== null && _a !== void 0 ? _a : 'unknown'} execution error, ${isCriticalError
81
112
  ? `${error.name} error are expected and will stop actions execution and prevent page rendering`
82
113
  : `this action will be automatically executed on client - https://tramvai.dev/docs/features/data-fetching/action#synchronizing-between-server-and-client
83
- If the request in this action takes too long, you can move it to the client using "onlyBrowser" condition.
114
+ If the request in this action takes too long, you can move it to the client using "onlyBrowser" condition or use Deferred Actions.
84
115
  Also, the necessary network accesses may not be present.`}`,
85
116
  });
86
117
  }
@@ -21,7 +21,18 @@ class ActionPageRunner {
21
21
  return this.deps.executionContextManager.withContext(this.deps.commandLineExecutionContext(), { name: 'pageActions', values: { pageActions: true } }, (executionContext, abortController) => {
22
22
  return new Promise((resolve, reject) => {
23
23
  const timeoutMarker = setTimeout(() => {
24
- this.log.warn(`page actions has exceeded timeout of ${this.deps.limitTime}ms, ignore some results of execution`);
24
+ const unfinishedActions = [];
25
+ this.deps.actionExecution.execution.forEach((value, key) => {
26
+ if (value.status === 'pending') {
27
+ unfinishedActions.push(key);
28
+ }
29
+ });
30
+ this.log.warn({
31
+ event: `actions-execution-timeout`,
32
+ message: `Page actions has exceeded timeout of ${this.deps.limitTime}ms, ignore some results of execution.
33
+ You can find more detailed information from "action-execution-error" logs, and find relative logs by using the same "x-request-id" header`,
34
+ unfinishedActions,
35
+ });
25
36
  abortController.abort();
26
37
  // eslint-disable-next-line @typescript-eslint/no-use-before-define
27
38
  endChecker();
@@ -72,19 +83,39 @@ class ActionPageRunner {
72
83
  });
73
84
  }
74
85
  return promise;
86
+ })
87
+ .then((payload) => {
88
+ var _a;
89
+ if (executionContext.abortSignal.aborted) {
90
+ const parameters = core.isTramvaiAction(action) ? action : action[core.ACTION_PARAMETERS];
91
+ const actionName = (_a = parameters === null || parameters === void 0 ? void 0 : parameters.name) !== null && _a !== void 0 ? _a : 'unknown';
92
+ const contextName = `${executionContext.name}.${actionName}`;
93
+ this.log.warn({
94
+ error: new errors.ExecutionAbortError({
95
+ message: `Execution aborted in context "${contextName}"`,
96
+ contextName,
97
+ }),
98
+ event: `action-execution-error`,
99
+ message: `${actionName} has exceeded timeout of ${this.deps.limitTime}ms, execution results will be ignored.
100
+ This action will be automatically executed on client - https://tramvai.dev/docs/features/data-fetching/action#synchronizing-between-server-and-client
101
+ If the request in this action takes too long, you can move it to the client using "onlyBrowser" condition or use Deferred Actions.
102
+ Also, the necessary network accesses may not be present.`,
103
+ });
104
+ }
105
+ return payload;
75
106
  })
76
107
  .catch((error) => {
77
108
  var _a;
78
109
  const isCriticalError = stopRunAtError(error);
79
110
  if (!errors.isSilentError(error)) {
80
111
  const parameters = core.isTramvaiAction(action) ? action : action[core.ACTION_PARAMETERS];
81
- this.log.error({
112
+ this.log.warn({
82
113
  error,
83
114
  event: `action-execution-error`,
84
115
  message: `${(_a = parameters === null || parameters === void 0 ? void 0 : parameters.name) !== null && _a !== void 0 ? _a : 'unknown'} execution error, ${isCriticalError
85
116
  ? `${error.name} error are expected and will stop actions execution and prevent page rendering`
86
117
  : `this action will be automatically executed on client - https://tramvai.dev/docs/features/data-fetching/action#synchronizing-between-server-and-client
87
- If the request in this action takes too long, you can move it to the client using "onlyBrowser" condition.
118
+ If the request in this action takes too long, you can move it to the client using "onlyBrowser" condition or use Deferred Actions.
88
119
  Also, the necessary network accesses may not be present.`}`,
89
120
  });
90
121
  }
@@ -18,7 +18,7 @@ AsyncLocalStorageModule = __decorate([
18
18
  provide({
19
19
  provide: WEB_FASTIFY_APP_INIT_TOKEN,
20
20
  multi: true,
21
- scope: Scope.REQUEST,
21
+ scope: Scope.SINGLETON,
22
22
  useFactory: ({ app, storage }) => {
23
23
  return () => {
24
24
  app.addHook('onRequest', (req, reply, done) => {
@@ -22,7 +22,7 @@ exports.AsyncLocalStorageModule = tslib.__decorate([
22
22
  core.provide({
23
23
  provide: tokensServerPrivate.WEB_FASTIFY_APP_INIT_TOKEN,
24
24
  multi: true,
25
- scope: core.Scope.REQUEST,
25
+ scope: core.Scope.SINGLETON,
26
26
  useFactory: ({ app, storage }) => {
27
27
  return () => {
28
28
  app.addHook('onRequest', (req, reply, done) => {
@@ -1,6 +1,6 @@
1
1
  import { __decorate } from 'tslib';
2
2
  import { createToken } from '@tinkoff/dippy';
3
- import { Module, Scope, IS_DI_CHILD_CONTAINER_TOKEN } from '@tramvai/core';
3
+ import { Module, Scope } from '@tramvai/core';
4
4
  import { CREATE_CACHE_TOKEN, CLEAR_CACHE_TOKEN, REGISTER_CLEAR_CACHE_TOKEN } from '@tramvai/tokens-common';
5
5
  import { cacheFactory } from './cacheFactory.browser.browser.js';
6
6
  import { providers } from './clientProviders.browser.js';
@@ -19,10 +19,8 @@ CacheModule = __decorate([
19
19
  },
20
20
  {
21
21
  provide: CREATE_CACHE_TOKEN,
22
- useFactory: ({ caches, isChildDi }) => {
23
- if (isChildDi) {
24
- return cacheFactory;
25
- }
22
+ scope: Scope.SINGLETON,
23
+ useFactory: ({ caches }) => {
26
24
  return (type, ...args) => {
27
25
  const cache = cacheFactory(type, ...args);
28
26
  caches.push(cache);
@@ -31,7 +29,6 @@ CacheModule = __decorate([
31
29
  },
32
30
  deps: {
33
31
  caches: cachesToken,
34
- isChildDi: { token: IS_DI_CHILD_CONTAINER_TOKEN, optional: true },
35
32
  },
36
33
  },
37
34
  {
@@ -1,6 +1,6 @@
1
1
  import { __decorate } from 'tslib';
2
2
  import { createToken } from '@tinkoff/dippy';
3
- import { Module, Scope, IS_DI_CHILD_CONTAINER_TOKEN } from '@tramvai/core';
3
+ import { Module, Scope } from '@tramvai/core';
4
4
  import { CREATE_CACHE_TOKEN, CLEAR_CACHE_TOKEN, REGISTER_CLEAR_CACHE_TOKEN } from '@tramvai/tokens-common';
5
5
  import { cacheFactory } from './cacheFactory.es.js';
6
6
  import { providers } from './serverProviders.es.js';
@@ -19,10 +19,8 @@ CacheModule = __decorate([
19
19
  },
20
20
  {
21
21
  provide: CREATE_CACHE_TOKEN,
22
- useFactory: ({ caches, isChildDi }) => {
23
- if (isChildDi) {
24
- return cacheFactory;
25
- }
22
+ scope: Scope.SINGLETON,
23
+ useFactory: ({ caches }) => {
26
24
  return (type, ...args) => {
27
25
  const cache = cacheFactory(type, ...args);
28
26
  caches.push(cache);
@@ -31,7 +29,6 @@ CacheModule = __decorate([
31
29
  },
32
30
  deps: {
33
31
  caches: cachesToken,
34
- isChildDi: { token: IS_DI_CHILD_CONTAINER_TOKEN, optional: true },
35
32
  },
36
33
  },
37
34
  {
@@ -23,10 +23,8 @@ exports.CacheModule = tslib.__decorate([
23
23
  },
24
24
  {
25
25
  provide: tokensCommon.CREATE_CACHE_TOKEN,
26
- useFactory: ({ caches, isChildDi }) => {
27
- if (isChildDi) {
28
- return cacheFactory.cacheFactory;
29
- }
26
+ scope: core.Scope.SINGLETON,
27
+ useFactory: ({ caches }) => {
30
28
  return (type, ...args) => {
31
29
  const cache = cacheFactory.cacheFactory(type, ...args);
32
30
  caches.push(cache);
@@ -35,7 +33,6 @@ exports.CacheModule = tslib.__decorate([
35
33
  },
36
34
  deps: {
37
35
  caches: cachesToken,
38
- isChildDi: { token: core.IS_DI_CHILD_CONTAINER_TOKEN, optional: true },
39
36
  },
40
37
  },
41
38
  {
@@ -1,2 +1,14 @@
1
- export declare const providers: never[];
1
+ export declare const providers: import("@tramvai/core").Provider<{
2
+ di: import("@tinkoff/dippy").Container & {
3
+ __type?: "base token" | undefined;
4
+ };
5
+ logger: import("@tramvai/tokens-common").Logger & ((configOrName: string | {
6
+ [key: string]: any;
7
+ name: string;
8
+ }) => import("@tramvai/tokens-common").Logger) & {
9
+ __type?: "base token" | undefined;
10
+ };
11
+ }, import("@tramvai/core").Command & {
12
+ __type?: "multi token" | undefined;
13
+ }>[];
2
14
  //# sourceMappingURL=serverProviders.d.ts.map
@@ -1,3 +1,150 @@
1
- const providers = [];
1
+ import { provide, commandLineListTokens, DI_TOKEN } from '@tramvai/core';
2
+ import { LOGGER_TOKEN } from '@tramvai/tokens-common';
3
+
4
+ // transform "Symbol(token)" to human readable "token"
5
+ function tokenToString(token) {
6
+ return token.toString().replace(/^Symbol\((.+)\)$/, '$1');
7
+ }
8
+ function traverseUp(node, path, callback) {
9
+ const parents = Array.from(node.parents);
10
+ parents.forEach((parentNode) => {
11
+ const pathCopy = [...path];
12
+ pathCopy.push(node);
13
+ const stop = callback(parentNode, pathCopy) === false;
14
+ if (!stop) {
15
+ traverseUp(parentNode, pathCopy, callback);
16
+ }
17
+ });
18
+ }
19
+ /**
20
+ * Usage of "Request" scoped dependencies in "Singleton" providers can lead to errors,
21
+ * for example usage of `DispatcherContext` in `commandLineListTokens.init` is meaningless
22
+ * - for this case one specific `DispatcherContext` instance will be created and cached in `init` deps.
23
+ *
24
+ * In a future, we have plan to prohibit usage of "Request" dependencies inside "Singleton" providers.
25
+ * For now, we will detect such cases and log an error in development mode.
26
+ */
27
+ function detectDiScopesCollisions({ di, logger, }) {
28
+ const log = logger('di-validator');
29
+ const collisions = new Map();
30
+ // eslint-disable-next-line prefer-destructuring
31
+ const diRecords = di.records;
32
+ const nodes = new Map();
33
+ diRecords.forEach((rootRecord, rootTokenName) => {
34
+ const rootTokenStr = tokenToString(rootTokenName);
35
+ if (rootRecord.multi) {
36
+ rootRecord.multi.forEach((rootMultiRecord) => {
37
+ processRootRecord(rootMultiRecord);
38
+ });
39
+ }
40
+ else {
41
+ processRootRecord(rootRecord);
42
+ }
43
+ function processRootRecord(rootRecord) {
44
+ if (!nodes.has(rootRecord)) {
45
+ nodes.set(rootRecord, {
46
+ name: rootTokenStr,
47
+ record: rootRecord,
48
+ parents: new Set(),
49
+ children: new Set(),
50
+ });
51
+ }
52
+ // create dependencies graph from flat list
53
+ walkOverDepsRecords(rootRecord, createAndConnectNodes);
54
+ // looking for request deps, used in singleton providers
55
+ walkOverDepsRecords(rootRecord, checkCollision);
56
+ }
57
+ function createAndConnectNodes(name, record, rootRecord) {
58
+ if (!nodes.has(record)) {
59
+ nodes.set(record, {
60
+ name,
61
+ record,
62
+ parents: new Set(),
63
+ children: new Set(),
64
+ });
65
+ }
66
+ nodes.get(record).parents.add(nodes.get(rootRecord));
67
+ nodes.get(rootRecord).children.add(nodes.get(record));
68
+ }
69
+ function checkCollision(name, record) {
70
+ if (record.scope !== 'request' || !record.factory || collisions.has(name)) {
71
+ return;
72
+ }
73
+ let collision = false;
74
+ let collisionPaths = [];
75
+ traverseUp(nodes.get(record), [], (node, paths) => {
76
+ if (collision) {
77
+ return;
78
+ }
79
+ const isSingleton = node.record.scope === 'singleton';
80
+ if (isSingleton) {
81
+ paths.push(node);
82
+ collision = true;
83
+ collisionPaths = paths.reverse();
84
+ // it is enough to detect first collision and stop traverse
85
+ return false;
86
+ }
87
+ });
88
+ if (collision) {
89
+ if (!collisions.has(name)) {
90
+ collisions.set(name, new Set());
91
+ }
92
+ collisions.get(name).add({ record, paths: collisionPaths });
93
+ }
94
+ }
95
+ function walkOverDepsRecords(rootRecord, callback) {
96
+ var _a;
97
+ const resolvedDepsList = Object.values((_a = rootRecord.resolvedDeps) !== null && _a !== void 0 ? _a : {});
98
+ resolvedDepsList.forEach((token) => {
99
+ const tokenObj = token.token || token;
100
+ const tokenName = typeof tokenObj === 'string' ? Symbol.for(tokenObj) : tokenObj.name;
101
+ const tokenStr = tokenToString(tokenName);
102
+ const tokenRecord = di.getRecord(tokenName);
103
+ if (!tokenRecord) {
104
+ return;
105
+ }
106
+ if (tokenRecord.multi) {
107
+ tokenRecord.multi.forEach((multiRecord) => {
108
+ callback(tokenStr, multiRecord, rootRecord);
109
+ });
110
+ }
111
+ else {
112
+ callback(tokenStr, tokenRecord, rootRecord);
113
+ }
114
+ });
115
+ }
116
+ });
117
+ collisions.forEach((records, key) => {
118
+ var _a, _b, _c, _d, _e;
119
+ const recordsArr = Array.from(records.values());
120
+ const { record, paths } = (_a = recordsArr[0]) !== null && _a !== void 0 ? _a : {};
121
+ let message = `\nIncorrect usage of "${key}" - token has Request scope, but requested in Singleton scoped provider.
122
+ Requested dependency path: \n ${paths.map((path) => path.name).join(' -> ')}\n`;
123
+ if (record.stack) {
124
+ message += `Request token stack trace: \n ${(_c = (_b = record.stack.split('\n')[1]) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : 'unknown'}`;
125
+ }
126
+ const root = paths[0];
127
+ if (root.record.stack) {
128
+ message += `\nSingleton token stack trace: \n ${(_e = (_d = root.record.stack.split('\n')[1]) === null || _d === void 0 ? void 0 : _d.trim()) !== null && _e !== void 0 ? _e : 'unknown'}`;
129
+ }
130
+ log.error(message);
131
+ });
132
+ }
133
+ const providers = process.env.NODE_ENV === 'development'
134
+ ? [
135
+ provide({
136
+ provide: commandLineListTokens.listen,
137
+ useFactory: (deps) => {
138
+ return () => {
139
+ detectDiScopesCollisions(deps);
140
+ };
141
+ },
142
+ deps: {
143
+ di: DI_TOKEN,
144
+ logger: LOGGER_TOKEN,
145
+ },
146
+ }),
147
+ ]
148
+ : [];
2
149
 
3
150
  export { providers };
@@ -2,6 +2,153 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- const providers = [];
5
+ var core = require('@tramvai/core');
6
+ var tokensCommon = require('@tramvai/tokens-common');
7
+
8
+ // transform "Symbol(token)" to human readable "token"
9
+ function tokenToString(token) {
10
+ return token.toString().replace(/^Symbol\((.+)\)$/, '$1');
11
+ }
12
+ function traverseUp(node, path, callback) {
13
+ const parents = Array.from(node.parents);
14
+ parents.forEach((parentNode) => {
15
+ const pathCopy = [...path];
16
+ pathCopy.push(node);
17
+ const stop = callback(parentNode, pathCopy) === false;
18
+ if (!stop) {
19
+ traverseUp(parentNode, pathCopy, callback);
20
+ }
21
+ });
22
+ }
23
+ /**
24
+ * Usage of "Request" scoped dependencies in "Singleton" providers can lead to errors,
25
+ * for example usage of `DispatcherContext` in `commandLineListTokens.init` is meaningless
26
+ * - for this case one specific `DispatcherContext` instance will be created and cached in `init` deps.
27
+ *
28
+ * In a future, we have plan to prohibit usage of "Request" dependencies inside "Singleton" providers.
29
+ * For now, we will detect such cases and log an error in development mode.
30
+ */
31
+ function detectDiScopesCollisions({ di, logger, }) {
32
+ const log = logger('di-validator');
33
+ const collisions = new Map();
34
+ // eslint-disable-next-line prefer-destructuring
35
+ const diRecords = di.records;
36
+ const nodes = new Map();
37
+ diRecords.forEach((rootRecord, rootTokenName) => {
38
+ const rootTokenStr = tokenToString(rootTokenName);
39
+ if (rootRecord.multi) {
40
+ rootRecord.multi.forEach((rootMultiRecord) => {
41
+ processRootRecord(rootMultiRecord);
42
+ });
43
+ }
44
+ else {
45
+ processRootRecord(rootRecord);
46
+ }
47
+ function processRootRecord(rootRecord) {
48
+ if (!nodes.has(rootRecord)) {
49
+ nodes.set(rootRecord, {
50
+ name: rootTokenStr,
51
+ record: rootRecord,
52
+ parents: new Set(),
53
+ children: new Set(),
54
+ });
55
+ }
56
+ // create dependencies graph from flat list
57
+ walkOverDepsRecords(rootRecord, createAndConnectNodes);
58
+ // looking for request deps, used in singleton providers
59
+ walkOverDepsRecords(rootRecord, checkCollision);
60
+ }
61
+ function createAndConnectNodes(name, record, rootRecord) {
62
+ if (!nodes.has(record)) {
63
+ nodes.set(record, {
64
+ name,
65
+ record,
66
+ parents: new Set(),
67
+ children: new Set(),
68
+ });
69
+ }
70
+ nodes.get(record).parents.add(nodes.get(rootRecord));
71
+ nodes.get(rootRecord).children.add(nodes.get(record));
72
+ }
73
+ function checkCollision(name, record) {
74
+ if (record.scope !== 'request' || !record.factory || collisions.has(name)) {
75
+ return;
76
+ }
77
+ let collision = false;
78
+ let collisionPaths = [];
79
+ traverseUp(nodes.get(record), [], (node, paths) => {
80
+ if (collision) {
81
+ return;
82
+ }
83
+ const isSingleton = node.record.scope === 'singleton';
84
+ if (isSingleton) {
85
+ paths.push(node);
86
+ collision = true;
87
+ collisionPaths = paths.reverse();
88
+ // it is enough to detect first collision and stop traverse
89
+ return false;
90
+ }
91
+ });
92
+ if (collision) {
93
+ if (!collisions.has(name)) {
94
+ collisions.set(name, new Set());
95
+ }
96
+ collisions.get(name).add({ record, paths: collisionPaths });
97
+ }
98
+ }
99
+ function walkOverDepsRecords(rootRecord, callback) {
100
+ var _a;
101
+ const resolvedDepsList = Object.values((_a = rootRecord.resolvedDeps) !== null && _a !== void 0 ? _a : {});
102
+ resolvedDepsList.forEach((token) => {
103
+ const tokenObj = token.token || token;
104
+ const tokenName = typeof tokenObj === 'string' ? Symbol.for(tokenObj) : tokenObj.name;
105
+ const tokenStr = tokenToString(tokenName);
106
+ const tokenRecord = di.getRecord(tokenName);
107
+ if (!tokenRecord) {
108
+ return;
109
+ }
110
+ if (tokenRecord.multi) {
111
+ tokenRecord.multi.forEach((multiRecord) => {
112
+ callback(tokenStr, multiRecord, rootRecord);
113
+ });
114
+ }
115
+ else {
116
+ callback(tokenStr, tokenRecord, rootRecord);
117
+ }
118
+ });
119
+ }
120
+ });
121
+ collisions.forEach((records, key) => {
122
+ var _a, _b, _c, _d, _e;
123
+ const recordsArr = Array.from(records.values());
124
+ const { record, paths } = (_a = recordsArr[0]) !== null && _a !== void 0 ? _a : {};
125
+ let message = `\nIncorrect usage of "${key}" - token has Request scope, but requested in Singleton scoped provider.
126
+ Requested dependency path: \n ${paths.map((path) => path.name).join(' -> ')}\n`;
127
+ if (record.stack) {
128
+ message += `Request token stack trace: \n ${(_c = (_b = record.stack.split('\n')[1]) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : 'unknown'}`;
129
+ }
130
+ const root = paths[0];
131
+ if (root.record.stack) {
132
+ message += `\nSingleton token stack trace: \n ${(_e = (_d = root.record.stack.split('\n')[1]) === null || _d === void 0 ? void 0 : _d.trim()) !== null && _e !== void 0 ? _e : 'unknown'}`;
133
+ }
134
+ log.error(message);
135
+ });
136
+ }
137
+ const providers = process.env.NODE_ENV === 'development'
138
+ ? [
139
+ core.provide({
140
+ provide: core.commandLineListTokens.listen,
141
+ useFactory: (deps) => {
142
+ return () => {
143
+ detectDiScopesCollisions(deps);
144
+ };
145
+ },
146
+ deps: {
147
+ di: core.DI_TOKEN,
148
+ logger: tokensCommon.LOGGER_TOKEN,
149
+ },
150
+ }),
151
+ ]
152
+ : [];
6
153
 
7
154
  exports.providers = providers;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tramvai/module-common",
3
- "version": "3.21.0",
3
+ "version": "3.23.0",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
@@ -37,28 +37,28 @@
37
37
  "@tinkoff/pubsub": "0.6.1",
38
38
  "@tinkoff/url": "0.9.2",
39
39
  "@tramvai/safe-strings": "0.6.2",
40
- "@tramvai/experiments": "3.21.0",
41
- "@tramvai/module-cookie": "3.21.0",
42
- "@tramvai/module-environment": "3.21.0",
43
- "@tramvai/module-log": "3.21.0",
44
- "@tramvai/tokens-child-app": "3.21.0",
45
- "@tramvai/tokens-core-private": "3.21.0",
46
- "@tramvai/tokens-common": "3.21.0",
47
- "@tramvai/tokens-render": "3.21.0",
48
- "@tramvai/tokens-server-private": "3.21.0",
49
- "@tramvai/types-actions-state-context": "3.21.0",
40
+ "@tramvai/experiments": "3.23.0",
41
+ "@tramvai/module-cookie": "3.23.0",
42
+ "@tramvai/module-environment": "3.23.0",
43
+ "@tramvai/module-log": "3.23.0",
44
+ "@tramvai/tokens-child-app": "3.23.0",
45
+ "@tramvai/tokens-core-private": "3.23.0",
46
+ "@tramvai/tokens-common": "3.23.0",
47
+ "@tramvai/tokens-render": "3.23.0",
48
+ "@tramvai/tokens-server-private": "3.23.0",
49
+ "@tramvai/types-actions-state-context": "3.23.0",
50
50
  "hoist-non-react-statics": "^3.3.1",
51
51
  "node-abort-controller": "^3.0.1"
52
52
  },
53
53
  "peerDependencies": {
54
- "@tinkoff/dippy": "0.9.1",
54
+ "@tinkoff/dippy": "0.9.2",
55
55
  "@tinkoff/utils": "^2.1.2",
56
- "@tramvai/cli": "3.21.0",
57
- "@tramvai/core": "3.21.0",
58
- "@tramvai/papi": "3.21.0",
59
- "@tramvai/react": "3.21.0",
60
- "@tramvai/state": "3.21.0",
61
- "@tramvai/tokens-server": "3.21.0",
56
+ "@tramvai/cli": "3.23.0",
57
+ "@tramvai/core": "3.23.0",
58
+ "@tramvai/papi": "3.23.0",
59
+ "@tramvai/react": "3.23.0",
60
+ "@tramvai/state": "3.23.0",
61
+ "@tramvai/tokens-server": "3.23.0",
62
62
  "react": ">=16.14.0",
63
63
  "tslib": "^2.4.0"
64
64
  },