@maxisoft/instantcms-mcp 1.2.3

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 (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +308 -0
  3. package/dist/__tests__/artifact-tool.test.js +196 -0
  4. package/dist/__tests__/db-tool.test.js +152 -0
  5. package/dist/__tests__/define-tool.test.js +106 -0
  6. package/dist/__tests__/find-tool.test.js +185 -0
  7. package/dist/__tests__/get-component-api.test.js +144 -0
  8. package/dist/__tests__/hardening.test.js +73 -0
  9. package/dist/__tests__/hooks-tool.test.js +173 -0
  10. package/dist/__tests__/mcp-integration-extra.test.js +166 -0
  11. package/dist/__tests__/mcp-integration.test.js +53 -0
  12. package/dist/__tests__/pagination.test.js +114 -0
  13. package/dist/__tests__/performance-baseline.test.js +56 -0
  14. package/dist/__tests__/project-patch.test.js +144 -0
  15. package/dist/__tests__/project-source.test.js +44 -0
  16. package/dist/__tests__/project-workflows-extra.test.js +165 -0
  17. package/dist/__tests__/project-workflows.test.js +51 -0
  18. package/dist/__tests__/scaffold-addon-roundtrip.test.js +287 -0
  19. package/dist/__tests__/serialization.test.js +170 -0
  20. package/dist/__tests__/source-knowledge-parsers.test.js +42 -0
  21. package/dist/__tests__/template-development.test.js +78 -0
  22. package/dist/__tests__/template-productivity.test.js +87 -0
  23. package/dist/__tests__/tools.test.js +2143 -0
  24. package/dist/data/components.js +2818 -0
  25. package/dist/data/controllers-map.js +7571 -0
  26. package/dist/data/core-api.js +9356 -0
  27. package/dist/data/database-schema.js +4555 -0
  28. package/dist/data/events-map.js +1339 -0
  29. package/dist/data/fields-map.js +3692 -0
  30. package/dist/data/hooks.js +2257 -0
  31. package/dist/data/js-api.js +123 -0
  32. package/dist/data/libs-api.js +264 -0
  33. package/dist/data/routes-map.js +203 -0
  34. package/dist/data/schemas-validation.js +255 -0
  35. package/dist/data/schemas.js +1404 -0
  36. package/dist/data/traits-map.js +1004 -0
  37. package/dist/data/version-profiles.js +41 -0
  38. package/dist/data/widgets-map.js +131 -0
  39. package/dist/data/wysiwyg-map.js +486 -0
  40. package/dist/generated/components-source.js +14748 -0
  41. package/dist/generated/hooks-source.js +3399 -0
  42. package/dist/generated/knowledge-meta.js +101 -0
  43. package/dist/index.js +13 -0
  44. package/dist/registry/database-tools.js +112 -0
  45. package/dist/registry/extension-tools.js +473 -0
  46. package/dist/registry/generator-tools.js +506 -0
  47. package/dist/registry/knowledge-tools.js +441 -0
  48. package/dist/registry/language-tools.js +99 -0
  49. package/dist/registry/meta-tools.js +152 -0
  50. package/dist/registry/project-tools.js +48 -0
  51. package/dist/registry/resources.js +98 -0
  52. package/dist/registry/source-tools.js +211 -0
  53. package/dist/registry/template-development-tools.js +79 -0
  54. package/dist/server.js +32 -0
  55. package/dist/tools/addon-tool.js +1437 -0
  56. package/dist/tools/admin-partial-tool.js +498 -0
  57. package/dist/tools/api-tool.js +294 -0
  58. package/dist/tools/artifact-tool.js +95 -0
  59. package/dist/tools/cache-tool.js +360 -0
  60. package/dist/tools/component-tool.js +415 -0
  61. package/dist/tools/controllers-tool.js +125 -0
  62. package/dist/tools/cron-tool.js +197 -0
  63. package/dist/tools/crud-tool.js +659 -0
  64. package/dist/tools/db-tool.js +198 -0
  65. package/dist/tools/email-tool.js +222 -0
  66. package/dist/tools/external-api-tool.js +596 -0
  67. package/dist/tools/filter-tool.js +341 -0
  68. package/dist/tools/form-tool.js +275 -0
  69. package/dist/tools/grid-tool.js +189 -0
  70. package/dist/tools/hooks-tool.js +104 -0
  71. package/dist/tools/import-export-tool.js +548 -0
  72. package/dist/tools/lang-tool.js +251 -0
  73. package/dist/tools/layout-override-tool.js +125 -0
  74. package/dist/tools/layout-tool.js +548 -0
  75. package/dist/tools/maria-tool.js +127 -0
  76. package/dist/tools/mariadb.js +201 -0
  77. package/dist/tools/migration-tool.js +329 -0
  78. package/dist/tools/oauth-tool.js +520 -0
  79. package/dist/tools/parser/components-parser.js +76 -0
  80. package/dist/tools/parser/controllers-parser.js +313 -0
  81. package/dist/tools/parser/core-parser.js +294 -0
  82. package/dist/tools/parser/coverage-generator.js +424 -0
  83. package/dist/tools/parser/events-parser.js +127 -0
  84. package/dist/tools/parser/fields-parser.js +382 -0
  85. package/dist/tools/parser/hooks-parser.js +106 -0
  86. package/dist/tools/parser/sql-parser.js +197 -0
  87. package/dist/tools/parser/traits-parser.js +161 -0
  88. package/dist/tools/parser/widgets-parser.js +150 -0
  89. package/dist/tools/permission-tool.js +348 -0
  90. package/dist/tools/project-patch-tool.js +73 -0
  91. package/dist/tools/project-source-tool.js +188 -0
  92. package/dist/tools/project-workflow-tool.js +186 -0
  93. package/dist/tools/requirement-tool.js +212 -0
  94. package/dist/tools/scaffold-tool.js +815 -0
  95. package/dist/tools/seo-tool.js +412 -0
  96. package/dist/tools/source-tool.js +155 -0
  97. package/dist/tools/template-development-tool.js +271 -0
  98. package/dist/tools/template-overrides-tool.js +294 -0
  99. package/dist/tools/template-productivity-tool.js +319 -0
  100. package/dist/tools/template-tool.js +665 -0
  101. package/dist/tools/test-tool.js +183 -0
  102. package/dist/tools/webhook-tool.js +427 -0
  103. package/dist/tools/widget-tool.js +331 -0
  104. package/dist/tools/wysiwyg-tool.js +137 -0
  105. package/dist/types/scaffold.js +68 -0
  106. package/dist/utils/define-tool.js +37 -0
  107. package/dist/utils/find-tool.js +97 -0
  108. package/dist/utils/mcp-result.js +18 -0
  109. package/dist/utils/pagination.js +27 -0
  110. package/dist/utils/serialization.js +27 -0
  111. package/package.json +94 -0
@@ -0,0 +1,415 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Component scaffolding tool for InstantCMS
4
+ * Generates complete components with backend, frontend, model, and routes
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.componentToolSchema = void 0;
8
+ exports.scaffoldComponent = scaffoldComponent;
9
+ const zod_1 = require("zod");
10
+ const scaffold_1 = require("../types/scaffold");
11
+ /**
12
+ * Generates component manifest
13
+ */
14
+ function generateManifest(name, Name, controllers, options) {
15
+ return `<?php
16
+ // InstantCMS 2. manifest.json
17
+
18
+ return [
19
+ 'type' => 'component',
20
+ 'name' => '${name}',
21
+ 'title' => '${Name}',
22
+ 'description' => '${Name} component',
23
+ 'icon' => 'component_${name}',
24
+ 'version' => '1.0.0',
25
+ 'category' => 'content',
26
+ 'files' => [
27
+ 'backend.php',
28
+ 'frontend.php',
29
+ 'model.php',
30
+ 'routes.php',
31
+ ],
32
+ 'controllers' => ['${controllers.map(c => c.name).join("', '")}'],
33
+ 'options' => [
34
+ 'frontend' => ${options.with_frontend},
35
+ 'admin' => ${options.with_admin},
36
+ ],
37
+ 'dependencies' => [],
38
+ ];`;
39
+ }
40
+ /**
41
+ * Generates backend controller
42
+ */
43
+ function generateBackend(name, Name, _controllers, _options) {
44
+ return `<?php
45
+ // InstantCMS 2. ${name}/backend.php
46
+
47
+ class ${Name}Backend extends cmsBackend {
48
+
49
+ protected $useOptions = true;
50
+ protected $showTaxonomy = true;
51
+
52
+ public function __construct($request) {
53
+ parent::__construct($request);
54
+
55
+ cmsTemplate::getInstance()->addChainLinks(
56
+ 'Настройки',
57
+ $this->cms_config->href_to('controllers', '${name}')
58
+ );
59
+ }
60
+
61
+ public function getBackendMenu() {
62
+ return [
63
+ [
64
+ 'title' => '${Name}',
65
+ 'url' => $this->cms_config->href_to(''),
66
+ 'options' => ['icon' => 'component_${name}'],
67
+ ],
68
+ [
69
+ 'title' => 'Настройки',
70
+ 'url' => $this->cms_config->href_to('options'),
71
+ 'icon' => 'settings',
72
+ ],
73
+ ];
74
+ }
75
+
76
+ public function validateVariants($${name}_item) {
77
+ return true;
78
+ }
79
+
80
+ public function getContentDescription($item) {
81
+ return '';
82
+ }
83
+
84
+ public function actionToggleEnabled($${name}_item) {
85
+ return $this->redirectToAction('index');
86
+ }
87
+ }`;
88
+ }
89
+ /**
90
+ * Generates frontend controller
91
+ */
92
+ function generateFrontend(name, Name, controllers) {
93
+ const firstController = controllers[0]?.name || 'index';
94
+ return `<?php
95
+ // InstantCMS 2. ${name}/frontend.php
96
+
97
+ class ${Name}Frontend extends cmsFrontend {
98
+
99
+ protected $useOptions = false;
100
+
101
+ public function __construct($request) {
102
+ parent::__construct($request);
103
+
104
+ $this->setContext('${Name}', '${firstController}');
105
+ }
106
+
107
+ public function routes() {
108
+ return include __DIR__ . '/routes.php';
109
+ }
110
+
111
+ public function beforeWallPost($item, $user) {
112
+ return $item;
113
+ }
114
+
115
+ public function getSeoPatterns() {
116
+ return [
117
+ 'index' => [
118
+ 'title' => '{${name}_page}.title | {site.name}',
119
+ 'description' => '{${name}_page}.description',
120
+ 'keywords' => '{${name}_page}.keywords',
121
+ ],
122
+ ];
123
+ }
124
+
125
+ public function getTagCategory() {
126
+ return [
127
+ 'name' => '${name}',
128
+ 'title' => '${Name}',
129
+ ];
130
+ }
131
+ }`;
132
+ }
133
+ /**
134
+ * Generates model class
135
+ */
136
+ function generateModel(name, Name, _controllers) {
137
+ return `<?php
138
+ // InstantCMS 2. ${name}/model.php
139
+
140
+ class ${Name}Model extends cmsModel {
141
+
142
+ public $table = '${name}';
143
+ public $cat_table = '${name}_cats';
144
+
145
+ public function __construct() {
146
+ parent::__construct();
147
+
148
+ $this->useCategories = true;
149
+ $this->cats_table = $this->cat_table;
150
+ }
151
+
152
+ public function get${Name}($id, $field = 'id') {
153
+ return $this->getItem($this->table, function ($item) {
154
+ $item['user'] = cmsModel::getInstance('users')->getUser($item['user_id']);
155
+ return $item;
156
+ }, [$field => $id]);
157
+ }
158
+
159
+ public function get${Name}s($filters = [], $order = 'id DESC', $page = 1, $perpage = 20) {
160
+ $this->filterColumns($filters);
161
+
162
+ return $this->get($this->table, function ($item) {
163
+ $item['user'] = cmsModel::getInstance('users')->getUser($item['user_id']);
164
+ return $item;
165
+ }, $filters, $order, $page, $perpage);
166
+ }
167
+
168
+ public function getCategories() {
169
+ return $this->get($this->cat_table, function ($item) {
170
+ return $item;
171
+ }, [], 'ordering ASC');
172
+ }
173
+
174
+ public function add${Name}($data) {
175
+ $data['user_id'] = cmsUser::getInstance()->id;
176
+ $data['date_pub'] = date('Y-m-d H:i:s');
177
+
178
+ return $this->insert($this->table, $data);
179
+ }
180
+
181
+ public function update${Name}($id, $data) {
182
+ $data['updated_at'] = date('Y-m-d H:i:s');
183
+
184
+ return $this->update($this->table, $id, $data);
185
+ }
186
+
187
+ public function delete${Name}($id) {
188
+ return $this->delete($this->table, $id, [
189
+ 'images' => ['path' => 'upload/${name}/', 'field' => 'image'],
190
+ ]);
191
+ }
192
+
193
+ public function countItems($category_id = null) {
194
+ $this->filterIsNull('is_deleted');
195
+
196
+ if ($category_id) {
197
+ $this->filterEqual('category_id', $category_id);
198
+ }
199
+
200
+ return $this->getCount($this->table);
201
+ }
202
+ }`;
203
+ }
204
+ /**
205
+ * Generates routes configuration
206
+ */
207
+ function generateRoutes(name, Name, controllers) {
208
+ const routes = controllers
209
+ .map(c => {
210
+ return ` '${c.name}' => [
211
+ '/' => ['${Name}Frontend', 'actionIndex', '{${c.name}_page}'],
212
+ '/{slug}' => ['${Name}Frontend', 'actionView', '{slug}'],
213
+ ],`;
214
+ })
215
+ .join('\n');
216
+ return `<?php
217
+ // InstantCMS 2. ${name}/routes.php
218
+
219
+ return [
220
+ ${routes}
221
+ ];`;
222
+ }
223
+ /**
224
+ * Generates backend index controller
225
+ */
226
+ function generateBackendIndex(name, Name) {
227
+ return `<?php
228
+ // InstantCMS 2. ${name}/backend/index.php
229
+
230
+ class ${Name}Backend extends cmsBackend {
231
+
232
+ public function actionIndex() {
233
+ $grid = $this->loadDataGrid('${name}');
234
+
235
+ $this->renderTemplate('index', [
236
+ 'grid' => $grid,
237
+ ]);
238
+ }
239
+
240
+ public function actionAdd() {
241
+ $form = $this->getForm('${name}');
242
+
243
+ if ($this->request->has('submit')) {
244
+ $data = $form->parse($this->request, ['submit']);
245
+
246
+ if ($form->validate($this, $data)) {
247
+ $id = $this->model->add${Name}($data);
248
+
249
+ cmsUser::addSessionMessage('Запись добавлена', 'success');
250
+ $this->redirectToAction('edit', $id);
251
+ }
252
+ }
253
+
254
+ $this->renderTemplate('form', [
255
+ 'form' => $form,
256
+ ]);
257
+ }
258
+
259
+ public function actionEdit($id) {
260
+ $item = $this->model->get${Name}($id);
261
+
262
+ if (!$item) {
263
+ cmsCore::error404();
264
+ }
265
+
266
+ $form = $this->getForm('${name}');
267
+
268
+ if ($this->request->has('submit')) {
269
+ $data = $form->parse($this->request, ['submit']);
270
+
271
+ if ($form->validate($this, $data)) {
272
+ $this->model->update${Name}($id, $data);
273
+
274
+ cmsUser::addSessionMessage('Запись сохранена', 'success');
275
+ $this->redirectToAction('edit', $id);
276
+ }
277
+ }
278
+
279
+ $this->renderTemplate('form', [
280
+ 'item' => $item,
281
+ 'form' => $form,
282
+ ]);
283
+ }
284
+
285
+ public function actionDelete($id) {
286
+ $this->model->delete${Name}($id);
287
+
288
+ cmsUser::addSessionMessage('Запись удалена', 'success');
289
+ $this->redirectToAction('index');
290
+ }
291
+ }`;
292
+ }
293
+ /**
294
+ * Generates menu configuration
295
+ */
296
+ function generateMenu(name, Name, controllers) {
297
+ const firstController = controllers[0]?.name || name;
298
+ return `<?php
299
+ // InstantCMS 2. ${name}/menu.php
300
+
301
+ return [
302
+ 'url' => href_to('${firstController}'),
303
+ 'title' => '${Name}',
304
+ 'icon' => 'component_${name}',
305
+ 'visibility' => ['0' => 'all', '1' => 'auth', '2' => 'nonauth'],
306
+ ];`;
307
+ }
308
+ /**
309
+ * Generates controller configuration
310
+ */
311
+ function generateConfig(name, _Name, controllers) {
312
+ return `<?php
313
+ // InstantCMS 2. system/config/controllers/${name}.php
314
+
315
+ return [
316
+ 'enabled' => true,
317
+ 'controllers' => ['${controllers.map(c => c.name).join("', '")}'],
318
+ 'options' => [
319
+ 'is_premod' => false,
320
+ 'is_tags' => true,
321
+ 'is_rs' => true,
322
+ 'hits_to_top' => true,
323
+ 'profile_on' => true,
324
+ 'pub_days' => 0,
325
+ 'front_edit' => false,
326
+ ],
327
+ ];`;
328
+ }
329
+ /**
330
+ * Generates a complete component for InstantCMS
331
+ *
332
+ * @param opts - Configuration options for the component
333
+ * @returns Object containing generated files and metadata
334
+ *
335
+ * @example
336
+ * ```typescript
337
+ * const result = scaffoldComponent({
338
+ * addon_name: 'board',
339
+ * controllers: [{ name: 'index', actions: ['index', 'view', 'add', 'edit'], use_model: true }],
340
+ * options: { with_frontend: true, with_model: true }
341
+ * });
342
+ * ```
343
+ */
344
+ function scaffoldComponent(opts) {
345
+ const { lowercase, UpperCamelCase } = (0, scaffold_1.normalizeAddonName)(opts.addon_name);
346
+ const files = {};
347
+ const options = {
348
+ with_frontend: opts.options?.with_frontend ?? true,
349
+ with_admin: opts.options?.with_admin ?? true,
350
+ with_model: opts.options?.with_model ?? true,
351
+ with_routes: opts.options?.with_routes ?? true,
352
+ with_menu: opts.options?.with_menu ?? true,
353
+ };
354
+ const controllers = opts.controllers || [
355
+ { name: 'index', actions: ['index', 'view'], use_model: true },
356
+ ];
357
+ files[`${lowercase}/manifest.json`] = generateManifest(lowercase, UpperCamelCase, controllers, options);
358
+ files[`${lowercase}/backend.php`] = generateBackend(lowercase, UpperCamelCase, controllers, options);
359
+ if (options.with_frontend) {
360
+ files[`${lowercase}/frontend.php`] = generateFrontend(lowercase, UpperCamelCase, controllers);
361
+ }
362
+ if (options.with_model || controllers.some(c => c.use_model)) {
363
+ files[`${lowercase}/model.php`] = generateModel(lowercase, UpperCamelCase, controllers);
364
+ }
365
+ if (options.with_routes) {
366
+ files[`${lowercase}/routes.php`] = generateRoutes(lowercase, UpperCamelCase, controllers);
367
+ }
368
+ files[`${lowercase}/backend/index.php`] = generateBackendIndex(lowercase, UpperCamelCase);
369
+ if (options.with_menu) {
370
+ files[`${lowercase}/menu.php`] = generateMenu(lowercase, UpperCamelCase, controllers);
371
+ }
372
+ files[`system/config/controllers/${lowercase}.php`] = generateConfig(lowercase, UpperCamelCase, controllers);
373
+ return {
374
+ addon_name: lowercase,
375
+ files,
376
+ controllers_count: controllers.length,
377
+ options,
378
+ };
379
+ }
380
+ exports.componentToolSchema = {
381
+ name: 'scaffold_component',
382
+ description: 'Генерация полного компонента InstantCMS с backend, frontend, model',
383
+ inputSchema: {
384
+ type: 'object',
385
+ properties: {
386
+ addon_name: { type: 'string', description: 'Имя компонента' },
387
+ controllers: zod_1.z
388
+ .array(zod_1.z.object({
389
+ name: zod_1.z.string().describe('Имя контроллера'),
390
+ actions: zod_1.z.array(zod_1.z.string()).describe('Экшены'),
391
+ use_model: zod_1.z.boolean().optional().describe('Использовать модель'),
392
+ }))
393
+ .optional()
394
+ .describe('Контроллеры'),
395
+ options: zod_1.z
396
+ .object({
397
+ with_frontend: zod_1.z.boolean().optional().describe('С frontend'),
398
+ with_admin: zod_1.z.boolean().optional().describe('С админкой'),
399
+ with_model: zod_1.z.boolean().optional().describe('С моделью'),
400
+ with_routes: zod_1.z.boolean().optional().describe('С роутами'),
401
+ with_menu: zod_1.z.boolean().optional().describe('С меню'),
402
+ })
403
+ .optional()
404
+ .describe('Опции'),
405
+ },
406
+ required: ['addon_name'],
407
+ },
408
+ inputExamples: [
409
+ {
410
+ addon_name: 'board',
411
+ controllers: [{ name: 'index', actions: ['index', 'view', 'add', 'edit'], use_model: true }],
412
+ options: { with_frontend: true, with_model: true },
413
+ },
414
+ ],
415
+ };
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.analyzeController = analyzeController;
4
+ exports.listControllers = listControllers;
5
+ exports.getControllerActionsList = getControllerActionsList;
6
+ exports.listSystemTraits = listSystemTraits;
7
+ const controllers_map_js_1 = require("../data/controllers-map.js");
8
+ function analyzeController(name, type) {
9
+ const controller = (0, controllers_map_js_1.getController)(name, type);
10
+ if (!controller) {
11
+ const suggestions = controllers_map_js_1.controllersMap.controllers
12
+ .filter(c => c.name.includes(name.toLowerCase()))
13
+ .slice(0, 5)
14
+ .map(c => ({ name: c.name, type: c.type }));
15
+ return {
16
+ error: `Контроллер "${name}" не найден`,
17
+ suggestions,
18
+ };
19
+ }
20
+ const traits = new Set();
21
+ for (const action of controller.actions) {
22
+ for (const trait of action.traits) {
23
+ traits.add(trait);
24
+ }
25
+ }
26
+ return {
27
+ name: controller.name,
28
+ className: controller.className,
29
+ type: controller.type,
30
+ extends: controller.extends,
31
+ filePath: controller.filePath,
32
+ actions: {
33
+ count: controller.actions.length,
34
+ list: controller.actions.map(a => ({
35
+ name: a.name,
36
+ className: a.className,
37
+ filePath: a.filePath,
38
+ hasParams: a.hasParams,
39
+ params: a.params,
40
+ visibility: a.visibility,
41
+ })),
42
+ },
43
+ traits: Array.from(traits),
44
+ hasBackendFolder: controller.hasBackendFolder,
45
+ hasModel: controller.hasModel,
46
+ modelFile: controller.modelFile,
47
+ };
48
+ }
49
+ function listControllers(filter) {
50
+ let controllers = controllers_map_js_1.controllersMap.controllers;
51
+ if (filter === 'frontend') {
52
+ controllers = controllers.filter(c => c.type === 'frontend');
53
+ }
54
+ else if (filter === 'backend') {
55
+ controllers = controllers.filter(c => c.type === 'backend');
56
+ }
57
+ const byType = {
58
+ frontend: [],
59
+ backend: [],
60
+ };
61
+ for (const ctrl of controllers) {
62
+ byType[ctrl.type].push({
63
+ name: ctrl.name,
64
+ className: ctrl.className,
65
+ actionsCount: ctrl.actions.length,
66
+ hasBackend: ctrl.hasBackendFolder,
67
+ hasModel: ctrl.hasModel,
68
+ });
69
+ }
70
+ return {
71
+ total: controllers.length,
72
+ byType,
73
+ allControllers: controllers.map(c => ({
74
+ name: c.name,
75
+ type: c.type,
76
+ extends: c.extends,
77
+ actionsCount: c.actions.length,
78
+ hasBackendFolder: c.hasBackendFolder,
79
+ hasModel: c.hasModel,
80
+ })),
81
+ };
82
+ }
83
+ function getControllerActionsList(name, type) {
84
+ const controller = (0, controllers_map_js_1.getController)(name, type);
85
+ if (!controller) {
86
+ return { error: `Контроллер "${name}" не найден` };
87
+ }
88
+ return {
89
+ controller: controller.name,
90
+ type: controller.type,
91
+ actions: controller.actions.map(a => ({
92
+ name: a.name,
93
+ className: a.className,
94
+ filePath: a.filePath,
95
+ visibility: a.visibility,
96
+ hasParams: a.hasParams,
97
+ params: a.params,
98
+ traits: a.traits,
99
+ })),
100
+ };
101
+ }
102
+ function listSystemTraits() {
103
+ const allTraits = {};
104
+ for (const controller of controllers_map_js_1.controllersMap.controllers) {
105
+ for (const action of controller.actions) {
106
+ for (const trait of action.traits) {
107
+ const parts = trait.split('\\');
108
+ const namespace = parts.slice(0, -1).join('\\');
109
+ const traitName = parts[parts.length - 1];
110
+ if (!allTraits[namespace]) {
111
+ allTraits[namespace] = new Set();
112
+ }
113
+ allTraits[namespace].add(traitName);
114
+ }
115
+ }
116
+ }
117
+ const result = {};
118
+ for (const [ns, traits] of Object.entries(allTraits)) {
119
+ result[ns] = Array.from(traits).sort();
120
+ }
121
+ return {
122
+ namespaces: Object.keys(result).length,
123
+ traits: result,
124
+ };
125
+ }