Merge pull request #619 from hashicorp/feature-more-session-creds
Feature more session creds
This commit is contained in:
12 files changed
+346
-83
No files matched your search
@@ -2,14 +2,88 @@ import GeneratedSessionModel from '../generated/models/session';
|
||||
import { computed } from '@ember/object';
|
||||
import { equal } from '@ember/object/computed';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { A } from '@ember/array';
|
||||
import { typeOf } from '@ember/utils';
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class SessionCredential {
|
||||
// =classes
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static Library = class Library {
|
||||
id;
|
||||
name;
|
||||
description;
|
||||
type;
|
||||
|
||||
constructor(id, name, description, type) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.type = type;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static SecretItem = class SecretItem {
|
||||
key;
|
||||
value;
|
||||
|
||||
constructor(key, value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
};
|
||||
|
||||
// =attributes
|
||||
rawCredential;
|
||||
library;
|
||||
purpose;
|
||||
#rawSecretValue;
|
||||
|
||||
/**
|
||||
* The credential secret as an array of key/value objects.
|
||||
* @type {SessionCredential.SecretItem[]}
|
||||
*/
|
||||
get secrets() {
|
||||
if (typeOf(this.#rawSecretValue) === 'object') {
|
||||
return Object.keys(this.#rawSecretValue).map(
|
||||
(key) =>
|
||||
new SessionCredential.SecretItem(key, this.#rawSecretValue[key])
|
||||
);
|
||||
} else {
|
||||
return [new SessionCredential.SecretItem('secret', this.#rawSecretValue)];
|
||||
}
|
||||
}
|
||||
|
||||
// =methods
|
||||
|
||||
constructor(cred) {
|
||||
const { id, name, description, type } = cred.credential_library;
|
||||
this.library = new SessionCredential.Library(id, name, description, type);
|
||||
this.#rawSecretValue = cred.secret;
|
||||
this.rawCredential = cred;
|
||||
this.purpose = cred.purpose;
|
||||
}
|
||||
}
|
||||
|
||||
export default class SessionModel extends GeneratedSessionModel {
|
||||
// =attributes
|
||||
|
||||
// These transient attributes are used for values that come from the CLI
|
||||
// `connect` command as triggered by Desktop. These attributes represent
|
||||
// transient read-only values.
|
||||
@tracked acknowledged;
|
||||
@tracked started_desktop_client;
|
||||
@tracked proxy_address;
|
||||
@tracked proxy_port;
|
||||
credentials = A();
|
||||
|
||||
@equal('status', 'active') isActive;
|
||||
@equal('status', 'pending') isPending;
|
||||
@@ -43,6 +117,13 @@ export default class SessionModel extends GeneratedSessionModel {
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
addCredential(cred) {
|
||||
this.credentials.pushObject(new SessionCredential(cred));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the session via the `cancel` method.
|
||||
* See serializer and adapter for more information.
|
||||
|
||||
@@ -58,6 +58,60 @@ export default function initializeMockIPC(server) {
|
||||
port: datatype.number(),
|
||||
protocol: type,
|
||||
session_id: newSession.id,
|
||||
credentials: [
|
||||
{
|
||||
credential_library: {
|
||||
id: 'clvlt_4cvscMTl0N',
|
||||
name: 'Library Name',
|
||||
description: 'Library Description',
|
||||
credential_store_id: 'csvlt_Q1HFGt7Jpm',
|
||||
type: 'vault',
|
||||
},
|
||||
purpose: 'application',
|
||||
secret: 'just-a-random-string',
|
||||
},
|
||||
{
|
||||
credential_library: {
|
||||
id: 'clvlt_4cvscMTl0N',
|
||||
name: 'Library Name',
|
||||
description: 'Library Description',
|
||||
credential_store_id: 'csvlt_Q1HFGt7Jpm',
|
||||
type: 'vault',
|
||||
},
|
||||
purpose: 'ingress',
|
||||
secret: {
|
||||
key1: 'value 1',
|
||||
key2: true,
|
||||
key3: 'test',
|
||||
},
|
||||
},
|
||||
{
|
||||
credential_library: {
|
||||
id: 'clvlt_9KWscxpcY7',
|
||||
name: 'Library Name',
|
||||
description: 'Library Description',
|
||||
credential_store_id: 'csvlt_Q1HFGt7Jpm',
|
||||
type: 'vault',
|
||||
},
|
||||
purpose: 'application',
|
||||
secret: 'just-a-random-string',
|
||||
},
|
||||
{
|
||||
credential_library: {
|
||||
id: 'clvlt_9KWscxpcY7',
|
||||
name: 'Library Name',
|
||||
description: 'Library Description',
|
||||
credential_store_id: 'csvlt_Q1HFGt7Jpm',
|
||||
type: 'vault',
|
||||
},
|
||||
purpose: 'egress',
|
||||
secret: {
|
||||
key1: 'value 1',
|
||||
key2: true,
|
||||
key3: 'test',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -108,3 +108,7 @@ email:
|
||||
label: Email
|
||||
full_name:
|
||||
label: Full Name
|
||||
key:
|
||||
label: Key
|
||||
value:
|
||||
label: Value
|
||||
@@ -147,6 +147,17 @@ session:
|
||||
description: No active sessions available to display yet.
|
||||
actions:
|
||||
connect: Connect
|
||||
credential:
|
||||
title: Credential
|
||||
title_plural: Credentials
|
||||
secret:
|
||||
label: Secret
|
||||
credential_library:
|
||||
title: Credential Library
|
||||
actions:
|
||||
raw-api:
|
||||
show: View raw API output
|
||||
hide: Hide raw API output
|
||||
org:
|
||||
title: Org
|
||||
title_plural: Orgs
|
||||
@@ -256,3 +267,4 @@ target:
|
||||
add:
|
||||
title: Add Host Sets
|
||||
description: Select host sets to assign to this target.
|
||||
secret: ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
|
||||
@@ -1,5 +1,4 @@
|
||||
<div class='hidden-secret'>
|
||||
{{! TODO: The span wrap within a div because later we will add the toggle secret icon/button}}
|
||||
<Copyable
|
||||
@text={{@secret}}
|
||||
@buttonText={{t 'actions.copy-to-clipboard'}}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { helper } from '@ember/component/helper';
|
||||
|
||||
/**
|
||||
* Helper to convert json object to string for display purposes.
|
||||
*/
|
||||
export default helper(function rawJson([json]) {
|
||||
return JSON.stringify(json, null, ' ');
|
||||
});
|
||||
@@ -109,7 +109,7 @@ export default class ScopesScopeProjectsTargetsRoute extends Route {
|
||||
const connectionDetails = await this.ipc.invoke('connect', options);
|
||||
|
||||
// Associate the connection details with the session
|
||||
const { session_id, address, port } = connectionDetails;
|
||||
const { session_id, address, port, credentials } = connectionDetails;
|
||||
const session = await this.store.findRecord('session', session_id);
|
||||
|
||||
// Flag the session has been open in the desktop client
|
||||
@@ -119,6 +119,9 @@ export default class ScopesScopeProjectsTargetsRoute extends Route {
|
||||
// we don't have to manually persist the proxy details.
|
||||
session.proxy_address = address;
|
||||
session.proxy_port = port;
|
||||
if (credentials) {
|
||||
credentials.forEach(cred => session.addCredential(cred));
|
||||
}
|
||||
} catch(e) {
|
||||
this.confirm.confirm(e.message, { isConnectError: true })
|
||||
// Retry
|
||||
|
||||
@@ -395,19 +395,30 @@ $chevron-down: url("data:image/svg+xml;utf-8,<svg viewBox='0 0 24 24' fill='%231
|
||||
}
|
||||
|
||||
&:not([open]) {
|
||||
.reveal-trigger-text-close {
|
||||
background: $chevron-down no-repeat right center;
|
||||
padding-right: sizing.rems(l);
|
||||
}
|
||||
|
||||
.reveal-trigger-text-open {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&[open] {
|
||||
.reveal-trigger {
|
||||
background: url($chevron-up) no-repeat right center;
|
||||
.reveal-trigger-text-open {
|
||||
background: $chevron-up no-repeat right center;
|
||||
padding-right: sizing.rems(l);
|
||||
}
|
||||
|
||||
.reveal-trigger-text-close {
|
||||
display: none;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,8 +433,8 @@ $chevron-down: url("data:image/svg+xml;utf-8,<svg viewBox='0 0 24 24' fill='%231
|
||||
align-items: center;
|
||||
border-bottom: sizing.rems(xxxxs) solid var(--ui-gray-subtler-3);
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
padding: sizing.rems(s) sizing.rems(m);
|
||||
justify-content: space-between;
|
||||
padding: sizing.rems(s) sizing.rems(s) sizing.rems(s) sizing.rems(m);
|
||||
|
||||
h2 {
|
||||
@include type.type(s, normal);
|
||||
@@ -445,6 +456,23 @@ $chevron-down: url("data:image/svg+xml;utf-8,<svg viewBox='0 0 24 24' fill='%231
|
||||
border-top: sizing.rems(xxxxs) solid var(--ui-gray-subtler-3);
|
||||
padding: sizing.rems(s);
|
||||
}
|
||||
|
||||
// Allow text wrapping for copyable content in table
|
||||
.rose-table .copyable {
|
||||
white-space: inherit;
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
|
||||
.hidden-secret {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: var(--ui-gray-subtler-2);
|
||||
|
||||
.rose-button-ghost {
|
||||
color: var(--ui-gray-subtler-1);
|
||||
}
|
||||
}
|
||||
|
||||
// Hidden secret component (app/components/hidden-secret)
|
||||
|
||||
@@ -18,71 +18,122 @@
|
||||
@dismissButtonText={{t 'actions.ok'}}
|
||||
>
|
||||
<:body>
|
||||
<h3>{{t 'resources.target.messages.proxy_url.title'}}
|
||||
(<code>{{t 'resources.target.types.tcp'}}</code>)</h3>
|
||||
<h3>
|
||||
{{t 'resources.target.messages.proxy_url.title'}}
|
||||
({{t 'resources.target.types.tcp'}})
|
||||
</h3>
|
||||
<p>
|
||||
<Copyable
|
||||
@text={{session.proxy}}
|
||||
@buttonText={{t 'actions.copy-to-clipboard'}}
|
||||
@acknowledgeText={{t 'states.copied'}}
|
||||
>
|
||||
<code>{{session.proxy_address}}:{{session.proxy_port}}</code>
|
||||
<code>{{session.proxy}}</code>
|
||||
</Copyable>
|
||||
</p>
|
||||
|
||||
{{!-- <Rose::Separator />
|
||||
{{#if session.credentials}}
|
||||
<Rose::Separator />
|
||||
|
||||
<h3>Credentials</h3>
|
||||
<h3>{{t 'resources.session.credential.title_plural'}}</h3>
|
||||
|
||||
<Card @heading="Credentials" @icon='app-icons/credentials'>
|
||||
<:header>
|
||||
<Rose::Dropdown
|
||||
@style="condensed"
|
||||
@text="Credential library details"
|
||||
@showCaret={{true}}
|
||||
as |dropdown|
|
||||
{{#each session.credentials as |credential|}}
|
||||
<Card
|
||||
@heading={{concat
|
||||
(t 'resources.session.credential.title')
|
||||
' ('
|
||||
credential.purpose
|
||||
')'
|
||||
}}
|
||||
@icon='app-icons/credential'
|
||||
>
|
||||
</Rose::Dropdown>
|
||||
</:header>
|
||||
<:body>
|
||||
<Rose::Table @style='condensed' as |table|>
|
||||
<table.header as |header|>
|
||||
<header.row as |row|>
|
||||
<row.headerCell>Key</row.headerCell>
|
||||
<row.headerCell colspan='2'>Value</row.headerCell>
|
||||
</header.row>
|
||||
</table.header>
|
||||
<table.body as |body|>
|
||||
<body.row as |row|>
|
||||
<row.headerCell>foo</row.headerCell>
|
||||
<row.cell>Value</row.cell>
|
||||
</body.row>
|
||||
<body.row as |row|>
|
||||
<row.headerCell>ttl</row.headerCell>
|
||||
<row.cell>Test</row.cell>
|
||||
</body.row>
|
||||
</table.body>
|
||||
</Rose::Table>
|
||||
</:body>
|
||||
<:footer>
|
||||
<Reveal
|
||||
@textWhenOpen='Hide content'
|
||||
@textWhenClose='View content'
|
||||
>
|
||||
<pre>
|
||||
<code>
|
||||
{
|
||||
"auth": null,
|
||||
"lease_duration": 3600,
|
||||
"lease_id": "",
|
||||
"renewable": false
|
||||
}
|
||||
</code>
|
||||
</pre>
|
||||
</Reveal>
|
||||
</:footer>
|
||||
</Card> --}}
|
||||
|
||||
<:header>
|
||||
<Rose::Dropdown
|
||||
@text={{t
|
||||
'resources.session.credential.credential_library.title'
|
||||
}}
|
||||
@style='condensed'
|
||||
@dropdownRight={{true}}
|
||||
@ignoreClickInside={{true}}
|
||||
as |dropdown|
|
||||
>
|
||||
<dropdown.key-value>
|
||||
<:key>{{t 'form.id.label'}}</:key>
|
||||
<:value>
|
||||
<Copyable
|
||||
@text={{credential.library.id}}
|
||||
@buttonText={{t 'actions.copy-to-clipboard'}}
|
||||
@acknowledgeText={{t 'states.copied'}}
|
||||
>
|
||||
<code>{{credential.library.id}}</code>
|
||||
</Copyable>
|
||||
</:value>
|
||||
</dropdown.key-value>
|
||||
<dropdown.key-value>
|
||||
<:key>{{t 'form.type.label'}}</:key>
|
||||
<:value>{{credential.library.type}}</:value>
|
||||
</dropdown.key-value>
|
||||
{{#if credential.library.name}}
|
||||
<dropdown.key-value>
|
||||
<:key>{{t 'form.name.label'}}</:key>
|
||||
<:value>{{credential.library.name}}</:value>
|
||||
</dropdown.key-value>
|
||||
{{/if}}
|
||||
{{#if credential.library.description}}
|
||||
<dropdown.key-value>
|
||||
<:key>{{t 'form.description.label'}}</:key>
|
||||
<:value>{{credential.library.description}}</:value>
|
||||
</dropdown.key-value>
|
||||
{{/if}}
|
||||
</Rose::Dropdown>
|
||||
</:header>
|
||||
<:body>
|
||||
{{#if credential.secrets}}
|
||||
<Rose::Table @style='condensed' as |table|>
|
||||
<table.header as |header|>
|
||||
<header.row as |row|>
|
||||
<row.headerCell>{{t
|
||||
'form.key.label'
|
||||
}}</row.headerCell>
|
||||
<row.headerCell colspan='2'>{{t
|
||||
'form.value.label'
|
||||
}}</row.headerCell>
|
||||
</header.row>
|
||||
</table.header>
|
||||
<table.body as |body|>
|
||||
{{#each credential.secrets as |secret|}}
|
||||
<body.row as |row|>
|
||||
<row.headerCell>{{secret.key}}</row.headerCell>
|
||||
<row.cell colspan='2'>
|
||||
<Copyable
|
||||
@text={{secret.value}}
|
||||
@buttonText={{t 'actions.copy-to-clipboard'}}
|
||||
@acknowledgeText={{t 'states.copied'}}
|
||||
>
|
||||
{{secret.value}}
|
||||
</Copyable>
|
||||
</row.cell>
|
||||
</body.row>
|
||||
{{/each}}
|
||||
</table.body>
|
||||
</Rose::Table>
|
||||
{{/if}}
|
||||
</:body>
|
||||
<:footer>
|
||||
<Reveal
|
||||
@textWhenOpen={{t
|
||||
'resources.session.credential.actions.raw-api.hide'
|
||||
}}
|
||||
@textWhenClose={{t
|
||||
'resources.session.credential.actions.raw-api.show'
|
||||
}}
|
||||
>
|
||||
<pre>{{raw-json credential.rawCredential}}</pre>
|
||||
</Reveal>
|
||||
</:footer>
|
||||
</Card>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</:body>
|
||||
<:footer>
|
||||
<div>
|
||||
@@ -93,15 +144,6 @@
|
||||
{{t 'actions.close'}}
|
||||
</Rose::Button>
|
||||
</div>
|
||||
<div>
|
||||
{{! <Rose::LinkButton
|
||||
@route='index'
|
||||
@style='inline-link-action'
|
||||
@iconRight='chevron-right'
|
||||
>
|
||||
|
||||
</Rose::LinkButton> }}
|
||||
</div>
|
||||
</:footer>
|
||||
</DialogDetail>
|
||||
{{/unless}}
|
||||
|
||||
@@ -48,6 +48,10 @@ const extract = (artifactPath, destination) => {
|
||||
|
||||
module.exports = {
|
||||
setup: async () => {
|
||||
if (process.env.BYPASS_CLI_SETUP) {
|
||||
console.warn('WARNING: Bypassing cli setup');
|
||||
return;
|
||||
}
|
||||
const artifactPath = await downloadArtifact(artifactVersion);
|
||||
await extract(artifactPath, artifactDestination);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Helper | raw-json', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
this.set('inputValue', {});
|
||||
await render(hbs`{{raw-json inputValue}}`);
|
||||
assert.equal(this.element.textContent.trim(), '{}');
|
||||
this.set('inputValue', { key: 'value', nestedKey: { key: 'value' } });
|
||||
assert.equal(
|
||||
this.element.textContent.trim(),
|
||||
`{\n "key": "value",\n "nestedKey": {\n "key": "value"\n }\n}`
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -3671,7 +3671,7 @@ agent-base@4, agent-base@^4.3.0:
|
||||
dependencies:
|
||||
es6-promisify "^5.0.0"
|
||||
|
||||
agent-base@6:
|
||||
agent-base@6, agent-base@^6.0.2:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
|
||||
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
|
||||
@@ -6360,9 +6360,9 @@ caniuse-api@^3.0.0:
|
||||
lodash.uniq "^4.5.0"
|
||||
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001157, caniuse-lite@^1.0.30001219:
|
||||
version "1.0.30001235"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001235.tgz#ad5ca75bc5a1f7b12df79ad806d715a43a5ac4ed"
|
||||
integrity sha512-zWEwIVqnzPkSAXOUlQnPW2oKoYb2aLQ4Q5ejdjBcnH63rfypaW34CxaeBn1VMya2XaEU3P/R2qHpWyj+l0BT1A==
|
||||
version "1.0.30001239"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001239.tgz#66e8669985bb2cb84ccb10f68c25ce6dd3e4d2b8"
|
||||
integrity sha512-cyBkXJDMeI4wthy8xJ2FvDU6+0dtcZSJW3voUF8+e9f1bBeuvyZfc3PNbkOETyhbR+dGCPzn9E7MA3iwzusOhQ==
|
||||
|
||||
capture-exit@^2.0.0:
|
||||
version "2.0.0"
|
||||
@@ -15671,7 +15671,16 @@ npm-package-arg@^7.0.0:
|
||||
semver "^5.6.0"
|
||||
validate-npm-package-name "^3.0.0"
|
||||
|
||||
npm-package-arg@^8.0.0, npm-package-arg@^8.1.0:
|
||||
npm-package-arg@^8.0.0:
|
||||
version "8.1.5"
|
||||
resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.5.tgz#3369b2d5fe8fdc674baa7f1786514ddc15466e44"
|
||||
integrity sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==
|
||||
dependencies:
|
||||
hosted-git-info "^4.0.1"
|
||||
semver "^7.3.4"
|
||||
validate-npm-package-name "^3.0.0"
|
||||
|
||||
npm-package-arg@^8.1.0:
|
||||
version "8.1.4"
|
||||
resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.4.tgz#8001cdbc4363997b8ef6c6cf7aaf543c5805879d"
|
||||
integrity sha512-xLokoCFqj/rPdr3LvcdDL6Kj6ipXGEDHD/QGpzwU6/pibYUOXmp5DBmg76yukFyx4ZDbrXNOTn+BPyd8TD4Jlw==
|
||||
@@ -19098,11 +19107,11 @@ socks-proxy-agent@^4.0.0:
|
||||
socks "~2.3.2"
|
||||
|
||||
socks-proxy-agent@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.0.tgz#7c0f364e7b1cf4a7a437e71253bed72e9004be60"
|
||||
integrity sha512-lEpa1zsWCChxiynk+lCycKuC502RxDWLKJZoIhnxrWNjLSDGYRFflHA1/228VkRcnv9TIb8w98derGbpKxJRgA==
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz#032fb583048a29ebffec2e6a73fca0761f48177e"
|
||||
integrity sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==
|
||||
dependencies:
|
||||
agent-base "6"
|
||||
agent-base "^6.0.2"
|
||||
debug "4"
|
||||
socks "^2.3.3"
|
||||
|
||||
@@ -21634,9 +21643,9 @@ write@^0.2.1:
|
||||
mkdirp "^0.5.1"
|
||||
|
||||
ws@^7.4.5, ws@^7.4.6, ws@~7.4.2:
|
||||
version "7.4.6"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
|
||||
integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.0.tgz#0033bafea031fb9df041b2026fc72a571ca44691"
|
||||
integrity sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw==
|
||||
|
||||
x-is-string@^0.1.0:
|
||||
version "0.1.0"
|
||||
@@ -21744,9 +21753,9 @@ yaml@^1.10.0, yaml@^1.7.2:
|
||||
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
|
||||
|
||||
yargs-parser@20.2.4, yargs-parser@^15.0.1, yargs-parser@^18.1.2, yargs-parser@^20.0.0, yargs-parser@^20.2.2, yargs-parser@^20.2.3, yargs-parser@^7.0.0, yargs-parser@^9.0.2, yargs-parser@~20.2.7:
|
||||
version "20.2.7"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a"
|
||||
integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==
|
||||
version "20.2.9"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
|
||||
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||
|
||||
yargs-unparser@2.0.0:
|
||||
version "2.0.0"
|
||||
|
||||
Reference in new issue
Block a user