@achs/env 3.1.0 → 3.1.1

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 (138) hide show
  1. package/arguments.d.ts +25 -0
  2. package/arguments.d.ts.map +1 -0
  3. package/{src/arguments.ts → arguments.js} +8 -32
  4. package/arguments.js.map +1 -0
  5. package/commands/env.command.d.ts +8 -0
  6. package/commands/env.command.d.ts.map +1 -0
  7. package/commands/env.command.js +85 -0
  8. package/commands/env.command.js.map +1 -0
  9. package/commands/export.command.d.ts +8 -0
  10. package/commands/export.command.d.ts.map +1 -0
  11. package/commands/export.command.js +54 -0
  12. package/commands/export.command.js.map +1 -0
  13. package/{src/commands/index.ts → commands/index.d.ts} +1 -0
  14. package/commands/index.d.ts.map +1 -0
  15. package/commands/index.js +14 -0
  16. package/commands/index.js.map +1 -0
  17. package/commands/pull.command.d.ts +7 -0
  18. package/commands/pull.command.d.ts.map +1 -0
  19. package/commands/pull.command.js +39 -0
  20. package/commands/pull.command.js.map +1 -0
  21. package/commands/push.command.d.ts +7 -0
  22. package/commands/push.command.d.ts.map +1 -0
  23. package/commands/push.command.js +38 -0
  24. package/commands/push.command.js.map +1 -0
  25. package/commands/schema.command.d.ts +4 -0
  26. package/commands/schema.command.d.ts.map +1 -0
  27. package/commands/schema.command.js +18 -0
  28. package/commands/schema.command.js.map +1 -0
  29. package/exec.d.ts +3 -0
  30. package/exec.d.ts.map +1 -0
  31. package/exec.js +142 -0
  32. package/exec.js.map +1 -0
  33. package/{src/index.ts → index.d.ts} +1 -0
  34. package/index.d.ts.map +1 -0
  35. package/index.js +20 -0
  36. package/index.js.map +1 -0
  37. package/{src/interfaces/index.ts → interfaces/index.d.ts} +1 -0
  38. package/interfaces/index.d.ts.map +1 -0
  39. package/interfaces/index.js +18 -0
  40. package/interfaces/index.js.map +1 -0
  41. package/interfaces/loader.interface.d.ts +21 -0
  42. package/interfaces/loader.interface.d.ts.map +1 -0
  43. package/interfaces/loader.interface.js +3 -0
  44. package/interfaces/loader.interface.js.map +1 -0
  45. package/main.d.ts +3 -0
  46. package/main.d.ts.map +1 -0
  47. package/main.js +6 -0
  48. package/main.js.map +1 -0
  49. package/package.json +1 -1
  50. package/providers/app-settings.provider.d.ts +8 -0
  51. package/providers/app-settings.provider.d.ts.map +1 -0
  52. package/providers/app-settings.provider.js +50 -0
  53. package/providers/app-settings.provider.js.map +1 -0
  54. package/providers/azure-key-vault.provider.d.ts +20 -0
  55. package/providers/azure-key-vault.provider.d.ts.map +1 -0
  56. package/providers/azure-key-vault.provider.js +143 -0
  57. package/providers/azure-key-vault.provider.js.map +1 -0
  58. package/providers/index.d.ts +7 -0
  59. package/providers/index.d.ts.map +1 -0
  60. package/providers/index.js +30 -0
  61. package/providers/index.js.map +1 -0
  62. package/providers/local.provider.d.ts +8 -0
  63. package/providers/local.provider.d.ts.map +1 -0
  64. package/providers/local.provider.js +31 -0
  65. package/providers/local.provider.js.map +1 -0
  66. package/providers/package-json.provider.d.ts +8 -0
  67. package/providers/package-json.provider.d.ts.map +1 -0
  68. package/providers/package-json.provider.js +29 -0
  69. package/providers/package-json.provider.js.map +1 -0
  70. package/tsconfig.build.tsbuildinfo +1 -0
  71. package/utils/command.util.d.ts +13 -0
  72. package/utils/command.util.d.ts.map +1 -0
  73. package/utils/command.util.js +134 -0
  74. package/utils/command.util.js.map +1 -0
  75. package/{src/utils/index.ts → utils/index.d.ts} +1 -0
  76. package/utils/index.d.ts.map +1 -0
  77. package/utils/index.js +23 -0
  78. package/utils/index.js.map +1 -0
  79. package/utils/interpolate.util.d.ts +4 -0
  80. package/utils/interpolate.util.d.ts.map +1 -0
  81. package/utils/interpolate.util.js +33 -0
  82. package/utils/interpolate.util.js.map +1 -0
  83. package/utils/json.util.d.ts +5 -0
  84. package/utils/json.util.d.ts.map +1 -0
  85. package/utils/json.util.js +48 -0
  86. package/utils/json.util.js.map +1 -0
  87. package/utils/logger.d.ts +3 -0
  88. package/utils/logger.d.ts.map +1 -0
  89. package/{src/utils/logger.ts → utils/logger.js} +6 -6
  90. package/utils/logger.js.map +1 -0
  91. package/utils/normalize.util.d.ts +3 -0
  92. package/utils/normalize.util.d.ts.map +1 -0
  93. package/utils/normalize.util.js +61 -0
  94. package/utils/normalize.util.js.map +1 -0
  95. package/utils/schema.util.d.ts +11 -0
  96. package/utils/schema.util.d.ts.map +1 -0
  97. package/utils/schema.util.js +100 -0
  98. package/utils/schema.util.js.map +1 -0
  99. package/.eslintignore +0 -3
  100. package/.eslintrc.json +0 -329
  101. package/.vscode/extensions.json +0 -18
  102. package/.vscode/launch.json +0 -30
  103. package/.vscode/settings.json +0 -29
  104. package/jest.config.json +0 -28
  105. package/src/commands/env.command.ts +0 -139
  106. package/src/commands/export.command.ts +0 -88
  107. package/src/commands/pull.command.ts +0 -52
  108. package/src/commands/push.command.ts +0 -48
  109. package/src/commands/schema.command.ts +0 -31
  110. package/src/exec.ts +0 -221
  111. package/src/interfaces/loader.interface.ts +0 -66
  112. package/src/main.ts +0 -6
  113. package/src/providers/app-settings.provider.ts +0 -67
  114. package/src/providers/azure-key-vault.provider.ts +0 -277
  115. package/src/providers/index.ts +0 -29
  116. package/src/providers/local.provider.ts +0 -44
  117. package/src/providers/package-json.provider.ts +0 -39
  118. package/src/utils/command.util.ts +0 -223
  119. package/src/utils/interpolate.util.ts +0 -65
  120. package/src/utils/json.util.ts +0 -116
  121. package/src/utils/normalize.util.ts +0 -142
  122. package/src/utils/schema.util.ts +0 -191
  123. package/tests/env/appsettings.json +0 -32
  124. package/tests/env/dev.env.json +0 -12
  125. package/tests/env/dev.local.env.json +0 -9
  126. package/tests/env/env.schema.json +0 -225
  127. package/tests/env/keys.json +0 -7
  128. package/tests/env/settings/schema.json +0 -239
  129. package/tests/env/settings/settings.json +0 -22
  130. package/tests/env.int.test.ts +0 -42
  131. package/tests/exec.ts +0 -19
  132. package/tests/export.int.test.ts +0 -9
  133. package/tests/pull-push.int.test.ts +0 -15
  134. package/tests/run.js +0 -32
  135. package/tests/schema.int.test.ts +0 -9
  136. package/tests/setup.ts +0 -13
  137. package/tsconfig.build.json +0 -10
  138. package/tsconfig.json +0 -37
