@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,198 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.introspectDatabase = introspectDatabase;
4
+ exports.listContentTypes = listContentTypes;
5
+ exports.listDatabaseEvents = listDatabaseEvents;
6
+ exports.describeTable = describeTable;
7
+ const database_schema_js_1 = require("../data/database-schema.js");
8
+ const events_map_js_1 = require("../data/events-map.js");
9
+ function introspectDatabase(tableName) {
10
+ if (tableName) {
11
+ const table = database_schema_js_1.databaseSchema.tables.find(t => t.name === tableName || t.name.endsWith(`_${tableName}`));
12
+ if (!table) {
13
+ const similar = database_schema_js_1.databaseSchema.tables.filter(t => t.name.includes(tableName) || (t.comment && t.comment.includes(tableName)));
14
+ return {
15
+ error: `Таблица "${tableName}" не найдена`,
16
+ suggestions: similar.slice(0, 5).map(t => ({
17
+ name: t.name,
18
+ comment: t.comment
19
+ }))
20
+ };
21
+ }
22
+ return {
23
+ table: table.name,
24
+ comment: table.comment,
25
+ fieldCount: table.fields.length,
26
+ indexCount: table.indexes.length,
27
+ fields: table.fields.map(f => ({
28
+ name: f.name,
29
+ type: f.type,
30
+ nullable: f.nullable,
31
+ key: f.key,
32
+ default: f.default,
33
+ comment: f.comment
34
+ })),
35
+ indexes: table.indexes.map(i => ({
36
+ name: i.name,
37
+ type: i.type,
38
+ fields: i.fields
39
+ })),
40
+ relationships: findRelationships(table.name)
41
+ };
42
+ }
43
+ return {
44
+ totalTables: database_schema_js_1.databaseSchema.tableCount,
45
+ tables: database_schema_js_1.databaseSchema.tables.map(t => ({
46
+ name: t.name,
47
+ comment: t.comment,
48
+ fieldCount: t.fields.length,
49
+ hasPrimaryKey: t.indexes.some(i => i.name === 'PRIMARY')
50
+ })),
51
+ summary: generateSummary()
52
+ };
53
+ }
54
+ function listContentTypes() {
55
+ const contentTable = database_schema_js_1.databaseSchema.tables.find(t => t.name === 'cms_content_types');
56
+ if (!contentTable) {
57
+ return { error: 'Таблица content_types не найдена' };
58
+ }
59
+ const usersTable = database_schema_js_1.databaseSchema.tables.find(t => t.name === 'cms_users');
60
+ return {
61
+ table: contentTable.name,
62
+ fields: contentTable.fields.map(f => ({
63
+ name: f.name,
64
+ type: f.type,
65
+ comment: f.comment
66
+ })),
67
+ keyFields: contentTable.fields.filter(f => f.key === 'PRI' || f.key === 'UNI'),
68
+ systemTables: {
69
+ users: usersTable ? {
70
+ name: usersTable.name,
71
+ comment: usersTable.comment,
72
+ keyFields: usersTable.fields.filter(f => f.key === 'PRI').map(f => f.name)
73
+ } : null
74
+ }
75
+ };
76
+ }
77
+ function listDatabaseEvents() {
78
+ return {
79
+ totalEvents: events_map_js_1.eventsMap.eventCount,
80
+ byController: Object.entries(events_map_js_1.eventsMap.byController).map(([controller, events]) => ({
81
+ controller,
82
+ eventCount: events.length,
83
+ events: events.slice(0, 10)
84
+ })),
85
+ allEvents: events_map_js_1.eventsMap.events.map(e => ({
86
+ event: e.event,
87
+ listener: e.listener,
88
+ isEnabled: e.isEnabled
89
+ }))
90
+ };
91
+ }
92
+ function describeTable(tableName) {
93
+ const table = database_schema_js_1.databaseSchema.tables.find(t => t.name === tableName || t.name === `cms_${tableName}` || t.name.endsWith(`_${tableName}`));
94
+ if (!table) {
95
+ return { error: `Таблица "${tableName}" не найдена` };
96
+ }
97
+ const fieldTypes = {};
98
+ for (const field of table.fields) {
99
+ const baseType = field.type.replace(/[()].*/, '');
100
+ fieldTypes[baseType] = (fieldTypes[baseType] || 0) + 1;
101
+ }
102
+ return {
103
+ table: table.name,
104
+ comment: table.comment,
105
+ statistics: {
106
+ totalFields: table.fields.length,
107
+ totalIndexes: table.indexes.length,
108
+ fieldTypes
109
+ },
110
+ fields: table.fields.map(f => ({
111
+ name: f.name,
112
+ type: f.type,
113
+ nullable: f.nullable,
114
+ key: f.key,
115
+ default: f.default,
116
+ comment: f.comment,
117
+ extra: f.extra
118
+ })),
119
+ indexes: table.indexes.map(i => ({
120
+ name: i.name,
121
+ type: i.type,
122
+ fields: i.fields
123
+ })),
124
+ relationships: findRelationships(table.name),
125
+ sampleQuery: generateSampleQuery(table)
126
+ };
127
+ }
128
+ function findRelationships(tableName) {
129
+ const relationships = [];
130
+ const baseName = tableName.replace(/^cms_/, '');
131
+ for (const table of database_schema_js_1.databaseSchema.tables) {
132
+ for (const field of table.fields) {
133
+ if (field.name === baseName || field.name === `${baseName}_id`) {
134
+ if (table.name !== tableName) {
135
+ relationships.push({
136
+ type: 'has_many',
137
+ target: table.name,
138
+ via: field.name
139
+ });
140
+ }
141
+ }
142
+ }
143
+ if (table.name.includes(baseName) && table.name !== tableName) {
144
+ for (const index of table.indexes) {
145
+ if (index.fields.some(f => f.includes('id') || f.includes(baseName))) {
146
+ relationships.push({
147
+ type: 'related',
148
+ target: table.name,
149
+ on: index.fields
150
+ });
151
+ }
152
+ }
153
+ }
154
+ }
155
+ return relationships.slice(0, 10);
156
+ }
157
+ function generateSummary() {
158
+ const categories = {
159
+ 'users': 0,
160
+ 'content': 0,
161
+ 'widgets': 0,
162
+ 'system': 0,
163
+ 'other': 0
164
+ };
165
+ for (const table of database_schema_js_1.databaseSchema.tables) {
166
+ if (table.name.startsWith('cms_users'))
167
+ categories.users++;
168
+ else if (table.name.startsWith('cms_con_') || table.name.includes('content'))
169
+ categories.content++;
170
+ else if (table.name.startsWith('cms_widgets'))
171
+ categories.widgets++;
172
+ else if (['cms_sessions', 'cms_jobs', 'cms_scheduler'].includes(table.name))
173
+ categories.system++;
174
+ else
175
+ categories.other++;
176
+ }
177
+ return categories;
178
+ }
179
+ function generateSampleQuery(table) {
180
+ const primaryKey = table.indexes.find(i => i.name === 'PRIMARY');
181
+ const pk = primaryKey?.fields[0] || 'id';
182
+ return [
183
+ `-- Получить запись по ID`,
184
+ `SELECT * FROM ${table.name} WHERE ${pk} = 1;`,
185
+ ``,
186
+ `-- Получить список с пагинацией`,
187
+ `SELECT * FROM ${table.name} ORDER BY ${pk} DESC LIMIT 10 OFFSET 0;`,
188
+ ``,
189
+ `-- Фильтрация по полю`,
190
+ table.fields.find(f => f.name === 'is_pub')
191
+ ? `SELECT * FROM ${table.name} WHERE is_pub = 1 ORDER BY date_pub DESC;`
192
+ : null,
193
+ `-- JOIN с пользователями`,
194
+ table.fields.some(f => f.name === 'user_id')
195
+ ? `SELECT t.*, u.nickname as user_nickname \nFROM ${table.name} t\nLEFT JOIN cms_users u ON u.id = t.user_id\nWHERE t.id = 1;`
196
+ : null
197
+ ].filter(Boolean).join('\n');
198
+ }
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.scaffoldEmail = scaffoldEmail;
4
+ function scaffoldEmail(opts) {
5
+ const name = opts.addon_name.toLowerCase().replace(/[^a-z0-9_]/g, '_');
6
+ const NAME = name.toUpperCase();
7
+ const files = {};
8
+ const baseDir = `package/system/languages/ru/controllers/${name}`;
9
+ for (const template of opts.templates) {
10
+ const templateFileName = `${name}_${template.name}.email.php`;
11
+ files[`${baseDir}/${templateFileName}`] = generateEmailTemplate(name, NAME, template, opts.options);
12
+ }
13
+ files[`${baseDir}/${name}_emails.php`] = generateEmailIndex(name, NAME, opts.templates);
14
+ return {
15
+ addon_name: name,
16
+ templates_count: opts.templates.length,
17
+ files,
18
+ templates: opts.templates.map(t => ({
19
+ name: t.name,
20
+ file: `${name}_${t.name}.email.php`,
21
+ subject: t.subject,
22
+ variables_count: t.variables?.length || 0,
23
+ })),
24
+ structure_notes: [
25
+ `Email шаблоны: ${baseDir}/`,
26
+ `Использование в коде: $this->controller->emailTask('${name}', $template, $data)`,
27
+ `Отправка: cmsEventsManager::hook('send_email', ['to' => $email, 'template' => '${name}:${opts.templates[0]?.name || 'default'}', 'vars' => $data])`,
28
+ ],
29
+ };
30
+ }
31
+ function generateEmailTemplate(name, NAME, template, options) {
32
+ const useHtml = options?.use_html ?? true;
33
+ const baseTemplate = options?.base_template || 'default';
34
+ const header = generateEmailHeader(template.subject, baseTemplate, useHtml);
35
+ const body = generateEmailBody(template.body, name, NAME, template.variables);
36
+ const footer = generateEmailFooter(baseTemplate, useHtml);
37
+ let code = `<?php
38
+ /**
39
+ * Email шаблон: ${template.name}
40
+ * Для отправки используйте метод emailTask() контроллера
41
+ */
42
+
43
+ `;
44
+ if (useHtml) {
45
+ code += `$${name}_template = <<<'HTML'
46
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
47
+ <html xmlns="http://www.w3.org/1999/xhtml">
48
+ <head>
49
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
50
+ <title>${escapeHtml(template.subject)}</title>
51
+ ${header}
52
+ </head>
53
+ <body>
54
+ ${body}
55
+ ${footer}
56
+ </body>
57
+ </html>
58
+ HTML;
59
+
60
+ `;
61
+ }
62
+ else {
63
+ code += `$${name}_template = <<<'TEXT'
64
+ ${template.body}
65
+ TEXT;
66
+
67
+ `;
68
+ }
69
+ code += `
70
+ $${name}_subject = '${escapeHtml(template.subject)}';
71
+ `;
72
+ if (template.variables && template.variables.length > 0) {
73
+ code += `
74
+ // Доступные переменные:
75
+ `;
76
+ for (const v of template.variables) {
77
+ const example = v.example ? ` // Пример: ${v.example}` : '';
78
+ code += `// \${${v.name}}${example}
79
+ `;
80
+ }
81
+ }
82
+ return code;
83
+ }
84
+ function generateEmailHeader(subject, baseTemplate, useHtml) {
85
+ if (!useHtml) {
86
+ return '';
87
+ }
88
+ const baseStyles = `
89
+ body {
90
+ margin: 0;
91
+ padding: 0;
92
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
93
+ font-size: 14px;
94
+ line-height: 1.6;
95
+ color: #333;
96
+ }
97
+ .email-container {
98
+ max-width: 600px;
99
+ margin: 0 auto;
100
+ padding: 20px;
101
+ }
102
+ .email-header {
103
+ background: #4a90d9;
104
+ color: white;
105
+ padding: 20px;
106
+ text-align: center;
107
+ }
108
+ .email-body {
109
+ padding: 30px 20px;
110
+ background: #ffffff;
111
+ }
112
+ .email-footer {
113
+ padding: 20px;
114
+ text-align: center;
115
+ font-size: 12px;
116
+ color: #666;
117
+ background: #f5f5f5;
118
+ }
119
+ .button {
120
+ display: inline-block;
121
+ padding: 10px 20px;
122
+ background: #4a90d9;
123
+ color: white;
124
+ text-decoration: none;
125
+ border-radius: 4px;
126
+ }
127
+ `;
128
+ switch (baseTemplate) {
129
+ case 'minimal':
130
+ return `
131
+ <style type="text/css">
132
+ body { background: #fafafa; }
133
+ .email-container { background: #ffffff; padding: 30px; }
134
+ </style>
135
+ `;
136
+ case 'notifications':
137
+ return `
138
+ <style type="text/css">
139
+ ${baseStyles}
140
+ .email-header { background: #e74c3c; }
141
+ </style>
142
+ `;
143
+ default:
144
+ return `
145
+ <style type="text/css">
146
+ ${baseStyles}
147
+ </style>
148
+ `;
149
+ }
150
+ }
151
+ function generateEmailBody(body, name, NAME, variables) {
152
+ let processedBody = body;
153
+ if (variables) {
154
+ for (const v of variables) {
155
+ processedBody = processedBody.replace(new RegExp(`{${v.name}}`, 'g'), `\${\${${v.name}}}`);
156
+ }
157
+ }
158
+ processedBody = processedBody.replace(/\{site_name\}/g, '<?php echo \\$site_name; ?>');
159
+ processedBody = processedBody.replace(/\{site_url\}/g, '<?php echo \\$site_url; ?>');
160
+ processedBody = processedBody.replace(/\{user_name\}/g, '<?php echo \\$user_name; ?>');
161
+ processedBody = processedBody.replace(/\{user_email\}/g, '<?php echo \\$user_email; ?>');
162
+ const subjectPreview = escapeHtml(body.slice(0, 50));
163
+ return `
164
+ <div class="email-container">
165
+ <div class="email-header">
166
+ <h1><?php echo \\$subject ?: '${subjectPreview}'; ?></h1>
167
+ </div>
168
+ <div class="email-body">
169
+ ${processedBody
170
+ .split('\n')
171
+ .map(line => ` <p>${line}</p>`)
172
+ .join('\n')}
173
+ </div>
174
+ <div class="email-footer">
175
+ <p>С уважением, команда <?php echo \\$site_name; ?></p>
176
+ <p><a href="<?php echo \\$site_url; ?>"><?php echo \\$site_url; ?></a></p>
177
+ </div>
178
+ </div>
179
+ `;
180
+ }
181
+ function generateEmailFooter(baseTemplate, useHtml) {
182
+ if (!useHtml) {
183
+ return '';
184
+ }
185
+ switch (baseTemplate) {
186
+ case 'minimal':
187
+ return '';
188
+ default:
189
+ return '';
190
+ }
191
+ }
192
+ function generateEmailIndex(name, NAME, templates) {
193
+ let code = `<?php
194
+ /**
195
+ * Email шаблоны для ${name}
196
+ * Генерируется автоматически
197
+ */
198
+
199
+ `;
200
+ for (const template of templates) {
201
+ code += `/**
202
+ * ${template.name} - ${template.subject}
203
+ */
204
+ define('LANG_${NAME}_EMAIL_${template.name.toUpperCase()}_SUBJECT', '${escapeHtml(template.subject)}');
205
+ define('LANG_${NAME}_EMAIL_${template.name.toUpperCase()}_BODY', '${escapeHtml(template.body.slice(0, 100))}...');
206
+
207
+ `;
208
+ }
209
+ code += `
210
+ // Отправка email
211
+ // $this->controller->emailTask('${name}', '${templates[0]?.name || 'default'}', $data);
212
+ `;
213
+ return code;
214
+ }
215
+ function escapeHtml(text) {
216
+ return text
217
+ .replace(/&/g, '&amp;')
218
+ .replace(/</g, '&lt;')
219
+ .replace(/>/g, '&gt;')
220
+ .replace(/"/g, '&quot;')
221
+ .replace(/'/g, '&#039;');
222
+ }