@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,382 @@
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.generateFieldsMap = generateFieldsMap;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const TYPE_MAP = {
40
+ string: 'text',
41
+ number: 'number',
42
+ checkbox: 'boolean',
43
+ list: 'select',
44
+ listmultiple: 'multiselect',
45
+ textarea: 'text',
46
+ html: 'wysiwyg',
47
+ image: 'image',
48
+ file: 'file',
49
+ date: 'date',
50
+ datetime: 'datetime',
51
+ color: 'color',
52
+ hidden: 'hidden',
53
+ captcha: 'captcha',
54
+ user: 'user',
55
+ users: 'users',
56
+ category: 'category',
57
+ url: 'url',
58
+ email: 'email',
59
+ age: 'age',
60
+ child: 'child',
61
+ parent: 'parent',
62
+ toolbar: 'toolbar',
63
+ listbitmask: 'bitmask',
64
+ listgroups: 'groups',
65
+ fieldsgroup: 'fieldsgroup',
66
+ caption: 'caption',
67
+ forms: 'forms',
68
+ htmlcross: 'htmlcross',
69
+ htmlhint: 'htmlhint',
70
+ paypal: 'paypal',
71
+ };
72
+ function parseFieldFile(filePath) {
73
+ const content = fs.readFileSync(filePath, 'utf-8');
74
+ const nameMatch = filePath.match(/([^\/]+)\.php$/);
75
+ if (!nameMatch)
76
+ return null;
77
+ const fileName = nameMatch[1];
78
+ const classMatch = content.match(/class\s+(field\w+)\s+extends\s+(\w+)/);
79
+ if (!classMatch)
80
+ return null;
81
+ const className = classMatch[1];
82
+ const isSystem = content.includes('public $is_system = true') || content.includes('public \\$is_system = true');
83
+ const titleMatch = content.match(/public\s+\$title\s*=\s*LANG_(\w+)/);
84
+ const title = titleMatch ? titleMatch[1].replace(/_/g, ' ').toLowerCase() : undefined;
85
+ const sqlMatch = content.match(/public\s+\$sql\s*=\s*['"]([^'"]+)['"]/);
86
+ const sqlTemplate = sqlMatch ? sqlMatch[1] : undefined;
87
+ const filterTypeMatch = content.match(/public\s+\$filter_type\s*=\s*['"](\w+)['"]/);
88
+ const filterType = filterTypeMatch ? filterTypeMatch[1] : undefined;
89
+ const filterHintMatch = content.match(/public\s+\$filter_hint\s*=\s*LANG_(\w+)/);
90
+ const filterHint = filterHintMatch
91
+ ? filterHintMatch[1].replace(/_/g, ' ').toLowerCase()
92
+ : undefined;
93
+ const varTypeMatch = content.match(/public\s+\$var_type\s*=\s*['"](\w+)['"]/);
94
+ const varType = varTypeMatch ? varTypeMatch[1] : undefined;
95
+ const allowIndexMatch = content.match(/public\s+\$allow_index\s*=\s*(true|false)/);
96
+ const allowIndex = allowIndexMatch ? allowIndexMatch[1] === 'true' : undefined;
97
+ const nativeTagMatch = content.match(/public\s+\$native_tag\s*=\s*(true|false)/);
98
+ const nativeTag = nativeTagMatch ? nativeTagMatch[1] === 'true' : undefined;
99
+ const dynamicListMatch = content.match(/public\s+\$dynamic_list\s*=\s*(true|false)/);
100
+ const dynamicList = dynamicListMatch ? dynamicListMatch[1] === 'true' : undefined;
101
+ const validationRules = [];
102
+ const getRulesMatch = content.match(/public\s+function\s+getRules\s*\(\s*\)\s*\{([\s\S]*?)\n\s*\}/);
103
+ if (getRulesMatch) {
104
+ const rulesBlock = getRulesMatch[1];
105
+ const ruleMatches = rulesBlock.matchAll(/\[\s*['"](\w+)['"]/g);
106
+ for (const match of ruleMatches) {
107
+ validationRules.push(match[1]);
108
+ }
109
+ }
110
+ const methods = {};
111
+ if (content.match(/public\s+function\s+parse\s*\(/))
112
+ methods.parse = true;
113
+ if (content.match(/public\s+function\s+store\s*\(/))
114
+ methods.store = true;
115
+ if (content.match(/public\s+function\s+getInput\s*\(/))
116
+ methods.getInput = true;
117
+ if (content.match(/public\s+function\s+applyFilter\s*\(/))
118
+ methods.applyFilter = true;
119
+ if (content.match(/public\s+function\s+getStringValue\s*\(/))
120
+ methods.getStringValue = true;
121
+ if (content.match(/public\s+function\s+getFilterInput\s*\(/))
122
+ methods.getFilterInput = true;
123
+ const options = [];
124
+ function extractOptionsBlock(content) {
125
+ const returnArray = content.indexOf('return array(');
126
+ let returnBracket = -1;
127
+ let searchFrom = 0;
128
+ while (true) {
129
+ returnBracket = content.indexOf('return [', searchFrom);
130
+ if (returnBracket < 0)
131
+ break;
132
+ if (content.substring(returnBracket, returnBracket + 9) === 'return []') {
133
+ searchFrom = returnBracket + 8;
134
+ continue;
135
+ }
136
+ break;
137
+ }
138
+ if (returnBracket < 0 && returnArray < 0)
139
+ return null;
140
+ if (returnBracket >= 0 && (returnArray < 0 || returnBracket < returnArray)) {
141
+ const start = content.indexOf('[', returnBracket) + 1;
142
+ if (start === 0)
143
+ return null;
144
+ let bracketCount = 1;
145
+ let end = start;
146
+ for (let i = start; i < content.length; i++) {
147
+ if (content[i] === '[')
148
+ bracketCount++;
149
+ if (content[i] === ']') {
150
+ bracketCount--;
151
+ if (bracketCount === 0) {
152
+ end = i;
153
+ break;
154
+ }
155
+ }
156
+ if (i > start + 15000)
157
+ break;
158
+ }
159
+ return end > start ? content.substring(start, end) : null;
160
+ }
161
+ if (returnArray >= 0) {
162
+ const start = returnArray + 'return array('.length;
163
+ let end = start;
164
+ for (let i = start; i < content.length; i++) {
165
+ if (content[i] === ')' && content[i + 1] === ';') {
166
+ end = i;
167
+ break;
168
+ }
169
+ if (i > start + 15000)
170
+ break;
171
+ }
172
+ return end > start ? content.substring(start, end) : null;
173
+ }
174
+ return null;
175
+ }
176
+ const optionsBlock = extractOptionsBlock(content);
177
+ if (optionsBlock) {
178
+ const fieldObjectMatches = optionsBlock.matchAll(/new\s+field(\w+)\s*\(\s*['"](\w+)['"]\s*,\s*\[([\s\S]*?)\]\s*\)/gi);
179
+ for (const match of fieldObjectMatches) {
180
+ const fieldType = match[1].toLowerCase();
181
+ const optionName = match[2];
182
+ const configBlock = match[3];
183
+ let optionDescription;
184
+ let defaultValue = undefined;
185
+ let required = false;
186
+ let extended = false;
187
+ let hint;
188
+ let visibleDepend;
189
+ let items;
190
+ const titleLangMatch = configBlock.match(/['"]title['"]\s*=>\s*LANG_(\w+)/);
191
+ if (titleLangMatch) {
192
+ optionDescription = titleLangMatch[1].replace(/_/g, ' ').toLowerCase();
193
+ }
194
+ const defaultMatch = configBlock.match(/['"]default['"]\s*=>\s*(\w+)/);
195
+ if (defaultMatch) {
196
+ defaultValue =
197
+ defaultMatch[1] === 'true' ? true : defaultMatch[1] === 'false' ? false : defaultMatch[1];
198
+ }
199
+ const numberDefaultMatch = configBlock.match(/['"]default['"]\s*=>\s*(\d+)/);
200
+ if (numberDefaultMatch && !defaultMatch) {
201
+ defaultValue = parseInt(numberDefaultMatch[1], 10);
202
+ }
203
+ const requiredMatch = configBlock.match(/\['required'\]/);
204
+ if (requiredMatch) {
205
+ required = true;
206
+ }
207
+ const extendedMatch = configBlock.match(/['"]extended_option['"]\s*=>\s*(true|false)/);
208
+ if (extendedMatch) {
209
+ extended = extendedMatch[1] === 'true';
210
+ }
211
+ const hintMatch = configBlock.match(/['"]hint['"]\s*=>\s*LANG_(\w+)/);
212
+ if (hintMatch) {
213
+ hint = hintMatch[1].replace(/_/g, ' ').toLowerCase();
214
+ }
215
+ const visibleDependMatch = configBlock.match(/['"]visible_depend['"]\s*=>\s*\[([\s\S]*?)\]/);
216
+ if (visibleDependMatch) {
217
+ try {
218
+ const dependStr = visibleDependMatch[1];
219
+ const keyMatch = dependStr.match(/['"](\w+)['"]\s*=>\s*\[[\s\S]*?['"](\w+)['"][\s\S]*?\[[\s\S]*?['"](\d+)['"][\s\S]*?\]/);
220
+ if (keyMatch) {
221
+ visibleDepend = { [keyMatch[1]]: { [keyMatch[2]]: [keyMatch[3]] } };
222
+ }
223
+ }
224
+ catch {
225
+ // ignore parse errors for visible_depend
226
+ }
227
+ }
228
+ const staticItemsMatch = configBlock.match(/['"]items['"]\s*=>\s*\[([\s\S]*?)\]\s*/);
229
+ if (staticItemsMatch) {
230
+ items = {};
231
+ const itemMatches = staticItemsMatch[1].matchAll(/['"](\w+)['"]\s*=>\s*LANG_(\w+)/g);
232
+ for (const itemMatch of itemMatches) {
233
+ items[itemMatch[1]] = itemMatch[2].replace(/_/g, ' ').toLowerCase();
234
+ }
235
+ }
236
+ const mappedType = TYPE_MAP[fieldType] || fieldType;
237
+ if (!options.find(o => o.name === optionName)) {
238
+ options.push({
239
+ name: optionName,
240
+ type: mappedType,
241
+ description: optionDescription,
242
+ default: defaultValue,
243
+ required,
244
+ extended,
245
+ hint,
246
+ visible_depend: Object.keys(visibleDepend || {}).length > 0 ? visibleDepend : undefined,
247
+ items: Object.keys(items || {}).length > 0 ? items : undefined,
248
+ });
249
+ }
250
+ }
251
+ }
252
+ return {
253
+ name: fileName,
254
+ className,
255
+ filePath: filePath.replace(process.cwd(), ''),
256
+ hasOptions: options.length > 0,
257
+ isSystem,
258
+ options: options.length > 0 ? options : undefined,
259
+ title,
260
+ sqlTemplate,
261
+ filterType,
262
+ filterHint,
263
+ varType,
264
+ allowIndex,
265
+ nativeTag,
266
+ dynamicList,
267
+ validationRules: validationRules.length > 0 ? validationRules : undefined,
268
+ methods: Object.keys(methods).length > 0 ? methods : undefined,
269
+ };
270
+ }
271
+ function scanFieldsDir(dirPath, fields) {
272
+ if (!fs.existsSync(dirPath))
273
+ return;
274
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
275
+ for (const entry of entries) {
276
+ if (entry.isDirectory())
277
+ continue;
278
+ if (entry.name.endsWith('.php')) {
279
+ const field = parseFieldFile(path.join(dirPath, entry.name));
280
+ if (field) {
281
+ fields.push(field);
282
+ }
283
+ }
284
+ }
285
+ }
286
+ function generateFieldsMap(sourceDir, outputPath) {
287
+ const fieldsDir = path.join(sourceDir, 'system', 'fields');
288
+ const fields = [];
289
+ scanFieldsDir(fieldsDir, fields);
290
+ fields.sort((a, b) => a.name.localeCompare(b.name));
291
+ const byName = {};
292
+ for (const field of fields) {
293
+ byName[field.name] = field;
294
+ }
295
+ const systemFields = fields.filter(f => f.isSystem);
296
+ const data = {
297
+ fields,
298
+ byName,
299
+ systemFields,
300
+ fieldCount: fields.length,
301
+ generatedAt: process.env.KNOWLEDGE_GENERATED_AT || new Date().toISOString(),
302
+ };
303
+ const typescriptContent = `// AUTO-GENERATED from source/system/fields
304
+ // Do not edit manually - run 'npm run parse:fields' to regenerate
305
+
306
+ export interface FieldOption {
307
+ name: string;
308
+ type: string;
309
+ description?: string;
310
+ default?: any;
311
+ required?: boolean;
312
+ extended?: boolean;
313
+ visible_depend?: Record<string, Record<string, string[]>>;
314
+ hint?: string;
315
+ items?: Record<string, string>;
316
+ }
317
+
318
+ export interface FieldInfo {
319
+ name: string;
320
+ className: string;
321
+ filePath: string;
322
+ hasOptions: boolean;
323
+ isSystem: boolean;
324
+ options?: FieldOption[];
325
+ description?: string;
326
+ title?: string;
327
+ sqlTemplate?: string;
328
+ filterType?: string;
329
+ filterHint?: string;
330
+ varType?: string;
331
+ allowIndex?: boolean;
332
+ nativeTag?: boolean;
333
+ dynamicList?: boolean;
334
+ validationRules?: string[];
335
+ methods?: {
336
+ parse?: boolean;
337
+ store?: boolean;
338
+ getInput?: boolean;
339
+ applyFilter?: boolean;
340
+ getStringValue?: boolean;
341
+ getFilterInput?: boolean;
342
+ };
343
+ }
344
+
345
+ export interface FieldsMap {
346
+ fields: FieldInfo[];
347
+ byName: Record<string, FieldInfo>;
348
+ systemFields: FieldInfo[];
349
+ fieldCount: number;
350
+ generatedAt: string;
351
+ }
352
+
353
+ export const fieldsMap: FieldsMap = ${JSON.stringify(data, null, 2)};
354
+
355
+ export function getField(name: string): FieldInfo | undefined {
356
+ return fieldsMap.byName[name];
357
+ }
358
+
359
+ export function getFieldTypes(): string[] {
360
+ return fieldsMap.fields.map(f => f.name);
361
+ }
362
+
363
+ export function getSystemFields(): FieldInfo[] {
364
+ return fieldsMap.systemFields;
365
+ }
366
+
367
+ export function getFieldOptions(name: string): FieldOption[] | undefined {
368
+ return fieldsMap.byName[name]?.options;
369
+ }
370
+
371
+ export function getFieldBySqlTemplate(sql: string): FieldInfo | undefined {
372
+ return fieldsMap.fields.find(f => f.sqlTemplate?.includes(sql));
373
+ }
374
+ `;
375
+ fs.writeFileSync(outputPath, typescriptContent);
376
+ console.log(`Generated ${data.fieldCount} fields to ${outputPath}`);
377
+ }
378
+ if (require.main === module) {
379
+ const sourcePath = path.resolve(process.env.INSTANTCMS_SOURCE || 'source');
380
+ const outputPath = path.resolve('src/data/fields-map.ts');
381
+ generateFieldsMap(sourcePath, outputPath);
382
+ }
@@ -0,0 +1,106 @@
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.discoverHooks = discoverHooks;
37
+ exports.generateHooksSource = generateHooksSource;
38
+ const fs = __importStar(require("node:fs"));
39
+ const path = __importStar(require("node:path"));
40
+ const PHP_EXTENSIONS = new Set(['.php']);
41
+ const HOOK_PATTERN = /(?:(cmsEventsManager)::(hook|hookAll)|(?:\$[a-zA-Z_][\w]*|\$this|\$controller)->(runHook))\(\s*(['"])([a-z][a-z0-9_]+)\4(?:\s*,\s*([^;\n]+))?/g;
42
+ function walk(directory) {
43
+ if (!fs.existsSync(directory))
44
+ return [];
45
+ return fs.readdirSync(directory, { withFileTypes: true }).flatMap(entry => {
46
+ const fullPath = path.join(directory, entry.name);
47
+ if (entry.isDirectory())
48
+ return walk(fullPath);
49
+ return PHP_EXTENSIONS.has(path.extname(entry.name)) ? [fullPath] : [];
50
+ });
51
+ }
52
+ function extractParameters(expression = '') {
53
+ const names = [...expression.matchAll(/\$[a-zA-Z_][\w]*/g)].map(match => match[0]);
54
+ return [...new Set(names)].slice(0, 20);
55
+ }
56
+ function discoverHooks(sourceRoot) {
57
+ const systemRoot = path.join(sourceRoot, 'system');
58
+ const discovered = new Map();
59
+ for (const filePath of walk(systemRoot)) {
60
+ const content = fs.readFileSync(filePath, 'utf8');
61
+ for (const match of content.matchAll(HOOK_PATTERN)) {
62
+ const name = match[5];
63
+ const prefix = content.slice(Math.max(0, match.index - 80), match.index);
64
+ const isFilter = /(?:return\s+|=\s*)$/.test(prefix.trimEnd());
65
+ const relativeFile = path.relative(sourceRoot, filePath).replaceAll(path.sep, '/');
66
+ const existing = discovered.get(name);
67
+ if (existing) {
68
+ existing.occurrences += 1;
69
+ if (!existing.files.includes(relativeFile))
70
+ existing.files.push(relativeFile);
71
+ existing.parameters = [
72
+ ...new Set([...existing.parameters, ...extractParameters(match[6])]),
73
+ ];
74
+ if (isFilter)
75
+ existing.inferredType = 'filter';
76
+ }
77
+ else {
78
+ discovered.set(name, {
79
+ name,
80
+ inferredType: isFilter ? 'filter' : 'action',
81
+ parameters: extractParameters(match[6]),
82
+ files: [relativeFile],
83
+ occurrences: 1,
84
+ });
85
+ }
86
+ }
87
+ }
88
+ return [...discovered.values()]
89
+ .map(hook => ({ ...hook, files: hook.files.sort() }))
90
+ .sort((left, right) => left.name.localeCompare(right.name));
91
+ }
92
+ function generateHooksSource(sourceRoot) {
93
+ const hooks = discoverHooks(sourceRoot);
94
+ return (`// Generated from official InstantCMS PHP hook calls. Do not edit.\n` +
95
+ `export interface SourceHookEvidence {\n` +
96
+ ` name: string;\n inferredType: 'filter' | 'action';\n parameters: string[];\n` +
97
+ ` files: string[];\n occurrences: number;\n}\n\n` +
98
+ `export const sourceHooks: SourceHookEvidence[] = ${JSON.stringify(hooks, null, 2)};\n`);
99
+ }
100
+ if (require.main === module) {
101
+ const sourceRoot = path.resolve(process.env.INSTANTCMS_SOURCE || 'source');
102
+ const outputPath = path.resolve('src/generated/hooks-source.ts');
103
+ const output = generateHooksSource(sourceRoot);
104
+ fs.writeFileSync(outputPath, output);
105
+ console.log(`Generated ${discoverHooks(sourceRoot).length} source-backed hooks to ${outputPath}`);
106
+ }
@@ -0,0 +1,197 @@
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.parseSqlFile = parseSqlFile;
37
+ exports.generateDatabaseSchema = generateDatabaseSchema;
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const PREFIX_PLACEHOLDER = '{#}';
41
+ function parseSqlFile(filePath) {
42
+ const content = fs.readFileSync(filePath, 'utf-8');
43
+ const tables = [];
44
+ const statements = content.split(/DROP TABLE IF EXISTS.*?;/i);
45
+ for (const statement of statements) {
46
+ if (!statement.trim())
47
+ continue;
48
+ const createMatch = statement.match(/CREATE TABLE.*?\(([\s\S]*?)\)\s*ENGINE/i);
49
+ if (!createMatch)
50
+ continue;
51
+ const tableNameMatch = statement.match(/CREATE TABLE\s+`?([^`\s]+)`?\s*\(/i);
52
+ if (!tableNameMatch)
53
+ continue;
54
+ const tableName = tableNameMatch[1].replace(PREFIX_PLACEHOLDER, 'cms_');
55
+ const tableCommentMatch = statement.match(/COMMENT='([^']*)'/);
56
+ const tableComment = tableCommentMatch ? tableCommentMatch[1] : undefined;
57
+ const columnsBlock = createMatch[1];
58
+ const lines = columnsBlock.split(/,\s*\n/);
59
+ const fields = [];
60
+ const indexes = [];
61
+ for (const line of lines) {
62
+ const trimmed = line.trim();
63
+ if (!trimmed)
64
+ continue;
65
+ if (trimmed.startsWith('PRIMARY KEY') ||
66
+ trimmed.startsWith('UNIQUE KEY') ||
67
+ trimmed.startsWith('KEY')) {
68
+ const indexMatch = parseIndex(trimmed, tableName);
69
+ if (indexMatch) {
70
+ indexes.push(indexMatch);
71
+ }
72
+ continue;
73
+ }
74
+ const fieldMatch = parseField(trimmed);
75
+ if (fieldMatch) {
76
+ fields.push(fieldMatch);
77
+ }
78
+ }
79
+ tables.push({
80
+ name: tableName,
81
+ comment: tableComment,
82
+ fields,
83
+ indexes,
84
+ });
85
+ }
86
+ return {
87
+ tables,
88
+ tableCount: tables.length,
89
+ generatedAt: process.env.KNOWLEDGE_GENERATED_AT || new Date().toISOString(),
90
+ sourceFile: path.basename(filePath),
91
+ };
92
+ }
93
+ function parseField(line) {
94
+ const match = line.match(/`?(\w+)`?\s+((?:ENUM\([^)]+\)|[\w()]+))(?:\s+(.+))?/i);
95
+ if (!match)
96
+ return null;
97
+ const name = match[1];
98
+ const type = match[2].toUpperCase();
99
+ const rest = match[3] || '';
100
+ const nullable = !rest.toUpperCase().includes('NOT NULL');
101
+ const defaultMatch = rest.match(/DEFAULT\s+('[^']*'|"[^"]*"|[^\s,]+)/i);
102
+ const commentMatch = rest.match(/COMMENT\s+'([^']*)'/i);
103
+ const keyMatch = rest.match(/(PRIMARY|UNIQUE)?\s*KEY/i);
104
+ let key;
105
+ if (keyMatch) {
106
+ if (keyMatch[1]?.toUpperCase() === 'PRIMARY')
107
+ key = 'PRI';
108
+ else if (keyMatch[0].toUpperCase().includes('UNIQUE'))
109
+ key = 'UNI';
110
+ }
111
+ const extraMatch = rest.match(/(AUTO_INCREMENT|ON UPDATE CURRENT_TIMESTAMP)/i);
112
+ return {
113
+ name,
114
+ type,
115
+ nullable,
116
+ default: defaultMatch ? defaultMatch[1].replace(/^['"]|['"]$/g, '') : undefined,
117
+ comment: commentMatch ? commentMatch[1] : undefined,
118
+ key,
119
+ extra: extraMatch ? extraMatch[1] : undefined,
120
+ };
121
+ }
122
+ function parseIndex(line, _tableName) {
123
+ const primaryMatch = line.match(/PRIMARY\s+KEY\s*\(([^)]+)\)/i);
124
+ if (primaryMatch) {
125
+ const fields = primaryMatch[1].split(',').map(f => f.trim().replace(/`/g, ''));
126
+ return { name: 'PRIMARY', fields };
127
+ }
128
+ const uniqueMatch = line.match(/UNIQUE\s+KEY\s+`?(\w+)`?\s*\(([^)]+)\)/i);
129
+ if (uniqueMatch) {
130
+ const fields = uniqueMatch[2].split(',').map(f => f.trim().replace(/`/g, ''));
131
+ return { name: uniqueMatch[1], fields, type: 'UNIQUE' };
132
+ }
133
+ const keyMatch = line.match(/KEY\s+`?(\w+)`?\s*\(([^)]+)\)/i);
134
+ if (keyMatch) {
135
+ const fields = keyMatch[2].split(',').map(f => f.trim().replace(/`/g, ''));
136
+ return { name: keyMatch[1], fields };
137
+ }
138
+ const fulltextMatch = line.match(/FULLTEXT\s+(?:KEY\s+)?`?(\w+)`?\s*\(([^)]+)\)/i);
139
+ if (fulltextMatch) {
140
+ const fields = fulltextMatch[2].split(',').map(f => f.trim().replace(/`/g, ''));
141
+ return { name: fulltextMatch[1], fields, type: 'FULLTEXT' };
142
+ }
143
+ return null;
144
+ }
145
+ function generateDatabaseSchema(sqlFilePath, outputPath) {
146
+ const schema = parseSqlFile(sqlFilePath);
147
+ const typescriptContent = `// AUTO-GENERATED from ${schema.sourceFile}
148
+ // Do not edit manually - run 'npm run parse:database' to regenerate
149
+
150
+ export interface DbField {
151
+ name: string;
152
+ type: string;
153
+ nullable: boolean;
154
+ default?: string;
155
+ comment?: string;
156
+ key?: 'PRI' | 'UNI' | 'MUL';
157
+ extra?: string;
158
+ }
159
+
160
+ export interface DbIndex {
161
+ name: string;
162
+ fields: string[];
163
+ type?: string;
164
+ }
165
+
166
+ export interface DbTable {
167
+ name: string;
168
+ comment?: string;
169
+ fields: DbField[];
170
+ indexes: DbIndex[];
171
+ }
172
+
173
+ export interface DatabaseSchema {
174
+ tables: DbTable[];
175
+ tableCount: number;
176
+ generatedAt: string;
177
+ sourceFile?: string;
178
+ }
179
+
180
+ export const databaseSchema: DatabaseSchema = ${JSON.stringify(schema, null, 2)};
181
+
182
+ export function getTable(name: string): DbTable | undefined {
183
+ return databaseSchema.tables.find(t => t.name === name);
184
+ }
185
+
186
+ export function getTablesByPrefix(prefix: string): DbTable[] {
187
+ return databaseSchema.tables.filter(t => t.name.startsWith(prefix));
188
+ }
189
+ `;
190
+ fs.writeFileSync(outputPath, typescriptContent);
191
+ console.log(`Generated ${schema.tableCount} tables to ${outputPath}`);
192
+ }
193
+ if (require.main === module) {
194
+ const sqlPath = path.resolve(process.env.INSTANTCMS_SOURCE || 'source', 'install/languages/ru/sql/base.sql');
195
+ const outputPath = path.resolve('src/data/database-schema.ts');
196
+ generateDatabaseSchema(sqlPath, outputPath);
197
+ }