@@ -1,67 +0,0 @@
1
- import chalk from 'chalk';
2
- import { CommandArguments } from '../arguments';
3
- import { EnvProvider } from '../interfaces';
4
- import { logger as globalLogger, readJson, writeJson } from '../utils';
5
-
6
- const KEY = 'app-settings';
7
-
8
- const logger = globalLogger.getChildLogger({
9
- prefix: [chalk.bold.blue(`[${KEY}]`)]
10
- });
11
-
12
- const APP_SETTINGS_DEFAULT = {
13
- '|DEFAULT|': {},
14
- '|MODE|': {},
15
- '|ENV|': {},
16
- '|LOCAL|': {}
17
- };
18
-
19
- interface AppSettingsCommandArguments extends CommandArguments {
20
- envFile: string;
21
- }
22
-
23
- /**
24
- * Loads config from appsettings.json.
25
- */
26
- export const AppSettingsProvider: EnvProvider<AppSettingsCommandArguments> = {
27
- key: KEY,
28
-
29
- builder: (builder) => {
30
- builder.options({
31
- envFile: {
32
- group: KEY,
33
- alias: 'ef',
34
- type: 'string',
35
- default: '[[root]]/appsettings.json',
36
- describe: 'Environment variables file path (non secrets)'
37
- }
38
- });
39
- },
40
-
41
- load: async ({ env, modes = [], envFile, local }) => {
42
- const [appsettings = APP_SETTINGS_DEFAULT, wasFound] = await readJson(
43
- envFile
44
- );
45
-
46
- if (!wasFound) {
47
- logger.warn(`${chalk.blue(envFile)} not found`);
48
-
49
- logger.debug(`creating default ${chalk.blue(envFile)} file`);
50
-
51
- await writeJson(envFile, APP_SETTINGS_DEFAULT);
52
- }
53
-
54
- // only load local in env load cmd
55
- if (!local) appsettings['|LOCAL|'] = null;
56
-
57
- return [
58
- appsettings['|DEFAULT|'],
59
-
60
- appsettings['|ENV|']?.[env],
61
-
62
- ...modes.map((mode) => appsettings['|MODE|']?.[mode]),
63
-
64
- appsettings['|LOCAL|']?.[env]
65
- ];
66
- }
67
- };
@@ -1,277 +0,0 @@
1
- import chalk from 'chalk';
2
- import {
3
- AzureKeyVault,
4
- AzureKeyVaultSecrets,
5
- createAzureKeyVaultMock
6
- } from '@achs/azure-key-vault';
7
- import { CommandArguments } from '../arguments';
8
- import { EnvProvider } from '../interfaces';
9
- import {
10
- generateSchemaFrom,
11
- logger as globalLogger,
12
- readJson,
13
- schemaToJson,
14
- writeJson
15
- } from '../utils';
16
- import { Arguments } from 'yargs';
17
- import { existsSync } from 'fs';
18
- import { PullCommandArguments } from 'commands/pull.command';
19
-
20
- const KEY = 'azure-key-vault';
21
-
22
- const logger = globalLogger.getChildLogger({
23
- prefix: [chalk.bold.blue(`[${KEY}]`)]
24
- });
25
-
26
- interface AzureKeyVaultCommandArguments extends CommandArguments {
27
- secretsFile: string;
28
- keysFile?: string[];
29
- vaultUrl: string;
30
- spn: string;
31
- password: string;
32
- tenant: string;
33
- mock: boolean;
34
- }
35
-
36
- interface AzureKeyVaultCommandConfig {
37
- [key: string]: {
38
- vaultUrl: string;
39
- };
40
- }
41
-
42
- /**
43
- * Loads secrets from env files in env/secrets
44
- * folder, loaded from Azure Key Vault.
45
- */
46
- export const AzureKeyVaultProvider: EnvProvider<
47
- AzureKeyVaultCommandArguments & PullCommandArguments,
48
- AzureKeyVaultCommandConfig
49
- > = {
50
- key: KEY,
51
-
52
- builder: (builder) => {
53
- builder.options({
54
- secretsFile: {
55
- group: KEY,
56
- type: 'string',
57
- default: '[[root]]/[[env]].env.json',
58
- describe: 'Secret variables file path'
59
- },
60
- keysFile: {
61
- group: KEY,
62
- alias: ['k', 'keys'],
63
- type: 'array',
64
- default: ['[[root]]/keys.json', '../keys.json'],
65
- describe: 'Azure Key Vault keys file path'
66
- },
67
- vaultUrl: {
68
- group: KEY,
69
- alias: 'url',
70
- type: 'string',
71
- describe: 'Azure Key Vault URL'
72
- },
73
- spn: {
74
- group: KEY,
75
- alias: ['clientId', 'id'],
76
- type: 'string',
77
- describe: 'SPN Client ID'
78
- },
79
- password: {
80
- group: KEY,
81
- alias: ['p', 'pass', 'clientSecret'],
82
- type: 'string',
83
- describe: 'SPN Client Secret Password'
84
- },
85
- tenant: {
86
- group: KEY,
87
- alias: 't',
88
- type: 'string',
89
- describe: 'Azure Tenant ID'
90
- },
91
- mock: {
92
- group: KEY,
93
- type: 'boolean',
94
- default: false,
95
- describe: 'Mocks Azure Key Vault client'
96
- }
97
- });
98
- },
99
-
100
- push: async (argv, config) => {
101
- const [secrets, secretsWasFound] = await readJson(argv.secretsFile);
102
-
103
- if (!secretsWasFound) {
104
- logger.error(`${chalk.blue(argv.secretFile)} not found`);
105
-
106
- process.exit(1);
107
- }
108
-
109
- logger.silly('local secrets loaded:', secrets);
110
- const akv = await loadAzureKeyVaultClient(
111
- argv,
112
- config?.[argv.env]?.vaultUrl
113
- );
114
- logger.info('pushing variables to store');
115
- const results = await akv.setAll(secrets);
116
- logger.silly('secrets pushed:', results);
117
-
118
- const schema = await generateSchemaFrom(
119
- [{ key: KEY, value: secrets }],
120
- argv
121
- );
122
-
123
- logger.silly('schema for akv updated:', schema);
124
- },
125
-
126
- pull: async (argv, config) => {
127
- const schema = argv.schema?.[KEY] as
128
- | Record<string, unknown>
129
- | undefined;
130
-
131
- if (!schema) {
132
- logger.error('no schema found');
133
-
134
- process.exit(1);
135
- }
136
-
137
- const jsonTemplate = schemaToJson(schema) as Record<string, unknown>;
138
-
139
- const akv = await loadAzureKeyVaultClient(
140
- argv,
141
- config?.[argv.env]?.vaultUrl
142
- );
143
-
144
- logger.info('pulling variables from store');
145
-
146
- const secrets = await akv.getFor(
147
- jsonTemplate as AzureKeyVaultSecrets,
148
- true
149
- );
150
-
151
- logger.silly('remote secrets loaded:', secrets);
152
-
153
- await writeJson(argv.secretsFile, secrets, argv.overwrite, true);
154
- },
155
-
156
- load: async (argv, config) => {
157
- const { secretsFile } = argv;
158
-
159
- if (!existsSync(secretsFile)) {
160
- logger.warn('secrets file not found, pulling from store');
161
-
162
- await AzureKeyVaultProvider.pull!(argv, config);
163
- }
164
-
165
- const [secrets] = await readJson(secretsFile);
166
-
167
- return [secrets];
168
- }
169
- };
170
-
171
- /**
172
- * Validate SPN credentials/keys.
173
- *
174
- * @param {(Record<string, any> | null)} keys
175
- *
176
- * @returns {boolean}
177
- */
178
- function keysAreValid(keys: Record<string, any> | null): boolean {
179
- return keys && keys.clientId && keys.clientSecret && keys.tenantId;
180
- }
181
-
182
- /**
183
- * Loads SPN Azure Key Vault credentials from keys file.
184
- *
185
- * @param {string} env
186
- * @param {string[]} paths
187
- *
188
- * @returns {Record<string, string>} credentials
189
- */
190
- async function loadKeysFile(
191
- env: string,
192
- paths: string[]
193
- ): Promise<Record<string, string>> {
194
- logger.debug(`searching keys at ${chalk.yellow(paths.join(','))}`);
195
-
196
- const readers = await Promise.all(paths.map((path) => readJson(path)));
197
-
198
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
199
- const [keys, wasFound] = readers.find(
200
- ([keys, wasFound]) => wasFound && keysAreValid(keys[env])
201
- ) ?? [null, false];
202
-
203
- // if (!wasFound || !keys) {
204
- // logger.error(`no credentials found for ${chalk.green.underline(env)}`);
205
- // logger.warn(
206
- // 'if you use Azure CLI, set "keysFile": null in your env.config file'
207
- // );
208
-
209
- // process.exit(1);
210
- // }
211
-
212
- return keys?.[env] ?? {};
213
- }
214
-
215
- /**
216
- * Loads credentials and initializes Azure Key Vault client.
217
- *
218
- * @param {Arguments<AzureKeyVaultCommandArguments>} argv command arguments
219
- *
220
- * @returns {*} {Promise<AzureKeyVault>}
221
- */
222
- async function loadAzureKeyVaultClient(
223
- {
224
- env,
225
- vaultUrl,
226
- spn,
227
- password,
228
- tenant,
229
- app,
230
- keysFile,
231
- mock
232
- }: Arguments<AzureKeyVaultCommandArguments>,
233
- configVaultUrl?: string
234
- ): Promise<AzureKeyVault> {
235
- let url = process.env.AZURE_VAULT_URL ?? vaultUrl ?? configVaultUrl;
236
-
237
- const config = {
238
- project: process.env.AZURE_PROJECT ?? (app?.project as string),
239
- group: process.env.AZURE_GROUP ?? (app?.name as string),
240
- env
241
- };
242
-
243
- if (!config.project) {
244
- logger.error(
245
- `no project info from ${chalk.blue('package.json')} found`
246
- );
247
-
248
- process.exit(1);
249
- }
250
-
251
- const credentials = {
252
- clientId: process.env.AZURE_CLIENT_ID ?? spn,
253
- clientSecret: process.env.AZURE_CLIENT_SECRET ?? password,
254
- tenantId: process.env.AZURE_TENANT_ID ?? tenant
255
- };
256
-
257
- if (keysFile && keysFile.length > 0 && !keysAreValid(credentials)) {
258
- const keys = await loadKeysFile(env, keysFile);
259
-
260
- url ??= keys.vaultUrl;
261
- credentials.clientId ??= keys.clientId;
262
- credentials.clientSecret ??= keys.clientSecret;
263
- credentials.tenantId ??= keys.tenantId;
264
- }
265
-
266
- logger.debug(
267
- `credentials loaded for project ${chalk.bold.underline.yellowBright(
268
- config.project
269
- )} and group ${chalk.bold.underline.yellowBright(config.group)}`
270
- );
271
-
272
- logger.debug(`connected to ${chalk.bold.underline.greenBright(url)}`);
273
-
274
- return mock
275
- ? createAzureKeyVaultMock(config)
276
- : new AzureKeyVault(url, config, credentials);
277
- }
@@ -1,29 +0,0 @@
1
- import { EnvProvider } from '../interfaces';
2
- import { AppSettingsProvider } from './app-settings.provider';
3
- import { PackageJsonProvider } from './package-json.provider';
4
- import { AzureKeyVaultProvider } from './azure-key-vault.provider';
5
- import { LocalProvider } from './local.provider';
6
-
7
- const IntegratedProviders: Record<string, EnvProvider<any, any>> = {
8
- [PackageJsonProvider.key]: PackageJsonProvider,
9
- [AppSettingsProvider.key]: AppSettingsProvider,
10
- [AzureKeyVaultProvider.key]: AzureKeyVaultProvider,
11
- [LocalProvider.key]: LocalProvider
12
- };
13
-
14
- const IntegratedProviderConfig = [
15
- {
16
- path: PackageJsonProvider.key
17
- },
18
- {
19
- path: AppSettingsProvider.key
20
- },
21
- {
22
- path: AzureKeyVaultProvider.key
23
- },
24
- {
25
- path: LocalProvider.key
26
- }
27
- ];
28
-
29
- export { IntegratedProviders, IntegratedProviderConfig };
@@ -1,44 +0,0 @@
1
- import { existsSync } from 'fs';
2
- import { CommandArguments } from '../arguments';
3
- import { EnvProvider } from '../interfaces';
4
- import { readJson, writeJson } from '../utils';
5
-
6
- const KEY = 'local';
7
-
8
- interface LocalCommandArguments extends CommandArguments {
9
- localFile: string;
10
- }
11
-
12
- /**
13
- * Loads local variables from env files in env folder.
14
- */
15
- export const LocalProvider: EnvProvider<LocalCommandArguments> = {
16
- key: KEY,
17
-
18
- builder: (builder) => {
19
- builder.options({
20
- localFile: {
21
- group: KEY,
22
- alias: 'lf',
23
- type: 'string',
24
- default: '[[root]]/[[env]].local.env.json',
25
- describe: 'Local secret variables file path'
26
- }
27
- });
28
- },
29
-
30
- load: async ({ localFile, ci }) => {
31
- // ci mode doesn't load local vars
32
- if (ci) return [];
33
-
34
- if (!existsSync(localFile)) {
35
- await writeJson(localFile, {});
36
-
37
- return [];
38
- }
39
-
40
- const [vars] = await readJson(localFile);
41
-
42
- return [vars];
43
- }
44
- };
@@ -1,39 +0,0 @@
1
- import { CommandArguments } from '../arguments';
2
- import { EnvProvider } from '../interfaces';
3
-
4
- const KEY = 'package-json';
5
-
6
- interface PackageJsonCommandArguments extends CommandArguments {
7
- varPrefix: string;
8
- }
9
-
10
- /**
11
- * Loads project info from package.json.
12
- */
13
- export const PackageJsonProvider: EnvProvider<PackageJsonCommandArguments> = {
14
- key: KEY,
15
-
16
- builder: (builder) => {
17
- builder.options({
18
- varPrefix: {
19
- group: KEY,
20
- alias: 'vp',
21
- type: 'string',
22
- default: '',
23
- describe: 'Prefix for loaded variables'
24
- }
25
- });
26
- },
27
-
28
- load: ({ env, app, varPrefix }) => {
29
- return {
30
- [`${varPrefix}ENV`]: env,
31
-
32
- [`${varPrefix}VERSION`]: app?.version,
33
- [`${varPrefix}PROJECT`]: app?.project,
34
- [`${varPrefix}NAME`]: app?.name,
35
- [`${varPrefix}TITLE`]: app?.title,
36
- [`${varPrefix}DESCRIPTION`]: app?.description
37
- };
38
- }
39
- };
@@ -1,223 +0,0 @@
1
- import chalk from 'chalk';
2
- import merge from 'merge-deep';
3
- import { Arguments } from 'yargs';
4
- import { CommandArguments } from 'arguments';
5
- import { EnvCommandArguments } from 'commands/env.command';
6
- import { EnvProviderConfig, EnvProviderResult } from '../interfaces';
7
- import {
8
- createValidators,
9
- interpolate,
10
- logger,
11
- readJson,
12
- schemaFrom,
13
- writeJson
14
- } from '../utils';
15
-
16
- /**
17
- * Injects config to command arguments from file.
18
- *
19
- * @param {Record<string, unknown>} argv
20
- * @param {[string, string]} delimiters
21
- */
22
- export async function loadConfigFile(
23
- argv: Record<string, unknown>,
24
- delimiters: [string, string]
25
- ): Promise<void> {
26
- if (typeof argv.configFile === 'string') {
27
- const path = interpolate(argv.configFile, argv, delimiters);
28
- const [config, success] = await readJson(path);
29
-
30
- if (success) for (const key in config) argv[key] ??= config[key];
31
- }
32
- }
33
-
34
- /**
35
- * Extracts subcommand from command line parameters.
36
- *
37
- * @export
38
- * @param {string[]} rawArgv process.argv.slice(2)
39
- * @param {[string, string]} delimiters
40
- *
41
- * @returns {string[]} subcommand for wrap if exists
42
- */
43
- export function getSubcommand(rawArgv: string[], delimiters: [string, string]) {
44
- let subcommand: string[] = [];
45
-
46
- // subcommand delimiter indexes
47
- const begin = rawArgv.indexOf(delimiters[0]);
48
- const count = rawArgv.lastIndexOf(delimiters[1]) - begin;
49
-
50
- // calculates subcommand surrounded by delimiters
51
- if (begin > 0) {
52
- subcommand =
53
- count > 0
54
- ? rawArgv.splice(begin, count + 1).slice(1, -1)
55
- : rawArgv.splice(begin).slice(1);
56
- }
57
-
58
- return subcommand;
59
- }
60
-
61
- /**
62
- * Loads providers JSON schema from file.
63
- *
64
- * @param {Record<string, unknown>} argv
65
- * @param {[string, string]} delimiters
66
- *
67
- * @returns {Promise<Record<string, unknown>>}
68
- */
69
- export async function loadSchemaFile(
70
- argv: Record<string, unknown>,
71
- delimiters: [string, string]
72
- ): Promise<Record<string, unknown> | undefined> {
73
- if (typeof argv.schemaFile === 'string') {
74
- const path = interpolate(argv.schemaFile, argv, delimiters);
75
- const [schema, success] = await readJson(path);
76
-
77
- return success ? schema : undefined;
78
- }
79
-
80
- return undefined;
81
- }
82
-
83
- /**
84
- * Reads project package.json.
85
- *
86
- * @export
87
- * @returns {Promise<Record<string, unknown>> | never}
88
- */
89
- export async function loadProjectInfo(): Promise<
90
- Record<string, unknown> | undefined
91
- > {
92
- try {
93
- return await import(`${process.cwd()}/package.json`);
94
- } catch {
95
- logger.warn(
96
- `project file ${chalk.underline.yellow('package.json')} not found`
97
- );
98
-
99
- return undefined;
100
- }
101
- }
102
-
103
- /**
104
- * Executes load functions from provider handlers.
105
- *
106
- * @param {EnvProviderConfig[]} providers
107
- * @param {Partial<Arguments<EnvCommandArguments>>} argv
108
- *
109
- * @returns {EnvProviderResult[]}
110
- */
111
- export function loadVariablesFromProviders(
112
- providers: EnvProviderConfig[],
113
- argv: Partial<Arguments<EnvCommandArguments>>
114
- ): Promise<EnvProviderResult[]> {
115
- if (!providers) return Promise.resolve([]) as Promise<EnvProviderResult[]>;
116
-
117
- return Promise.all(
118
- providers.map(({ handler: { key, load }, config }) => {
119
- logger.silly(`executing ${chalk.yellow(key)} provider`);
120
-
121
- const value = load(argv, config);
122
-
123
- if (value instanceof Promise) {
124
- return value.then((value) => ({
125
- key,
126
- config,
127
- value
128
- }));
129
- } else {
130
- return { key, config, value };
131
- }
132
- })
133
- );
134
- }
135
-
136
- /**
137
- * Flattern environment provider results.
138
- *
139
- * @param {EnvProviderResult[]} results
140
- * @param {Partial<Arguments<EnvCommandArguments>>} argv
141
- *
142
- * @throws {Error} on schema validation failed
143
- *
144
- * @returns {EnvProviderResult[]} flatten results
145
- */
146
- export function flatResults(
147
- results: EnvProviderResult[]
148
- ): EnvProviderResult[] | never {
149
- return results.flatMap(({ value }) => {
150
- if (Array.isArray(value)) value = merge({}, ...value);
151
-
152
- return value;
153
- });
154
- }
155
-
156
- /**
157
- * Flattern and validates environment provider results.
158
- *
159
- * @param {EnvProviderResult[]} results
160
- * @param {Partial<Arguments<EnvCommandArguments>>} argv
161
- *
162
- * @throws {Error} on schema validation failed
163
- *
164
- * @returns {EnvProviderResult[]}
165
- */
166
- export function flatAndValidateResults(
167
- results: EnvProviderResult[],
168
- argv: Partial<Arguments<EnvCommandArguments>>
169
- ): EnvProviderResult[] | never {
170
- if (!argv.schemaValidate) return flatResults(results);
171
-
172
- const validators = createValidators(argv.schema!, argv.detectFormat);
173
-
174
- return results.flatMap(({ key, value }) => {
175
- if (Array.isArray(value)) value = merge({}, ...value);
176
-
177
- const validator = validators![key];
178
-
179
- if (validator(value)) return value;
180
-
181
- logger.error(
182
- `schema validation failed for ${chalk.yellow(key)}`,
183
- validator.errors
184
- );
185
-
186
- throw new Error(`schema validation failed for ${key}`);
187
- });
188
- }
189
-
190
- /**
191
- * Creates or updates JSON schema from
192
- * environment variables grouped by provider key.
193
- *
194
- * @export
195
- * @param {EnvProviderResult[]} env
196
- * @param {Arguments<EnvCommandArguments>} argv
197
- *
198
- * @returns {Promise<object>} JSON schema grouped by provider key.
199
- */
200
- export async function generateSchemaFrom(
201
- env: EnvProviderResult[],
202
- argv: Arguments<CommandArguments>
203
- ): Promise<object> {
204
- const { resolve, nullable, detectFormat, schemaFile } = argv;
205
-
206
- // generates schemas from proviers results
207
- let schema = env.reduce((schema, { key, value }) => {
208
- const env = Array.isArray(value) ? merge({}, ...value) : value;
209
-
210
- schema[key] = schemaFrom(env, {
211
- nullable,
212
- strings: { detectFormat }
213
- });
214
-
215
- return schema;
216
- }, {} as Record<string, unknown>);
217
-
218
- if (resolve === 'merge') schema = merge(argv.schema, schema);
219
-
220
- await writeJson(schemaFile, schema, true);
221
-
222
- return schema;
223
- }