@scaleway/configuration-loader 2.2.0 → 2.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.
@@ -1,19 +1,23 @@
1
1
 
2
- > @scaleway/configuration-loader@2.1.0 build /home/runner/work/scaleway-sdk-js/scaleway-sdk-js/packages/configuration-loader
2
+ > @scaleway/configuration-loader@2.2.0 build /home/runner/work/scaleway-sdk-js/scaleway-sdk-js/packages/configuration-loader
3
3
  > vite build --config ../../vite.config.ts && pnpm run type:generate
4
4
 
5
- vite v8.0.0-beta.16 building ssr environment for production...
6
- 
5
+ vite v8.0.1 building ssr environment for production...
6
+ 
7
7
  rendering chunks...
8
8
  computing gzip size...
9
- dist/index.js 0.26 kB │ gzip: 0.12 kB
10
- dist/path-resolver.js 0.89 kB │ gzip: 0.38 kB
11
- dist/env.js 0.90 kB │ gzip: 0.38 kB
12
- dist/yml-loader.js 1.40 kB │ gzip: 0.64 kB
13
- dist/config-loader.js 2.56 kB │ gzip: 0.81 kB
9
+ dist/types.js 0.00 kB │ gzip: 0.02 kB
10
+ dist/index.js 0.51 kB │ gzip: 0.16 kB
11
+ dist/env.js 0.94 kB │ gzip: 0.41 kB
12
+ dist/path-resolver.js 0.96 kB │ gzip: 0.41 kB
13
+ dist/__tests__/path-resolver.test.js 1.50 kB │ gzip: 0.57 kB
14
+ dist/__tests__/yml-loader.test.js 2.63 kB │ gzip: 0.81 kB
15
+ dist/yml-loader.js 2.78 kB │ gzip: 1.09 kB
16
+ dist/__tests__/config-loader.test.js 3.08 kB │ gzip: 1.04 kB
17
+ dist/config-loader.js 4.34 kB │ gzip: 0.95 kB
14
18
 
15
- ✓ built in 28ms
19
+ ✓ built in 32ms
16
20
 
17
- > @scaleway/configuration-loader@2.1.0 type:generate /home/runner/work/scaleway-sdk-js/scaleway-sdk-js/packages/configuration-loader
21
+ > @scaleway/configuration-loader@2.2.0 type:generate /home/runner/work/scaleway-sdk-js/scaleway-sdk-js/packages/configuration-loader
18
22
  > tsc --declaration -p tsconfig.build.json
19
23
 
