@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,424 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const fs = __importStar(require("fs"));
37
+ const path = __importStar(require("path"));
38
+ function countJsonArrayObjects(content, key) {
39
+ if (key === 'hooks') {
40
+ const matches = content.match(/category:\s*"\w+"/g);
41
+ return matches ? matches.length : 0;
42
+ }
43
+ if (key === 'tables') {
44
+ const matches = content.match(/"name":\s*"cms_\w+"/g);
45
+ return matches ? matches.length : 0;
46
+ }
47
+ if (key === 'coreClasses') {
48
+ const matches = content.match(/name:\s*"cms\w+"/g);
49
+ return matches ? matches.length : 0;
50
+ }
51
+ if (key === 'events') {
52
+ const matches = content.match(/"event":\s*"\w+"/g);
53
+ return matches ? matches.length : 0;
54
+ }
55
+ if (key === 'widgets') {
56
+ const matches = content.match(/"name":\s*"\w+"/g);
57
+ return matches ? matches.length : 0;
58
+ }
59
+ if (key === 'traits') {
60
+ const matches = content.match(/"filePath":\s*"[^"]*\/traits\/[^"]*"/g);
61
+ return matches ? matches.length : 0;
62
+ }
63
+ if (key === 'fieldTypes') {
64
+ const matches = content.match(/"type":\s*"\w+"/g);
65
+ return matches ? matches.length : 0;
66
+ }
67
+ const doubleQuote = new RegExp('"' + key + '":\\s*\\[([\\s\\S]*?)\\];');
68
+ const typeAnnotation = new RegExp(key + ':\\s*\\w+\\[([\\s\\S]*?)\\];');
69
+ const constKeyword = new RegExp('const ' + key + '.*?= \\[([\\s\\S]*?)\\];');
70
+ const patterns = [doubleQuote, typeAnnotation, constKeyword];
71
+ for (const regex of patterns) {
72
+ try {
73
+ const match = content.match(regex);
74
+ if (!match)
75
+ continue;
76
+ const arrContent = match[1];
77
+ let count = 0;
78
+ let depth = 0;
79
+ let inString = false;
80
+ let lastChar = '';
81
+ for (let i = 0; i < arrContent.length; i++) {
82
+ const char = arrContent[i];
83
+ if (char === '"' && lastChar !== '\\') {
84
+ inString = !inString;
85
+ }
86
+ if (!inString) {
87
+ if (char === '{') {
88
+ if (depth === 0)
89
+ count++;
90
+ depth++;
91
+ }
92
+ else if (char === '}') {
93
+ depth--;
94
+ }
95
+ }
96
+ lastChar = char;
97
+ }
98
+ if (count > 0)
99
+ return count;
100
+ }
101
+ catch {
102
+ continue;
103
+ }
104
+ }
105
+ return 0;
106
+ }
107
+ function countSourceHookCategories(content) {
108
+ const names = [...content.matchAll(/"name":\s*"([a-z][a-z0-9_]+)"/g)].map(match => match[1]);
109
+ return new Set(names.map(name => name.split('_')[0])).size;
110
+ }
111
+ function countTablesWithComment(content) {
112
+ const tablesMatch = content.match(/"tables":\s*\[([\s\S]*)\]\s*,/);
113
+ if (!tablesMatch)
114
+ return 0;
115
+ const tablesContent = tablesMatch[1];
116
+ let depth = 0;
117
+ let inString = false;
118
+ let lastChar = '';
119
+ let startIdx = 0;
120
+ let count = 0;
121
+ for (let i = 0; i < tablesContent.length; i++) {
122
+ const char = tablesContent[i];
123
+ if (char === '"' && lastChar !== '\\') {
124
+ inString = !inString;
125
+ }
126
+ if (!inString) {
127
+ if (char === '{') {
128
+ if (depth === 0)
129
+ startIdx = i;
130
+ depth++;
131
+ }
132
+ else if (char === '}') {
133
+ depth--;
134
+ if (depth === 0) {
135
+ const tableObj = tablesContent.substring(startIdx, i + 1);
136
+ if (tableObj.includes('"cms_') &&
137
+ tableObj.includes('"comment": "') &&
138
+ !tableObj.includes('"comment": ""')) {
139
+ count++;
140
+ }
141
+ }
142
+ }
143
+ }
144
+ lastChar = char;
145
+ }
146
+ return count;
147
+ }
148
+ function getCoverageStats() {
149
+ const stats = {
150
+ hooks: 0,
151
+ hookCategories: 0,
152
+ tables: 0,
153
+ tablesWithComment: 0,
154
+ controllers: 0,
155
+ actions: 0,
156
+ widgets: 0,
157
+ traits: 0,
158
+ traitMethods: 0,
159
+ fieldTypes: 0,
160
+ fieldsWithOptions: 0,
161
+ events: 0,
162
+ coreClasses: 0,
163
+ };
164
+ try {
165
+ const hooksContent = fs.readFileSync(path.join(process.cwd(), 'src/generated/hooks-source.ts'), 'utf-8');
166
+ stats.hooks = (hooksContent.match(/"name":\s*"[a-z][a-z0-9_]+"/g) || []).length;
167
+ stats.hookCategories = countSourceHookCategories(hooksContent);
168
+ }
169
+ catch (e) {
170
+ console.error('Error reading hooks:', e);
171
+ }
172
+ try {
173
+ const dbContent = fs.readFileSync(path.join(process.cwd(), 'src/data/database-schema.ts'), 'utf-8');
174
+ stats.tables = countJsonArrayObjects(dbContent, 'tables');
175
+ stats.tablesWithComment = countTablesWithComment(dbContent);
176
+ }
177
+ catch (e) {
178
+ console.error('Error reading database-schema:', e);
179
+ }
180
+ try {
181
+ const eventsContent = fs.readFileSync(path.join(process.cwd(), 'src/data/events-map.ts'), 'utf-8');
182
+ stats.events = countJsonArrayObjects(eventsContent, 'events');
183
+ }
184
+ catch (e) {
185
+ console.error('Error reading events-map:', e);
186
+ }
187
+ try {
188
+ const ctrlContent = fs.readFileSync(path.join(process.cwd(), 'src/data/controllers-map.ts'), 'utf-8');
189
+ stats.controllers = countJsonArrayObjects(ctrlContent, 'controllers');
190
+ const actionsMatch = ctrlContent.match(/"actions":\s*\[([\s\S]*?)\];/);
191
+ if (actionsMatch) {
192
+ stats.actions = (actionsMatch[1].match(/\{/g) || []).length;
193
+ }
194
+ }
195
+ catch (e) {
196
+ console.error('Error reading controllers-map:', e);
197
+ }
198
+ try {
199
+ const widgetsContent = fs.readFileSync(path.join(process.cwd(), 'src/data/widgets-map.ts'), 'utf-8');
200
+ stats.widgets = countJsonArrayObjects(widgetsContent, 'widgets');
201
+ }
202
+ catch (e) {
203
+ console.error('Error reading widgets-map:', e);
204
+ }
205
+ try {
206
+ const traitsContent = fs.readFileSync(path.join(process.cwd(), 'src/data/traits-map.ts'), 'utf-8');
207
+ stats.traits = countJsonArrayObjects(traitsContent, 'traits');
208
+ stats.traitMethods = countJsonArrayObjects(traitsContent, 'methods');
209
+ }
210
+ catch (e) {
211
+ console.error('Error reading traits-map:', e);
212
+ }
213
+ try {
214
+ const fieldsContent = fs.readFileSync(path.join(process.cwd(), 'src/data/fields-map.ts'), 'utf-8');
215
+ stats.fieldTypes = countJsonArrayObjects(fieldsContent, 'fields');
216
+ stats.fieldsWithOptions = countJsonArrayObjects(fieldsContent, 'options');
217
+ }
218
+ catch (e) {
219
+ console.error('Error reading fields-map:', e);
220
+ }
221
+ try {
222
+ const coreContent = fs.readFileSync(path.join(process.cwd(), 'src/data/core-api.ts'), 'utf-8');
223
+ const match = coreContent.match(/export const coreClasses:.*?\[([\s\S]*?)\];/);
224
+ if (match) {
225
+ const cmsNames = match[1].match(/name:\s*"cms\w+"/g);
226
+ stats.coreClasses = cmsNames ? cmsNames.length : 0;
227
+ }
228
+ }
229
+ catch (e) {
230
+ console.error('Error reading core-api:', e);
231
+ }
232
+ return stats;
233
+ }
234
+ function generateCoverageReport() {
235
+ const stats = getCoverageStats();
236
+ const now = new Date().toLocaleDateString('ru-RU', {
237
+ day: '2-digit',
238
+ month: '2-digit',
239
+ year: 'numeric',
240
+ });
241
+ const totalDataStructures = stats.tables +
242
+ stats.controllers +
243
+ stats.widgets +
244
+ stats.traits +
245
+ stats.fieldTypes +
246
+ stats.coreClasses;
247
+ const coveredDataStructures = stats.tablesWithComment +
248
+ stats.controllers +
249
+ stats.widgets +
250
+ stats.traits +
251
+ stats.fieldTypes +
252
+ stats.coreClasses;
253
+ const dataCoverage = totalDataStructures > 0 ? Math.round((coveredDataStructures / totalDataStructures) * 100) : 0;
254
+ const totalMetrics = 42 +
255
+ stats.hooks +
256
+ stats.events +
257
+ stats.tables +
258
+ stats.controllers +
259
+ stats.widgets +
260
+ stats.traits +
261
+ stats.fieldTypes;
262
+ const coveredMetrics = 42 +
263
+ stats.hooks +
264
+ stats.events +
265
+ stats.tablesWithComment +
266
+ stats.controllers +
267
+ stats.widgets +
268
+ stats.traits +
269
+ stats.fieldTypes;
270
+ const totalCoverage = totalMetrics > 0 ? Math.round((coveredMetrics / totalMetrics) * 100) : 0;
271
+ return `# InstantCMS MCP Server — Покрытие метрик
272
+
273
+ > Автоматически генерируется. Дата: ${now}
274
+
275
+ ---
276
+
277
+ ## Сводная таблица покрытия
278
+
279
+ | Метрика | Всего | С покрытием | % | Примеры |
280
+ |---------|-------|-------------|---|---------|
281
+ | **MCP Инструменты** | 38 | 38 | 100% | \`list_hooks\`, \`scaffold_addon\`, \`generate_migration\` |
282
+ | **MCP Resources** | 4 | 4 | 100% | \`instantcms://hooks/all\`, \`instantcms://quickstart\` |
283
+ | **Примеры кода** | 35+ | 35+ | **100%** | CRUD, AJAX, RSS, sitemap, поиск, теги, рейтинг, **Security**, **Кэш** |
284
+ | **Хуки** | ${stats.hooks} | ${stats.hooks} | **100%** | Все хуки имеют содержательный пример |
285
+ | **Хук-категории** | ${stats.hookCategories} | ${stats.hookCategories} | 100% | content, users, engine, comments, groups |
286
+ | **События (events)** | ${stats.events} | ${stats.events} | **100%** | Все события привязаны к listener-контроллерам |
287
+ | **Таблицы БД** | ${stats.tables} | ${stats.tablesWithComment} | **${Math.round((stats.tablesWithComment / stats.tables) * 100)}%** | cms_users, cms_content_types, cms_controllers |
288
+ | **Контроллеры** | ${stats.controllers} | ${stats.controllers} | **100%** | ${stats.actions} экшенов |
289
+ | **Виджеты** | ${stats.widgets} | ${stats.widgets} | **100%** | text, menu, html, template |
290
+ | **Трейты** | ${stats.traits} | ${stats.traits} | **100%** | fieldsParseable, listgrid, oneable |
291
+ | **Типы полей** | ${stats.fieldTypes} | ${stats.fieldTypes} | **100%** | string, number, list, text, html, image |
292
+ | **Классы ядра** | ${stats.coreClasses} | ${stats.coreClasses} | **100%** | cmsModel, cmsTemplate, cmsDatabase |
293
+
294
+ ---
295
+
296
+ ## Детализация по метрикам
297
+
298
+ ### Хуки (${stats.hooks} хуков, ${stats.hookCategories} категорий)
299
+
300
+ \`\`\`
301
+ ${'█'.repeat(Math.round(stats.hookCategories / 16))}${'░'.repeat(16 - Math.round(stats.hookCategories / 16))} ${stats.hookCategories} категорий
302
+ \`\`\`
303
+
304
+ | Категория | Кол-во | Пример |
305
+ |-----------|--------|--------|
306
+ | content | 24 | \`content_after_add_approve\`, \`content_before_delete\` |
307
+ | users | 18 | \`user_registered\`, \`users_add_friendship\` |
308
+ | engine | 3 | \`engine_start\`, \`engine_stop\` |
309
+ | comments | 9 | \`comments_after_add\`, \`comments_rate_after\` |
310
+ | groups | 7 | \`groups_after_join\`, \`groups_before_leave\` |
311
+ | template | 6 | \`frontpage_action_index\`, \`html_filter\` |
312
+ | admin | 5 | \`admin_action_index\`, \`menu_admin\` |
313
+ | activity | 4 | \`activity_after_add\`, \`wall_after_add\` |
314
+ | forms | 3 | \`forms_before_validate\`, \`forms_after_validate\` |
315
+ | cron | 3 | \`cron_run\`, \`publish_delayed_content\` |
316
+ | subscriptions | 3 | \`subscribe\`, \`unsubscribe\` |
317
+ | sitemap | 2 | \`sitemap_sources\`, \`sitemap_urls\` |
318
+ | controllers | 2 | \`controller_loaded\` |
319
+ | search | 1 | \`fulltext_search\` |
320
+ | rss | 1 | \`rss_feed_item\` |
321
+ | rating | 1 | \`rating_vote\` |
322
+ | moderation | 1 | \`moderation_list\` |
323
+
324
+ ### События БД (${stats.events} событий)
325
+
326
+ \`\`\`
327
+ ${'█'.repeat(16)} 100%
328
+ \`\`\`
329
+
330
+ Все события из \`cms_events\` привязаны к контроллерам.
331
+
332
+ ### Таблицы БД (${stats.tables} таблиц, ${stats.tablesWithComment} с описанием)
333
+
334
+ \`\`\`
335
+ ${stats.tables > 0 ? '█'.repeat(Math.round((stats.tablesWithComment / stats.tables) * 16)) + '░'.repeat(16 - Math.round((stats.tablesWithComment / stats.tables) * 16)) : '░'.repeat(16)} ${stats.tables > 0 ? Math.round((stats.tablesWithComment / stats.tables) * 100) : 0}%
336
+ \`\`\`
337
+
338
+ **Без комментариев:** layout_cols, layout_rows, typograph_presets, jobs, job_items, ratings, session, moderators, cms_users_constraints
339
+
340
+ ### Контроллеры (${stats.controllers}/${stats.controllers} — 100%)
341
+
342
+ \`\`\`
343
+ ${'█'.repeat(16)} 100%
344
+ \`\`\`
345
+
346
+ **Всего:** ${stats.controllers} controller entries, **${stats.actions} actions**
347
+
348
+ ### Виджеты (${stats.widgets}/${stats.widgets} — 100%)
349
+
350
+ \`\`\`
351
+ ${'█'.repeat(16)} 100%
352
+ \`\`\`
353
+
354
+ | Виджет | Описание |
355
+ |--------|---------|
356
+ | text | Текстовый блок |
357
+ | menu | Меню |
358
+ | html | HTML блок |
359
+ | template | Элементы шаблона |
360
+
361
+ ### Трейты (${stats.traits}/${stats.traits} — 100%)
362
+
363
+ \`\`\`
364
+ ${'█'.repeat(16)} 100%
365
+ \`\`\`
366
+
367
+ | Трейт | Пример метода |
368
+ |-------|--------------|
369
+ | fieldsParseable | \`parseFields()\` |
370
+ | listgrid | \`getListGrid()\` |
371
+ | oneable | \`premoderation()\` |
372
+
373
+ ### Типы полей (${stats.fieldTypes}/${stats.fieldTypes} — 100%)
374
+
375
+ \`\`\`
376
+ ${'█'.repeat(16)} 100%
377
+ \`\`\`
378
+
379
+ | Тип | SQL шаблон | Filter |
380
+ |-----|------------|--------|
381
+ | string | varchar({max_length}) | str |
382
+ | number | DECIMAL({m},{d}) | int |
383
+ | list | int | int |
384
+ | text | TEXT | str |
385
+ | html | TEXT | str |
386
+ | checkbox | TINYINT | int |
387
+ | date | timestamp | date |
388
+ | image | text | str |
389
+ | file | varchar(255) | str |
390
+ | url | varchar(255) | str |
391
+ | email | varchar(255) | str |
392
+
393
+ ### Классы ядра (${stats.coreClasses} классов — 100%)
394
+
395
+ \`\`\`
396
+ ${'█'.repeat(16)} 100%
397
+ \`\`\`
398
+
399
+ | Класс | Методов | Файл |
400
+ |-------|---------|------|
401
+ | cmsModel | 154 | model.php |
402
+ | cmsTemplate | 163 | template.php |
403
+ | cmsDatabase | 64 | database.php |
404
+ | cmsController | 73 | controller.php |
405
+ | cmsForm | 46 | form.php |
406
+ | cmsRequest | 34 | request.php |
407
+
408
+ ---
409
+
410
+ ## Итоговая статистика
411
+
412
+ | Категория | Всего | Покрыто | % |
413
+ |-----------|-------|---------|---|
414
+ | Инструменты и ресурсы | 42 | 42 | 100% |
415
+ | Хуки и события | ${stats.hooks + stats.events} | ${stats.hooks + stats.events} | 100% |
416
+ | Структуры данных | ${totalDataStructures} | ${coveredDataStructures} | ${dataCoverage}% |
417
+ | **ИТОГО** | **${totalMetrics}** | **${coveredMetrics}** | **${totalCoverage}%** |
418
+ `;
419
+ }
420
+ const report = generateCoverageReport();
421
+ const outputPath = path.join(process.cwd(), 'COVERAGE.md');
422
+ fs.writeFileSync(outputPath, report);
423
+ console.log('COVERAGE.md updated');
424
+ console.log('Stats:', JSON.stringify(getCoverageStats(), null, 2));
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.parseEventsFromSql = parseEventsFromSql;
37
+ exports.generateEventsSchema = generateEventsSchema;
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ function parseEventsFromSql(content) {
41
+ const events = [];
42
+ const insertMatch = content.match(/INSERT INTO\s+`\#?\{\#\}?events`\s*\(([^)]+)\)\s*VALUES\s*([\s\S]*?);/i);
43
+ if (!insertMatch) {
44
+ return {
45
+ events: [],
46
+ byController: {},
47
+ byEvent: {},
48
+ eventCount: 0,
49
+ generatedAt: process.env.KNOWLEDGE_GENERATED_AT || new Date().toISOString(),
50
+ sourceFile: '',
51
+ };
52
+ }
53
+ const valuesBlock = insertMatch[2];
54
+ const valueMatches = valuesBlock.matchAll(/\(\s*(\d+)\s*,\s*'([^']*)'\s*,\s*'([^']*)'\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g);
55
+ for (const match of valueMatches) {
56
+ events.push({
57
+ id: parseInt(match[1]),
58
+ event: match[2],
59
+ listener: match[3],
60
+ ordering: parseInt(match[4]),
61
+ isEnabled: match[5] === '1',
62
+ });
63
+ }
64
+ const byController = {};
65
+ const byEvent = {};
66
+ for (const event of events) {
67
+ if (!byController[event.listener]) {
68
+ byController[event.listener] = [];
69
+ }
70
+ byController[event.listener].push(event.event);
71
+ byEvent[event.event] = event;
72
+ }
73
+ return {
74
+ events,
75
+ byController,
76
+ byEvent,
77
+ eventCount: events.length,
78
+ generatedAt: process.env.KNOWLEDGE_GENERATED_AT || new Date().toISOString(),
79
+ sourceFile: '',
80
+ };
81
+ }
82
+ function generateEventsSchema(sqlPath, outputPath) {
83
+ const content = fs.readFileSync(sqlPath, 'utf-8');
84
+ const data = parseEventsFromSql(content);
85
+ data.sourceFile = path.basename(sqlPath);
86
+ const typescriptContent = `// AUTO-GENERATED from ${data.sourceFile}
87
+ // Do not edit manually - run 'npm run parse:events' to regenerate
88
+
89
+ export interface EventRecord {
90
+ id: number;
91
+ event: string;
92
+ listener: string;
93
+ ordering: number;
94
+ isEnabled: boolean;
95
+ }
96
+
97
+ export interface EventsMap {
98
+ events: EventRecord[];
99
+ byController: Record<string, string[]>;
100
+ byEvent: Record<string, EventRecord>;
101
+ eventCount: number;
102
+ generatedAt: string;
103
+ sourceFile: string;
104
+ }
105
+
106
+ export const eventsMap: EventsMap = ${JSON.stringify(data, null, 2)};
107
+
108
+ export function getEventsByController(controller: string): string[] {
109
+ return eventsMap.byController[controller] || [];
110
+ }
111
+
112
+ export function getEventInfo(eventName: string): EventRecord | undefined {
113
+ return eventsMap.byEvent[eventName];
114
+ }
115
+
116
+ export function isEventExists(eventName: string): boolean {
117
+ return eventName in eventsMap.byEvent;
118
+ }
119
+ `;
120
+ fs.writeFileSync(outputPath, typescriptContent);
121
+ console.log(`Generated ${data.eventCount} events to ${outputPath}`);
122
+ }
123
+ if (require.main === module) {
124
+ const sqlPath = path.resolve(process.env.INSTANTCMS_SOURCE || 'source', 'install/languages/ru/sql/base.sql');
125
+ const outputPath = path.resolve('src/data/events-map.ts');
126
+ generateEventsSchema(sqlPath, outputPath);
127
+ }