@sanlam-fintech-digital/mfe-platform-cli 0.2.8 → 0.4.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/dist/nuget/manager.js +45 -13
- package/dist/types/config.js +36 -0
- package/package.json +1 -1
package/dist/nuget/manager.js
CHANGED
|
@@ -13,25 +13,59 @@ const os_1 = __importDefault(require("os"));
|
|
|
13
13
|
const chalk_1 = __importDefault(require("chalk"));
|
|
14
14
|
const fast_xml_parser_1 = require("fast-xml-parser");
|
|
15
15
|
/**
|
|
16
|
-
* Get platform-specific NuGet config
|
|
16
|
+
* Get platform-specific NuGet config paths (primary and legacy fallback)
|
|
17
17
|
*/
|
|
18
|
-
function
|
|
19
|
-
if (configPath)
|
|
20
|
-
return configPath;
|
|
18
|
+
function getNugetConfigPaths() {
|
|
21
19
|
const platform = process.platform;
|
|
22
20
|
if (platform === 'win32') {
|
|
23
21
|
const appData = process.env.APPDATA || path_1.default.join(os_1.default.homedir(), 'AppData', 'Roaming');
|
|
24
|
-
|
|
22
|
+
const primary = path_1.default.join(appData, 'NuGet', 'NuGet.Config');
|
|
23
|
+
return { primary, legacy: null };
|
|
25
24
|
}
|
|
26
25
|
else {
|
|
27
|
-
|
|
26
|
+
const primary = path_1.default.join(os_1.default.homedir(), '.nuget', 'NuGet', 'NuGet.Config');
|
|
27
|
+
const legacy = path_1.default.join(os_1.default.homedir(), '.config', 'NuGet', 'NuGet.Config');
|
|
28
|
+
return { primary, legacy };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolve the NuGet config path to use, checking both new and legacy locations.
|
|
33
|
+
* If a custom path is provided it is used as-is.
|
|
34
|
+
* Otherwise the new primary path (~/.nuget/NuGet/NuGet.Config) takes precedence,
|
|
35
|
+
* falling back to the legacy path (~/.config/NuGet/NuGet.Config) when it exists
|
|
36
|
+
* and the primary does not.
|
|
37
|
+
*/
|
|
38
|
+
async function resolveNugetConfigPath(configPath) {
|
|
39
|
+
if (configPath)
|
|
40
|
+
return configPath;
|
|
41
|
+
const { primary, legacy } = getNugetConfigPaths();
|
|
42
|
+
if (!legacy)
|
|
43
|
+
return primary;
|
|
44
|
+
// Prefer primary; fall back to legacy only when primary is absent (ENOENT)
|
|
45
|
+
try {
|
|
46
|
+
await promises_1.default.access(primary);
|
|
47
|
+
return primary;
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
// Only fall back to legacy when the primary path truly does not exist.
|
|
51
|
+
// For any other error (e.g. EACCES, EIO) keep the primary path so the
|
|
52
|
+
// caller sees the real failure rather than silently switching paths.
|
|
53
|
+
if (err.code !== 'ENOENT')
|
|
54
|
+
return primary;
|
|
55
|
+
try {
|
|
56
|
+
await promises_1.default.access(legacy);
|
|
57
|
+
return legacy;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return primary;
|
|
61
|
+
}
|
|
28
62
|
}
|
|
29
63
|
}
|
|
30
64
|
/**
|
|
31
65
|
* Backup existing NuGet.config
|
|
32
66
|
*/
|
|
33
67
|
async function backupNugetConfig(configPath) {
|
|
34
|
-
const nugetPath =
|
|
68
|
+
const nugetPath = await resolveNugetConfigPath(configPath);
|
|
35
69
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
36
70
|
const backupPath = `${nugetPath}.backup.${timestamp}`;
|
|
37
71
|
try {
|
|
@@ -49,7 +83,7 @@ async function backupNugetConfig(configPath) {
|
|
|
49
83
|
* Read and parse NuGet.config
|
|
50
84
|
*/
|
|
51
85
|
async function readNugetConfig(configPath) {
|
|
52
|
-
const nugetPath =
|
|
86
|
+
const nugetPath = await resolveNugetConfigPath(configPath);
|
|
53
87
|
try {
|
|
54
88
|
const content = await promises_1.default.readFile(nugetPath, 'utf-8');
|
|
55
89
|
const parser = new fast_xml_parser_1.XMLParser({
|
|
@@ -103,9 +137,7 @@ async function addNuGetFeeds(feeds, tokenMap, configPath) {
|
|
|
103
137
|
// Add credentials if token provided
|
|
104
138
|
const token = tokenMap?.get(feed.url);
|
|
105
139
|
if (token) {
|
|
106
|
-
|
|
107
|
-
const credKey = sourceName.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
108
|
-
credentials[credKey] = {
|
|
140
|
+
credentials[sourceName] = {
|
|
109
141
|
add: [
|
|
110
142
|
{ '@_key': 'Username', '@_value': 'PersonalAccessToken' },
|
|
111
143
|
{ '@_key': 'ClearTextPassword', '@_value': token }
|
|
@@ -119,7 +151,7 @@ async function addNuGetFeeds(feeds, tokenMap, configPath) {
|
|
|
119
151
|
* Write NuGet.config
|
|
120
152
|
*/
|
|
121
153
|
async function writeNugetConfig(config, configPath) {
|
|
122
|
-
const nugetPath =
|
|
154
|
+
const nugetPath = await resolveNugetConfigPath(configPath);
|
|
123
155
|
const dir = path_1.default.dirname(nugetPath);
|
|
124
156
|
// Ensure directory exists
|
|
125
157
|
await promises_1.default.mkdir(dir, { recursive: true });
|
|
@@ -140,6 +172,6 @@ async function writeNugetConfig(config, configPath) {
|
|
|
140
172
|
* Restore from backup
|
|
141
173
|
*/
|
|
142
174
|
async function restoreNugetFromBackup(backupPath, configPath) {
|
|
143
|
-
const nugetPath =
|
|
175
|
+
const nugetPath = await resolveNugetConfigPath(configPath);
|
|
144
176
|
await promises_1.default.copyFile(backupPath, nugetPath);
|
|
145
177
|
}
|
package/dist/types/config.js
CHANGED
|
@@ -6,6 +6,29 @@ const zod_1 = require("zod");
|
|
|
6
6
|
* Package feed type
|
|
7
7
|
*/
|
|
8
8
|
const FeedTypeSchema = zod_1.z.enum(['npm', 'nuget']).default('npm');
|
|
9
|
+
/**
|
|
10
|
+
* Validates if a string is a valid XML element name.
|
|
11
|
+
* XML 1.0 specification (https://www.w3.org/TR/xml/#NT-Name):
|
|
12
|
+
* - Must start with a letter (A-Z, a-z) or underscore (_)
|
|
13
|
+
* - Can contain letters, digits, hyphens (-), underscores (_), and periods (.)
|
|
14
|
+
* - Cannot start with "xml" (case-insensitive)
|
|
15
|
+
* - Cannot contain spaces or special characters like @, $, %, etc.
|
|
16
|
+
*/
|
|
17
|
+
function isValidXmlElementName(name) {
|
|
18
|
+
// Empty string is invalid
|
|
19
|
+
if (!name)
|
|
20
|
+
return false;
|
|
21
|
+
// Must start with letter or underscore (not digit, hyphen, or period)
|
|
22
|
+
if (!/^[A-Za-z_]/.test(name))
|
|
23
|
+
return false;
|
|
24
|
+
// Can only contain letters, digits, hyphens, underscores, and periods
|
|
25
|
+
if (!/^[A-Za-z_][A-Za-z0-9._-]*$/.test(name))
|
|
26
|
+
return false;
|
|
27
|
+
// Cannot start with "xml" (case-insensitive)
|
|
28
|
+
if (/^xml/i.test(name))
|
|
29
|
+
return false;
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
9
32
|
/**
|
|
10
33
|
* Individual registry entry (npm or NuGet feed)
|
|
11
34
|
*/
|
|
@@ -13,6 +36,19 @@ const RegistryEntrySchema = zod_1.z.object({
|
|
|
13
36
|
scope: zod_1.z.string().describe('npm scope (e.g., "@org/package") or NuGet source name'),
|
|
14
37
|
url: zod_1.z.url().describe('Registry/Feed URL (Azure DevOps, GitHub, or public)').transform((url) => url.replace(/\/+$/, '')),
|
|
15
38
|
feedType: FeedTypeSchema.optional().describe('Package feed type (defaults to npm for backward compatibility)'),
|
|
39
|
+
}).superRefine((data, ctx) => {
|
|
40
|
+
// For NuGet feeds, the scope becomes an XML element name in NuGet.Config
|
|
41
|
+
// Validate it meets XML element name requirements
|
|
42
|
+
const feedType = data.feedType || 'npm';
|
|
43
|
+
if (feedType === 'nuget' && !isValidXmlElementName(data.scope)) {
|
|
44
|
+
ctx.addIssue({
|
|
45
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
46
|
+
message: `NuGet source name "${data.scope}" is not a valid XML element name. ` +
|
|
47
|
+
`It must start with a letter or underscore, contain only letters, digits, hyphens, underscores, and periods, ` +
|
|
48
|
+
`and cannot start with "xml" (case-insensitive).`,
|
|
49
|
+
path: ['scope'],
|
|
50
|
+
});
|
|
51
|
+
}
|
|
16
52
|
});
|
|
17
53
|
/**
|
|
18
54
|
* Azure DevOps auth group (requires tenantId)
|