@rglabs/butterfly 2.0.1

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 (117) hide show
  1. package/CLAUDE.md +201 -0
  2. package/README.md +371 -0
  3. package/dist/commands/add.d.ts +23 -0
  4. package/dist/commands/add.js +303 -0
  5. package/dist/commands/code.d.ts +11 -0
  6. package/dist/commands/code.js +72 -0
  7. package/dist/commands/create-object.d.ts +6 -0
  8. package/dist/commands/create-object.js +293 -0
  9. package/dist/commands/create-report.d.ts +6 -0
  10. package/dist/commands/create-report.js +154 -0
  11. package/dist/commands/diff.d.ts +4 -0
  12. package/dist/commands/diff.js +238 -0
  13. package/dist/commands/download.d.ts +4 -0
  14. package/dist/commands/download.js +374 -0
  15. package/dist/commands/layout.d.ts +12 -0
  16. package/dist/commands/layout.js +83 -0
  17. package/dist/commands/record.d.ts +21 -0
  18. package/dist/commands/record.js +483 -0
  19. package/dist/commands/run-poc.d.ts +3 -0
  20. package/dist/commands/run-poc.js +18 -0
  21. package/dist/commands/setup.d.ts +3 -0
  22. package/dist/commands/setup.js +66 -0
  23. package/dist/commands/start-poc.d.ts +3 -0
  24. package/dist/commands/start-poc.js +55 -0
  25. package/dist/commands/sync-docs.d.ts +3 -0
  26. package/dist/commands/sync-docs.js +27 -0
  27. package/dist/commands/translate.d.ts +13 -0
  28. package/dist/commands/translate.js +401 -0
  29. package/dist/commands/upload.d.ts +3 -0
  30. package/dist/commands/upload.js +150 -0
  31. package/dist/commands/workflow-info.d.ts +13 -0
  32. package/dist/commands/workflow-info.js +161 -0
  33. package/dist/components/ConflictResolver.d.ts +12 -0
  34. package/dist/components/ConflictResolver.js +77 -0
  35. package/dist/components/DiffView.d.ts +11 -0
  36. package/dist/components/DiffView.js +101 -0
  37. package/dist/components/DownloadProgress.d.ts +11 -0
  38. package/dist/components/DownloadProgress.js +29 -0
  39. package/dist/components/RecordPreview.d.ts +11 -0
  40. package/dist/components/RecordPreview.js +91 -0
  41. package/dist/components/SetupForm.d.ts +8 -0
  42. package/dist/components/SetupForm.js +56 -0
  43. package/dist/components/UploadProgress.d.ts +13 -0
  44. package/dist/components/UploadProgress.js +42 -0
  45. package/dist/diff/adapters/index.d.ts +8 -0
  46. package/dist/diff/adapters/index.js +18 -0
  47. package/dist/diff/adapters/objectsAdapter.d.ts +13 -0
  48. package/dist/diff/adapters/objectsAdapter.js +177 -0
  49. package/dist/diff/adapters/reportsAdapter.d.ts +14 -0
  50. package/dist/diff/adapters/reportsAdapter.js +212 -0
  51. package/dist/diff/adapters/types.d.ts +19 -0
  52. package/dist/diff/adapters/types.js +2 -0
  53. package/dist/diff/engine.d.ts +19 -0
  54. package/dist/diff/engine.js +57 -0
  55. package/dist/diff/types.d.ts +34 -0
  56. package/dist/diff/types.js +110 -0
  57. package/dist/index.d.ts +3 -0
  58. package/dist/index.js +117 -0
  59. package/dist/types/index.d.ts +18 -0
  60. package/dist/types/index.js +2 -0
  61. package/dist/utils/api.d.ts +85 -0
  62. package/dist/utils/api.js +1031 -0
  63. package/dist/utils/auth.d.ts +4 -0
  64. package/dist/utils/auth.js +22 -0
  65. package/dist/utils/bfySplitter.d.ts +12 -0
  66. package/dist/utils/bfySplitter.js +151 -0
  67. package/dist/utils/docs.d.ts +16 -0
  68. package/dist/utils/docs.js +186 -0
  69. package/dist/utils/errorLogger.d.ts +6 -0
  70. package/dist/utils/errorLogger.js +29 -0
  71. package/dist/utils/files.d.ts +14 -0
  72. package/dist/utils/files.js +772 -0
  73. package/dist/utils/lockManager.d.ts +15 -0
  74. package/dist/utils/lockManager.js +126 -0
  75. package/dist/utils/resourceHandlers.d.ts +50 -0
  76. package/dist/utils/resourceHandlers.js +684 -0
  77. package/dist/utils/resourceMapping.d.ts +32 -0
  78. package/dist/utils/resourceMapping.js +210 -0
  79. package/dist/utils/singleResourceDownload.d.ts +14 -0
  80. package/dist/utils/singleResourceDownload.js +261 -0
  81. package/dist/utils/summaryGenerator.d.ts +2 -0
  82. package/dist/utils/summaryGenerator.js +183 -0
  83. package/dist/utils/uploadHandler.d.ts +31 -0
  84. package/dist/utils/uploadHandler.js +263 -0
  85. package/docs/AI_API.md +93 -0
  86. package/docs/CLAUDE.md +216 -0
  87. package/docs/PROJECT_SPECIFIC.md +1 -0
  88. package/docs/RECORD_COMMAND.md +262 -0
  89. package/docs/WORKFLOW_API.md +480 -0
  90. package/docs/bfy-splitting.md +126 -0
  91. package/docs/cli-commands.md +333 -0
  92. package/docs/examples/README.md +95 -0
  93. package/docs/examples/order-system.md +147 -0
  94. package/docs/examples/product-catalog.md +195 -0
  95. package/docs/examples/reports.md +187 -0
  96. package/docs/excel-export.md +216 -0
  97. package/docs/field-types/README.md +29 -0
  98. package/docs/field-types/calculated.md +147 -0
  99. package/docs/field-types/code-mappings.md +84 -0
  100. package/docs/field-types/custom.md +340 -0
  101. package/docs/object-specs/README.md +136 -0
  102. package/docs/object-specs/code-parameters.md +151 -0
  103. package/docs/object-specs/creating.md +203 -0
  104. package/docs/object-specs/js-code-examples.md +208 -0
  105. package/docs/object-specs/js-field-updates.md +168 -0
  106. package/docs/objects/README.md +89 -0
  107. package/docs/objects/creating.md +127 -0
  108. package/docs/page-layout.md +361 -0
  109. package/docs/permissions.md +260 -0
  110. package/docs/reports.md +197 -0
  111. package/docs/state-machines.md +544 -0
  112. package/docs/tasks/create-object.md +81 -0
  113. package/docs/translations.md +346 -0
  114. package/docs/twig-helpers.md +283 -0
  115. package/docs/webservices.md +159 -0
  116. package/docs/workspaces.md +176 -0
  117. package/package.json +59 -0
