@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
package/package.json
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@magentrix-corp/magentrix-cli",
|
|
3
|
-
"version": "1.3.
|
|
4
|
-
"description": "CLI tool for synchronizing local files with Magentrix cloud platform",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"bin": {
|
|
8
|
-
"magentrix": "./bin/magentrix.js"
|
|
9
|
-
},
|
|
10
|
-
"scripts": {
|
|
11
|
-
"test": "node tests/all.js"
|
|
12
|
-
},
|
|
13
|
-
"keywords": [
|
|
14
|
-
"magentrix",
|
|
15
|
-
"cli",
|
|
16
|
-
"sync",
|
|
17
|
-
"cloud",
|
|
18
|
-
"automation",
|
|
19
|
-
"development-tools"
|
|
20
|
-
],
|
|
21
|
-
"author": "Magentrix Corporation",
|
|
22
|
-
"license": "UNLICENSED",
|
|
23
|
-
"engines": {
|
|
24
|
-
"node": ">=20.0.0"
|
|
25
|
-
},
|
|
26
|
-
"publishConfig": {
|
|
27
|
-
"access": "public"
|
|
28
|
-
},
|
|
29
|
-
"files": [
|
|
30
|
-
"bin/",
|
|
31
|
-
"actions/",
|
|
32
|
-
"utils/",
|
|
33
|
-
"vars/",
|
|
34
|
-
"README.md",
|
|
35
|
-
"LICENSE"
|
|
36
|
-
],
|
|
37
|
-
"dependencies": {
|
|
38
|
-
"@inquirer/prompts": "^7.6.0",
|
|
39
|
-
"archiver": "^7.0.1",
|
|
40
|
-
"chalk": "^5.4.1",
|
|
41
|
-
"chokidar": "^4.0.3",
|
|
42
|
-
"commander": "^14.0.0",
|
|
43
|
-
"diff": "^8.0.2",
|
|
44
|
-
"dotenv": "^17.2.3",
|
|
45
|
-
"extract-zip": "^2.0.1",
|
|
46
|
-
"fuzzy": "^0.1.3",
|
|
47
|
-
"inquirer": "^12.7.0",
|
|
48
|
-
"node-diff3": "^3.1.2",
|
|
49
|
-
"ora": "^8.2.0",
|
|
50
|
-
"pako": "^2.1.0",
|
|
51
|
-
"prompts": "^2.4.2",
|
|
52
|
-
"readline-sync": "^1.4.10",
|
|
53
|
-
"uuid": "^11.1.0"
|
|
54
|
-
}
|
|
55
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@magentrix-corp/magentrix-cli",
|
|
3
|
+
"version": "1.3.17",
|
|
4
|
+
"description": "CLI tool for synchronizing local files with Magentrix cloud platform",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"magentrix": "./bin/magentrix.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node tests/all.js"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"magentrix",
|
|
15
|
+
"cli",
|
|
16
|
+
"sync",
|
|
17
|
+
"cloud",
|
|
18
|
+
"automation",
|
|
19
|
+
"development-tools"
|
|
20
|
+
],
|
|
21
|
+
"author": "Magentrix Corporation",
|
|
22
|
+
"license": "UNLICENSED",
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=20.0.0"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"bin/",
|
|
31
|
+
"actions/",
|
|
32
|
+
"utils/",
|
|
33
|
+
"vars/",
|
|
34
|
+
"README.md",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@inquirer/prompts": "^7.6.0",
|
|
39
|
+
"archiver": "^7.0.1",
|
|
40
|
+
"chalk": "^5.4.1",
|
|
41
|
+
"chokidar": "^4.0.3",
|
|
42
|
+
"commander": "^14.0.0",
|
|
43
|
+
"diff": "^8.0.2",
|
|
44
|
+
"dotenv": "^17.2.3",
|
|
45
|
+
"extract-zip": "^2.0.1",
|
|
46
|
+
"fuzzy": "^0.1.3",
|
|
47
|
+
"inquirer": "^12.7.0",
|
|
48
|
+
"node-diff3": "^3.1.2",
|
|
49
|
+
"ora": "^8.2.0",
|
|
50
|
+
"pako": "^2.1.0",
|
|
51
|
+
"prompts": "^2.4.2",
|
|
52
|
+
"readline-sync": "^1.4.10",
|
|
53
|
+
"uuid": "^11.1.0"
|
|
54
|
+
}
|
|
55
|
+
}
|
package/utils/assetPaths.js
CHANGED
|
@@ -1,158 +1,158 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { EXPORT_ROOT } from '../vars/global.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Asset path utilities for handling the difference between local and API paths.
|
|
6
|
-
*
|
|
7
|
-
* Local structure: src/Assets/...
|
|
8
|
-
* API structure: /contents/assets/...
|
|
9
|
-
*
|
|
10
|
-
* These helpers abstract away the "Contents" prefix requirement for the Magentrix API.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Convert a local asset path to an API path by adding the 'contents' prefix
|
|
15
|
-
* and normalizing with forward slashes (preserves original casing).
|
|
16
|
-
*
|
|
17
|
-
* @param {string} localPath - Local file path (e.g., "src/Assets/Images/Logo.png")
|
|
18
|
-
* @returns {string} API path (e.g., "/contents/assets/Images")
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* toApiPath("src/Assets/Images/Logo.png") // "/contents/assets/Images"
|
|
22
|
-
* toApiPath("src/Assets") // "/contents/assets"
|
|
23
|
-
*/
|
|
24
|
-
export const toApiPath = (localPath) => {
|
|
25
|
-
// Handle undefined or empty paths
|
|
26
|
-
if (!localPath) {
|
|
27
|
-
return '/contents/assets';
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Normalize the path
|
|
31
|
-
const normalized = path.normalize(localPath);
|
|
32
|
-
|
|
33
|
-
// Remove EXPORT_ROOT from path (handle both absolute and relative paths)
|
|
34
|
-
let relative;
|
|
35
|
-
if (path.isAbsolute(normalized)) {
|
|
36
|
-
// For absolute paths, find and remove everything up to and including EXPORT_ROOT
|
|
37
|
-
const exportRootPattern = new RegExp(`.*[\\\\/]${EXPORT_ROOT}[\\\\/]`);
|
|
38
|
-
relative = normalized.replace(exportRootPattern, '');
|
|
39
|
-
} else {
|
|
40
|
-
// For relative paths, just remove EXPORT_ROOT prefix
|
|
41
|
-
relative = normalized.replace(new RegExp(`^${EXPORT_ROOT}[\\\\/]?`), '');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Replace 'Assets' with 'contents/assets' (case insensitive search, but replace with lowercase)
|
|
45
|
-
const apiPath = relative.replace(/^Assets/i, 'contents/assets');
|
|
46
|
-
|
|
47
|
-
// Normalize to forward slashes (preserve casing!), remove filename
|
|
48
|
-
let dirPath = path.dirname(apiPath).replace(/\\/g, '/');
|
|
49
|
-
|
|
50
|
-
// Handle edge case where dirname returns '.' for root
|
|
51
|
-
if (dirPath === '.') {
|
|
52
|
-
dirPath = 'contents/assets';
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Ensure it starts with /
|
|
56
|
-
return dirPath.startsWith('/') ? dirPath : `/${dirPath}`;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Convert an API path to a local path by removing the 'contents' prefix.
|
|
61
|
-
* Returns a relative path (without EXPORT_ROOT) that can be joined with EXPORT_ROOT.
|
|
62
|
-
*
|
|
63
|
-
* @param {string} apiPath - API path (e.g., "/contents/assets/images/logo.png")
|
|
64
|
-
* @returns {string} Relative local path (e.g., "Assets/images/logo.png")
|
|
65
|
-
*
|
|
66
|
-
* @example
|
|
67
|
-
* toLocalPath("/contents/assets/images") // "Assets/images"
|
|
68
|
-
* toLocalPath("/contents/assets") // "Assets"
|
|
69
|
-
*/
|
|
70
|
-
export const toLocalPath = (apiPath) => {
|
|
71
|
-
// Remove leading slash and 'contents/' prefix
|
|
72
|
-
const cleaned = apiPath.replace(/^\/+/, '').replace(/^contents\//i, '');
|
|
73
|
-
|
|
74
|
-
// Capitalize 'assets' to 'Assets'
|
|
75
|
-
const withCapitalAssets = cleaned.replace(/^assets/i, 'Assets');
|
|
76
|
-
|
|
77
|
-
return withCapitalAssets;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Convert a local folder path to an API path (keeps the folder, doesn't extract parent).
|
|
82
|
-
* Similar to toApiPath but for folders (preserves original casing).
|
|
83
|
-
*
|
|
84
|
-
* @param {string} localFolderPath - Local folder path (e.g., "src/Assets/Images")
|
|
85
|
-
* @returns {string} API path (e.g., "/contents/assets/Images")
|
|
86
|
-
*
|
|
87
|
-
* @example
|
|
88
|
-
* toApiFolderPath("src/Assets/Images") // "/contents/assets/Images"
|
|
89
|
-
* toApiFolderPath("src/Assets") // "/contents/assets"
|
|
90
|
-
*/
|
|
91
|
-
export const toApiFolderPath = (localFolderPath) => {
|
|
92
|
-
// Handle undefined or empty paths
|
|
93
|
-
if (!localFolderPath) {
|
|
94
|
-
return '/contents/assets';
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Normalize the path
|
|
98
|
-
const normalized = path.normalize(localFolderPath);
|
|
99
|
-
|
|
100
|
-
// Remove EXPORT_ROOT from path (handle both absolute and relative paths)
|
|
101
|
-
let relative;
|
|
102
|
-
if (path.isAbsolute(normalized)) {
|
|
103
|
-
// For absolute paths, find and remove everything up to and including EXPORT_ROOT
|
|
104
|
-
const exportRootPattern = new RegExp(`.*[\\\\/]${EXPORT_ROOT}[\\\\/]`);
|
|
105
|
-
relative = normalized.replace(exportRootPattern, '');
|
|
106
|
-
} else {
|
|
107
|
-
// For relative paths, just remove EXPORT_ROOT prefix
|
|
108
|
-
relative = normalized.replace(new RegExp(`^${EXPORT_ROOT}[\\\\/]?`), '');
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Replace 'Assets' with 'contents/assets' (case insensitive search, but replace with lowercase)
|
|
112
|
-
let apiPath = relative.replace(/^Assets/i, 'contents/assets');
|
|
113
|
-
|
|
114
|
-
// Normalize to forward slashes (preserve casing!)
|
|
115
|
-
apiPath = apiPath.replace(/\\/g, '/');
|
|
116
|
-
|
|
117
|
-
// Handle edge case where path is just 'Assets'
|
|
118
|
-
if (apiPath === 'contents/assets' || apiPath === '') {
|
|
119
|
-
return '/contents/assets';
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Ensure it starts with /
|
|
123
|
-
return apiPath.startsWith('/') ? apiPath : `/${apiPath}`;
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Check if a local path is within the Assets directory.
|
|
128
|
-
*
|
|
129
|
-
* @param {string} localPath - Local file path
|
|
130
|
-
* @returns {boolean} True if path is within Assets directory
|
|
131
|
-
*
|
|
132
|
-
* @example
|
|
133
|
-
* isAssetPath("src/Assets/images/logo.png") // true
|
|
134
|
-
* isAssetPath("src/Classes/MyClass.ac") // false
|
|
135
|
-
*/
|
|
136
|
-
export const isAssetPath = (localPath) => {
|
|
137
|
-
const normalized = path.normalize(localPath);
|
|
138
|
-
const assetsDir = path.join(EXPORT_ROOT, 'Assets');
|
|
139
|
-
return normalized.startsWith(assetsDir);
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Extract the folder path from a full file path (removes filename).
|
|
144
|
-
* Returns path relative to Assets root.
|
|
145
|
-
*
|
|
146
|
-
* @param {string} filePath - Full file path
|
|
147
|
-
* @returns {string} Folder path relative to EXPORT_ROOT
|
|
148
|
-
*
|
|
149
|
-
* @example
|
|
150
|
-
* getAssetFolder("src/Assets/images/logo.png") // "Assets/images"
|
|
151
|
-
* getAssetFolder("src/Assets/logo.png") // "Assets"
|
|
152
|
-
*/
|
|
153
|
-
export const getAssetFolder = (filePath) => {
|
|
154
|
-
const normalized = path.normalize(filePath);
|
|
155
|
-
const relative = normalized.replace(new RegExp(`^${EXPORT_ROOT}[\\\\/]?`), '');
|
|
156
|
-
const dirPath = path.dirname(relative);
|
|
157
|
-
return dirPath === '.' ? 'Assets' : dirPath;
|
|
158
|
-
};
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { EXPORT_ROOT } from '../vars/global.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Asset path utilities for handling the difference between local and API paths.
|
|
6
|
+
*
|
|
7
|
+
* Local structure: src/Assets/...
|
|
8
|
+
* API structure: /contents/assets/...
|
|
9
|
+
*
|
|
10
|
+
* These helpers abstract away the "Contents" prefix requirement for the Magentrix API.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Convert a local asset path to an API path by adding the 'contents' prefix
|
|
15
|
+
* and normalizing with forward slashes (preserves original casing).
|
|
16
|
+
*
|
|
17
|
+
* @param {string} localPath - Local file path (e.g., "src/Assets/Images/Logo.png")
|
|
18
|
+
* @returns {string} API path (e.g., "/contents/assets/Images")
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* toApiPath("src/Assets/Images/Logo.png") // "/contents/assets/Images"
|
|
22
|
+
* toApiPath("src/Assets") // "/contents/assets"
|
|
23
|
+
*/
|
|
24
|
+
export const toApiPath = (localPath) => {
|
|
25
|
+
// Handle undefined or empty paths
|
|
26
|
+
if (!localPath) {
|
|
27
|
+
return '/contents/assets';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Normalize the path
|
|
31
|
+
const normalized = path.normalize(localPath);
|
|
32
|
+
|
|
33
|
+
// Remove EXPORT_ROOT from path (handle both absolute and relative paths)
|
|
34
|
+
let relative;
|
|
35
|
+
if (path.isAbsolute(normalized)) {
|
|
36
|
+
// For absolute paths, find and remove everything up to and including EXPORT_ROOT
|
|
37
|
+
const exportRootPattern = new RegExp(`.*[\\\\/]${EXPORT_ROOT}[\\\\/]`);
|
|
38
|
+
relative = normalized.replace(exportRootPattern, '');
|
|
39
|
+
} else {
|
|
40
|
+
// For relative paths, just remove EXPORT_ROOT prefix
|
|
41
|
+
relative = normalized.replace(new RegExp(`^${EXPORT_ROOT}[\\\\/]?`), '');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Replace 'Assets' with 'contents/assets' (case insensitive search, but replace with lowercase)
|
|
45
|
+
const apiPath = relative.replace(/^Assets/i, 'contents/assets');
|
|
46
|
+
|
|
47
|
+
// Normalize to forward slashes (preserve casing!), remove filename
|
|
48
|
+
let dirPath = path.dirname(apiPath).replace(/\\/g, '/');
|
|
49
|
+
|
|
50
|
+
// Handle edge case where dirname returns '.' for root
|
|
51
|
+
if (dirPath === '.') {
|
|
52
|
+
dirPath = 'contents/assets';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Ensure it starts with /
|
|
56
|
+
return dirPath.startsWith('/') ? dirPath : `/${dirPath}`;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Convert an API path to a local path by removing the 'contents' prefix.
|
|
61
|
+
* Returns a relative path (without EXPORT_ROOT) that can be joined with EXPORT_ROOT.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} apiPath - API path (e.g., "/contents/assets/images/logo.png")
|
|
64
|
+
* @returns {string} Relative local path (e.g., "Assets/images/logo.png")
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* toLocalPath("/contents/assets/images") // "Assets/images"
|
|
68
|
+
* toLocalPath("/contents/assets") // "Assets"
|
|
69
|
+
*/
|
|
70
|
+
export const toLocalPath = (apiPath) => {
|
|
71
|
+
// Remove leading slash and 'contents/' prefix
|
|
72
|
+
const cleaned = apiPath.replace(/^\/+/, '').replace(/^contents\//i, '');
|
|
73
|
+
|
|
74
|
+
// Capitalize 'assets' to 'Assets'
|
|
75
|
+
const withCapitalAssets = cleaned.replace(/^assets/i, 'Assets');
|
|
76
|
+
|
|
77
|
+
return withCapitalAssets;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Convert a local folder path to an API path (keeps the folder, doesn't extract parent).
|
|
82
|
+
* Similar to toApiPath but for folders (preserves original casing).
|
|
83
|
+
*
|
|
84
|
+
* @param {string} localFolderPath - Local folder path (e.g., "src/Assets/Images")
|
|
85
|
+
* @returns {string} API path (e.g., "/contents/assets/Images")
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* toApiFolderPath("src/Assets/Images") // "/contents/assets/Images"
|
|
89
|
+
* toApiFolderPath("src/Assets") // "/contents/assets"
|
|
90
|
+
*/
|
|
91
|
+
export const toApiFolderPath = (localFolderPath) => {
|
|
92
|
+
// Handle undefined or empty paths
|
|
93
|
+
if (!localFolderPath) {
|
|
94
|
+
return '/contents/assets';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Normalize the path
|
|
98
|
+
const normalized = path.normalize(localFolderPath);
|
|
99
|
+
|
|
100
|
+
// Remove EXPORT_ROOT from path (handle both absolute and relative paths)
|
|
101
|
+
let relative;
|
|
102
|
+
if (path.isAbsolute(normalized)) {
|
|
103
|
+
// For absolute paths, find and remove everything up to and including EXPORT_ROOT
|
|
104
|
+
const exportRootPattern = new RegExp(`.*[\\\\/]${EXPORT_ROOT}[\\\\/]`);
|
|
105
|
+
relative = normalized.replace(exportRootPattern, '');
|
|
106
|
+
} else {
|
|
107
|
+
// For relative paths, just remove EXPORT_ROOT prefix
|
|
108
|
+
relative = normalized.replace(new RegExp(`^${EXPORT_ROOT}[\\\\/]?`), '');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Replace 'Assets' with 'contents/assets' (case insensitive search, but replace with lowercase)
|
|
112
|
+
let apiPath = relative.replace(/^Assets/i, 'contents/assets');
|
|
113
|
+
|
|
114
|
+
// Normalize to forward slashes (preserve casing!)
|
|
115
|
+
apiPath = apiPath.replace(/\\/g, '/');
|
|
116
|
+
|
|
117
|
+
// Handle edge case where path is just 'Assets'
|
|
118
|
+
if (apiPath === 'contents/assets' || apiPath === '') {
|
|
119
|
+
return '/contents/assets';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Ensure it starts with /
|
|
123
|
+
return apiPath.startsWith('/') ? apiPath : `/${apiPath}`;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Check if a local path is within the Assets directory.
|
|
128
|
+
*
|
|
129
|
+
* @param {string} localPath - Local file path
|
|
130
|
+
* @returns {boolean} True if path is within Assets directory
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* isAssetPath("src/Assets/images/logo.png") // true
|
|
134
|
+
* isAssetPath("src/Classes/MyClass.ac") // false
|
|
135
|
+
*/
|
|
136
|
+
export const isAssetPath = (localPath) => {
|
|
137
|
+
const normalized = path.normalize(localPath);
|
|
138
|
+
const assetsDir = path.join(EXPORT_ROOT, 'Assets');
|
|
139
|
+
return normalized.startsWith(assetsDir);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Extract the folder path from a full file path (removes filename).
|
|
144
|
+
* Returns path relative to Assets root.
|
|
145
|
+
*
|
|
146
|
+
* @param {string} filePath - Full file path
|
|
147
|
+
* @returns {string} Folder path relative to EXPORT_ROOT
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* getAssetFolder("src/Assets/images/logo.png") // "Assets/images"
|
|
151
|
+
* getAssetFolder("src/Assets/logo.png") // "Assets"
|
|
152
|
+
*/
|
|
153
|
+
export const getAssetFolder = (filePath) => {
|
|
154
|
+
const normalized = path.normalize(filePath);
|
|
155
|
+
const relative = normalized.replace(new RegExp(`^${EXPORT_ROOT}[\\\\/]?`), '');
|
|
156
|
+
const dirPath = path.dirname(relative);
|
|
157
|
+
return dirPath === '.' ? 'Assets' : dirPath;
|
|
158
|
+
};
|
package/utils/autopublishLock.js
CHANGED
|
@@ -1,77 +1,77 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
|
|
4
|
-
const LOCK_FILE_NAME = 'autopublish.lock';
|
|
5
|
-
const LOCK_EXPIRY_MS = 3600000; // 1 hour
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Get the path to the autopublish lock file.
|
|
9
|
-
* @returns {string} - Path to lock file
|
|
10
|
-
*/
|
|
11
|
-
export function getLockFilePath() {
|
|
12
|
-
return path.join(process.cwd(), '.magentrix', LOCK_FILE_NAME);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Check if autopublish is currently running.
|
|
17
|
-
* @returns {boolean} - True if autopublish is running (lock file exists and is not stale)
|
|
18
|
-
*/
|
|
19
|
-
export function isAutopublishRunning() {
|
|
20
|
-
const lockFile = getLockFilePath();
|
|
21
|
-
|
|
22
|
-
if (!fs.existsSync(lockFile)) return false;
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
const lockData = JSON.parse(fs.readFileSync(lockFile, 'utf-8'));
|
|
26
|
-
const lockAge = Date.now() - lockData.timestamp;
|
|
27
|
-
|
|
28
|
-
// If lock is older than 1 hour, consider it stale
|
|
29
|
-
return lockAge < LOCK_EXPIRY_MS;
|
|
30
|
-
} catch {
|
|
31
|
-
// If we can't read the lock file, assume it's not running
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Create a lock file to prevent multiple autopublish instances.
|
|
38
|
-
* @returns {boolean} - True if lock was acquired, false if already locked
|
|
39
|
-
*/
|
|
40
|
-
export function acquireAutopublishLock() {
|
|
41
|
-
const lockFile = getLockFilePath();
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
if (fs.existsSync(lockFile)) {
|
|
45
|
-
// Check if the lock is stale (process might have crashed)
|
|
46
|
-
if (isAutopublishRunning()) {
|
|
47
|
-
return false; // Lock is active
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Create lock file
|
|
52
|
-
fs.mkdirSync(path.dirname(lockFile), { recursive: true });
|
|
53
|
-
fs.writeFileSync(lockFile, JSON.stringify({
|
|
54
|
-
pid: process.pid,
|
|
55
|
-
timestamp: Date.now()
|
|
56
|
-
}));
|
|
57
|
-
|
|
58
|
-
return true;
|
|
59
|
-
} catch (err) {
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Release the autopublish lock file.
|
|
66
|
-
*/
|
|
67
|
-
export function releaseAutopublishLock() {
|
|
68
|
-
const lockFile = getLockFilePath();
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
if (fs.existsSync(lockFile)) {
|
|
72
|
-
fs.unlinkSync(lockFile);
|
|
73
|
-
}
|
|
74
|
-
} catch {
|
|
75
|
-
// Ignore errors during cleanup
|
|
76
|
-
}
|
|
77
|
-
}
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
const LOCK_FILE_NAME = 'autopublish.lock';
|
|
5
|
+
const LOCK_EXPIRY_MS = 3600000; // 1 hour
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get the path to the autopublish lock file.
|
|
9
|
+
* @returns {string} - Path to lock file
|
|
10
|
+
*/
|
|
11
|
+
export function getLockFilePath() {
|
|
12
|
+
return path.join(process.cwd(), '.magentrix', LOCK_FILE_NAME);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Check if autopublish is currently running.
|
|
17
|
+
* @returns {boolean} - True if autopublish is running (lock file exists and is not stale)
|
|
18
|
+
*/
|
|
19
|
+
export function isAutopublishRunning() {
|
|
20
|
+
const lockFile = getLockFilePath();
|
|
21
|
+
|
|
22
|
+
if (!fs.existsSync(lockFile)) return false;
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const lockData = JSON.parse(fs.readFileSync(lockFile, 'utf-8'));
|
|
26
|
+
const lockAge = Date.now() - lockData.timestamp;
|
|
27
|
+
|
|
28
|
+
// If lock is older than 1 hour, consider it stale
|
|
29
|
+
return lockAge < LOCK_EXPIRY_MS;
|
|
30
|
+
} catch {
|
|
31
|
+
// If we can't read the lock file, assume it's not running
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Create a lock file to prevent multiple autopublish instances.
|
|
38
|
+
* @returns {boolean} - True if lock was acquired, false if already locked
|
|
39
|
+
*/
|
|
40
|
+
export function acquireAutopublishLock() {
|
|
41
|
+
const lockFile = getLockFilePath();
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
if (fs.existsSync(lockFile)) {
|
|
45
|
+
// Check if the lock is stale (process might have crashed)
|
|
46
|
+
if (isAutopublishRunning()) {
|
|
47
|
+
return false; // Lock is active
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Create lock file
|
|
52
|
+
fs.mkdirSync(path.dirname(lockFile), { recursive: true });
|
|
53
|
+
fs.writeFileSync(lockFile, JSON.stringify({
|
|
54
|
+
pid: process.pid,
|
|
55
|
+
timestamp: Date.now()
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
return true;
|
|
59
|
+
} catch (err) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Release the autopublish lock file.
|
|
66
|
+
*/
|
|
67
|
+
export function releaseAutopublishLock() {
|
|
68
|
+
const lockFile = getLockFilePath();
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
if (fs.existsSync(lockFile)) {
|
|
72
|
+
fs.unlinkSync(lockFile);
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
// Ignore errors during cleanup
|
|
76
|
+
}
|
|
77
|
+
}
|