@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,684 @@
1
+ import { restoreFieldsFromFiles, restoreAiTaskFromFiles, restoreTableRecordFromFiles } from './files.js';
2
+ import { smartReadBfyContent } from './bfySplitter.js';
3
+ import { promises as fs } from 'fs';
4
+ import { basename, dirname, join } from 'path';
5
+ import { parse as yamlParse } from 'yaml';
6
+ export async function updateRecord(api, tableName, data) {
7
+ const formData = new URLSearchParams();
8
+ formData.append('id', data.id.toString());
9
+ Object.entries(data).forEach(([key, value]) => {
10
+ if (key !== 'id' && value !== null && value !== undefined) {
11
+ formData.append(key, typeof value === 'object' ? JSON.stringify(value) : String(value));
12
+ }
13
+ });
14
+ const response = await api.httpClient.post(`/admin/ajax/cms_object/operation?do=${tableName}__edit`, formData, {
15
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
16
+ });
17
+ if (response.data?.success) {
18
+ return { success: true, message: `Updated ${tableName} (ID: ${data.id})`, resourceId: data.id };
19
+ }
20
+ else {
21
+ const errorMsg = response.data?.error || response.data?.message || 'Unknown error';
22
+ return { success: false, message: `Failed to update ${tableName}: ${errorMsg}` };
23
+ }
24
+ }
25
+ export async function updateRecordField(api, tableName, id, fieldName, fieldValue) {
26
+ const formData = new URLSearchParams();
27
+ formData.append('id', id.toString());
28
+ formData.append(fieldName, fieldValue);
29
+ const response = await api.httpClient.post(`/admin/ajax/cms_object/operation?do=${tableName}__edit`, formData, {
30
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
31
+ });
32
+ if (response.data?.success) {
33
+ return { success: true, message: `Updated ${tableName}.${fieldName} (ID: ${id})`, resourceId: id };
34
+ }
35
+ else {
36
+ const errorMsg = response.data?.error || response.data?.message || 'Unknown error';
37
+ return { success: false, message: `Failed to update ${tableName}.${fieldName}: ${errorMsg}` };
38
+ }
39
+ }
40
+ export async function updateRecordFields(api, tableName, id, fields) {
41
+ const formData = new URLSearchParams();
42
+ formData.append('id', id.toString());
43
+ Object.entries(fields).forEach(([key, value]) => {
44
+ if (value !== undefined) {
45
+ formData.append(key, value);
46
+ }
47
+ });
48
+ const response = await api.httpClient.post(`/admin/ajax/cms_object/operation?do=${tableName}__edit`, formData, {
49
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
50
+ });
51
+ if (response.data?.success) {
52
+ return { success: true, message: `Updated ${tableName} (ID: ${id})`, resourceId: id };
53
+ }
54
+ else {
55
+ const errorMsg = response.data?.error || response.data?.message || 'Unknown error';
56
+ return { success: false, message: `Failed to update ${tableName}: ${errorMsg}` };
57
+ }
58
+ }
59
+ export async function handleWorkflowJson(api, filePath) {
60
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
61
+ return updateRecord(api, 'bfy_workflows', data);
62
+ }
63
+ export async function handleWorkflowVersionJson(api, filePath) {
64
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
65
+ return updateRecord(api, 'bfy_workflow_versions', data);
66
+ }
67
+ export async function handleWorkflowNodeJson(api, filePath) {
68
+ const nodeData = JSON.parse(await fs.readFile(filePath, 'utf-8'));
69
+ const fileDir = dirname(filePath);
70
+ let codeContent = null;
71
+ try {
72
+ codeContent = await smartReadBfyContent(join(fileDir, 'code.bfy'));
73
+ }
74
+ catch { }
75
+ let paramsData = null;
76
+ try {
77
+ paramsData = yamlParse(await fs.readFile(join(fileDir, 'params.yaml'), 'utf-8')) || {};
78
+ }
79
+ catch { }
80
+ const formData = new URLSearchParams();
81
+ formData.append('id', nodeData.id.toString());
82
+ Object.entries(nodeData).forEach(([key, value]) => {
83
+ if (key !== 'id' && value !== null && value !== undefined) {
84
+ formData.append(key, String(value));
85
+ }
86
+ });
87
+ if (paramsData !== null) {
88
+ if (codeContent !== null)
89
+ paramsData.code = codeContent;
90
+ formData.append('params', JSON.stringify(paramsData));
91
+ }
92
+ else if (codeContent !== null) {
93
+ formData.append('code', codeContent);
94
+ }
95
+ const response = await api.httpClient.post('/admin/ajax/cms_object/operation?do=bfy_workflow_nodes__edit', formData, {
96
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
97
+ });
98
+ if (response.data?.success) {
99
+ return { success: true, message: `Updated workflow node (ID: ${nodeData.id})`, resourceId: nodeData.id };
100
+ }
101
+ else {
102
+ const errorMsg = response.data?.error || response.data?.message || 'Unknown error';
103
+ return { success: false, message: `Failed to update workflow node: ${errorMsg}` };
104
+ }
105
+ }
106
+ export async function handleWorkflowNodeCode(api, filePath) {
107
+ const nodeData = JSON.parse(await fs.readFile(join(dirname(filePath), 'node.json'), 'utf-8'));
108
+ const codeContent = await smartReadBfyContent(filePath);
109
+ const paramsYamlPath = join(dirname(filePath), 'params.yaml');
110
+ let hasParamsFile = false;
111
+ try {
112
+ await fs.access(paramsYamlPath);
113
+ hasParamsFile = true;
114
+ }
115
+ catch { }
116
+ const formData = new URLSearchParams();
117
+ formData.append('id', nodeData.id.toString());
118
+ if (hasParamsFile) {
119
+ const paramsData = yamlParse(await fs.readFile(paramsYamlPath, 'utf-8')) || {};
120
+ paramsData.code = codeContent;
121
+ formData.append('params', JSON.stringify(paramsData));
122
+ }
123
+ else {
124
+ formData.append('code', codeContent);
125
+ }
126
+ const response = await api.httpClient.post('/admin/ajax/cms_object/operation?do=bfy_workflow_nodes__edit', formData, {
127
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
128
+ });
129
+ if (response.data?.success) {
130
+ return { success: true, message: `Updated workflow node code (ID: ${nodeData.id})`, resourceId: nodeData.id };
131
+ }
132
+ else {
133
+ const errorMsg = response.data?.error || response.data?.message || 'Unknown error';
134
+ return { success: false, message: `Failed to update workflow node code: ${errorMsg}` };
135
+ }
136
+ }
137
+ export async function handleWorkflowNodeParams(api, filePath) {
138
+ const nodeData = JSON.parse(await fs.readFile(join(dirname(filePath), 'node.json'), 'utf-8'));
139
+ const paramsData = yamlParse(await fs.readFile(filePath, 'utf-8')) || {};
140
+ try {
141
+ paramsData.code = await smartReadBfyContent(join(dirname(filePath), 'code.bfy'));
142
+ }
143
+ catch { }
144
+ const formData = new URLSearchParams();
145
+ formData.append('id', nodeData.id.toString());
146
+ formData.append('params', JSON.stringify(paramsData));
147
+ const response = await api.httpClient.post('/admin/ajax/cms_object/operation?do=bfy_workflow_nodes__edit', formData, {
148
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
149
+ });
150
+ if (response.data?.success) {
151
+ return { success: true, message: `Updated workflow node params (ID: ${nodeData.id})`, resourceId: nodeData.id };
152
+ }
153
+ else {
154
+ const errorMsg = response.data?.error || response.data?.message || 'Unknown error';
155
+ return { success: false, message: `Failed to update workflow node params: ${errorMsg}` };
156
+ }
157
+ }
158
+ export async function handleWorkflowConnections(api, filePath) {
159
+ try {
160
+ const fileConnections = JSON.parse(await fs.readFile(filePath, 'utf-8'));
161
+ if (!Array.isArray(fileConnections)) {
162
+ return { success: false, message: 'connections.json must contain an array of connections' };
163
+ }
164
+ const pathParts = filePath.split('/');
165
+ const versionDir = pathParts[pathParts.length - 2];
166
+ if (!versionDir || !versionDir.startsWith('v')) {
167
+ return { success: false, message: 'Could not determine workflow version from path' };
168
+ }
169
+ const versionJsonPath = join(dirname(filePath), 'version.json');
170
+ let versionData;
171
+ try {
172
+ versionData = JSON.parse(await fs.readFile(versionJsonPath, 'utf-8'));
173
+ }
174
+ catch {
175
+ return { success: false, message: 'version.json not found in same directory as connections.json' };
176
+ }
177
+ const workflowVersionId = versionData.id;
178
+ if (!workflowVersionId) {
179
+ return { success: false, message: 'workflow_version_id not found in version.json' };
180
+ }
181
+ const dbConnections = await api.fetchTable('bfy_workflow_connections', 'workflow_version_id', workflowVersionId.toString());
182
+ for (const fileConn of fileConnections) {
183
+ if (fileConn.id) {
184
+ const matchingDbConn = dbConnections.find(db => db.id === fileConn.id);
185
+ if (matchingDbConn && matchingDbConn.workflow_version_id !== workflowVersionId) {
186
+ return {
187
+ success: false,
188
+ message: `Connection ID ${fileConn.id} belongs to a different workflow version (${matchingDbConn.workflow_version_id}). Cannot proceed - file may be from wrong workflow.`
189
+ };
190
+ }
191
+ }
192
+ }
193
+ const workflowNodes = await api.fetchTable('bfy_workflow_nodes', 'workflow_version_id', workflowVersionId.toString());
194
+ const validNodeIds = new Set(workflowNodes.map((n) => n.id));
195
+ for (const fileConn of fileConnections) {
196
+ if (fileConn.source_node_id && !validNodeIds.has(fileConn.source_node_id)) {
197
+ return {
198
+ success: false,
199
+ message: `Invalid source_node_id ${fileConn.source_node_id} in connections.json - node does not exist in this workflow version`
200
+ };
201
+ }
202
+ if (fileConn.target_node_id && !validNodeIds.has(fileConn.target_node_id)) {
203
+ return {
204
+ success: false,
205
+ message: `Invalid target_node_id ${fileConn.target_node_id} in connections.json - node does not exist in this workflow version`
206
+ };
207
+ }
208
+ }
209
+ const dbConnectionMap = new Map();
210
+ dbConnections.forEach(conn => dbConnectionMap.set(conn.id, conn));
211
+ const fileConnectionMap = new Map();
212
+ fileConnections.forEach(conn => {
213
+ if (conn.id) {
214
+ fileConnectionMap.set(conn.id, conn);
215
+ }
216
+ });
217
+ const connectionsToDelete = Array.from(dbConnectionMap.keys()).filter(dbId => !fileConnectionMap.has(dbId));
218
+ if (dbConnections.length > 2 && connectionsToDelete.length > dbConnections.length * 0.5) {
219
+ return {
220
+ success: false,
221
+ message: `Safety check failed: ${connectionsToDelete.length} of ${dbConnections.length} connections would be deleted (>${(connectionsToDelete.length / dbConnections.length * 100).toFixed(0)}%). This may indicate an incomplete connections.json file. If this is intentional, manually delete connections first, then upload the file.`
222
+ };
223
+ }
224
+ const operations = {
225
+ created: 0,
226
+ updated: 0,
227
+ deleted: 0,
228
+ skipped: 0,
229
+ errors: []
230
+ };
231
+ for (const dbId of connectionsToDelete) {
232
+ try {
233
+ const result = await api.deleteWorkflowConnection(dbId);
234
+ if (result?.success) {
235
+ operations.deleted++;
236
+ }
237
+ else {
238
+ operations.errors.push(`Failed to delete connection ${dbId}: ${result?.error || 'Unknown error'}`);
239
+ }
240
+ }
241
+ catch (error) {
242
+ operations.errors.push(`Error deleting connection ${dbId}: ${error.message}`);
243
+ }
244
+ }
245
+ for (const fileConn of fileConnections) {
246
+ if (!fileConn.id) {
247
+ try {
248
+ const createData = {
249
+ workflow_version_id: workflowVersionId,
250
+ source_node_id: fileConn.source_node_id,
251
+ target_node_id: fileConn.target_node_id,
252
+ source_output_key: fileConn.source_output_key || '',
253
+ target_input_key: fileConn.target_input_key || '',
254
+ connection_type: fileConn.connection_type || 'default'
255
+ };
256
+ const result = await api.createWorkflowConnection(createData);
257
+ if (result?.success) {
258
+ operations.created++;
259
+ fileConn.id = result.id;
260
+ }
261
+ else {
262
+ operations.errors.push(`Failed to create connection: ${result?.error || 'Unknown error'}`);
263
+ }
264
+ }
265
+ catch (error) {
266
+ operations.errors.push(`Error creating connection: ${error.message}`);
267
+ }
268
+ }
269
+ else {
270
+ const dbConn = dbConnectionMap.get(fileConn.id);
271
+ if (!dbConn) {
272
+ operations.errors.push(`Connection ID ${fileConn.id} not found in database - may have been deleted externally`);
273
+ continue;
274
+ }
275
+ if (dbConn.workflow_version_id !== workflowVersionId) {
276
+ operations.errors.push(`Connection ID ${fileConn.id} belongs to different workflow version ${dbConn.workflow_version_id}`);
277
+ continue;
278
+ }
279
+ const hasChanged = fileConn.source_node_id !== dbConn.source_node_id ||
280
+ fileConn.target_node_id !== dbConn.target_node_id ||
281
+ (fileConn.source_output_key || '') !== (dbConn.source_output_key || '') ||
282
+ (fileConn.target_input_key || '') !== (dbConn.target_input_key || '') ||
283
+ (fileConn.connection_type || 'default') !== (dbConn.connection_type || 'default');
284
+ if (hasChanged) {
285
+ try {
286
+ const updateData = {
287
+ source_node_id: fileConn.source_node_id,
288
+ target_node_id: fileConn.target_node_id,
289
+ source_output_key: fileConn.source_output_key || '',
290
+ target_input_key: fileConn.target_input_key || '',
291
+ connection_type: fileConn.connection_type || 'default'
292
+ };
293
+ const result = await api.updateWorkflowConnection(fileConn.id, updateData);
294
+ if (result?.success) {
295
+ operations.updated++;
296
+ }
297
+ else {
298
+ operations.errors.push(`Failed to update connection ${fileConn.id}: ${result?.error || 'Unknown error'}`);
299
+ }
300
+ }
301
+ catch (error) {
302
+ operations.errors.push(`Error updating connection ${fileConn.id}: ${error.message}`);
303
+ }
304
+ }
305
+ else {
306
+ operations.skipped++;
307
+ }
308
+ }
309
+ }
310
+ if (operations.created > 0 && operations.errors.length === 0) {
311
+ try {
312
+ const backupPath = filePath + '.backup';
313
+ await fs.copyFile(filePath, backupPath);
314
+ await fs.writeFile(filePath, JSON.stringify(fileConnections, null, 2), 'utf-8');
315
+ await fs.unlink(backupPath);
316
+ }
317
+ catch (error) {
318
+ operations.errors.push(`Failed to update connections.json with new IDs: ${error.message}`);
319
+ }
320
+ }
321
+ const messageParts = [];
322
+ if (operations.created > 0)
323
+ messageParts.push(`${operations.created} created`);
324
+ if (operations.updated > 0)
325
+ messageParts.push(`${operations.updated} updated`);
326
+ if (operations.deleted > 0)
327
+ messageParts.push(`${operations.deleted} deleted`);
328
+ if (operations.skipped > 0)
329
+ messageParts.push(`${operations.skipped} unchanged`);
330
+ const message = messageParts.length > 0
331
+ ? `Synced workflow connections (${messageParts.join(', ')})`
332
+ : 'No connection changes detected';
333
+ if (operations.errors.length > 0) {
334
+ const hasSuccesses = operations.created > 0 || operations.updated > 0 || operations.deleted > 0;
335
+ return {
336
+ success: !hasSuccesses,
337
+ message: `${message} - Errors encountered: ${operations.errors.join('; ')}`
338
+ };
339
+ }
340
+ return { success: true, message };
341
+ }
342
+ catch (error) {
343
+ return { success: false, message: `Failed to sync connections: ${error.message}` };
344
+ }
345
+ }
346
+ export async function handleWebserviceJson(api, filePath) {
347
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
348
+ const restoredData = await restoreTableRecordFromFiles(dirname(filePath), 'webservices', data);
349
+ return updateRecord(api, 'webservices', restoredData);
350
+ }
351
+ export async function handleWebservicePageCode(api, filePath) {
352
+ const webserviceData = JSON.parse(await fs.readFile(join(dirname(filePath), 'webservice.json'), 'utf-8'));
353
+ const pageCode = await smartReadBfyContent(filePath);
354
+ return updateRecordField(api, 'webservices', webserviceData.id, 'page_code', pageCode);
355
+ }
356
+ export async function handlePdfTemplateJson(api, filePath) {
357
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
358
+ const restoredData = await restoreTableRecordFromFiles(dirname(filePath), 'pdf_templates', data);
359
+ return updateRecord(api, 'pdf_templates', restoredData);
360
+ }
361
+ export async function handlePdfTemplateTwig(api, filePath) {
362
+ const templateData = JSON.parse(await fs.readFile(join(dirname(filePath), 'pdf_template.json'), 'utf-8'));
363
+ const restoredData = await restoreTableRecordFromFiles(dirname(filePath), 'pdf_templates', templateData);
364
+ return updateRecordField(api, 'pdf_templates', restoredData.id, 'template', restoredData.template || '');
365
+ }
366
+ export async function handleCronjobJson(api, filePath) {
367
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
368
+ const restoredData = await restoreTableRecordFromFiles(dirname(filePath), 'bfy_cronjobs', data);
369
+ return updateRecord(api, 'bfy_cronjobs', restoredData);
370
+ }
371
+ export async function handleCronjobCode(api, filePath) {
372
+ const cronjobData = JSON.parse(await fs.readFile(join(dirname(filePath), 'cronjob.json'), 'utf-8'));
373
+ const restoredData = await restoreTableRecordFromFiles(dirname(filePath), 'bfy_cronjobs', cronjobData);
374
+ return updateRecordField(api, 'bfy_cronjobs', restoredData.id, 'code', restoredData.code || '');
375
+ }
376
+ export async function handleEmailTemplateJson(api, filePath) {
377
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
378
+ const restoredData = await restoreTableRecordFromFiles(dirname(filePath), 'cms_email_templates', data);
379
+ return updateRecord(api, 'cms_email_templates', restoredData);
380
+ }
381
+ export async function handleEmailTemplateBfy(api, filePath) {
382
+ const templateData = JSON.parse(await fs.readFile(join(dirname(filePath), 'template.json'), 'utf-8'));
383
+ const restoredData = await restoreTableRecordFromFiles(dirname(filePath), 'cms_email_templates', templateData);
384
+ return updateRecordFields(api, 'cms_email_templates', restoredData.id, {
385
+ subject: restoredData.subject,
386
+ content: restoredData.content
387
+ });
388
+ }
389
+ export async function handleEmailLayoutJson(api, filePath) {
390
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
391
+ const restoredData = await restoreTableRecordFromFiles(dirname(filePath), 'cms_email_layouts', data);
392
+ return updateRecord(api, 'cms_email_layouts', restoredData);
393
+ }
394
+ export async function handleEmailLayoutBfy(api, filePath) {
395
+ const layoutData = JSON.parse(await fs.readFile(join(dirname(filePath), 'layout.json'), 'utf-8'));
396
+ const restoredData = await restoreTableRecordFromFiles(dirname(filePath), 'cms_email_layouts', layoutData);
397
+ return updateRecordFields(api, 'cms_email_layouts', restoredData.id, {
398
+ layout_html: restoredData.layout_html,
399
+ layout_text: restoredData.layout_text
400
+ });
401
+ }
402
+ export async function handleObjectJson(api, filePath) {
403
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
404
+ return updateRecord(api, 'objects', data);
405
+ }
406
+ export async function handleObjectSpecJson(api, filePath) {
407
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
408
+ const restoredData = await restoreFieldsFromFiles(dirname(filePath), data);
409
+ return updateRecord(api, 'object_specs', restoredData);
410
+ }
411
+ export async function handleObjectListingQuery(api, filePath) {
412
+ const objectData = JSON.parse(await fs.readFile(join(dirname(filePath), 'object.json'), 'utf-8'));
413
+ const listingQueryContent = await smartReadBfyContent(filePath);
414
+ return updateRecordField(api, 'objects', objectData.id, 'listing_query', listingQueryContent);
415
+ }
416
+ export async function handleObjectSpecCodeFile(api, filePath) {
417
+ const fileName = basename(filePath);
418
+ const specDir = dirname(filePath);
419
+ const specData = JSON.parse(await fs.readFile(join(specDir, 'spec.json'), 'utf-8'));
420
+ let columnName;
421
+ let content;
422
+ if (fileName === 'code.bfy') {
423
+ columnName = 'val_1';
424
+ content = await smartReadBfyContent(filePath);
425
+ }
426
+ else if (fileName === 'template_code.bfy') {
427
+ columnName = 'val_1';
428
+ content = await smartReadBfyContent(filePath);
429
+ }
430
+ else if (fileName === 'processing_code.bfy') {
431
+ columnName = 'val_2';
432
+ content = await smartReadBfyContent(filePath);
433
+ }
434
+ else if (fileName === 'filter_code.bfy') {
435
+ columnName = 'val_3';
436
+ content = await smartReadBfyContent(filePath);
437
+ }
438
+ else if (fileName === 'configuration.yaml') {
439
+ columnName = 'val_1';
440
+ content = await fs.readFile(filePath, 'utf-8');
441
+ }
442
+ else if (fileName === 'code.js') {
443
+ columnName = 'js_code';
444
+ content = await fs.readFile(filePath, 'utf-8');
445
+ }
446
+ else if (fileName === 'style.css') {
447
+ columnName = 'css_code';
448
+ content = await fs.readFile(filePath, 'utf-8');
449
+ }
450
+ else {
451
+ return { success: false, message: `Unsupported code file: ${fileName}` };
452
+ }
453
+ return updateRecordField(api, 'object_specs', specData.id, columnName, content);
454
+ }
455
+ export async function handlePageJson(api, filePath) {
456
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
457
+ const restoredData = await restoreTableRecordFromFiles(dirname(filePath), 'pages', data);
458
+ return updateRecord(api, 'pages', restoredData);
459
+ }
460
+ export async function handleStateMachineJson(api, filePath) {
461
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
462
+ return updateRecord(api, 'bfy_state_machines', data);
463
+ }
464
+ export async function handleStateMachineStateJson(api, filePath) {
465
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
466
+ return updateRecord(api, 'bfy_state_machine_states', data);
467
+ }
468
+ export async function handleStateMachineRoleJson(api, filePath) {
469
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
470
+ const restoredData = await restoreTableRecordFromFiles(dirname(filePath), 'bfy_state_machine_roles', data);
471
+ return updateRecord(api, 'bfy_state_machine_roles', restoredData);
472
+ }
473
+ export async function handleStateMachineRoleCode(api, filePath) {
474
+ const roleData = JSON.parse(await fs.readFile(join(dirname(filePath), 'role.json'), 'utf-8'));
475
+ const restoredData = await restoreTableRecordFromFiles(dirname(filePath), 'bfy_state_machine_roles', roleData);
476
+ return updateRecordField(api, 'bfy_state_machine_roles', restoredData.id, 'validation_code', restoredData.validation_code || '');
477
+ }
478
+ export async function handleStateMachineTransitionJson(api, filePath) {
479
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
480
+ const restoredData = await restoreTableRecordFromFiles(dirname(filePath), 'bfy_state_machine_transitions', data);
481
+ return updateRecord(api, 'bfy_state_machine_transitions', restoredData);
482
+ }
483
+ export async function handleStateMachineTransitionCode(api, filePath, fieldName) {
484
+ const transitionData = JSON.parse(await fs.readFile(join(dirname(filePath), 'transition.json'), 'utf-8'));
485
+ const restoredData = await restoreTableRecordFromFiles(dirname(filePath), 'bfy_state_machine_transitions', transitionData);
486
+ return updateRecordField(api, 'bfy_state_machine_transitions', restoredData.id, fieldName, restoredData[fieldName] || '');
487
+ }
488
+ export async function handleStateMachineActionJson(api, filePath) {
489
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
490
+ const restoredData = await restoreTableRecordFromFiles(dirname(filePath), 'bfy_sm_actions', data);
491
+ return updateRecord(api, 'bfy_sm_actions', restoredData);
492
+ }
493
+ export async function handleStateMachineActionCode(api, filePath) {
494
+ const actionData = JSON.parse(await fs.readFile(join(dirname(filePath), 'action.json'), 'utf-8'));
495
+ const restoredData = await restoreTableRecordFromFiles(dirname(filePath), 'bfy_sm_actions', actionData);
496
+ return updateRecordField(api, 'bfy_sm_actions', restoredData.id, 'code', restoredData.code || '');
497
+ }
498
+ export async function handleTransitionActionJson(api, filePath) {
499
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
500
+ return updateRecord(api, 'bfy_sm_transition_actions', data);
501
+ }
502
+ export async function handleStateMachineSpecJson(api, filePath) {
503
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
504
+ const restoredData = await restoreFieldsFromFiles(dirname(filePath), data);
505
+ let tableName = '';
506
+ if (filePath.includes('/transitions/')) {
507
+ tableName = 'bfy_state_machine_transition_specs';
508
+ }
509
+ else if (filePath.includes('/actions/')) {
510
+ tableName = 'bfy_sm_action_specs';
511
+ }
512
+ else {
513
+ return { success: false, message: 'Unknown spec type' };
514
+ }
515
+ return updateRecord(api, tableName, restoredData);
516
+ }
517
+ export async function handleAiTaskJson(api, filePath) {
518
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
519
+ const restoredData = await restoreAiTaskFromFiles(dirname(filePath), data);
520
+ const formData = new URLSearchParams();
521
+ formData.append('id', restoredData.id.toString());
522
+ Object.entries(restoredData).forEach(([key, value]) => {
523
+ if (key !== 'id' && value !== null && value !== undefined) {
524
+ formData.append(key, typeof value === 'string' ? value : JSON.stringify(value));
525
+ }
526
+ });
527
+ const response = await api.httpClient.post('/admin/ajax/cms_object/operation?do=bfy_ai_tasks__edit', formData, {
528
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
529
+ });
530
+ if (response.data?.success) {
531
+ return { success: true, message: `Updated AI task (ID: ${restoredData.id})`, resourceId: restoredData.id };
532
+ }
533
+ else {
534
+ const errorMsg = response.data?.error || response.data?.message || 'Unknown error';
535
+ return { success: false, message: `Failed to update AI task: ${errorMsg}` };
536
+ }
537
+ }
538
+ export async function handleAiTaskCodeFile(api, filePath, fieldName) {
539
+ const taskData = JSON.parse(await fs.readFile(join(dirname(filePath), 'task.json'), 'utf-8'));
540
+ let fieldValue;
541
+ if (basename(filePath).endsWith('.json')) {
542
+ const content = await fs.readFile(filePath, 'utf-8');
543
+ fieldValue = JSON.stringify(JSON.parse(content));
544
+ }
545
+ else if (basename(filePath).endsWith('.bfy')) {
546
+ fieldValue = await smartReadBfyContent(filePath);
547
+ }
548
+ else {
549
+ fieldValue = await fs.readFile(filePath, 'utf-8');
550
+ }
551
+ return updateRecordField(api, 'bfy_ai_tasks', taskData.id, fieldName, fieldValue);
552
+ }
553
+ export async function handleReportJson(api, filePath) {
554
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
555
+ const restoredData = await restoreFieldsFromFiles(dirname(filePath), data);
556
+ const response = await api.updateReport(restoredData.id, restoredData);
557
+ if (response?.success) {
558
+ return { success: true, message: `Updated report (ID: ${restoredData.id})`, resourceId: restoredData.id };
559
+ }
560
+ else {
561
+ const errorMsg = response?.error || response?.message || 'Unknown error';
562
+ return { success: false, message: `Failed to update report: ${errorMsg}` };
563
+ }
564
+ }
565
+ export async function handleReportSpecJson(api, filePath) {
566
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
567
+ const restoredData = await restoreFieldsFromFiles(dirname(filePath), data);
568
+ const response = await api.updateReportSpec(restoredData.id, restoredData);
569
+ if (response?.success) {
570
+ return { success: true, message: `Updated report spec (ID: ${restoredData.id})`, resourceId: restoredData.id };
571
+ }
572
+ else {
573
+ const errorMsg = response?.error || response?.message || 'Unknown error';
574
+ return { success: false, message: `Failed to update report spec: ${errorMsg}` };
575
+ }
576
+ }
577
+ export async function handleReportSpecCode(api, filePath) {
578
+ const specData = JSON.parse(await fs.readFile(join(dirname(filePath), 'spec.json'), 'utf-8'));
579
+ const codeContent = await fs.readFile(filePath, 'utf-8');
580
+ const response = await api.updateReportSpec(specData.id, { js_code: codeContent });
581
+ if (response?.success) {
582
+ return { success: true, message: `Updated report spec js_code (ID: ${specData.id})`, resourceId: specData.id };
583
+ }
584
+ else {
585
+ const errorMsg = response?.error || response?.message || 'Unknown error';
586
+ return { success: false, message: `Failed to update report spec js_code: ${errorMsg}` };
587
+ }
588
+ }
589
+ export async function handleReportQueryJson(api, filePath) {
590
+ const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
591
+ const restoredData = await restoreFieldsFromFiles(dirname(filePath), data);
592
+ const response = await api.updateReportQuery(restoredData.id, restoredData);
593
+ if (response?.success) {
594
+ return { success: true, message: `Updated report query (ID: ${restoredData.id})`, resourceId: restoredData.id };
595
+ }
596
+ else {
597
+ const errorMsg = response?.error || response?.message || 'Unknown error';
598
+ return { success: false, message: `Failed to update report query: ${errorMsg}` };
599
+ }
600
+ }
601
+ export async function handleReportQueryCode(api, filePath, field) {
602
+ const queryData = JSON.parse(await fs.readFile(join(dirname(filePath), 'query.json'), 'utf-8'));
603
+ const codeContent = filePath.endsWith('.bfy') ? await smartReadBfyContent(filePath) : await fs.readFile(filePath, 'utf-8');
604
+ if (process.env.BUTTERFLY_DEBUG === 'true') {
605
+ console.log(`[DEBUG] handleReportQueryCode: Updating query ID ${queryData.id}, field: ${field}, content length: ${codeContent.length}`);
606
+ }
607
+ const response = await api.updateReportQuery(queryData.id, { [field]: codeContent });
608
+ if (process.env.BUTTERFLY_DEBUG === 'true') {
609
+ console.log('[DEBUG] handleReportQueryCode response:', JSON.stringify(response, null, 2));
610
+ }
611
+ if (response?.success) {
612
+ return { success: true, message: `Updated report query ${field} (ID: ${queryData.id})`, resourceId: queryData.id };
613
+ }
614
+ else {
615
+ const errorMsg = response?.error || response?.message || 'Unknown error';
616
+ return { success: false, message: `Failed to update report query ${field}: ${errorMsg}` };
617
+ }
618
+ }
619
+ export async function handleReportMainQuery(api, filePath) {
620
+ const reportData = JSON.parse(await fs.readFile(join(dirname(filePath), 'report.json'), 'utf-8'));
621
+ const queryContent = await smartReadBfyContent(filePath);
622
+ const response = await api.updateReport(reportData.id, { query: queryContent });
623
+ if (response?.success) {
624
+ return { success: true, message: `Updated report main query (ID: ${reportData.id})`, resourceId: reportData.id };
625
+ }
626
+ else {
627
+ const errorMsg = response?.error || response?.message || 'Unknown error';
628
+ return { success: false, message: `Failed to update report main query: ${errorMsg}` };
629
+ }
630
+ }
631
+ export async function handleTranslationYaml(api, filePath) {
632
+ const isoCode = basename(filePath).replace(/\.(yaml|yml)$/, '');
633
+ const content = await fs.readFile(filePath, 'utf-8');
634
+ const translations = yamlParse(content);
635
+ if (!translations || typeof translations !== 'object') {
636
+ return { success: false, message: `Invalid YAML content in ${filePath}` };
637
+ }
638
+ const languages = await api.fetchTable('bfy_languages', 'iso_code', isoCode);
639
+ if (!languages || languages.length === 0) {
640
+ return { success: false, message: `Language not found for ISO code: ${isoCode}` };
641
+ }
642
+ const language = languages[0];
643
+ const sourceTexts = await api.fetchTable('bfy_translation_texts');
644
+ const sourceTextMap = new Map();
645
+ sourceTexts.forEach((t) => sourceTextMap.set(t.source, t.id));
646
+ const translationEntries = [];
647
+ let newSourceTexts = 0;
648
+ for (const [source, translated] of Object.entries(translations)) {
649
+ let textId = sourceTextMap.get(source);
650
+ if (textId === undefined) {
651
+ const result = await api.performOperation('bfy_translation_texts', 'add', { source });
652
+ if (result.success && result.id) {
653
+ textId = result.id;
654
+ sourceTextMap.set(source, textId);
655
+ newSourceTexts++;
656
+ }
657
+ else {
658
+ continue;
659
+ }
660
+ }
661
+ translationEntries.push({ textId, text: translated || '' });
662
+ }
663
+ const formData = new URLSearchParams();
664
+ formData.append('id', language.id.toString());
665
+ formData.append('name', language.name);
666
+ formData.append('iso_code', language.iso_code);
667
+ formData.append('translations', '1');
668
+ for (const entry of translationEntries) {
669
+ formData.append(`translation[${entry.textId}]`, entry.text);
670
+ }
671
+ const response = await api.httpClient.post('/admin/ajax/cms_object/operation?do=bfy_languages__edit', formData, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } });
672
+ if (response.data?.success) {
673
+ const count = translationEntries.length;
674
+ const message = newSourceTexts > 0
675
+ ? `Updated ${count} translations for ${language.name} (${isoCode}), created ${newSourceTexts} new source texts`
676
+ : `Updated ${count} translations for ${language.name} (${isoCode})`;
677
+ return { success: true, message, resourceId: language.id };
678
+ }
679
+ else {
680
+ const errorMsg = response.data?.error || response.data?.message || 'Unknown error';
681
+ return { success: false, message: `Failed to update translations for ${isoCode}: ${errorMsg}` };
682
+ }
683
+ }
684
+ //# sourceMappingURL=resourceHandlers.js.map