@achs/env 5.0.0-alpha.3 → 5.0.0-alpha.4
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/commands/env.command.d.ts.map +1 -1
- package/commands/env.command.js +35 -25
- package/commands/env.command.js.map +1 -1
- package/commands/export.command.d.ts.map +1 -1
- package/commands/export.command.js +12 -11
- package/commands/export.command.js.map +1 -1
- package/commands/pull.command.js +7 -6
- package/commands/pull.command.js.map +1 -1
- package/commands/push.command.js +7 -6
- package/commands/push.command.js.map +1 -1
- package/commands/schema.command.d.ts.map +1 -1
- package/commands/schema.command.js +7 -6
- package/commands/schema.command.js.map +1 -1
- package/exec.d.ts.map +1 -1
- package/exec.js +40 -41
- package/exec.js.map +1 -1
- package/package.json +1 -1
- package/providers/azure-key-vault.provider.d.ts.map +1 -1
- package/providers/azure-key-vault.provider.js +48 -47
- package/providers/azure-key-vault.provider.js.map +1 -1
- package/utils/command.util.d.ts +1 -1
- package/utils/command.util.d.ts.map +1 -1
- package/utils/command.util.js +3 -3
- package/utils/command.util.js.map +1 -1
- package/utils/index.d.ts +1 -0
- package/utils/index.d.ts.map +1 -1
- package/utils/index.js +2 -1
- package/utils/json.util.d.ts.map +1 -1
- package/utils/json.util.js +7 -2
- package/utils/json.util.js.map +1 -1
- package/utils/schema.util.d.ts +6 -6
- package/utils/schema.util.d.ts.map +1 -1
- package/utils/schema.util.js +20 -19
- package/utils/schema.util.js.map +1 -1
- package/utils/ui.d.ts +12 -0
- package/utils/ui.d.ts.map +1 -0
- package/utils/ui.js +49 -0
- package/utils/ui.js.map +1 -0
|
@@ -5,24 +5,23 @@ import { schemaToJson as i } from "../utils/schema.util.js";
|
|
|
5
5
|
import "../utils/index.js";
|
|
6
6
|
import a from "picocolors";
|
|
7
7
|
import { existsSync as o } from "node:fs";
|
|
8
|
-
import
|
|
9
|
-
import l from "node:dns";
|
|
8
|
+
import s from "node:dns";
|
|
10
9
|
//#region src/providers/azure-key-vault.provider.ts
|
|
11
|
-
var
|
|
12
|
-
key:
|
|
10
|
+
var c = "azure-key-vault", l = r.getSubLogger({ prefix: [a.bold(a.blue(`[${c}]`))] }), u = {
|
|
11
|
+
key: c,
|
|
13
12
|
builder: (e) => {
|
|
14
13
|
e.options({
|
|
15
14
|
keysFile: {
|
|
16
15
|
alias: ["k", "keys"],
|
|
17
16
|
default: ["[[root]]/keys.json", "../keys.json"],
|
|
18
17
|
describe: "Azure Key Vault keys file path",
|
|
19
|
-
group:
|
|
18
|
+
group: c,
|
|
20
19
|
type: "array"
|
|
21
20
|
},
|
|
22
21
|
mock: {
|
|
23
22
|
default: !1,
|
|
24
23
|
describe: "Mocks Azure Key Vault client",
|
|
25
|
-
group:
|
|
24
|
+
group: c,
|
|
26
25
|
type: "boolean"
|
|
27
26
|
},
|
|
28
27
|
password: {
|
|
@@ -32,13 +31,13 @@ var u = "azure-key-vault", d = r.getSubLogger({ prefix: [a.bold(a.blue(`[${u}]`)
|
|
|
32
31
|
"clientSecret"
|
|
33
32
|
],
|
|
34
33
|
describe: "SPN Client Secret Password",
|
|
35
|
-
group:
|
|
34
|
+
group: c,
|
|
36
35
|
type: "string"
|
|
37
36
|
},
|
|
38
37
|
secretsFile: {
|
|
39
38
|
default: "[[root]]/[[env]].env.json",
|
|
40
39
|
describe: "Secret variables file path",
|
|
41
|
-
group:
|
|
40
|
+
group: c,
|
|
42
41
|
type: "string"
|
|
43
42
|
},
|
|
44
43
|
skipDnsCheck: {
|
|
@@ -50,94 +49,96 @@ var u = "azure-key-vault", d = r.getSubLogger({ prefix: [a.bold(a.blue(`[${u}]`)
|
|
|
50
49
|
spn: {
|
|
51
50
|
alias: ["clientId", "id"],
|
|
52
51
|
describe: "SPN Client ID",
|
|
53
|
-
group:
|
|
52
|
+
group: c,
|
|
54
53
|
type: "string"
|
|
55
54
|
},
|
|
56
55
|
tenant: {
|
|
57
56
|
alias: "t",
|
|
58
57
|
describe: "Azure Tenant ID",
|
|
59
|
-
group:
|
|
58
|
+
group: c,
|
|
60
59
|
type: "string"
|
|
61
60
|
},
|
|
62
61
|
vaultUrl: {
|
|
63
62
|
alias: "url",
|
|
64
63
|
describe: "Azure Key Vault URL",
|
|
65
|
-
group:
|
|
64
|
+
group: c,
|
|
66
65
|
type: "string"
|
|
67
66
|
}
|
|
68
67
|
});
|
|
69
68
|
},
|
|
70
69
|
push: async (n, r) => {
|
|
71
70
|
let [i, o] = await t(n.secretsFile);
|
|
72
|
-
o || (
|
|
73
|
-
let s = await
|
|
74
|
-
|
|
75
|
-
let
|
|
76
|
-
|
|
77
|
-
let
|
|
78
|
-
key:
|
|
71
|
+
o || (l.error(`${a.blue(n.secretsFile)} not found`), process.exit(1)), l.silly("local secrets loaded:", i);
|
|
72
|
+
let s = await p(n, r?.[n.env]?.vaultUrl);
|
|
73
|
+
l.info("pushing variables to store");
|
|
74
|
+
let u = await s.setAll(i);
|
|
75
|
+
l.silly("secrets pushed:", u);
|
|
76
|
+
let d = await e([{
|
|
77
|
+
key: c,
|
|
79
78
|
value: i
|
|
80
79
|
}], n);
|
|
81
|
-
|
|
80
|
+
l.silly("schema for akv updated:", d);
|
|
82
81
|
},
|
|
83
82
|
pull: async (e, t) => {
|
|
84
|
-
let r = e.schema?.[
|
|
83
|
+
let r = e.schema?.[c];
|
|
85
84
|
if (!r) {
|
|
86
|
-
|
|
85
|
+
l.error("no schema found, you can gen one with \"env schema -e {env} -m {modes}\""), l.info(`generated empty ${a.bold(a.underline(a.yellowBright(e.secretsFile)))} file (you can retry the command)`), await n(e.secretsFile, {}, !1, !0);
|
|
87
86
|
return;
|
|
88
87
|
}
|
|
89
88
|
let o = i(r), s = t?.[e.env]?.vaultUrl;
|
|
90
89
|
if (!s) return n(e.secretsFile, {}, !1, !0);
|
|
91
|
-
let
|
|
92
|
-
|
|
93
|
-
let
|
|
94
|
-
|
|
90
|
+
let u = await p(e, s);
|
|
91
|
+
l.info(`pulling stored secrets from ${a.underline(a.yellowBright(s))}`);
|
|
92
|
+
let d = await u.getFor(o, !0);
|
|
93
|
+
l.silly("remote secrets loaded:", d), await n(e.secretsFile, d, e.overwrite, !0);
|
|
95
94
|
},
|
|
96
95
|
load: async (e, n) => {
|
|
97
96
|
let { env: r, secretsFile: i } = e;
|
|
98
|
-
if (!r) return
|
|
99
|
-
o(i) || (
|
|
97
|
+
if (!r) return l.silly("no env, so skipping provider"), [];
|
|
98
|
+
o(i) || (l.warn("secrets file not found, pulling from store"), await u.pull(e, n));
|
|
100
99
|
let [a] = await t(i);
|
|
101
100
|
return [a];
|
|
102
101
|
}
|
|
103
102
|
};
|
|
104
|
-
function
|
|
103
|
+
function d(e) {
|
|
105
104
|
return !!(e && e.clientId && e.clientSecret && e.tenantId);
|
|
106
105
|
}
|
|
107
|
-
async function
|
|
108
|
-
|
|
109
|
-
let [r] = (await Promise.all(n.map((e) => t(e)))).find(([t, n]) => n &&
|
|
106
|
+
async function f(e, n) {
|
|
107
|
+
l.debug(`searching keys at ${a.yellow(n.join(","))}`);
|
|
108
|
+
let [r] = (await Promise.all(n.map((e) => t(e)))).find(([t, n]) => n && d(t[e])) ?? [null];
|
|
110
109
|
return r?.[e] ?? {};
|
|
111
110
|
}
|
|
112
|
-
async function
|
|
113
|
-
let
|
|
111
|
+
async function p({ env: e, keysFile: t, mock: n, password: r, projectInfo: i, skipDnsCheck: o, spn: s, tenant: c, vaultUrl: u }, p) {
|
|
112
|
+
let h = process.env.AZURE_VAULT_URL ?? u ?? p, g = {
|
|
114
113
|
env: e,
|
|
115
114
|
group: process.env.AZURE_GROUP ?? i.name,
|
|
116
115
|
project: process.env.AZURE_PROJECT ?? i.project
|
|
117
116
|
};
|
|
118
|
-
|
|
119
|
-
let
|
|
120
|
-
clientId: process.env.AZURE_CLIENT_ID ??
|
|
121
|
-
tenantId: process.env.AZURE_TENANT_ID ??
|
|
117
|
+
g.project || (l.error(`no project info from ${a.blue("package.json")} found`), process.exit(1));
|
|
118
|
+
let _ = {
|
|
119
|
+
clientId: process.env.AZURE_CLIENT_ID ?? s,
|
|
120
|
+
tenantId: process.env.AZURE_TENANT_ID ?? c,
|
|
122
121
|
clientSecret: process.env.AZURE_CLIENT_SECRET ?? r
|
|
123
122
|
};
|
|
124
|
-
if (t && t.length > 0 && !
|
|
125
|
-
let n = await
|
|
126
|
-
|
|
123
|
+
if (t && t.length > 0 && !d(_)) {
|
|
124
|
+
let n = await f(e, t);
|
|
125
|
+
h ??= n.vaultUrl, _.clientId ??= n.clientId, _.clientSecret ??= n.clientSecret, _.tenantId ??= n.tenantId;
|
|
127
126
|
}
|
|
128
|
-
|
|
129
|
-
let
|
|
130
|
-
|
|
127
|
+
l.debug(`credentials loaded for project ${a.bold(a.underline(a.yellowBright(g.project)))} and group ${a.bold(a.underline(a.yellowBright(g.group)))}`), l.debug(`connected to ${a.bold(a.underline(a.greenBright(h)))}`);
|
|
128
|
+
let { AzureKeyVault: v, createAzureKeyVaultMock: y } = await import("@achs/azure-key-vault");
|
|
129
|
+
if (n) return y(g);
|
|
130
|
+
let b = new v(h, g, _);
|
|
131
|
+
return o || await m(h), b;
|
|
131
132
|
}
|
|
132
|
-
function
|
|
133
|
-
let t = new
|
|
133
|
+
function m(e) {
|
|
134
|
+
let t = new s.Resolver();
|
|
134
135
|
return t.setServers(["8.8.8.8", "1.1.1.1"]), new Promise((n) => {
|
|
135
136
|
t.resolve4(new URL(e).hostname, (t) => {
|
|
136
|
-
t && (
|
|
137
|
+
t && (l.error(`cannot resolve vault host ${a.red(e)}`, t), process.exit(1)), l.debug(`vault host reachable ${a.green(e)}`), n();
|
|
137
138
|
});
|
|
138
139
|
});
|
|
139
140
|
}
|
|
140
141
|
//#endregion
|
|
141
|
-
export {
|
|
142
|
+
export { u as AzureKeyVaultProvider };
|
|
142
143
|
|
|
143
144
|
//# sourceMappingURL=azure-key-vault.provider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"azure-key-vault.provider.js","names":[],"sources":["../../src/providers/azure-key-vault.provider.ts"],"sourcesContent":["import { AzureKeyVault, createAzureKeyVaultMock } from '@achs/azure-key-vault';\r\nimport type { AzureKeyVaultSecrets } from '@achs/azure-key-vault';\r\nimport dns from 'node:dns';\r\nimport { existsSync } from 'node:fs';\r\nimport pc from 'picocolors';\r\nimport type { Arguments } from 'yargs';\r\nimport type { CommandArguments } from '../arguments.js';\r\nimport type { PullCommandArguments } from '../commands/pull.command.js';\r\nimport type { EnvProvider } from '../interfaces/index.js';\r\nimport {\r\n\tgenerateSchemaFrom,\r\n\tlogger as globalLogger,\r\n\treadJson,\r\n\tschemaToJson,\r\n\twriteJson,\r\n} from '../utils/index.js';\r\n\r\nconst KEY = 'azure-key-vault';\r\n\r\nconst logger = globalLogger.getSubLogger({\r\n\tprefix: [pc.bold(pc.blue(`[${KEY}]`))],\r\n});\r\n\r\ninterface AzureKeyVaultCommandArguments extends CommandArguments {\r\n\tmock: boolean;\r\n\tpassword: string;\r\n\tsecretsFile: string;\r\n\tskipDnsCheck: boolean;\r\n\tspn: string;\r\n\ttenant: string;\r\n\tvaultUrl: string;\r\n\tkeysFile?: string[];\r\n}\r\n\r\ninterface AzureKeyVaultCommandConfig {\r\n\t[key: string]: {\r\n\t\tvaultUrl: string;\r\n\t};\r\n}\r\n\r\n/**\r\n * Loads secrets from env files in env/secrets\r\n * folder, loaded from Azure Key Vault.\r\n */\r\nexport const AzureKeyVaultProvider: EnvProvider<\r\n\tAzureKeyVaultCommandArguments & PullCommandArguments,\r\n\tAzureKeyVaultCommandConfig\r\n> = {\r\n\tkey: KEY,\r\n\r\n\tbuilder: (builder) => {\r\n\t\tbuilder.options({\r\n\t\t\tkeysFile: {\r\n\t\t\t\talias: ['k', 'keys'],\r\n\t\t\t\tdefault: ['[[root]]/keys.json', '../keys.json'],\r\n\t\t\t\tdescribe: 'Azure Key Vault keys file path',\r\n\t\t\t\tgroup: KEY,\r\n\t\t\t\ttype: 'array',\r\n\t\t\t},\r\n\t\t\tmock: {\r\n\t\t\t\tdefault: false,\r\n\t\t\t\tdescribe: 'Mocks Azure Key Vault client',\r\n\t\t\t\tgroup: KEY,\r\n\t\t\t\ttype: 'boolean',\r\n\t\t\t},\r\n\t\t\tpassword: {\r\n\t\t\t\talias: ['p', 'pass', 'clientSecret'],\r\n\t\t\t\tdescribe: 'SPN Client Secret Password',\r\n\t\t\t\tgroup: KEY,\r\n\t\t\t\ttype: 'string',\r\n\t\t\t},\r\n\t\t\tsecretsFile: {\r\n\t\t\t\tdefault: '[[root]]/[[env]].env.json',\r\n\t\t\t\tdescribe: 'Secret variables file path',\r\n\t\t\t\tgroup: KEY,\r\n\t\t\t\ttype: 'string',\r\n\t\t\t},\r\n\t\t\tskipDnsCheck: {\r\n\t\t\t\talias: 'dns',\r\n\t\t\t\tdefault: false,\r\n\t\t\t\tdescribe: 'Skips DNS checking when connect to Azure Key Vault',\r\n\t\t\t\ttype: 'boolean',\r\n\t\t\t},\r\n\t\t\tspn: {\r\n\t\t\t\talias: ['clientId', 'id'],\r\n\t\t\t\tdescribe: 'SPN Client ID',\r\n\t\t\t\tgroup: KEY,\r\n\t\t\t\ttype: 'string',\r\n\t\t\t},\r\n\t\t\ttenant: {\r\n\t\t\t\talias: 't',\r\n\t\t\t\tdescribe: 'Azure Tenant ID',\r\n\t\t\t\tgroup: KEY,\r\n\t\t\t\ttype: 'string',\r\n\t\t\t},\r\n\t\t\tvaultUrl: {\r\n\t\t\t\talias: 'url',\r\n\t\t\t\tdescribe: 'Azure Key Vault URL',\r\n\t\t\t\tgroup: KEY,\r\n\t\t\t\ttype: 'string',\r\n\t\t\t},\r\n\t\t});\r\n\t},\r\n\r\n\tpush: async (argv, config) => {\r\n\t\tconst [secrets, secretsWasFound] = await readJson(argv.secretsFile);\r\n\r\n\t\tif (!secretsWasFound) {\r\n\t\t\tlogger.error(`${pc.blue(argv.secretsFile)} not found`);\r\n\r\n\t\t\tprocess.exit(1);\r\n\t\t}\r\n\r\n\t\tlogger.silly('local secrets loaded:', secrets);\r\n\t\tconst akv = await loadAzureKeyVaultClient(\r\n\t\t\targv,\r\n\t\t\tconfig?.[argv.env]?.vaultUrl,\r\n\t\t);\r\n\t\tlogger.info('pushing variables to store');\r\n\t\tconst results = await akv.setAll(secrets);\r\n\t\tlogger.silly('secrets pushed:', results);\r\n\r\n\t\tconst schema = await generateSchemaFrom(\r\n\t\t\t[{ key: KEY, value: secrets }],\r\n\t\t\targv,\r\n\t\t);\r\n\r\n\t\tlogger.silly('schema for akv updated:', schema);\r\n\t},\r\n\r\n\tpull: async (argv, config) => {\r\n\t\tconst schema = argv.schema?.[KEY] as\r\n\t\t\t| Record<string, unknown>\r\n\t\t\t| undefined;\r\n\r\n\t\tif (!schema) {\r\n\t\t\tlogger.error(\r\n\t\t\t\t'no schema found, you can gen one with \"env schema -e {env} -m {modes}\"',\r\n\t\t\t);\r\n\r\n\t\t\tlogger.info(\r\n\t\t\t\t`generated empty ${pc.bold(\r\n\t\t\t\t\tpc.underline(pc.yellowBright(argv.secretsFile)),\r\n\t\t\t\t)} file (you can retry the command)`,\r\n\t\t\t);\r\n\t\t\tawait writeJson(argv.secretsFile, {}, false, true);\r\n\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst jsonTemplate = schemaToJson(schema) as Record<string, unknown>;\r\n\r\n\t\tconst vaultUrl = config?.[argv.env]?.vaultUrl;\r\n\t\tif (!vaultUrl) return writeJson(argv.secretsFile, {}, false, true);\r\n\r\n\t\tconst akv = await loadAzureKeyVaultClient(argv, vaultUrl);\r\n\r\n\t\tlogger.info(\r\n\t\t\t`pulling stored secrets from ${pc.underline(pc.yellowBright(vaultUrl))}`,\r\n\t\t);\r\n\r\n\t\tconst secrets = await akv.getFor(\r\n\t\t\tjsonTemplate as AzureKeyVaultSecrets,\r\n\t\t\ttrue,\r\n\t\t);\r\n\r\n\t\tlogger.silly('remote secrets loaded:', secrets);\r\n\r\n\t\tawait writeJson(argv.secretsFile, secrets, argv.overwrite, true);\r\n\t},\r\n\r\n\tload: async (argv, config) => {\r\n\t\tconst { env, secretsFile } = argv;\r\n\r\n\t\tif (!env) {\r\n\t\t\tlogger.silly('no env, so skipping provider');\r\n\r\n\t\t\treturn [];\r\n\t\t}\r\n\r\n\t\tif (!existsSync(secretsFile)) {\r\n\t\t\tlogger.warn('secrets file not found, pulling from store');\r\n\r\n\t\t\tawait AzureKeyVaultProvider.pull!(argv, config);\r\n\t\t}\r\n\r\n\t\tconst [secrets] = await readJson(secretsFile);\r\n\r\n\t\treturn [secrets];\r\n\t},\r\n};\r\n\r\n/**\r\n * Validate SPN credentials/keys.\r\n *\r\n * @param {(Record<string, any> | null)} keys\r\n *\r\n * @returns {boolean}\r\n */\r\nfunction keysAreValid(keys: Record<string, any> | null): boolean {\r\n\treturn Boolean(keys && keys.clientId && keys.clientSecret && keys.tenantId);\r\n}\r\n\r\n/**\r\n * Loads SPN Azure Key Vault credentials from keys file.\r\n *\r\n * @param {string} env\r\n * @param {string[]} paths\r\n *\r\n * @returns {Record<string, string>} credentials\r\n */\r\nasync function loadKeysFile(\r\n\tenv: string,\r\n\tpaths: string[],\r\n): Promise<Record<string, string>> {\r\n\tlogger.debug(`searching keys at ${pc.yellow(paths.join(','))}`);\r\n\r\n\tconst readers = await Promise.all(paths.map((path) => readJson(path)));\r\n\r\n\tconst [keys] = readers.find(\r\n\t\t([candidate, wasFound]) => wasFound && keysAreValid(candidate[env]),\r\n\t) ?? [null];\r\n\r\n\treturn keys?.[env] ?? {};\r\n}\r\n\r\n/**\r\n * Loads credentials and initializes Azure Key Vault client.\r\n *\r\n * @param {Arguments<AzureKeyVaultCommandArguments>} argv command arguments\r\n *\r\n * @returns {*} {Promise<AzureKeyVault>}\r\n */\r\n// linear credential-resolution (env vars → args → keys file precedence);\r\n// complexity comes from `??` fallbacks, not tangled control flow\r\n// eslint-disable-next-line complexity\r\nasync function loadAzureKeyVaultClient(\r\n\t{\r\n\t\tenv,\r\n\t\tkeysFile,\r\n\t\tmock,\r\n\t\tpassword,\r\n\t\tprojectInfo,\r\n\t\tskipDnsCheck,\r\n\t\tspn,\r\n\t\ttenant,\r\n\t\tvaultUrl,\r\n\t}: Arguments<AzureKeyVaultCommandArguments>,\r\n\tconfigVaultUrl?: string,\r\n): Promise<AzureKeyVault> {\r\n\tlet url = process.env.AZURE_VAULT_URL ?? vaultUrl ?? configVaultUrl;\r\n\r\n\tconst config = {\r\n\t\tenv,\r\n\t\tgroup: process.env.AZURE_GROUP ?? projectInfo.name,\r\n\t\tproject: process.env.AZURE_PROJECT ?? projectInfo.project,\r\n\t};\r\n\r\n\tif (!config.project) {\r\n\t\tlogger.error(`no project info from ${pc.blue('package.json')} found`);\r\n\r\n\t\tprocess.exit(1);\r\n\t}\r\n\r\n\tconst credentials = {\r\n\t\tclientId: process.env.AZURE_CLIENT_ID ?? spn,\r\n\t\ttenantId: process.env.AZURE_TENANT_ID ?? tenant,\r\n\t\tclientSecret: process.env.AZURE_CLIENT_SECRET ?? password,\r\n\t};\r\n\r\n\tif (keysFile && keysFile.length > 0 && !keysAreValid(credentials)) {\r\n\t\tconst keys = await loadKeysFile(env, keysFile);\r\n\r\n\t\turl ??= keys.vaultUrl;\r\n\t\tcredentials.clientId ??= keys.clientId;\r\n\t\tcredentials.clientSecret ??= keys.clientSecret;\r\n\t\tcredentials.tenantId ??= keys.tenantId;\r\n\t}\r\n\r\n\tlogger.debug(\r\n\t\t`credentials loaded for project ${pc.bold(\r\n\t\t\tpc.underline(pc.yellowBright(config.project)),\r\n\t\t)} and group ${pc.bold(pc.underline(pc.yellowBright(config.group)))}`,\r\n\t);\r\n\r\n\tlogger.debug(`connected to ${pc.bold(pc.underline(pc.greenBright(url)))}`);\r\n\r\n\tif (mock) return createAzureKeyVaultMock(config);\r\n\r\n\tconst akv = new AzureKeyVault(url, config, credentials);\r\n\r\n\tif (!skipDnsCheck) await verifyVaultReachable(url);\r\n\r\n\treturn akv;\r\n}\r\n\r\n/**\r\n * Best-effort DNS reachability check for the vault host. Resolves the hostname\r\n * via public resolvers; logs and exits on failure. Does NOT issue a\r\n * guaranteed-failing secret call (the old `getSecret('')` probe).\r\n */\r\nfunction verifyVaultReachable(url: string): Promise<void> {\r\n\tconst resolver = new dns.Resolver();\r\n\r\n\tresolver.setServers(['8.8.8.8', '1.1.1.1']);\r\n\r\n\treturn new Promise<void>((resolve) => {\r\n\t\tresolver.resolve4(new URL(url).hostname, (error) => {\r\n\t\t\tif (error) {\r\n\t\t\t\tlogger.error(`cannot resolve vault host ${pc.red(url)}`, error);\r\n\t\t\t\tprocess.exit(1);\r\n\t\t\t}\r\n\r\n\t\t\tlogger.debug(`vault host reachable ${pc.green(url)}`);\r\n\t\t\tresolve();\r\n\t\t});\r\n\t});\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"azure-key-vault.provider.js","names":[],"sources":["../../src/providers/azure-key-vault.provider.ts"],"sourcesContent":["import type {\r\n\tAzureKeyVault,\r\n\tAzureKeyVaultSecrets,\r\n} from '@achs/azure-key-vault';\r\nimport dns from 'node:dns';\r\nimport { existsSync } from 'node:fs';\r\nimport pc from 'picocolors';\r\nimport type { Arguments } from 'yargs';\r\nimport type { CommandArguments } from '../arguments.js';\r\nimport type { PullCommandArguments } from '../commands/pull.command.js';\r\nimport type { EnvProvider } from '../interfaces/index.js';\r\nimport {\r\n\tgenerateSchemaFrom,\r\n\tlogger as globalLogger,\r\n\treadJson,\r\n\tschemaToJson,\r\n\twriteJson,\r\n} from '../utils/index.js';\r\n\r\nconst KEY = 'azure-key-vault';\r\n\r\nconst logger = globalLogger.getSubLogger({\r\n\tprefix: [pc.bold(pc.blue(`[${KEY}]`))],\r\n});\r\n\r\ninterface AzureKeyVaultCommandArguments extends CommandArguments {\r\n\tmock: boolean;\r\n\tpassword: string;\r\n\tsecretsFile: string;\r\n\tskipDnsCheck: boolean;\r\n\tspn: string;\r\n\ttenant: string;\r\n\tvaultUrl: string;\r\n\tkeysFile?: string[];\r\n}\r\n\r\ninterface AzureKeyVaultCommandConfig {\r\n\t[key: string]: {\r\n\t\tvaultUrl: string;\r\n\t};\r\n}\r\n\r\n/**\r\n * Loads secrets from env files in env/secrets\r\n * folder, loaded from Azure Key Vault.\r\n */\r\nexport const AzureKeyVaultProvider: EnvProvider<\r\n\tAzureKeyVaultCommandArguments & PullCommandArguments,\r\n\tAzureKeyVaultCommandConfig\r\n> = {\r\n\tkey: KEY,\r\n\r\n\tbuilder: (builder) => {\r\n\t\tbuilder.options({\r\n\t\t\tkeysFile: {\r\n\t\t\t\talias: ['k', 'keys'],\r\n\t\t\t\tdefault: ['[[root]]/keys.json', '../keys.json'],\r\n\t\t\t\tdescribe: 'Azure Key Vault keys file path',\r\n\t\t\t\tgroup: KEY,\r\n\t\t\t\ttype: 'array',\r\n\t\t\t},\r\n\t\t\tmock: {\r\n\t\t\t\tdefault: false,\r\n\t\t\t\tdescribe: 'Mocks Azure Key Vault client',\r\n\t\t\t\tgroup: KEY,\r\n\t\t\t\ttype: 'boolean',\r\n\t\t\t},\r\n\t\t\tpassword: {\r\n\t\t\t\talias: ['p', 'pass', 'clientSecret'],\r\n\t\t\t\tdescribe: 'SPN Client Secret Password',\r\n\t\t\t\tgroup: KEY,\r\n\t\t\t\ttype: 'string',\r\n\t\t\t},\r\n\t\t\tsecretsFile: {\r\n\t\t\t\tdefault: '[[root]]/[[env]].env.json',\r\n\t\t\t\tdescribe: 'Secret variables file path',\r\n\t\t\t\tgroup: KEY,\r\n\t\t\t\ttype: 'string',\r\n\t\t\t},\r\n\t\t\tskipDnsCheck: {\r\n\t\t\t\talias: 'dns',\r\n\t\t\t\tdefault: false,\r\n\t\t\t\tdescribe: 'Skips DNS checking when connect to Azure Key Vault',\r\n\t\t\t\ttype: 'boolean',\r\n\t\t\t},\r\n\t\t\tspn: {\r\n\t\t\t\talias: ['clientId', 'id'],\r\n\t\t\t\tdescribe: 'SPN Client ID',\r\n\t\t\t\tgroup: KEY,\r\n\t\t\t\ttype: 'string',\r\n\t\t\t},\r\n\t\t\ttenant: {\r\n\t\t\t\talias: 't',\r\n\t\t\t\tdescribe: 'Azure Tenant ID',\r\n\t\t\t\tgroup: KEY,\r\n\t\t\t\ttype: 'string',\r\n\t\t\t},\r\n\t\t\tvaultUrl: {\r\n\t\t\t\talias: 'url',\r\n\t\t\t\tdescribe: 'Azure Key Vault URL',\r\n\t\t\t\tgroup: KEY,\r\n\t\t\t\ttype: 'string',\r\n\t\t\t},\r\n\t\t});\r\n\t},\r\n\r\n\tpush: async (argv, config) => {\r\n\t\tconst [secrets, secretsWasFound] = await readJson(argv.secretsFile);\r\n\r\n\t\tif (!secretsWasFound) {\r\n\t\t\tlogger.error(`${pc.blue(argv.secretsFile)} not found`);\r\n\r\n\t\t\tprocess.exit(1);\r\n\t\t}\r\n\r\n\t\tlogger.silly('local secrets loaded:', secrets);\r\n\t\tconst akv = await loadAzureKeyVaultClient(\r\n\t\t\targv,\r\n\t\t\tconfig?.[argv.env]?.vaultUrl,\r\n\t\t);\r\n\t\tlogger.info('pushing variables to store');\r\n\t\tconst results = await akv.setAll(secrets);\r\n\t\tlogger.silly('secrets pushed:', results);\r\n\r\n\t\tconst schema = await generateSchemaFrom(\r\n\t\t\t[{ key: KEY, value: secrets }],\r\n\t\t\targv,\r\n\t\t);\r\n\r\n\t\tlogger.silly('schema for akv updated:', schema);\r\n\t},\r\n\r\n\tpull: async (argv, config) => {\r\n\t\tconst schema = argv.schema?.[KEY] as\r\n\t\t\t| Record<string, unknown>\r\n\t\t\t| undefined;\r\n\r\n\t\tif (!schema) {\r\n\t\t\tlogger.error(\r\n\t\t\t\t'no schema found, you can gen one with \"env schema -e {env} -m {modes}\"',\r\n\t\t\t);\r\n\r\n\t\t\tlogger.info(\r\n\t\t\t\t`generated empty ${pc.bold(\r\n\t\t\t\t\tpc.underline(pc.yellowBright(argv.secretsFile)),\r\n\t\t\t\t)} file (you can retry the command)`,\r\n\t\t\t);\r\n\t\t\tawait writeJson(argv.secretsFile, {}, false, true);\r\n\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst jsonTemplate = schemaToJson(schema) as Record<string, unknown>;\r\n\r\n\t\tconst vaultUrl = config?.[argv.env]?.vaultUrl;\r\n\t\tif (!vaultUrl) return writeJson(argv.secretsFile, {}, false, true);\r\n\r\n\t\tconst akv = await loadAzureKeyVaultClient(argv, vaultUrl);\r\n\r\n\t\tlogger.info(\r\n\t\t\t`pulling stored secrets from ${pc.underline(pc.yellowBright(vaultUrl))}`,\r\n\t\t);\r\n\r\n\t\tconst secrets = await akv.getFor(\r\n\t\t\tjsonTemplate as AzureKeyVaultSecrets,\r\n\t\t\ttrue,\r\n\t\t);\r\n\r\n\t\tlogger.silly('remote secrets loaded:', secrets);\r\n\r\n\t\tawait writeJson(argv.secretsFile, secrets, argv.overwrite, true);\r\n\t},\r\n\r\n\tload: async (argv, config) => {\r\n\t\tconst { env, secretsFile } = argv;\r\n\r\n\t\tif (!env) {\r\n\t\t\tlogger.silly('no env, so skipping provider');\r\n\r\n\t\t\treturn [];\r\n\t\t}\r\n\r\n\t\tif (!existsSync(secretsFile)) {\r\n\t\t\tlogger.warn('secrets file not found, pulling from store');\r\n\r\n\t\t\tawait AzureKeyVaultProvider.pull!(argv, config);\r\n\t\t}\r\n\r\n\t\tconst [secrets] = await readJson(secretsFile);\r\n\r\n\t\treturn [secrets];\r\n\t},\r\n};\r\n\r\n/**\r\n * Validate SPN credentials/keys.\r\n *\r\n * @param {(Record<string, any> | null)} keys\r\n *\r\n * @returns {boolean}\r\n */\r\nfunction keysAreValid(keys: Record<string, any> | null): boolean {\r\n\treturn Boolean(keys && keys.clientId && keys.clientSecret && keys.tenantId);\r\n}\r\n\r\n/**\r\n * Loads SPN Azure Key Vault credentials from keys file.\r\n *\r\n * @param {string} env\r\n * @param {string[]} paths\r\n *\r\n * @returns {Record<string, string>} credentials\r\n */\r\nasync function loadKeysFile(\r\n\tenv: string,\r\n\tpaths: string[],\r\n): Promise<Record<string, string>> {\r\n\tlogger.debug(`searching keys at ${pc.yellow(paths.join(','))}`);\r\n\r\n\tconst readers = await Promise.all(paths.map((path) => readJson(path)));\r\n\r\n\tconst [keys] = readers.find(\r\n\t\t([candidate, wasFound]) => wasFound && keysAreValid(candidate[env]),\r\n\t) ?? [null];\r\n\r\n\treturn keys?.[env] ?? {};\r\n}\r\n\r\n/**\r\n * Loads credentials and initializes Azure Key Vault client.\r\n *\r\n * @param {Arguments<AzureKeyVaultCommandArguments>} argv command arguments\r\n *\r\n * @returns {*} {Promise<AzureKeyVault>}\r\n */\r\n// linear credential-resolution (env vars → args → keys file precedence);\r\n// complexity comes from `??` fallbacks, not tangled control flow\r\n// eslint-disable-next-line complexity\r\nasync function loadAzureKeyVaultClient(\r\n\t{\r\n\t\tenv,\r\n\t\tkeysFile,\r\n\t\tmock,\r\n\t\tpassword,\r\n\t\tprojectInfo,\r\n\t\tskipDnsCheck,\r\n\t\tspn,\r\n\t\ttenant,\r\n\t\tvaultUrl,\r\n\t}: Arguments<AzureKeyVaultCommandArguments>,\r\n\tconfigVaultUrl?: string,\r\n): Promise<AzureKeyVault> {\r\n\tlet url = process.env.AZURE_VAULT_URL ?? vaultUrl ?? configVaultUrl;\r\n\r\n\tconst config = {\r\n\t\tenv,\r\n\t\tgroup: process.env.AZURE_GROUP ?? projectInfo.name,\r\n\t\tproject: process.env.AZURE_PROJECT ?? projectInfo.project,\r\n\t};\r\n\r\n\tif (!config.project) {\r\n\t\tlogger.error(`no project info from ${pc.blue('package.json')} found`);\r\n\r\n\t\tprocess.exit(1);\r\n\t}\r\n\r\n\tconst credentials = {\r\n\t\tclientId: process.env.AZURE_CLIENT_ID ?? spn,\r\n\t\ttenantId: process.env.AZURE_TENANT_ID ?? tenant,\r\n\t\tclientSecret: process.env.AZURE_CLIENT_SECRET ?? password,\r\n\t};\r\n\r\n\tif (keysFile && keysFile.length > 0 && !keysAreValid(credentials)) {\r\n\t\tconst keys = await loadKeysFile(env, keysFile);\r\n\r\n\t\turl ??= keys.vaultUrl;\r\n\t\tcredentials.clientId ??= keys.clientId;\r\n\t\tcredentials.clientSecret ??= keys.clientSecret;\r\n\t\tcredentials.tenantId ??= keys.tenantId;\r\n\t}\r\n\r\n\tlogger.debug(\r\n\t\t`credentials loaded for project ${pc.bold(\r\n\t\t\tpc.underline(pc.yellowBright(config.project)),\r\n\t\t)} and group ${pc.bold(pc.underline(pc.yellowBright(config.group)))}`,\r\n\t);\r\n\r\n\tlogger.debug(`connected to ${pc.bold(pc.underline(pc.greenBright(url)))}`);\r\n\r\n\tconst { AzureKeyVault, createAzureKeyVaultMock } =\r\n\t\tawait import('@achs/azure-key-vault');\r\n\r\n\tif (mock) return createAzureKeyVaultMock(config);\r\n\r\n\tconst akv = new AzureKeyVault(url, config, credentials);\r\n\r\n\tif (!skipDnsCheck) await verifyVaultReachable(url);\r\n\r\n\treturn akv;\r\n}\r\n\r\n/**\r\n * Best-effort DNS reachability check for the vault host. Resolves the hostname\r\n * via public resolvers; logs and exits on failure. Does NOT issue a\r\n * guaranteed-failing secret call (the old `getSecret('')` probe).\r\n */\r\nfunction verifyVaultReachable(url: string): Promise<void> {\r\n\tconst resolver = new dns.Resolver();\r\n\r\n\tresolver.setServers(['8.8.8.8', '1.1.1.1']);\r\n\r\n\treturn new Promise<void>((resolve) => {\r\n\t\tresolver.resolve4(new URL(url).hostname, (error) => {\r\n\t\t\tif (error) {\r\n\t\t\t\tlogger.error(`cannot resolve vault host ${pc.red(url)}`, error);\r\n\t\t\t\tprocess.exit(1);\r\n\t\t\t}\r\n\r\n\t\t\tlogger.debug(`vault host reachable ${pc.green(url)}`);\r\n\t\t\tresolve();\r\n\t\t});\r\n\t});\r\n}\r\n"],"mappings":"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"}
|
package/utils/command.util.d.ts
CHANGED
|
@@ -65,7 +65,7 @@ export declare function flatResults(results: EnvProviderResult[], nestingDelimit
|
|
|
65
65
|
*
|
|
66
66
|
* @returns {EnvProviderResult[]}
|
|
67
67
|
*/
|
|
68
|
-
export declare function flatAndValidateResults(results: EnvProviderResult[], argv: Partial<Arguments<EnvCommandArguments>>): EnvProviderResult[]
|
|
68
|
+
export declare function flatAndValidateResults(results: EnvProviderResult[], argv: Partial<Arguments<EnvCommandArguments>>): Promise<EnvProviderResult[]>;
|
|
69
69
|
/**
|
|
70
70
|
* Creates or updates JSON schema from
|
|
71
71
|
* environment variables grouped by provider key.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.util.d.ts","sourceRoot":"","sources":["../../src/utils/command.util.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,KAAK,EACX,iBAAiB,EACjB,iBAAiB,EACjB,MAAM,wBAAwB,CAAC;AAWhC;;;;;GAKG;AACH,wBAAsB,cAAc,CACnC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1B,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,YAkB5E;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CACnC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAS9C;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,YAAY,SAAK,GACf,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAiBlC;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACzC,SAAS,EAAE,iBAAiB,EAAE,EAC9B,IAAI,EAAE,OAAO,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC,GAC3C,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAY9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAC1B,OAAO,EAAE,iBAAiB,EAAE,EAC5B,gBAAgB,SAAO,GACrB,iBAAiB,EAAE,GAAG,KAAK,CAO7B;AAED;;;;;;;;;GASG;AACH,
|
|
1
|
+
{"version":3,"file":"command.util.d.ts","sourceRoot":"","sources":["../../src/utils/command.util.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,KAAK,EACX,iBAAiB,EACjB,iBAAiB,EACjB,MAAM,wBAAwB,CAAC;AAWhC;;;;;GAKG;AACH,wBAAsB,cAAc,CACnC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1B,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,YAkB5E;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CACnC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAS9C;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,YAAY,SAAK,GACf,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAiBlC;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACzC,SAAS,EAAE,iBAAiB,EAAE,EAC9B,IAAI,EAAE,OAAO,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC,GAC3C,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAY9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAC1B,OAAO,EAAE,iBAAiB,EAAE,EAC5B,gBAAgB,SAAO,GACrB,iBAAiB,EAAE,GAAG,KAAK,CAO7B;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAC3C,OAAO,EAAE,iBAAiB,EAAE,EAC5B,IAAI,EAAE,OAAO,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC,GAC3C,OAAO,CAAC,iBAAiB,EAAE,CAAC,CA6B9B;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACvC,GAAG,EAAE,iBAAiB,EAAE,EACxB,IAAI,EAAE,SAAS,CAAC,gBAAgB,CAAC,GAC/B,OAAO,CAAC,MAAM,CAAC,CAqBjB"}
|
package/utils/command.util.js
CHANGED
|
@@ -43,9 +43,9 @@ function h(e, t) {
|
|
|
43
43
|
function g(e, t = "__") {
|
|
44
44
|
return e.flatMap(({ value: e }) => Array.isArray(e) ? c({}, ...e.map((e) => i(e, t))) : i(e, t));
|
|
45
45
|
}
|
|
46
|
-
function _(e, t) {
|
|
46
|
+
async function _(e, t) {
|
|
47
47
|
if (!t.schemaValidate) return g(e, t.nestingDelimiter);
|
|
48
|
-
let n = a(t.schema, t.detectFormat);
|
|
48
|
+
let n = await a(t.schema, t.detectFormat);
|
|
49
49
|
return e.flatMap(({ key: e, value: a }) => {
|
|
50
50
|
let o = a;
|
|
51
51
|
Array.isArray(a) ? (a = c({}, ...a), o = c({}, ...o.map((e) => i(e, t.nestingDelimiter)))) : o = i(a, t.nestingDelimiter);
|
|
@@ -56,7 +56,7 @@ function _(e, t) {
|
|
|
56
56
|
}
|
|
57
57
|
async function v(e, t) {
|
|
58
58
|
let { detectFormat: r, nullable: i, resolve: a, schemaFile: s } = t, l = {};
|
|
59
|
-
for (let { key: t, value: n } of e) l[t] = o(Array.isArray(n) ? c({}, ...n) : n, {
|
|
59
|
+
for (let { key: t, value: n } of e) l[t] = await o(Array.isArray(n) ? c({}, ...n) : n, {
|
|
60
60
|
nullable: i,
|
|
61
61
|
strings: { detectFormat: r }
|
|
62
62
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.util.js","names":[],"sources":["../../src/utils/command.util.ts"],"sourcesContent":["import merge from 'merge-deep';\r\nimport { readFileSync } from 'node:fs';\r\nimport Path from 'node:path';\r\nimport pc from 'picocolors';\r\nimport type { Arguments } from 'yargs';\r\nimport type { CommandArguments } from '../arguments.js';\r\nimport type { EnvCommandArguments } from '../commands/env.command.js';\r\nimport type {\r\n\tEnvProviderConfig,\r\n\tEnvProviderResult,\r\n} from '../interfaces/index.js';\r\nimport {\r\n\tcreateValidators,\r\n\tflatten,\r\n\tinterpolate,\r\n\tlogger,\r\n\treadJson,\r\n\tschemaFrom,\r\n\twriteJson,\r\n} from './index.js';\r\n\r\n/**\r\n * Injects config to command arguments from file.\r\n *\r\n * @param {Record<string, unknown>} argv\r\n * @param {[string, string]} delimiters\r\n */\r\nexport async function loadConfigFile(\r\n\targv: Record<string, unknown>,\r\n\tdelimiters: [string, string],\r\n): Promise<void> {\r\n\tif (typeof argv.configFile === 'string') {\r\n\t\tconst path = interpolate(argv.configFile, argv, delimiters);\r\n\t\tconst [config, success] = await readJson(path);\r\n\r\n\t\tif (success) for (const key in config) argv[key] ??= config[key];\r\n\t}\r\n}\r\n\r\n/**\r\n * Extracts subcommand from command line parameters.\r\n *\r\n * @export\r\n * @param {string[]} rawArgv process.argv.slice(2)\r\n * @param {[string, string]} delimiters\r\n *\r\n * @returns {string[]} subcommand for wrap if exists\r\n */\r\nexport function getSubcommand(rawArgv: string[], delimiters: [string, string]) {\r\n\tlet subcommand: string[] = [];\r\n\r\n\t// subcommand delimiter indexes\r\n\r\n\tconst begin = rawArgv.indexOf(delimiters[0]);\r\n\r\n\tconst count = rawArgv.lastIndexOf(delimiters[1]) - begin;\r\n\r\n\t// calculates subcommand surrounded by delimiters\r\n\tif (begin > 0) {\r\n\t\tsubcommand =\r\n\t\t\tcount > 0\r\n\t\t\t\t? rawArgv.splice(begin, count + 1).slice(1, -1)\r\n\t\t\t\t: rawArgv.splice(begin).slice(1);\r\n\t}\r\n\r\n\treturn subcommand;\r\n}\r\n\r\n/**\r\n * Loads providers JSON schema from file.\r\n *\r\n * @param {Record<string, unknown>} argv\r\n * @param {[string, string]} delimiters\r\n *\r\n * @returns {Promise<Record<string, unknown>>}\r\n */\r\nexport async function loadSchemaFile(\r\n\targv: Record<string, unknown>,\r\n\tdelimiters: [string, string],\r\n): Promise<Record<string, unknown> | undefined> {\r\n\tif (typeof argv.schemaFile === 'string') {\r\n\t\tconst path = interpolate(argv.schemaFile, argv, delimiters);\r\n\t\tconst [schema, success] = await readJson(path);\r\n\r\n\t\treturn success ? schema : undefined;\r\n\t}\r\n\r\n\treturn undefined;\r\n}\r\n\r\n/**\r\n * Reads project package.json.\r\n *\r\n * @export\r\n * @returns {Promise<Record<string, unknown>> | never}\r\n */\r\nexport function loadProjectInfo(\r\n\trelativePath = '',\r\n): Promise<Record<string, unknown>> {\r\n\ttry {\r\n\t\tconst filePath = Path.join(process.cwd(), relativePath, 'package.json');\r\n\r\n\t\treturn Promise.resolve(\r\n\t\t\tJSON.parse(readFileSync(filePath, 'utf8')) as Record<\r\n\t\t\t\tstring,\r\n\t\t\t\tunknown\r\n\t\t\t>,\r\n\t\t);\r\n\t} catch {\r\n\t\tlogger.warn(\r\n\t\t\t`project file ${pc.underline(pc.yellow('package.json'))} not found`,\r\n\t\t);\r\n\r\n\t\treturn Promise.resolve({});\r\n\t}\r\n}\r\n\r\n/**\r\n * Executes load functions from provider handlers.\r\n *\r\n * @param {EnvProviderConfig[]} providers\r\n * @param {Partial<Arguments<EnvCommandArguments>>} argv\r\n *\r\n * @returns {EnvProviderResult[]}\r\n */\r\nexport function loadVariablesFromProviders(\r\n\tproviders: EnvProviderConfig[],\r\n\targv: Partial<Arguments<EnvCommandArguments>>,\r\n): Promise<EnvProviderResult[]> {\r\n\tif (!providers) return Promise.resolve([]) as Promise<EnvProviderResult[]>;\r\n\r\n\treturn Promise.all(\r\n\t\tproviders.map(async ({ config, handler: { key, load } }) => {\r\n\t\t\tlogger.silly(`executing ${pc.yellow(key)} provider`);\r\n\r\n\t\t\tconst value = await load(argv, config);\r\n\r\n\t\t\treturn { config, key, value };\r\n\t\t}),\r\n\t);\r\n}\r\n\r\n/**\r\n * Flattern environment provider results.\r\n *\r\n * @param {EnvProviderResult[]} results\r\n * @param {Partial<Arguments<EnvCommandArguments>>} argv\r\n *\r\n * @throws {Error} on schema validation failed\r\n *\r\n * @returns {EnvProviderResult[]} flatten results\r\n */\r\nexport function flatResults(\r\n\tresults: EnvProviderResult[],\r\n\tnestingDelimiter = '__',\r\n): EnvProviderResult[] | never {\r\n\treturn results.flatMap(({ value }) => {\r\n\t\tif (Array.isArray(value))\r\n\t\t\treturn merge({}, ...value.map((v) => flatten(v, nestingDelimiter)));\r\n\r\n\t\treturn flatten(value, nestingDelimiter);\r\n\t});\r\n}\r\n\r\n/**\r\n * Flattern and validates environment provider results.\r\n *\r\n * @param {EnvProviderResult[]} results\r\n * @param {Partial<Arguments<EnvCommandArguments>>} argv\r\n *\r\n * @throws {Error} on schema validation failed\r\n *\r\n * @returns {EnvProviderResult[]}\r\n */\r\nexport function flatAndValidateResults(\r\n\tresults: EnvProviderResult[],\r\n\targv: Partial<Arguments<EnvCommandArguments>>,\r\n): EnvProviderResult[]
|
|
1
|
+
{"version":3,"file":"command.util.js","names":[],"sources":["../../src/utils/command.util.ts"],"sourcesContent":["import merge from 'merge-deep';\r\nimport { readFileSync } from 'node:fs';\r\nimport Path from 'node:path';\r\nimport pc from 'picocolors';\r\nimport type { Arguments } from 'yargs';\r\nimport type { CommandArguments } from '../arguments.js';\r\nimport type { EnvCommandArguments } from '../commands/env.command.js';\r\nimport type {\r\n\tEnvProviderConfig,\r\n\tEnvProviderResult,\r\n} from '../interfaces/index.js';\r\nimport {\r\n\tcreateValidators,\r\n\tflatten,\r\n\tinterpolate,\r\n\tlogger,\r\n\treadJson,\r\n\tschemaFrom,\r\n\twriteJson,\r\n} from './index.js';\r\n\r\n/**\r\n * Injects config to command arguments from file.\r\n *\r\n * @param {Record<string, unknown>} argv\r\n * @param {[string, string]} delimiters\r\n */\r\nexport async function loadConfigFile(\r\n\targv: Record<string, unknown>,\r\n\tdelimiters: [string, string],\r\n): Promise<void> {\r\n\tif (typeof argv.configFile === 'string') {\r\n\t\tconst path = interpolate(argv.configFile, argv, delimiters);\r\n\t\tconst [config, success] = await readJson(path);\r\n\r\n\t\tif (success) for (const key in config) argv[key] ??= config[key];\r\n\t}\r\n}\r\n\r\n/**\r\n * Extracts subcommand from command line parameters.\r\n *\r\n * @export\r\n * @param {string[]} rawArgv process.argv.slice(2)\r\n * @param {[string, string]} delimiters\r\n *\r\n * @returns {string[]} subcommand for wrap if exists\r\n */\r\nexport function getSubcommand(rawArgv: string[], delimiters: [string, string]) {\r\n\tlet subcommand: string[] = [];\r\n\r\n\t// subcommand delimiter indexes\r\n\r\n\tconst begin = rawArgv.indexOf(delimiters[0]);\r\n\r\n\tconst count = rawArgv.lastIndexOf(delimiters[1]) - begin;\r\n\r\n\t// calculates subcommand surrounded by delimiters\r\n\tif (begin > 0) {\r\n\t\tsubcommand =\r\n\t\t\tcount > 0\r\n\t\t\t\t? rawArgv.splice(begin, count + 1).slice(1, -1)\r\n\t\t\t\t: rawArgv.splice(begin).slice(1);\r\n\t}\r\n\r\n\treturn subcommand;\r\n}\r\n\r\n/**\r\n * Loads providers JSON schema from file.\r\n *\r\n * @param {Record<string, unknown>} argv\r\n * @param {[string, string]} delimiters\r\n *\r\n * @returns {Promise<Record<string, unknown>>}\r\n */\r\nexport async function loadSchemaFile(\r\n\targv: Record<string, unknown>,\r\n\tdelimiters: [string, string],\r\n): Promise<Record<string, unknown> | undefined> {\r\n\tif (typeof argv.schemaFile === 'string') {\r\n\t\tconst path = interpolate(argv.schemaFile, argv, delimiters);\r\n\t\tconst [schema, success] = await readJson(path);\r\n\r\n\t\treturn success ? schema : undefined;\r\n\t}\r\n\r\n\treturn undefined;\r\n}\r\n\r\n/**\r\n * Reads project package.json.\r\n *\r\n * @export\r\n * @returns {Promise<Record<string, unknown>> | never}\r\n */\r\nexport function loadProjectInfo(\r\n\trelativePath = '',\r\n): Promise<Record<string, unknown>> {\r\n\ttry {\r\n\t\tconst filePath = Path.join(process.cwd(), relativePath, 'package.json');\r\n\r\n\t\treturn Promise.resolve(\r\n\t\t\tJSON.parse(readFileSync(filePath, 'utf8')) as Record<\r\n\t\t\t\tstring,\r\n\t\t\t\tunknown\r\n\t\t\t>,\r\n\t\t);\r\n\t} catch {\r\n\t\tlogger.warn(\r\n\t\t\t`project file ${pc.underline(pc.yellow('package.json'))} not found`,\r\n\t\t);\r\n\r\n\t\treturn Promise.resolve({});\r\n\t}\r\n}\r\n\r\n/**\r\n * Executes load functions from provider handlers.\r\n *\r\n * @param {EnvProviderConfig[]} providers\r\n * @param {Partial<Arguments<EnvCommandArguments>>} argv\r\n *\r\n * @returns {EnvProviderResult[]}\r\n */\r\nexport function loadVariablesFromProviders(\r\n\tproviders: EnvProviderConfig[],\r\n\targv: Partial<Arguments<EnvCommandArguments>>,\r\n): Promise<EnvProviderResult[]> {\r\n\tif (!providers) return Promise.resolve([]) as Promise<EnvProviderResult[]>;\r\n\r\n\treturn Promise.all(\r\n\t\tproviders.map(async ({ config, handler: { key, load } }) => {\r\n\t\t\tlogger.silly(`executing ${pc.yellow(key)} provider`);\r\n\r\n\t\t\tconst value = await load(argv, config);\r\n\r\n\t\t\treturn { config, key, value };\r\n\t\t}),\r\n\t);\r\n}\r\n\r\n/**\r\n * Flattern environment provider results.\r\n *\r\n * @param {EnvProviderResult[]} results\r\n * @param {Partial<Arguments<EnvCommandArguments>>} argv\r\n *\r\n * @throws {Error} on schema validation failed\r\n *\r\n * @returns {EnvProviderResult[]} flatten results\r\n */\r\nexport function flatResults(\r\n\tresults: EnvProviderResult[],\r\n\tnestingDelimiter = '__',\r\n): EnvProviderResult[] | never {\r\n\treturn results.flatMap(({ value }) => {\r\n\t\tif (Array.isArray(value))\r\n\t\t\treturn merge({}, ...value.map((v) => flatten(v, nestingDelimiter)));\r\n\r\n\t\treturn flatten(value, nestingDelimiter);\r\n\t});\r\n}\r\n\r\n/**\r\n * Flattern and validates environment provider results.\r\n *\r\n * @param {EnvProviderResult[]} results\r\n * @param {Partial<Arguments<EnvCommandArguments>>} argv\r\n *\r\n * @throws {Error} on schema validation failed\r\n *\r\n * @returns {EnvProviderResult[]}\r\n */\r\nexport async function flatAndValidateResults(\r\n\tresults: EnvProviderResult[],\r\n\targv: Partial<Arguments<EnvCommandArguments>>,\r\n): Promise<EnvProviderResult[]> {\r\n\tif (!argv.schemaValidate)\r\n\t\treturn flatResults(results, argv.nestingDelimiter);\r\n\r\n\tconst validators = await createValidators(argv.schema!, argv.detectFormat);\r\n\r\n\treturn results.flatMap(({ key, value }) => {\r\n\t\tlet baseValue = value;\r\n\t\tif (Array.isArray(value)) {\r\n\t\t\tvalue = merge({}, ...value);\r\n\t\t\tbaseValue = merge(\r\n\t\t\t\t{},\r\n\t\t\t\t...baseValue.map((v: any) => flatten(v, argv.nestingDelimiter)),\r\n\t\t\t);\r\n\t\t} else {\r\n\t\t\tbaseValue = flatten(value, argv.nestingDelimiter);\r\n\t\t}\r\n\r\n\t\tconst validator = validators![key];\r\n\r\n\t\tif (!validator || validator?.(value)) return baseValue;\r\n\r\n\t\tlogger.error(\r\n\t\t\t`schema validation failed for ${pc.yellow(key)}`,\r\n\t\t\tvalidator?.errors,\r\n\t\t);\r\n\r\n\t\tthrow new Error(`schema validation failed for ${key}`);\r\n\t});\r\n}\r\n\r\n/**\r\n * Creates or updates JSON schema from\r\n * environment variables grouped by provider key.\r\n *\r\n * @export\r\n * @param {EnvProviderResult[]} env\r\n * @param {Arguments<EnvCommandArguments>} argv\r\n *\r\n * @returns {Promise<object>} JSON schema grouped by provider key.\r\n */\r\nexport async function generateSchemaFrom(\r\n\tenv: EnvProviderResult[],\r\n\targv: Arguments<CommandArguments>,\r\n): Promise<object> {\r\n\tconst { detectFormat, nullable, resolve, schemaFile } = argv;\r\n\r\n\t// generates schemas from providers results\r\n\tconst schemaEntries: Record<string, unknown> = {};\r\n\tfor (const { key, value } of env) {\r\n\t\tconst merged = Array.isArray(value) ? merge({}, ...value) : value;\r\n\r\n\t\tschemaEntries[key] = await schemaFrom(merged, {\r\n\t\t\tnullable,\r\n\t\t\tstrings: { detectFormat },\r\n\t\t});\r\n\t}\r\n\r\n\tlet schema: Record<string, unknown> = schemaEntries;\r\n\r\n\tif (resolve === 'merge') schema = merge(argv.schema, schema);\r\n\r\n\tawait writeJson(schemaFile, schema, true);\r\n\r\n\treturn schema;\r\n}\r\n"],"mappings":"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"}
|
package/utils/index.d.ts
CHANGED
package/utils/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,SAAS,CAAC"}
|
package/utils/index.js
CHANGED
|
@@ -5,4 +5,5 @@ import { readJson as f, resolvePath as p, writeEnvFromJson as m, writeJson as h
|
|
|
5
5
|
import { LOG_LEVELS as g, configureLogger as _, createLogger as v, logger as y } from "./logger.js";
|
|
6
6
|
import { flatten as b, normalize as x } from "./normalize.util.js";
|
|
7
7
|
import { createValidator as S, createValidators as C, flatSchema as w, isJsonSchemaObject as T, schemaFrom as E, schemaToJson as D } from "./schema.util.js";
|
|
8
|
-
|
|
8
|
+
import { formatDuration as O, ui as k } from "./ui.js";
|
|
9
|
+
export { g as LOG_LEVELS, _ as configureLogger, v as createLogger, S as createValidator, C as createValidators, t as flatAndValidateResults, n as flatResults, w as flatSchema, b as flatten, O as formatDuration, r as generateSchemaFrom, i as getSubcommand, l as interpolate, u as interpolateJson, T as isJsonSchemaObject, d as isRecord, a as loadConfigFile, o as loadProjectInfo, s as loadSchemaFile, c as loadVariablesFromProviders, y as logger, x as normalize, e as normalizeRawArgv, f as readJson, p as resolvePath, E as schemaFrom, D as schemaToJson, k as ui, m as writeEnvFromJson, h as writeJson };
|
package/utils/json.util.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json.util.d.ts","sourceRoot":"","sources":["../../src/utils/json.util.ts"],"names":[],"mappings":"AAgBA;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMpD;AAED;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACrD,IAAI,EAAE,MAAM,GACV,OAAO,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"json.util.d.ts","sourceRoot":"","sources":["../../src/utils/json.util.ts"],"names":[],"mappings":"AAgBA;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMpD;AAED;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACrD,IAAI,EAAE,MAAM,GACV,OAAO,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC,CAS7C;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,SAAS,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,SAAS,UAAQ,EACjB,eAAe,UAAQ,GACrB,OAAO,CAAC,OAAO,CAAC,CAgBlB;AAED;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,SAAS,UAAQ,EACjB,MAAM,UAAQ,GACZ,OAAO,CAAC,OAAO,CAAC,CAiBlB"}
|
package/utils/json.util.js
CHANGED
|
@@ -8,8 +8,13 @@ function o(e) {
|
|
|
8
8
|
let n = i.homedir();
|
|
9
9
|
return n && (e = e.replace(/^~($|\/|\\)/, `${n}$1`)), t.resolve(process.cwd(), e);
|
|
10
10
|
}
|
|
11
|
-
async function s(
|
|
12
|
-
|
|
11
|
+
async function s(e) {
|
|
12
|
+
try {
|
|
13
|
+
return [JSON.parse(await n(e, "utf8")), !0];
|
|
14
|
+
} catch (e) {
|
|
15
|
+
if (e.code === "ENOENT") return [{}, !1];
|
|
16
|
+
throw e;
|
|
17
|
+
}
|
|
13
18
|
}
|
|
14
19
|
async function c(t, n, i = !1, o = !1) {
|
|
15
20
|
return e(t) && !i ? !1 : (await r(t, `${JSON.stringify(n, o ? a : void 0, 4)}\n`, "utf8"), !0);
|
package/utils/json.util.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json.util.js","names":[],"sources":["../../src/utils/json.util.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\r\nimport { readFile, writeFile } from 'node:fs/promises';\r\nimport os from 'node:os';\r\nimport path from 'node:path';\r\n\r\n/**\r\n * Replaces undefined by null in JSON.stringify()\r\n *\r\n * @param {string} _ property key\r\n * @param {any} value property value\r\n *\r\n * @returns {any} value\r\n */\r\nconst replacer = (_: string, value: any): any =>\r\n\tvalue === undefined ? null : value;\r\n\r\n/**\r\n * Resolve a relative path for os.\r\n *\r\n * @export\r\n * @param {string} filePath relative path from project root\r\n *\r\n * @returns {string} path\r\n */\r\nexport function resolvePath(filePath: string): string {\r\n\tconst home = os.homedir();\r\n\r\n\tif (home) filePath = filePath.replace(/^~($|\\/|\\\\)/, `${home}$1`);\r\n\r\n\treturn path.resolve(process.cwd(), filePath);\r\n}\r\n\r\n/**\r\n * Reads and parses a JSON file.\r\n *\r\n * @export\r\n * @template T\r\n * @param {string} path\r\n *\r\n * @returns {Promise<[Record<string, any>, boolean]>}\r\n */\r\nexport async function readJson<T = Record<string, any>>(\r\n\tpath: string,\r\n): Promise<[Record<string, any> | T, boolean]> {\r\n\
|
|
1
|
+
{"version":3,"file":"json.util.js","names":[],"sources":["../../src/utils/json.util.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\r\nimport { readFile, writeFile } from 'node:fs/promises';\r\nimport os from 'node:os';\r\nimport path from 'node:path';\r\n\r\n/**\r\n * Replaces undefined by null in JSON.stringify()\r\n *\r\n * @param {string} _ property key\r\n * @param {any} value property value\r\n *\r\n * @returns {any} value\r\n */\r\nconst replacer = (_: string, value: any): any =>\r\n\tvalue === undefined ? null : value;\r\n\r\n/**\r\n * Resolve a relative path for os.\r\n *\r\n * @export\r\n * @param {string} filePath relative path from project root\r\n *\r\n * @returns {string} path\r\n */\r\nexport function resolvePath(filePath: string): string {\r\n\tconst home = os.homedir();\r\n\r\n\tif (home) filePath = filePath.replace(/^~($|\\/|\\\\)/, `${home}$1`);\r\n\r\n\treturn path.resolve(process.cwd(), filePath);\r\n}\r\n\r\n/**\r\n * Reads and parses a JSON file.\r\n *\r\n * @export\r\n * @template T\r\n * @param {string} path\r\n *\r\n * @returns {Promise<[Record<string, any>, boolean]>}\r\n */\r\nexport async function readJson<T = Record<string, any>>(\r\n\tpath: string,\r\n): Promise<[Record<string, any> | T, boolean]> {\r\n\ttry {\r\n\t\treturn [JSON.parse(await readFile(path, 'utf8')), true];\r\n\t} catch (error) {\r\n\t\tif ((error as NodeJS.ErrnoException).code === 'ENOENT')\r\n\t\t\treturn [{}, false];\r\n\r\n\t\tthrow error; // invalid JSON or other IO error must still surface\r\n\t}\r\n}\r\n\r\n/**\r\n * Saves a JSON into a file.\r\n *\r\n * @export\r\n * @param {string} path\r\n * @param {unknown} content\r\n * @param {false} overwrite\r\n * @param {false} undefinedAsNull replaces undefined by null\r\n *\r\n * @returns {Promise<boolean>}\r\n */\r\nexport async function writeJson(\r\n\tpath: string,\r\n\tcontent: Record<string, unknown>,\r\n\toverwrite = false,\r\n\tundefinedAsNull = false,\r\n): Promise<boolean> {\r\n\tconst exists = existsSync(path);\r\n\r\n\tif (exists && !overwrite) return false;\r\n\r\n\tawait writeFile(\r\n\t\tpath,\r\n\t\t`${JSON.stringify(\r\n\t\t\tcontent,\r\n\t\t\tundefinedAsNull ? replacer : undefined,\r\n\t\t\t4,\r\n\t\t)}\\n`,\r\n\t\t'utf8',\r\n\t);\r\n\r\n\treturn true;\r\n}\r\n\r\n/**\r\n * Saves a JSON into a file as dotenv.\r\n *\r\n * @export\r\n * @param {string} path\r\n * @param {unknown} content\r\n * @param {false} overwrite\r\n *\r\n * @returns {Promise<boolean>}\r\n */\r\nexport async function writeEnvFromJson(\r\n\tpath: string,\r\n\tcontent: Record<string, unknown>,\r\n\toverwrite = false,\r\n\tquotes = false,\r\n): Promise<boolean> {\r\n\tconst exists = existsSync(path);\r\n\r\n\tif (exists && !overwrite) return false;\r\n\r\n\tlet data = '';\r\n\r\n\tfor (const key in content) {\r\n\t\tlet value = content[key];\r\n\t\tif (quotes) value = `\"${value}\"`;\r\n\r\n\t\tdata += `${key}=${value}\\n`;\r\n\t}\r\n\r\n\tawait writeFile(path, data, 'utf8');\r\n\r\n\treturn true;\r\n}\r\n"],"mappings":";;;;;AAaA,IAAM,KAAY,GAAW,MAC5B,MAAU,KAAA,IAAY,OAAO;AAU9B,SAAgB,EAAY,GAA0B;CACrD,IAAM,IAAO,EAAG,QAAQ;CAIxB,OAFI,MAAM,IAAW,EAAS,QAAQ,eAAe,GAAG,EAAK,GAAG,IAEzD,EAAK,QAAQ,QAAQ,IAAI,GAAG,CAAQ;AAC5C;AAWA,eAAsB,EACrB,GAC8C;CAC9C,IAAI;EACH,OAAO,CAAC,KAAK,MAAM,MAAM,EAAS,GAAM,MAAM,CAAC,GAAG,EAAI;CACvD,SAAS,GAAO;EACf,IAAK,EAAgC,SAAS,UAC7C,OAAO,CAAC,CAAC,GAAG,EAAK;EAElB,MAAM;CACP;AACD;AAaA,eAAsB,EACrB,GACA,GACA,IAAY,IACZ,IAAkB,IACC;CAenB,OAde,EAAW,CAEtB,KAAU,CAAC,IAAkB,MAEjC,MAAM,EACL,GACA,GAAG,KAAK,UACP,GACA,IAAkB,IAAW,KAAA,GAC7B,CACD,EAAE,KACF,MACD,GAEO;AACR;AAYA,eAAsB,EACrB,GACA,GACA,IAAY,IACZ,IAAS,IACU;CAGnB,IAFe,EAAW,CAEtB,KAAU,CAAC,GAAW,OAAO;CAEjC,IAAI,IAAO;CAEX,KAAK,IAAM,KAAO,GAAS;EAC1B,IAAI,IAAQ,EAAQ;EAGpB,AAFI,MAAQ,IAAQ,IAAI,EAAM,KAE9B,KAAQ,GAAG,EAAI,GAAG,EAAM;CACzB;CAIA,OAFA,MAAM,EAAU,GAAM,GAAM,MAAM,GAE3B;AACR"}
|
package/utils/schema.util.d.ts
CHANGED
|
@@ -7,11 +7,11 @@ import { Options } from 'to-json-schema';
|
|
|
7
7
|
* @param {Record<string, unknown>} json json object
|
|
8
8
|
* @param {Options} [options]
|
|
9
9
|
*
|
|
10
|
-
* @returns {*} {Record<string, unknown
|
|
10
|
+
* @returns {*} {Promise<Record<string, unknown>>}
|
|
11
11
|
*/
|
|
12
12
|
export declare function schemaFrom(json: Record<string, unknown>, options?: Options & {
|
|
13
13
|
nullable?: boolean;
|
|
14
|
-
}): Record<string, unknown
|
|
14
|
+
}): Promise<Record<string, unknown>>;
|
|
15
15
|
/**
|
|
16
16
|
* Validates if a object is a JSON schema.
|
|
17
17
|
*
|
|
@@ -52,9 +52,9 @@ export declare function flatSchema(schema: Record<string, unknown>, parentKey?:
|
|
|
52
52
|
* @param {Record<string, object>} schema json schema by provider
|
|
53
53
|
* @param {boolean} enableFormats whether formats are enabled
|
|
54
54
|
*
|
|
55
|
-
* @returns {ValidateFunction} validators
|
|
55
|
+
* @returns {Promise<ValidateFunction>} validators
|
|
56
56
|
*/
|
|
57
|
-
export declare function createValidator(schema: Record<string, unknown>, enableFormats?: boolean): ValidateFunction
|
|
57
|
+
export declare function createValidator(schema: Record<string, unknown>, enableFormats?: boolean): Promise<ValidateFunction>;
|
|
58
58
|
/**
|
|
59
59
|
* Creates a JSON schema validator lookup using AJV.
|
|
60
60
|
*
|
|
@@ -64,7 +64,7 @@ export declare function createValidator(schema: Record<string, unknown>, enableF
|
|
|
64
64
|
* @param {Record<string, object>} schemaLookup json schema by provider
|
|
65
65
|
* @param {boolean} enableFormats whether formats are enabled
|
|
66
66
|
*
|
|
67
|
-
* @returns {Record<string, ValidateFunction
|
|
67
|
+
* @returns {Promise<Record<string, ValidateFunction>>} validators lookup
|
|
68
68
|
*/
|
|
69
|
-
export declare function createValidators(schemaLookup: Record<string, object>, enableFormats?: boolean): Record<string, ValidateFunction
|
|
69
|
+
export declare function createValidators(schemaLookup: Record<string, object>, enableFormats?: boolean): Promise<Record<string, ValidateFunction>>;
|
|
70
70
|
//# sourceMappingURL=schema.util.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.util.d.ts","sourceRoot":"","sources":["../../src/utils/schema.util.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"schema.util.d.ts","sourceRoot":"","sources":["../../src/utils/schema.util.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,cAAc,EAAE,gBAAgB,EAAE,MAAM,KAAK,CAAC;AACpE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAiC9C;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAC/B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,OAAO,GAAG;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GACxC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAyBlC;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,IAAI,cAAc,CAAC,MAAM,CAAC,CAOlC;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GACjC,OAAO,CAST;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,SAAS,SAAK,EACd,gBAAgB,SAAO,EACvB,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GACjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmBzB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACpC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,aAAa,UAAO,GAClB,OAAO,CAAC,gBAAgB,CAAC,CAI3B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CACrC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,aAAa,UAAO,GAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAS3C"}
|
package/utils/schema.util.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import { Ajv as e } from "ajv";
|
|
2
|
-
import t from "ajv-formats";
|
|
3
|
-
import n from "to-json-schema";
|
|
4
1
|
//#region src/utils/schema.util.ts
|
|
5
|
-
var r, i, a = {
|
|
2
|
+
var e, t, n, r, i, a = {
|
|
6
3
|
"ip-address": /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})$/,
|
|
7
4
|
color: /^(#?([\dA-Fa-f]{3}){1,2}\b|aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow|(rgb\(\s*\b(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\b\s*,\s*\b(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\b\s*,\s*\b(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\b\s*\))|(rgb\(\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*\)))$/,
|
|
8
5
|
hostname: /^(?=.{1,255}$)[\da-z](?:[\da-z-]{0,61}[\da-z])?(?:\.[\da-z](?:[\da-z-]{0,61}[\da-z])?)*\.?$/i,
|
|
@@ -12,8 +9,8 @@ var r, i, a = {
|
|
|
12
9
|
style: /\s*(.+?):\s*([^;]+);?/g,
|
|
13
10
|
phone: /^\+(?:\d ?){6,14}\d$/
|
|
14
11
|
};
|
|
15
|
-
function o(e, t) {
|
|
16
|
-
return
|
|
12
|
+
async function o(e, t) {
|
|
13
|
+
return i ||= (await import("to-json-schema")).default, i(e, {
|
|
17
14
|
required: !1,
|
|
18
15
|
...t,
|
|
19
16
|
postProcessFnc: (n, r, i, a) => (i !== e && (r.type = [n], r.nullable = t?.nullable ?? !1), i ?? (r.nullable = !0), a(n, r, i))
|
|
@@ -41,29 +38,33 @@ function l(e, t = "", n = "__", r = {}) {
|
|
|
41
38
|
return r;
|
|
42
39
|
} else return { [t]: e };
|
|
43
40
|
}
|
|
44
|
-
function u(e, t = !0) {
|
|
45
|
-
return f(t).compile(e);
|
|
41
|
+
async function u(e, t = !0) {
|
|
42
|
+
return (await f(t)).compile(e);
|
|
46
43
|
}
|
|
47
|
-
function d(e, t = !0) {
|
|
48
|
-
let n = f(t), r = {};
|
|
44
|
+
async function d(e, t = !0) {
|
|
45
|
+
let n = await f(t), r = {};
|
|
49
46
|
for (let t in e) r[t] = n.compile(e[t]);
|
|
50
47
|
return r;
|
|
51
48
|
}
|
|
52
|
-
function f(
|
|
53
|
-
if (n) {
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
async function f(i) {
|
|
50
|
+
if (!n) {
|
|
51
|
+
let [e, t] = await Promise.all([import("ajv"), import("ajv-formats")]);
|
|
52
|
+
n = e.Ajv, r = t.default;
|
|
53
|
+
}
|
|
54
|
+
if (i) {
|
|
55
|
+
if (!t) {
|
|
56
|
+
t = new n({
|
|
56
57
|
allErrors: !0,
|
|
57
58
|
allowUnionTypes: !0
|
|
58
|
-
}), t
|
|
59
|
-
for (let e in a)
|
|
59
|
+
}), r(t, { mode: "fast" });
|
|
60
|
+
for (let e in a) t.addFormat(e, a[e]);
|
|
60
61
|
}
|
|
61
|
-
return
|
|
62
|
+
return t;
|
|
62
63
|
}
|
|
63
|
-
return
|
|
64
|
+
return e ||= new n({
|
|
64
65
|
allErrors: !0,
|
|
65
66
|
allowUnionTypes: !0
|
|
66
|
-
}),
|
|
67
|
+
}), e;
|
|
67
68
|
}
|
|
68
69
|
//#endregion
|
|
69
70
|
export { u as createValidator, d as createValidators, l as flatSchema, s as isJsonSchemaObject, o as schemaFrom, c as schemaToJson };
|
package/utils/schema.util.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.util.js","names":[],"sources":["../../src/utils/schema.util.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"schema.util.js","names":[],"sources":["../../src/utils/schema.util.ts"],"sourcesContent":["import type { Format, JSONSchemaType, ValidateFunction } from 'ajv';\r\nimport type { Options } from 'to-json-schema';\r\n\r\n// cached AJV instances (with and without formats)\r\nlet _ajv: any;\r\nlet _ajvWithFormats: any;\r\n\r\n// cached module references\r\nlet _AjvClass: any;\r\nlet _addFormats: any;\r\nlet _toJsonSchema: any;\r\n\r\n/* eslint-disable regexp/no-useless-assertions, regexp/no-super-linear-backtracking -- domain validation regexes from JSON Schema spec; modifying these patterns would change format validation behavior */\r\nconst FORMAT_REGEXPS: Record<string, Format> = {\r\n\t'ip-address':\r\n\t\t/^(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d{1,2})\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d{1,2})$/,\r\n\r\n\tcolor: /^(#?([\\dA-Fa-f]{3}){1,2}\\b|aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow|(rgb\\(\\s*\\b(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\b\\s*,\\s*\\b(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\b\\s*,\\s*\\b(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\b\\s*\\))|(rgb\\(\\s*(\\d?\\d%|100%)+\\s*,\\s*(\\d?\\d%|100%)+\\s*,\\s*(\\d?\\d%|100%)+\\s*\\)))$/,\r\n\r\n\thostname:\r\n\t\t/^(?=.{1,255}$)[\\da-z](?:[\\da-z-]{0,61}[\\da-z])?(?:\\.[\\da-z](?:[\\da-z-]{0,61}[\\da-z])?)*\\.?$/i,\r\n\r\n\talphanumeric: /^[\\da-z]+$/i,\r\n\r\n\t'utc-millisec': (input: string) => !Number.isNaN(+input),\r\n\r\n\talpha: /^[a-z]+$/i,\r\n\r\n\tstyle: /\\s*(.+?):\\s*([^;]+);?/g,\r\n\r\n\tphone: /^\\+(?:\\d ?){6,14}\\d$/,\r\n};\r\n/* eslint-enable regexp/no-useless-assertions, regexp/no-super-linear-backtracking */\r\n\r\n/**\r\n * Generates JSON schema from JSON template/object.\r\n *\r\n * @export\r\n * @param {Record<string, unknown>} json json object\r\n * @param {Options} [options]\r\n *\r\n * @returns {*} {Promise<Record<string, unknown>>}\r\n */\r\nexport async function schemaFrom(\r\n\tjson: Record<string, unknown>,\r\n\toptions?: Options & { nullable?: boolean },\r\n): Promise<Record<string, unknown>> {\r\n\tif (!_toJsonSchema) {\r\n\t\tconst mod = await import('to-json-schema');\r\n\t\t_toJsonSchema = mod.default;\r\n\t}\r\n\r\n\treturn _toJsonSchema(json, {\r\n\t\trequired: false,\r\n\t\t...options,\r\n\t\tpostProcessFnc: (\r\n\t\t\ttype: string,\r\n\t\t\tschema: any,\r\n\t\t\tvalue: unknown,\r\n\t\t\tdefaultFunc: any,\r\n\t\t) => {\r\n\t\t\tif (value !== json) {\r\n\t\t\t\tschema.type = [type];\r\n\t\t\t\tschema.nullable = options?.nullable ?? false;\r\n\t\t\t}\r\n\r\n\t\t\tif (value === null || value === undefined) schema.nullable = true;\r\n\r\n\t\t\treturn defaultFunc(type, schema, value);\r\n\t\t},\r\n\t});\r\n}\r\n\r\n/**\r\n * Validates if a object is a JSON schema.\r\n *\r\n * @export\r\n * @param {Record<string, unknown>} schema\r\n *\r\n * @returns {boolean} if is a JSON schema\r\n */\r\nexport function isJsonSchemaObject(\r\n\tschema: Record<string, unknown>,\r\n): schema is JSONSchemaType<object> {\r\n\tif (schema.type === 'object') return true;\r\n\r\n\treturn (\r\n\t\tArray.isArray(schema.type) &&\r\n\t\t(schema.type as string[]).includes('object')\r\n\t);\r\n}\r\n\r\n/**\r\n * Converts a JSON schema to JSON template.\r\n *\r\n * @export\r\n * @param {Record<string, unknown>} schema JSON schema\r\n * @param {Record<string, any>} [container] template container\r\n *\r\n * @returns {unknown} object or default value\r\n */\r\nexport function schemaToJson(\r\n\tschema: Record<string, unknown>,\r\n\tcontainer: Record<string, any> = {},\r\n): unknown {\r\n\tif (isJsonSchemaObject(schema)) {\r\n\t\tfor (const key in schema.properties)\r\n\t\t\tcontainer[key] = schemaToJson(schema.properties[key]);\r\n\r\n\t\treturn container;\r\n\t} else {\r\n\t\treturn schema.default ?? (schema.nullable ? null : undefined);\r\n\t}\r\n}\r\n\r\n/**\r\n * Flatten a JSON schema.\r\n *\r\n * @export\r\n * @param {Record<string, unknown>} schema JSON schema\r\n * @param {string} [parentKey] previous level key\r\n * @param {string} [nestingDelimiter] char for delimit nesting levels\r\n * @param {Record<string, any>} [container] result container\r\n *\r\n * @returns {Record<string, unknown>} flattened schema\r\n */\r\nexport function flatSchema(\r\n\tschema: Record<string, unknown>,\r\n\tparentKey = '',\r\n\tnestingDelimiter = '__',\r\n\tcontainer: Record<string, any> = {},\r\n): Record<string, unknown> {\r\n\tif (isJsonSchemaObject(schema)) {\r\n\t\tfor (const key in schema.properties) {\r\n\t\t\tif (key[0] === '#') continue;\r\n\r\n\t\t\t// global property, but prefix removed for injection\r\n\t\t\tconst subKey =\r\n\t\t\t\tparentKey + (parentKey ? nestingDelimiter : '') + key;\r\n\r\n\t\t\tcontainer = {\r\n\t\t\t\t...container,\r\n\t\t\t\t...flatSchema(schema.properties[key], subKey, nestingDelimiter),\r\n\t\t\t};\r\n\t\t}\r\n\r\n\t\treturn container;\r\n\t} else {\r\n\t\treturn { [parentKey]: schema };\r\n\t}\r\n}\r\n\r\n/**\r\n * Creates a JSON schema validator using AJV.\r\n *\r\n * @see https://ajv.js.org/\r\n *\r\n * @export\r\n * @param {Record<string, object>} schema json schema by provider\r\n * @param {boolean} enableFormats whether formats are enabled\r\n *\r\n * @returns {Promise<ValidateFunction>} validators\r\n */\r\nexport async function createValidator(\r\n\tschema: Record<string, unknown>,\r\n\tenableFormats = true,\r\n): Promise<ValidateFunction> {\r\n\tconst ajv = await getAjv(enableFormats);\r\n\r\n\treturn ajv.compile(schema);\r\n}\r\n\r\n/**\r\n * Creates a JSON schema validator lookup using AJV.\r\n *\r\n * @see https://ajv.js.org/\r\n *\r\n * @export\r\n * @param {Record<string, object>} schemaLookup json schema by provider\r\n * @param {boolean} enableFormats whether formats are enabled\r\n *\r\n * @returns {Promise<Record<string, ValidateFunction>>} validators lookup\r\n */\r\nexport async function createValidators(\r\n\tschemaLookup: Record<string, object>,\r\n\tenableFormats = true,\r\n): Promise<Record<string, ValidateFunction>> {\r\n\tconst ajv = await getAjv(enableFormats);\r\n\r\n\tconst validators: Record<string, ValidateFunction> = {};\r\n\r\n\tfor (const key in schemaLookup)\r\n\t\tvalidators[key] = ajv.compile(schemaLookup[key]);\r\n\r\n\treturn validators;\r\n}\r\n\r\n/**\r\n * Returns a cached AJV instance (lazy-loads ajv and ajv-formats on first call).\r\n *\r\n * @param {boolean} withFormats whether to include format validators\r\n * @returns {Promise<any>} cached AJV instance\r\n */\r\nasync function getAjv(withFormats: boolean): Promise<any> {\r\n\tif (!_AjvClass) {\r\n\t\tconst [ajvMod, formatsMod] = await Promise.all([\r\n\t\t\timport('ajv'),\r\n\t\t\timport('ajv-formats'),\r\n\t\t]);\r\n\t\t_AjvClass = ajvMod.Ajv;\r\n\t\t_addFormats = formatsMod.default;\r\n\t}\r\n\r\n\tif (withFormats) {\r\n\t\tif (!_ajvWithFormats) {\r\n\t\t\t_ajvWithFormats = new _AjvClass({\r\n\t\t\t\tallErrors: true,\r\n\t\t\t\tallowUnionTypes: true,\r\n\t\t\t});\r\n\r\n\t\t\t(\r\n\t\t\t\t_addFormats as unknown as (\r\n\t\t\t\t\tajv: any,\r\n\t\t\t\t\toptions?: { mode?: string },\r\n\t\t\t\t) => void\r\n\t\t\t)(_ajvWithFormats, { mode: 'fast' });\r\n\r\n\t\t\tfor (const key in FORMAT_REGEXPS)\r\n\t\t\t\t_ajvWithFormats.addFormat(key, FORMAT_REGEXPS[key]);\r\n\t\t}\r\n\r\n\t\treturn _ajvWithFormats;\r\n\t}\r\n\r\n\tif (!_ajv) {\r\n\t\t_ajv = new _AjvClass({\r\n\t\t\tallErrors: true,\r\n\t\t\tallowUnionTypes: true,\r\n\t\t});\r\n\t}\r\n\r\n\treturn _ajv;\r\n}\r\n"],"mappings":";AAIA,IAAI,GACA,GAGA,GACA,GACA,GAGE,IAAyC;CAC9C,cACC;CAED,OAAO;CAEP,UACC;CAED,cAAc;CAEd,iBAAiB,MAAkB,CAAC,OAAO,MAAM,CAAC,CAAK;CAEvD,OAAO;CAEP,OAAO;CAEP,OAAO;AACR;AAYA,eAAsB,EACrB,GACA,GACmC;CAMnC,OALA,AAEC,OAAgB,MADE,OAAO,mBACL,SAGd,EAAc,GAAM;EAC1B,UAAU;EACV,GAAG;EACH,iBACC,GACA,GACA,GACA,OAEI,MAAU,MACb,EAAO,OAAO,CAAC,CAAI,GACnB,EAAO,WAAW,GAAS,YAAY,KAGpC,MAAuC,EAAO,WAAW,KAEtD,EAAY,GAAM,GAAQ,CAAK;CAExC,CAAC;AACF;AAUA,SAAgB,EACf,GACmC;CAGnC,OAFI,EAAO,SAAS,WAAiB,KAGpC,MAAM,QAAQ,EAAO,IAAI,KACxB,EAAO,KAAkB,SAAS,QAAQ;AAE7C;AAWA,SAAgB,EACf,GACA,IAAiC,CAAC,GACxB;CACV,IAAI,EAAmB,CAAM,GAAG;EAC/B,KAAK,IAAM,KAAO,EAAO,YACxB,EAAU,KAAO,EAAa,EAAO,WAAW,EAAI;EAErD,OAAO;CACR,OACC,OAAO,EAAO,YAAY,EAAO,WAAW,OAAO,KAAA;AAErD;AAaA,SAAgB,EACf,GACA,IAAY,IACZ,IAAmB,MACnB,IAAiC,CAAC,GACR;CAC1B,IAAI,EAAmB,CAAM,GAAG;EAC/B,KAAK,IAAM,KAAO,EAAO,YAAY;GACpC,IAAI,EAAI,OAAO,KAAK;GAGpB,IAAM,IACL,KAAa,IAAY,IAAmB,MAAM;GAEnD,IAAY;IACX,GAAG;IACH,GAAG,EAAW,EAAO,WAAW,IAAM,GAAQ,CAAgB;GAC/D;EACD;EAEA,OAAO;CACR,OACC,OAAO,GAAG,IAAY,EAAO;AAE/B;AAaA,eAAsB,EACrB,GACA,IAAgB,IACY;CAG5B,QAAO,MAFW,EAAO,CAAa,GAE3B,QAAQ,CAAM;AAC1B;AAaA,eAAsB,EACrB,GACA,IAAgB,IAC4B;CAC5C,IAAM,IAAM,MAAM,EAAO,CAAa,GAEhC,IAA+C,CAAC;CAEtD,KAAK,IAAM,KAAO,GACjB,EAAW,KAAO,EAAI,QAAQ,EAAa,EAAI;CAEhD,OAAO;AACR;AAQA,eAAe,EAAO,GAAoC;CACzD,IAAI,CAAC,GAAW;EACf,IAAM,CAAC,GAAQ,KAAc,MAAM,QAAQ,IAAI,CAC9C,OAAO,QACP,OAAO,cACR,CAAC;EAED,AADA,IAAY,EAAO,KACnB,IAAc,EAAW;CAC1B;CAEA,IAAI,GAAa;EAChB,IAAI,CAAC,GAAiB;GAMrB,AALA,IAAkB,IAAI,EAAU;IAC/B,WAAW;IACX,iBAAiB;GAClB,CAAC,GAED,EAKE,GAAiB,EAAE,MAAM,OAAO,CAAC;GAEnC,KAAK,IAAM,KAAO,GACjB,EAAgB,UAAU,GAAK,EAAe,EAAI;EACpD;EAEA,OAAO;CACR;CASA,OAPA,AACC,MAAO,IAAI,EAAU;EACpB,WAAW;EACX,iBAAiB;CAClB,CAAC,GAGK;AACR"}
|
package/utils/ui.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Formats a duration in ms as "142ms" or "1.4s". */
|
|
2
|
+
export declare function formatDuration(ms: number): string;
|
|
3
|
+
export declare const ui: {
|
|
4
|
+
header(version: string, env?: string, modes?: string[]): void;
|
|
5
|
+
provider(key: string, count: number): void;
|
|
6
|
+
summary(total: number, ms: number): void;
|
|
7
|
+
running(command: string): void;
|
|
8
|
+
finished(ms: number): void;
|
|
9
|
+
failed(code: number): void;
|
|
10
|
+
action(emoji: string, message: string): void;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=ui.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/utils/ui.ts"],"names":[],"mappings":"AAoBA,qDAAqD;AACrD,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,eAAO,MAAM,EAAE;oBACE,MAAM,QAAQ,MAAM,UAAU,MAAM,EAAE,GAAG,IAAI;kBAY/C,MAAM,SAAS,MAAM,GAAG,IAAI;mBAS3B,MAAM,MAAM,MAAM,GAAG,IAAI;qBAQvB,MAAM,GAAG,IAAI;iBAMjB,MAAM,GAAG,IAAI;iBAKb,MAAM,GAAG,IAAI;kBAKZ,MAAM,WAAW,MAAM,GAAG,IAAI;CAG5C,CAAC"}
|