@memlab/mcp-server 2.0.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.
Files changed (107) hide show
  1. package/README.md +285 -0
  2. package/bin/memlab-mcp.js +3 -0
  3. package/dist/heap-state.d.ts +14 -0
  4. package/dist/heap-state.d.ts.map +1 -0
  5. package/dist/heap-state.js +25 -0
  6. package/dist/heap-state.js.map +1 -0
  7. package/dist/index.d.ts +12 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +71 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/tools/aggregate-nodes.d.ts +12 -0
  12. package/dist/tools/aggregate-nodes.d.ts.map +1 -0
  13. package/dist/tools/aggregate-nodes.js +136 -0
  14. package/dist/tools/aggregate-nodes.js.map +1 -0
  15. package/dist/tools/class-histogram.d.ts +12 -0
  16. package/dist/tools/class-histogram.d.ts.map +1 -0
  17. package/dist/tools/class-histogram.js +94 -0
  18. package/dist/tools/class-histogram.js.map +1 -0
  19. package/dist/tools/closure-inspection.d.ts +12 -0
  20. package/dist/tools/closure-inspection.d.ts.map +1 -0
  21. package/dist/tools/closure-inspection.js +107 -0
  22. package/dist/tools/closure-inspection.js.map +1 -0
  23. package/dist/tools/detached-dom.d.ts +12 -0
  24. package/dist/tools/detached-dom.d.ts.map +1 -0
  25. package/dist/tools/detached-dom.js +53 -0
  26. package/dist/tools/detached-dom.js.map +1 -0
  27. package/dist/tools/dominator-subtree.d.ts +12 -0
  28. package/dist/tools/dominator-subtree.d.ts.map +1 -0
  29. package/dist/tools/dominator-subtree.js +77 -0
  30. package/dist/tools/dominator-subtree.js.map +1 -0
  31. package/dist/tools/duplicated-strings.d.ts +12 -0
  32. package/dist/tools/duplicated-strings.d.ts.map +1 -0
  33. package/dist/tools/duplicated-strings.js +78 -0
  34. package/dist/tools/duplicated-strings.js.map +1 -0
  35. package/dist/tools/eval.d.ts +12 -0
  36. package/dist/tools/eval.d.ts.map +1 -0
  37. package/dist/tools/eval.js +119 -0
  38. package/dist/tools/eval.js.map +1 -0
  39. package/dist/tools/find-by-property.d.ts +12 -0
  40. package/dist/tools/find-by-property.d.ts.map +1 -0
  41. package/dist/tools/find-by-property.js +77 -0
  42. package/dist/tools/find-by-property.js.map +1 -0
  43. package/dist/tools/find-nodes-by-class.d.ts +12 -0
  44. package/dist/tools/find-nodes-by-class.d.ts.map +1 -0
  45. package/dist/tools/find-nodes-by-class.js +38 -0
  46. package/dist/tools/find-nodes-by-class.js.map +1 -0
  47. package/dist/tools/for-each.d.ts +12 -0
  48. package/dist/tools/for-each.d.ts.map +1 -0
  49. package/dist/tools/for-each.js +185 -0
  50. package/dist/tools/for-each.js.map +1 -0
  51. package/dist/tools/get-node.d.ts +12 -0
  52. package/dist/tools/get-node.d.ts.map +1 -0
  53. package/dist/tools/get-node.js +51 -0
  54. package/dist/tools/get-node.js.map +1 -0
  55. package/dist/tools/get-property.d.ts +12 -0
  56. package/dist/tools/get-property.d.ts.map +1 -0
  57. package/dist/tools/get-property.js +88 -0
  58. package/dist/tools/get-property.js.map +1 -0
  59. package/dist/tools/get-references.d.ts +12 -0
  60. package/dist/tools/get-references.d.ts.map +1 -0
  61. package/dist/tools/get-references.js +61 -0
  62. package/dist/tools/get-references.js.map +1 -0
  63. package/dist/tools/get-referrers.d.ts +12 -0
  64. package/dist/tools/get-referrers.d.ts.map +1 -0
  65. package/dist/tools/get-referrers.js +61 -0
  66. package/dist/tools/get-referrers.js.map +1 -0
  67. package/dist/tools/global-variables.d.ts +12 -0
  68. package/dist/tools/global-variables.d.ts.map +1 -0
  69. package/dist/tools/global-variables.js +331 -0
  70. package/dist/tools/global-variables.js.map +1 -0
  71. package/dist/tools/largest-objects.d.ts +12 -0
  72. package/dist/tools/largest-objects.d.ts.map +1 -0
  73. package/dist/tools/largest-objects.js +32 -0
  74. package/dist/tools/largest-objects.js.map +1 -0
  75. package/dist/tools/load-snapshot.d.ts +12 -0
  76. package/dist/tools/load-snapshot.d.ts.map +1 -0
  77. package/dist/tools/load-snapshot.js +39 -0
  78. package/dist/tools/load-snapshot.js.map +1 -0
  79. package/dist/tools/object-shape.d.ts +12 -0
  80. package/dist/tools/object-shape.d.ts.map +1 -0
  81. package/dist/tools/object-shape.js +86 -0
  82. package/dist/tools/object-shape.js.map +1 -0
  83. package/dist/tools/reports.d.ts +12 -0
  84. package/dist/tools/reports.d.ts.map +1 -0
  85. package/dist/tools/reports.js +540 -0
  86. package/dist/tools/reports.js.map +1 -0
  87. package/dist/tools/retainer-trace.d.ts +12 -0
  88. package/dist/tools/retainer-trace.d.ts.map +1 -0
  89. package/dist/tools/retainer-trace.js +68 -0
  90. package/dist/tools/retainer-trace.js.map +1 -0
  91. package/dist/tools/search-nodes.d.ts +12 -0
  92. package/dist/tools/search-nodes.d.ts.map +1 -0
  93. package/dist/tools/search-nodes.js +85 -0
  94. package/dist/tools/search-nodes.js.map +1 -0
  95. package/dist/tools/snapshot-summary.d.ts +12 -0
  96. package/dist/tools/snapshot-summary.d.ts.map +1 -0
  97. package/dist/tools/snapshot-summary.js +73 -0
  98. package/dist/tools/snapshot-summary.js.map +1 -0
  99. package/dist/tools/stale-collections.d.ts +12 -0
  100. package/dist/tools/stale-collections.d.ts.map +1 -0
  101. package/dist/tools/stale-collections.js +97 -0
  102. package/dist/tools/stale-collections.js.map +1 -0
  103. package/dist/utils.d.ts +82 -0
  104. package/dist/utils.d.ts.map +1 -0
  105. package/dist/utils.js +220 -0
  106. package/dist/utils.js.map +1 -0
  107. package/package.json +58 -0
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall memory_lab
9
+ */
10
+ import { z } from 'zod';
11
+ import { getSnapshot } from '../heap-state.js';
12
+ import { queryNodes, formatQueryNodesResult, errorResult, textResult, } from '../utils.js';
13
+ export function registerSearchNodes(server) {
14
+ server.tool('memlab_search_nodes', 'General-purpose search for heap nodes by combining filters: name pattern (regex), node type, minimum retained/self size, and detachment status. Results sorted by retained size. Supports count-only and ids-only modes for large result sets.', {
15
+ name_pattern: z
16
+ .string()
17
+ .optional()
18
+ .describe('Regex pattern to match against node names'),
19
+ type: z
20
+ .string()
21
+ .optional()
22
+ .describe('Node type filter (e.g. object, string, closure, regexp, number, array, hidden, native, code, synthetic)'),
23
+ min_retained_size: z
24
+ .number()
25
+ .optional()
26
+ .describe('Minimum retained size in bytes'),
27
+ min_self_size: z
28
+ .number()
29
+ .optional()
30
+ .describe('Minimum self (shallow) size in bytes'),
31
+ is_detached: z
32
+ .boolean()
33
+ .optional()
34
+ .describe('Filter by detachment status'),
35
+ output_mode: z
36
+ .enum(['full', 'count', 'ids'])
37
+ .optional()
38
+ .default('full')
39
+ .describe('Output verbosity: "full" returns node summaries (default), "count" returns only the total count, "ids" returns only node IDs'),
40
+ offset: z
41
+ .number()
42
+ .optional()
43
+ .default(0)
44
+ .describe('Skip the first N results (for pagination)'),
45
+ limit: z
46
+ .number()
47
+ .optional()
48
+ .default(20)
49
+ .describe('Maximum number of results (default 20, up to 10000 for ids mode)'),
50
+ }, async ({ name_pattern, type, min_retained_size, min_self_size, is_detached, output_mode, offset, limit, }) => {
51
+ try {
52
+ const snapshot = getSnapshot();
53
+ const nameRegex = name_pattern ? new RegExp(name_pattern, 'i') : null;
54
+ const effectiveLimit = output_mode === 'ids' ? Math.min(limit, 10000) : Math.min(limit, 500);
55
+ const filter = (node) => {
56
+ if (node.id <= 3)
57
+ return false;
58
+ if (type && node.type !== type)
59
+ return false;
60
+ if (nameRegex && !nameRegex.test(node.name))
61
+ return false;
62
+ if (min_retained_size && node.retainedSize < min_retained_size)
63
+ return false;
64
+ if (min_self_size && node.self_size < min_self_size)
65
+ return false;
66
+ if (is_detached !== undefined) {
67
+ const detached = node.is_detached || node.name.startsWith('Detached ');
68
+ if (is_detached !== detached)
69
+ return false;
70
+ }
71
+ return true;
72
+ };
73
+ const result = queryNodes(snapshot, filter, {
74
+ limit: effectiveLimit,
75
+ offset,
76
+ outputMode: output_mode,
77
+ });
78
+ return textResult(formatQueryNodesResult(result, offset));
79
+ }
80
+ catch (err) {
81
+ return errorResult(err);
82
+ }
83
+ });
84
+ }
85
+ //# sourceMappingURL=search-nodes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-nodes.js","sourceRoot":"","sources":["../../src/tools/search-nodes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall memory_lab
9
+ */
10
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
11
+ export declare function registerSnapshotSummary(server: McpServer): void;
12
+ //# sourceMappingURL=snapshot-summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot-summary.d.ts","sourceRoot":"","sources":["../../src/tools/snapshot-summary.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AAavE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAmF/D"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall memory_lab
9
+ */
10
+ import memlabCore from '@memlab/core';
11
+ const { utils } = memlabCore;
12
+ import { getSnapshot, getFilePath } from '../heap-state.js';
13
+ import { formatBytes, formatNumber, markdownTable, errorResult, textResult, } from '../utils.js';
14
+ export function registerSnapshotSummary(server) {
15
+ server.tool('memlab_snapshot_summary', 'Get an overview of the loaded heap snapshot: total nodes, edges, size, and breakdown by node type with count, self size, and aggregate retained size (dominator-aware, no double-counting).', {}, async () => {
16
+ try {
17
+ const snapshot = getSnapshot();
18
+ let nodeCount = 0;
19
+ let edgeCount = 0;
20
+ let totalSelfSize = 0;
21
+ const typeStats = new Map();
22
+ snapshot.nodes.forEach(node => {
23
+ nodeCount++;
24
+ totalSelfSize += node.self_size;
25
+ let stats = typeStats.get(node.type);
26
+ if (!stats) {
27
+ stats = { count: 0, self_size: 0, node_ids: new Set() };
28
+ typeStats.set(node.type, stats);
29
+ }
30
+ stats.count++;
31
+ stats.self_size += node.self_size;
32
+ stats.node_ids.add(node.id);
33
+ });
34
+ snapshot.edges.forEach(() => {
35
+ edgeCount++;
36
+ });
37
+ // Compute aggregate retained size per type using dominator-aware
38
+ // aggregation. This deduplicates overlapping retained sizes in the
39
+ // dominator tree so the result reflects how much memory would be freed
40
+ // if all nodes of a given type were garbage-collected together.
41
+ const breakdown = [];
42
+ for (const [type, stats] of typeStats) {
43
+ const retainedSize = utils.aggregateDominatorMetrics(stats.node_ids, snapshot, () => true, (node) => node.retainedSize);
44
+ breakdown.push({
45
+ type,
46
+ count: stats.count,
47
+ self_size: stats.self_size,
48
+ retained_size: retainedSize,
49
+ });
50
+ }
51
+ breakdown.sort((a, b) => b.retained_size - a.retained_size);
52
+ const lines = [
53
+ `**File:** ${getFilePath()}`,
54
+ `**Nodes:** ${formatNumber(nodeCount)} | **Edges:** ${formatNumber(edgeCount)} | **Total Self Size:** ${formatBytes(totalSelfSize)}`,
55
+ '',
56
+ ];
57
+ const headers = ['Type', 'Count', 'Self Size', 'Retained Size'];
58
+ const rightCols = new Set([1, 2, 3]);
59
+ const rows = breakdown.map(b => [
60
+ b.type,
61
+ formatNumber(b.count),
62
+ formatBytes(b.self_size),
63
+ formatBytes(b.retained_size),
64
+ ]);
65
+ lines.push(markdownTable(headers, rows, rightCols));
66
+ return textResult(lines.join('\n'));
67
+ }
68
+ catch (err) {
69
+ return errorResult(err);
70
+ }
71
+ });
72
+ }
73
+ //# sourceMappingURL=snapshot-summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot-summary.js","sourceRoot":"","sources":["../../src/tools/snapshot-summary.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall memory_lab
9
+ */
10
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
11
+ export declare function registerStaleCollections(server: McpServer): void;
12
+ //# sourceMappingURL=stale-collections.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-collections.d.ts","sourceRoot":"","sources":["../../src/tools/stale-collections.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AA6CvE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAsEhE"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall memory_lab
9
+ */
10
+ import { z } from 'zod';
11
+ import { getSnapshot } from '../heap-state.js';
12
+ import { formatBytes, formatNumber, markdownTable, errorResult, textResult, } from '../utils.js';
13
+ const COLLECTION_NAMES = new Set(['Map', 'Set', 'WeakMap', 'WeakSet', 'Array']);
14
+ function isCollectionNode(node) {
15
+ return node.type === 'object' && COLLECTION_NAMES.has(node.name);
16
+ }
17
+ function isStaleNode(node) {
18
+ // Detached DOM nodes
19
+ if (node.is_detached || node.name.startsWith('Detached '))
20
+ return true;
21
+ // Detached React Fiber nodes
22
+ if (node.name === 'FiberNode' && node.is_detached)
23
+ return true;
24
+ return false;
25
+ }
26
+ function getCollectionChildren(node) {
27
+ // For Map/Set, follow the 'table' edge to get internal hash table children
28
+ if (node.name === 'Map' || node.name === 'Set') {
29
+ const tableEdge = node.references.find(e => e.name_or_index === 'table');
30
+ if (tableEdge) {
31
+ return tableEdge.toNode.references;
32
+ }
33
+ }
34
+ // For Array and others, use direct references
35
+ return node.references;
36
+ }
37
+ export function registerStaleCollections(server) {
38
+ server.tool('memlab_stale_collections', 'Find Map, Set, and Array collections holding references to detached DOM nodes or unmounted React Fiber nodes. These collections prevent garbage collection of stale objects.', {
39
+ limit: z
40
+ .number()
41
+ .optional()
42
+ .default(15)
43
+ .describe('Maximum number of results (default 15)'),
44
+ }, async ({ limit }) => {
45
+ try {
46
+ const snapshot = getSnapshot();
47
+ const results = [];
48
+ snapshot.nodes.forEach(node => {
49
+ if (!isCollectionNode(node))
50
+ return;
51
+ const children = getCollectionChildren(node);
52
+ let staleCount = 0;
53
+ let staleRetainedSize = 0;
54
+ let totalChildren = 0;
55
+ for (const edge of children) {
56
+ totalChildren++;
57
+ if (isStaleNode(edge.toNode)) {
58
+ staleCount++;
59
+ staleRetainedSize += edge.toNode.retainedSize;
60
+ }
61
+ }
62
+ if (staleCount > 0) {
63
+ results.push({
64
+ collection: node,
65
+ stale_item_count: staleCount,
66
+ stale_retained_size: staleRetainedSize,
67
+ total_children: totalChildren,
68
+ });
69
+ }
70
+ });
71
+ // Sort by stale retained size descending
72
+ results.sort((a, b) => b.stale_retained_size - a.stale_retained_size);
73
+ const topResults = results.slice(0, limit);
74
+ if (topResults.length === 0) {
75
+ return textResult('No stale collections found.');
76
+ }
77
+ const headers = [
78
+ 'Collection ID',
79
+ 'Name',
80
+ 'Stale / Total',
81
+ 'Stale Retained',
82
+ ];
83
+ const rightCols = new Set([2, 3]);
84
+ const rows = topResults.map(r => [
85
+ `@${r.collection.id}`,
86
+ r.collection.name,
87
+ `${formatNumber(r.stale_item_count)} / ${formatNumber(r.total_children)}`,
88
+ formatBytes(r.stale_retained_size),
89
+ ]);
90
+ return textResult(`Stale collections (${topResults.length} found)\n\n${markdownTable(headers, rows, rightCols)}`);
91
+ }
92
+ catch (err) {
93
+ return errorResult(err);
94
+ }
95
+ });
96
+ }
97
+ //# sourceMappingURL=stale-collections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-collections.js","sourceRoot":"","sources":["../../src/tools/stale-collections.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall memory_lab
9
+ */
10
+ import type { IHeapNode, IHeapEdge, IHeapSnapshot } from '@memlab/core';
11
+ export interface NodeSummary {
12
+ id: number;
13
+ name: string;
14
+ type: string;
15
+ self_size: number;
16
+ retained_size: number;
17
+ }
18
+ export interface NodeDetail extends NodeSummary {
19
+ edge_count: number;
20
+ referrer_count: number;
21
+ is_detached: boolean;
22
+ dominator_id: number | null;
23
+ location: {
24
+ script_id: number;
25
+ line: number;
26
+ column: number;
27
+ } | null;
28
+ string_value?: string;
29
+ }
30
+ export interface EdgeSummary {
31
+ edge_name: string;
32
+ edge_type: string;
33
+ }
34
+ export interface OutgoingEdge extends EdgeSummary {
35
+ to_node: NodeSummary;
36
+ }
37
+ export interface IncomingEdge extends EdgeSummary {
38
+ from_node: NodeSummary;
39
+ }
40
+ export declare function serializeNodeSummary(node: IHeapNode): NodeSummary;
41
+ export declare function serializeNodeDetail(node: IHeapNode): NodeDetail;
42
+ export declare function serializeOutgoingEdge(edge: IHeapEdge): OutgoingEdge;
43
+ export declare function serializeIncomingEdge(edge: IHeapEdge): IncomingEdge;
44
+ export declare function isNodeWorthInspecting(node: IHeapNode): boolean;
45
+ export declare function filterLargestObjects(snapshot: IHeapSnapshot, filter: (node: IHeapNode) => boolean, limit: number): IHeapNode[];
46
+ export type OutputMode = 'full' | 'count' | 'ids';
47
+ export interface QueryNodesResult {
48
+ total_count: number;
49
+ nodes?: NodeSummary[];
50
+ ids?: number[];
51
+ }
52
+ export declare function queryNodes(snapshot: IHeapSnapshot, filter: (node: IHeapNode) => boolean, opts: {
53
+ limit: number;
54
+ offset: number;
55
+ outputMode: OutputMode;
56
+ }): QueryNodesResult;
57
+ export declare function formatBytes(bytes: number): string;
58
+ export declare function formatNumber(n: number): string;
59
+ export declare function markdownTable(headers: string[], rows: string[][], alignRight?: Set<number>): string;
60
+ export declare function formatNodeInline(id: number, name: string, type: string): string;
61
+ export declare function formatNodeSummaryTable(nodes: NodeSummary[]): string;
62
+ export declare function formatQueryNodesResult(result: QueryNodesResult, offset?: number): string;
63
+ export declare function textResult(text: string): {
64
+ content: {
65
+ type: "text";
66
+ text: string;
67
+ }[];
68
+ };
69
+ export declare function jsonResult(data: unknown): {
70
+ content: {
71
+ type: "text";
72
+ text: string;
73
+ }[];
74
+ };
75
+ export declare function errorResult(err: unknown): {
76
+ content: {
77
+ type: "text";
78
+ text: string;
79
+ }[];
80
+ isError: boolean;
81
+ };
82
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
package/dist/utils.js ADDED
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall memory_lab
9
+ */
10
+ export function serializeNodeSummary(node) {
11
+ return {
12
+ id: node.id,
13
+ name: node.name,
14
+ type: node.type,
15
+ self_size: node.self_size,
16
+ retained_size: node.retainedSize,
17
+ };
18
+ }
19
+ export function serializeNodeDetail(node) {
20
+ const detail = {
21
+ id: node.id,
22
+ name: node.name,
23
+ type: node.type,
24
+ self_size: node.self_size,
25
+ retained_size: node.retainedSize,
26
+ edge_count: node.edge_count,
27
+ referrer_count: node.numOfReferrers,
28
+ is_detached: node.is_detached,
29
+ dominator_id: node.dominatorNode?.id ?? null,
30
+ location: null,
31
+ };
32
+ const loc = node.location;
33
+ if (loc) {
34
+ detail.location = {
35
+ script_id: loc.script_id,
36
+ line: loc.line,
37
+ column: loc.column,
38
+ };
39
+ }
40
+ if (node.isString) {
41
+ const strNode = node.toStringNode();
42
+ if (strNode) {
43
+ detail.string_value = strNode.stringValue;
44
+ }
45
+ }
46
+ return detail;
47
+ }
48
+ export function serializeOutgoingEdge(edge) {
49
+ return {
50
+ edge_name: String(edge.name_or_index),
51
+ edge_type: edge.type,
52
+ to_node: serializeNodeSummary(edge.toNode),
53
+ };
54
+ }
55
+ export function serializeIncomingEdge(edge) {
56
+ return {
57
+ edge_name: String(edge.name_or_index),
58
+ edge_type: edge.type,
59
+ from_node: serializeNodeSummary(edge.fromNode),
60
+ };
61
+ }
62
+ const NODE_TYPE_BLOCK_LIST = new Set([
63
+ 'array',
64
+ 'native',
65
+ 'code',
66
+ 'synthetic',
67
+ 'hidden',
68
+ ]);
69
+ const NODE_NAME_BLOCK_LIST = new Set([
70
+ '(Startup object cache)',
71
+ '(Global handles)',
72
+ '(External strings)',
73
+ '(Builtins)',
74
+ ]);
75
+ export function isNodeWorthInspecting(node) {
76
+ if (node.id <= 3) {
77
+ return false;
78
+ }
79
+ if (NODE_TYPE_BLOCK_LIST.has(node.type)) {
80
+ return false;
81
+ }
82
+ if (NODE_NAME_BLOCK_LIST.has(node.name)) {
83
+ return false;
84
+ }
85
+ return true;
86
+ }
87
+ export function filterLargestObjects(snapshot, filter, limit) {
88
+ let result = [];
89
+ snapshot.nodes.forEach(node => {
90
+ if (!filter(node)) {
91
+ return;
92
+ }
93
+ const size = node.retainedSize;
94
+ let i;
95
+ for (i = result.length - 1; i >= 0; --i) {
96
+ if (result[i].retainedSize >= size) {
97
+ result.splice(i + 1, 0, node);
98
+ break;
99
+ }
100
+ }
101
+ if (i < 0) {
102
+ result.unshift(node);
103
+ }
104
+ result = result.slice(0, limit);
105
+ });
106
+ return result;
107
+ }
108
+ export function queryNodes(snapshot, filter, opts) {
109
+ const { limit, offset, outputMode } = opts;
110
+ if (outputMode === 'count') {
111
+ let total = 0;
112
+ snapshot.nodes.forEach(node => {
113
+ if (filter(node))
114
+ total++;
115
+ });
116
+ return { total_count: total };
117
+ }
118
+ // Collect all matching nodes sorted by retained size desc
119
+ const sorted = [];
120
+ snapshot.nodes.forEach(node => {
121
+ if (!filter(node))
122
+ return;
123
+ const size = node.retainedSize;
124
+ let i;
125
+ for (i = sorted.length - 1; i >= 0; --i) {
126
+ if (sorted[i].retainedSize >= size) {
127
+ sorted.splice(i + 1, 0, node);
128
+ break;
129
+ }
130
+ }
131
+ if (i < 0) {
132
+ sorted.unshift(node);
133
+ }
134
+ });
135
+ const total_count = sorted.length;
136
+ const sliced = sorted.slice(offset, offset + limit);
137
+ if (outputMode === 'ids') {
138
+ return { total_count, ids: sliced.map(n => n.id) };
139
+ }
140
+ return { total_count, nodes: sliced.map(serializeNodeSummary) };
141
+ }
142
+ export function formatBytes(bytes) {
143
+ if (bytes < 1024)
144
+ return `${bytes} B`;
145
+ if (bytes < 1024 * 1024)
146
+ return `${(bytes / 1024).toFixed(1)} KB`;
147
+ if (bytes < 1024 * 1024 * 1024)
148
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
149
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
150
+ }
151
+ export function formatNumber(n) {
152
+ return n.toLocaleString('en-US');
153
+ }
154
+ export function markdownTable(headers, rows, alignRight) {
155
+ // Compute column widths
156
+ const widths = headers.map((h, i) => Math.max(h.length, ...rows.map(r => (r[i] ?? '').length)));
157
+ const pad = (s, w, right) => right ? s.padStart(w) : s.padEnd(w);
158
+ const headerLine = '| ' +
159
+ headers.map((h, i) => pad(h, widths[i], alignRight?.has(i))).join(' | ') +
160
+ ' |';
161
+ const separatorLine = '|' +
162
+ widths
163
+ .map((w, i) => alignRight?.has(i) ? '-'.repeat(w + 1) + ':' : '-'.repeat(w + 2))
164
+ .map(s => (s.length < 3 ? s + '-' : s))
165
+ .join('|') +
166
+ '|';
167
+ const dataLines = rows.map(row => '| ' +
168
+ row
169
+ .map((cell, i) => pad(cell, widths[i], alignRight?.has(i)))
170
+ .join(' | ') +
171
+ ' |');
172
+ return [headerLine, separatorLine, ...dataLines].join('\n');
173
+ }
174
+ export function formatNodeInline(id, name, type) {
175
+ return `@${id} ${name} (${type})`;
176
+ }
177
+ export function formatNodeSummaryTable(nodes) {
178
+ const headers = ['ID', 'Name', 'Type', 'Self Size', 'Retained Size'];
179
+ const rightCols = new Set([3, 4]);
180
+ const rows = nodes.map(n => [
181
+ `@${n.id}`,
182
+ n.name,
183
+ n.type,
184
+ formatBytes(n.self_size),
185
+ formatBytes(n.retained_size),
186
+ ]);
187
+ return markdownTable(headers, rows, rightCols);
188
+ }
189
+ export function formatQueryNodesResult(result, offset) {
190
+ if (result.nodes != null) {
191
+ if (result.nodes.length === 0) {
192
+ return `No matching nodes found (total: ${formatNumber(result.total_count)})`;
193
+ }
194
+ const lines = [`Total: ${formatNumber(result.total_count)} nodes\n`];
195
+ lines.push(formatNodeSummaryTable(result.nodes));
196
+ return lines.join('\n');
197
+ }
198
+ if (result.ids != null) {
199
+ const lines = [
200
+ `Total: ${formatNumber(result.total_count)} | Showing ${formatNumber(result.ids.length)} (offset ${offset ?? 0})`,
201
+ ];
202
+ lines.push(`IDs: ${result.ids.join(', ')}`);
203
+ return lines.join('\n');
204
+ }
205
+ return `Total matching nodes: ${formatNumber(result.total_count)}`;
206
+ }
207
+ export function textResult(text) {
208
+ return { content: [{ type: 'text', text }] };
209
+ }
210
+ export function jsonResult(data) {
211
+ return textResult(JSON.stringify(data, null, 2));
212
+ }
213
+ export function errorResult(err) {
214
+ const msg = err instanceof Error ? err.message : String(err);
215
+ return {
216
+ content: [{ type: 'text', text: `Error: ${msg}` }],
217
+ isError: true,
218
+ };
219
+ }
220
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAkCH,MAAM,UAAU,oBAAoB,CAAC,IAAe;IAClD,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,aAAa,EAAE,IAAI,CAAC,YAAY;KACjC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAe;IACjD,MAAM,MAAM,GAAe;QACzB,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,aAAa,EAAE,IAAI,CAAC,YAAY;QAChC,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,YAAY,EAAE,IAAI,CAAC,aAAa,EAAE,EAAE,IAAI,IAAI;QAC5C,QAAQ,EAAE,IAAI;KACf,CAAC;IAEF,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC1B,IAAI,GAAG,EAAE,CAAC;QACR,MAAM,CAAC,QAAQ,GAAG;YAChB,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,MAAM,EAAE,GAAG,CAAC,MAAM;SACnB,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QACpC,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,IAAe;IACnD,OAAO;QACL,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC;QACrC,SAAS,EAAE,IAAI,CAAC,IAAI;QACpB,OAAO,EAAE,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC;KAC3C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,IAAe;IACnD,OAAO;QACL,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC;QACrC,SAAS,EAAE,IAAI,CAAC,IAAI;QACpB,SAAS,EAAE,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC;IACnC,OAAO;IACP,QAAQ;IACR,MAAM;IACN,WAAW;IACX,QAAQ;CACT,CAAC,CAAC;AAEH,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC;IACnC,wBAAwB;IACxB,kBAAkB;IAClB,oBAAoB;IACpB,YAAY;CACb,CAAC,CAAC;AAEH,MAAM,UAAU,qBAAqB,CAAC,IAAe;IACnD,IAAI,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC;QACjB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,QAAuB,EACvB,MAAoC,EACpC,KAAa;IAEb,IAAI,MAAM,GAAgB,EAAE,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QAC5B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC;QAC/B,IAAI,CAAS,CAAC;QACd,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YACxC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,IAAI,IAAI,EAAE,CAAC;gBACnC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;gBAC9B,MAAM;YACR,CAAC;QACH,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACV,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;QACD,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC;AAUD,MAAM,UAAU,UAAU,CACxB,QAAuB,EACvB,MAAoC,EACpC,IAA6D;IAE7D,MAAM,EAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAC,GAAG,IAAI,CAAC;IAEzC,IAAI,UAAU,KAAK,OAAO,EAAE,CAAC;QAC3B,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;YAC5B,IAAI,MAAM,CAAC,IAAI,CAAC;gBAAE,KAAK,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,OAAO,EAAC,WAAW,EAAE,KAAK,EAAC,CAAC;IAC9B,CAAC;IAED,0DAA0D;IAC1D,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QAC5B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;YAAE,OAAO;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC;QAC/B,IAAI,CAAS,CAAC;QACd,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YACxC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,IAAI,IAAI,EAAE,CAAC;gBACnC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;gBAC9B,MAAM;YACR,CAAC;QACH,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACV,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC;IAClC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC;IAEpD,IAAI,UAAU,KAAK,KAAK,EAAE,CAAC;QACzB,OAAO,EAAC,WAAW,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAC,CAAC;IACnD,CAAC;IAED,OAAO,EAAC,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAC,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,GAAG,KAAK,IAAI,CAAC;IACtC,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAClE,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI;QAC5B,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IACpD,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,OAAO,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,OAAiB,EACjB,IAAgB,EAChB,UAAwB;IAExB,wBAAwB;IACxB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAClC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAC1D,CAAC;IAEF,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,KAAe,EAAE,EAAE,CACpD,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAEtC,MAAM,UAAU,GACd,IAAI;QACJ,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;QACxE,IAAI,CAAC;IACP,MAAM,aAAa,GACjB,GAAG;QACH,MAAM;aACH,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACZ,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CACjE;aACA,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;aACtC,IAAI,CAAC,GAAG,CAAC;QACZ,GAAG,CAAC;IACN,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CACxB,GAAG,CAAC,EAAE,CACJ,IAAI;QACJ,GAAG;aACA,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;aAC1D,IAAI,CAAC,KAAK,CAAC;QACd,IAAI,CACP,CAAC;IAEF,OAAO,CAAC,UAAU,EAAE,aAAa,EAAE,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,EAAU,EACV,IAAY,EACZ,IAAY;IAEZ,OAAO,IAAI,EAAE,IAAI,IAAI,KAAK,IAAI,GAAG,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAoB;IACzD,MAAM,OAAO,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,CAAC,CAAC;IACrE,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1B,IAAI,CAAC,CAAC,EAAE,EAAE;QACV,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,IAAI;QACN,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;QACxB,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC;KAC7B,CAAC,CAAC;IACH,OAAO,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,MAAwB,EACxB,MAAe;IAEf,IAAI,MAAM,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;QACzB,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,mCAAmC,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC;QAChF,CAAC;QACD,MAAM,KAAK,GAAG,CAAC,UAAU,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;QACrE,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACjD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IACD,IAAI,MAAM,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG;YACZ,UAAU,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,cAAc,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,MAAM,IAAI,CAAC,GAAG;SAClH,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,QAAQ,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5C,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,yBAAyB,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,EAAC,OAAO,EAAE,CAAC,EAAC,IAAI,EAAE,MAAe,EAAE,IAAI,EAAC,CAAC,EAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAa;IACtC,OAAO,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7D,OAAO;QACL,OAAO,EAAE,CAAC,EAAC,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,UAAU,GAAG,EAAE,EAAC,CAAC;QACzD,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC"}