@tramvai/module-child-app 4.41.104 → 4.41.110

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.
@@ -1,5 +1,5 @@
1
1
  import type { ChildApp } from '@tramvai/child-app-core';
2
- import type { ChildAppFinalConfig } from '@tramvai/tokens-child-app';
2
+ import { CHILD_APP_LOADER_CACHE_OPTIONS_TOKEN, type ChildAppFinalConfig } from '@tramvai/tokens-child-app';
3
3
  import type { CREATE_CACHE_TOKEN, LOGGER_TOKEN, ENV_MANAGER_TOKEN } from '@tramvai/tokens-common';
4
4
  import { Loader } from '../shared/loader';
5
5
  import type { LoadableStats, ModuleFederationStats } from '../shared/webpack/moduleFederation';
@@ -8,10 +8,11 @@ export declare class ServerLoader extends Loader {
8
8
  private readonly initializedMap;
9
9
  private internalLoadCache;
10
10
  private log;
11
- constructor({ logger, createCache, envManager, }: {
11
+ constructor({ logger, createCache, envManager, cacheOptions, }: {
12
12
  logger: typeof LOGGER_TOKEN;
13
13
  createCache: typeof CREATE_CACHE_TOKEN;
14
14
  envManager: typeof ENV_MANAGER_TOKEN;
15
+ cacheOptions: typeof CHILD_APP_LOADER_CACHE_OPTIONS_TOKEN | null;
15
16
  });
16
17
  load(config: ChildAppFinalConfig): Promise<ChildApp | void>;
17
18
  init(config: ChildAppFinalConfig): Promise<void>;
@@ -4,13 +4,22 @@ import { Loader } from '../shared/loader.es.js';
4
4
  import { initModuleFederation, getModuleFederation } from '../shared/webpack/moduleFederation.es.js';
5
5
 
6
6
  class ServerLoader extends Loader {
7
- constructor({ logger, createCache, envManager, }) {
7
+ constructor({ logger, createCache, envManager, cacheOptions, }) {
8
8
  super();
9
9
  this.initializedMap = new WeakMap();
10
10
  const cache = createCache('memory', {
11
11
  name: 'child-app-loader',
12
12
  ttl: 1000 * 60 * 60 * 24 * 5,
13
- max: 20,
13
+ // When Child App script is evicted from server loader cache, we get a small memory leak,
14
+ // because providers in singleton child DI, page components / actions, will store a reference to removed script,
15
+ // and server loader cache will contain a new instance of the same script.
16
+ //
17
+ // So, it is better to have bigger cache size to prevent evicting from cache,
18
+ // also for one Child App we need to save 3 elements - server JS, stats JSON and loadable stats JSON.
19
+ //
20
+ // TODO: cache cleanup for previous versions of Child Apps
21
+ max: 100,
22
+ ...cacheOptions,
14
23
  });
15
24
  this.internalLoadCache = cache;
16
25
  this.log = logger('child-app:loader');
@@ -12,13 +12,22 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
12
12
  var isNil__default = /*#__PURE__*/_interopDefaultLegacy(isNil);
13
13
 
14
14
  class ServerLoader extends loader.Loader {
15
- constructor({ logger, createCache, envManager, }) {
15
+ constructor({ logger, createCache, envManager, cacheOptions, }) {
16
16
  super();
17
17
  this.initializedMap = new WeakMap();
18
18
  const cache = createCache('memory', {
19
19
  name: 'child-app-loader',
20
20
  ttl: 1000 * 60 * 60 * 24 * 5,
21
- max: 20,
21
+ // When Child App script is evicted from server loader cache, we get a small memory leak,
22
+ // because providers in singleton child DI, page components / actions, will store a reference to removed script,
23
+ // and server loader cache will contain a new instance of the same script.
24
+ //
25
+ // So, it is better to have bigger cache size to prevent evicting from cache,
26
+ // also for one Child App we need to save 3 elements - server JS, stats JSON and loadable stats JSON.
27
+ //
28
+ // TODO: cache cleanup for previous versions of Child Apps
29
+ max: 100,
30
+ ...cacheOptions,
22
31
  });
23
32
  this.internalLoadCache = cache;
24
33
  this.log = logger('child-app:loader');
@@ -3,7 +3,7 @@ import { Scope, optional, DI_TOKEN } from '@tinkoff/dippy';
3
3
  import { provide, commandLineListTokens } from '@tramvai/core';
4
4
  import { ENV_USED_TOKEN, LOGGER_TOKEN, CREATE_CACHE_TOKEN, ENV_MANAGER_TOKEN, STORE_TOKEN, ASYNC_LOCAL_STORAGE_TOKEN } from '@tramvai/tokens-common';
5
5
  import { RESOURCES_REGISTRY, RENDER_SLOTS, ResourceType, ResourceSlot, RENDER_FLOW_AFTER_TOKEN, REACT_SERVER_RENDER_MODE, EXTEND_RENDER } from '@tramvai/tokens-render';
6
- import { CHILD_APP_LOADER_TOKEN, CHILD_APP_STATE_MANAGER_TOKEN, CHILD_APP_DI_MANAGER_TOKEN, CHILD_APP_PRELOAD_MANAGER_TOKEN, CHILD_APP_COMMAND_LINE_RUNNER_TOKEN, CHILD_APP_RESOLUTION_CONFIG_MANAGER_TOKEN, CHILD_APP_RESOLVE_CONFIG_TOKEN, CHILD_APP_RENDER_MANAGER_TOKEN, CHILD_APP_CONTRACT_MANAGER, HOST_PROVIDED_CONTRACTS, HOST_REQUIRED_CONTRACTS } from '@tramvai/tokens-child-app';
6
+ import { CHILD_APP_LOADER_TOKEN, CHILD_APP_LOADER_CACHE_OPTIONS_TOKEN, CHILD_APP_STATE_MANAGER_TOKEN, CHILD_APP_DI_MANAGER_TOKEN, CHILD_APP_PRELOAD_MANAGER_TOKEN, CHILD_APP_COMMAND_LINE_RUNNER_TOKEN, CHILD_APP_RESOLUTION_CONFIG_MANAGER_TOKEN, CHILD_APP_RESOLVE_CONFIG_TOKEN, CHILD_APP_RENDER_MANAGER_TOKEN, CHILD_APP_CONTRACT_MANAGER, HOST_PROVIDED_CONTRACTS, HOST_REQUIRED_CONTRACTS } from '@tramvai/tokens-child-app';
7
7
  import { safeStringify } from '@tramvai/safe-strings';
8
8
  import { ServerLoader } from './loader.es.js';
9
9
  import { PreloadManager } from './preload.es.js';
@@ -33,6 +33,7 @@ const serverProviders = [
33
33
  deps: {
34
34
  logger: LOGGER_TOKEN,
35
35
  createCache: CREATE_CACHE_TOKEN,
36
+ cacheOptions: optional(CHILD_APP_LOADER_CACHE_OPTIONS_TOKEN),
36
37
  envManager: ENV_MANAGER_TOKEN,
37
38
  },
38
39
  }),
@@ -37,6 +37,7 @@ const serverProviders = [
37
37
  deps: {
38
38
  logger: tokensCommon.LOGGER_TOKEN,
39
39
  createCache: tokensCommon.CREATE_CACHE_TOKEN,
40
+ cacheOptions: dippy.optional(tokensChildApp.CHILD_APP_LOADER_CACHE_OPTIONS_TOKEN),
40
41
  envManager: tokensCommon.ENV_MANAGER_TOKEN,
41
42
  },
42
43
  }),
@@ -11,9 +11,10 @@ class ChildAppPageService {
11
11
  (pageComponents || []).forEach((record) => {
12
12
  Object.keys(record).forEach((pageComponentName) => {
13
13
  const pageComponent = record[pageComponentName];
14
- if (!this.componentRegistry.get(pageComponentName, componentsGroupName)) {
15
- this.componentRegistry.add(pageComponentName, pageComponent, componentsGroupName);
16
- }
14
+ // to prevent memory leaks, always register Child App components,
15
+ // because if Child App server JS is loaded again after eviction from server loader cache,
16
+ // we want to load new instance of components and actions and clear memory references to old server JS
17
+ this.componentRegistry.add(pageComponentName, pageComponent, componentsGroupName);
17
18
  });
18
19
  });
19
20
  }
@@ -11,9 +11,10 @@ class ChildAppPageService {
11
11
  (pageComponents || []).forEach((record) => {
12
12
  Object.keys(record).forEach((pageComponentName) => {
13
13
  const pageComponent = record[pageComponentName];
14
- if (!this.componentRegistry.get(pageComponentName, componentsGroupName)) {
15
- this.componentRegistry.add(pageComponentName, pageComponent, componentsGroupName);
16
- }
14
+ // to prevent memory leaks, always register Child App components,
15
+ // because if Child App server JS is loaded again after eviction from server loader cache,
16
+ // we want to load new instance of components and actions and clear memory references to old server JS
17
+ this.componentRegistry.add(pageComponentName, pageComponent, componentsGroupName);
17
18
  });
18
19
  });
19
20
  }
@@ -19,9 +19,10 @@ class ChildAppPageService {
19
19
  (pageComponents || []).forEach((record) => {
20
20
  Object.keys(record).forEach((pageComponentName) => {
21
21
  const pageComponent = record[pageComponentName];
22
- if (!this.componentRegistry.get(pageComponentName, componentsGroupName)) {
23
- this.componentRegistry.add(pageComponentName, pageComponent, componentsGroupName);
24
- }
22
+ // to prevent memory leaks, always register Child App components,
23
+ // because if Child App server JS is loaded again after eviction from server loader cache,
24
+ // we want to load new instance of components and actions and clear memory references to old server JS
25
+ this.componentRegistry.add(pageComponentName, pageComponent, componentsGroupName);
25
26
  });
26
27
  });
27
28
  }
@@ -9,6 +9,7 @@ import { shouldIsolateDi } from './isolatedDi.browser.js';
9
9
  class SingletonDiManager {
10
10
  constructor({ logger, appDi, loader, rootDiAccessMode, contractManager, }) {
11
11
  this.cache = new Map();
12
+ this.moduleCache = new WeakMap();
12
13
  this.log = logger('child-app:singleton-di-manager');
13
14
  this.appDi = appDi;
14
15
  this.loader = loader;
@@ -18,7 +19,16 @@ class SingletonDiManager {
18
19
  getChildDi(config) {
19
20
  const { key, tag } = config;
20
21
  if (this.cache.has(key)) {
21
- return this.cache.get(key);
22
+ const children = this.loader.get(config);
23
+ // When Child App script is evicted from server loader cache, we get a memory leak,
24
+ // because providers in singleton child DI will store a reference to removed script,
25
+ // and server loader cache will contain a new instance of the same script.
26
+ // To solve this case, we will try to create a new singleton child DI
27
+ // when new Child App script instance is fetched and compiled.
28
+ if (children && this.moduleCache.has(children) && this.moduleCache.get(children) === key) {
29
+ return this.cache.get(key);
30
+ }
31
+ this.cache.delete(key);
22
32
  }
23
33
  try {
24
34
  const di = this.resolveDi(config);
@@ -108,6 +118,7 @@ class SingletonDiManager {
108
118
  di.borrowToken(this.appDi, token);
109
119
  });
110
120
  }
121
+ this.moduleCache.set(children, config.key);
111
122
  return di;
112
123
  }
113
124
  }
@@ -11,6 +11,7 @@ export declare class SingletonDiManager implements ChildAppDiManager {
11
11
  private rootDiAccessMode?;
12
12
  private contractManager;
13
13
  private cache;
14
+ private moduleCache;
14
15
  constructor({ logger, appDi, loader, rootDiAccessMode, contractManager, }: {
15
16
  logger: typeof LOGGER_TOKEN;
16
17
  appDi: Container;
@@ -9,6 +9,7 @@ import { shouldIsolateDi } from './isolatedDi.es.js';
9
9
  class SingletonDiManager {
10
10
  constructor({ logger, appDi, loader, rootDiAccessMode, contractManager, }) {
11
11
  this.cache = new Map();
12
+ this.moduleCache = new WeakMap();
12
13
  this.log = logger('child-app:singleton-di-manager');
13
14
  this.appDi = appDi;
14
15
  this.loader = loader;
@@ -18,7 +19,16 @@ class SingletonDiManager {
18
19
  getChildDi(config) {
19
20
  const { key, tag } = config;
20
21
  if (this.cache.has(key)) {
21
- return this.cache.get(key);
22
+ const children = this.loader.get(config);
23
+ // When Child App script is evicted from server loader cache, we get a memory leak,
24
+ // because providers in singleton child DI will store a reference to removed script,
25
+ // and server loader cache will contain a new instance of the same script.
26
+ // To solve this case, we will try to create a new singleton child DI
27
+ // when new Child App script instance is fetched and compiled.
28
+ if (children && this.moduleCache.has(children) && this.moduleCache.get(children) === key) {
29
+ return this.cache.get(key);
30
+ }
31
+ this.cache.delete(key);
22
32
  }
23
33
  try {
24
34
  const di = this.resolveDi(config);
@@ -108,6 +118,7 @@ class SingletonDiManager {
108
118
  di.borrowToken(this.appDi, token);
109
119
  });
110
120
  }
121
+ this.moduleCache.set(children, config.key);
111
122
  return di;
112
123
  }
113
124
  }
@@ -17,6 +17,7 @@ var flatten__default = /*#__PURE__*/_interopDefaultLegacy(flatten);
17
17
  class SingletonDiManager {
18
18
  constructor({ logger, appDi, loader, rootDiAccessMode, contractManager, }) {
19
19
  this.cache = new Map();
20
+ this.moduleCache = new WeakMap();
20
21
  this.log = logger('child-app:singleton-di-manager');
21
22
  this.appDi = appDi;
22
23
  this.loader = loader;
@@ -26,7 +27,16 @@ class SingletonDiManager {
26
27
  getChildDi(config) {
27
28
  const { key, tag } = config;
28
29
  if (this.cache.has(key)) {
29
- return this.cache.get(key);
30
+ const children = this.loader.get(config);
31
+ // When Child App script is evicted from server loader cache, we get a memory leak,
32
+ // because providers in singleton child DI will store a reference to removed script,
33
+ // and server loader cache will contain a new instance of the same script.
34
+ // To solve this case, we will try to create a new singleton child DI
35
+ // when new Child App script instance is fetched and compiled.
36
+ if (children && this.moduleCache.has(children) && this.moduleCache.get(children) === key) {
37
+ return this.cache.get(key);
38
+ }
39
+ this.cache.delete(key);
30
40
  }
31
41
  try {
32
42
  const di = this.resolveDi(config);
@@ -116,6 +126,7 @@ class SingletonDiManager {
116
126
  di.borrowToken(this.appDi, token);
117
127
  });
118
128
  }
129
+ this.moduleCache.set(children, config.key);
119
130
  return di;
120
131
  }
121
132
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tramvai/module-child-app",
3
- "version": "4.41.104",
3
+ "version": "4.41.110",
4
4
  "description": "Module for child apps",
5
5
  "browser": {
6
6
  "./lib/server.js": "./lib/browser.js",
@@ -33,24 +33,24 @@
33
33
  "@tinkoff/env-validators": "0.3.3",
34
34
  "@tinkoff/module-loader-client": "0.6.7",
35
35
  "@tinkoff/module-loader-server": "0.7.7",
36
- "@tramvai/module-common": "4.41.104",
36
+ "@tramvai/module-common": "4.41.110",
37
37
  "@tinkoff/url": "0.10.3",
38
38
  "@tinkoff/errors": "0.5.3",
39
- "@tramvai/child-app-core": "4.41.104",
39
+ "@tramvai/child-app-core": "4.41.110",
40
40
  "@tramvai/safe-strings": "0.7.9",
41
- "@tramvai/tokens-child-app": "4.41.104"
41
+ "@tramvai/tokens-child-app": "4.41.110"
42
42
  },
43
43
  "devDependencies": {},
44
44
  "peerDependencies": {
45
- "@tinkoff/dippy": "0.10.11",
46
- "@tinkoff/router": "0.4.239",
45
+ "@tinkoff/dippy": "0.10.12",
46
+ "@tinkoff/router": "0.4.245",
47
47
  "@tinkoff/utils": "^2.1.2",
48
- "@tramvai/core": "4.41.104",
49
- "@tramvai/state": "4.41.104",
50
- "@tramvai/react": "4.41.104",
51
- "@tramvai/tokens-common": "4.41.104",
52
- "@tramvai/tokens-render": "4.41.104",
53
- "@tramvai/tokens-router": "4.41.104",
48
+ "@tramvai/core": "4.41.110",
49
+ "@tramvai/state": "4.41.110",
50
+ "@tramvai/react": "4.41.110",
51
+ "@tramvai/tokens-common": "4.41.110",
52
+ "@tramvai/tokens-render": "4.41.110",
53
+ "@tramvai/tokens-router": "4.41.110",
54
54
  "react": ">=16.14.0",
55
55
  "react-dom": ">=16.14.0",
56
56
  "object-assign": "^4.1.1",