@@ -0,0 +1,183 @@
1
+ import { join } from 'path';
2
+ import { readdir, readFile, writeFile, mkdir } from 'fs/promises';
3
+ import { existsSync } from 'fs';
4
+ function parseFieldConfig(fieldType, val1, val3) {
5
+ const result = { val1, val3 };
6
+ if (!val1)
7
+ return result;
8
+ if (fieldType === 'from_list') {
9
+ if (val1.includes('|') && val1.includes(':')) {
10
+ result.options = val1;
11
+ }
12
+ return result;
13
+ }
14
+ if (fieldType === 'dropdown' || fieldType == 'autocomplete') {
15
+ result.sourceTable = val1;
16
+ if (val3) {
17
+ result.displayColumn = val3;
18
+ }
19
+ return result;
20
+ }
21
+ return result;
22
+ }
23
+ function formatColumnType(spec) {
24
+ const baseType = spec.type || 'text';
25
+ switch (baseType) {
26
+ case 'from_list':
27
+ case 'autocomplete':
28
+ case 'dropdown':
29
+ const dropdownConfig = parseFieldConfig(spec.type, spec.val_1, spec.val_3);
30
+ return {
31
+ type: baseType,
32
+ dropdownConfig
33
+ };
34
+ case 'custom':
35
+ return { type: 'custom field' };
36
+ case 'nested':
37
+ return { type: 'structured data' };
38
+ case 'calculated':
39
+ return { type: 'calculated' };
40
+ case 'textarea':
41
+ return { type: 'text area' };
42
+ default:
43
+ return { type: baseType };
44
+ }
45
+ }
46
+ async function parseObjectDirectory(objectPath) {
47
+ try {
48
+ const objectJsonPath = join(objectPath, 'object.json');
49
+ if (!existsSync(objectJsonPath))
50
+ return null;
51
+ const objectData = JSON.parse(await readFile(objectJsonPath, 'utf-8'));
52
+ const columns = [];
53
+ const items = await readdir(objectPath, { withFileTypes: true });
54
+ const specDirs = items.filter(item => item.isDirectory());
55
+ for (const specDir of specDirs) {
56
+ const specPath = join(objectPath, specDir.name, 'spec.json');
57
+ if (existsSync(specPath)) {
58
+ const specData = JSON.parse(await readFile(specPath, 'utf-8'));
59
+ const { type, dropdownConfig } = formatColumnType(specData);
60
+ columns.push({
61
+ columnName: specData.column_name || specDir.name,
62
+ name: specData.name || specDir.name,
63
+ type,
64
+ required: Boolean(specData.required),
65
+ description: specData.description,
66
+ dropdownConfig
67
+ });
68
+ }
69
+ }
70
+ columns.sort((a, b) => {
71
+ return a.columnName.localeCompare(b.columnName);
72
+ });
73
+ return {
74
+ tableName: objectData.table_name,
75
+ name: objectData.name || objectData.table_name,
76
+ description: objectData.description,
77
+ isCmsObject: Boolean(objectData.is_cms_object),
78
+ databaseAlias: objectData.database_alias,
79
+ columns
80
+ };
81
+ }
82
+ catch (error) {
83
+ console.error(`Error parsing object directory ${objectPath}:`, error);
84
+ return null;
85
+ }
86
+ }
87
+ function generateObjectMarkdown(obj) {
88
+ let markdown = `# ${obj.name}\n\n`;
89
+ markdown += `**Table Name:** ${obj.tableName}\n\n`;
90
+ if (obj.description) {
91
+ markdown += `**Description:** ${obj.description}\n\n`;
92
+ }
93
+ if (obj.databaseAlias) {
94
+ markdown += `**Database Alias:** ${obj.databaseAlias}\n\n`;
95
+ }
96
+ markdown += '## Columns\n\n';
97
+ markdown += '| Column | Type | Required | Details |\n';
98
+ markdown += '|--------|------|----------|----------|\n';
99
+ obj.columns.forEach(col => {
100
+ let details = '';
101
+ if (col.dropdownConfig) {
102
+ const parts = [];
103
+ if (col.dropdownConfig.options) {
104
+ parts.push(`Options: ${col.dropdownConfig.options}`);
105
+ }
106
+ if (col.dropdownConfig.sourceTable) {
107
+ parts.push(`Source Table: ${col.dropdownConfig.sourceTable}`);
108
+ }
109
+ if (col.dropdownConfig.displayColumn) {
110
+ parts.push(`Display Column: ${col.dropdownConfig.displayColumn}`);
111
+ }
112
+ if (col.dropdownConfig.valueColumns) {
113
+ parts.push(`Value Columns: ${col.dropdownConfig.valueColumns.join(', ')}`);
114
+ }
115
+ details = parts.join(' | ');
116
+ }
117
+ if (col.description && !details) {
118
+ details = col.description;
119
+ }
120
+ markdown += `| ${col.columnName} | ${col.type} | ${col.required ? 'Yes' : 'No'} | ${details} |\n`;
121
+ });
122
+ return markdown;
123
+ }
124
+ function generateMarkdownSummary(objects) {
125
+ const appObjects = objects.filter(obj => !obj.isCmsObject);
126
+ const cmsObjects = objects.filter(obj => obj.isCmsObject);
127
+ let markdown = '# Database Tables Summary\n\n';
128
+ markdown += `Generated on: ${new Date().toISOString()}\n\n`;
129
+ if (appObjects.length > 0) {
130
+ markdown += '## Application Tables\n\n';
131
+ appObjects.forEach(obj => {
132
+ markdown += `- [${obj.name}](./docs/app/${obj.tableName}.md) (${obj.tableName})\n`;
133
+ });
134
+ markdown += '\n';
135
+ }
136
+ if (cmsObjects.length > 0) {
137
+ markdown += '## CMS/Core Tables\n\n';
138
+ cmsObjects.forEach(obj => {
139
+ markdown += `- [${obj.name}](./docs/butterfly/${obj.tableName}.md) (${obj.tableName})\n`;
140
+ });
141
+ markdown += '\n';
142
+ }
143
+ return markdown;
144
+ }
145
+ export async function generateTableSummary(outputPath) {
146
+ const objectsPath = join(outputPath, 'objects');
147
+ if (!existsSync(objectsPath)) {
148
+ console.log('No objects directory found, skipping summary generation');
149
+ return;
150
+ }
151
+ const objects = [];
152
+ const docsPath = join(outputPath, 'docs');
153
+ const appDocsPath = join(docsPath, 'app');
154
+ const butterflyDocsPath = join(docsPath, 'butterfly');
155
+ await mkdir(docsPath, { recursive: true });
156
+ await mkdir(appDocsPath, { recursive: true });
157
+ await mkdir(butterflyDocsPath, { recursive: true });
158
+ const subdirs = await readdir(objectsPath, { withFileTypes: true });
159
+ for (const subdir of subdirs.filter(item => item.isDirectory())) {
160
+ const subdirPath = join(objectsPath, subdir.name);
161
+ const tableItems = await readdir(subdirPath, { withFileTypes: true });
162
+ for (const item of tableItems.filter(item => item.isDirectory())) {
163
+ const objectSummary = await parseObjectDirectory(join(subdirPath, item.name));
164
+ if (objectSummary) {
165
+ objects.push(objectSummary);
166
+ const targetDocsPath = objectSummary.isCmsObject ? butterflyDocsPath : appDocsPath;
167
+ const objectMarkdown = generateObjectMarkdown(objectSummary);
168
+ const objectMdPath = join(targetDocsPath, `${objectSummary.tableName}.md`);
169
+ await writeFile(objectMdPath, objectMarkdown, 'utf-8');
170
+ }
171
+ }
172
+ }
173
+ if (objects.length === 0) {
174
+ console.log('No objects found to summarize');
175
+ return;
176
+ }
177
+ const markdown = generateMarkdownSummary(objects);
178
+ const summaryPath = join(outputPath, 'tables-summary.md');
179
+ await writeFile(summaryPath, markdown, 'utf-8');
180
+ console.log(`\nTable summary generated: ${summaryPath}`);
181
+ console.log(`Individual object docs created in: ${docsPath}`);
182
+ }
183
+ //# sourceMappingURL=summaryGenerator.js.map
@@ -0,0 +1,31 @@
1
+ import { ButterflyAPI } from './api.js';
2
+ export interface UploadResult {
3
+ success: boolean;
4
+ message: string;
5
+ resourceId?: number;
6
+ }
7
+ export interface UploadOptions {
8
+ onProgress?: (message: string, type: 'info' | 'success' | 'error') => void;
9
+ }
10
+ export declare class UploadHandler {
11
+ private api;
12
+ private options;
13
+ constructor(api: ButterflyAPI, options?: UploadOptions);
14
+ private log;
15
+ private handleResult;
16
+ private getContainerFileIfPartFile;
17
+ uploadFile(filePath: string): Promise<UploadResult>;
18
+ private handleWorkflowFile;
19
+ private handleWebserviceFile;
20
+ private handlePdfTemplateFile;
21
+ private handleCronjobFile;
22
+ private handleEmailTemplateFile;
23
+ private handleEmailLayoutFile;
24
+ private handleObjectFile;
25
+ private handlePageFile;
26
+ private handleStateMachineFile;
27
+ private handleAiTaskFile;
28
+ private handleReportFile;
29
+ private handleTranslationFile;
30
+ }
31
+ //# sourceMappingURL=uploadHandler.d.ts.map
@@ -0,0 +1,263 @@
1
+ import { basename, normalize } from 'path';
2
+ import { promises as fs } from 'fs';
3
+ import { getContainerPathFromPartFile } from './bfySplitter.js';
4
+ import { handleWorkflowJson, handleWorkflowVersionJson, handleWorkflowNodeJson, handleWorkflowNodeCode, handleWorkflowNodeParams, handleWorkflowConnections, handleWebserviceJson, handleWebservicePageCode, handlePdfTemplateJson, handlePdfTemplateTwig, handleCronjobJson, handleCronjobCode, handleEmailTemplateJson, handleEmailTemplateBfy, handleEmailLayoutJson, handleEmailLayoutBfy, handleObjectJson, handleObjectSpecJson, handleObjectListingQuery, handleObjectSpecCodeFile, handlePageJson, handleStateMachineJson, handleStateMachineStateJson, handleStateMachineRoleJson, handleStateMachineRoleCode, handleStateMachineTransitionJson, handleStateMachineTransitionCode, handleStateMachineActionJson, handleStateMachineActionCode, handleTransitionActionJson, handleStateMachineSpecJson, handleAiTaskJson, handleAiTaskCodeFile, handleReportJson, handleReportSpecJson, handleReportSpecCode, handleReportQueryJson, handleReportQueryCode, handleReportMainQuery, handleTranslationYaml, } from './resourceHandlers.js';
5
+ const AI_TASK_FILE_MAPPINGS = {
6
+ 'prompt.bfy': 'prompt',
7
+ 'output_format.json': 'output_format',
8
+ 'system_message.bfy': 'system_message',
9
+ 'action_code.bfy': 'action_code',
10
+ 'action_preview_code.bfy': 'action_preview_code',
11
+ 'initial_code.bfy': 'initial_code'
12
+ };
13
+ const OBJECT_SPEC_CODE_FILES = [
14
+ 'code.bfy', 'template_code.bfy', 'processing_code.bfy',
15
+ 'filter_code.bfy', 'configuration.yaml', 'code.js', 'style.css'
16
+ ];
17
+ export class UploadHandler {
18
+ api;
19
+ options;
20
+ constructor(api, options = {}) {
21
+ this.api = api;
22
+ this.options = options;
23
+ }
24
+ log(message, type = 'info') {
25
+ if (this.options.onProgress) {
26
+ this.options.onProgress(message, type);
27
+ }
28
+ }
29
+ handleResult(result) {
30
+ this.log(result.success ? `✓ ${result.message}` : `✗ ${result.message}`, result.success ? 'success' : 'error');
31
+ return result;
32
+ }
33
+ async getContainerFileIfPartFile(filePath) {
34
+ if (!filePath.endsWith('.bfy')) {
35
+ return null;
36
+ }
37
+ const containerPath = getContainerPathFromPartFile(filePath);
38
+ if (!containerPath) {
39
+ return null;
40
+ }
41
+ try {
42
+ await fs.access(containerPath);
43
+ return containerPath;
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ }
49
+ async uploadFile(filePath) {
50
+ try {
51
+ const containerPath = await this.getContainerFileIfPartFile(filePath);
52
+ if (containerPath) {
53
+ this.log(`Part file detected, uploading container: ${basename(containerPath)}`, 'info');
54
+ filePath = containerPath;
55
+ }
56
+ const parts = filePath.split('/');
57
+ const resourcesIndex = parts.findIndex(part => part === 'butterfly-resources');
58
+ if (resourcesIndex === -1 || parts.length < resourcesIndex + 2) {
59
+ return { success: false, message: 'Invalid file path structure' };
60
+ }
61
+ const resourceType = parts[resourcesIndex + 1];
62
+ const fileName = basename(filePath);
63
+ const normalizedPath = normalize(filePath);
64
+ switch (resourceType) {
65
+ case 'bfy_workflows':
66
+ return this.handleResult(await this.handleWorkflowFile(filePath, fileName, normalizedPath));
67
+ case 'webservices':
68
+ return this.handleResult(await this.handleWebserviceFile(filePath, fileName));
69
+ case 'pdf_templates':
70
+ return this.handleResult(await this.handlePdfTemplateFile(filePath, fileName));
71
+ case 'bfy_cronjobs':
72
+ return this.handleResult(await this.handleCronjobFile(filePath, fileName));
73
+ case 'cms_email_templates':
74
+ return this.handleResult(await this.handleEmailTemplateFile(filePath, fileName));
75
+ case 'cms_email_layouts':
76
+ return this.handleResult(await this.handleEmailLayoutFile(filePath, fileName));
77
+ case 'objects':
78
+ return this.handleResult(await this.handleObjectFile(filePath, fileName));
79
+ case 'pages':
80
+ return this.handleResult(await this.handlePageFile(filePath, fileName));
81
+ case 'bfy_state_machines':
82
+ return this.handleResult(await this.handleStateMachineFile(filePath, fileName, normalizedPath));
83
+ case 'bfy_ai_tasks':
84
+ return this.handleResult(await this.handleAiTaskFile(filePath, fileName));
85
+ case 'reports':
86
+ return this.handleResult(await this.handleReportFile(filePath, fileName, normalizedPath));
87
+ case 'translations':
88
+ return this.handleResult(await this.handleTranslationFile(filePath, fileName));
89
+ default:
90
+ return { success: false, message: `Unsupported resource type: ${resourceType}` };
91
+ }
92
+ }
93
+ catch (error) {
94
+ const message = error instanceof Error ? error.message : 'Unknown error';
95
+ this.log(`Error uploading file: ${message}`, 'error');
96
+ return { success: false, message };
97
+ }
98
+ }
99
+ async handleWorkflowFile(filePath, fileName, normalizedPath) {
100
+ if (fileName === 'workflow.json') {
101
+ return handleWorkflowJson(this.api, filePath);
102
+ }
103
+ else if (fileName === 'version.json') {
104
+ return handleWorkflowVersionJson(this.api, filePath);
105
+ }
106
+ else if (fileName === 'node.json') {
107
+ return handleWorkflowNodeJson(this.api, filePath);
108
+ }
109
+ else if (fileName === 'code.bfy' && normalizedPath.includes('/nodes/')) {
110
+ return handleWorkflowNodeCode(this.api, filePath);
111
+ }
112
+ else if (fileName === 'params.yaml' && normalizedPath.includes('/nodes/')) {
113
+ return handleWorkflowNodeParams(this.api, filePath);
114
+ }
115
+ else if (fileName === 'connections.json') {
116
+ return handleWorkflowConnections(this.api, filePath);
117
+ }
118
+ return { success: false, message: `Unsupported workflow file type: ${fileName}` };
119
+ }
120
+ async handleWebserviceFile(filePath, fileName) {
121
+ if (fileName === 'webservice.json') {
122
+ return handleWebserviceJson(this.api, filePath);
123
+ }
124
+ else if (fileName === 'page_code.bfy') {
125
+ return handleWebservicePageCode(this.api, filePath);
126
+ }
127
+ return { success: false, message: `Unsupported webservice file type: ${fileName}` };
128
+ }
129
+ async handlePdfTemplateFile(filePath, fileName) {
130
+ if (fileName === 'pdf_template.json') {
131
+ return handlePdfTemplateJson(this.api, filePath);
132
+ }
133
+ else if (fileName === 'template.twig') {
134
+ return handlePdfTemplateTwig(this.api, filePath);
135
+ }
136
+ return { success: false, message: `Unsupported PDF template file type: ${fileName}` };
137
+ }
138
+ async handleCronjobFile(filePath, fileName) {
139
+ if (fileName === 'cronjob.json') {
140
+ return handleCronjobJson(this.api, filePath);
141
+ }
142
+ else if (fileName === 'code.bfy') {
143
+ return handleCronjobCode(this.api, filePath);
144
+ }
145
+ return { success: false, message: `Unsupported cronjob file type: ${fileName}` };
146
+ }
147
+ async handleEmailTemplateFile(filePath, fileName) {
148
+ if (fileName === 'template.json') {
149
+ return handleEmailTemplateJson(this.api, filePath);
150
+ }
151
+ else if (fileName.endsWith('.bfy')) {
152
+ return handleEmailTemplateBfy(this.api, filePath);
153
+ }
154
+ return { success: false, message: `Unsupported email template file type: ${fileName}` };
155
+ }
156
+ async handleEmailLayoutFile(filePath, fileName) {
157
+ if (fileName === 'layout.json') {
158
+ return handleEmailLayoutJson(this.api, filePath);
159
+ }
160
+ else if (fileName.endsWith('.bfy')) {
161
+ return handleEmailLayoutBfy(this.api, filePath);
162
+ }
163
+ return { success: false, message: `Unsupported email layout file type: ${fileName}` };
164
+ }
165
+ async handleObjectFile(filePath, fileName) {
166
+ if (fileName === 'object.json') {
167
+ return handleObjectJson(this.api, filePath);
168
+ }
169
+ else if (fileName === 'spec.json') {
170
+ return handleObjectSpecJson(this.api, filePath);
171
+ }
172
+ else if (fileName === 'listing_query.bfy') {
173
+ return handleObjectListingQuery(this.api, filePath);
174
+ }
175
+ else if (OBJECT_SPEC_CODE_FILES.includes(fileName)) {
176
+ return handleObjectSpecCodeFile(this.api, filePath);
177
+ }
178
+ return { success: false, message: `Unsupported object file type: ${fileName}` };
179
+ }
180
+ async handlePageFile(filePath, fileName) {
181
+ if (fileName === 'page.json') {
182
+ return handlePageJson(this.api, filePath);
183
+ }
184
+ return { success: false, message: `Unsupported page file type: ${fileName}` };
185
+ }
186
+ async handleStateMachineFile(filePath, fileName, normalizedPath) {
187
+ if (fileName === 'state_machine.json') {
188
+ return handleStateMachineJson(this.api, filePath);
189
+ }
190
+ else if (normalizedPath.includes('/states/') && fileName === 'state.json') {
191
+ return handleStateMachineStateJson(this.api, filePath);
192
+ }
193
+ else if (normalizedPath.includes('/roles/') && fileName === 'role.json') {
194
+ return handleStateMachineRoleJson(this.api, filePath);
195
+ }
196
+ else if (normalizedPath.includes('/roles/') && fileName === 'validation_code.bfy') {
197
+ return handleStateMachineRoleCode(this.api, filePath);
198
+ }
199
+ else if (normalizedPath.includes('/transitions/') && fileName === 'transition.json') {
200
+ return handleStateMachineTransitionJson(this.api, filePath);
201
+ }
202
+ else if (normalizedPath.includes('/transitions/') && fileName === 'action_code.bfy') {
203
+ return handleStateMachineTransitionCode(this.api, filePath, 'action_code');
204
+ }
205
+ else if (normalizedPath.includes('/transitions/') && fileName === 'validation_code.bfy') {
206
+ return handleStateMachineTransitionCode(this.api, filePath, 'validation_code');
207
+ }
208
+ else if (normalizedPath.includes('/actions/') && fileName === 'action.json') {
209
+ return handleStateMachineActionJson(this.api, filePath);
210
+ }
211
+ else if (normalizedPath.includes('/actions/') && fileName === 'code.bfy') {
212
+ return handleStateMachineActionCode(this.api, filePath);
213
+ }
214
+ else if (normalizedPath.includes('/transition_actions/') && fileName.endsWith('.json')) {
215
+ return handleTransitionActionJson(this.api, filePath);
216
+ }
217
+ else if (normalizedPath.includes('/specs/') && fileName === 'spec.json') {
218
+ return handleStateMachineSpecJson(this.api, filePath);
219
+ }
220
+ return { success: false, message: `Unsupported state machine file type: ${fileName}` };
221
+ }
222
+ async handleAiTaskFile(filePath, fileName) {
223
+ if (fileName === 'task.json') {
224
+ return handleAiTaskJson(this.api, filePath);
225
+ }
226
+ const fieldName = AI_TASK_FILE_MAPPINGS[fileName];
227
+ if (fieldName) {
228
+ return handleAiTaskCodeFile(this.api, filePath, fieldName);
229
+ }
230
+ return { success: false, message: `Unsupported AI task file type: ${fileName}` };
231
+ }
232
+ async handleReportFile(filePath, fileName, normalizedPath) {
233
+ if (fileName === 'report.json') {
234
+ return handleReportJson(this.api, filePath);
235
+ }
236
+ else if (normalizedPath.includes('/specs/') && fileName === 'spec.json') {
237
+ return handleReportSpecJson(this.api, filePath);
238
+ }
239
+ else if (normalizedPath.includes('/specs/') && fileName === 'code.js') {
240
+ return handleReportSpecCode(this.api, filePath);
241
+ }
242
+ else if (normalizedPath.includes('/queries/') && fileName === 'query.json') {
243
+ return handleReportQueryJson(this.api, filePath);
244
+ }
245
+ else if (normalizedPath.includes('/queries/') && fileName === 'query_code.bfy') {
246
+ return handleReportQueryCode(this.api, filePath, 'query');
247
+ }
248
+ else if (normalizedPath.includes('/queries/') && fileName === 'script.js') {
249
+ return handleReportQueryCode(this.api, filePath, 'js_code');
250
+ }
251
+ else if (fileName === 'main_query.bfy') {
252
+ return handleReportMainQuery(this.api, filePath);
253
+ }
254
+ return { success: false, message: `Unsupported report file type: ${fileName}` };
255
+ }
256
+ async handleTranslationFile(filePath, fileName) {
257
+ if (fileName.endsWith('.yaml') || fileName.endsWith('.yml')) {
258
+ return handleTranslationYaml(this.api, filePath);
259
+ }
260
+ return { success: false, message: `Unsupported translation file type: ${fileName}` };
261
+ }
262
+ }
263
+ //# sourceMappingURL=uploadHandler.js.map
package/docs/AI_API.md ADDED
@@ -0,0 +1,93 @@
1
+ # AI API Documentation
2
+
3
+ ## Endpoint
4
+
5
+ ```
6
+ POST /admin/ai/gpt/chat
7
+ ```
8
+
9
+ ## Parameters
10
+
11
+ | Parameter | Type | Description |
12
+ |-----------|------|-------------|
13
+ | `systemMessage` | string | AI instructions and output format |
14
+ | `previousMessages` | array | Conversation context (optional) |
15
+ | `task` | string | `bfy_ai_task` system_name (optional) |
16
+
17
+ ## JavaScript Usage
18
+
19
+ ### With Custom System Message
20
+
21
+ ```javascript
22
+ rg_confirm_ajax_with_prompt({
23
+ url: '/admin/ai/gpt/chat',
24
+ title: 'Dialog Title',
25
+ text: 'Input placeholder',
26
+ data: {
27
+ systemMessage: 'You are an API. Respond in JSON: {"field": "value"}',
28
+ previousMessages: []
29
+ },
30
+ callback: function(data) {
31
+ console.log(data.message.field);
32
+ }
33
+ });
34
+ ```
35
+
36
+ ### With Predefined Task
37
+
38
+ ```javascript
39
+ $.post('/admin/ai/gpt/chat', {
40
+ task: 'task-system-name',
41
+ prompt: 'User input'
42
+ }, function(data) {
43
+ if (data.success) {
44
+ console.log(data.message);
45
+ }
46
+ });
47
+ ```
48
+
49
+ ## Twig Usage (Server-Side)
50
+
51
+ ```twig
52
+ {% set response = ai()
53
+ .task('task-system-name')
54
+ .prompt('Generate something')
55
+ .execute()
56
+ %}
57
+
58
+ {# Or with custom system message #}
59
+ {% set response = ai()
60
+ .systemMessage('Respond in JSON.')
61
+ .prompt('User input')
62
+ .execute()
63
+ %}
64
+ ```
65
+
66
+ ## AI Task Structure (`bfy_ai_tasks`)
67
+
68
+ Tasks stored in `butterfly-resources/bfy_ai_tasks/[system_name]/`:
69
+
70
+ ```
71
+ [system-name]/
72
+ ├── task.json # Metadata
73
+ ├── system_message.bfy # AI instructions
74
+ ├── output_format.json # Expected response schema
75
+ ├── action_code.bfy # Post-response server action
76
+ └── action_preview_code.bfy # Preview template
77
+ ```
78
+
79
+ ### Key Task Fields
80
+
81
+ | Field | Description |
82
+ |-------|-------------|
83
+ | `system_name` | Unique identifier |
84
+ | `response_type` | `json` or `text` |
85
+ | `system_message` | AI instructions |
86
+ | `output_format` | JSON schema for response |
87
+
88
+ ## Helper Functions
89
+
90
+ | Function | Description |
91
+ |----------|-------------|
92
+ | `rg_confirm_ajax_with_prompt({...})` | Show prompt dialog + AJAX |
93
+ | `rG_Admin.getObjectSpec('field')` | Get field element |