package/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 2.3.0 (2026-04-02)
7
+
8
+ ### Features
9
+
10
+ - **configuration-loader:** add async config loading and file permission check ([#2860](https://github.com/scaleway/scaleway-sdk-js/issues/2860)) ([1377d94](https://github.com/scaleway/scaleway-sdk-js/commit/1377d94a2a01c6a717781513affd634c7fc61b98))
11
+
6
12
  # 2.2.0 (2026-03-05)
7
13
 
8
14
  ### Features
@@ -32,3 +32,32 @@ export declare const loadAllProfilesFromConfigurationFile: (params?: Readonly<Al
32
32
  * @public
33
33
  */
34
34
  export declare const loadProfileFromConfigurationFile: (params?: Readonly<ProfileFromFileParams>) => Profile;
35
+ /**
36
+ * Loads all profiles from configuration file (asynchronous).
37
+ *
38
+ * Non-blocking alternative to {@link loadAllProfilesFromConfigurationFile}.
39
+ *
40
+ * @param params - The parameters to load the profile
41
+ * @returns The profiles filled with values found in the configuration profile
42
+ *
43
+ * @throws Error
44
+ * Thrown if the configuration file couldn't be found.
45
+ *
46
+ * @public
47
+ */
48
+ export declare const loadAllProfilesFromConfigurationFileAsync: (params?: Readonly<AllProfilesFromFileParams>) => Promise<Record<string, Profile>>;
49
+ /**
50
+ * Loads profile from configuration file (asynchronous).
51
+ *
52
+ * Non-blocking alternative to {@link loadProfileFromConfigurationFile}.
53
+ *
54
+ * @param params - The parameters to load the profile
55
+ * @returns The profile filled with values found in the configuration profile
56
+ *
57
+ * @throws Error
58
+ * Thrown if the configuration file couldn't be found,
59
+ * or if the specified profile can't be found.
60
+ *
61
+ * @public
62
+ */
63
+ export declare const loadProfileFromConfigurationFileAsync: (params?: Readonly<ProfileFromFileParams>) => Promise<Profile>;
@@ -1,7 +1,8 @@
1
1
  import { EnvironmentKey } from "./env.js";
2
2
  import { resolveConfigurationFilePath } from "./path-resolver.js";
3
- import { loadConfigurationFromFile } from "./yml-loader.js";
3
+ import { loadConfigurationFromFile, loadConfigurationFromFileAsync } from "./yml-loader.js";
4
4
  import { env } from "node:process";
5
+ //#region src/config-loader.ts
5
6
  var convertFileConfigToSDK = (obj) => ({
6
7
  accessKey: obj.access_key,
7
8
  apiURL: obj.api_url,
@@ -18,7 +19,7 @@ var convertFileConfigToSDK = (obj) => ({
18
19
  *
19
20
  * @public
20
21
  */
21
- const loadProfileFromEnvironmentValues = () => ({
22
+ var loadProfileFromEnvironmentValues = () => ({
22
23
  accessKey: env[EnvironmentKey.ScwAccessKey],
23
24
  apiURL: env[EnvironmentKey.ScwAPIURL],
24
25
  defaultOrganizationId: env[EnvironmentKey.ScwDefaultOrganizationId],
@@ -38,7 +39,7 @@ const loadProfileFromEnvironmentValues = () => ({
38
39
  *
39
40
  * @public
40
41
  */
41
- const loadAllProfilesFromConfigurationFile = (params) => {
42
+ var loadAllProfilesFromConfigurationFile = (params) => {
42
43
  const filePath = params?.filepath ?? resolveConfigurationFilePath();
43
44
  if (typeof filePath !== "string" || filePath.length === 0) throw new Error("Could not find the path to the configuration file.");
44
45
  const configs = loadConfigurationFromFile(filePath);
@@ -58,11 +59,54 @@ const loadAllProfilesFromConfigurationFile = (params) => {
58
59
  *
59
60
  * @public
60
61
  */
61
- const loadProfileFromConfigurationFile = (params) => {
62
+ var loadProfileFromConfigurationFile = (params) => {
62
63
  const configs = loadAllProfilesFromConfigurationFile(params);
63
64
  const profileName = params?.profileName ?? "default";
64
65
  const profileMap = configs[profileName];
65
66
  if (typeof profileMap !== "object") throw new Error(`Could not find the desired profile '${profileName}' in the configuration file.`);
66
67
  return profileMap;
67
68
  };
68
- export { loadAllProfilesFromConfigurationFile, loadProfileFromConfigurationFile, loadProfileFromEnvironmentValues };
69
+ /**
70
+ * Loads all profiles from configuration file (asynchronous).
71
+ *
72
+ * Non-blocking alternative to {@link loadAllProfilesFromConfigurationFile}.
73
+ *
74
+ * @param params - The parameters to load the profile
75
+ * @returns The profiles filled with values found in the configuration profile
76
+ *
77
+ * @throws Error
78
+ * Thrown if the configuration file couldn't be found.
79
+ *
80
+ * @public
81
+ */
82
+ var loadAllProfilesFromConfigurationFileAsync = async (params) => {
83
+ const filePath = params?.filepath ?? resolveConfigurationFilePath();
84
+ if (typeof filePath !== "string" || filePath.length === 0) throw new Error("Could not find the path to the configuration file.");
85
+ const configs = await loadConfigurationFromFileAsync(filePath);
86
+ const result = {};
87
+ for (const pKey of Object.keys(configs)) result[pKey] = convertFileConfigToSDK(configs[pKey]);
88
+ return result;
89
+ };
90
+ /**
91
+ * Loads profile from configuration file (asynchronous).
92
+ *
93
+ * Non-blocking alternative to {@link loadProfileFromConfigurationFile}.
94
+ *
95
+ * @param params - The parameters to load the profile
96
+ * @returns The profile filled with values found in the configuration profile
97
+ *
98
+ * @throws Error
99
+ * Thrown if the configuration file couldn't be found,
100
+ * or if the specified profile can't be found.
101
+ *
102
+ * @public
103
+ */
104
+ var loadProfileFromConfigurationFileAsync = async (params) => {
105
+ const configs = await loadAllProfilesFromConfigurationFileAsync(params);
106
+ const profileName = params?.profileName ?? "default";
107
+ const profileMap = configs[profileName];
108
+ if (typeof profileMap !== "object") throw new Error(`Could not find the desired profile '${profileName}' in the configuration file.`);
109
+ return profileMap;
110
+ };
111
+ //#endregion
112
+ export { loadAllProfilesFromConfigurationFile, loadAllProfilesFromConfigurationFileAsync, loadProfileFromConfigurationFile, loadProfileFromConfigurationFileAsync, loadProfileFromEnvironmentValues };
package/dist/env.js CHANGED
@@ -1,7 +1,8 @@
1
+ //#region src/env.ts
1
2
  /**
2
3
  * Environment Key.
3
4
  */
4
- let EnvironmentKey = /* @__PURE__ */ function(EnvironmentKey) {
5
+ var EnvironmentKey = /* @__PURE__ */ function(EnvironmentKey) {
5
6
  /** Path to the Scaleway configuration file */
6
7
  EnvironmentKey["ScwConfigPath"] = "SCW_CONFIG_PATH";
7
8
  /** Scaleway access key */
@@ -23,4 +24,5 @@ let EnvironmentKey = /* @__PURE__ */ function(EnvironmentKey) {
23
24
  EnvironmentKey["ScwDefaultZone"] = "SCW_DEFAULT_ZONE";
24
25
  return EnvironmentKey;
25
26
  }({});
27
+ //#endregion
26
28
  export { EnvironmentKey };
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
- export { loadAllProfilesFromConfigurationFile, loadProfileFromConfigurationFile, loadProfileFromEnvironmentValues, } from './config-loader.js';
1
+ export { loadAllProfilesFromConfigurationFile, loadAllProfilesFromConfigurationFileAsync, loadProfileFromConfigurationFile, loadProfileFromConfigurationFileAsync, loadProfileFromEnvironmentValues, } from './config-loader.js';
2
2
  export type { AllProfilesFromFileParams, Profile, ProfileFromFileParams, } from './types.js';
3
+ export { hasSecureFilePermissions } from './yml-loader.js';
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
- import { loadAllProfilesFromConfigurationFile, loadProfileFromConfigurationFile, loadProfileFromEnvironmentValues } from "./config-loader.js";
2
- export { loadAllProfilesFromConfigurationFile, loadProfileFromConfigurationFile, loadProfileFromEnvironmentValues };
1
+ import { hasSecureFilePermissions } from "./yml-loader.js";
2
+ import { loadAllProfilesFromConfigurationFile, loadAllProfilesFromConfigurationFileAsync, loadProfileFromConfigurationFile, loadProfileFromConfigurationFileAsync, loadProfileFromEnvironmentValues } from "./config-loader.js";
3
+ export { hasSecureFilePermissions, loadAllProfilesFromConfigurationFile, loadAllProfilesFromConfigurationFileAsync, loadProfileFromConfigurationFile, loadProfileFromConfigurationFileAsync, loadProfileFromEnvironmentValues };
@@ -1,7 +1,8 @@
1
1
  import { EnvironmentKey } from "./env.js";
2
- import { env } from "node:process";
3
2
  import { homedir } from "node:os";
4
3
  import * as path from "node:path";
4
+ import { env } from "node:process";
5
+ //#region src/path-resolver.ts
5
6
  /**
6
7
  * Gets the Scaleway directory.
7
8
  *
@@ -9,7 +10,7 @@ import * as path from "node:path";
9
10
  *
10
11
  * @internal
11
12
  */
12
- const getScwConfigurationDirectory = () => {
13
+ var getScwConfigurationDirectory = () => {
13
14
  const xdgConfigPath = env.XDG_CONFIG_HOME;
14
15
  if (typeof xdgConfigPath === "string" && xdgConfigPath.length > 0) return path.join(xdgConfigPath, "scw");
15
16
  return path.join(homedir(), ".config", "scw");
@@ -21,9 +22,10 @@ const getScwConfigurationDirectory = () => {
21
22
  *
22
23
  * @internal
23
24
  */
24
- const resolveConfigurationFilePath = () => {
25
+ var resolveConfigurationFilePath = () => {
25
26
  const envFilePath = env[EnvironmentKey.ScwConfigPath];
26
27
  if (typeof envFilePath === "string" && envFilePath.length > 0) return envFilePath;
27
28
  return path.join(getScwConfigurationDirectory(), "config.yaml");
28
29
  };
29
- export { resolveConfigurationFilePath };
30
+ //#endregion
31
+ export { getScwConfigurationDirectory, resolveConfigurationFilePath };
package/dist/types.js ADDED
File without changes
@@ -9,7 +9,7 @@ import type { ConfigurationType } from './types.js';
9
9
  */
10
10
  export declare const convertYamlToConfiguration: (input: string | null) => ConfigurationType;
11
11
  /**
12
- * Loads configuration from a file.
12
+ * Loads configuration from a file (synchronous).
13
13
  *
14
14
  * @param filePath - Path to the configuration file
15
15
  * @returns The configuration
@@ -20,3 +20,32 @@ export declare const convertYamlToConfiguration: (input: string | null) => Confi
20
20
  * @internal
21
21
  */
22
22
  export declare const loadConfigurationFromFile: (filePath: string) => ConfigurationType;
23
+ /**
24
+ * Loads configuration from a file (asynchronous).
25
+ *
26
+ * Non-blocking alternative to {@link loadConfigurationFromFile} that avoids
27
+ * stalling the event loop on slow filesystems or large config files.
28
+ *
29
+ * @param filePath - Path to the configuration file
30
+ * @returns The configuration
31
+ *
32
+ * @throws Error
33
+ * Thrown if the file doesn't exist.
34
+ *
35
+ * @public
36
+ */
37
+ export declare const loadConfigurationFromFileAsync: (filePath: string) => Promise<ConfigurationType>;
38
+ /**
39
+ * Checks whether a configuration file has secure permissions.
40
+ *
41
+ * The Scaleway config file contains secret keys that grant full API access.
42
+ * On POSIX systems this function verifies the file is not readable by group
43
+ * or others (mode 0o600 or stricter). Returns `true` on Windows where POSIX
44
+ * permission bits are not meaningful.
45
+ *
46
+ * @param filePath - Path to the configuration file
47
+ * @returns `true` if the file has secure permissions (or on Windows)
48
+ *
49
+ * @public
50
+ */
51
+ export declare const hasSecureFilePermissions: (filePath: string) => Promise<boolean>;
@@ -1,4 +1,8 @@
1
- import { readFileSync } from "node:fs";
1
+ import { readFile, readFileSync, stat } from "node:fs";
2
+ import { promisify } from "node:util";
3
+ //#region src/yml-loader.ts
4
+ var readFileAsync = promisify(readFile);
5
+ var statAsync = promisify(stat);
2
6
  var STRIP_COMMENT_REGEX = /(^|\s)[;#]/;
3
7
  var DETECT_SECTION_REGEX = /^\s*([\s\S]+?):\s*$/;
4
8
  var DETECT_ITEM_REGEX = /^\s*(.+?)\s*:\s*(.+?)\s*$/;
@@ -10,7 +14,7 @@ var DETECT_ITEM_REGEX = /^\s*(.+?)\s*:\s*(.+?)\s*$/;
10
14
  *
11
15
  * @internal
12
16
  */
13
- const convertYamlToConfiguration = (input) => {
17
+ var convertYamlToConfiguration = (input) => {
14
18
  let foundProfilesKey = false;
15
19
  let currentSection = "default";
16
20
  const map = {};
@@ -33,7 +37,7 @@ const convertYamlToConfiguration = (input) => {
33
37
  return map;
34
38
  };
35
39
  /**
36
- * Loads configuration from a file.
40
+ * Loads configuration from a file (synchronous).
37
41
  *
38
42
  * @param filePath - Path to the configuration file
39
43
  * @returns The configuration
@@ -43,7 +47,42 @@ const convertYamlToConfiguration = (input) => {
43
47
  *
44
48
  * @internal
45
49
  */
46
- const loadConfigurationFromFile = (filePath) => {
50
+ var loadConfigurationFromFile = (filePath) => {
47
51
  return convertYamlToConfiguration(readFileSync(filePath, "utf-8"));
48
52
  };
49
- export { loadConfigurationFromFile };
53
+ /**
54
+ * Loads configuration from a file (asynchronous).
55
+ *
56
+ * Non-blocking alternative to {@link loadConfigurationFromFile} that avoids
57
+ * stalling the event loop on slow filesystems or large config files.
58
+ *
59
+ * @param filePath - Path to the configuration file
60
+ * @returns The configuration
61
+ *
62
+ * @throws Error
63
+ * Thrown if the file doesn't exist.
64
+ *
65
+ * @public
66
+ */
67
+ var loadConfigurationFromFileAsync = async (filePath) => {
68
+ return convertYamlToConfiguration(await readFileAsync(filePath, "utf-8"));
69
+ };
70
+ /**
71
+ * Checks whether a configuration file has secure permissions.
72
+ *
73
+ * The Scaleway config file contains secret keys that grant full API access.
74
+ * On POSIX systems this function verifies the file is not readable by group
75
+ * or others (mode 0o600 or stricter). Returns `true` on Windows where POSIX
76
+ * permission bits are not meaningful.
77
+ *
78
+ * @param filePath - Path to the configuration file
79
+ * @returns `true` if the file has secure permissions (or on Windows)
80
+ *
81
+ * @public
82
+ */
83
+ var hasSecureFilePermissions = async (filePath) => {
84
+ if (process.platform === "win32") return true;
85
+ return ((await statAsync(filePath)).mode & 63) === 0;
86
+ };
87
+ //#endregion
88
+ export { convertYamlToConfiguration, hasSecureFilePermissions, loadConfigurationFromFile, loadConfigurationFromFileAsync };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scaleway/configuration-loader",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Load configuration via file or environment for NodeJS.",
6
6
  "publishConfig": {