@deployport/api-services-corelib 0.2.0 → 0.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/configurator/config.d.ts +54 -0
- package/configurator/config.js +16 -0
- package/configurator/config.ts +69 -0
- package/configurator/index.d.ts +11 -1
- package/configurator/index.js +26 -7
- package/configurator/index.ts +29 -7
- package/configurator/loader.browser.d.ts +14 -0
- package/configurator/loader.browser.js +28 -0
- package/configurator/loader.browser.ts +36 -0
- package/configurator/loader.node.d.ts +18 -0
- package/configurator/loader.node.js +166 -0
- package/configurator/loader.node.ts +240 -0
- package/configurator/loader.test.ts +243 -0
- package/configurator/signer.d.ts +7 -7
- package/configurator/signer.js +21 -21
- package/configurator/signer.test.ts +30 -0
- package/configurator/signer.ts +22 -29
- package/package.json +18 -4
- package/tsconfig.json +5 -1
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AccountScope selects which account slot of a profile's credentials to use.
|
|
3
|
+
* - `"auto"` uses the current account if present, otherwise the global account.
|
|
4
|
+
* - `"global"` uses the profile's global account.
|
|
5
|
+
* - `"current"` uses the profile's current (switched) account.
|
|
6
|
+
*/
|
|
7
|
+
export type AccountScope = "auto" | "global" | "current";
|
|
8
|
+
/**
|
|
9
|
+
* DeployportConfig holds the DeployPort signing configuration. On Node, unset
|
|
10
|
+
* fields are resolved from the environment and `~/.deployport` files by
|
|
11
|
+
* {@link loadDefaultConfig}; in the browser they must be provided explicitly.
|
|
12
|
+
*/
|
|
13
|
+
export type DeployportConfig = {
|
|
14
|
+
region?: string;
|
|
15
|
+
accessKeyID?: string;
|
|
16
|
+
secretAccessKey?: string;
|
|
17
|
+
/** Profile name; else `DEPLOYPORT_PROFILE`; else `"default"`. */
|
|
18
|
+
profile?: string;
|
|
19
|
+
/** Which account slot to read from the profile's credentials. Defaults to `"auto"`. */
|
|
20
|
+
accountScope?: AccountScope;
|
|
21
|
+
/** Disables credential auto-loading from env and files. Explicit credentials are still honored. */
|
|
22
|
+
anonymous?: boolean;
|
|
23
|
+
/** Overrides the credentials file path. Empty uses {@link DEFAULT_CREDENTIALS_FILE_PATH}. */
|
|
24
|
+
credentialsFilePath?: string;
|
|
25
|
+
/** Overrides the settings file path. Empty uses {@link DEFAULT_SETTINGS_FILE_PATH}. */
|
|
26
|
+
settingsFilePath?: string;
|
|
27
|
+
};
|
|
28
|
+
/** Config is the DeployPort-specific configuration attached to a client config. */
|
|
29
|
+
export type Config = {
|
|
30
|
+
deployport?: DeployportConfig;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* LoadDefaultConfig resolves a Config using the full default chain. The Node
|
|
34
|
+
* implementation ({@link loader.node.ts}) fills unset values from the
|
|
35
|
+
* environment then the default config files; the browser implementation
|
|
36
|
+
* ({@link loader.browser.ts}) returns the config unchanged (explicit only).
|
|
37
|
+
*/
|
|
38
|
+
export type LoadDefaultConfig = (config?: Config) => Promise<Config>;
|
|
39
|
+
/**
|
|
40
|
+
* GetEndpointOverride reads a per-service endpoint URL override for a profile.
|
|
41
|
+
* On Node it reads the settings file; in the browser it always returns "".
|
|
42
|
+
*/
|
|
43
|
+
export type GetEndpointOverride = (service: string, options?: {
|
|
44
|
+
profile?: string;
|
|
45
|
+
settingsFilePath?: string;
|
|
46
|
+
}) => Promise<string>;
|
|
47
|
+
export declare const ENV_ACCESS_KEY_ID = "DEPLOYPORT_ACCESS_KEY_ID";
|
|
48
|
+
export declare const ENV_SECRET_ACCESS_KEY = "DEPLOYPORT_SECRET_ACCESS_KEY";
|
|
49
|
+
export declare const ENV_REGION = "DEPLOYPORT_REGION";
|
|
50
|
+
export declare const ENV_PROFILE = "DEPLOYPORT_PROFILE";
|
|
51
|
+
export declare const DEFAULT_PROFILE = "default";
|
|
52
|
+
export declare const DEFAULT_REGION = "us-east-2";
|
|
53
|
+
export declare const DEFAULT_CREDENTIALS_FILE_PATH = "$HOME/.deployport/credentials.yml";
|
|
54
|
+
export declare const DEFAULT_SETTINGS_FILE_PATH = "$HOME/.deployport/settings.yml";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Universal (browser + Node) configuration types and constants for the
|
|
2
|
+
// DeployPort configurator. This module MUST NOT import any Node builtins
|
|
3
|
+
// (`node:fs`, `node:os`, ...) or Node-only packages so it stays safe to bundle
|
|
4
|
+
// for the browser. The Node-only resolution logic lives in `loader.node.ts`,
|
|
5
|
+
// which is swapped in for `loader.browser.ts` via the package `#config-loader`
|
|
6
|
+
// import condition.
|
|
7
|
+
// Environment variable names used for default configuration loading (Node only).
|
|
8
|
+
export const ENV_ACCESS_KEY_ID = "DEPLOYPORT_ACCESS_KEY_ID";
|
|
9
|
+
export const ENV_SECRET_ACCESS_KEY = "DEPLOYPORT_SECRET_ACCESS_KEY";
|
|
10
|
+
export const ENV_REGION = "DEPLOYPORT_REGION";
|
|
11
|
+
export const ENV_PROFILE = "DEPLOYPORT_PROFILE";
|
|
12
|
+
// Defaults used when resolving configuration from the environment and files.
|
|
13
|
+
export const DEFAULT_PROFILE = "default";
|
|
14
|
+
export const DEFAULT_REGION = "us-east-2";
|
|
15
|
+
export const DEFAULT_CREDENTIALS_FILE_PATH = "$HOME/.deployport/credentials.yml";
|
|
16
|
+
export const DEFAULT_SETTINGS_FILE_PATH = "$HOME/.deployport/settings.yml";
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Universal (browser + Node) configuration types and constants for the
|
|
2
|
+
// DeployPort configurator. This module MUST NOT import any Node builtins
|
|
3
|
+
// (`node:fs`, `node:os`, ...) or Node-only packages so it stays safe to bundle
|
|
4
|
+
// for the browser. The Node-only resolution logic lives in `loader.node.ts`,
|
|
5
|
+
// which is swapped in for `loader.browser.ts` via the package `#config-loader`
|
|
6
|
+
// import condition.
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* AccountScope selects which account slot of a profile's credentials to use.
|
|
10
|
+
* - `"auto"` uses the current account if present, otherwise the global account.
|
|
11
|
+
* - `"global"` uses the profile's global account.
|
|
12
|
+
* - `"current"` uses the profile's current (switched) account.
|
|
13
|
+
*/
|
|
14
|
+
export type AccountScope = "auto" | "global" | "current";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* DeployportConfig holds the DeployPort signing configuration. On Node, unset
|
|
18
|
+
* fields are resolved from the environment and `~/.deployport` files by
|
|
19
|
+
* {@link loadDefaultConfig}; in the browser they must be provided explicitly.
|
|
20
|
+
*/
|
|
21
|
+
export type DeployportConfig = {
|
|
22
|
+
region?: string;
|
|
23
|
+
accessKeyID?: string;
|
|
24
|
+
secretAccessKey?: string;
|
|
25
|
+
/** Profile name; else `DEPLOYPORT_PROFILE`; else `"default"`. */
|
|
26
|
+
profile?: string;
|
|
27
|
+
/** Which account slot to read from the profile's credentials. Defaults to `"auto"`. */
|
|
28
|
+
accountScope?: AccountScope;
|
|
29
|
+
/** Disables credential auto-loading from env and files. Explicit credentials are still honored. */
|
|
30
|
+
anonymous?: boolean;
|
|
31
|
+
/** Overrides the credentials file path. Empty uses {@link DEFAULT_CREDENTIALS_FILE_PATH}. */
|
|
32
|
+
credentialsFilePath?: string;
|
|
33
|
+
/** Overrides the settings file path. Empty uses {@link DEFAULT_SETTINGS_FILE_PATH}. */
|
|
34
|
+
settingsFilePath?: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** Config is the DeployPort-specific configuration attached to a client config. */
|
|
38
|
+
export type Config = {
|
|
39
|
+
deployport?: DeployportConfig;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* LoadDefaultConfig resolves a Config using the full default chain. The Node
|
|
44
|
+
* implementation ({@link loader.node.ts}) fills unset values from the
|
|
45
|
+
* environment then the default config files; the browser implementation
|
|
46
|
+
* ({@link loader.browser.ts}) returns the config unchanged (explicit only).
|
|
47
|
+
*/
|
|
48
|
+
export type LoadDefaultConfig = (config?: Config) => Promise<Config>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* GetEndpointOverride reads a per-service endpoint URL override for a profile.
|
|
52
|
+
* On Node it reads the settings file; in the browser it always returns "".
|
|
53
|
+
*/
|
|
54
|
+
export type GetEndpointOverride = (
|
|
55
|
+
service: string,
|
|
56
|
+
options?: { profile?: string; settingsFilePath?: string },
|
|
57
|
+
) => Promise<string>;
|
|
58
|
+
|
|
59
|
+
// Environment variable names used for default configuration loading (Node only).
|
|
60
|
+
export const ENV_ACCESS_KEY_ID = "DEPLOYPORT_ACCESS_KEY_ID";
|
|
61
|
+
export const ENV_SECRET_ACCESS_KEY = "DEPLOYPORT_SECRET_ACCESS_KEY";
|
|
62
|
+
export const ENV_REGION = "DEPLOYPORT_REGION";
|
|
63
|
+
export const ENV_PROFILE = "DEPLOYPORT_PROFILE";
|
|
64
|
+
|
|
65
|
+
// Defaults used when resolving configuration from the environment and files.
|
|
66
|
+
export const DEFAULT_PROFILE = "default";
|
|
67
|
+
export const DEFAULT_REGION = "us-east-2";
|
|
68
|
+
export const DEFAULT_CREDENTIALS_FILE_PATH = "$HOME/.deployport/credentials.yml";
|
|
69
|
+
export const DEFAULT_SETTINGS_FILE_PATH = "$HOME/.deployport/settings.yml";
|
package/configurator/index.d.ts
CHANGED
|
@@ -1,2 +1,12 @@
|
|
|
1
|
+
import type { Config } from "./config.js";
|
|
1
2
|
import { Runtime } from "@deployport/specular-runtime";
|
|
2
|
-
export
|
|
3
|
+
export type { Config, DeployportConfig, AccountScope } from "./config.js";
|
|
4
|
+
/**
|
|
5
|
+
* Configure installs the DeployPort request signer on a client config.
|
|
6
|
+
*
|
|
7
|
+
* On Node, config left unset is resolved from the DEPLOYPORT_* environment
|
|
8
|
+
* variables and the ~/.deployport files on the first request (the resolution is
|
|
9
|
+
* async and memoized). In the browser, `loadDefaultConfig` is a no-op, so the
|
|
10
|
+
* `deployport` config must be provided explicitly.
|
|
11
|
+
*/
|
|
12
|
+
export declare function Configure(config?: Runtime.ClientConfig & Config): Runtime.ClientConfig;
|
package/configurator/index.js
CHANGED
|
@@ -1,12 +1,31 @@
|
|
|
1
|
-
import { ConfigureSignatureV1 } from "./signer.js";
|
|
1
|
+
import { ConfigureSignatureV1, replaceRegionInRequest } from "./signer.js";
|
|
2
|
+
import { loadDefaultConfig } from "#config-loader";
|
|
2
3
|
import { Runtime } from "@deployport/specular-runtime";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Configure installs the DeployPort request signer on a client config.
|
|
6
|
+
*
|
|
7
|
+
* On Node, config left unset is resolved from the DEPLOYPORT_* environment
|
|
8
|
+
* variables and the ~/.deployport files on the first request (the resolution is
|
|
9
|
+
* async and memoized). In the browser, `loadDefaultConfig` is a no-op, so the
|
|
10
|
+
* `deployport` config must be provided explicitly.
|
|
11
|
+
*/
|
|
12
|
+
export function Configure(config) {
|
|
13
|
+
const requestConfigurators = config?.requestConfigurators ?? [];
|
|
14
|
+
// Resolve once, lazily, on the first request. Keeps Configure synchronous
|
|
15
|
+
// and browser-safe while deferring any Node I/O to request time.
|
|
16
|
+
let resolved;
|
|
17
|
+
requestConfigurators.push(async (submission) => {
|
|
18
|
+
resolved ??= loadDefaultConfig(config);
|
|
19
|
+
const cfg = await resolved;
|
|
20
|
+
// Substitute the <region> endpoint placeholder for every request that
|
|
21
|
+
// has a resolved region, whether or not it is signed (mirrors Go).
|
|
22
|
+
const region = cfg.deployport?.region;
|
|
23
|
+
if (region) {
|
|
24
|
+
replaceRegionInRequest(submission.request, region);
|
|
25
|
+
}
|
|
26
|
+
await ConfigureSignatureV1(submission, cfg);
|
|
8
27
|
});
|
|
9
|
-
return Runtime.MergeClientConfig(
|
|
28
|
+
return Runtime.MergeClientConfig(config, {
|
|
10
29
|
requestConfigurators,
|
|
11
30
|
});
|
|
12
31
|
}
|
package/configurator/index.ts
CHANGED
|
@@ -1,13 +1,35 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ConfigureSignatureV1, replaceRegionInRequest } from "./signer.js";
|
|
2
|
+
import type { Config } from "./config.js";
|
|
3
|
+
import { loadDefaultConfig } from "#config-loader";
|
|
2
4
|
import { Runtime } from "@deployport/specular-runtime";
|
|
3
5
|
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
export type { Config, DeployportConfig, AccountScope } from "./config.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Configure installs the DeployPort request signer on a client config.
|
|
10
|
+
*
|
|
11
|
+
* On Node, config left unset is resolved from the DEPLOYPORT_* environment
|
|
12
|
+
* variables and the ~/.deployport files on the first request (the resolution is
|
|
13
|
+
* async and memoized). In the browser, `loadDefaultConfig` is a no-op, so the
|
|
14
|
+
* `deployport` config must be provided explicitly.
|
|
15
|
+
*/
|
|
16
|
+
export function Configure(config?: Runtime.ClientConfig & Config): Runtime.ClientConfig {
|
|
17
|
+
const requestConfigurators = config?.requestConfigurators ?? [];
|
|
18
|
+
// Resolve once, lazily, on the first request. Keeps Configure synchronous
|
|
19
|
+
// and browser-safe while deferring any Node I/O to request time.
|
|
20
|
+
let resolved: Promise<Config> | undefined;
|
|
21
|
+
requestConfigurators.push(async (submission) => {
|
|
22
|
+
resolved ??= loadDefaultConfig(config);
|
|
23
|
+
const cfg = await resolved;
|
|
24
|
+
// Substitute the <region> endpoint placeholder for every request that
|
|
25
|
+
// has a resolved region, whether or not it is signed (mirrors Go).
|
|
26
|
+
const region = cfg.deployport?.region;
|
|
27
|
+
if (region) {
|
|
28
|
+
replaceRegionInRequest(submission.request, region);
|
|
29
|
+
}
|
|
30
|
+
await ConfigureSignatureV1(submission, cfg);
|
|
9
31
|
});
|
|
10
|
-
return Runtime.MergeClientConfig(
|
|
32
|
+
return Runtime.MergeClientConfig(config, {
|
|
11
33
|
requestConfigurators,
|
|
12
34
|
});
|
|
13
35
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Config } from "./config.js";
|
|
2
|
+
/**
|
|
3
|
+
* loadDefaultConfig is a no-op passthrough in the browser: whatever the
|
|
4
|
+
* consumer passed is what gets used. There is no env/file resolution.
|
|
5
|
+
*/
|
|
6
|
+
export declare function loadDefaultConfig(config?: Config): Promise<Config>;
|
|
7
|
+
/**
|
|
8
|
+
* getEndpointOverride always returns "" in the browser: there is no settings
|
|
9
|
+
* file to read. Consumers set endpoints explicitly on the client config.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getEndpointOverride(_service: string, _options?: {
|
|
12
|
+
profile?: string;
|
|
13
|
+
settingsFilePath?: string;
|
|
14
|
+
}): Promise<string>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Browser configuration loader. This module is selected via the
|
|
2
|
+
// `#config-loader` package import for non-Node targets (see package.json
|
|
3
|
+
// `imports`). It contains NO Node builtins, so bundlers never pull `fs`/`os`
|
|
4
|
+
// into a browser build. In the browser there is no environment or filesystem
|
|
5
|
+
// to read, so config must be provided explicitly by the consumer.
|
|
6
|
+
//
|
|
7
|
+
// It intentionally mirrors the public surface of `loader.node.ts` so that
|
|
8
|
+
// `#config-loader` resolves to the same shape in both environments.
|
|
9
|
+
/**
|
|
10
|
+
* loadDefaultConfig is a no-op passthrough in the browser: whatever the
|
|
11
|
+
* consumer passed is what gets used. There is no env/file resolution.
|
|
12
|
+
*/
|
|
13
|
+
export async function loadDefaultConfig(config) {
|
|
14
|
+
return config ?? {};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* getEndpointOverride always returns "" in the browser: there is no settings
|
|
18
|
+
* file to read. Consumers set endpoints explicitly on the client config.
|
|
19
|
+
*/
|
|
20
|
+
export async function getEndpointOverride(_service, _options) {
|
|
21
|
+
return "";
|
|
22
|
+
}
|
|
23
|
+
// Compile-time guard: this variant must match the shared loader signatures so
|
|
24
|
+
// `#config-loader` resolves identically in Node and the browser.
|
|
25
|
+
const _loadDefaultConfig = loadDefaultConfig;
|
|
26
|
+
const _getEndpointOverride = getEndpointOverride;
|
|
27
|
+
void _loadDefaultConfig;
|
|
28
|
+
void _getEndpointOverride;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Browser configuration loader. This module is selected via the
|
|
2
|
+
// `#config-loader` package import for non-Node targets (see package.json
|
|
3
|
+
// `imports`). It contains NO Node builtins, so bundlers never pull `fs`/`os`
|
|
4
|
+
// into a browser build. In the browser there is no environment or filesystem
|
|
5
|
+
// to read, so config must be provided explicitly by the consumer.
|
|
6
|
+
//
|
|
7
|
+
// It intentionally mirrors the public surface of `loader.node.ts` so that
|
|
8
|
+
// `#config-loader` resolves to the same shape in both environments.
|
|
9
|
+
|
|
10
|
+
import type { Config, GetEndpointOverride, LoadDefaultConfig } from "./config.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* loadDefaultConfig is a no-op passthrough in the browser: whatever the
|
|
14
|
+
* consumer passed is what gets used. There is no env/file resolution.
|
|
15
|
+
*/
|
|
16
|
+
export async function loadDefaultConfig(config?: Config): Promise<Config> {
|
|
17
|
+
return config ?? {};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* getEndpointOverride always returns "" in the browser: there is no settings
|
|
22
|
+
* file to read. Consumers set endpoints explicitly on the client config.
|
|
23
|
+
*/
|
|
24
|
+
export async function getEndpointOverride(
|
|
25
|
+
_service: string,
|
|
26
|
+
_options?: { profile?: string; settingsFilePath?: string },
|
|
27
|
+
): Promise<string> {
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Compile-time guard: this variant must match the shared loader signatures so
|
|
32
|
+
// `#config-loader` resolves identically in Node and the browser.
|
|
33
|
+
const _loadDefaultConfig: LoadDefaultConfig = loadDefaultConfig;
|
|
34
|
+
const _getEndpointOverride: GetEndpointOverride = getEndpointOverride;
|
|
35
|
+
void _loadDefaultConfig;
|
|
36
|
+
void _getEndpointOverride;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type Config } from "./config.js";
|
|
2
|
+
/**
|
|
3
|
+
* loadDefaultConfig resolves the DeployPort config using the full default
|
|
4
|
+
* chain, filling only values not already set explicitly: environment variables,
|
|
5
|
+
* then the default config files for the resolved profile, then DEFAULT_REGION
|
|
6
|
+
* as a last resort. Explicit values always win. Missing files are ignored;
|
|
7
|
+
* malformed files throw.
|
|
8
|
+
*/
|
|
9
|
+
export declare function loadDefaultConfig(config?: Config): Promise<Config>;
|
|
10
|
+
/**
|
|
11
|
+
* getEndpointOverride reads `profiles.<profile>.services.<service>.endpoint.url`
|
|
12
|
+
* from the settings file, or "" if none is set. Lets a Node consumer (e.g. a
|
|
13
|
+
* CLI) pick a per-service endpoint while corelib stays service-agnostic.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getEndpointOverride(service: string, options?: {
|
|
16
|
+
profile?: string;
|
|
17
|
+
settingsFilePath?: string;
|
|
18
|
+
}): Promise<string>;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// Node-only configuration loader. This module is selected via the
|
|
2
|
+
// `#config-loader` package import when running under Node (see package.json
|
|
3
|
+
// `imports`). It is NEVER bundled for the browser, so it may freely import
|
|
4
|
+
// Node builtins. The browser gets `loader.browser.ts` instead.
|
|
5
|
+
//
|
|
6
|
+
// The resolution chain mirrors the Go corelib (configurator/config_files.go,
|
|
7
|
+
// configurator/configurator.go): explicit config -> DEPLOYPORT_* env ->
|
|
8
|
+
// ~/.deployport files (for the resolved profile) -> DefaultRegion.
|
|
9
|
+
import { readFile } from "node:fs/promises";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
import { parse as parseYAML } from "yaml";
|
|
12
|
+
import { DEFAULT_CREDENTIALS_FILE_PATH, DEFAULT_PROFILE, DEFAULT_REGION, DEFAULT_SETTINGS_FILE_PATH, ENV_ACCESS_KEY_ID, ENV_PROFILE, ENV_REGION, ENV_SECRET_ACCESS_KEY, } from "./config.js";
|
|
13
|
+
// --- Public API -------------------------------------------------------------
|
|
14
|
+
/**
|
|
15
|
+
* loadDefaultConfig resolves the DeployPort config using the full default
|
|
16
|
+
* chain, filling only values not already set explicitly: environment variables,
|
|
17
|
+
* then the default config files for the resolved profile, then DEFAULT_REGION
|
|
18
|
+
* as a last resort. Explicit values always win. Missing files are ignored;
|
|
19
|
+
* malformed files throw.
|
|
20
|
+
*/
|
|
21
|
+
export async function loadDefaultConfig(config) {
|
|
22
|
+
const dp = { ...(config?.deployport ?? {}) };
|
|
23
|
+
const anonymous = dp.anonymous ?? false;
|
|
24
|
+
const scope = dp.accountScope ?? "auto";
|
|
25
|
+
const profile = resolveProfile(dp.profile);
|
|
26
|
+
// Credentials are treated as a unit: if the caller set either field
|
|
27
|
+
// explicitly, we do not auto-load them from env or files (mirrors Go's
|
|
28
|
+
// `Credentials == nil` gate).
|
|
29
|
+
const credsExplicit = dp.accessKeyID != null || dp.secretAccessKey != null;
|
|
30
|
+
if (!anonymous && !credsExplicit) {
|
|
31
|
+
const envKeyID = process.env[ENV_ACCESS_KEY_ID];
|
|
32
|
+
const envSecret = process.env[ENV_SECRET_ACCESS_KEY];
|
|
33
|
+
if (envKeyID || envSecret) {
|
|
34
|
+
// Env provides the credentials as a unit; files are skipped.
|
|
35
|
+
dp.accessKeyID = envKeyID ?? "";
|
|
36
|
+
dp.secretAccessKey = envSecret ?? "";
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
const credsFile = await loadCredentialsFile(credentialsPath(dp));
|
|
40
|
+
const acc = accountFor(credsFile, profile, scope);
|
|
41
|
+
if (acc && acc.accessKeyID) {
|
|
42
|
+
dp.accessKeyID = acc.accessKeyID;
|
|
43
|
+
dp.secretAccessKey = acc.secretAccessKey ?? "";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Region: explicit -> env -> settings file -> default.
|
|
48
|
+
if (!dp.region) {
|
|
49
|
+
const envRegion = process.env[ENV_REGION];
|
|
50
|
+
if (envRegion) {
|
|
51
|
+
dp.region = envRegion;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (!dp.region) {
|
|
55
|
+
const settingsFile = await loadSettingsFile(settingsPath(dp));
|
|
56
|
+
const region = regionFor(settingsFile, profile);
|
|
57
|
+
if (region) {
|
|
58
|
+
dp.region = region;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!dp.region) {
|
|
62
|
+
dp.region = DEFAULT_REGION;
|
|
63
|
+
}
|
|
64
|
+
return { ...config, deployport: dp };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* getEndpointOverride reads `profiles.<profile>.services.<service>.endpoint.url`
|
|
68
|
+
* from the settings file, or "" if none is set. Lets a Node consumer (e.g. a
|
|
69
|
+
* CLI) pick a per-service endpoint while corelib stays service-agnostic.
|
|
70
|
+
*/
|
|
71
|
+
export async function getEndpointOverride(service, options) {
|
|
72
|
+
const profile = resolveProfile(options?.profile);
|
|
73
|
+
const settingsFile = await loadSettingsFile(options?.settingsFilePath ? expandPath(options.settingsFilePath) : defaultSettingsPath());
|
|
74
|
+
return endpointFor(settingsFile, profile, service);
|
|
75
|
+
}
|
|
76
|
+
// --- Resolution helpers (mirror Go) -----------------------------------------
|
|
77
|
+
function resolveProfile(explicit) {
|
|
78
|
+
if (explicit) {
|
|
79
|
+
return explicit;
|
|
80
|
+
}
|
|
81
|
+
const fromEnv = process.env[ENV_PROFILE];
|
|
82
|
+
if (fromEnv) {
|
|
83
|
+
return fromEnv;
|
|
84
|
+
}
|
|
85
|
+
return DEFAULT_PROFILE;
|
|
86
|
+
}
|
|
87
|
+
function accountFor(file, profile, scope) {
|
|
88
|
+
const p = file.profiles?.[profile];
|
|
89
|
+
if (!p) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
switch (scope) {
|
|
93
|
+
case "global":
|
|
94
|
+
return p.global;
|
|
95
|
+
case "current":
|
|
96
|
+
return p.current;
|
|
97
|
+
default: // "auto"
|
|
98
|
+
return p.current ?? p.global;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function regionFor(file, profile) {
|
|
102
|
+
return file.profiles?.[profile]?.region ?? "";
|
|
103
|
+
}
|
|
104
|
+
function endpointFor(file, profile, service) {
|
|
105
|
+
return file.profiles?.[profile]?.services?.[service]?.endpoint?.url ?? "";
|
|
106
|
+
}
|
|
107
|
+
// --- File loading ------------------------------------------------------------
|
|
108
|
+
async function loadCredentialsFile(path) {
|
|
109
|
+
return loadYamlFile(path, "credentials");
|
|
110
|
+
}
|
|
111
|
+
async function loadSettingsFile(path) {
|
|
112
|
+
return loadYamlFile(path, "settings");
|
|
113
|
+
}
|
|
114
|
+
async function loadYamlFile(path, kind) {
|
|
115
|
+
let data;
|
|
116
|
+
try {
|
|
117
|
+
data = await readFile(path, "utf8");
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
if (isNotFound(err)) {
|
|
121
|
+
return { profiles: {} };
|
|
122
|
+
}
|
|
123
|
+
throw new Error(`failed to read ${kind} file: ${errMessage(err)}`);
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
return parseYAML(data) ?? { profiles: {} };
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
throw new Error(`failed to parse ${kind} file: ${errMessage(err)}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function isNotFound(err) {
|
|
133
|
+
return err?.code === "ENOENT";
|
|
134
|
+
}
|
|
135
|
+
function errMessage(err) {
|
|
136
|
+
return err instanceof Error ? err.message : String(err);
|
|
137
|
+
}
|
|
138
|
+
// --- Path resolution ---------------------------------------------------------
|
|
139
|
+
function credentialsPath(dp) {
|
|
140
|
+
return dp.credentialsFilePath
|
|
141
|
+
? expandPath(dp.credentialsFilePath)
|
|
142
|
+
: defaultCredentialsPath();
|
|
143
|
+
}
|
|
144
|
+
function settingsPath(dp) {
|
|
145
|
+
return dp.settingsFilePath ? expandPath(dp.settingsFilePath) : defaultSettingsPath();
|
|
146
|
+
}
|
|
147
|
+
function defaultCredentialsPath() {
|
|
148
|
+
return expandPath(DEFAULT_CREDENTIALS_FILE_PATH);
|
|
149
|
+
}
|
|
150
|
+
function defaultSettingsPath() {
|
|
151
|
+
return expandPath(DEFAULT_SETTINGS_FILE_PATH);
|
|
152
|
+
}
|
|
153
|
+
/** Expands a leading `~` and `$HOME`/`${HOME}` to the user's home directory. */
|
|
154
|
+
function expandPath(path) {
|
|
155
|
+
const home = homedir();
|
|
156
|
+
return path
|
|
157
|
+
.replace(/^~(?=\/|$)/, home)
|
|
158
|
+
.replace(/\$\{HOME\}/g, home)
|
|
159
|
+
.replace(/\$HOME/g, home);
|
|
160
|
+
}
|
|
161
|
+
// Compile-time guard: this variant must match the shared loader signatures so
|
|
162
|
+
// `#config-loader` resolves identically in Node and the browser.
|
|
163
|
+
const _loadDefaultConfig = loadDefaultConfig;
|
|
164
|
+
const _getEndpointOverride = getEndpointOverride;
|
|
165
|
+
void _loadDefaultConfig;
|
|
166
|
+
void _getEndpointOverride;
|