@equinor/fusion-framework-cli 11.2.0 → 11.3.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/CHANGELOG.md +123 -0
- package/README.md +17 -0
- package/bin/build/cli.mjs +3 -3
- package/dist/esm/lib/utils/assert.js +70 -15
- package/dist/esm/lib/utils/assert.js.map +1 -1
- package/dist/esm/lib/utils/is-git-dir.js +19 -0
- package/dist/esm/lib/utils/is-git-dir.js.map +1 -0
- package/dist/esm/lib/utils/package-info.js +135 -0
- package/dist/esm/lib/utils/package-info.js.map +1 -0
- package/dist/esm/lib/utils/path-security.js +56 -0
- package/dist/esm/lib/utils/path-security.js.map +1 -0
- package/dist/esm/version.js +1 -1
- package/dist/types/bin/helpers/ProjectTemplate.d.ts +61 -0
- package/dist/types/bin/helpers/ProjectTemplateRepository.d.ts +113 -0
- package/dist/types/bin/helpers/install-package-dependencies.d.ts +11 -0
- package/dist/types/bin/helpers/project-templates.schema.d.ts +301 -0
- package/dist/types/cli/commands/create/_helpers/check-target-directory.d.ts +12 -0
- package/dist/types/cli/commands/create/_helpers/cleanup-template-files.d.ts +15 -0
- package/dist/types/cli/commands/create/_helpers/install-dependencies.d.ts +14 -0
- package/dist/types/cli/commands/create/_helpers/open-in-ide.d.ts +15 -0
- package/dist/types/cli/commands/create/_helpers/resolve-workspace-dependencies.d.ts +27 -0
- package/dist/types/cli/commands/create/_helpers/select-template.d.ts +24 -0
- package/dist/types/cli/commands/create/_helpers/setup-repository.d.ts +23 -0
- package/dist/types/cli/commands/create/_helpers/start-dev-server.d.ts +17 -0
- package/dist/types/cli/commands/create/_helpers/update-package-json.d.ts +41 -0
- package/dist/types/cli/commands/create/app.d.ts +28 -0
- package/dist/types/cli/commands/create/index.d.ts +2 -0
- package/dist/types/lib/utils/assert.d.ts +61 -13
- package/dist/types/lib/utils/is-git-dir.d.ts +9 -0
- package/dist/types/lib/utils/package-info.d.ts +106 -0
- package/dist/types/lib/utils/path-security.d.ts +36 -0
- package/dist/types/version.d.ts +1 -1
- package/docs/creating-apps.md +275 -0
- package/package.json +14 -5
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
import assert, { AssertionError } from 'node:assert';
|
|
2
2
|
import { fileExists } from './file-exists.js';
|
|
3
|
+
import { isGitDir } from './is-git-dir.js';
|
|
3
4
|
/**
|
|
4
5
|
* Re-exports the core Node.js assert function and AssertionError class.
|
|
5
|
-
*
|
|
6
|
+
*
|
|
7
|
+
* This provides consistent assertion handling throughout the codebase
|
|
8
|
+
* with proper TypeScript type narrowing support.
|
|
6
9
|
*/
|
|
7
10
|
export { assert, AssertionError };
|
|
8
11
|
/**
|
|
9
12
|
* Asserts that the provided value is a valid number (not NaN).
|
|
10
|
-
* Throws an AssertionError if the value is not a number.
|
|
11
13
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
14
|
+
* This function checks that the value is not NaN, which is useful for
|
|
15
|
+
* validating numeric inputs that might be strings or other types that
|
|
16
|
+
* could convert to NaN.
|
|
17
|
+
*
|
|
18
|
+
* @param value - The value to check for being a valid number
|
|
19
|
+
* @param message - Optional custom error message for assertion failure
|
|
20
|
+
* @throws {AssertionError} If value is NaN
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* assertNumber(42); // ✅ Passes
|
|
25
|
+
* assertNumber('42'); // ✅ Passes (string converts to number)
|
|
26
|
+
* assertNumber(NaN); // ❌ Throws AssertionError
|
|
27
|
+
* assertNumber('invalid'); // ❌ Throws AssertionError
|
|
28
|
+
* ```
|
|
15
29
|
*/
|
|
16
30
|
export function assertNumber(value, message) {
|
|
17
31
|
// Ensure the value is not NaN; this does not check for type 'number'.
|
|
@@ -23,26 +37,44 @@ export function assertNumber(value, message) {
|
|
|
23
37
|
}
|
|
24
38
|
/**
|
|
25
39
|
* Asserts that a file exists at the given path.
|
|
26
|
-
* Throws an error if the file does not exist.
|
|
27
40
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
41
|
+
* This function uses the fileExists utility to check for file presence
|
|
42
|
+
* and throws an AssertionError if the file is not found.
|
|
43
|
+
*
|
|
44
|
+
* @param value - The file path to check
|
|
45
|
+
* @param message - Optional custom error message for assertion failure
|
|
46
|
+
* @throws {AssertionError} If the file does not exist
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* assertFileExists('/path/to/file.txt'); // ✅ Passes if file exists
|
|
51
|
+
* assertFileExists('/nonexistent/file.txt'); // ❌ Throws AssertionError
|
|
52
|
+
* ```
|
|
31
53
|
*/
|
|
32
54
|
export const assertFileExists = (value, message) => {
|
|
33
|
-
// Use fileExists utility to check for file presence
|
|
55
|
+
// Use fileExists utility to check for file presence
|
|
34
56
|
assert(fileExists(value), message ?? `file ${String(value)} does not exist`);
|
|
35
57
|
};
|
|
36
58
|
/**
|
|
37
59
|
* Asserts that the provided value is an object.
|
|
38
|
-
* Throws an error if the value is not an object.
|
|
39
60
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
61
|
+
* This function checks that the value has type 'object'. Note that
|
|
62
|
+
* typeof null is 'object' in JavaScript, so null values will pass this check.
|
|
63
|
+
*
|
|
64
|
+
* @param value - The value to check for being an object
|
|
65
|
+
* @param message - Optional custom error message or Error instance
|
|
66
|
+
* @throws {AssertionError} If value is not an object
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```typescript
|
|
70
|
+
* assertObject({}); // ✅ Passes
|
|
71
|
+
* assertObject([]); // ✅ Passes
|
|
72
|
+
* assertObject(null); // ✅ Passes (typeof null === 'object')
|
|
73
|
+
* assertObject('string'); // ❌ Throws AssertionError
|
|
74
|
+
* ```
|
|
43
75
|
*/
|
|
44
76
|
export function assertObject(value, message) {
|
|
45
|
-
// typeof null is 'object', so this does not exclude null values
|
|
77
|
+
// typeof null is 'object', so this does not exclude null values
|
|
46
78
|
assert(typeof value === 'object', message);
|
|
47
79
|
}
|
|
48
80
|
/**
|
|
@@ -84,4 +116,27 @@ export function assertObjectEntries(value, options) {
|
|
|
84
116
|
assertion(value[prop], prop, `${preMessage} property [${String(prop)}] to have value`);
|
|
85
117
|
}
|
|
86
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Asserts that a directory exists and is a valid git repository.
|
|
121
|
+
*
|
|
122
|
+
* This function checks if the specified directory contains a valid
|
|
123
|
+
* git repository by looking for the .git directory or file.
|
|
124
|
+
*
|
|
125
|
+
* @param dir - Directory path to check for git repository
|
|
126
|
+
* @param message - Optional custom error message for assertion failure
|
|
127
|
+
* @throws {AssertionError} If the directory is not a git repository
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* assertGitRepository('/path/to/git/repo'); // ✅ Passes if .git exists
|
|
132
|
+
* assertGitRepository('/path/to/regular/dir'); // ❌ Throws AssertionError
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
export function assertGitRepository(dir, message) {
|
|
136
|
+
assert(isGitDir(dir), new AssertionError({
|
|
137
|
+
message: message ?? `Directory is not a git repository: ${dir}`,
|
|
138
|
+
actual: dir,
|
|
139
|
+
expected: '<git repository>',
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
87
142
|
//# sourceMappingURL=assert.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assert.js","sourceRoot":"","sources":["../../../../src/lib/utils/assert.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"assert.js","sourceRoot":"","sources":["../../../../src/lib/utils/assert.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C;;;;;GAKG;AACH,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;AAElC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc,EAAE,OAAgB;IAC3D,sEAAsE;IACtE,MAAM,CACJ,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EACpB,IAAI,cAAc,CAAC;QACjB,OAAO;QACP,MAAM,EAAE,KAAK;QACb,QAAQ,EAAE,UAAU;KACrB,CAAC,CACH,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAc,EAAE,OAAgB,EAAiB,EAAE;IAClF,oDAAoD;IACpD,MAAM,CAAC,UAAU,CAAC,KAAe,CAAC,EAAE,OAAO,IAAI,QAAQ,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;AACzF,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,OAAwB;IAClE,gEAAgE;IAChE,MAAM,CAAC,OAAO,KAAK,KAAK,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,sBAAsB,CAAI,KAAc,EAAE,IAAO,EAAE,OAAgB;IAC1E,kEAAkE;IAClE,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,OAAO,IAAI,6BAA6B,IAAI,EAAE,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAQ,EACR,OAIC;IAED,+DAA+D;IAC/D,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,EAAE,CAAC;IAC7C,4DAA4D;IAC5D,MAAM,CAAC,OAAO,KAAK,KAAK,QAAQ,EAAE,GAAG,UAAU,oBAAoB,CAAC,CAAC;IACrE,uDAAuD;IACvD,MAAM,SAAS,GAAqC,OAAO,EAAE,SAAS,IAAI,sBAAsB,CAAC;IACjG,wDAAwD;IACxD,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,gDAAgD;QAChD,MAAM,CAAC,IAAI,IAAI,KAAK,EAAE,GAAG,UAAU,sBAAsB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1E,sDAAsD;QACtD,SAAS,CACP,KAAK,CAAC,IAAe,CAAC,EACtB,IAAoB,EACpB,GAAG,UAAU,cAAc,MAAM,CAAC,IAAI,CAAC,iBAAiB,CACzD,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW,EAAE,OAAgB;IAC/D,MAAM,CACJ,QAAQ,CAAC,GAAG,CAAC,EACb,IAAI,cAAc,CAAC;QACjB,OAAO,EAAE,OAAO,IAAI,sCAAsC,GAAG,EAAE;QAC/D,MAAM,EAAE,GAAG;QACX,QAAQ,EAAE,kBAAkB;KAC7B,CAAC,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
/**
|
|
4
|
+
* Checks if a directory exists and is a valid git repository.
|
|
5
|
+
*
|
|
6
|
+
* @param dir - Directory path to check.
|
|
7
|
+
* @returns True if the directory exists and is a git repository, false otherwise.
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
export function isGitDir(dir) {
|
|
11
|
+
if (!existsSync(dir)) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
// Check if .git directory exists (for regular git repos) or .git file exists (for worktrees)
|
|
15
|
+
const gitDir = join(dir, '.git');
|
|
16
|
+
return existsSync(gitDir);
|
|
17
|
+
}
|
|
18
|
+
export default isGitDir;
|
|
19
|
+
//# sourceMappingURL=is-git-dir.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-git-dir.js","sourceRoot":"","sources":["../../../../src/lib/utils/is-git-dir.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,6FAA6F;IAC7F,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAED,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetches complete package information from npm registry.
|
|
3
|
+
*
|
|
4
|
+
* This function retrieves all available metadata for a package including
|
|
5
|
+
* version information, dependencies, and package details. It performs
|
|
6
|
+
* validation to ensure the package exists and has a valid latest version.
|
|
7
|
+
*
|
|
8
|
+
* @param packageName - The name of the package to fetch (e.g., "@equinor/fusion-framework")
|
|
9
|
+
* @param registry - The npm registry URL (defaults to https://registry.npmjs.org)
|
|
10
|
+
* @returns Promise resolving to complete package information
|
|
11
|
+
* @throws {Error} If the package cannot be found, fetched, or has invalid data
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* // Fetch package info for a scoped package
|
|
16
|
+
* const info = await fetchPackageInfo('@equinor/fusion-framework');
|
|
17
|
+
* console.log(`Latest version: ${info.latest}`);
|
|
18
|
+
*
|
|
19
|
+
* // Fetch from custom registry
|
|
20
|
+
* const info = await fetchPackageInfo('my-package', 'https://my-registry.com');
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export async function fetchPackageInfo(packageName, registry = 'https://registry.npmjs.org') {
|
|
24
|
+
try {
|
|
25
|
+
// Make HTTP request to npm registry API
|
|
26
|
+
const response = await fetch(`${registry}/${packageName}`);
|
|
27
|
+
if (!response.ok) {
|
|
28
|
+
throw new Error(`Failed to fetch package info for ${packageName}: ${response.statusText}`);
|
|
29
|
+
}
|
|
30
|
+
// Parse JSON response from registry
|
|
31
|
+
const data = await response.json();
|
|
32
|
+
// Validate that we received valid package data
|
|
33
|
+
if (!data.name) {
|
|
34
|
+
throw new Error(`Invalid package data received for ${packageName}`);
|
|
35
|
+
}
|
|
36
|
+
// Extract latest version from dist-tags (required for package resolution)
|
|
37
|
+
const latestVersion = data['dist-tags']?.latest;
|
|
38
|
+
if (!latestVersion) {
|
|
39
|
+
throw new Error(`No latest version found for package ${packageName}`);
|
|
40
|
+
}
|
|
41
|
+
// Transform registry data to our PackageInfo interface
|
|
42
|
+
return {
|
|
43
|
+
name: data.name,
|
|
44
|
+
latest: latestVersion,
|
|
45
|
+
versions: Object.keys(data.versions || {}),
|
|
46
|
+
'dist-tags': data['dist-tags'] || {},
|
|
47
|
+
description: data.description,
|
|
48
|
+
homepage: data.homepage,
|
|
49
|
+
repository: data.repository,
|
|
50
|
+
author: data.author,
|
|
51
|
+
license: data.license,
|
|
52
|
+
keywords: data.keywords,
|
|
53
|
+
dependencies: data.dependencies,
|
|
54
|
+
devDependencies: data.devDependencies,
|
|
55
|
+
peerDependencies: data.peerDependencies,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
// Wrap any errors with context about the failed operation
|
|
60
|
+
throw new Error(`Failed to fetch package info for ${packageName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Fetches only the latest version of a package from npm registry.
|
|
65
|
+
*
|
|
66
|
+
* This is a convenience function that retrieves only the latest version
|
|
67
|
+
* string without the overhead of fetching complete package metadata.
|
|
68
|
+
* Useful for simple version checks and dependency resolution.
|
|
69
|
+
*
|
|
70
|
+
* @param packageName - The name of the package to fetch (e.g., "@equinor/fusion-framework")
|
|
71
|
+
* @param registry - The npm registry URL (defaults to https://registry.npmjs.org)
|
|
72
|
+
* @returns Promise resolving to the latest version string (e.g., "1.0.0")
|
|
73
|
+
* @throws {Error} If the package cannot be found or fetched
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* // Get latest version for dependency resolution
|
|
78
|
+
* const version = await fetchLatestVersion('@equinor/fusion-framework');
|
|
79
|
+
* console.log(`Latest version: ${version}`);
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export async function fetchLatestVersion(packageName, registry = 'https://registry.npmjs.org') {
|
|
83
|
+
// Delegate to fetchPackageInfo and extract only the latest version
|
|
84
|
+
const packageInfo = await fetchPackageInfo(packageName, registry);
|
|
85
|
+
return packageInfo.latest;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Fetches multiple packages' information in parallel for better performance.
|
|
89
|
+
*
|
|
90
|
+
* This function efficiently retrieves package information for multiple packages
|
|
91
|
+
* simultaneously, using Promise.allSettled to handle individual failures gracefully.
|
|
92
|
+
* Failed packages are silently excluded from the results.
|
|
93
|
+
*
|
|
94
|
+
* @param packageNames - Array of package names to fetch (e.g., ["@equinor/fusion-framework", "react"])
|
|
95
|
+
* @param registry - The npm registry URL (defaults to https://registry.npmjs.org)
|
|
96
|
+
* @returns Promise resolving to a map of package names to their information
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* // Fetch multiple packages for dependency analysis
|
|
101
|
+
* const packages = await fetchMultiplePackageInfo([
|
|
102
|
+
* '@equinor/fusion-framework',
|
|
103
|
+
* 'react',
|
|
104
|
+
* 'typescript'
|
|
105
|
+
* ]);
|
|
106
|
+
*
|
|
107
|
+
* // Check which packages were successfully fetched
|
|
108
|
+
* console.log(`Fetched ${Object.keys(packages).length} packages`);
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
export async function fetchMultiplePackageInfo(packageNames, registry = 'https://registry.npmjs.org') {
|
|
112
|
+
const results = {};
|
|
113
|
+
// Create promises for all packages to fetch them in parallel
|
|
114
|
+
const promises = packageNames.map(async (packageName) => {
|
|
115
|
+
try {
|
|
116
|
+
const packageInfo = await fetchPackageInfo(packageName, registry);
|
|
117
|
+
return { packageName, packageInfo };
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
// Return null for failed packages - they'll be filtered out later
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
// Wait for all promises to settle (both success and failure)
|
|
125
|
+
const settledPromises = await Promise.allSettled(promises);
|
|
126
|
+
// Process results and build the final map
|
|
127
|
+
for (const result of settledPromises) {
|
|
128
|
+
if (result.status === 'fulfilled' && result.value) {
|
|
129
|
+
const { packageName, packageInfo } = result.value;
|
|
130
|
+
results[packageName] = packageInfo;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return results;
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=package-info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-info.js","sourceRoot":"","sources":["../../../../src/lib/utils/package-info.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { rmSync } from 'node:fs';
|
|
3
|
+
import isPathInside from 'is-path-inside';
|
|
4
|
+
/**
|
|
5
|
+
* Validates that a target path is safe for file system operations.
|
|
6
|
+
*
|
|
7
|
+
* Uses the well-established `is-path-inside` library to prevent path traversal attacks
|
|
8
|
+
* by ensuring the target path is within expected bounds.
|
|
9
|
+
*
|
|
10
|
+
* @param targetPath - The path to validate
|
|
11
|
+
* @param baseDir - The base directory that the target path should be within (optional)
|
|
12
|
+
* @returns The resolved, validated path
|
|
13
|
+
* @throws {Error} If the path is invalid or potentially dangerous
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // Validate a user-provided path within a specific directory
|
|
18
|
+
* const safePath = validateSafePath(userInput, '/path/to/base/directory');
|
|
19
|
+
*
|
|
20
|
+
* // Validate a path without base directory constraint
|
|
21
|
+
* const safePath = validateSafePath('/tmp/safe-directory');
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export function validateSafePath(targetPath, baseDir) {
|
|
25
|
+
if (typeof targetPath !== 'string' || targetPath.trim() === '') {
|
|
26
|
+
throw new Error('Target path must be a non-empty string');
|
|
27
|
+
}
|
|
28
|
+
// Resolve the target path to get absolute path
|
|
29
|
+
const resolvedPath = resolve(targetPath);
|
|
30
|
+
// If baseDir is provided, ensure target path is within it using the established library
|
|
31
|
+
if (baseDir) {
|
|
32
|
+
const resolvedBaseDir = resolve(baseDir);
|
|
33
|
+
if (!isPathInside(resolvedPath, resolvedBaseDir)) {
|
|
34
|
+
throw new Error('The target path must be within the specified base directory. Please specify a relative path or ensure the absolute path is within the base directory.');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return resolvedPath;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Safely removes a directory with path traversal protection.
|
|
41
|
+
*
|
|
42
|
+
* This function validates the target path before performing the removal
|
|
43
|
+
* operation to prevent accidental deletion of unintended directories.
|
|
44
|
+
*
|
|
45
|
+
* @param targetPath - The path to remove
|
|
46
|
+
* @param options - rmSync options
|
|
47
|
+
* @param baseDir - Optional base directory constraint
|
|
48
|
+
* @throws {Error} If path validation fails or removal operation fails
|
|
49
|
+
*/
|
|
50
|
+
export function safeRmSync(targetPath, options, baseDir) {
|
|
51
|
+
// Validate the path before removal
|
|
52
|
+
const safePath = validateSafePath(targetPath, baseDir);
|
|
53
|
+
// Perform the removal operation
|
|
54
|
+
rmSync(safePath, options);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=path-security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-security.js","sourceRoot":"","sources":["../../../../src/lib/utils/path-security.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAkB,EAAE,OAAgB;IACnE,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC/D,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IAED,+CAA+C;IAC/C,MAAM,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAEzC,wFAAwF;IACxF,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAEzC,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,eAAe,CAAC,EAAE,CAAC;YACjD,MAAM,IAAI,KAAK,CACb,uJAAuJ,CACxJ,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU,CACxB,UAAkB,EAClB,OAA+C,EAC/C,OAAgB;IAEhB,mCAAmC;IACnC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAEvD,gCAAgC;IAChC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC"}
|
package/dist/esm/version.js
CHANGED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { ConsoleLogger } from '@equinor/fusion-framework-cli/bin';
|
|
2
|
+
import type { TemplateResource, TemplateItem } from './project-templates.schema.js';
|
|
3
|
+
/**
|
|
4
|
+
* Represents a template for creating Fusion Framework applications.
|
|
5
|
+
*
|
|
6
|
+
* This class encapsulates a project template definition and provides methods
|
|
7
|
+
* for copying template resources to a target directory. Templates are defined
|
|
8
|
+
* in the templates.json manifest and can include files, directories, and other
|
|
9
|
+
* resources that make up a complete project structure.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const template = new ProjectTemplate(templateItem, '/path/to/source', { logger });
|
|
14
|
+
* template.copyTo('/path/to/target');
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare class ProjectTemplate {
|
|
18
|
+
#private;
|
|
19
|
+
/**
|
|
20
|
+
* The name of the template as defined in the manifest.
|
|
21
|
+
* Used for template identification and selection.
|
|
22
|
+
*/
|
|
23
|
+
get name(): string;
|
|
24
|
+
/**
|
|
25
|
+
* The description of the template as defined in the manifest.
|
|
26
|
+
* Provides human-readable information about what the template creates.
|
|
27
|
+
*/
|
|
28
|
+
get description(): string;
|
|
29
|
+
/**
|
|
30
|
+
* The resources included in this template.
|
|
31
|
+
* Each resource defines a file or directory to be copied to the target.
|
|
32
|
+
*/
|
|
33
|
+
get resources(): TemplateResource[];
|
|
34
|
+
/**
|
|
35
|
+
* Creates a new ProjectTemplate instance.
|
|
36
|
+
*
|
|
37
|
+
* @param item - The template item definition from the manifest
|
|
38
|
+
* @param source - The source directory path where template files are located
|
|
39
|
+
* @param options - Configuration options including optional logger
|
|
40
|
+
*/
|
|
41
|
+
constructor(item: TemplateItem, source: string, options: {
|
|
42
|
+
logger?: ConsoleLogger;
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* Copies all template resources to the specified target directory.
|
|
46
|
+
*
|
|
47
|
+
* This method iterates through all resources defined in the template and copies
|
|
48
|
+
* them to the target directory. Files are copied using copyFileSync, while
|
|
49
|
+
* directories are copied using cpSync with optional recursive copying.
|
|
50
|
+
*
|
|
51
|
+
* @param targetDir - The target directory where resources should be copied
|
|
52
|
+
* @throws {Error} If any resource fails to copy
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* const template = new ProjectTemplate(templateItem, '/source', { logger });
|
|
57
|
+
* template.copyTo('/path/to/new/project');
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
copyTo(targetDir: string): void;
|
|
61
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { ConsoleLogger } from '@equinor/fusion-framework-cli/bin';
|
|
2
|
+
import { ProjectTemplate } from './ProjectTemplate.js';
|
|
3
|
+
/**
|
|
4
|
+
* Git protocol options for repository operations.
|
|
5
|
+
* Supports both HTTPS and SSH authentication methods.
|
|
6
|
+
*/
|
|
7
|
+
export type GitClientProtocol = 'https' | 'ssh';
|
|
8
|
+
/**
|
|
9
|
+
* Manages a Git repository containing project templates.
|
|
10
|
+
*
|
|
11
|
+
* This class handles cloning, updating, and managing a template repository
|
|
12
|
+
* that contains project templates defined in a templates.json manifest.
|
|
13
|
+
* It provides methods for initializing the repository, fetching templates,
|
|
14
|
+
* and cleaning up temporary files.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const repo = new ProjectTemplateRepository('equinor/fusion-app-template', {
|
|
19
|
+
* baseDir: '/tmp/templates',
|
|
20
|
+
* log: logger,
|
|
21
|
+
* protocol: 'https',
|
|
22
|
+
* branch: 'main'
|
|
23
|
+
* });
|
|
24
|
+
* await repo.initialize();
|
|
25
|
+
* const templates = await repo.getAvailableTemplates();
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare class ProjectTemplateRepository {
|
|
29
|
+
#private;
|
|
30
|
+
readonly repo: string;
|
|
31
|
+
/**
|
|
32
|
+
* Gets the current Git protocol being used for repository operations.
|
|
33
|
+
* @returns The current protocol ('https' or 'ssh')
|
|
34
|
+
*/
|
|
35
|
+
get protocol(): 'https' | 'ssh';
|
|
36
|
+
/**
|
|
37
|
+
* Sets the Git protocol for repository operations.
|
|
38
|
+
* @param protocol - The protocol to use ('https' or 'ssh')
|
|
39
|
+
* @throws {AssertionError} If protocol is not 'https' or 'ssh'
|
|
40
|
+
*/
|
|
41
|
+
set protocol(protocol: GitClientProtocol);
|
|
42
|
+
/**
|
|
43
|
+
* Gets the current branch being used for repository operations.
|
|
44
|
+
* @returns The current branch name
|
|
45
|
+
*/
|
|
46
|
+
get branch(): string;
|
|
47
|
+
/**
|
|
48
|
+
* Gets the full GitHub URL for the repository based on the current protocol.
|
|
49
|
+
* @returns The complete GitHub URL (HTTPS or SSH format)
|
|
50
|
+
*/
|
|
51
|
+
private get repoUrl();
|
|
52
|
+
/**
|
|
53
|
+
* Sets the branch for repository operations.
|
|
54
|
+
* If the repository is already initialized, it will checkout the new branch.
|
|
55
|
+
* @param branch - The branch name to use
|
|
56
|
+
*/
|
|
57
|
+
set branch(branch: string);
|
|
58
|
+
/**
|
|
59
|
+
* Creates a new ProjectTemplateRepository instance.
|
|
60
|
+
*
|
|
61
|
+
* @param repo - The repository name (e.g., 'equinor/fusion-app-template')
|
|
62
|
+
* @param options - Configuration options for the repository
|
|
63
|
+
* @param options.baseDir - Base directory for the repository (defaults to temp directory)
|
|
64
|
+
* @param options.log - Optional logger instance for output
|
|
65
|
+
* @param options.protocol - Git protocol to use (auto-detected if not specified)
|
|
66
|
+
* @param options.branch - Branch to checkout (defaults to 'main')
|
|
67
|
+
*/
|
|
68
|
+
constructor(repo: string, options: {
|
|
69
|
+
baseDir?: string;
|
|
70
|
+
log?: ConsoleLogger;
|
|
71
|
+
protocol?: GitClientProtocol;
|
|
72
|
+
branch?: string;
|
|
73
|
+
});
|
|
74
|
+
/**
|
|
75
|
+
* Initializes the repository by cloning or updating it.
|
|
76
|
+
*
|
|
77
|
+
* This method handles the complete repository setup process:
|
|
78
|
+
* - Creates the base directory if it doesn't exist
|
|
79
|
+
* - Clones the repository if it's not already initialized
|
|
80
|
+
* - Updates the repository if it already exists
|
|
81
|
+
* - Checks out the specified branch
|
|
82
|
+
*
|
|
83
|
+
* @throws {Error} If repository initialization fails
|
|
84
|
+
*/
|
|
85
|
+
initialize(): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Retrieves all available project templates from the repository.
|
|
88
|
+
*
|
|
89
|
+
* This method reads the templates.json manifest file from the repository
|
|
90
|
+
* and parses it to create ProjectTemplate instances. It combines global
|
|
91
|
+
* resources with template-specific resources to create complete templates.
|
|
92
|
+
*
|
|
93
|
+
* @returns Promise resolving to an array of available project templates
|
|
94
|
+
* @throws {Error} If templates.json cannot be read or parsed
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* const templates = await repo.getAvailableTemplates();
|
|
99
|
+
* console.log(`Found ${templates.length} templates`);
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
getAvailableTemplates(): Promise<ProjectTemplate[]>;
|
|
103
|
+
/**
|
|
104
|
+
* Clean up the repository directory by removing it from the filesystem.
|
|
105
|
+
* This is useful for cleaning up temporary template repositories.
|
|
106
|
+
*
|
|
107
|
+
* @returns Promise resolving to true if cleanup was performed, false if failed
|
|
108
|
+
*/
|
|
109
|
+
cleanup(): Promise<boolean>;
|
|
110
|
+
_cloneRepo(): Promise<void>;
|
|
111
|
+
_checkoutBranch(): Promise<void>;
|
|
112
|
+
_setupOutputHandler(): void;
|
|
113
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ConsoleLogger } from '@equinor/fusion-framework-cli/bin';
|
|
2
|
+
/**
|
|
3
|
+
* Install package dependencies using the specified package manager.
|
|
4
|
+
*
|
|
5
|
+
* @param targetDir - Directory to run package manager install in
|
|
6
|
+
* @param packageManager - Package manager to use ('npm' or 'pnpm')
|
|
7
|
+
* @param logger - Logger instance for output
|
|
8
|
+
* @returns Promise resolving to the package manager name used
|
|
9
|
+
*/
|
|
10
|
+
export declare function installPackageDependencies(targetDir: string, packageManager: string, logger?: ConsoleLogger): Promise<string>;
|
|
11
|
+
export default installPackageDependencies;
|