@magentrix-corp/magentrix-cli 1.3.16 → 1.3.17
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.
- package/LICENSE +25 -25
- package/README.md +1166 -1166
- package/actions/autopublish.old.js +293 -293
- package/actions/config.js +182 -182
- package/actions/create.js +466 -466
- package/actions/help.js +164 -164
- package/actions/iris/buildStage.js +874 -874
- package/actions/iris/delete.js +256 -256
- package/actions/iris/dev.js +391 -391
- package/actions/iris/index.js +6 -6
- package/actions/iris/link.js +375 -375
- package/actions/iris/recover.js +268 -268
- package/actions/main.js +80 -80
- package/actions/publish.js +1420 -1420
- package/actions/pull.js +684 -684
- package/actions/setup.js +148 -148
- package/actions/status.js +17 -17
- package/actions/update.js +248 -248
- package/bin/magentrix.js +393 -393
- package/package.json +55 -55
- package/utils/assetPaths.js +158 -158
- package/utils/autopublishLock.js +77 -77
- package/utils/cacher.js +206 -206
- package/utils/cli/checkInstanceUrl.js +76 -74
- package/utils/cli/helpers/compare.js +282 -282
- package/utils/cli/helpers/ensureApiKey.js +63 -63
- package/utils/cli/helpers/ensureCredentials.js +68 -68
- package/utils/cli/helpers/ensureInstanceUrl.js +75 -75
- package/utils/cli/writeRecords.js +262 -262
- package/utils/compare.js +135 -135
- package/utils/compress.js +17 -17
- package/utils/config.js +527 -527
- package/utils/debug.js +144 -144
- package/utils/diagnostics/testPublishLogic.js +96 -96
- package/utils/diff.js +49 -49
- package/utils/downloadAssets.js +291 -291
- package/utils/filetag.js +115 -115
- package/utils/hash.js +14 -14
- package/utils/iris/backup.js +411 -411
- package/utils/iris/builder.js +541 -541
- package/utils/iris/config-reader.js +664 -664
- package/utils/iris/deleteHelper.js +150 -150
- package/utils/iris/errors.js +537 -537
- package/utils/iris/linker.js +601 -601
- package/utils/iris/lock.js +360 -360
- package/utils/iris/validation.js +360 -360
- package/utils/iris/validator.js +281 -281
- package/utils/iris/zipper.js +248 -248
- package/utils/logger.js +291 -291
- package/utils/magentrix/api/assets.js +220 -220
- package/utils/magentrix/api/auth.js +107 -107
- package/utils/magentrix/api/createEntity.js +61 -61
- package/utils/magentrix/api/deleteEntity.js +55 -55
- package/utils/magentrix/api/iris.js +251 -251
- package/utils/magentrix/api/meqlQuery.js +36 -36
- package/utils/magentrix/api/retrieveEntity.js +86 -86
- package/utils/magentrix/api/updateEntity.js +66 -66
- package/utils/magentrix/fetch.js +168 -168
- package/utils/merge.js +22 -22
- package/utils/permissionError.js +70 -70
- package/utils/preferences.js +40 -40
- package/utils/progress.js +469 -469
- package/utils/spinner.js +43 -43
- package/utils/template.js +52 -52
- package/utils/updateFileBase.js +121 -121
- package/utils/workspaces.js +108 -108
- package/vars/config.js +11 -11
- package/vars/global.js +50 -50
|
@@ -1,293 +1,293 @@
|
|
|
1
|
-
import chokidar from 'chokidar';
|
|
2
|
-
import { ensureValidCredentials } from '../utils/cli/helpers/ensureCredentials.js';
|
|
3
|
-
import { withSpinner } from '../utils/spinner.js';
|
|
4
|
-
import { ENTITY_FIELD_MAP, ENTITY_TYPE_MAP, TYPE_DIR_MAP } from '../vars/global.js';
|
|
5
|
-
import fs from 'fs';
|
|
6
|
-
import Config from '../utils/config.js';
|
|
7
|
-
import { updateEntity } from '../utils/magentrix/api/updateEntity.js';
|
|
8
|
-
import chalk from 'chalk';
|
|
9
|
-
import { deleteEntity } from '../utils/magentrix/api/deleteEntity.js';
|
|
10
|
-
import { decompressString } from '../utils/compress.js';
|
|
11
|
-
|
|
12
|
-
const config = new Config();
|
|
13
|
-
|
|
14
|
-
let credentials = {};
|
|
15
|
-
let isUpdating = null;
|
|
16
|
-
let alreadyWarned = false;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Handler for file creation events.
|
|
20
|
-
* Triggered when a new file is added to the watched directory.
|
|
21
|
-
*
|
|
22
|
-
* @async
|
|
23
|
-
* @function onAdd
|
|
24
|
-
* @param {string} path - The path of the newly added file.
|
|
25
|
-
*/
|
|
26
|
-
const onAdd = async (path) => {
|
|
27
|
-
// TODO: Ensure it doesn't try and create a file that has already been created using CLI
|
|
28
|
-
// TODO: Handle file addition logic (e.g., queue for remote sync)
|
|
29
|
-
// console.log(`[+] File created: ${path}`);
|
|
30
|
-
|
|
31
|
-
const pathParts = path.split("\\");
|
|
32
|
-
const acceptedTypes = Object.keys(TYPE_DIR_MAP).map(key => TYPE_DIR_MAP[key].directory);
|
|
33
|
-
const fileName = pathParts[pathParts.length - 1];
|
|
34
|
-
const name = fileName.split(".")[0];
|
|
35
|
-
|
|
36
|
-
const fileContent = fs.readFileSync(path, 'utf-8');
|
|
37
|
-
|
|
38
|
-
let pathType = null;
|
|
39
|
-
|
|
40
|
-
for (const part of pathParts) {
|
|
41
|
-
if (acceptedTypes.includes(part)) {
|
|
42
|
-
pathType = part;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
let formattedData;
|
|
47
|
-
|
|
48
|
-
if (['Classes', 'Trigger', 'Controllers'].includes(pathType)) {
|
|
49
|
-
formattedData = {
|
|
50
|
-
Name: name,
|
|
51
|
-
Body: fileContent,
|
|
52
|
-
Description: "",
|
|
53
|
-
Type: ({
|
|
54
|
-
"Classes": "Class",
|
|
55
|
-
"Triggers": "Trigger",
|
|
56
|
-
"Controllers": "Controller"
|
|
57
|
-
})[pathType]
|
|
58
|
-
};
|
|
59
|
-
} else if (['Templates', 'Pages'].includes(pathType)) {
|
|
60
|
-
formattedData = {
|
|
61
|
-
Name: name,
|
|
62
|
-
Content: fileContent,
|
|
63
|
-
Description: "",
|
|
64
|
-
Type: "Active Page"
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
console.log(formattedData)
|
|
69
|
-
// Uncomment to perform creation via API:
|
|
70
|
-
// const creationResponse = await withSpinner('Creating file...', async () => {
|
|
71
|
-
// return await createEntity(credentials.instanceUrl, credentials.token.value, entityType, formattedData);
|
|
72
|
-
// });
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Handler for file modification events.
|
|
77
|
-
* Triggered when an existing file is changed.
|
|
78
|
-
*
|
|
79
|
-
* @async
|
|
80
|
-
* @function onChange
|
|
81
|
-
* @param {string} path - The path of the modified file.
|
|
82
|
-
*/
|
|
83
|
-
const onChange = async (path) => {
|
|
84
|
-
if (isUpdating) {
|
|
85
|
-
if (!alreadyWarned) {
|
|
86
|
-
process.stdout.write(chalk.yellow('\r⚠ File is currently compiling. Please wait...\n'));
|
|
87
|
-
alreadyWarned = true;
|
|
88
|
-
}
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
alreadyWarned = false;
|
|
93
|
-
|
|
94
|
-
const fileDataQuery = config.searchObject({ filePath: path }, { global: false, filename: 'base.json' });
|
|
95
|
-
const fileData = fileDataQuery?.[0];
|
|
96
|
-
if (!fileData) {
|
|
97
|
-
// No file found in the base index, no action needed here
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
process.stdout.write('\x1Bc'); // Clear the console
|
|
102
|
-
console.log('🔄 Watching for file changes... Any edits will be detected and queued for remote update.');
|
|
103
|
-
console.log();
|
|
104
|
-
|
|
105
|
-
const recordId = fileData.value.recordId;
|
|
106
|
-
const fileType = fileData.value.type;
|
|
107
|
-
const fileContents = fs.readFileSync(path, 'utf-8');
|
|
108
|
-
|
|
109
|
-
const updateField = ENTITY_FIELD_MAP[fileType];
|
|
110
|
-
const entityName = ENTITY_TYPE_MAP[fileType];
|
|
111
|
-
|
|
112
|
-
const updateBody = {
|
|
113
|
-
Id: recordId,
|
|
114
|
-
[updateField]: fileContents
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
isUpdating = true;
|
|
118
|
-
const response = await withSpinner(chalk.gray(`Compiling ${path}...`), async () => {
|
|
119
|
-
return await updateEntity(
|
|
120
|
-
credentials.instanceUrl,
|
|
121
|
-
credentials.token.value,
|
|
122
|
-
entityName,
|
|
123
|
-
recordId,
|
|
124
|
-
updateBody
|
|
125
|
-
).catch(err => {
|
|
126
|
-
return { ...err, hasErrors: true }
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
isUpdating = false;
|
|
130
|
-
|
|
131
|
-
if (response?.hasErrors) {
|
|
132
|
-
const err = response;
|
|
133
|
-
|
|
134
|
-
// Clear line and provide an error heading
|
|
135
|
-
console.log();
|
|
136
|
-
console.log(chalk.bgRed.bold.white(' ✖ Magentrix API Error '));
|
|
137
|
-
console.log(chalk.redBright('─'.repeat(48)));
|
|
138
|
-
|
|
139
|
-
if (err.response && Array.isArray(err.response.errors)) {
|
|
140
|
-
const errors = err.response.errors;
|
|
141
|
-
console.log(
|
|
142
|
-
chalk.red.bold(
|
|
143
|
-
`Found ${errors.length} error${errors.length !== 1 ? 's' : ''}:`
|
|
144
|
-
)
|
|
145
|
-
);
|
|
146
|
-
errors.forEach((error, idx) => {
|
|
147
|
-
// Show error status (if available), code, and message, all prettified
|
|
148
|
-
const code = error.code ? chalk.gray(`[${error.code}] `) : '';
|
|
149
|
-
const status = error.status ? chalk.yellow(`[${error.status}] `) : '';
|
|
150
|
-
const msg = chalk.whiteBright(error.message);
|
|
151
|
-
console.log(
|
|
152
|
-
`${chalk.redBright(' •')} ${status}${code}${msg}`
|
|
153
|
-
);
|
|
154
|
-
});
|
|
155
|
-
} else if (err.response && err.response.message) {
|
|
156
|
-
// Single message fallback
|
|
157
|
-
console.log(chalk.red(' • ') + chalk.whiteBright(err.response.message));
|
|
158
|
-
} else if (err.message) {
|
|
159
|
-
// Any unexpected error
|
|
160
|
-
console.log(chalk.red(' • ') + chalk.whiteBright(err.message));
|
|
161
|
-
} else {
|
|
162
|
-
console.log(chalk.red(' • ') + chalk.whiteBright('An unexpected error has occurred.'));
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
console.log(chalk.redBright('─'.repeat(48)));
|
|
166
|
-
} else {
|
|
167
|
-
// Clean, celebratory message!
|
|
168
|
-
console.log();
|
|
169
|
-
console.log(
|
|
170
|
-
chalk.bgGreen.bold.white(' ✔ File Compiled & Saved! ')
|
|
171
|
-
);
|
|
172
|
-
console.log(chalk.greenBright('─'.repeat(48)));
|
|
173
|
-
console.log(
|
|
174
|
-
chalk.greenBright.bold('Your file was compiled and saved on the remote Magentrix server.')
|
|
175
|
-
);
|
|
176
|
-
// Optionally: include file info or extra details if available
|
|
177
|
-
if (response.fileName) {
|
|
178
|
-
console.log(chalk.whiteBright(`File: ${chalk.cyan(response.fileName)}`));
|
|
179
|
-
}
|
|
180
|
-
if (response.lastModified) {
|
|
181
|
-
console.log(
|
|
182
|
-
chalk.gray(`Last Modified: ${new Date(response.lastModified).toLocaleString()}`)
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
if (response.recordId) {
|
|
186
|
-
console.log(chalk.gray(`Record ID: ${chalk.yellow(response.recordId)}`));
|
|
187
|
-
}
|
|
188
|
-
// Add spacing and line
|
|
189
|
-
console.log(chalk.greenBright('─'.repeat(48)));
|
|
190
|
-
console.log();
|
|
191
|
-
}
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Handler for file deletion events.
|
|
196
|
-
* Triggered when a file is removed from the watched directory.
|
|
197
|
-
*
|
|
198
|
-
* @async
|
|
199
|
-
* @function onUnlink
|
|
200
|
-
* @param {string} path - The path of the deleted file.
|
|
201
|
-
*/
|
|
202
|
-
const onUnlink = async (path) => {
|
|
203
|
-
process.stdout.write('\x1Bc'); // Clear the console
|
|
204
|
-
console.log('🔄 Watching for file changes... Any edits will be detected and queued for remote update.');
|
|
205
|
-
console.log();
|
|
206
|
-
|
|
207
|
-
const fileDataQuery = config.searchObject({ filePath: path }, { global: false, filename: 'base.json' });
|
|
208
|
-
const fileData = fileDataQuery?.[0];
|
|
209
|
-
if (!fileData) {
|
|
210
|
-
console.log(chalk.gray(`⚠️ File removed, but not found in index: ${path}`));
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
const recordId = fileData.value.recordId;
|
|
215
|
-
const fileType = fileData.value.type;
|
|
216
|
-
const entityName = ENTITY_TYPE_MAP[fileType];
|
|
217
|
-
|
|
218
|
-
const response = await withSpinner(`🗑️ Deleting remote ${entityName} for: ${path}`, async () => {
|
|
219
|
-
return await deleteEntity(
|
|
220
|
-
credentials.instanceUrl,
|
|
221
|
-
credentials.token.value,
|
|
222
|
-
entityName,
|
|
223
|
-
recordId
|
|
224
|
-
).catch(err => {
|
|
225
|
-
return { ...err?.response, hasErrors: true };
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
if (response?.hasErrors) {
|
|
230
|
-
try {
|
|
231
|
-
// Put the file back with the last synced content
|
|
232
|
-
const lastContent = decompressString(fileData.value.compressedContent);
|
|
233
|
-
fs.writeFileSync(path, lastContent);
|
|
234
|
-
} catch (err) {
|
|
235
|
-
console.warn(`⚠️ Failed to restore deleted file at "${path}". Reason: ${err.message}`);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (response?.success) {
|
|
240
|
-
console.log(chalk.green.bold(`✔ Successfully deleted remote ${entityName} for:`), chalk.whiteBright(path));
|
|
241
|
-
} else if (response?.hasErrors || response?.errors?.length > 0) {
|
|
242
|
-
console.log();
|
|
243
|
-
console.log(chalk.bgRed.bold.white(' ✖ Deletion Failed '));
|
|
244
|
-
console.log(chalk.redBright('─'.repeat(48)));
|
|
245
|
-
const errors = response.errors || [];
|
|
246
|
-
if (errors.length > 0) {
|
|
247
|
-
errors.forEach((err, i) => {
|
|
248
|
-
const code = err.code ? chalk.gray(`[${err.code}] `) : '';
|
|
249
|
-
const status = err.status ? chalk.yellow(`[${err.status}] `) : '';
|
|
250
|
-
const msg = chalk.whiteBright(err.message);
|
|
251
|
-
console.log(`${chalk.redBright(' •')} ${status}${code}${msg}`);
|
|
252
|
-
});
|
|
253
|
-
} else {
|
|
254
|
-
console.log(chalk.red('An unknown error occurred during deletion.'));
|
|
255
|
-
}
|
|
256
|
-
} else {
|
|
257
|
-
console.log(chalk.yellow(`⚠️ Unexpected response while deleting: ${path}`));
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
const startWatcher = () => {
|
|
262
|
-
console.log('🔄 Watching for file changes... Any edits will be detected and queued for remote update.');
|
|
263
|
-
console.log();
|
|
264
|
-
|
|
265
|
-
const watcher = chokidar.watch('.', {
|
|
266
|
-
ignored: /(^|[\/\\])\../, // Ignore dotfiles and .git etc.
|
|
267
|
-
persistent: true,
|
|
268
|
-
ignoreInitial: true // Don't fire events for files already present
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
watcher
|
|
272
|
-
.on('add', onAdd)
|
|
273
|
-
.on('change', onChange)
|
|
274
|
-
.on('unlink', onUnlink);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Initializes and starts the file watcher for Magentrix CLI.
|
|
279
|
-
* Watches the current directory for file changes (add, change, unlink)
|
|
280
|
-
* and triggers remote sync as needed.
|
|
281
|
-
*
|
|
282
|
-
* @function watch
|
|
283
|
-
* @returns {void}
|
|
284
|
-
*/
|
|
285
|
-
export const autoPublish = async () => {
|
|
286
|
-
process.stdout.write('\x1Bc'); // Clear the console
|
|
287
|
-
|
|
288
|
-
credentials = await withSpinner('Authenticating...', async () => {
|
|
289
|
-
return await ensureValidCredentials();
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
startWatcher();
|
|
293
|
-
};
|
|
1
|
+
import chokidar from 'chokidar';
|
|
2
|
+
import { ensureValidCredentials } from '../utils/cli/helpers/ensureCredentials.js';
|
|
3
|
+
import { withSpinner } from '../utils/spinner.js';
|
|
4
|
+
import { ENTITY_FIELD_MAP, ENTITY_TYPE_MAP, TYPE_DIR_MAP } from '../vars/global.js';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import Config from '../utils/config.js';
|
|
7
|
+
import { updateEntity } from '../utils/magentrix/api/updateEntity.js';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { deleteEntity } from '../utils/magentrix/api/deleteEntity.js';
|
|
10
|
+
import { decompressString } from '../utils/compress.js';
|
|
11
|
+
|
|
12
|
+
const config = new Config();
|
|
13
|
+
|
|
14
|
+
let credentials = {};
|
|
15
|
+
let isUpdating = null;
|
|
16
|
+
let alreadyWarned = false;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Handler for file creation events.
|
|
20
|
+
* Triggered when a new file is added to the watched directory.
|
|
21
|
+
*
|
|
22
|
+
* @async
|
|
23
|
+
* @function onAdd
|
|
24
|
+
* @param {string} path - The path of the newly added file.
|
|
25
|
+
*/
|
|
26
|
+
const onAdd = async (path) => {
|
|
27
|
+
// TODO: Ensure it doesn't try and create a file that has already been created using CLI
|
|
28
|
+
// TODO: Handle file addition logic (e.g., queue for remote sync)
|
|
29
|
+
// console.log(`[+] File created: ${path}`);
|
|
30
|
+
|
|
31
|
+
const pathParts = path.split("\\");
|
|
32
|
+
const acceptedTypes = Object.keys(TYPE_DIR_MAP).map(key => TYPE_DIR_MAP[key].directory);
|
|
33
|
+
const fileName = pathParts[pathParts.length - 1];
|
|
34
|
+
const name = fileName.split(".")[0];
|
|
35
|
+
|
|
36
|
+
const fileContent = fs.readFileSync(path, 'utf-8');
|
|
37
|
+
|
|
38
|
+
let pathType = null;
|
|
39
|
+
|
|
40
|
+
for (const part of pathParts) {
|
|
41
|
+
if (acceptedTypes.includes(part)) {
|
|
42
|
+
pathType = part;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let formattedData;
|
|
47
|
+
|
|
48
|
+
if (['Classes', 'Trigger', 'Controllers'].includes(pathType)) {
|
|
49
|
+
formattedData = {
|
|
50
|
+
Name: name,
|
|
51
|
+
Body: fileContent,
|
|
52
|
+
Description: "",
|
|
53
|
+
Type: ({
|
|
54
|
+
"Classes": "Class",
|
|
55
|
+
"Triggers": "Trigger",
|
|
56
|
+
"Controllers": "Controller"
|
|
57
|
+
})[pathType]
|
|
58
|
+
};
|
|
59
|
+
} else if (['Templates', 'Pages'].includes(pathType)) {
|
|
60
|
+
formattedData = {
|
|
61
|
+
Name: name,
|
|
62
|
+
Content: fileContent,
|
|
63
|
+
Description: "",
|
|
64
|
+
Type: "Active Page"
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
console.log(formattedData)
|
|
69
|
+
// Uncomment to perform creation via API:
|
|
70
|
+
// const creationResponse = await withSpinner('Creating file...', async () => {
|
|
71
|
+
// return await createEntity(credentials.instanceUrl, credentials.token.value, entityType, formattedData);
|
|
72
|
+
// });
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Handler for file modification events.
|
|
77
|
+
* Triggered when an existing file is changed.
|
|
78
|
+
*
|
|
79
|
+
* @async
|
|
80
|
+
* @function onChange
|
|
81
|
+
* @param {string} path - The path of the modified file.
|
|
82
|
+
*/
|
|
83
|
+
const onChange = async (path) => {
|
|
84
|
+
if (isUpdating) {
|
|
85
|
+
if (!alreadyWarned) {
|
|
86
|
+
process.stdout.write(chalk.yellow('\r⚠ File is currently compiling. Please wait...\n'));
|
|
87
|
+
alreadyWarned = true;
|
|
88
|
+
}
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
alreadyWarned = false;
|
|
93
|
+
|
|
94
|
+
const fileDataQuery = config.searchObject({ filePath: path }, { global: false, filename: 'base.json' });
|
|
95
|
+
const fileData = fileDataQuery?.[0];
|
|
96
|
+
if (!fileData) {
|
|
97
|
+
// No file found in the base index, no action needed here
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
process.stdout.write('\x1Bc'); // Clear the console
|
|
102
|
+
console.log('🔄 Watching for file changes... Any edits will be detected and queued for remote update.');
|
|
103
|
+
console.log();
|
|
104
|
+
|
|
105
|
+
const recordId = fileData.value.recordId;
|
|
106
|
+
const fileType = fileData.value.type;
|
|
107
|
+
const fileContents = fs.readFileSync(path, 'utf-8');
|
|
108
|
+
|
|
109
|
+
const updateField = ENTITY_FIELD_MAP[fileType];
|
|
110
|
+
const entityName = ENTITY_TYPE_MAP[fileType];
|
|
111
|
+
|
|
112
|
+
const updateBody = {
|
|
113
|
+
Id: recordId,
|
|
114
|
+
[updateField]: fileContents
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
isUpdating = true;
|
|
118
|
+
const response = await withSpinner(chalk.gray(`Compiling ${path}...`), async () => {
|
|
119
|
+
return await updateEntity(
|
|
120
|
+
credentials.instanceUrl,
|
|
121
|
+
credentials.token.value,
|
|
122
|
+
entityName,
|
|
123
|
+
recordId,
|
|
124
|
+
updateBody
|
|
125
|
+
).catch(err => {
|
|
126
|
+
return { ...err, hasErrors: true }
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
isUpdating = false;
|
|
130
|
+
|
|
131
|
+
if (response?.hasErrors) {
|
|
132
|
+
const err = response;
|
|
133
|
+
|
|
134
|
+
// Clear line and provide an error heading
|
|
135
|
+
console.log();
|
|
136
|
+
console.log(chalk.bgRed.bold.white(' ✖ Magentrix API Error '));
|
|
137
|
+
console.log(chalk.redBright('─'.repeat(48)));
|
|
138
|
+
|
|
139
|
+
if (err.response && Array.isArray(err.response.errors)) {
|
|
140
|
+
const errors = err.response.errors;
|
|
141
|
+
console.log(
|
|
142
|
+
chalk.red.bold(
|
|
143
|
+
`Found ${errors.length} error${errors.length !== 1 ? 's' : ''}:`
|
|
144
|
+
)
|
|
145
|
+
);
|
|
146
|
+
errors.forEach((error, idx) => {
|
|
147
|
+
// Show error status (if available), code, and message, all prettified
|
|
148
|
+
const code = error.code ? chalk.gray(`[${error.code}] `) : '';
|
|
149
|
+
const status = error.status ? chalk.yellow(`[${error.status}] `) : '';
|
|
150
|
+
const msg = chalk.whiteBright(error.message);
|
|
151
|
+
console.log(
|
|
152
|
+
`${chalk.redBright(' •')} ${status}${code}${msg}`
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
} else if (err.response && err.response.message) {
|
|
156
|
+
// Single message fallback
|
|
157
|
+
console.log(chalk.red(' • ') + chalk.whiteBright(err.response.message));
|
|
158
|
+
} else if (err.message) {
|
|
159
|
+
// Any unexpected error
|
|
160
|
+
console.log(chalk.red(' • ') + chalk.whiteBright(err.message));
|
|
161
|
+
} else {
|
|
162
|
+
console.log(chalk.red(' • ') + chalk.whiteBright('An unexpected error has occurred.'));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
console.log(chalk.redBright('─'.repeat(48)));
|
|
166
|
+
} else {
|
|
167
|
+
// Clean, celebratory message!
|
|
168
|
+
console.log();
|
|
169
|
+
console.log(
|
|
170
|
+
chalk.bgGreen.bold.white(' ✔ File Compiled & Saved! ')
|
|
171
|
+
);
|
|
172
|
+
console.log(chalk.greenBright('─'.repeat(48)));
|
|
173
|
+
console.log(
|
|
174
|
+
chalk.greenBright.bold('Your file was compiled and saved on the remote Magentrix server.')
|
|
175
|
+
);
|
|
176
|
+
// Optionally: include file info or extra details if available
|
|
177
|
+
if (response.fileName) {
|
|
178
|
+
console.log(chalk.whiteBright(`File: ${chalk.cyan(response.fileName)}`));
|
|
179
|
+
}
|
|
180
|
+
if (response.lastModified) {
|
|
181
|
+
console.log(
|
|
182
|
+
chalk.gray(`Last Modified: ${new Date(response.lastModified).toLocaleString()}`)
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
if (response.recordId) {
|
|
186
|
+
console.log(chalk.gray(`Record ID: ${chalk.yellow(response.recordId)}`));
|
|
187
|
+
}
|
|
188
|
+
// Add spacing and line
|
|
189
|
+
console.log(chalk.greenBright('─'.repeat(48)));
|
|
190
|
+
console.log();
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Handler for file deletion events.
|
|
196
|
+
* Triggered when a file is removed from the watched directory.
|
|
197
|
+
*
|
|
198
|
+
* @async
|
|
199
|
+
* @function onUnlink
|
|
200
|
+
* @param {string} path - The path of the deleted file.
|
|
201
|
+
*/
|
|
202
|
+
const onUnlink = async (path) => {
|
|
203
|
+
process.stdout.write('\x1Bc'); // Clear the console
|
|
204
|
+
console.log('🔄 Watching for file changes... Any edits will be detected and queued for remote update.');
|
|
205
|
+
console.log();
|
|
206
|
+
|
|
207
|
+
const fileDataQuery = config.searchObject({ filePath: path }, { global: false, filename: 'base.json' });
|
|
208
|
+
const fileData = fileDataQuery?.[0];
|
|
209
|
+
if (!fileData) {
|
|
210
|
+
console.log(chalk.gray(`⚠️ File removed, but not found in index: ${path}`));
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const recordId = fileData.value.recordId;
|
|
215
|
+
const fileType = fileData.value.type;
|
|
216
|
+
const entityName = ENTITY_TYPE_MAP[fileType];
|
|
217
|
+
|
|
218
|
+
const response = await withSpinner(`🗑️ Deleting remote ${entityName} for: ${path}`, async () => {
|
|
219
|
+
return await deleteEntity(
|
|
220
|
+
credentials.instanceUrl,
|
|
221
|
+
credentials.token.value,
|
|
222
|
+
entityName,
|
|
223
|
+
recordId
|
|
224
|
+
).catch(err => {
|
|
225
|
+
return { ...err?.response, hasErrors: true };
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
if (response?.hasErrors) {
|
|
230
|
+
try {
|
|
231
|
+
// Put the file back with the last synced content
|
|
232
|
+
const lastContent = decompressString(fileData.value.compressedContent);
|
|
233
|
+
fs.writeFileSync(path, lastContent);
|
|
234
|
+
} catch (err) {
|
|
235
|
+
console.warn(`⚠️ Failed to restore deleted file at "${path}". Reason: ${err.message}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (response?.success) {
|
|
240
|
+
console.log(chalk.green.bold(`✔ Successfully deleted remote ${entityName} for:`), chalk.whiteBright(path));
|
|
241
|
+
} else if (response?.hasErrors || response?.errors?.length > 0) {
|
|
242
|
+
console.log();
|
|
243
|
+
console.log(chalk.bgRed.bold.white(' ✖ Deletion Failed '));
|
|
244
|
+
console.log(chalk.redBright('─'.repeat(48)));
|
|
245
|
+
const errors = response.errors || [];
|
|
246
|
+
if (errors.length > 0) {
|
|
247
|
+
errors.forEach((err, i) => {
|
|
248
|
+
const code = err.code ? chalk.gray(`[${err.code}] `) : '';
|
|
249
|
+
const status = err.status ? chalk.yellow(`[${err.status}] `) : '';
|
|
250
|
+
const msg = chalk.whiteBright(err.message);
|
|
251
|
+
console.log(`${chalk.redBright(' •')} ${status}${code}${msg}`);
|
|
252
|
+
});
|
|
253
|
+
} else {
|
|
254
|
+
console.log(chalk.red('An unknown error occurred during deletion.'));
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
console.log(chalk.yellow(`⚠️ Unexpected response while deleting: ${path}`));
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const startWatcher = () => {
|
|
262
|
+
console.log('🔄 Watching for file changes... Any edits will be detected and queued for remote update.');
|
|
263
|
+
console.log();
|
|
264
|
+
|
|
265
|
+
const watcher = chokidar.watch('.', {
|
|
266
|
+
ignored: /(^|[\/\\])\../, // Ignore dotfiles and .git etc.
|
|
267
|
+
persistent: true,
|
|
268
|
+
ignoreInitial: true // Don't fire events for files already present
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
watcher
|
|
272
|
+
.on('add', onAdd)
|
|
273
|
+
.on('change', onChange)
|
|
274
|
+
.on('unlink', onUnlink);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Initializes and starts the file watcher for Magentrix CLI.
|
|
279
|
+
* Watches the current directory for file changes (add, change, unlink)
|
|
280
|
+
* and triggers remote sync as needed.
|
|
281
|
+
*
|
|
282
|
+
* @function watch
|
|
283
|
+
* @returns {void}
|
|
284
|
+
*/
|
|
285
|
+
export const autoPublish = async () => {
|
|
286
|
+
process.stdout.write('\x1Bc'); // Clear the console
|
|
287
|
+
|
|
288
|
+
credentials = await withSpinner('Authenticating...', async () => {
|
|
289
|
+
return await ensureValidCredentials();
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
startWatcher();
|
|
293
|
+
};
|