File size: 2,491 Bytes
4e23b01
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import { IInstantiationService } from '#/_base/di/instantiation';
import type { InstantiationService } from '#/_base/di/instantiationService';
import { ScopeActivation, registerScopedService } from '#/_base/di/scope';
import { LifecycleScope } from '#/app/scopes';

import {
  IDebugGraphService,
  type DebugGraph,
  type DebugGraphEdge,
  type DebugGraphNode,
} from './debugGraph';
import { walkScopeContainers } from './scopeTree';

export class DebugGraphService implements IDebugGraphService {
  declare readonly _serviceBrand: undefined;

  private readonly root: InstantiationService;

  constructor(@IInstantiationService instantiation: IInstantiationService) {
    this.root = instantiation as InstantiationService;
  }

  graph(): DebugGraph {
    const infos = walkScopeContainers(this.root);
    const pathByContainer = new Map<InstantiationService, string>(
      infos.map((info) => [info.container, info.path]),
    );
    const nodes = new Map<string, DebugGraphNode>();
    for (const info of infos) {
      const states = new Map(
        info.container.cascade.unitsSnapshot().map((unit) => [unit.token, unit]),
      );
      for (const registration of info.container.servicesSnapshot()) {
        const id = nodeId(info.path, registration.token);
        nodes.set(id, {
          id,
          token: registration.token,
          scopePath: info.path,
          uid: registration.uid,
          state: states.get(registration.token)?.state,
        });
      }
    }
    const pathOf = (scope: object): string =>
      pathByContainer.get(scope as InstantiationService) ??
      `#${this.root.cascadeTree.seqOf(scope)}`;
    const edges: DebugGraphEdge[] = [];
    const endpointNode = (path: string, token: string): string => {
      const id = nodeId(path, token);
      if (!nodes.has(id)) {
        nodes.set(id, { id, token, scopePath: path });
      }
      return id;
    };
    for (const edge of this.root.dependencyGraph.edges()) {
      edges.push({
        from: endpointNode(pathOf(edge.consumer.scope), edge.consumer.token.toString()),
        to: endpointNode(pathOf(edge.dependency.scope), edge.dependency.token.toString()),
        kind: edge.kind,
      });
    }
    return { nodes: [...nodes.values()], edges };
  }
}

function nodeId(scopePath: string, token: string): string {
  return `${scopePath}::${token}`;
}

registerScopedService(
  LifecycleScope.App,
  IDebugGraphService,
  DebugGraphService,
  ScopeActivation.OnDemand,
  'debug',
);