poker-mcp 1.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.
- package/README.md +241 -0
- package/config/mcp-manifest.json +1713 -0
- package/config/package-lock.json +1335 -0
- package/config/poker_mcp.code-workspace +8 -0
- package/package.json +51 -0
- package/src/config/ConfigManager.js +120 -0
- package/src/config/default.json +62 -0
- package/src/mcp/handlers/bodyHandlers.js +63 -0
- package/src/mcp/handlers/buildupFactorHandlers.js +43 -0
- package/src/mcp/handlers/calculationHandlers.js +265 -0
- package/src/mcp/handlers/detectorHandlers.js +201 -0
- package/src/mcp/handlers/index.js +43 -0
- package/src/mcp/handlers/sourceHandlers.js +25 -0
- package/src/mcp/handlers/transformHandlers.js +26 -0
- package/src/mcp/handlers/unitHandlers.js +305 -0
- package/src/mcp/handlers/zoneHandlers.js +83 -0
- package/src/mcp/middleware/errorHandler.js +49 -0
- package/src/mcp/middleware/requestValidator.js +129 -0
- package/src/mcp/server.js +76 -0
- package/src/mcp/tools/bodyTools.js +185 -0
- package/src/mcp/tools/buildupFactorTools.js +82 -0
- package/src/mcp/tools/calculationTools.js +58 -0
- package/src/mcp/tools/commonTools.js +22 -0
- package/src/mcp/tools/detectorTools.js +131 -0
- package/src/mcp/tools/index.js +34 -0
- package/src/mcp/tools/sourceTools.js +726 -0
- package/src/mcp/tools/sourceTools_fixed_part2.js +41 -0
- package/src/mcp/tools/transformTools.js +73 -0
- package/src/mcp/tools/unitTools.js +137 -0
- package/src/mcp/tools/zoneTools.js +65 -0
- package/src/mcp_server_stdio_v4.js +45 -0
- package/src/routes/restApi.js +184 -0
- package/src/services/CalculationService.js +525 -0
- package/src/services/DataManager.js +738 -0
- package/src/services/TaskManager.js +1622 -0
- package/src/utils/MaterialAlternatives.js +189 -0
- package/src/utils/errorHandler.js +68 -0
- package/src/utils/errors.js +37 -0
- package/src/utils/logger.js +24 -0
- package/src/utils/logger_no_color.js +28 -0
- package/src/utils/mcpErrors.js +225 -0
- package/src/validators/CMBValidator.js +191 -0
- package/src/validators/DetectorValidator.js +512 -0
- package/src/validators/ManifestValidator.js +249 -0
- package/src/validators/NuclideValidator.js +171 -0
- package/src/validators/PhysicsValidator.js +401 -0
- package/src/validators/PhysicsValidatorSimple.js +87 -0
- package/src/validators/SourceValidator.js +515 -0
- package/src/validators/TransformValidator.js +472 -0
- package/src/validators/UnitValidator.js +562 -0
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
// validators/TransformValidator.js
|
|
2
|
+
import { PokerMcpError } from '../utils/mcpErrors.js';
|
|
3
|
+
import { ManifestValidator } from './ManifestValidator.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Transform参照の検証を行うクラス(強化版)
|
|
7
|
+
* YAMLデータのtransformセクション内での名前存在確認と依存関係管理
|
|
8
|
+
*/
|
|
9
|
+
export class TransformValidator {
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Transform参照の存在確認
|
|
13
|
+
* @param {string} transformName - 参照するtransform名
|
|
14
|
+
* @param {Array} existingTransforms - YAMLファイル内のtransform配列
|
|
15
|
+
* @param {string} fieldName - フィールド名(エラー表示用)
|
|
16
|
+
*/
|
|
17
|
+
static validateTransformReference(transformName, existingTransforms, fieldName = 'transform') {
|
|
18
|
+
// transform名自体のバリデーション
|
|
19
|
+
ManifestValidator.validateObjectName(transformName, fieldName);
|
|
20
|
+
|
|
21
|
+
// 既存transformsがnullまたは未定義の場合
|
|
22
|
+
if (!existingTransforms || !Array.isArray(existingTransforms)) {
|
|
23
|
+
throw PokerMcpError.transformNotFound(transformName);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// transform名の存在確認
|
|
27
|
+
const transformExists = existingTransforms.some(t => t && t.name === transformName);
|
|
28
|
+
|
|
29
|
+
if (!transformExists) {
|
|
30
|
+
throw PokerMcpError.invalidTransformReference(transformName);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Transform操作の基本バリデーション
|
|
38
|
+
* @param {Array} operations - 変換操作の配列
|
|
39
|
+
*/
|
|
40
|
+
static validateTransformOperations(operations) {
|
|
41
|
+
if (!Array.isArray(operations) || operations.length === 0) {
|
|
42
|
+
throw PokerMcpError.validationError(
|
|
43
|
+
'operations must be a non-empty array',
|
|
44
|
+
'operations',
|
|
45
|
+
operations
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (let i = 0; i < operations.length; i++) {
|
|
50
|
+
const operation = operations[i];
|
|
51
|
+
|
|
52
|
+
if (!operation || typeof operation !== 'object') {
|
|
53
|
+
throw PokerMcpError.validationError(
|
|
54
|
+
`operation[${i}] must be an object`,
|
|
55
|
+
`operations[${i}]`,
|
|
56
|
+
operation
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const keys = Object.keys(operation);
|
|
61
|
+
if (keys.length !== 1) {
|
|
62
|
+
throw PokerMcpError.validationError(
|
|
63
|
+
`operation[${i}] must have exactly one key`,
|
|
64
|
+
`operations[${i}]`,
|
|
65
|
+
operation
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const operationType = keys[0];
|
|
70
|
+
const operationValue = operation[operationType];
|
|
71
|
+
|
|
72
|
+
switch (operationType) {
|
|
73
|
+
case 'translate':
|
|
74
|
+
ManifestValidator.validateCoordinateString(operationValue, `operations[${i}].translate`);
|
|
75
|
+
break;
|
|
76
|
+
|
|
77
|
+
case 'rotate_around_x':
|
|
78
|
+
case 'rotate_around_y':
|
|
79
|
+
case 'rotate_around_z':
|
|
80
|
+
ManifestValidator.validateRotationAngle(operationValue, `operations[${i}].${operationType}`);
|
|
81
|
+
break;
|
|
82
|
+
|
|
83
|
+
default:
|
|
84
|
+
throw PokerMcpError.validationError(
|
|
85
|
+
`Invalid operation type: ${operationType}. Valid types: translate, rotate_around_x, rotate_around_y, rotate_around_z`,
|
|
86
|
+
`operations[${i}]`,
|
|
87
|
+
operationType
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Transform操作の物理的妥当性チェック
|
|
97
|
+
* @param {Array} operations - 変換操作の配列
|
|
98
|
+
* @param {string} transformName - Transform名(エラー表示用)
|
|
99
|
+
*/
|
|
100
|
+
static validateTransformPhysics(operations, transformName = 'transform') {
|
|
101
|
+
let hasRotation = false;
|
|
102
|
+
let hasTranslation = false;
|
|
103
|
+
let totalRotationMagnitude = 0;
|
|
104
|
+
|
|
105
|
+
for (let i = 0; i < operations.length; i++) {
|
|
106
|
+
const operation = operations[i];
|
|
107
|
+
const operationType = Object.keys(operation)[0];
|
|
108
|
+
const operationValue = operation[operationType];
|
|
109
|
+
|
|
110
|
+
switch (operationType) {
|
|
111
|
+
case 'translate':
|
|
112
|
+
hasTranslation = true;
|
|
113
|
+
// 移動量の大きさチェック
|
|
114
|
+
const [dx, dy, dz] = operationValue.split(/\s+/).map(Number);
|
|
115
|
+
const translationMagnitude = Math.sqrt(dx*dx + dy*dy + dz*dz);
|
|
116
|
+
if (translationMagnitude > 10000) {
|
|
117
|
+
console.warn(`Transform '${transformName}': 非常に大きな移動量です (${translationMagnitude.toFixed(2)})`);
|
|
118
|
+
}
|
|
119
|
+
break;
|
|
120
|
+
|
|
121
|
+
case 'rotate_around_x':
|
|
122
|
+
case 'rotate_around_y':
|
|
123
|
+
case 'rotate_around_z':
|
|
124
|
+
hasRotation = true;
|
|
125
|
+
totalRotationMagnitude += Math.abs(operationValue);
|
|
126
|
+
|
|
127
|
+
// 異常な回転角度のチェック
|
|
128
|
+
if (Math.abs(operationValue) > 360) {
|
|
129
|
+
console.warn(`Transform '${transformName}': 360度を超える回転角度です (${operationValue}度)`);
|
|
130
|
+
}
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 総回転量のチェック
|
|
136
|
+
if (totalRotationMagnitude > 720) {
|
|
137
|
+
console.warn(`Transform '${transformName}': 総回転量が非常に大きいです (${totalRotationMagnitude.toFixed(1)}度)`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Transformの意味チェック
|
|
141
|
+
if (!hasRotation && !hasTranslation) {
|
|
142
|
+
console.warn(`Transform '${transformName}': 回転も移動も含まれていません`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
hasRotation,
|
|
147
|
+
hasTranslation,
|
|
148
|
+
totalRotationMagnitude,
|
|
149
|
+
operationCount: operations.length
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Transform名の一意性チェック
|
|
155
|
+
* @param {string} transformName - 新しいTransform名
|
|
156
|
+
* @param {Array} existingTransforms - 既存Transform配列
|
|
157
|
+
* @param {Array} pendingChanges - 保留中の変更配列
|
|
158
|
+
*/
|
|
159
|
+
static validateTransformUniqueness(transformName, existingTransforms = [], pendingChanges = []) {
|
|
160
|
+
// 既存Transformとの重複チェック
|
|
161
|
+
if (existingTransforms.some(t => t && t.name === transformName)) {
|
|
162
|
+
throw PokerMcpError.duplicateName(transformName, 'transform');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// 保留中の変更との重複チェック
|
|
166
|
+
const pendingTransformChanges = pendingChanges.filter(
|
|
167
|
+
change => change.action === 'proposeTransform'
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
for (const change of pendingTransformChanges) {
|
|
171
|
+
if (change.data && change.data.name === transformName) {
|
|
172
|
+
throw PokerMcpError.duplicateName(transformName, 'transform (pending)');
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Body/Source/DetectorのTransform参照を検証
|
|
181
|
+
* @param {string} transformName - 参照するtransform名
|
|
182
|
+
* @param {Object} yamlData - YAMLデータ全体
|
|
183
|
+
* @param {string} contextType - コンテキストタイプ(body/source/detector)
|
|
184
|
+
* @param {string} contextName - コンテキスト名
|
|
185
|
+
*/
|
|
186
|
+
static validateContextTransformReference(transformName, yamlData, contextType, contextName) {
|
|
187
|
+
if (!transformName) {
|
|
188
|
+
return true; // transform参照はオプション
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
this.validateTransformReference(transformName, yamlData.transform, 'transform');
|
|
193
|
+
} catch (error) {
|
|
194
|
+
// エラーメッセージにコンテキスト情報を追加
|
|
195
|
+
if (error instanceof PokerMcpError) {
|
|
196
|
+
throw new PokerMcpError(
|
|
197
|
+
error.code,
|
|
198
|
+
`${contextType} '${contextName}' references invalid transform: ${error.message}`,
|
|
199
|
+
'transform',
|
|
200
|
+
transformName
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Transform依存関係チェック(詳細版)
|
|
211
|
+
* Transform削除時に、そのTransformを参照しているBody/Source/Detectorがないかチェック
|
|
212
|
+
* @param {string} transformName - 削除対象のtransform名
|
|
213
|
+
* @param {Object} yamlData - YAMLデータ全体
|
|
214
|
+
* @param {Array} pendingChanges - 保留中の変更配列
|
|
215
|
+
*/
|
|
216
|
+
static checkTransformDependencies(transformName, yamlData, pendingChanges = []) {
|
|
217
|
+
const dependencies = [];
|
|
218
|
+
|
|
219
|
+
// 既存データの依存関係チェック
|
|
220
|
+
this._checkExistingDependencies(transformName, yamlData, dependencies);
|
|
221
|
+
|
|
222
|
+
// 保留中の変更の依存関係チェック
|
|
223
|
+
this._checkPendingDependencies(transformName, pendingChanges, dependencies);
|
|
224
|
+
|
|
225
|
+
if (dependencies.length > 0) {
|
|
226
|
+
throw PokerMcpError.dependencyExists(transformName, dependencies);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
dependencies: [],
|
|
231
|
+
canDelete: true,
|
|
232
|
+
checkedSections: ['body', 'source', 'detector', 'pending_changes']
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* 既存データの依存関係チェック
|
|
238
|
+
* @private
|
|
239
|
+
*/
|
|
240
|
+
static _checkExistingDependencies(transformName, yamlData, dependencies) {
|
|
241
|
+
// Body内のtransform参照をチェック
|
|
242
|
+
if (yamlData.body && Array.isArray(yamlData.body)) {
|
|
243
|
+
for (const body of yamlData.body) {
|
|
244
|
+
if (body.transform === transformName) {
|
|
245
|
+
dependencies.push({
|
|
246
|
+
type: 'body',
|
|
247
|
+
name: body.name,
|
|
248
|
+
context: `Body '${body.name}' uses transform '${transformName}'`
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Source内のtransform参照をチェック
|
|
255
|
+
if (yamlData.source && Array.isArray(yamlData.source)) {
|
|
256
|
+
for (const source of yamlData.source) {
|
|
257
|
+
// geometry内のtransform参照をチェック
|
|
258
|
+
if (source.geometry && source.geometry.transform === transformName) {
|
|
259
|
+
dependencies.push({
|
|
260
|
+
type: 'source',
|
|
261
|
+
name: source.name,
|
|
262
|
+
context: `Source '${source.name}' geometry uses transform '${transformName}'`
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Detector内のtransform参照をチェック
|
|
269
|
+
if (yamlData.detector && Array.isArray(yamlData.detector)) {
|
|
270
|
+
for (const detector of yamlData.detector) {
|
|
271
|
+
if (detector.transform === transformName) {
|
|
272
|
+
dependencies.push({
|
|
273
|
+
type: 'detector',
|
|
274
|
+
name: detector.name,
|
|
275
|
+
context: `Detector '${detector.name}' uses transform '${transformName}'`
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* 保留中の変更の依存関係チェック
|
|
284
|
+
* @private
|
|
285
|
+
*/
|
|
286
|
+
static _checkPendingDependencies(transformName, pendingChanges, dependencies) {
|
|
287
|
+
for (const change of pendingChanges) {
|
|
288
|
+
// Body関連の保留中変更
|
|
289
|
+
if (change.action === 'add_body' || change.action === 'updateBody') {
|
|
290
|
+
const bodyData = change.data.body || change.data;
|
|
291
|
+
if (bodyData && bodyData.transform === transformName) {
|
|
292
|
+
dependencies.push({
|
|
293
|
+
type: 'pending_body',
|
|
294
|
+
name: bodyData.name,
|
|
295
|
+
context: `Pending body '${bodyData.name}' will use transform '${transformName}'`
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Source関連の保留中変更
|
|
301
|
+
if (change.action === 'proposeSource' || change.action === 'updateSource') {
|
|
302
|
+
const sourceData = change.data;
|
|
303
|
+
if (sourceData && sourceData.geometry && sourceData.geometry.transform === transformName) {
|
|
304
|
+
dependencies.push({
|
|
305
|
+
type: 'pending_source',
|
|
306
|
+
name: sourceData.name,
|
|
307
|
+
context: `Pending source '${sourceData.name}' will use transform '${transformName}'`
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Detector関連の保留中変更
|
|
313
|
+
if (change.action === 'proposeDetector' || change.action === 'updateDetector') {
|
|
314
|
+
const detectorData = change.data;
|
|
315
|
+
if (detectorData && detectorData.transform === transformName) {
|
|
316
|
+
dependencies.push({
|
|
317
|
+
type: 'pending_detector',
|
|
318
|
+
name: detectorData.name,
|
|
319
|
+
context: `Pending detector '${detectorData.name}' will use transform '${transformName}'`
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Transform参照の完全性チェック
|
|
328
|
+
* システム全体で参照されているtransformが実際に存在するかチェック
|
|
329
|
+
* @param {Object} yamlData - YAMLデータ全体
|
|
330
|
+
*/
|
|
331
|
+
static validateTransformIntegrity(yamlData) {
|
|
332
|
+
const issues = [];
|
|
333
|
+
const availableTransforms = new Set();
|
|
334
|
+
|
|
335
|
+
// 利用可能なTransform名を収集
|
|
336
|
+
if (yamlData.transform && Array.isArray(yamlData.transform)) {
|
|
337
|
+
for (const transform of yamlData.transform) {
|
|
338
|
+
if (transform && transform.name) {
|
|
339
|
+
availableTransforms.add(transform.name);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Bodyのtransform参照をチェック
|
|
345
|
+
if (yamlData.body && Array.isArray(yamlData.body)) {
|
|
346
|
+
for (const body of yamlData.body) {
|
|
347
|
+
if (body.transform && !availableTransforms.has(body.transform)) {
|
|
348
|
+
issues.push({
|
|
349
|
+
type: 'missing_transform',
|
|
350
|
+
context: 'body',
|
|
351
|
+
name: body.name,
|
|
352
|
+
missingTransform: body.transform,
|
|
353
|
+
message: `Body '${body.name}' references non-existent transform '${body.transform}'`
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Sourceのtransform参照をチェック
|
|
360
|
+
if (yamlData.source && Array.isArray(yamlData.source)) {
|
|
361
|
+
for (const source of yamlData.source) {
|
|
362
|
+
if (source.geometry && source.geometry.transform && !availableTransforms.has(source.geometry.transform)) {
|
|
363
|
+
issues.push({
|
|
364
|
+
type: 'missing_transform',
|
|
365
|
+
context: 'source',
|
|
366
|
+
name: source.name,
|
|
367
|
+
missingTransform: source.geometry.transform,
|
|
368
|
+
message: `Source '${source.name}' references non-existent transform '${source.geometry.transform}'`
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Detectorのtransform参照をチェック
|
|
375
|
+
if (yamlData.detector && Array.isArray(yamlData.detector)) {
|
|
376
|
+
for (const detector of yamlData.detector) {
|
|
377
|
+
if (detector.transform && !availableTransforms.has(detector.transform)) {
|
|
378
|
+
issues.push({
|
|
379
|
+
type: 'missing_transform',
|
|
380
|
+
context: 'detector',
|
|
381
|
+
name: detector.name,
|
|
382
|
+
missingTransform: detector.transform,
|
|
383
|
+
message: `Detector '${detector.name}' references non-existent transform '${detector.transform}'`
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return {
|
|
390
|
+
isValid: issues.length === 0,
|
|
391
|
+
issues,
|
|
392
|
+
availableTransforms: Array.from(availableTransforms),
|
|
393
|
+
checkedReferences: {
|
|
394
|
+
body: yamlData.body?.length || 0,
|
|
395
|
+
source: yamlData.source?.length || 0,
|
|
396
|
+
detector: yamlData.detector?.length || 0
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Transform使用状況の統計情報を取得
|
|
403
|
+
* @param {Object} yamlData - YAMLデータ全体
|
|
404
|
+
*/
|
|
405
|
+
static getTransformUsageStats(yamlData) {
|
|
406
|
+
const stats = new Map();
|
|
407
|
+
const allTransforms = new Set();
|
|
408
|
+
|
|
409
|
+
// 定義されているTransformを登録
|
|
410
|
+
if (yamlData.transform && Array.isArray(yamlData.transform)) {
|
|
411
|
+
for (const transform of yamlData.transform) {
|
|
412
|
+
if (transform && transform.name) {
|
|
413
|
+
allTransforms.add(transform.name);
|
|
414
|
+
stats.set(transform.name, {
|
|
415
|
+
name: transform.name,
|
|
416
|
+
operationCount: transform.operations?.length || 0,
|
|
417
|
+
usedBy: {
|
|
418
|
+
body: [],
|
|
419
|
+
source: [],
|
|
420
|
+
detector: []
|
|
421
|
+
},
|
|
422
|
+
totalUsage: 0
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Bodyでの使用状況を記録
|
|
429
|
+
if (yamlData.body && Array.isArray(yamlData.body)) {
|
|
430
|
+
for (const body of yamlData.body) {
|
|
431
|
+
if (body.transform && stats.has(body.transform)) {
|
|
432
|
+
const stat = stats.get(body.transform);
|
|
433
|
+
stat.usedBy.body.push(body.name);
|
|
434
|
+
stat.totalUsage++;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Sourceでの使用状況を記録
|
|
440
|
+
if (yamlData.source && Array.isArray(yamlData.source)) {
|
|
441
|
+
for (const source of yamlData.source) {
|
|
442
|
+
if (source.geometry && source.geometry.transform && stats.has(source.geometry.transform)) {
|
|
443
|
+
const stat = stats.get(source.geometry.transform);
|
|
444
|
+
stat.usedBy.source.push(source.name);
|
|
445
|
+
stat.totalUsage++;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Detectorでの使用状況を記録
|
|
451
|
+
if (yamlData.detector && Array.isArray(yamlData.detector)) {
|
|
452
|
+
for (const detector of yamlData.detector) {
|
|
453
|
+
if (detector.transform && stats.has(detector.transform)) {
|
|
454
|
+
const stat = stats.get(detector.transform);
|
|
455
|
+
stat.usedBy.detector.push(detector.name);
|
|
456
|
+
stat.totalUsage++;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return {
|
|
462
|
+
totalTransforms: allTransforms.size,
|
|
463
|
+
usedTransforms: Array.from(stats.values()).filter(s => s.totalUsage > 0).length,
|
|
464
|
+
unusedTransforms: Array.from(stats.values()).filter(s => s.totalUsage === 0).map(s => s.name),
|
|
465
|
+
detailedStats: Array.from(stats.values()),
|
|
466
|
+
summary: {
|
|
467
|
+
mostUsed: Array.from(stats.values()).sort((a, b) => b.totalUsage - a.totalUsage)[0],
|
|
468
|
+
averageUsage: Array.from(stats.values()).reduce((sum, s) => sum + s.totalUsage, 0) / allTransforms.size || 0
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
}
|