mirror of
https://github.com/logto-io/logto.git
synced 2025-04-07 23:01:25 -05:00
refactor(core): inquire when required env not found (#586)
* refactor(core): inquire when required env not found * refactor(core): add comments for create DB pool
This commit is contained in:
parent
4ba37e7e73
commit
6a62e32fa5
9 changed files with 225 additions and 68 deletions
|
@ -29,6 +29,7 @@
|
|||
"got": "^11.8.2",
|
||||
"i18next": "^20.3.5",
|
||||
"iconv-lite": "0.6.3",
|
||||
"inquirer": "^8.2.2",
|
||||
"jose": "^3.14.3",
|
||||
"koa": "^2.13.1",
|
||||
"koa-body": "^4.2.0",
|
||||
|
@ -52,6 +53,7 @@
|
|||
"@shopify/jest-koa-mocks": "^3.0.8",
|
||||
"@silverhand/eslint-config": "^0.10.2",
|
||||
"@silverhand/ts-config": "^0.10.2",
|
||||
"@types/inquirer": "^8.2.1",
|
||||
"@types/jest": "^27.4.1",
|
||||
"@types/koa": "^2.13.3",
|
||||
"@types/koa-logger": "^3.1.1",
|
||||
|
|
7
packages/core/src/env-set/append-dot-env.ts
Normal file
7
packages/core/src/env-set/append-dot-env.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { appendFileSync } from 'fs';
|
||||
|
||||
const appendDotEnv = (key: string, value: string) => {
|
||||
appendFileSync('.env', `${key}=${value}\n`);
|
||||
};
|
||||
|
||||
export default appendDotEnv;
|
37
packages/core/src/env-set/create-pool-by-env.ts
Normal file
37
packages/core/src/env-set/create-pool-by-env.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { assertEnv } from '@silverhand/essentials';
|
||||
import inquirer from 'inquirer';
|
||||
import { createPool } from 'slonik';
|
||||
import { createInterceptors } from 'slonik-interceptor-preset';
|
||||
|
||||
import appendDotEnv from './append-dot-env';
|
||||
|
||||
const createPoolByEnv = async (isTest: boolean) => {
|
||||
// Database connection is disabled in unit test environment
|
||||
if (isTest) {
|
||||
return;
|
||||
}
|
||||
|
||||
const key = 'DB_URL';
|
||||
const interceptors = [...createInterceptors()];
|
||||
|
||||
try {
|
||||
const databaseDsn = assertEnv(key);
|
||||
|
||||
return createPool(databaseDsn, { interceptors });
|
||||
} catch (error: unknown) {
|
||||
const answer = await inquirer.prompt({
|
||||
name: 'dsn',
|
||||
message: `No Postgres DSN (${key}) found in env variables. Please input the DSN which points to Logto database:`,
|
||||
});
|
||||
|
||||
if (!answer.dsn) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
appendDotEnv(key, answer.dsn);
|
||||
|
||||
return createPool(answer.dsn, { interceptors });
|
||||
}
|
||||
};
|
||||
|
||||
export default createPoolByEnv;
|
|
@ -1,11 +1,9 @@
|
|||
import crypto from 'crypto';
|
||||
import { readFileSync } from 'fs';
|
||||
import { getEnv, Optional } from '@silverhand/essentials';
|
||||
import { DatabasePoolType } from 'slonik';
|
||||
|
||||
import { assertEnv, getEnv, Optional } from '@silverhand/essentials';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { createPool, DatabasePoolType } from 'slonik';
|
||||
import { createInterceptors } from 'slonik-interceptor-preset';
|
||||
import { string, number } from 'zod';
|
||||
import createPoolByEnv from './create-pool-by-env';
|
||||
import loadOidcValues from './oidc';
|
||||
import loadPasswordValues from './password';
|
||||
|
||||
export enum MountedApps {
|
||||
Api = 'api',
|
||||
|
@ -13,50 +11,21 @@ export enum MountedApps {
|
|||
Console = 'console',
|
||||
}
|
||||
|
||||
const readPrivateKey = (path: string): Optional<string> => {
|
||||
try {
|
||||
return readFileSync(path, 'utf-8');
|
||||
} catch {}
|
||||
};
|
||||
|
||||
const loadOidcValues = (port: number) => {
|
||||
const privateKeyPath = getEnv('OIDC_PRIVATE_KEY_PATH', 'oidc-private-key.pem');
|
||||
const privateKey = crypto.createPrivateKey(readPrivateKey(privateKeyPath) ?? '');
|
||||
const publicKey = crypto.createPublicKey(privateKey);
|
||||
|
||||
return {
|
||||
privateKeyPath,
|
||||
privateKey,
|
||||
publicKey,
|
||||
issuer: getEnv('OIDC_ISSUER', `http://localhost:${port}/oidc`),
|
||||
adminResource: getEnv('ADMIN_RESOURCE', 'https://api.logto.io'),
|
||||
defaultIdTokenTtl: 60 * 60,
|
||||
defaultRefreshTokenTtl: 14 * 24 * 60 * 60,
|
||||
};
|
||||
};
|
||||
|
||||
const loadEnvValues = async () => {
|
||||
const isProduction = getEnv('NODE_ENV') === 'production';
|
||||
const isTest = getEnv('NODE_ENV') === 'test';
|
||||
const port = Number(getEnv('PORT', '3001'));
|
||||
const databaseUrl = isTest ? getEnv('DB_URL') : assertEnv('DB_URL');
|
||||
|
||||
return Object.freeze({
|
||||
isTest,
|
||||
isProduction,
|
||||
databaseUrl,
|
||||
httpsCert: process.env.HTTPS_CERT,
|
||||
httpsKey: process.env.HTTPS_KEY,
|
||||
port,
|
||||
developmentUserId: getEnv('DEVELOPMENT_USER_ID'),
|
||||
trustingTlsOffloadingProxies: getEnv('TRUSTING_TLS_OFFLOADING_PROXIES') === 'true',
|
||||
passwordPeppers: string()
|
||||
.array()
|
||||
.parse(isTest ? [nanoid()] : JSON.parse(assertEnv('PASSWORD_PEPPERS'))),
|
||||
passwordIterationCount: number()
|
||||
.min(100)
|
||||
.parse(Number(getEnv('PASSWORD_ITERATION_COUNT', '1000'))),
|
||||
oidc: loadOidcValues(port),
|
||||
password: await loadPasswordValues(isTest),
|
||||
oidc: await loadOidcValues(port),
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -89,11 +58,7 @@ function createEnvSet() {
|
|||
|
||||
load: async () => {
|
||||
values = await loadEnvValues();
|
||||
|
||||
if (!values.isTest) {
|
||||
const interceptors = [...createInterceptors()];
|
||||
pool = createPool(values.databaseUrl, { interceptors });
|
||||
}
|
||||
pool = await createPoolByEnv(values.isTest);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
56
packages/core/src/env-set/oidc.ts
Normal file
56
packages/core/src/env-set/oidc.ts
Normal file
|
@ -0,0 +1,56 @@
|
|||
import crypto, { generateKeyPairSync } from 'crypto';
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
|
||||
import { getEnv } from '@silverhand/essentials';
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
const readPrivateKey = async (path: string): Promise<string> => {
|
||||
const privateKeyPath = getEnv('OIDC_PRIVATE_KEY_PATH', 'oidc-private-key.pem');
|
||||
|
||||
try {
|
||||
return readFileSync(path, 'utf-8');
|
||||
} catch (error: unknown) {
|
||||
const answer = await inquirer.prompt({
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: `No private key found in \`${privateKeyPath}\`, would you like to generate a new one?`,
|
||||
});
|
||||
|
||||
if (!answer.confirm) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const { privateKey } = generateKeyPairSync('rsa', {
|
||||
modulusLength: 4096,
|
||||
publicKeyEncoding: {
|
||||
type: 'spki',
|
||||
format: 'pem',
|
||||
},
|
||||
privateKeyEncoding: {
|
||||
type: 'pkcs8',
|
||||
format: 'pem',
|
||||
},
|
||||
});
|
||||
writeFileSync(privateKeyPath, privateKey);
|
||||
|
||||
return privateKey;
|
||||
}
|
||||
};
|
||||
|
||||
const loadOidcValues = async (port: number) => {
|
||||
const privateKeyPath = getEnv('OIDC_PRIVATE_KEY_PATH', 'oidc-private-key.pem');
|
||||
const privateKey = crypto.createPrivateKey(await readPrivateKey(privateKeyPath));
|
||||
const publicKey = crypto.createPublicKey(privateKey);
|
||||
|
||||
return Object.freeze({
|
||||
privateKeyPath,
|
||||
privateKey,
|
||||
publicKey,
|
||||
issuer: getEnv('OIDC_ISSUER', `http://localhost:${port}/oidc`),
|
||||
adminResource: getEnv('ADMIN_RESOURCE', 'https://api.logto.io'),
|
||||
defaultIdTokenTtl: 60 * 60,
|
||||
defaultRefreshTokenTtl: 14 * 24 * 60 * 60,
|
||||
});
|
||||
};
|
||||
|
||||
export default loadOidcValues;
|
50
packages/core/src/env-set/password.ts
Normal file
50
packages/core/src/env-set/password.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
import { assertEnv, getEnv } from '@silverhand/essentials';
|
||||
import inquirer from 'inquirer';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { number, string } from 'zod';
|
||||
|
||||
import appendDotEnv from './append-dot-env';
|
||||
|
||||
const loadPeppers = async (isTest: boolean): Promise<string[]> => {
|
||||
if (isTest) {
|
||||
return [nanoid()];
|
||||
}
|
||||
|
||||
const key = 'PASSWORD_PEPPERS';
|
||||
|
||||
try {
|
||||
return string()
|
||||
.array()
|
||||
.parse(JSON.parse(assertEnv(key)));
|
||||
} catch (error: unknown) {
|
||||
if (!(error instanceof Error && error.message === `env variable ${key} not found`)) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const answer = await inquirer.prompt({
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: `No password peppers (${key}) found in env variables, would you like to generate a new set and save it into \`.env\`?`,
|
||||
});
|
||||
|
||||
if (!answer.confirm) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const peppers = [nanoid(), nanoid(), nanoid()];
|
||||
appendDotEnv(key, JSON.stringify(peppers));
|
||||
|
||||
return peppers;
|
||||
}
|
||||
};
|
||||
|
||||
const loadPasswordValues = async (isTest: boolean) => {
|
||||
return Object.freeze({
|
||||
peppers: await loadPeppers(isTest),
|
||||
iterationCount: number()
|
||||
.min(100)
|
||||
.parse(Number(getEnv('PASSWORD_ITERATION_COUNT', '1000'))),
|
||||
});
|
||||
};
|
||||
|
||||
export default loadPasswordValues;
|
|
@ -51,8 +51,7 @@ describe('koaSpaProxy middleware', () => {
|
|||
const spy = jest.spyOn(envSet, 'values', 'get').mockReturnValue({
|
||||
...envSet.values,
|
||||
isProduction: true,
|
||||
passwordPeppers: ['foo'],
|
||||
databaseUrl: 'some_db_url',
|
||||
password: { peppers: ['foo'], iterationCount: 1000 },
|
||||
});
|
||||
|
||||
const ctx = createContextWithRouteParameters({
|
||||
|
@ -70,8 +69,7 @@ describe('koaSpaProxy middleware', () => {
|
|||
const spy = jest.spyOn(envSet, 'values', 'get').mockReturnValue({
|
||||
...envSet.values,
|
||||
isProduction: true,
|
||||
passwordPeppers: ['foo'],
|
||||
databaseUrl: 'some_db_url',
|
||||
password: { peppers: ['foo'], iterationCount: 1000 },
|
||||
});
|
||||
|
||||
const ctx = createContextWithRouteParameters({
|
||||
|
|
|
@ -24,9 +24,8 @@ export const encryptPassword = (
|
|||
(accumulator, current) => accumulator + (current.codePointAt(0) ?? 0),
|
||||
0
|
||||
);
|
||||
const peppers = envSet.values.passwordPeppers;
|
||||
const { peppers, iterationCount } = envSet.values.password;
|
||||
const pepper = peppers[sum % peppers.length];
|
||||
const iterationCount = envSet.values.passwordIterationCount;
|
||||
|
||||
assertThat(pepper, 'password.pepper_not_found');
|
||||
|
||||
|
|
81
pnpm-lock.yaml
generated
81
pnpm-lock.yaml
generated
|
@ -164,6 +164,7 @@ importers:
|
|||
'@silverhand/eslint-config': ^0.10.2
|
||||
'@silverhand/essentials': ^1.1.0
|
||||
'@silverhand/ts-config': ^0.10.2
|
||||
'@types/inquirer': ^8.2.1
|
||||
'@types/jest': ^27.4.1
|
||||
'@types/koa': ^2.13.3
|
||||
'@types/koa-logger': ^3.1.1
|
||||
|
@ -181,6 +182,7 @@ importers:
|
|||
got: ^11.8.2
|
||||
i18next: ^20.3.5
|
||||
iconv-lite: 0.6.3
|
||||
inquirer: ^8.2.2
|
||||
jest: ^27.5.1
|
||||
jest-matcher-specific-error: ^1.0.0
|
||||
jose: ^3.14.3
|
||||
|
@ -220,6 +222,7 @@ importers:
|
|||
got: 11.8.3
|
||||
i18next: 20.6.1
|
||||
iconv-lite: 0.6.3
|
||||
inquirer: 8.2.2
|
||||
jose: 3.20.3
|
||||
koa: 2.13.4
|
||||
koa-body: 4.2.0
|
||||
|
@ -242,6 +245,7 @@ importers:
|
|||
'@shopify/jest-koa-mocks': 3.0.8
|
||||
'@silverhand/eslint-config': 0.10.2_3a533fa6cc3da0cf8525ef55d41c4384
|
||||
'@silverhand/ts-config': 0.10.2_typescript@4.6.2
|
||||
'@types/inquirer': 8.2.1
|
||||
'@types/jest': 27.4.1
|
||||
'@types/koa': 2.13.4
|
||||
'@types/koa-logger': 3.1.2
|
||||
|
@ -5792,6 +5796,13 @@ packages:
|
|||
'@types/node': 17.0.23
|
||||
dev: false
|
||||
|
||||
/@types/inquirer/8.2.1:
|
||||
resolution: {integrity: sha512-wKW3SKIUMmltbykg4I5JzCVzUhkuD9trD6efAmYgN2MrSntY0SMRQzEnD3mkyJ/rv9NLbTC7g3hKKE86YwEDLw==}
|
||||
dependencies:
|
||||
'@types/through': 0.0.30
|
||||
rxjs: 7.5.5
|
||||
dev: true
|
||||
|
||||
/@types/istanbul-lib-coverage/2.0.3:
|
||||
resolution: {integrity: sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==}
|
||||
dev: true
|
||||
|
@ -6066,6 +6077,12 @@ packages:
|
|||
'@types/superagent': 4.1.15
|
||||
dev: true
|
||||
|
||||
/@types/through/0.0.30:
|
||||
resolution: {integrity: sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==}
|
||||
dependencies:
|
||||
'@types/node': 17.0.23
|
||||
dev: true
|
||||
|
||||
/@types/unist/2.0.6:
|
||||
resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==}
|
||||
|
||||
|
@ -6617,7 +6634,6 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
type-fest: 0.21.3
|
||||
dev: true
|
||||
|
||||
/ansi-html-community/0.0.8:
|
||||
resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==}
|
||||
|
@ -7462,7 +7478,6 @@ packages:
|
|||
|
||||
/chardet/0.7.0:
|
||||
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
|
||||
dev: true
|
||||
|
||||
/cheerio-select/1.6.0:
|
||||
resolution: {integrity: sha512-eq0GdBvxVFbqWgmCm7M3XGs1I8oLy/nExUnh6oLqmBditPO9AqQJrkslDpMun/hZ0yyTs8L0m85OHp4ho6Qm9g==}
|
||||
|
@ -7584,7 +7599,11 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
restore-cursor: 3.1.0
|
||||
dev: true
|
||||
|
||||
/cli-spinners/2.6.1:
|
||||
resolution: {integrity: sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==}
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/cli-table3/0.6.1:
|
||||
resolution: {integrity: sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==}
|
||||
|
@ -7606,7 +7625,6 @@ packages:
|
|||
/cli-width/3.0.0:
|
||||
resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==}
|
||||
engines: {node: '>= 10'}
|
||||
dev: true
|
||||
|
||||
/cliui/7.0.4:
|
||||
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
|
||||
|
@ -7639,7 +7657,6 @@ packages:
|
|||
/clone/1.0.4:
|
||||
resolution: {integrity: sha1-2jCcwmPfFZlMaIypAheco8fNfH4=}
|
||||
engines: {node: '>=0.8'}
|
||||
dev: true
|
||||
|
||||
/clone/2.1.2:
|
||||
resolution: {integrity: sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=}
|
||||
|
@ -8521,7 +8538,6 @@ packages:
|
|||
resolution: {integrity: sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=}
|
||||
dependencies:
|
||||
clone: 1.0.4
|
||||
dev: true
|
||||
|
||||
/defer-to-connect/1.1.3:
|
||||
resolution: {integrity: sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==}
|
||||
|
@ -9583,7 +9599,6 @@ packages:
|
|||
chardet: 0.7.0
|
||||
iconv-lite: 0.4.24
|
||||
tmp: 0.0.33
|
||||
dev: true
|
||||
|
||||
/extsprintf/1.3.0:
|
||||
resolution: {integrity: sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=}
|
||||
|
@ -9711,7 +9726,6 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
escape-string-regexp: 1.0.5
|
||||
dev: true
|
||||
|
||||
/file-entry-cache/6.0.1:
|
||||
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
|
||||
|
@ -11052,6 +11066,26 @@ packages:
|
|||
through: 2.3.8
|
||||
dev: true
|
||||
|
||||
/inquirer/8.2.2:
|
||||
resolution: {integrity: sha512-pG7I/si6K/0X7p1qU+rfWnpTE1UIkTONN1wxtzh0d+dHXtT/JG6qBgLxoyHVsQa8cFABxAPh0pD6uUUHiAoaow==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dependencies:
|
||||
ansi-escapes: 4.3.2
|
||||
chalk: 4.1.2
|
||||
cli-cursor: 3.1.0
|
||||
cli-width: 3.0.0
|
||||
external-editor: 3.1.0
|
||||
figures: 3.2.0
|
||||
lodash: 4.17.21
|
||||
mute-stream: 0.0.8
|
||||
ora: 5.4.1
|
||||
run-async: 2.4.1
|
||||
rxjs: 7.5.5
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
through: 2.3.8
|
||||
dev: false
|
||||
|
||||
/int64-buffer/0.99.1007:
|
||||
resolution: {integrity: sha512-XDBEu44oSTqlvCSiOZ/0FoUkpWu/vwjJLGSKDabNISPQNZ5wub1FodGHBljRsrR0IXRPq7SslshZYMuA55CgTQ==}
|
||||
engines: {node: '>= 4.5.0'}
|
||||
|
@ -11235,6 +11269,11 @@ packages:
|
|||
is-path-inside: 3.0.3
|
||||
dev: false
|
||||
|
||||
/is-interactive/1.0.0:
|
||||
resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/is-js-type/2.0.0:
|
||||
resolution: {integrity: sha1-c2FwBtZZtOtHKbunR9KHgt8PfiI=}
|
||||
dependencies:
|
||||
|
@ -11398,7 +11437,6 @@ packages:
|
|||
/is-unicode-supported/0.1.0:
|
||||
resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/is-weakref/1.0.2:
|
||||
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
|
||||
|
@ -12670,7 +12708,6 @@ packages:
|
|||
dependencies:
|
||||
chalk: 4.1.2
|
||||
is-unicode-supported: 0.1.0
|
||||
dev: true
|
||||
|
||||
/log-update/4.0.0:
|
||||
resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==}
|
||||
|
@ -13602,7 +13639,6 @@ packages:
|
|||
|
||||
/mute-stream/0.0.8:
|
||||
resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==}
|
||||
dev: true
|
||||
|
||||
/nan/2.15.0:
|
||||
resolution: {integrity: sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==}
|
||||
|
@ -14126,6 +14162,21 @@ packages:
|
|||
word-wrap: 1.2.3
|
||||
dev: true
|
||||
|
||||
/ora/5.4.1:
|
||||
resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
bl: 4.1.0
|
||||
chalk: 4.1.2
|
||||
cli-cursor: 3.1.0
|
||||
cli-spinners: 2.6.1
|
||||
is-interactive: 1.0.0
|
||||
is-unicode-supported: 0.1.0
|
||||
log-symbols: 4.1.0
|
||||
strip-ansi: 6.0.1
|
||||
wcwidth: 1.0.1
|
||||
dev: false
|
||||
|
||||
/ordered-binary/1.2.4:
|
||||
resolution: {integrity: sha512-A/csN0d3n+igxBPfUrjbV5GC69LWj2pjZzAAeeHXLukQ4+fytfP4T1Lg0ju7MSPSwq7KtHkGaiwO8URZN5IpLg==}
|
||||
dev: true
|
||||
|
@ -14138,7 +14189,6 @@ packages:
|
|||
/os-tmpdir/1.0.2:
|
||||
resolution: {integrity: sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/osenv/0.1.5:
|
||||
resolution: {integrity: sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==}
|
||||
|
@ -16882,7 +16932,6 @@ packages:
|
|||
dependencies:
|
||||
onetime: 5.1.2
|
||||
signal-exit: 3.0.6
|
||||
dev: true
|
||||
|
||||
/retry/0.12.0:
|
||||
resolution: {integrity: sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=}
|
||||
|
@ -16968,7 +17017,6 @@ packages:
|
|||
/run-async/2.4.1:
|
||||
resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
dev: true
|
||||
|
||||
/run-parallel/1.2.0:
|
||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||
|
@ -16992,7 +17040,6 @@ packages:
|
|||
resolution: {integrity: sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==}
|
||||
dependencies:
|
||||
tslib: 2.3.1
|
||||
dev: false
|
||||
|
||||
/sade/1.8.1:
|
||||
resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
|
||||
|
@ -18305,7 +18352,6 @@ packages:
|
|||
|
||||
/through/2.3.8:
|
||||
resolution: {integrity: sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=}
|
||||
dev: true
|
||||
|
||||
/through2/2.0.5:
|
||||
resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==}
|
||||
|
@ -18347,7 +18393,6 @@ packages:
|
|||
engines: {node: '>=0.6.0'}
|
||||
dependencies:
|
||||
os-tmpdir: 1.0.2
|
||||
dev: true
|
||||
|
||||
/tmpl/1.0.5:
|
||||
resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
|
||||
|
@ -18679,7 +18724,6 @@ packages:
|
|||
/type-fest/0.21.3:
|
||||
resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/type-fest/0.4.1:
|
||||
resolution: {integrity: sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==}
|
||||
|
@ -19265,7 +19309,6 @@ packages:
|
|||
resolution: {integrity: sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=}
|
||||
dependencies:
|
||||
defaults: 1.0.3
|
||||
dev: true
|
||||
|
||||
/weak-lru-cache/1.2.2:
|
||||
resolution: {integrity: sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==}
|
||||
|
|
Loading…
Add table
Reference in a new issue