@magentrix-corp/magentrix-cli 1.0.0
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 -0
- package/README.md +471 -0
- package/actions/autopublish.js +283 -0
- package/actions/autopublish.old.js +293 -0
- package/actions/autopublish.v2.js +447 -0
- package/actions/create.js +329 -0
- package/actions/help.js +165 -0
- package/actions/main.js +81 -0
- package/actions/publish.js +567 -0
- package/actions/pull.js +139 -0
- package/actions/setup.js +61 -0
- package/actions/status.js +17 -0
- package/bin/magentrix.js +159 -0
- package/package.json +61 -0
- package/utils/cacher.js +112 -0
- package/utils/cli/checkInstanceUrl.js +29 -0
- package/utils/cli/helpers/compare.js +281 -0
- package/utils/cli/helpers/ensureApiKey.js +57 -0
- package/utils/cli/helpers/ensureCredentials.js +60 -0
- package/utils/cli/helpers/ensureInstanceUrl.js +63 -0
- package/utils/cli/writeRecords.js +223 -0
- package/utils/compare.js +135 -0
- package/utils/compress.js +18 -0
- package/utils/config.js +451 -0
- package/utils/diff.js +49 -0
- package/utils/downloadAssets.js +75 -0
- package/utils/filetag.js +115 -0
- package/utils/hash.js +14 -0
- package/utils/magentrix/api/assets.js +145 -0
- package/utils/magentrix/api/auth.js +56 -0
- package/utils/magentrix/api/createEntity.js +61 -0
- package/utils/magentrix/api/deleteEntity.js +55 -0
- package/utils/magentrix/api/meqlQuery.js +31 -0
- package/utils/magentrix/api/retrieveEntity.js +32 -0
- package/utils/magentrix/api/updateEntity.js +66 -0
- package/utils/magentrix/fetch.js +154 -0
- package/utils/merge.js +22 -0
- package/utils/preferences.js +40 -0
- package/utils/spinner.js +43 -0
- package/utils/template.js +52 -0
- package/utils/updateFileBase.js +103 -0
- package/vars/config.js +1 -0
- package/vars/global.js +33 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if a request body should be JSON-stringified.
|
|
3
|
+
* Excludes FormData, Blob, ArrayBuffer, URLSearchParams, and typed arrays.
|
|
4
|
+
* @param {any} body
|
|
5
|
+
* @returns {boolean}
|
|
6
|
+
*/
|
|
7
|
+
function isJsonBody(body) {
|
|
8
|
+
return (
|
|
9
|
+
typeof body === 'object' &&
|
|
10
|
+
body !== null &&
|
|
11
|
+
!(body instanceof FormData) &&
|
|
12
|
+
!(body instanceof Blob) &&
|
|
13
|
+
!(body instanceof ArrayBuffer) &&
|
|
14
|
+
!(body instanceof URLSearchParams) &&
|
|
15
|
+
!ArrayBuffer.isView(body) // covers Uint8Array, etc.
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Fetch helper for Magentrix API.
|
|
21
|
+
* Handles network, HTTP, and API-level errors with detailed messages or returns error info as JSON object.
|
|
22
|
+
*
|
|
23
|
+
* @async
|
|
24
|
+
* @function fetchMagentrix
|
|
25
|
+
* @param {Object} opts - Fetch options.
|
|
26
|
+
* @param {string} opts.instanceUrl - Magentrix instance base URL (e.g. https://your.magentrix.com).
|
|
27
|
+
* @param {string} [opts.token] - OAuth2 bearer token for authentication (optional for public endpoints).
|
|
28
|
+
* @param {string} opts.path - API path (e.g. '/api/3.0/entity/activeclass').
|
|
29
|
+
* @param {string} [opts.method='GET'] - HTTP method.
|
|
30
|
+
* @param {any} [opts.body] - Request body (object for JSON, or raw for FormData, Blob, string, etc).
|
|
31
|
+
* @param {Object} [opts.headers] - Additional headers to merge with defaults.
|
|
32
|
+
* @param {boolean} [opts.returnErrorObject=false] - If true, errors are returned as JSON objects instead of thrown as Error.
|
|
33
|
+
* @returns {Promise<Object>} Parsed JSON response from the API if successful.
|
|
34
|
+
* @throws {Error|Object} Throws Error (default) or error object if returnErrorObject is true.
|
|
35
|
+
*/
|
|
36
|
+
export const fetchMagentrix = async ({
|
|
37
|
+
instanceUrl,
|
|
38
|
+
token,
|
|
39
|
+
path,
|
|
40
|
+
method = 'GET',
|
|
41
|
+
body,
|
|
42
|
+
headers = {},
|
|
43
|
+
ignoreContentType = false,
|
|
44
|
+
returnErrorObject = false,
|
|
45
|
+
errorConfig = {
|
|
46
|
+
includeStatus: false,
|
|
47
|
+
includeURL: false,
|
|
48
|
+
label: '', // 'Magentrix errors:',
|
|
49
|
+
bullets: false
|
|
50
|
+
},
|
|
51
|
+
}) => {
|
|
52
|
+
if (!instanceUrl || !path) {
|
|
53
|
+
const err = { type: 'client', message: 'Missing required parameter(s): instanceUrl or path' };
|
|
54
|
+
if (returnErrorObject) throw err;
|
|
55
|
+
throw new Error(err.message);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const finalHeaders = {
|
|
59
|
+
'Accept': 'application/json',
|
|
60
|
+
...headers
|
|
61
|
+
};
|
|
62
|
+
if (isJsonBody(body)) finalHeaders['Content-Type'] = 'application/json';
|
|
63
|
+
if (token) finalHeaders['Authorization'] = `Bearer ${token}`;
|
|
64
|
+
let requestBody;
|
|
65
|
+
if (body === undefined || body === null) {
|
|
66
|
+
requestBody = undefined;
|
|
67
|
+
} else if (isJsonBody(body)) {
|
|
68
|
+
requestBody = JSON.stringify(body);
|
|
69
|
+
} else {
|
|
70
|
+
requestBody = body;
|
|
71
|
+
}
|
|
72
|
+
if (!finalHeaders['Content-Type'] && !ignoreContentType) finalHeaders['Content-Type'] = 'application/json';
|
|
73
|
+
|
|
74
|
+
let response, responseData;
|
|
75
|
+
try {
|
|
76
|
+
response = await fetch(`${instanceUrl.replace(/\/$/, '')}${path}`, {
|
|
77
|
+
method,
|
|
78
|
+
headers: finalHeaders,
|
|
79
|
+
body: requestBody
|
|
80
|
+
});
|
|
81
|
+
} catch (err) {
|
|
82
|
+
const errorObj = {
|
|
83
|
+
type: 'network',
|
|
84
|
+
message: `Network error contacting Magentrix API: ${err.message}`,
|
|
85
|
+
error: err
|
|
86
|
+
};
|
|
87
|
+
if (returnErrorObject) throw errorObj;
|
|
88
|
+
throw new Error(errorObj.message);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
responseData = await response.json();
|
|
93
|
+
} catch {
|
|
94
|
+
responseData = null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!response.ok) {
|
|
98
|
+
const errorObj = {
|
|
99
|
+
type: 'http',
|
|
100
|
+
status: response.status,
|
|
101
|
+
statusText: response.statusText,
|
|
102
|
+
url: response.url,
|
|
103
|
+
response: responseData,
|
|
104
|
+
};
|
|
105
|
+
// Optionally add detailed error message
|
|
106
|
+
let msg = errorConfig?.includeStatus ? `HTTP ${response.status} ${response.statusText}\n` : '';
|
|
107
|
+
if (responseData) {
|
|
108
|
+
const responseErrs = responseData.errors || responseData.Errors;
|
|
109
|
+
|
|
110
|
+
if (Array.isArray(responseErrs) && responseErrs.length) {
|
|
111
|
+
msg += `${errorConfig?.label}${errorConfig?.label ? '\n' : ''}` + responseErrs.map(e => `${errorConfig?.bullets ? " • " : ""}${e.code ? `[${e.code || '500'}] ` : ''}${e.message || e}`).join('\n');
|
|
112
|
+
} else if (responseData.message) {
|
|
113
|
+
msg += `Magentrix message: ${responseData.message}`;
|
|
114
|
+
} else {
|
|
115
|
+
msg += JSON.stringify(responseData);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (errorConfig?.includeURL) msg += `\nURL: ${response.url}`;
|
|
119
|
+
errorObj.message = msg;
|
|
120
|
+
if (returnErrorObject) throw errorObj;
|
|
121
|
+
throw new Error(msg);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Handle API-level business logic errors
|
|
125
|
+
if (
|
|
126
|
+
!responseData ||
|
|
127
|
+
responseData.success === false ||
|
|
128
|
+
(Array.isArray(responseData?.errors) && responseData.errors.length > 0) ||
|
|
129
|
+
responseData.error
|
|
130
|
+
) {
|
|
131
|
+
const errorObj = {
|
|
132
|
+
type: 'api',
|
|
133
|
+
url: response.url,
|
|
134
|
+
response: responseData
|
|
135
|
+
};
|
|
136
|
+
let details = '';
|
|
137
|
+
if (Array.isArray(responseData?.errors) && responseData.errors.length) {
|
|
138
|
+
details = responseData.errors
|
|
139
|
+
.map(e => ` • ${e.code ? `[${e.code}] ` : ''}${e.message}`)
|
|
140
|
+
.join('\n');
|
|
141
|
+
} else if (responseData?.message) {
|
|
142
|
+
details = responseData.message;
|
|
143
|
+
} else if (typeof responseData === 'object') {
|
|
144
|
+
details = JSON.stringify(responseData);
|
|
145
|
+
} else {
|
|
146
|
+
details = String(responseData);
|
|
147
|
+
}
|
|
148
|
+
errorObj.message = `Magentrix API error:\n${details}`;
|
|
149
|
+
if (returnErrorObject) throw errorObj;
|
|
150
|
+
throw new Error(errorObj.message);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return responseData;
|
|
154
|
+
};
|
package/utils/merge.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as diff3 from 'node-diff3'; // ESM import all
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Merges local and remote file contents with a common ancestor (base),
|
|
5
|
+
* returning the merged result and whether there was a conflict.
|
|
6
|
+
*
|
|
7
|
+
* @param {string} baseContent - The last synced (common ancestor) file content.
|
|
8
|
+
* @param {string} localContent - The current local file content.
|
|
9
|
+
* @param {string} remoteContent - The remote/server file content.
|
|
10
|
+
* @returns {{ mergedText: string, hasConflict: boolean }}
|
|
11
|
+
*/
|
|
12
|
+
export function mergeFiles(baseContent, localContent, remoteContent) {
|
|
13
|
+
const baseLines = baseContent.split('\n');
|
|
14
|
+
const localLines = localContent.split('\n');
|
|
15
|
+
const remoteLines = remoteContent.split('\n');
|
|
16
|
+
|
|
17
|
+
const result = diff3.merge(localLines, baseLines, remoteLines);
|
|
18
|
+
const mergedText = result.result.join('\n');
|
|
19
|
+
|
|
20
|
+
// Do NOT throw. Just return the merge, which will have conflict markers if unresolved.
|
|
21
|
+
return mergedText;
|
|
22
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Ensures that the .vscode/settings.json file exists in the project root and
|
|
6
|
+
* updates it to associate `.xyz` files with C# syntax highlighting in VS Code.
|
|
7
|
+
* If the file or directory doesn't exist, it will be created. Existing settings
|
|
8
|
+
* are preserved and merged.
|
|
9
|
+
*
|
|
10
|
+
* @async
|
|
11
|
+
* @function ensureVSCodeFileAssociation
|
|
12
|
+
* @param {string} projectRoot - The absolute path to the project root where the `.vscode` folder resides.
|
|
13
|
+
* @returns {Promise<void>} Resolves once the settings file is updated or created.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* await ensureVSCodeFileAssociation(process.cwd());
|
|
17
|
+
*/
|
|
18
|
+
export async function ensureVSCodeFileAssociation(projectRoot) {
|
|
19
|
+
const vscodeDir = path.join(projectRoot, '.vscode');
|
|
20
|
+
const settingsPath = path.join(vscodeDir, 'settings.json');
|
|
21
|
+
|
|
22
|
+
await fs.mkdir(vscodeDir, { recursive: true });
|
|
23
|
+
|
|
24
|
+
let settings = {};
|
|
25
|
+
try {
|
|
26
|
+
const raw = await fs.readFile(settingsPath, 'utf-8');
|
|
27
|
+
settings = JSON.parse(raw);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
// Ignore error if file doesn't exist or is invalid JSON
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
settings['files.associations'] = {
|
|
33
|
+
...settings['files.associations'],
|
|
34
|
+
'*.ac': 'csharp',
|
|
35
|
+
'*.trigger': 'csharp',
|
|
36
|
+
'*.ctrl': 'csharp'
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2));
|
|
40
|
+
}
|
package/utils/spinner.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export async function withSpinner(message, fn, config = { showCompletion: true }) {
|
|
2
|
+
const spinnerChars = ['|', '/', '-', '\\'];
|
|
3
|
+
let i = 0;
|
|
4
|
+
let spinnerActive = true;
|
|
5
|
+
let spinner;
|
|
6
|
+
|
|
7
|
+
// Patch both log and error for full coverage
|
|
8
|
+
const originalLog = console.log;
|
|
9
|
+
const originalError = console.error;
|
|
10
|
+
function clearSpinnerLine() {
|
|
11
|
+
process.stdout.write('\r' + ' '.repeat(message.length + 8) + '\r');
|
|
12
|
+
if (spinnerActive) {
|
|
13
|
+
clearInterval(spinner);
|
|
14
|
+
spinnerActive = false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
console.log = (...args) => { clearSpinnerLine(); originalLog(...args); };
|
|
18
|
+
console.error = (...args) => { clearSpinnerLine(); originalError(...args); };
|
|
19
|
+
|
|
20
|
+
process.stdout.write(`${message} `);
|
|
21
|
+
|
|
22
|
+
spinner = setInterval(() => {
|
|
23
|
+
process.stdout.write(`\r${message} ${spinnerChars[i++ % spinnerChars.length]}`);
|
|
24
|
+
}, 80);
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const result = await fn();
|
|
28
|
+
const treatAsError = result?.hasErrors;
|
|
29
|
+
spinnerActive = false;
|
|
30
|
+
clearInterval(spinner);
|
|
31
|
+
if (config?.showCompletion) process.stdout.write(`\r${message} ${treatAsError ? '❌' : '✅'}\n`);
|
|
32
|
+
return result;
|
|
33
|
+
} catch (err) {
|
|
34
|
+
spinnerActive = false;
|
|
35
|
+
clearInterval(spinner);
|
|
36
|
+
if (config?.showCompletion) process.stdout.write(`\r${message} ❌\n`);
|
|
37
|
+
throw err;
|
|
38
|
+
} finally {
|
|
39
|
+
// Restore logs always
|
|
40
|
+
console.log = originalLog;
|
|
41
|
+
console.error = originalError;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the default template for an ActiveClass Controller.
|
|
3
|
+
* @param {string} className
|
|
4
|
+
* @returns {string}
|
|
5
|
+
*/
|
|
6
|
+
export const getControllerTemplate = (className) => `
|
|
7
|
+
public class ${className} : AspxController {
|
|
8
|
+
public override ActionResponse Index()
|
|
9
|
+
{
|
|
10
|
+
return View();
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
`.trim();
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns the default template for an ActiveClass Trigger.
|
|
17
|
+
* @param {string} className - For instance ContactTrigger
|
|
18
|
+
* @param {string} entityName - For instance Force__Contact
|
|
19
|
+
* @returns {string}
|
|
20
|
+
*/
|
|
21
|
+
export const getTriggerTemplate = (className, entityName = '<ENTITYNAME>') => `
|
|
22
|
+
public class ${className} : ActiveTrigger<${entityName}>
|
|
23
|
+
{
|
|
24
|
+
public override void Execute(TransactionContext<${entityName}> trigger) {
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
`.trim();
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Returns the default template for a general ActiveClass.
|
|
32
|
+
* @param {string} className
|
|
33
|
+
* @returns {string}
|
|
34
|
+
*/
|
|
35
|
+
export const getClassTemplate = (className) => `
|
|
36
|
+
public class ${className} {
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
`.trim();
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Returns the default template for an Active Page.
|
|
43
|
+
* @param {string} pageName
|
|
44
|
+
* @param {string} pageLabel
|
|
45
|
+
* @returns {string}
|
|
46
|
+
*/
|
|
47
|
+
export const getPageTemplate = (pageName, pageLabel) => `
|
|
48
|
+
<aspx:AspxPage runat="server" Id="${pageName}" title="${pageLabel}">
|
|
49
|
+
<body>
|
|
50
|
+
</body>
|
|
51
|
+
</aspx:AspxPage>
|
|
52
|
+
`.trim();
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { EXPORT_ROOT } from "../vars/global.js";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { sha256 } from "./hash.js";
|
|
5
|
+
import Config from "./config.js";
|
|
6
|
+
import { compressString } from "./compress.js";
|
|
7
|
+
|
|
8
|
+
const config = new Config();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Recursively collects all file paths under a directory, returning relative paths from base.
|
|
12
|
+
*
|
|
13
|
+
* @param {string} dirPath - Absolute or relative path to the root directory to scan.
|
|
14
|
+
* @param {string} [basePath=''] - Internal use. The base path for recursion, relative to dirPath.
|
|
15
|
+
* @returns {string[]} Array of relative file paths found under dirPath.
|
|
16
|
+
*/
|
|
17
|
+
export function getAllFiles(dirPath, basePath = "") {
|
|
18
|
+
let results = [];
|
|
19
|
+
const fullDirPath = path.join(dirPath, basePath);
|
|
20
|
+
const entries = fs.readdirSync(fullDirPath, { withFileTypes: true });
|
|
21
|
+
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
const relPath = path.join(basePath, entry.name);
|
|
24
|
+
const entryPath = path.join(dirPath, relPath);
|
|
25
|
+
|
|
26
|
+
if (entry.isDirectory()) {
|
|
27
|
+
// Recurse into subdirectory
|
|
28
|
+
results = results.concat(getAllFiles(dirPath, relPath));
|
|
29
|
+
} else if (entry.isFile()) {
|
|
30
|
+
// Add file's relative path to results
|
|
31
|
+
results.push(relPath);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return results;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Updates (or creates) the base sync state for a specific file.
|
|
39
|
+
*
|
|
40
|
+
* Saves the current file's lastModified time, content hash, and contents
|
|
41
|
+
* to base.json using config.save(), under the key of the file's relative path.
|
|
42
|
+
*
|
|
43
|
+
* This should be called only when the local and remote file are confirmed to be in sync
|
|
44
|
+
* (i.e., after a successful pull, push, or conflict resolution).
|
|
45
|
+
*
|
|
46
|
+
* @param {string} filePath - The expected path to the file (relative to project root or EXPORT_ROOT).
|
|
47
|
+
* @param {object} record - The Magentrix record
|
|
48
|
+
* @param {string} actualPath - Should only be provided if the file has been renamed and the base has not been updated
|
|
49
|
+
* @param {object} contentSnapshot - Optional { content, hash } snapshot of what was actually published (prevents race conditions)
|
|
50
|
+
*/
|
|
51
|
+
export const updateBase = (filePath, record, actualPath = '', contentSnapshot = null) => {
|
|
52
|
+
// This is the true location of the file
|
|
53
|
+
const fileSystemLocation = actualPath || path.resolve(filePath);
|
|
54
|
+
|
|
55
|
+
if (!fs.existsSync(fileSystemLocation)) {
|
|
56
|
+
console.error(`❌ File does not exist: ${filePath}`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Get file stats for mtime
|
|
61
|
+
const fileStats = fs.statSync(fileSystemLocation);
|
|
62
|
+
|
|
63
|
+
// Use snapshot if provided (to avoid race conditions), otherwise read from disk
|
|
64
|
+
let fileContent, contentHash;
|
|
65
|
+
if (contentSnapshot && contentSnapshot.content) {
|
|
66
|
+
// Use the snapshot of what was actually published
|
|
67
|
+
fileContent = contentSnapshot.content;
|
|
68
|
+
contentHash = contentSnapshot.hash;
|
|
69
|
+
} else {
|
|
70
|
+
// Read from disk (normal behavior)
|
|
71
|
+
fileContent = fs.readFileSync(fileSystemLocation, "utf-8");
|
|
72
|
+
contentHash = sha256(fileContent);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Save sync metadata and content to base.json via config manager.
|
|
76
|
+
// - key: the relative path to the file
|
|
77
|
+
// - value: lastModified, contentHash, and full content
|
|
78
|
+
// - options: ensure writing to base.json
|
|
79
|
+
const saveData = {
|
|
80
|
+
lastModified: fileStats.mtimeMs,
|
|
81
|
+
contentHash,
|
|
82
|
+
compressedContent: compressString(fileContent),
|
|
83
|
+
recordId: record.Id,
|
|
84
|
+
type: record.Type,
|
|
85
|
+
filePath,
|
|
86
|
+
lastKnownActualPath: fileSystemLocation,
|
|
87
|
+
lastKnownPath: path.resolve(filePath)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (saveData.type === 'File') delete saveData.compressedContent;
|
|
91
|
+
|
|
92
|
+
config.save(
|
|
93
|
+
record.Id,
|
|
94
|
+
saveData,
|
|
95
|
+
{
|
|
96
|
+
filename: "base.json"
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const removeFromBase = (recordId) => {
|
|
102
|
+
config.removeKey(recordId, { filename: "base.json" });
|
|
103
|
+
}
|
package/vars/config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const VERSION = '1.0.0'
|
package/vars/global.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { sha256 } from '../utils/hash.js'; // Or wherever your hash function lives
|
|
2
|
+
|
|
3
|
+
export const CWD = process.cwd();
|
|
4
|
+
export const HASHED_CWD = sha256(CWD);
|
|
5
|
+
export const EXPORT_ROOT = "src";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Maps Magentrix Type fields to local folder names and extensions.
|
|
9
|
+
* Extensions chosen to avoid collisions and clearly indicate type.
|
|
10
|
+
*/
|
|
11
|
+
export const TYPE_DIR_MAP = {
|
|
12
|
+
Class: { directory: "Classes", extension: "ac" },
|
|
13
|
+
Trigger: { directory: "Triggers", extension: "trigger" },
|
|
14
|
+
Controller: { directory: "Controllers", extension: "ctrl" },
|
|
15
|
+
"Active Page": { directory: "Pages", extension: "aspx" }, // For ActivePage
|
|
16
|
+
"Active Template": { directory: "Templates", extension: "aspx" }
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const ENTITY_TYPE_MAP = {
|
|
20
|
+
'Active Page': "ActivePage",
|
|
21
|
+
"Active Template": "ActivePage",
|
|
22
|
+
"Class": "ActiveClass",
|
|
23
|
+
"Controller": "ActiveClass",
|
|
24
|
+
"Trigger": "ActiveClass"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const ENTITY_FIELD_MAP = {
|
|
28
|
+
"Active Page": "Content",
|
|
29
|
+
"Active Template": "Content",
|
|
30
|
+
"Class": "Body",
|
|
31
|
+
"Controller": "Body",
|
|
32
|
+
"Trigger": "Body",
|
|
33
|
+
}
|