@equinor/fusion-framework-cli 11.1.4 → 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.
Files changed (36) hide show
  1. package/CHANGELOG.md +149 -0
  2. package/README.md +18 -0
  3. package/bin/build/cli.mjs +3 -3
  4. package/dist/esm/lib/utils/assert.js +70 -15
  5. package/dist/esm/lib/utils/assert.js.map +1 -1
  6. package/dist/esm/lib/utils/is-git-dir.js +19 -0
  7. package/dist/esm/lib/utils/is-git-dir.js.map +1 -0
  8. package/dist/esm/lib/utils/package-info.js +135 -0
  9. package/dist/esm/lib/utils/package-info.js.map +1 -0
  10. package/dist/esm/lib/utils/path-security.js +56 -0
  11. package/dist/esm/lib/utils/path-security.js.map +1 -0
  12. package/dist/esm/version.js +1 -1
  13. package/dist/types/bin/helpers/ProjectTemplate.d.ts +61 -0
  14. package/dist/types/bin/helpers/ProjectTemplateRepository.d.ts +113 -0
  15. package/dist/types/bin/helpers/install-package-dependencies.d.ts +11 -0
  16. package/dist/types/bin/helpers/project-templates.schema.d.ts +301 -0
  17. package/dist/types/cli/commands/create/_helpers/check-target-directory.d.ts +12 -0
  18. package/dist/types/cli/commands/create/_helpers/cleanup-template-files.d.ts +15 -0
  19. package/dist/types/cli/commands/create/_helpers/install-dependencies.d.ts +14 -0
  20. package/dist/types/cli/commands/create/_helpers/open-in-ide.d.ts +15 -0
  21. package/dist/types/cli/commands/create/_helpers/resolve-workspace-dependencies.d.ts +27 -0
  22. package/dist/types/cli/commands/create/_helpers/select-template.d.ts +24 -0
  23. package/dist/types/cli/commands/create/_helpers/setup-repository.d.ts +23 -0
  24. package/dist/types/cli/commands/create/_helpers/start-dev-server.d.ts +17 -0
  25. package/dist/types/cli/commands/create/_helpers/update-package-json.d.ts +41 -0
  26. package/dist/types/cli/commands/create/app.d.ts +28 -0
  27. package/dist/types/cli/commands/create/index.d.ts +2 -0
  28. package/dist/types/lib/utils/assert.d.ts +61 -13
  29. package/dist/types/lib/utils/is-git-dir.d.ts +9 -0
  30. package/dist/types/lib/utils/package-info.d.ts +106 -0
  31. package/dist/types/lib/utils/path-security.d.ts +36 -0
  32. package/dist/types/version.d.ts +1 -1
  33. package/docs/creating-apps.md +275 -0
  34. package/docs/dev-server.md +367 -0
  35. package/docs/migration-v10-to-v11.md +13 -0
  36. 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
- * Useful for consistent assertion handling throughout the codebase.
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
- * @param value - The value to check for being a number.
13
- * @param message - Optional custom error message for assertion failure.
14
- * @throws {AssertionError} If value is NaN.
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
- * @param value - The file path to check.
29
- * @param message - Optional custom error message for assertion failure.
30
- * @throws {AssertionError} If the file does not exist.
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
- * @param value - The value to check for being an object.
41
- * @param message - Optional custom error message or Error instance.
42
- * @throws {AssertionError} If value is not an object.
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;AAE9C;;;GAGG;AACH,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;AAElC;;;;;;;GAOG;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;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAc,EAAE,OAAgB,EAAiB,EAAE;IAClF,qDAAqD;IACrD,MAAM,CAAC,UAAU,CAAC,KAAe,CAAC,EAAE,OAAO,IAAI,QAAQ,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;AACzF,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,OAAwB;IAClE,iEAAiE;IACjE,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"}
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"}
@@ -1,3 +1,3 @@
1
1
  // Generated by genversion.
2
- export const version = '11.1.4';
2
+ export const version = '11.3.0';
3
3
  //# sourceMappingURL=version.js.map
@@ -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;