From f9884483483a9aeaf202a5bd4467da0f93fc3283 Mon Sep 17 00:00:00 2001 From: RebeccaStankus Date: Tue, 2 Mar 2021 15:58:09 -0800 Subject: [PATCH 01/41] First round of tests on the communicator class --- .../HiFiCommunicator.integration.test.ts | 153 ++++++++++++++++++ .../testUtilities/integrationTestUtils.ts | 65 +++++++- 2 files changed, 213 insertions(+), 5 deletions(-) create mode 100644 tests/integration/src/classes/HiFiCommunicator.integration.test.ts diff --git a/tests/integration/src/classes/HiFiCommunicator.integration.test.ts b/tests/integration/src/classes/HiFiCommunicator.integration.test.ts new file mode 100644 index 00000000..65938255 --- /dev/null +++ b/tests/integration/src/classes/HiFiCommunicator.integration.test.ts @@ -0,0 +1,153 @@ +const fetch = require('node-fetch'); +import { HiFiCommunicator, HiFiConnectionStates, HiFiUserDataStreamingScopes } from "../../../../src/classes/HiFiCommunicator"; +import { TOKEN_GEN_TYPES, generateJWT } from '../../testUtilities/integrationTestUtils'; +const stackData = require('../../secrets/auth.json').stackData; +const hifiCommunicator = new HiFiCommunicator(); + +describe('Non admin server connections', () => { + let nonAdminSigned: string; + let adminSigned: string; + let nonAdminUnsigned: string; + let nonAdminNonexistantSpaceID: string; + let nonAdminNoSpaceName: string; + let nonAdminTimed: string; + let nonAdminExpired: string; + let nonAdminPreissued: string; + beforeAll(async () => { + try { + nonAdminSigned = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP1_SPACE1_SIGNED); + adminSigned = await generateJWT(TOKEN_GEN_TYPES.ADMIN_ID_APP1_SPACE1_SIGNED); + nonAdminUnsigned = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP1_SPACE1_UNSIGNED); + nonAdminNonexistantSpaceID = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP1_SPACE_ID_NONEXISTANT_SIGNED); + nonAdminNoSpaceName = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP1_NO_SPACE_NAME_SIGNED); + nonAdminTimed = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP1_SPACE1_TIMED_SIGNED); + nonAdminExpired = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP1_SPACE1_TIMED_EXPIRED); + nonAdminPreissued = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP1_SPACE1_TIMED_PREISSUED); + } catch (err) { + console.error("Unable to create tokens in preparation for testing server connections. Please check " + + "your 'auth.json' file for errors or discrepancies with your account data. ERR: ", err); + throw err; + } + }); + + test(`CAN connect with SIGNED correct token and correct stack URL`, async () => { + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminSigned, stackData.url) + .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); + }); + + test(`CANNOT connect with SIGNED correct token and incorrect stack URL`, async () => { + // append the stack url twice to create a stack name that does not exist + await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminSigned, stackData.url + stackData.url)) + .rejects.toMatchObject({ error: expect.stringMatching(/getaddrinfo ENOTFOUND/) }); + }); + + // FAIL getting object with enotfound error + test(`CAN connect with UNSIGNED correct token and correct stack URL if space ignores token signing`, async () => { + // set space to allow unsigned tokens + try { + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=true`); + } catch (err) { + console.error("Unable to set space to ignore token signing. ERR: ", err); + throw err; + } + + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminUnsigned, stackData.url) + .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); + }); + + test(`CANNOT connect with UNSIGNED correct token and correct stack URL if space does not ignore token signing`, async () => { + // set space to not allow unsigned tokens + try { + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=false`); + } catch (err) { + console.error("Unable to set space to ignore token signing. ERR: ", err); + throw err; + } + + await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminUnsigned, stackData.url)) + .rejects.toMatchObject({ error: expect.stringMatching(/Unexpected server response: 501/) }); + }); + + test(`CAN connect with SIGNED correct token and correct wss stackurl`, async () => { + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminSigned, stackData.wss + "?token=") + .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); + }); + + test(`CAN connect with UNSIGNED correct token and correct wss stackurl if space ignores token signing`, async () => { + // set space to allow unsigned tokens + try { + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=true`); + } catch (err) { + console.error("Unable to set space to ignore token signing. ERR: ", err); + throw err; + } + + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminUnsigned, stackData.wss + "?token=") + .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); + }); + + test(`CANNOT connect with UNSIGNED correct token and correct wss stackurl if space does not ignore token signing`, async () => { + // set space to not allow unsigned tokens + try { + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=false`); + } catch (err) { + console.error("Unable to set space to ignore token signing. ERR: ", err); + throw err; + } + + await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminUnsigned, stackData.wss + "?token=")) + .rejects.toMatchObject({ error: expect.stringMatching(/Unexpected server response: 501/) }); + }); + + test(`CANNOT connect with SIGNED incorrect token (nonexistant space ID) and correct stack URL`, async () => { + await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNonexistantSpaceID, stackData.url)) + .rejects.toMatchObject({ + error: expect.stringMatching(/Unexpected server response: 501/) + }); + }); + + // TODO AFTER pr from https://highfidelity.atlassian.net/browse/HIFI-302 is deployed + // test(`CAN create space by trying to connect with SIGNED incorrect token (nonexistant space NAME) and correct stack URL`, async () => { + // let nonexistantSpaceName = `this space does not exist`; + // // TODO Make sure space does not exist first + // let result = hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNoSpaceName, stackData.url) + // .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); + // // check the name exists? + // try { + // await fetch(`${stackData.url}/api/v1/spaces/${nonexistantSpaceName}?token=${adminSigned}`, { + // method: 'DELETE' + // }); + // } catch (err) { + // console.error(`Unable to delete the space called ${nonexistantSpaceName} that was created for testing. Please do this manually. ERR: ${err}`); + // } + // return result; + // }); + + test(`CANNOT connect to a space BY NAME when multiple spaces with the same name exist in the same app`, async () => { + await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNoSpaceName, stackData.url)) + .rejects.toMatchObject({ + error: expect.stringMatching(/Unexpected server response: 501/) + // for when the new log message gets merged + // error: expect.stringMatching(/multiple spaces with given name/) + }); + }); + + test(`CAN connect to a space using a timed token before the token expires`, async () => { + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminTimed, stackData.url) + .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); + }); + + test(`CANNOT connect to a space using a timed token after the token expires`, async () => { + await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminExpired, stackData.url)) + .rejects.toMatchObject({ + error: expect.stringMatching(/Unexpected server response: 501/) + }); + }); + + test(`CAN disconnect once connected`, async () => { + await hifiCommunicator.disconnectFromHiFiAudioAPIServer() + .then((data) => { + expect(data).toBe('Successfully disconnected.'); + }) + }); +}); diff --git a/tests/integration/testUtilities/integrationTestUtils.ts b/tests/integration/testUtilities/integrationTestUtils.ts index 38910434..1b5a2e77 100644 --- a/tests/integration/testUtilities/integrationTestUtils.ts +++ b/tests/integration/testUtilities/integrationTestUtils.ts @@ -41,8 +41,42 @@ export const TOKEN_GEN_TYPES = { "signed": true, "user_id": "qateamNonAdmin", "app_id": stackData.apps.app1.id, - "space_name": "stackData.apps.app1.spaces.nonexistant.name", + "space_name": stackData.apps.app1.spaces.nonexistant.name, "app_secret": stackData.apps.app1.secret + }, + "NON_ADMIN_APP1_NO_SPACE_NAME_SIGNED": { + "admin": false, + "signed": true, + "user_id": "qateamNonAdmin", + "app_id": stackData.apps.app1.id, + "app_secret": stackData.apps.app1.secret + }, + "NON_ADMIN_APP1_SPACE1_TIMED_SIGNED": { + "admin": false, + "signed": true, + "user_id": "qateamNonAdmin", + "app_id": stackData.apps.app1.id, + "space_id": stackData.apps.app1.spaces.space1.id, + "app_secret": stackData.apps.app1.secret, + "expired": false + }, + "NON_ADMIN_APP1_SPACE1_TIMED_EXPIRED": { + "admin": false, + "signed": true, + "user_id": "qateamNonAdmin", + "app_id": stackData.apps.app1.id, + "space_id": stackData.apps.app1.spaces.space1.id, + "app_secret": stackData.apps.app1.secret, + "expired": true + }, + "NON_ADMIN_APP1_SPACE1_TIMED_PREISSUED": { + "admin": false, + "signed": true, + "user_id": "qateamNonAdmin", + "app_id": stackData.apps.app1.id, + "space_id": stackData.apps.app1.spaces.space1.id, + "app_secret": stackData.apps.app1.secret, + "preissued": true } }; @@ -50,6 +84,7 @@ export async function generateJWT(tokenType: { [property: string]: any }) { const SECRET_KEY_FOR_SIGNING = crypto.createSecretKey(Buffer.from(tokenType.app_secret, "utf8")); try { let data: any = {}; + let token; data = { "user_id": tokenType.user_id, "app_id": tokenType.app_id @@ -58,12 +93,32 @@ export async function generateJWT(tokenType: { [property: string]: any }) { if (tokenType.space_id) data.space_id = tokenType.space_id; if (tokenType.space_name) data.space_name = tokenType.space_name; if (tokenType.signed) { - return await new SignedJWT(data) - .setProtectedHeader({ alg: 'HS256', typ: 'JWT' }) - .sign(SECRET_KEY_FOR_SIGNING); + if (tokenType.expired === true) { + token = await new SignedJWT(data) + .setProtectedHeader({ alg: 'HS256', typ: 'JWT' }) + .setIssuedAt() + .setExpirationTime(Math.round(Date.now() / 1000) - 60 * 60) + .sign(SECRET_KEY_FOR_SIGNING); + } else if (tokenType.expired === false) { + token = await new SignedJWT(data) + .setProtectedHeader({ alg: 'HS256', typ: 'JWT' }) + .setExpirationTime(Math.round(Date.now() / 1000) + 60 * 60) + .sign(SECRET_KEY_FOR_SIGNING); + } else if (tokenType.preissued === true) { + token = await new SignedJWT(data) + .setProtectedHeader({ alg: 'HS256', typ: 'JWT' }) + .setIssuedAt(Math.round(Date.now() / 1000) + 60 * 60) + .setExpirationTime(Math.round(Date.now() / 1000) + 60 * 60 * 2) + .sign(SECRET_KEY_FOR_SIGNING); + } else { + token = await new SignedJWT(data) + .setProtectedHeader({ alg: 'HS256', typ: 'JWT' }) + .sign(SECRET_KEY_FOR_SIGNING); + } } else { - return await new UnsecuredJWT(data).encode(); + token = await new UnsecuredJWT(data).encode(); } + return token; } catch (error) { console.error(`Couldn't create JWT! Error:\n${error}`); return; From 8875b6bb5de6830201563b283bfddf31e1f119a0 Mon Sep 17 00:00:00 2001 From: RebeccaStankus Date: Wed, 3 Mar 2021 14:07:18 -0800 Subject: [PATCH 02/41] Test to check space IDs --- tests/integration/rest.integration.test.ts | 29 +++++++- .../testUtilities/integrationTestUtils.ts | 70 ++++++++++++------- 2 files changed, 71 insertions(+), 28 deletions(-) diff --git a/tests/integration/rest.integration.test.ts b/tests/integration/rest.integration.test.ts index 251f5876..5d977de6 100644 --- a/tests/integration/rest.integration.test.ts +++ b/tests/integration/rest.integration.test.ts @@ -14,7 +14,7 @@ describe('HiFi API REST Calls', () => { nonAdminToken = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP1_SPACE1_SIGNED); } catch (err) { console.error("Unable to create non admin token for testing REST calls. ERR: ", err); - return; + process.exit(); } }); @@ -74,6 +74,33 @@ describe('HiFi API REST Calls', () => { }); }); + describe(`Admin CAN read accurate list of spaces for an app`, () => { + test(`CAN read the list of spaces`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/?token=${adminToken}`); + + let spacesListJSON: any = {}; + spacesListJSON = await returnMessage.json(); + expect(spacesListJSON).toBeDefined(); + }); + + test(`the list is accurate`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/?token=${adminToken}`); + + let spacesListJSON: any = {}; + spacesListJSON = await returnMessage.json(); + expect(spacesListJSON.length).toBe(Object.keys(stackData.apps.app1.spaces).length); + spacesListJSON.forEach(async (space: any) => { + let match = false; + for (var key in stackData.apps.app1.spaces) { + if (stackData.apps.app1.spaces[key].id === space['space-id']) { match = true; } + } + expect(match).toBe(true); + }); + + + }); + }); + describe(`Admin CAN read settings for a space`, () => { test(`CAN read all space settings simultaneously`, async () => { let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}`); diff --git a/tests/integration/testUtilities/integrationTestUtils.ts b/tests/integration/testUtilities/integrationTestUtils.ts index 1b5a2e77..d8c61058 100644 --- a/tests/integration/testUtilities/integrationTestUtils.ts +++ b/tests/integration/testUtilities/integrationTestUtils.ts @@ -20,62 +20,78 @@ export const TOKEN_GEN_TYPES = { "space_id": stackData.apps.app1.spaces.space1.id, "app_secret": stackData.apps.app1.secret }, - "NON_ADMIN_ID_APP1_SPACE1_UNSIGNED": { + "ADMIN_ID_APP2_SPACE1_SIGNED": { + "admin": true, + "signed": true, + "user_id": "qateamAdmin", + "app_id": stackData.apps.app2.id, + "space_id": stackData.apps.app2.spaces.space1.id, + "app_secret": stackData.apps.app2.secret + }, + "NON_ADMIN_ID_APP2_SPACE1_SIGNED": { + "admin": false, + "signed": true, + "user_id": "qateamNonAdmin", + "app_id": stackData.apps.app2.id, + "space_id": stackData.apps.app2.spaces.space1.id, + "app_secret": stackData.apps.app2.secret + }, + "NON_ADMIN_ID_APP2_SPACE1_UNSIGNED": { "admin": false, "signed": false, "user_id": "qateamNonAdmin", - "app_id": stackData.apps.app1.id, - "space_id": stackData.apps.app1.spaces.space1.id, - "app_secret": stackData.apps.app1.secret + "app_id": stackData.apps.app2.id, + "space_id": stackData.apps.app2.spaces.space1.id, + "app_secret": stackData.apps.app2.secret }, - "NON_ADMIN_APP1_SPACE_ID_NONEXISTANT_SIGNED": { + "NON_ADMIN_APP2_SPACE_ID_NONEXISTANT_SIGNED": { "admin": false, "signed": true, "user_id": "qateamNonAdmin", - "app_id": stackData.apps.app1.id, - "space_id": stackData.apps.app1.spaces.nonexistant.id, - "app_secret": stackData.apps.app1.secret + "app_id": stackData.apps.app2.id, + "space_id": stackData.apps.app2.spaces.nonexistant.id, + "app_secret": stackData.apps.app2.secret }, - "NON_ADMIN_APP1_SPACE_NAME_NONEXISTANT_SIGNED": { + "NON_ADMIN_APP2_SPACE_NAME_NONEXISTANT_SIGNED": { "admin": false, "signed": true, "user_id": "qateamNonAdmin", - "app_id": stackData.apps.app1.id, - "space_name": stackData.apps.app1.spaces.nonexistant.name, - "app_secret": stackData.apps.app1.secret + "app_id": stackData.apps.app2.id, + "space_name": stackData.apps.app2.spaces.nonexistant.name, + "app_secret": stackData.apps.app2.secret }, - "NON_ADMIN_APP1_NO_SPACE_NAME_SIGNED": { + "NON_ADMIN_APP2_NO_SPACE_NAME_SIGNED": { "admin": false, "signed": true, "user_id": "qateamNonAdmin", - "app_id": stackData.apps.app1.id, - "app_secret": stackData.apps.app1.secret + "app_id": stackData.apps.app2.id, + "app_secret": stackData.apps.app2.secret }, - "NON_ADMIN_APP1_SPACE1_TIMED_SIGNED": { + "NON_ADMIN_APP2_SPACE1_TIMED_SIGNED": { "admin": false, "signed": true, "user_id": "qateamNonAdmin", - "app_id": stackData.apps.app1.id, - "space_id": stackData.apps.app1.spaces.space1.id, - "app_secret": stackData.apps.app1.secret, + "app_id": stackData.apps.app2.id, + "space_id": stackData.apps.app2.spaces.space1.id, + "app_secret": stackData.apps.app2.secret, "expired": false }, - "NON_ADMIN_APP1_SPACE1_TIMED_EXPIRED": { + "NON_ADMIN_APP2_SPACE1_TIMED_EXPIRED": { "admin": false, "signed": true, "user_id": "qateamNonAdmin", - "app_id": stackData.apps.app1.id, - "space_id": stackData.apps.app1.spaces.space1.id, - "app_secret": stackData.apps.app1.secret, + "app_id": stackData.apps.app2.id, + "space_id": stackData.apps.app2.spaces.space1.id, + "app_secret": stackData.apps.app2.secret, "expired": true }, - "NON_ADMIN_APP1_SPACE1_TIMED_PREISSUED": { + "NON_ADMIN_APP2_SPACE1_TIMED_PREISSUED": { "admin": false, "signed": true, "user_id": "qateamNonAdmin", - "app_id": stackData.apps.app1.id, - "space_id": stackData.apps.app1.spaces.space1.id, - "app_secret": stackData.apps.app1.secret, + "app_id": stackData.apps.app2.id, + "space_id": stackData.apps.app2.spaces.space1.id, + "app_secret": stackData.apps.app2.secret, "preissued": true } }; From 7b7f7a12cdc20c4376192f88c9d6d2a69449ecac Mon Sep 17 00:00:00 2001 From: RebeccaStankus Date: Wed, 3 Mar 2021 16:16:33 -0800 Subject: [PATCH 03/41] Working server connection tests --- .gitignore | 2 + .../HiFiCommunicator.integration.test.ts | 60 ++++++++++--------- .../testUtilities/integrationTestUtils.ts | 13 +++- 3 files changed, 45 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index bbb0f63d..2dc036d9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ example/nodejs/node_modules/ docs/* docs.json auth.json +authProd.json +authStag.json # The contents of this file below this line were autogenerated when we created the # GitHub repository. diff --git a/tests/integration/src/classes/HiFiCommunicator.integration.test.ts b/tests/integration/src/classes/HiFiCommunicator.integration.test.ts index 65938255..5ec1ae9b 100644 --- a/tests/integration/src/classes/HiFiCommunicator.integration.test.ts +++ b/tests/integration/src/classes/HiFiCommunicator.integration.test.ts @@ -9,20 +9,20 @@ describe('Non admin server connections', () => { let adminSigned: string; let nonAdminUnsigned: string; let nonAdminNonexistantSpaceID: string; - let nonAdminNoSpaceName: string; + let nonAdminNewSpaceName: string; let nonAdminTimed: string; let nonAdminExpired: string; - let nonAdminPreissued: string; + let nonAdminDupSpaceName: string; beforeAll(async () => { try { - nonAdminSigned = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP1_SPACE1_SIGNED); - adminSigned = await generateJWT(TOKEN_GEN_TYPES.ADMIN_ID_APP1_SPACE1_SIGNED); - nonAdminUnsigned = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP1_SPACE1_UNSIGNED); - nonAdminNonexistantSpaceID = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP1_SPACE_ID_NONEXISTANT_SIGNED); - nonAdminNoSpaceName = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP1_NO_SPACE_NAME_SIGNED); - nonAdminTimed = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP1_SPACE1_TIMED_SIGNED); - nonAdminExpired = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP1_SPACE1_TIMED_EXPIRED); - nonAdminPreissued = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP1_SPACE1_TIMED_PREISSUED); + nonAdminSigned = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP2_SPACE1_SIGNED); + adminSigned = await generateJWT(TOKEN_GEN_TYPES.ADMIN_ID_APP2_SPACE1_SIGNED); + nonAdminUnsigned = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP2_SPACE1_UNSIGNED); + nonAdminNonexistantSpaceID = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE_ID_NONEXISTANT_SIGNED); + nonAdminNewSpaceName = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED); + nonAdminTimed = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_TIMED_SIGNED); + nonAdminExpired = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_TIMED_EXPIRED); + nonAdminDupSpaceName = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_DUP_SIGNED); } catch (err) { console.error("Unable to create tokens in preparation for testing server connections. Please check " + "your 'auth.json' file for errors or discrepancies with your account data. ERR: ", err); @@ -30,6 +30,10 @@ describe('Non admin server connections', () => { } }); + afterEach(async () => { + await hifiCommunicator.disconnectFromHiFiAudioAPIServer(); + }); + test(`CAN connect with SIGNED correct token and correct stack URL`, async () => { await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminSigned, stackData.url) .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); @@ -41,11 +45,10 @@ describe('Non admin server connections', () => { .rejects.toMatchObject({ error: expect.stringMatching(/getaddrinfo ENOTFOUND/) }); }); - // FAIL getting object with enotfound error test(`CAN connect with UNSIGNED correct token and correct stack URL if space ignores token signing`, async () => { // set space to allow unsigned tokens try { - await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=true`); + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=true`); } catch (err) { console.error("Unable to set space to ignore token signing. ERR: ", err); throw err; @@ -58,7 +61,7 @@ describe('Non admin server connections', () => { test(`CANNOT connect with UNSIGNED correct token and correct stack URL if space does not ignore token signing`, async () => { // set space to not allow unsigned tokens try { - await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=false`); + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=false`); } catch (err) { console.error("Unable to set space to ignore token signing. ERR: ", err); throw err; @@ -76,7 +79,7 @@ describe('Non admin server connections', () => { test(`CAN connect with UNSIGNED correct token and correct wss stackurl if space ignores token signing`, async () => { // set space to allow unsigned tokens try { - await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=true`); + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=true`); } catch (err) { console.error("Unable to set space to ignore token signing. ERR: ", err); throw err; @@ -89,7 +92,7 @@ describe('Non admin server connections', () => { test(`CANNOT connect with UNSIGNED correct token and correct wss stackurl if space does not ignore token signing`, async () => { // set space to not allow unsigned tokens try { - await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=false`); + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=false`); } catch (err) { console.error("Unable to set space to ignore token signing. ERR: ", err); throw err; @@ -101,16 +104,14 @@ describe('Non admin server connections', () => { test(`CANNOT connect with SIGNED incorrect token (nonexistant space ID) and correct stack URL`, async () => { await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNonexistantSpaceID, stackData.url)) - .rejects.toMatchObject({ - error: expect.stringMatching(/Unexpected server response: 501/) - }); + .rejects.toMatchObject({ error: expect.stringMatching(/Unexpected server response: 501/) }); }); // TODO AFTER pr from https://highfidelity.atlassian.net/browse/HIFI-302 is deployed - // test(`CAN create space by trying to connect with SIGNED incorrect token (nonexistant space NAME) and correct stack URL`, async () => { - // let nonexistantSpaceName = `this space does not exist`; + // test(`CAN create space by trying to connect with SIGNED incorrect token (nonexistant space NAME, no space ID) and correct stack URL`, async () => { + // let nonexistantSpaceName = TOKEN_GEN_TYPES.NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED["space-name"]; // // TODO Make sure space does not exist first - // let result = hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNoSpaceName, stackData.url) + // let result = hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNewSpaceName, stackData.url) // .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); // // check the name exists? // try { @@ -124,7 +125,7 @@ describe('Non admin server connections', () => { // }); test(`CANNOT connect to a space BY NAME when multiple spaces with the same name exist in the same app`, async () => { - await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNoSpaceName, stackData.url)) + await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminDupSpaceName, stackData.url)) .rejects.toMatchObject({ error: expect.stringMatching(/Unexpected server response: 501/) // for when the new log message gets merged @@ -139,15 +140,18 @@ describe('Non admin server connections', () => { test(`CANNOT connect to a space using a timed token after the token expires`, async () => { await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminExpired, stackData.url)) - .rejects.toMatchObject({ - error: expect.stringMatching(/Unexpected server response: 501/) - }); + .rejects.toMatchObject({ error: expect.stringMatching(/Unexpected server response: 501/) }); }); test(`CAN disconnect once connected`, async () => { + // make sure we're connected first + try { + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminSigned, stackData.url) + } catch (err) { + console.error("Unable to connect before testing disconnect. ERR: ", err); + throw err; + } await hifiCommunicator.disconnectFromHiFiAudioAPIServer() - .then((data) => { - expect(data).toBe('Successfully disconnected.'); - }) + .then((data) => { expect(data).toBe('Successfully disconnected.') }); }); }); diff --git a/tests/integration/testUtilities/integrationTestUtils.ts b/tests/integration/testUtilities/integrationTestUtils.ts index d8c61058..77892287 100644 --- a/tests/integration/testUtilities/integrationTestUtils.ts +++ b/tests/integration/testUtilities/integrationTestUtils.ts @@ -60,12 +60,13 @@ export const TOKEN_GEN_TYPES = { "space_name": stackData.apps.app2.spaces.nonexistant.name, "app_secret": stackData.apps.app2.secret }, - "NON_ADMIN_APP2_NO_SPACE_NAME_SIGNED": { + "NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED": { "admin": false, "signed": true, "user_id": "qateamNonAdmin", "app_id": stackData.apps.app2.id, - "app_secret": stackData.apps.app2.secret + "app_secret": stackData.apps.app2.secret, + "space-name": "this space does not exist" }, "NON_ADMIN_APP2_SPACE1_TIMED_SIGNED": { "admin": false, @@ -93,6 +94,14 @@ export const TOKEN_GEN_TYPES = { "space_id": stackData.apps.app2.spaces.space1.id, "app_secret": stackData.apps.app2.secret, "preissued": true + }, + "NON_ADMIN_APP2_SPACE1_DUP_SIGNED": { + "admin": false, + "signed": true, + "user_id": "qateamNonAdmin", + "app_id": stackData.apps.app2.id, + "space_name": stackData.apps.app2.spaces.space1.name, + "app_secret": stackData.apps.app2.secret } }; From 91331b28c092bd21c5c1d699bf39d5a3feef636b Mon Sep 17 00:00:00 2001 From: RebeccaStankus Date: Thu, 4 Mar 2021 12:03:55 -0800 Subject: [PATCH 04/41] Working on bug when trying to create space during connection --- .../HiFiCommunicator.integration.test.ts | 78 +++++++++++++++---- .../testUtilities/integrationTestUtils.ts | 11 +-- 2 files changed, 63 insertions(+), 26 deletions(-) diff --git a/tests/integration/src/classes/HiFiCommunicator.integration.test.ts b/tests/integration/src/classes/HiFiCommunicator.integration.test.ts index 5ec1ae9b..ebec0e0e 100644 --- a/tests/integration/src/classes/HiFiCommunicator.integration.test.ts +++ b/tests/integration/src/classes/HiFiCommunicator.integration.test.ts @@ -3,6 +3,7 @@ import { HiFiCommunicator, HiFiConnectionStates, HiFiUserDataStreamingScopes } f import { TOKEN_GEN_TYPES, generateJWT } from '../../testUtilities/integrationTestUtils'; const stackData = require('../../secrets/auth.json').stackData; const hifiCommunicator = new HiFiCommunicator(); +jest.setTimeout(10000); describe('Non admin server connections', () => { let nonAdminSigned: string; @@ -23,6 +24,31 @@ describe('Non admin server connections', () => { nonAdminTimed = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_TIMED_SIGNED); nonAdminExpired = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_TIMED_EXPIRED); nonAdminDupSpaceName = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_DUP_SIGNED); + // Make sure the space we try to create later does not already exist, delet it if it does + try { + let spaceAlreadyExistsID: string; + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${adminSigned}`); + let spacesListJSON: any = {}; + spacesListJSON = await returnMessage.json(); + spacesListJSON.forEach(async (space: any) => { + if (space['name'] === TOKEN_GEN_TYPES.NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED["space_name"]) { spaceAlreadyExistsID = space['space-id']; } + }); + + // TODO handle if more than 1 already exists + if (spaceAlreadyExistsID) { + let deleteReturnMessage = await fetch(`${stackData.url}/api/v1/spaces/${spaceAlreadyExistsID}?token=${adminSigned}`, { + method: 'DELETE' + }); + let deleteReturnMessageJSON: any = {}; + deleteReturnMessageJSON = await deleteReturnMessage.json(); + + expect(deleteReturnMessageJSON['space-id']).toBe(spaceAlreadyExistsID); + console.log("EXISTING SPACE WITH SAME NAME WAS DELETED"); + } + } catch (err) { + console.error(`Unable to ensure the nonexistant space does not exist. Please check your app. ERR: ${err}`); + throw err; + } } catch (err) { console.error("Unable to create tokens in preparation for testing server connections. Please check " + "your 'auth.json' file for errors or discrepancies with your account data. ERR: ", err); @@ -107,22 +133,42 @@ describe('Non admin server connections', () => { .rejects.toMatchObject({ error: expect.stringMatching(/Unexpected server response: 501/) }); }); - // TODO AFTER pr from https://highfidelity.atlassian.net/browse/HIFI-302 is deployed - // test(`CAN create space by trying to connect with SIGNED incorrect token (nonexistant space NAME, no space ID) and correct stack URL`, async () => { - // let nonexistantSpaceName = TOKEN_GEN_TYPES.NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED["space-name"]; - // // TODO Make sure space does not exist first - // let result = hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNewSpaceName, stackData.url) - // .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); - // // check the name exists? - // try { - // await fetch(`${stackData.url}/api/v1/spaces/${nonexistantSpaceName}?token=${adminSigned}`, { - // method: 'DELETE' - // }); - // } catch (err) { - // console.error(`Unable to delete the space called ${nonexistantSpaceName} that was created for testing. Please do this manually. ERR: ${err}`); - // } - // return result; - // }); + test.only(`CAN create space by trying to connect with SIGNED token with nonexistant space NAME and no space ID and correct stack URL`, async () => { + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNewSpaceName, stackData.url) + .then(data => { + expect(data.audionetInitResponse.success).toBe(true); + }); + + // confirm that space was created and get ID for deletion + let createdSpaceID: string; + try { + let spaceWasCreated = false; + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${adminSigned}`); + let spacesListJSON: any = {}; + spacesListJSON = await returnMessage.json(); + spacesListJSON.forEach((space: any) => { + if (space['name'] === TOKEN_GEN_TYPES.NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED["space_name"]) { + spaceWasCreated = true; + createdSpaceID = space['space-id']; + } + }); + + expect(spaceWasCreated).toBe(true); + } catch (err) { + console.error(`Unable to check that a new space was created. Please check your app. ERR: ${err}`); + throw err; + } + + // delete the created space for clean up + try { + await fetch(`${stackData.url}/api/v1/spaces/${createdSpaceID}?token=${adminSigned}`, { + method: 'DELETE' + }); + } catch (err) { + console.error(`Unable to delete the space with ID ${createdSpaceID} that was created for testing. Please do this manually. ERR: ${err}`); + throw err; + } + }); test(`CANNOT connect to a space BY NAME when multiple spaces with the same name exist in the same app`, async () => { await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminDupSpaceName, stackData.url)) diff --git a/tests/integration/testUtilities/integrationTestUtils.ts b/tests/integration/testUtilities/integrationTestUtils.ts index 77892287..ca04bdff 100644 --- a/tests/integration/testUtilities/integrationTestUtils.ts +++ b/tests/integration/testUtilities/integrationTestUtils.ts @@ -66,7 +66,7 @@ export const TOKEN_GEN_TYPES = { "user_id": "qateamNonAdmin", "app_id": stackData.apps.app2.id, "app_secret": stackData.apps.app2.secret, - "space-name": "this space does not exist" + "space_name": "holding space" }, "NON_ADMIN_APP2_SPACE1_TIMED_SIGNED": { "admin": false, @@ -86,15 +86,6 @@ export const TOKEN_GEN_TYPES = { "app_secret": stackData.apps.app2.secret, "expired": true }, - "NON_ADMIN_APP2_SPACE1_TIMED_PREISSUED": { - "admin": false, - "signed": true, - "user_id": "qateamNonAdmin", - "app_id": stackData.apps.app2.id, - "space_id": stackData.apps.app2.spaces.space1.id, - "app_secret": stackData.apps.app2.secret, - "preissued": true - }, "NON_ADMIN_APP2_SPACE1_DUP_SIGNED": { "admin": false, "signed": true, From 98cccecb1b205afa1ae8adef2fbb3d7b055c1aa3 Mon Sep 17 00:00:00 2001 From: RebeccaStankus Date: Thu, 4 Mar 2021 14:01:08 -0800 Subject: [PATCH 05/41] Update fetch calls --- tests/integration/rest.integration.test.ts | 2 +- .../HiFiCommunicator.integration.test.ts | 63 ++++++++++--------- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/tests/integration/rest.integration.test.ts b/tests/integration/rest.integration.test.ts index 5d977de6..f9460710 100644 --- a/tests/integration/rest.integration.test.ts +++ b/tests/integration/rest.integration.test.ts @@ -1,7 +1,7 @@ const fetch = require('node-fetch'); import { TOKEN_GEN_TYPES, generateJWT } from './testUtilities/integrationTestUtils'; const stackData = require('./secrets/auth.json').stackData; -const stackURL = stackData.url; +const stackURL = 'https://' + stackData.url; //TODO Any of the above (delete, change / get settings, ignore / don't ignore signing) should all FAIL if the admin JWT being used is for an application that is NOT the application the space is in. (This test will fail -- i.e. the action will succeed -- right now! Yikes!!) describe('HiFi API REST Calls', () => { diff --git a/tests/integration/src/classes/HiFiCommunicator.integration.test.ts b/tests/integration/src/classes/HiFiCommunicator.integration.test.ts index ebec0e0e..35525ab9 100644 --- a/tests/integration/src/classes/HiFiCommunicator.integration.test.ts +++ b/tests/integration/src/classes/HiFiCommunicator.integration.test.ts @@ -24,36 +24,37 @@ describe('Non admin server connections', () => { nonAdminTimed = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_TIMED_SIGNED); nonAdminExpired = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_TIMED_EXPIRED); nonAdminDupSpaceName = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_DUP_SIGNED); - // Make sure the space we try to create later does not already exist, delet it if it does - try { - let spaceAlreadyExistsID: string; - let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${adminSigned}`); - let spacesListJSON: any = {}; - spacesListJSON = await returnMessage.json(); - spacesListJSON.forEach(async (space: any) => { - if (space['name'] === TOKEN_GEN_TYPES.NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED["space_name"]) { spaceAlreadyExistsID = space['space-id']; } - }); + } catch (err) { + console.error("Unable to create tokens in preparation for testing server connections. Please check " + + "your 'auth.json' file for errors or discrepancies with your account data. ERR: ", err); + throw err; + } + // Make sure the space we try to create later does not already exist, delete it if it does + try { + let spaceAlreadyExistsID: string; + let returnMessage = await fetch(`https://${stackData.url}/api/v1/spaces/?token=${adminSigned}`); + let spacesListJSON: any = {}; + spacesListJSON = await returnMessage.json(); + spacesListJSON.forEach(async (space: any) => { + if (space['name'] === TOKEN_GEN_TYPES.NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED["space_name"]) { spaceAlreadyExistsID = space['space-id']; } + }); - // TODO handle if more than 1 already exists - if (spaceAlreadyExistsID) { - let deleteReturnMessage = await fetch(`${stackData.url}/api/v1/spaces/${spaceAlreadyExistsID}?token=${adminSigned}`, { - method: 'DELETE' - }); - let deleteReturnMessageJSON: any = {}; - deleteReturnMessageJSON = await deleteReturnMessage.json(); + // TODO handle if more than 1 already exists + if (spaceAlreadyExistsID) { + let deleteReturnMessage = await fetch(`https://${stackData.url}/api/v1/spaces/${spaceAlreadyExistsID}?token=${adminSigned}`, { + method: 'DELETE' + }); + let deleteReturnMessageJSON: any = {}; + deleteReturnMessageJSON = await deleteReturnMessage.json(); - expect(deleteReturnMessageJSON['space-id']).toBe(spaceAlreadyExistsID); - console.log("EXISTING SPACE WITH SAME NAME WAS DELETED"); - } - } catch (err) { - console.error(`Unable to ensure the nonexistant space does not exist. Please check your app. ERR: ${err}`); - throw err; + expect(deleteReturnMessageJSON['space-id']).toBe(spaceAlreadyExistsID); + console.log("EXISTING SPACE WITH SAME NAME WAS DELETED"); } } catch (err) { - console.error("Unable to create tokens in preparation for testing server connections. Please check " + - "your 'auth.json' file for errors or discrepancies with your account data. ERR: ", err); + console.error(`Unable to ensure the nonexistant space does not exist. Please check your app. ERR: ${err}`); throw err; } + }); afterEach(async () => { @@ -74,7 +75,7 @@ describe('Non admin server connections', () => { test(`CAN connect with UNSIGNED correct token and correct stack URL if space ignores token signing`, async () => { // set space to allow unsigned tokens try { - await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=true`); + await fetch(`https://${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=true`); } catch (err) { console.error("Unable to set space to ignore token signing. ERR: ", err); throw err; @@ -87,7 +88,7 @@ describe('Non admin server connections', () => { test(`CANNOT connect with UNSIGNED correct token and correct stack URL if space does not ignore token signing`, async () => { // set space to not allow unsigned tokens try { - await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=false`); + await fetch(`https://${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=false`); } catch (err) { console.error("Unable to set space to ignore token signing. ERR: ", err); throw err; @@ -105,7 +106,7 @@ describe('Non admin server connections', () => { test(`CAN connect with UNSIGNED correct token and correct wss stackurl if space ignores token signing`, async () => { // set space to allow unsigned tokens try { - await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=true`); + await fetch(`https://${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=true`); } catch (err) { console.error("Unable to set space to ignore token signing. ERR: ", err); throw err; @@ -118,7 +119,7 @@ describe('Non admin server connections', () => { test(`CANNOT connect with UNSIGNED correct token and correct wss stackurl if space does not ignore token signing`, async () => { // set space to not allow unsigned tokens try { - await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=false`); + await fetch(`https://${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=false`); } catch (err) { console.error("Unable to set space to ignore token signing. ERR: ", err); throw err; @@ -133,7 +134,7 @@ describe('Non admin server connections', () => { .rejects.toMatchObject({ error: expect.stringMatching(/Unexpected server response: 501/) }); }); - test.only(`CAN create space by trying to connect with SIGNED token with nonexistant space NAME and no space ID and correct stack URL`, async () => { + test(`CAN create space by trying to connect with SIGNED token with nonexistant space NAME and no space ID and correct stack URL`, async () => { await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNewSpaceName, stackData.url) .then(data => { expect(data.audionetInitResponse.success).toBe(true); @@ -143,7 +144,7 @@ describe('Non admin server connections', () => { let createdSpaceID: string; try { let spaceWasCreated = false; - let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${adminSigned}`); + let returnMessage = await fetch(`https://${stackData.url}/api/v1/spaces/?token=${adminSigned}`); let spacesListJSON: any = {}; spacesListJSON = await returnMessage.json(); spacesListJSON.forEach((space: any) => { @@ -161,7 +162,7 @@ describe('Non admin server connections', () => { // delete the created space for clean up try { - await fetch(`${stackData.url}/api/v1/spaces/${createdSpaceID}?token=${adminSigned}`, { + await fetch(`https://${stackData.url}/api/v1/spaces/${createdSpaceID}?token=${adminSigned}`, { method: 'DELETE' }); } catch (err) { From b6685ecbb301a6093135756f273990025137b11c Mon Sep 17 00:00:00 2001 From: RebeccaStankus Date: Thu, 4 Mar 2021 15:02:07 -0800 Subject: [PATCH 06/41] Create new communicator for each test --- .../src/classes/HiFiCommunicator.integration.test.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/integration/src/classes/HiFiCommunicator.integration.test.ts b/tests/integration/src/classes/HiFiCommunicator.integration.test.ts index 35525ab9..a1cc9fad 100644 --- a/tests/integration/src/classes/HiFiCommunicator.integration.test.ts +++ b/tests/integration/src/classes/HiFiCommunicator.integration.test.ts @@ -2,7 +2,6 @@ const fetch = require('node-fetch'); import { HiFiCommunicator, HiFiConnectionStates, HiFiUserDataStreamingScopes } from "../../../../src/classes/HiFiCommunicator"; import { TOKEN_GEN_TYPES, generateJWT } from '../../testUtilities/integrationTestUtils'; const stackData = require('../../secrets/auth.json').stackData; -const hifiCommunicator = new HiFiCommunicator(); jest.setTimeout(10000); describe('Non admin server connections', () => { @@ -14,6 +13,7 @@ describe('Non admin server connections', () => { let nonAdminTimed: string; let nonAdminExpired: string; let nonAdminDupSpaceName: string; + let hifiCommunicator: HiFiCommunicator; beforeAll(async () => { try { nonAdminSigned = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP2_SPACE1_SIGNED); @@ -48,7 +48,6 @@ describe('Non admin server connections', () => { deleteReturnMessageJSON = await deleteReturnMessage.json(); expect(deleteReturnMessageJSON['space-id']).toBe(spaceAlreadyExistsID); - console.log("EXISTING SPACE WITH SAME NAME WAS DELETED"); } } catch (err) { console.error(`Unable to ensure the nonexistant space does not exist. Please check your app. ERR: ${err}`); @@ -57,6 +56,10 @@ describe('Non admin server connections', () => { }); + beforeEach( () => { + hifiCommunicator = new HiFiCommunicator(); + }) + afterEach(async () => { await hifiCommunicator.disconnectFromHiFiAudioAPIServer(); }); From 568064e88e6d4ec0fc37c763d6591cf5765bb5d6 Mon Sep 17 00:00:00 2001 From: RebeccaStankus Date: Mon, 8 Mar 2021 09:27:32 -0800 Subject: [PATCH 07/41] Save before switching to UI work --- .gitignore | 2 + tests/integration/rest.integration.test.ts | 880 ++++++++++++------ .../testUtilities/integrationTestUtils.ts | 32 + 3 files changed, 610 insertions(+), 304 deletions(-) diff --git a/.gitignore b/.gitignore index bbb0f63d..2dc036d9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ example/nodejs/node_modules/ docs/* docs.json auth.json +authProd.json +authStag.json # The contents of this file below this line were autogenerated when we created the # GitHub repository. diff --git a/tests/integration/rest.integration.test.ts b/tests/integration/rest.integration.test.ts index 2c4a69ba..1810650f 100644 --- a/tests/integration/rest.integration.test.ts +++ b/tests/integration/rest.integration.test.ts @@ -1,399 +1,671 @@ const fetch = require('node-fetch'); import { TOKEN_GEN_TYPES, generateJWT } from './testUtilities/integrationTestUtils'; const stackData = require('./secrets/auth.json').stackData; -const stackURL = stackData.url; +const stackURL = 'https://' + stackData.url; +import { HiFiCommunicator, HiFiConnectionStates } from "../../src/classes/HiFiCommunicator"; + +class TestUser { + connectionState: HiFiConnectionStates; + communicator: HiFiCommunicator; + + constructor() { + this.connectionState = HiFiConnectionStates.Disconnected; + this.communicator = new HiFiCommunicator({ onConnectionStateChanged: this.onConnectionStateChanged.bind(this) }); + } + + onConnectionStateChanged(connectionState: HiFiConnectionStates) { this.connectionState = connectionState; } +} -//TODO Any of the above (delete, change / get settings, ignore / don't ignore signing) should all FAIL if the admin JWT being used is for an application that is NOT the application the space is in. (This test will fail -- i.e. the action will succeed -- right now! Yikes!!) describe('HiFi API REST Calls', () => { let adminToken: string; let nonAdminToken: string; + let wrongAdminToken: string; + let user1token: string; + let user2token: string; + let user3token: string; + let user4token: string; beforeAll(async () => { try { adminToken = await generateJWT(TOKEN_GEN_TYPES.ADMIN_ID_APP1_SPACE1_SIGNED); nonAdminToken = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP1_SPACE1_SIGNED); + wrongAdminToken = await generateJWT(TOKEN_GEN_TYPES.ADMIN_ID_APP2_SPACE1_SIGNED); + user1token = await generateJWT(TOKEN_GEN_TYPES.USER1_APP1_SPACE1_SIGNED); + user2token = await generateJWT(TOKEN_GEN_TYPES.USER2_APP1_SPACE1_SIGNED); + user3token = await generateJWT(TOKEN_GEN_TYPES.USER3_APP1_SPACE1_SIGNED); + user4token = await generateJWT(TOKEN_GEN_TYPES.USER4_APP1_SPACE1_SIGNED); } catch (err) { - console.error("Unable to create non admin token for testing REST calls. ERR: ", err); + console.error("Unable to create tokens for testing REST calls. ERR: ", err); process.exit(); } }); - describe('Admin CAN create and delete a space', () => { - let newSpaceName = "newSpace"; - let createdSpaceJSON: any = {}; - test(`CAN create a space`, async () => { - // TODO ensure space does not already exist - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/create?token=${adminToken}&name=${newSpaceName}`); - createdSpaceJSON = await returnMessage.json(); - expect(createdSpaceJSON['space-id']).toBeDefined(); - expect(createdSpaceJSON['app-id']).toBe(stackData.apps.app1.id); - }); + describe('Creating and deleting spaces', () => { + describe('Admin CAN create and delete a space', () => { + let newSpaceName = "newSpace"; + let createdSpaceJSON: any = {}; + test(`Create a space`, async () => { + // TODO ensure space does not already exist + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/create?token=${adminToken}&name=${newSpaceName}`); + createdSpaceJSON = await returnMessage.json(); + expect(createdSpaceJSON['space-id']).toBeDefined(); + expect(createdSpaceJSON['app-id']).toBe(stackData.apps.app1.id); + }); - test(`CAN delete a space`, async () => { - // TODO ensure space already exists - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${createdSpaceJSON['space-id']}?token=${adminToken}`, { - method: 'DELETE' + test(`Delete a space`, async () => { + // TODO ensure space already exists + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${createdSpaceJSON['space-id']}?token=${adminToken}`, { + method: 'DELETE' + }); + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON['space-id']).toBe(returnMessageJSON['space-id']); + expect(returnMessageJSON['app-id']).toBe(stackData.apps.app1.id); }); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON['space-id']).toBe(returnMessageJSON['space-id']); - expect(returnMessageJSON['app-id']).toBe(stackData.apps.app1.id); }); - }); - describe('NonAdmin CANNOT create or delete a space', () => { - let newSpaceName = "someNewSpace"; - test(`Create a space`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/create?token=${nonAdminToken}&name=${newSpaceName}`) - let returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + describe('NonAdmin CANNOT create or delete a space', () => { + let newSpaceName = "someNewSpace"; + test(`Create a space`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/create?token=${nonAdminToken}&name=${newSpaceName}`) + let returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Delete a space`, async () => { + let spaceToDelete: string; + try { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/create?token=${adminToken}&name=${newSpaceName}`); + let createdSpaceJSON = await returnMessage.json(); + spaceToDelete = createdSpaceJSON['space-id']; + } catch (err) { + console.log("Cannot set up a space to test a nonadmin trying to delete a space! ERR: ", err); + } + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${spaceToDelete}?token=${nonAdminToken}`, { + method: 'DELETE' + }); + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + try { + await fetch(`${stackURL}/api/v1/spaces/${spaceToDelete}?token=${adminToken}`, { + method: 'DELETE' + }); + } catch (err) { + console.log("Cannot delete the space used to test a nonadmin trying to delete a space! ERR: ", err); + } + }); }); - test(`CAN delete a space`, async () => { - let spaceToDelete: string; - try { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/create?token=${adminToken}&name=${newSpaceName}`); - let createdSpaceJSON = await returnMessage.json(); - spaceToDelete = createdSpaceJSON['space-id']; - } catch (err) { - console.log("Cannot set up a space to test a nonadmin trying to delete a space! ERR: ", err); - } - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${spaceToDelete}?token=${nonAdminToken}`, { - method: 'DELETE' - }); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); - try { - await fetch(`${stackURL}/api/v1/spaces/${spaceToDelete}?token=${adminToken}`, { + // No need to worry about the wrong admin creating a space as the create request uses the token to get the app ID + describe('Wrong admin CANNOT delete a space', () => { + let newSpaceName = "someNewSpace"; + test(`Delete a space`, async () => { + let spaceToDelete: string; + try { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/create?token=${adminToken}&name=${newSpaceName}`); + let createdSpaceJSON = await returnMessage.json(); + spaceToDelete = createdSpaceJSON['space-id']; + } catch (err) { + console.log("Cannot set up a space to test a nonadmin trying to delete a space! ERR: ", err); + } + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${spaceToDelete}?token=${wrongAdminToken}`, { method: 'DELETE' }); - } catch (err) { - console.log("Cannot delete the space used to test a nonadmin trying to delete a space! ERR: ", err); - } + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("space/app mismatch"); + try { + await fetch(`${stackURL}/api/v1/spaces/${spaceToDelete}?token=${adminToken}`, { + method: 'DELETE' + }); + } catch (err) { + console.log("Cannot delete the space used to test a nonadmin trying to delete a space! ERR: ", err); + } + }); }); }); - describe(`Admin CAN read accurate list of spaces for an app`, () => { - test(`CAN read the list of spaces`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/?token=${adminToken}`); + describe('Reading space settings', () => { + describe(`Admin CAN read accurate list of spaces for an app`, () => { + test(`Read the list of spaces`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/?token=${adminToken}`); + + let spacesListJSON: any = {}; + spacesListJSON = await returnMessage.json(); + expect(spacesListJSON).toBeDefined(); + }); - let spacesListJSON: any = {}; - spacesListJSON = await returnMessage.json(); - expect(spacesListJSON).toBeDefined(); + test(`The list is accurate`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/?token=${adminToken}`); + + let spacesListJSON: any = {}; + spacesListJSON = await returnMessage.json(); + expect(spacesListJSON.length).toBe(Object.keys(stackData.apps.app1.spaces).length); + spacesListJSON.forEach(async (space: any) => { + let match = false; + for (var key in stackData.apps.app1.spaces) { + if (stackData.apps.app1.spaces[key].id === space['space-id']) { match = true; } + } + expect(match).toBe(true); + }); + }); }); - test(`the list is accurate`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/?token=${adminToken}`); + describe(`Non admin CANNOT read list of spaces for an app`, () => { + test(`Read the list of spaces`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/?token=${nonAdminToken}`); - let spacesListJSON: any = {}; - spacesListJSON = await returnMessage.json(); - expect(spacesListJSON.length).toBe(Object.keys(stackData.apps.app1.spaces).length); - spacesListJSON.forEach(async (space: any) => { - let match = false; - for (var key in stackData.apps.app1.spaces) { - if (stackData.apps.app1.spaces[key].id === space['space-id']) { match = true; } - } - expect(match).toBe(true); + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); }); }); - }); - describe(`Admin CAN read settings for a space`, () => { - test(`CAN read all space settings simultaneously`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}`); - - let settingsJSON: any = {}; - settingsJSON = await returnMessage.json(); - expect(settingsJSON['app-id']).toBeDefined(); - expect(settingsJSON['space-id']).toBeDefined(); - expect(settingsJSON['ignore-token-signing']).toBeDefined(); - expect(settingsJSON['name']).toBeDefined(); - expect(settingsJSON['new-connections-allowed']).toBeDefined(); - }); + describe(`Admin CAN read settings for a space`, () => { + test(`Read all space settings simultaneously`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}`); + + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + expect(settingsJSON['app-id']).toBeDefined(); + expect(settingsJSON['space-id']).toBeDefined(); + expect(settingsJSON['ignore-token-signing']).toBeDefined(); + expect(settingsJSON['name']).toBeDefined(); + expect(settingsJSON['new-connections-allowed']).toBeDefined(); + }); - test(`CAN read the 'space-id' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/space-id/?token=${adminToken}`); + test(`Read the 'space-id' setting`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/space-id/?token=${adminToken}`); - let settingsJSON: any = {}; - settingsJSON = await returnMessage.json(); - expect(settingsJSON['space-id']).toBeDefined(); - }); + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + expect(settingsJSON['space-id']).toBeDefined(); + }); - test(`CAN read the 'app-id' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/app-id/?token=${adminToken}`); + test(`Read the 'app-id' setting`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/app-id/?token=${adminToken}`); - let settingsJSON: any = {}; - settingsJSON = await returnMessage.json(); - expect(settingsJSON['app-id']).toBeDefined(); - }); + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + expect(settingsJSON['app-id']).toBeDefined(); + }); - test(`CAN read the 'ignore-token-signing' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/ignore-token-signing/?token=${adminToken}`); + test(`Read the 'ignore-token-signing' setting`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/ignore-token-signing/?token=${adminToken}`); - let settingsJSON: any = {}; - settingsJSON = await returnMessage.json(); - expect(settingsJSON['ignore-token-signing']).toBeDefined(); - }); + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + expect(settingsJSON['ignore-token-signing']).toBeDefined(); + }); - test(`CAN read the 'name' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/name/?token=${adminToken}`); + test(`Read the 'name' setting`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/name/?token=${adminToken}`); - let settingsJSON: any = {}; - settingsJSON = await returnMessage.json(); - expect(settingsJSON['name']).toBeDefined(); - }); + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + expect(settingsJSON['name']).toBeDefined(); + }); - test(`CAN read the 'new-connections-allowed' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/new-connections-allowed/?token=${adminToken}`); + test(`Read the 'new-connections-allowed' setting`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/new-connections-allowed/?token=${adminToken}`); - let settingsJSON: any = {}; - settingsJSON = await returnMessage.json(); - expect(settingsJSON['new-connections-allowed']).toBeDefined(); + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + expect(settingsJSON['new-connections-allowed']).toBeDefined(); + }); }); - }); - describe(`Non admin CANNOT read settings for a space`, () => { - test(`CANNOT read all space settings simultaneously`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}`); + describe(`Non admin CANNOT read settings for a space`, () => { + test(`Read all space settings simultaneously`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}`); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); - }); + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); - test(`CANNOT read the 'space-id' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/space-id/?token=${nonAdminToken}`); + test(`Read the 'space-id' setting`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/space-id/?token=${nonAdminToken}`); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); - }); + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); - test(`CANNOT read the 'app-id' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/app-id/?token=${nonAdminToken}`); + test(`Read the 'app-id' setting`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/app-id/?token=${nonAdminToken}`); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); - }); + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); - test(`CANNOT read the 'ignore-token-signing' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/ignore-token-signing/?token=${nonAdminToken}`); + test(`Read the 'ignore-token-signing' setting`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/ignore-token-signing/?token=${nonAdminToken}`); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); - }); + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); - test(`CANNOT read the 'name' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/name/?token=${nonAdminToken}`); + test(`Read the 'name' setting`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/name/?token=${nonAdminToken}`); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Read the 'new-connections-allowed' setting`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/new-connections-allowed/?token=${nonAdminToken}`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); }); - test(`CANNOT read the 'new-connections-allowed' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/new-connections-allowed/?token=${nonAdminToken}`); + describe(`Wrong admin CANNOT read settings for a space`, () => { + test(`Read all space settings simultaneously`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${wrongAdminToken}`); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("space/app mismatch"); + }); + + test(`Read the 'space-id' setting`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/space-id/?token=${wrongAdminToken}`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("space/app mismatch"); + }); + + test(`Read the 'app-id' setting`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/app-id/?token=${wrongAdminToken}`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("space/app mismatch"); + }); + + test(`Read the 'ignore-token-signing' setting`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/ignore-token-signing/?token=${wrongAdminToken}`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("space/app mismatch"); + }); + + test(`Read the 'name' setting`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/name/?token=${wrongAdminToken}`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("space/app mismatch"); + }); + + test(`Read the 'new-connections-allowed' setting`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/new-connections-allowed/?token=${wrongAdminToken}`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("space/app mismatch"); + }); }); }); - describe('Admin CAN change space settings', () => { - test(`CAN change multiple settings simultaneously using 'GET'`, async () => { - // preset the property to ensure its state before attempting to make changes - try { - await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=true`); - } catch (err) { - console.log("Cannot set space to allow unsigned tokens signing before testing."); - throw err; - } - let newName = "nameChanged"; - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false&name=${newName}`); - - let settingsJSON: any = {}; - settingsJSON = await returnMessage.json(); - - expect(settingsJSON['new-connections-allowed']).toBe(false); - expect(settingsJSON['name']).toBe(newName); - }); + describe('Changing space settings', () => { + describe('Admin CAN change space settings', () => { + test(`Change multiple settings simultaneously using 'GET'`, async () => { + // preset the property to ensure its state before attempting to make changes + try { + await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=true`); + } catch (err) { + console.log("Cannot set space to allow unsigned tokens signing before testing."); + throw err; + } + let newName = "nameChanged"; + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false&name=${newName}`); - test(`CAN change multiple settings simultaneously using 'POST'`, async () => { - // preset the property to ensure its state before attempting to make changes - try { - await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false`); - } catch (err) { - console.log("Cannot set space to allow unsigned tokens signing before testing."); - throw err; - } - let newName = "nameChangedAlso"; - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - 'name': newName, - 'new-connections-allowed': true - }) - }); - - let settingsJSON: any = {}; - settingsJSON = await returnMessage.json(); - - expect(settingsJSON['new-connections-allowed']).toBe(true); - expect(settingsJSON['name']).toBe(newName); - }); + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); - test(`CAN make a space not joinable`, async () => { - // preset the property to ensure its state before attempting to change it - try { - await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=true`); - } catch (err) { - console.log("Cannot make space joinable before testing."); - throw err; - } - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false`); + expect(settingsJSON['new-connections-allowed']).toBe(false); + expect(settingsJSON['name']).toBe(newName); + }); - let settingsJSON: any = {}; - settingsJSON = await returnMessage.json(); + test(`Change multiple settings simultaneously using 'POST'`, async () => { + // preset the property to ensure its state before attempting to make changes + try { + await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false`); + } catch (err) { + console.log("Cannot set space to allow unsigned tokens signing before testing."); + throw err; + } + let newName = "nameChangedAlso"; + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'name': newName, + 'new-connections-allowed': true + }) + }); - expect(settingsJSON['new-connections-allowed']).toBe(false); + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); - }); + expect(settingsJSON['new-connections-allowed']).toBe(true); + expect(settingsJSON['name']).toBe(newName); + }); - test(`CAN make a space joinable`, async () => { - // preset the property to ensure its state before attempting to change it - try { - await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false`); - } catch (err) { - console.log("Cannot make space not joinable before testing."); - throw err; - } - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=true`); + test(`Make a space not joinable`, async () => { + // preset the property to ensure its state before attempting to change it + try { + await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=true`); + } catch (err) { + console.log("Cannot make space joinable before testing."); + throw err; + } + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false`); - let settingsJSON: any = {}; - settingsJSON = await returnMessage.json(); + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); - expect(settingsJSON['new-connections-allowed']).toBe(true); - }); + expect(settingsJSON['new-connections-allowed']).toBe(false); - test(`CAN change the space name`, async () => { - let newName = "changed name"; - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&name=${newName}`); + }); - let settingsJSON: any = {}; - settingsJSON = await returnMessage.json(); + test(`Make a space joinable`, async () => { + // preset the property to ensure its state before attempting to change it + try { + await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false`); + } catch (err) { + console.log("Cannot make space not joinable before testing."); + throw err; + } + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=true`); - expect(settingsJSON['name']).toBe(newName); + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); - // restore name to default - returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&name=${stackData.apps.app1.spaces.space1.name}`); - }); + expect(settingsJSON['new-connections-allowed']).toBe(true); + }); - test(`CAN set space to allow unsigned tokens`, async () => { - // preset the property to ensure its state before attempting to change it - try { - await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=false`); - } catch (err) { - console.log("Cannot set space to disallow unsigned tokens before testing."); - throw err; - } - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=true`); - - let settingsJSON: any = {}; - settingsJSON = await returnMessage.json(); - expect(settingsJSON['ignore-token-signing']).toBe(true); - }); + test(`Change the space name`, async () => { + let newName = "changed name"; + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&name=${newName}`); - test(`CAN set space to disallow unsigned tokens`, async () => { - // preset the property to ensure its state before attempting to change it - try { - await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=true`); - } catch (err) { - console.log("Cannot set space to allow unsigned tokens signing before testing."); - throw err; - } - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=false`); - - let settingsJSON: any = {}; - settingsJSON = await returnMessage.json(); - expect(settingsJSON['ignore-token-signing']).toBe(false); - }); - }); + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); - describe('Non admin CANNOT change space settings', () => { - test(`CANNOT change multiple settings simultaneously using 'GET'`, async () => { - let newName = "nameChangedAgain"; - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&new-connections-allowed=false&name=${newName}`); + expect(settingsJSON['name']).toBe(newName); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); - }); + // restore name to default + returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&name=${stackData.apps.app1.spaces.space1.name}`); + }); - test(`CANNOT change multiple settings simultaneously using 'POST'`, async () => { - let newName = "nameChangedAgain"; - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - 'name': newName, - 'new-connections-allowed': true - }) - }); - - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); - }); + test(`Set space to allow unsigned tokens`, async () => { + // preset the property to ensure its state before attempting to change it + try { + await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=false`); + } catch (err) { + console.log("Cannot set space to disallow unsigned tokens before testing."); + throw err; + } + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=true`); - test(`CANNOT make a space not joinable`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&new-connections-allowed=false`); + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + expect(settingsJSON['ignore-token-signing']).toBe(true); + }); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + test(`Set space to disallow unsigned tokens`, async () => { + // preset the property to ensure its state before attempting to change it + try { + await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=true`); + } catch (err) { + console.log("Cannot set space to allow unsigned tokens signing before testing."); + throw err; + } + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=false`); + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + expect(settingsJSON['ignore-token-signing']).toBe(false); + }); }); - test(`CANNOT make a space joinable`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&new-connections-allowed=true`); + describe('Non admin CANNOT change space settings', () => { + test(`Change multiple settings simultaneously using 'GET'`, async () => { + let newName = "nameChangedAgain"; + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&new-connections-allowed=false&name=${newName}`); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); - }); + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Change multiple settings simultaneously using 'POST'`, async () => { + let newName = "nameChangedAgain"; + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'name': newName, + 'new-connections-allowed': true + }) + }); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Make a space not joinable`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&new-connections-allowed=false`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + + }); + + test(`Make a space joinable`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&new-connections-allowed=true`); - test(`CANNOT change the space name`, async () => { - let newName = "changed name"; - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&name=${newName}`); + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Change the space name`, async () => { + let newName = "changed name"; + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&name=${newName}`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Set space to ignore token signing`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&ignore-token-signing=true`); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Set space to not ignore token signing`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&ignore-token-signing=false`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); }); - test(`CANNOT set space to ignore token signing`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&ignore-token-signing=true`); + describe('Wrong admin CANNOT change space settings', () => { + test(`Change multiple settings simultaneously using 'GET'`, async () => { + let newName = "nameChangedAgain"; + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${wrongAdminToken}&new-connections-allowed=false&name=${newName}`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("space/app mismatch"); + }); + + test(`Change multiple settings simultaneously using 'POST'`, async () => { + let newName = "nameChangedAgain"; + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${wrongAdminToken}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'name': newName, + 'new-connections-allowed': true + }) + }); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("space/app mismatch"); + }); + + test(`Make a space not joinable`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${wrongAdminToken}&new-connections-allowed=false`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("space/app mismatch"); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Make a space joinable`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${wrongAdminToken}&new-connections-allowed=true`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("space/app mismatch"); + }); + + test(`Change the space name`, async () => { + let newName = "changed name"; + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${wrongAdminToken}&name=${newName}`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("space/app mismatch"); + }); + + test(`Set space to ignore token signing`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${wrongAdminToken}&ignore-token-signing=true`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("space/app mismatch"); + }); + + test(`Set space to not ignore token signing`, async () => { + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${wrongAdminToken}&ignore-token-signing=false`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("space/app mismatch"); + }); }); + }); - test(`CANNOT set space to not ignore token signing`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&ignore-token-signing=false`); + describe.only('Kicking users', () => { + jest.setTimeout(10000); + + let user1 = new TestUser(); + let user2 = new TestUser(); + let user3 = new TestUser(); + let user4 = new TestUser(); + beforeEach(async () => { + // if we ever change to disallow a previously kicked user (token) to connect, we will need to create unique tokens for each test + await user1.communicator.connectToHiFiAudioAPIServer(user1token, stackData.url); + await user2.communicator.connectToHiFiAudioAPIServer(user2token, stackData.url); + await user3.communicator.connectToHiFiAudioAPIServer(user3token, stackData.url); + await user4.communicator.connectToHiFiAudioAPIServer(user4token, stackData.url); + expect(user1.connectionState).toBe(HiFiConnectionStates.Connected); + expect(user2.connectionState).toBe(HiFiConnectionStates.Connected); + expect(user3.connectionState).toBe(HiFiConnectionStates.Connected); + expect(user4.connectionState).toBe(HiFiConnectionStates.Connected); + }); + + afterEach(async () => { + // disconnect communicators to avoid using too many mixers + await user1.communicator.disconnectFromHiFiAudioAPIServer(); + await user2.communicator.disconnectFromHiFiAudioAPIServer(); + await user3.communicator.disconnectFromHiFiAudioAPIServer(); + await user4.communicator.disconnectFromHiFiAudioAPIServer(); + }); + + // describe('Nonadmin CANNOT kick users', () => { + // // test(`Kick one user`, async () => { + // // let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users?token=${nonAdminToken}`, { + // // method: 'DELETE' + // // }); + // // let returnMessageJSON: any = {}; + // // returnMessageJSON = await returnMessage.json(); + // // expect(returnMessageJSON.error).toBe("token isn't an admin token"); + // // expect(user1.connectionState).toBe(HiFiConnectionStates.Connected); + // // }); + + // test(`Kick all users`, async () => { + // let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users?token=${nonAdminToken}`, { + // method: 'DELETE' + // }); + // console.log("__________________________\n", returnMessage.audionetInitResponse); + // let returnMessageJSON: any = {}; + // returnMessageJSON = await returnMessage.json(); + // expect(returnMessageJSON.error).toBe("token isn't an admin token"); + // expect(user1.connectionState).toBe(HiFiConnectionStates.Connected); + // }); + // }); + + // describe('Wrong admin CANNOT kick users', () => { + // // test(`Kick one user`, async () => { + // // }); + + // test(`Kick all users`, async () => { + // let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users?token=${wrongAdminToken}`, { + // method: 'DELETE' + // }); + // console.log("__________________________\n", returnMessage.audionetInitResponse); + // let returnMessageJSON: any = {}; + // returnMessageJSON = await returnMessage.json(); + // expect(returnMessageJSON.error).toBe("token isn't an admin token"); + // expect(user1.connectionState).toBe(HiFiConnectionStates.Connected); + // }); + // }); + + describe('Admin CAN kick users', () => { + // test(`Kick one user`, async () => { + // }); + + test(`Kick all users`, async () => { + console.log("_____________TOKEN_____________\n", adminToken); + let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users?token=${adminToken}`, { + method: 'DELETE' + }); + // let returnMessageJSON: any = {}; + // returnMessageJSON = await returnMessage.json(); + console.log("__________________________\n", returnMessage.error); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + // expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(user1.connectionState).toBe(HiFiConnectionStates.Disconnected); + }); }); }); -}); \ No newline at end of file +}); diff --git a/tests/integration/testUtilities/integrationTestUtils.ts b/tests/integration/testUtilities/integrationTestUtils.ts index ef9de71a..a1f3ec87 100644 --- a/tests/integration/testUtilities/integrationTestUtils.ts +++ b/tests/integration/testUtilities/integrationTestUtils.ts @@ -20,6 +20,38 @@ export const TOKEN_GEN_TYPES = { "space_id": stackData.apps.app1.spaces.space1.id, "app_secret": stackData.apps.app1.secret }, + "USER1_APP1_SPACE1_SIGNED": { + "admin": false, + "signed": true, + "user_id": "qa_user1", + "app_id": stackData.apps.app1.id, + "space_id": stackData.apps.app1.spaces.space1.id, + "app_secret": stackData.apps.app1.secret + }, + "USER2_APP1_SPACE1_SIGNED": { + "admin": false, + "signed": true, + "user_id": "qa_user1", + "app_id": stackData.apps.app1.id, + "space_id": stackData.apps.app1.spaces.space1.id, + "app_secret": stackData.apps.app1.secret + }, + "USER3_APP1_SPACE1_SIGNED": { + "admin": false, + "signed": true, + "user_id": "qa_user1", + "app_id": stackData.apps.app1.id, + "space_id": stackData.apps.app1.spaces.space1.id, + "app_secret": stackData.apps.app1.secret + }, + "USER4_APP1_SPACE1_SIGNED": { + "admin": false, + "signed": true, + "user_id": "qa_user1", + "app_id": stackData.apps.app1.id, + "space_id": stackData.apps.app1.spaces.space1.id, + "app_secret": stackData.apps.app1.secret + }, "ADMIN_ID_APP2_SPACE1_SIGNED": { "admin": true, "signed": true, From 427c639923c3602ed0a8b6f008b16461bd56f657 Mon Sep 17 00:00:00 2001 From: hifibuild Date: Thu, 11 Mar 2021 20:24:17 +0000 Subject: [PATCH 08/41] Bump package version to 0.5.1-3 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3b7645ea..66cc0934 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-2", + "version": "0.5.1-3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index f12d57af..62f220f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-2", + "version": "0.5.1-3", "description": "hifi-spatial-audio allows developers to integrate High Fidelity's spatial audio technology into their JavaScript projects.", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 8f7ecf99fecf03fd9f88c17f0351a5d37116e9b6 Mon Sep 17 00:00:00 2001 From: RebeccaStankus Date: Thu, 11 Mar 2021 12:52:54 -0800 Subject: [PATCH 09/41] REST calls smoke test --- jest.config.js | 4 +- tests/README.md | 41 +- tests/integration/rest.integration.test.ts | 417 +++++--------- .../HiFiCommunicator.integration.test.ts | 207 ------- .../secrets/auth_example.json | 0 tests/smoke/rest.smoke.test.ts | 514 +++++++++++++++++- tests/smoke/serverConnections.smoke.test.ts | 0 tests/testUtilities/TestUser.ts | 17 + .../testUtilities/globalTeardown.js | 0 tests/testUtilities/integrationTestUtils.ts | 174 ------ tests/testUtilities/testUtils.ts | 91 ++++ 11 files changed, 766 insertions(+), 699 deletions(-) delete mode 100644 tests/integration/src/classes/HiFiCommunicator.integration.test.ts rename tests/{integration => }/secrets/auth_example.json (100%) delete mode 100644 tests/smoke/serverConnections.smoke.test.ts create mode 100644 tests/testUtilities/TestUser.ts rename tests/{unit => }/testUtilities/globalTeardown.js (100%) delete mode 100644 tests/testUtilities/integrationTestUtils.ts create mode 100644 tests/testUtilities/testUtils.ts diff --git a/jest.config.js b/jest.config.js index c094d424..4bac306d 100644 --- a/jest.config.js +++ b/jest.config.js @@ -5,7 +5,7 @@ module.exports = { displayName: 'node', testEnvironment: 'node', testMatch: ['**.test.ts',], - globalTeardown: './tests/unit/testUtilities/globalTeardown.js', + globalTeardown: './tests/testUtilities/globalTeardown.js', moduleNameMapper: { "^jose/(.*)$": "/node_modules/jose/dist/node/cjs/$1" } @@ -15,7 +15,7 @@ module.exports = { displayName: 'dom', testEnvironment: 'jsdom', testMatch: ['**.test.dom.ts'], - globalTeardown: './tests/unit/testUtilities/globalTeardown.js', + globalTeardown: './tests/testUtilities/globalTeardown.js', moduleNameMapper: { "^jose/(.*)$": "/node_modules/jose/dist/node/cjs/$1" } diff --git a/tests/README.md b/tests/README.md index b85cfbf0..96183eda 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,19 +1,40 @@ # JEST AUTOMATED TESTING INFO -To run all tests, type `jest test` into the console. -## Unit Testing +## File structure for `tests` folder + + tests + ├── integration + ├── secrets + │ ├── auth.json (You will create this file for smoke or integration testing.) + │ └── auth.example + ├── smoke + │ └── rest.smoke.test.ts + ├── testUtilities + │ ├── testUser.ts + │ └── testUtils.ts + ├── unit + │ └── src + │ ├── classes + │ │ ├── HiFiAudioAPIData.unit.test.ts + │ │ ├── HiFiCommunicator.unit.test.ts + │ │ └── HiFiMixerSession.unit.test.ts + │ └── utilities + │ └── HiFiLogger.unit.test.ts + └── README.md + +To run all tests, type `jest test` into the console. All files in the `tests` directory and subdirectories ending in `.test.ts` will run. -To run only unit tests, type `jest unit` into the console. +## Smoke Testing + +Smoke testing will test only the most basic functions of the API using real server connections. To run only smoke tests, type `jest smoke` into the console. All files in the `tests/smoke` directory ending in `.test.ts` will run. + +## Unit Testing -...coming soon +Unit testing will test each part of the API to show that the individual functions are correct. These tests will not use real server connections and will mock any external pieces that a function relies on. To run only unit tests, type `jest unit` into the console. All files in the `tests/unit` directory and subdirectories ending in `.test.ts` will run. ## Integration Testing -To run only integration tests, type `jest integration` into the console. +Integration testing will combine different modules in the API and test as a group using actual server connections. To run only integration tests, type `jest integration` into the console. All files in the `tests/integration` directory ending in `.test.ts` will run. ### Secrets and Account Setup -Since integration tests require actual server connections (as opposed to mock functions and connections), -you will need to set up an account with some preset apps and spaces and a way to access private data -to test with. The tests will use specific names, IDs, secrets, and URLs, so create a copy of `auth.example.json` -named `auth.json` in your `secrets` folder and then replace the data to match your own account. Your -'stackData.url' will be the root URL of your account. \ No newline at end of file +Since integration and smoke tests require actual server connections (as opposed to mock functions and connections), you will need to set up an account with some preset apps and spaces and a way to access private data to test with. The tests will use specific names, IDs, secrets, and URLs, so create a copy of `auth.example.json` named `auth.json` in your `secrets` folder and then replace the data to match your own account. Your 'stackData.url' will be the root URL of your account. \ No newline at end of file diff --git a/tests/integration/rest.integration.test.ts b/tests/integration/rest.integration.test.ts index 0752df35..fae131ee 100644 --- a/tests/integration/rest.integration.test.ts +++ b/tests/integration/rest.integration.test.ts @@ -1,52 +1,43 @@ const fetch = require('node-fetch'); -import { TOKEN_GEN_TYPES, generateJWT } from '../testUtilities/integrationTestUtils'; -const stackData = require('./secrets/auth.json').stackData; -const stackURL = 'https://' + stackData.url; -import { HiFiCommunicator, HiFiConnectionStates } from "../../src/classes/HiFiCommunicator"; +const stackData = require('../secrets/auth.json').stackData; -class TestUser { - connectionState: HiFiConnectionStates; - communicator: HiFiCommunicator; - - constructor() { - this.connectionState = HiFiConnectionStates.Disconnected; - this.communicator = new HiFiCommunicator({ onConnectionStateChanged: this.onConnectionStateChanged.bind(this) }); - } - - onConnectionStateChanged(connectionState: HiFiConnectionStates) { this.connectionState = connectionState; } -} +import { TOKEN_GEN_TYPES, generateJWT, generateUUID } from '../testUtilities/testUtils'; +import { TestUser } from '../testUtilities/TestUser'; +import { HiFiConnectionStates } from "../../src/classes/HiFiCommunicator"; describe('HiFi API REST Calls', () => { - let adminToken: string; + let adminToken: string; // App 1 let nonAdminToken: string; - let wrongAdminToken: string; - let user1token: string; - let user2token: string; - let user3token: string; - let user4token: string; + let adminTokenApp2: string; beforeAll(async () => { try { adminToken = await generateJWT(TOKEN_GEN_TYPES.ADMIN_ID_APP1_SPACE1_SIGNED); nonAdminToken = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP1_SPACE1_SIGNED); - wrongAdminToken = await generateJWT(TOKEN_GEN_TYPES.ADMIN_ID_APP2_SPACE1_SIGNED); - user1token = await generateJWT(TOKEN_GEN_TYPES.USER1_APP1_SPACE1_SIGNED); - user2token = await generateJWT(TOKEN_GEN_TYPES.USER2_APP1_SPACE1_SIGNED); - user3token = await generateJWT(TOKEN_GEN_TYPES.USER3_APP1_SPACE1_SIGNED); - user4token = await generateJWT(TOKEN_GEN_TYPES.USER4_APP1_SPACE1_SIGNED); + adminTokenApp2 = await generateJWT(TOKEN_GEN_TYPES.ADMIN_ID_APP2_SPACE1_SIGNED); } catch (err) { console.error("Unable to create tokens for testing REST calls. ERR: ", err); process.exit(); } }); + beforeAll(async () => { + try { + adminToken = await generateJWT(TOKEN_GEN_TYPES.ADMIN_ID_APP1_SPACE1_SIGNED); + nonAdminToken = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP1_SPACE1_SIGNED); + } catch (err) { + console.error("Unable to create non admin token for testing REST calls. ERR: ", err); + process.exit(); + } + }); + describe('Creating and deleting spaces', () => { describe('Admin CAN create and delete a space', () => { let newSpaceName = "newSpace"; let createdSpaceJSON: any = {}; test(`Create a space`, async () => { // TODO ensure space does not already exist - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/create?token=${adminToken}&name=${newSpaceName}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/create?token=${adminToken}&name=${newSpaceName}`); createdSpaceJSON = await returnMessage.json(); expect(createdSpaceJSON['space-id']).toBeDefined(); expect(createdSpaceJSON['app-id']).toBe(stackData.apps.app1.id); @@ -54,7 +45,7 @@ describe('HiFi API REST Calls', () => { test(`Delete a space`, async () => { // TODO ensure space already exists - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${createdSpaceJSON['space-id']}?token=${adminToken}`, { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${createdSpaceJSON['space-id']}?token=${adminToken}`, { method: 'DELETE' }); let returnMessageJSON: any = {}; @@ -67,7 +58,7 @@ describe('HiFi API REST Calls', () => { describe('NonAdmin CANNOT create or delete a space', () => { let newSpaceName = "someNewSpace"; test(`Create a space`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/create?token=${nonAdminToken}&name=${newSpaceName}`) + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/create?token=${nonAdminToken}&name=${newSpaceName}`) let returnMessageJSON = await returnMessage.json(); expect(returnMessageJSON.error).toBe("token isn't an admin token"); }); @@ -75,48 +66,20 @@ describe('HiFi API REST Calls', () => { test(`Delete a space`, async () => { let spaceToDelete: string; try { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/create?token=${adminToken}&name=${newSpaceName}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/create?token=${adminToken}&name=${newSpaceName}`); let createdSpaceJSON = await returnMessage.json(); spaceToDelete = createdSpaceJSON['space-id']; } catch (err) { console.log("Cannot set up a space to test a nonadmin trying to delete a space! ERR: ", err); } - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${spaceToDelete}?token=${nonAdminToken}`, { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${spaceToDelete}?token=${nonAdminToken}`, { method: 'DELETE' }); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); expect(returnMessageJSON.error).toBe("token isn't an admin token"); try { - await fetch(`${stackURL}/api/v1/spaces/${spaceToDelete}?token=${adminToken}`, { - method: 'DELETE' - }); - } catch (err) { - console.log("Cannot delete the space used to test a nonadmin trying to delete a space! ERR: ", err); - } - }); - }); - - // No need to worry about the wrong admin creating a space as the create request uses the token to get the app ID - describe('Wrong admin CANNOT delete a space', () => { - let newSpaceName = "someNewSpace"; - test(`Delete a space`, async () => { - let spaceToDelete: string; - try { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/create?token=${adminToken}&name=${newSpaceName}`); - let createdSpaceJSON = await returnMessage.json(); - spaceToDelete = createdSpaceJSON['space-id']; - } catch (err) { - console.log("Cannot set up a space to test a nonadmin trying to delete a space! ERR: ", err); - } - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${spaceToDelete}?token=${wrongAdminToken}`, { - method: 'DELETE' - }); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("space/app mismatch"); - try { - await fetch(`${stackURL}/api/v1/spaces/${spaceToDelete}?token=${adminToken}`, { + await fetch(`${stackData.url}/api/v1/spaces/${spaceToDelete}?token=${adminToken}`, { method: 'DELETE' }); } catch (err) { @@ -126,10 +89,10 @@ describe('HiFi API REST Calls', () => { }); }); - describe('Reading space settings', () => { + describe('Reading app spaces', () => { describe(`Admin CAN read accurate list of spaces for an app`, () => { test(`Read the list of spaces`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/?token=${adminToken}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${adminToken}`); let spacesListJSON: any = {}; spacesListJSON = await returnMessage.json(); @@ -137,7 +100,7 @@ describe('HiFi API REST Calls', () => { }); test(`The list is accurate`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/?token=${adminToken}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${adminToken}`); let spacesListJSON: any = {}; spacesListJSON = await returnMessage.json(); @@ -152,19 +115,19 @@ describe('HiFi API REST Calls', () => { }); }); - describe(`Non admin CANNOT read list of spaces for an app`, () => { + describe(`Nonadmin CANNOT read list of spaces for an app`, () => { test(`Read the list of spaces`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/?token=${nonAdminToken}`); - - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${adminToken}`); + let returnMessageJSON = await returnMessage.json(); expect(returnMessageJSON.error).toBe("token isn't an admin token"); }); }); + }); + describe('Reading space settings', () => { describe(`Admin CAN read settings for a space`, () => { test(`Read all space settings simultaneously`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}`); let settingsJSON: any = {}; settingsJSON = await returnMessage.json(); @@ -176,7 +139,7 @@ describe('HiFi API REST Calls', () => { }); test(`Read the 'space-id' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/space-id/?token=${adminToken}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/space-id/?token=${adminToken}`); let settingsJSON: any = {}; settingsJSON = await returnMessage.json(); @@ -184,7 +147,7 @@ describe('HiFi API REST Calls', () => { }); test(`Read the 'app-id' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/app-id/?token=${adminToken}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/app-id/?token=${adminToken}`); let settingsJSON: any = {}; settingsJSON = await returnMessage.json(); @@ -192,7 +155,7 @@ describe('HiFi API REST Calls', () => { }); test(`Read the 'ignore-token-signing' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/ignore-token-signing/?token=${adminToken}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/ignore-token-signing/?token=${adminToken}`); let settingsJSON: any = {}; settingsJSON = await returnMessage.json(); @@ -200,7 +163,7 @@ describe('HiFi API REST Calls', () => { }); test(`Read the 'name' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/name/?token=${adminToken}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/name/?token=${adminToken}`); let settingsJSON: any = {}; settingsJSON = await returnMessage.json(); @@ -208,7 +171,7 @@ describe('HiFi API REST Calls', () => { }); test(`Read the 'new-connections-allowed' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/new-connections-allowed/?token=${adminToken}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/new-connections-allowed/?token=${adminToken}`); let settingsJSON: any = {}; settingsJSON = await returnMessage.json(); @@ -218,7 +181,7 @@ describe('HiFi API REST Calls', () => { describe(`Non admin CANNOT read settings for a space`, () => { test(`Read all space settings simultaneously`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}`); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); @@ -226,7 +189,7 @@ describe('HiFi API REST Calls', () => { }); test(`Read the 'space-id' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/space-id/?token=${nonAdminToken}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/space-id/?token=${nonAdminToken}`); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); @@ -234,7 +197,7 @@ describe('HiFi API REST Calls', () => { }); test(`Read the 'app-id' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/app-id/?token=${nonAdminToken}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/app-id/?token=${nonAdminToken}`); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); @@ -242,7 +205,7 @@ describe('HiFi API REST Calls', () => { }); test(`Read the 'ignore-token-signing' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/ignore-token-signing/?token=${nonAdminToken}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/ignore-token-signing/?token=${nonAdminToken}`); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); @@ -250,7 +213,7 @@ describe('HiFi API REST Calls', () => { }); test(`Read the 'name' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/name/?token=${nonAdminToken}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/name/?token=${nonAdminToken}`); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); @@ -258,63 +221,13 @@ describe('HiFi API REST Calls', () => { }); test(`Read the 'new-connections-allowed' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/new-connections-allowed/?token=${nonAdminToken}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/new-connections-allowed/?token=${nonAdminToken}`); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); expect(returnMessageJSON.error).toBe("token isn't an admin token"); }); }); - - describe(`Wrong admin CANNOT read settings for a space`, () => { - test(`Read all space settings simultaneously`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${wrongAdminToken}`); - - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("space/app mismatch"); - }); - - test(`Read the 'space-id' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/space-id/?token=${wrongAdminToken}`); - - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("space/app mismatch"); - }); - - test(`Read the 'app-id' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/app-id/?token=${wrongAdminToken}`); - - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("space/app mismatch"); - }); - - test(`Read the 'ignore-token-signing' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/ignore-token-signing/?token=${wrongAdminToken}`); - - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("space/app mismatch"); - }); - - test(`Read the 'name' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/name/?token=${wrongAdminToken}`); - - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("space/app mismatch"); - }); - - test(`Read the 'new-connections-allowed' setting`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/new-connections-allowed/?token=${wrongAdminToken}`); - - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("space/app mismatch"); - }); - }); }); describe('Changing space settings', () => { @@ -322,13 +235,13 @@ describe('HiFi API REST Calls', () => { test(`Change multiple settings simultaneously using 'GET'`, async () => { // preset the property to ensure its state before attempting to make changes try { - await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=true`); + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=true`); } catch (err) { console.log("Cannot set space to allow unsigned tokens signing before testing."); throw err; } let newName = "nameChanged"; - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false&name=${newName}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false&name=${newName}`); let settingsJSON: any = {}; settingsJSON = await returnMessage.json(); @@ -340,13 +253,13 @@ describe('HiFi API REST Calls', () => { test(`Change multiple settings simultaneously using 'POST'`, async () => { // preset the property to ensure its state before attempting to make changes try { - await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false`); + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false`); } catch (err) { console.log("Cannot set space to allow unsigned tokens signing before testing."); throw err; } let newName = "nameChangedAlso"; - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}`, { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}`, { method: 'POST', headers: { 'Content-Type': 'application/json' @@ -367,12 +280,12 @@ describe('HiFi API REST Calls', () => { test(`Make a space not joinable`, async () => { // preset the property to ensure its state before attempting to change it try { - await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=true`); + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=true`); } catch (err) { console.log("Cannot make space joinable before testing."); throw err; } - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false`); let settingsJSON: any = {}; settingsJSON = await returnMessage.json(); @@ -384,12 +297,12 @@ describe('HiFi API REST Calls', () => { test(`Make a space joinable`, async () => { // preset the property to ensure its state before attempting to change it try { - await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false`); + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false`); } catch (err) { console.log("Cannot make space not joinable before testing."); throw err; } - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=true`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=true`); let settingsJSON: any = {}; settingsJSON = await returnMessage.json(); @@ -399,7 +312,7 @@ describe('HiFi API REST Calls', () => { test(`Change the space name`, async () => { let newName = "changed name"; - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&name=${newName}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&name=${newName}`); let settingsJSON: any = {}; settingsJSON = await returnMessage.json(); @@ -407,18 +320,18 @@ describe('HiFi API REST Calls', () => { expect(settingsJSON['name']).toBe(newName); // restore name to default - returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&name=${stackData.apps.app1.spaces.space1.name}`); + returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&name=${stackData.apps.app1.spaces.space1.name}`); }); test(`Set space to allow unsigned tokens`, async () => { // preset the property to ensure its state before attempting to change it try { - await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=false`); + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=false`); } catch (err) { console.log("Cannot set space to disallow unsigned tokens before testing."); throw err; } - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=true`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=true`); let settingsJSON: any = {}; settingsJSON = await returnMessage.json(); @@ -428,12 +341,12 @@ describe('HiFi API REST Calls', () => { test(`Set space to disallow unsigned tokens`, async () => { // preset the property to ensure its state before attempting to change it try { - await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=true`); + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=true`); } catch (err) { console.log("Cannot set space to allow unsigned tokens signing before testing."); throw err; } - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=false`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=false`); let settingsJSON: any = {}; settingsJSON = await returnMessage.json(); @@ -444,7 +357,7 @@ describe('HiFi API REST Calls', () => { describe('Non admin CANNOT change space settings', () => { test(`Change multiple settings simultaneously using 'GET'`, async () => { let newName = "nameChangedAgain"; - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&new-connections-allowed=false&name=${newName}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&new-connections-allowed=false&name=${newName}`); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); @@ -453,7 +366,7 @@ describe('HiFi API REST Calls', () => { test(`Change multiple settings simultaneously using 'POST'`, async () => { let newName = "nameChangedAgain"; - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}`, { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}`, { method: 'POST', headers: { 'Content-Type': 'application/json' @@ -470,7 +383,7 @@ describe('HiFi API REST Calls', () => { }); test(`Make a space not joinable`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&new-connections-allowed=false`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&new-connections-allowed=false`); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); @@ -479,7 +392,7 @@ describe('HiFi API REST Calls', () => { }); test(`Make a space joinable`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&new-connections-allowed=true`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&new-connections-allowed=true`); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); @@ -488,7 +401,7 @@ describe('HiFi API REST Calls', () => { test(`Change the space name`, async () => { let newName = "changed name"; - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&name=${newName}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&name=${newName}`); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); @@ -496,7 +409,7 @@ describe('HiFi API REST Calls', () => { }); test(`Set space to ignore token signing`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&ignore-token-signing=true`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&ignore-token-signing=true`); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); @@ -504,167 +417,103 @@ describe('HiFi API REST Calls', () => { }); test(`Set space to not ignore token signing`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&ignore-token-signing=false`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&ignore-token-signing=false`); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); expect(returnMessageJSON.error).toBe("token isn't an admin token"); }); }); + }); - describe('Wrong admin CANNOT change space settings', () => { - test(`Change multiple settings simultaneously using 'GET'`, async () => { - let newName = "nameChangedAgain"; - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${wrongAdminToken}&new-connections-allowed=false&name=${newName}`); - - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("space/app mismatch"); - }); - - test(`Change multiple settings simultaneously using 'POST'`, async () => { - let newName = "nameChangedAgain"; - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${wrongAdminToken}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - 'name': newName, - 'new-connections-allowed': true - }) - }); + describe('Kicking users', () => { + const numberTestUsers = 1; + let testUsers: Array = []; - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("space/app mismatch"); - }); - - test(`Make a space not joinable`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${wrongAdminToken}&new-connections-allowed=false`); + beforeAll(async () => { + jest.setTimeout(10000); // these tests need longer to complete + }); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("space/app mismatch"); + afterAll(async () => { + jest.setTimeout(5000); // restore to default + }); - }); + beforeEach(async () => { + testUsers = []; + for (let i = 0; i < numberTestUsers; i++) { + let tokenData = TOKEN_GEN_TYPES.USER_APP1_SPACE1_SIGNED + tokenData['user_id'] = generateUUID(); + testUsers.push(new TestUser(tokenData['user_id'])); + let token = await generateJWT(tokenData); + await testUsers[i].communicator.connectToHiFiAudioAPIServer(token, stackData.url); + expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Connected); + } + }); - test(`Make a space joinable`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${wrongAdminToken}&new-connections-allowed=true`); + afterEach(async () => { + // disconnect communicators to avoid using too many mixers + for (let i = 0; i < numberTestUsers; i++) { + await testUsers[i].communicator.disconnectFromHiFiAudioAPIServer(); + expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Disconnected); + } + }); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("space/app mismatch"); + describe('Admin CAN kick users', () => { + test(`Kick one user`, async () => { + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users${testUsers[0]['user_id']}?token=${adminToken}`, { + method: 'DELETE' + }); + for (let i = 0; i < numberTestUsers; i++) { + if (i === 0) expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Disconnected); + else expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Connected); + } }); - test(`Change the space name`, async () => { - let newName = "changed name"; - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${wrongAdminToken}&name=${newName}`); - - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("space/app mismatch"); + test(`Kick all users`, async () => { + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users?token=${adminToken}`, { + method: 'DELETE' + }); + for (let i = 0; i < numberTestUsers; i++) { + expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Disconnected); + } }); + }); - test(`Set space to ignore token signing`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${wrongAdminToken}&ignore-token-signing=true`); - + describe('Nonadmin CANNOT kick users', () => { + test(`Kick one user`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users/${testUsers[0]['user_id']}?token=${nonAdminToken}`, { + method: 'DELETE' + }); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("space/app mismatch"); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + for (let i = 0; i < numberTestUsers; i++) { + expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Connected); + } }); - test(`Set space to not ignore token signing`, async () => { - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${wrongAdminToken}&ignore-token-signing=false`); - + test(`Kick all users`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users?token=${nonAdminToken}`, { + method: 'DELETE' + }); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("space/app mismatch"); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + for (let i = 0; i < numberTestUsers; i++) { + expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Connected); + } }); }); }); - describe.only('Kicking users', () => { - jest.setTimeout(10000); - - let user1 = new TestUser(); - let user2 = new TestUser(); - let user3 = new TestUser(); - let user4 = new TestUser(); - beforeEach(async () => { - // if we ever change to disallow a previously kicked user (token) to connect, we will need to create unique tokens for each test - await user1.communicator.connectToHiFiAudioAPIServer(user1token, stackData.url); - await user2.communicator.connectToHiFiAudioAPIServer(user2token, stackData.url); - await user3.communicator.connectToHiFiAudioAPIServer(user3token, stackData.url); - await user4.communicator.connectToHiFiAudioAPIServer(user4token, stackData.url); - expect(user1.connectionState).toBe(HiFiConnectionStates.Connected); - expect(user2.connectionState).toBe(HiFiConnectionStates.Connected); - expect(user3.connectionState).toBe(HiFiConnectionStates.Connected); - expect(user4.connectionState).toBe(HiFiConnectionStates.Connected); - }); - - afterEach(async () => { - // disconnect communicators to avoid using too many mixers - await user1.communicator.disconnectFromHiFiAudioAPIServer(); - await user2.communicator.disconnectFromHiFiAudioAPIServer(); - await user3.communicator.disconnectFromHiFiAudioAPIServer(); - await user4.communicator.disconnectFromHiFiAudioAPIServer(); - }); + describe('Wrong admin tokens', () => { + describe(`CANNOT read/alter App A by using a valid admin token for App B`, () => { + test(`Read settings for a space`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/app-id/?token=${adminTokenApp2}`); - // describe('Nonadmin CANNOT kick users', () => { - // // test(`Kick one user`, async () => { - // // let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users?token=${nonAdminToken}`, { - // // method: 'DELETE' - // // }); - // // let returnMessageJSON: any = {}; - // // returnMessageJSON = await returnMessage.json(); - // // expect(returnMessageJSON.error).toBe("token isn't an admin token"); - // // expect(user1.connectionState).toBe(HiFiConnectionStates.Connected); - // // }); - - // test(`Kick all users`, async () => { - // let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users?token=${nonAdminToken}`, { - // method: 'DELETE' - // }); - // console.log("__________________________\n", returnMessage.audionetInitResponse); - // let returnMessageJSON: any = {}; - // returnMessageJSON = await returnMessage.json(); - // expect(returnMessageJSON.error).toBe("token isn't an admin token"); - // expect(user1.connectionState).toBe(HiFiConnectionStates.Connected); - // }); - // }); - - // describe('Wrong admin CANNOT kick users', () => { - // // test(`Kick one user`, async () => { - // // }); - - // test(`Kick all users`, async () => { - // let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users?token=${wrongAdminToken}`, { - // method: 'DELETE' - // }); - // console.log("__________________________\n", returnMessage.audionetInitResponse); - // let returnMessageJSON: any = {}; - // returnMessageJSON = await returnMessage.json(); - // expect(returnMessageJSON.error).toBe("token isn't an admin token"); - // expect(user1.connectionState).toBe(HiFiConnectionStates.Connected); - // }); - // }); - - describe('Admin CAN kick users', () => { - // test(`Kick one user`, async () => { - // }); - - test(`Kick all users`, async () => { - console.log("_____________TOKEN_____________\n", adminToken); - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users?token=${adminToken}`, { - method: 'DELETE' - }); - // let returnMessageJSON: any = {}; - // returnMessageJSON = await returnMessage.json(); - console.log("__________________________\n", returnMessage.error); - - // expect(returnMessageJSON.error).toBe("token isn't an admin token"); - expect(user1.connectionState).toBe(HiFiConnectionStates.Disconnected); + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe('space/app mismatch'); }); }); }); diff --git a/tests/integration/src/classes/HiFiCommunicator.integration.test.ts b/tests/integration/src/classes/HiFiCommunicator.integration.test.ts deleted file mode 100644 index 712afcc6..00000000 --- a/tests/integration/src/classes/HiFiCommunicator.integration.test.ts +++ /dev/null @@ -1,207 +0,0 @@ -const fetch = require('node-fetch'); -import { HiFiCommunicator, HiFiConnectionStates, HiFiUserDataStreamingScopes } from "../../../../src/classes/HiFiCommunicator"; -import { TOKEN_GEN_TYPES, generateJWT } from '../../../testUtilities/integrationTestUtils'; -const stackData = require('../../secrets/auth.json').stackData; -jest.setTimeout(10000); - -describe('Non admin server connections', () => { - let nonAdminSigned: string; - let adminSigned: string; - let nonAdminUnsigned: string; - let nonAdminNonexistantSpaceID: string; - let nonAdminNewSpaceName: string; - let nonAdminTimed: string; - let nonAdminExpired: string; - let nonAdminDupSpaceName: string; - let hifiCommunicator: HiFiCommunicator; - beforeAll(async () => { - try { - nonAdminSigned = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP2_SPACE1_SIGNED); - adminSigned = await generateJWT(TOKEN_GEN_TYPES.ADMIN_ID_APP2_SPACE1_SIGNED); - nonAdminUnsigned = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP2_SPACE1_UNSIGNED); - nonAdminNonexistantSpaceID = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE_ID_NONEXISTANT_SIGNED); - nonAdminNewSpaceName = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED); - nonAdminTimed = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_TIMED_SIGNED); - nonAdminExpired = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_TIMED_EXPIRED); - nonAdminDupSpaceName = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_DUP_SIGNED); - } catch (err) { - console.error("Unable to create tokens in preparation for testing server connections. Please check " + - "your 'auth.json' file for errors or discrepancies with your account data. ERR: ", err); - throw err; - } - // Make sure the space we try to create later does not already exist, delete it if it does - try { - let spaceAlreadyExistsID: string; - let returnMessage = await fetch(`https://${stackData.url}/api/v1/spaces/?token=${adminSigned}`); - let spacesListJSON: any = {}; - spacesListJSON = await returnMessage.json(); - spacesListJSON.forEach(async (space: any) => { - if (space['name'] === TOKEN_GEN_TYPES.NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED["space_name"]) { spaceAlreadyExistsID = space['space-id']; } - }); - - // TODO handle if more than 1 already exists - if (spaceAlreadyExistsID) { - let deleteReturnMessage = await fetch(`https://${stackData.url}/api/v1/spaces/${spaceAlreadyExistsID}?token=${adminSigned}`, { - method: 'DELETE' - }); - let deleteReturnMessageJSON: any = {}; - deleteReturnMessageJSON = await deleteReturnMessage.json(); - - expect(deleteReturnMessageJSON['space-id']).toBe(spaceAlreadyExistsID); - } - } catch (err) { - console.error(`Unable to ensure the nonexistant space does not exist. Please check your app. ERR: ${err}`); - throw err; - } - - }); - - beforeEach( () => { - hifiCommunicator = new HiFiCommunicator(); - }) - - afterEach(async () => { - await hifiCommunicator.disconnectFromHiFiAudioAPIServer(); - }); - - test(`CAN connect with SIGNED correct token and correct stack URL`, async () => { - await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminSigned, stackData.url) - .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); - }); - - test(`CANNOT connect with SIGNED correct token and incorrect stack URL`, async () => { - // append the stack url twice to create a stack name that does not exist - await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminSigned, stackData.url + stackData.url)) - .rejects.toMatchObject({ error: expect.stringMatching(/getaddrinfo ENOTFOUND/) }); - }); - - test(`CAN connect with UNSIGNED correct token and correct stack URL if space ignores token signing`, async () => { - // set space to allow unsigned tokens - try { - await fetch(`https://${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=true`); - } catch (err) { - console.error("Unable to set space to ignore token signing. ERR: ", err); - throw err; - } - - await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminUnsigned, stackData.url) - .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); - }); - - test(`CANNOT connect with UNSIGNED correct token and correct stack URL if space does not ignore token signing`, async () => { - // set space to not allow unsigned tokens - try { - await fetch(`https://${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=false`); - } catch (err) { - console.error("Unable to set space to ignore token signing. ERR: ", err); - throw err; - } - - await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminUnsigned, stackData.url)) - .rejects.toMatchObject({ error: expect.stringMatching(/Unexpected server response: 501/) }); - }); - - test(`CAN connect with SIGNED correct token and correct wss stackurl`, async () => { - await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminSigned, stackData.wss + "?token=") - .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); - }); - - test(`CAN connect with UNSIGNED correct token and correct wss stackurl if space ignores token signing`, async () => { - // set space to allow unsigned tokens - try { - await fetch(`https://${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=true`); - } catch (err) { - console.error("Unable to set space to ignore token signing. ERR: ", err); - throw err; - } - - await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminUnsigned, stackData.wss + "?token=") - .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); - }); - - test(`CANNOT connect with UNSIGNED correct token and correct wss stackurl if space does not ignore token signing`, async () => { - // set space to not allow unsigned tokens - try { - await fetch(`https://${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=false`); - } catch (err) { - console.error("Unable to set space to ignore token signing. ERR: ", err); - throw err; - } - - await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminUnsigned, stackData.wss + "?token=")) - .rejects.toMatchObject({ error: expect.stringMatching(/Unexpected server response: 501/) }); - }); - - test(`CANNOT connect with SIGNED incorrect token (nonexistant space ID) and correct stack URL`, async () => { - await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNonexistantSpaceID, stackData.url)) - .rejects.toMatchObject({ error: expect.stringMatching(/Unexpected server response: 501/) }); - }); - - test(`CAN create space by trying to connect with SIGNED token with nonexistant space NAME and no space ID and correct stack URL`, async () => { - await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNewSpaceName, stackData.url) - .then(data => { - expect(data.audionetInitResponse.success).toBe(true); - }); - - // confirm that space was created and get ID for deletion - let createdSpaceID: string; - try { - let spaceWasCreated = false; - let returnMessage = await fetch(`https://${stackData.url}/api/v1/spaces/?token=${adminSigned}`); - let spacesListJSON: any = {}; - spacesListJSON = await returnMessage.json(); - spacesListJSON.forEach((space: any) => { - if (space['name'] === TOKEN_GEN_TYPES.NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED["space_name"]) { - spaceWasCreated = true; - createdSpaceID = space['space-id']; - } - }); - - expect(spaceWasCreated).toBe(true); - } catch (err) { - console.error(`Unable to check that a new space was created. Please check your app. ERR: ${err}`); - throw err; - } - - // delete the created space for clean up - try { - await fetch(`https://${stackData.url}/api/v1/spaces/${createdSpaceID}?token=${adminSigned}`, { - method: 'DELETE' - }); - } catch (err) { - console.error(`Unable to delete the space with ID ${createdSpaceID} that was created for testing. Please do this manually. ERR: ${err}`); - throw err; - } - }); - - test(`CANNOT connect to a space BY NAME when multiple spaces with the same name exist in the same app`, async () => { - await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminDupSpaceName, stackData.url)) - .rejects.toMatchObject({ - error: expect.stringMatching(/Unexpected server response: 501/) - // for when the new log message gets merged - // error: expect.stringMatching(/multiple spaces with given name/) - }); - }); - - test(`CAN connect to a space using a timed token before the token expires`, async () => { - await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminTimed, stackData.url) - .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); - }); - - test(`CANNOT connect to a space using a timed token after the token expires`, async () => { - await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminExpired, stackData.url)) - .rejects.toMatchObject({ error: expect.stringMatching(/Unexpected server response: 501/) }); - }); - - test(`CAN disconnect once connected`, async () => { - // make sure we're connected first - try { - await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminSigned, stackData.url) - } catch (err) { - console.error("Unable to connect before testing disconnect. ERR: ", err); - throw err; - } - await hifiCommunicator.disconnectFromHiFiAudioAPIServer() - .then((data) => { expect(data).toBe('Successfully disconnected.') }); - }); -}); diff --git a/tests/integration/secrets/auth_example.json b/tests/secrets/auth_example.json similarity index 100% rename from tests/integration/secrets/auth_example.json rename to tests/secrets/auth_example.json diff --git a/tests/smoke/rest.smoke.test.ts b/tests/smoke/rest.smoke.test.ts index 51cb7219..c6ed0262 100644 --- a/tests/smoke/rest.smoke.test.ts +++ b/tests/smoke/rest.smoke.test.ts @@ -1,11 +1,25 @@ const fetch = require('node-fetch'); -import { TOKEN_GEN_TYPES, generateJWT } from '../testUtilities/integrationTestUtils'; -const stackData = require('./secrets/auth.json').stackData; -const stackURL = 'https://' + stackData.url; +const stackData = require('../secrets/auth.json').stackData; + +import { TOKEN_GEN_TYPES, generateJWT, generateUUID } from '../testUtilities/testUtils'; +import { TestUser } from '../testUtilities/TestUser'; +import { HiFiConnectionStates } from "../../src/classes/HiFiCommunicator"; describe('HiFi API REST Calls', () => { - let adminToken: string; + let adminToken: string; // App 1 let nonAdminToken: string; + let adminTokenApp2: string; + + beforeAll(async () => { + try { + adminToken = await generateJWT(TOKEN_GEN_TYPES.ADMIN_ID_APP1_SPACE1_SIGNED); + nonAdminToken = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP1_SPACE1_SIGNED); + adminTokenApp2 = await generateJWT(TOKEN_GEN_TYPES.ADMIN_ID_APP2_SPACE1_SIGNED); + } catch (err) { + console.error("Unable to create tokens for testing REST calls. ERR: ", err); + process.exit(); + } + }); beforeAll(async () => { try { @@ -17,26 +31,482 @@ describe('HiFi API REST Calls', () => { } }); - describe('Admin CAN create and delete a space', () => { - let newSpaceName = "newSpace"; - let createdSpaceJSON: any = {}; - test(`CAN create a space`, async () => { - // TODO ensure space does not already exist - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/create?token=${adminToken}&name=${newSpaceName}`); - createdSpaceJSON = await returnMessage.json(); - expect(createdSpaceJSON['space-id']).toBeDefined(); - expect(createdSpaceJSON['app-id']).toBe(stackData.apps.app1.id); + describe('Creating and deleting spaces', () => { + describe('Admin CAN create and delete a space', () => { + let newSpaceName = "newSpace"; + let createdSpaceJSON: any = {}; + test(`Create a space`, async () => { + // TODO ensure space does not already exist + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/create?token=${adminToken}&name=${newSpaceName}`); + createdSpaceJSON = await returnMessage.json(); + expect(createdSpaceJSON['space-id']).toBeDefined(); + expect(createdSpaceJSON['app-id']).toBe(stackData.apps.app1.id); + }); + + test(`Delete a space`, async () => { + // TODO ensure space already exists + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${createdSpaceJSON['space-id']}?token=${adminToken}`, { + method: 'DELETE' + }); + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON['space-id']).toBe(returnMessageJSON['space-id']); + expect(returnMessageJSON['app-id']).toBe(stackData.apps.app1.id); + }); + }); + + describe('NonAdmin CANNOT create or delete a space', () => { + let newSpaceName = "someNewSpace"; + test(`Create a space`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/create?token=${nonAdminToken}&name=${newSpaceName}`) + let returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Delete a space`, async () => { + let spaceToDelete: string; + try { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/create?token=${adminToken}&name=${newSpaceName}`); + let createdSpaceJSON = await returnMessage.json(); + spaceToDelete = createdSpaceJSON['space-id']; + } catch (err) { + console.log("Cannot set up a space to test a nonadmin trying to delete a space! ERR: ", err); + } + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${spaceToDelete}?token=${nonAdminToken}`, { + method: 'DELETE' + }); + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + try { + await fetch(`${stackData.url}/api/v1/spaces/${spaceToDelete}?token=${adminToken}`, { + method: 'DELETE' + }); + } catch (err) { + console.log("Cannot delete the space used to test a nonadmin trying to delete a space! ERR: ", err); + } + }); + }); + }); + + describe('Reading app spaces', () => { + describe(`Admin CAN read accurate list of spaces for an app`, () => { + test(`Read the list of spaces`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${adminToken}`); + + let spacesListJSON: any = {}; + spacesListJSON = await returnMessage.json(); + expect(spacesListJSON).toBeDefined(); + }); + + test(`The list is accurate`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${adminToken}`); + + let spacesListJSON: any = {}; + spacesListJSON = await returnMessage.json(); + expect(spacesListJSON.length).toBe(Object.keys(stackData.apps.app1.spaces).length); + spacesListJSON.forEach(async (space: any) => { + let match = false; + for (var key in stackData.apps.app1.spaces) { + if (stackData.apps.app1.spaces[key].id === space['space-id']) { match = true; } + } + expect(match).toBe(true); + }); + }); + }); + }); + + describe('Reading space settings', () => { + describe(`Admin CAN read settings for a space`, () => { + test(`Read all space settings simultaneously`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}`); + + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + expect(settingsJSON['app-id']).toBeDefined(); + expect(settingsJSON['space-id']).toBeDefined(); + expect(settingsJSON['ignore-token-signing']).toBeDefined(); + expect(settingsJSON['name']).toBeDefined(); + expect(settingsJSON['new-connections-allowed']).toBeDefined(); + }); + + test(`Read the 'space-id' setting`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/space-id/?token=${adminToken}`); + + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + expect(settingsJSON['space-id']).toBeDefined(); + }); + + test(`Read the 'app-id' setting`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/app-id/?token=${adminToken}`); + + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + expect(settingsJSON['app-id']).toBeDefined(); + }); + + test(`Read the 'ignore-token-signing' setting`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/ignore-token-signing/?token=${adminToken}`); + + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + expect(settingsJSON['ignore-token-signing']).toBeDefined(); + }); + + test(`Read the 'name' setting`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/name/?token=${adminToken}`); + + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + expect(settingsJSON['name']).toBeDefined(); + }); + + test(`Read the 'new-connections-allowed' setting`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/new-connections-allowed/?token=${adminToken}`); + + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + expect(settingsJSON['new-connections-allowed']).toBeDefined(); + }); + }); + + describe(`Non admin CANNOT read settings for a space`, () => { + test(`Read all space settings simultaneously`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Read the 'space-id' setting`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/space-id/?token=${nonAdminToken}`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Read the 'app-id' setting`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/app-id/?token=${nonAdminToken}`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Read the 'ignore-token-signing' setting`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/ignore-token-signing/?token=${nonAdminToken}`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Read the 'name' setting`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/name/?token=${nonAdminToken}`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Read the 'new-connections-allowed' setting`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/new-connections-allowed/?token=${nonAdminToken}`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + }); + }); + + describe('Changing space settings', () => { + describe('Admin CAN change space settings', () => { + test(`Change multiple settings simultaneously using 'GET'`, async () => { + // preset the property to ensure its state before attempting to make changes + try { + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=true`); + } catch (err) { + console.log("Cannot set space to allow unsigned tokens signing before testing."); + throw err; + } + let newName = "nameChanged"; + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false&name=${newName}`); + + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + + expect(settingsJSON['new-connections-allowed']).toBe(false); + expect(settingsJSON['name']).toBe(newName); + }); + + test(`Change multiple settings simultaneously using 'POST'`, async () => { + // preset the property to ensure its state before attempting to make changes + try { + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false`); + } catch (err) { + console.log("Cannot set space to allow unsigned tokens signing before testing."); + throw err; + } + let newName = "nameChangedAlso"; + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'name': newName, + 'new-connections-allowed': true + }) + }); + + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + + expect(settingsJSON['new-connections-allowed']).toBe(true); + expect(settingsJSON['name']).toBe(newName); + }); + + test(`Make a space not joinable`, async () => { + // preset the property to ensure its state before attempting to change it + try { + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=true`); + } catch (err) { + console.log("Cannot make space joinable before testing."); + throw err; + } + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false`); + + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + + expect(settingsJSON['new-connections-allowed']).toBe(false); + + }); + + test(`Make a space joinable`, async () => { + // preset the property to ensure its state before attempting to change it + try { + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=false`); + } catch (err) { + console.log("Cannot make space not joinable before testing."); + throw err; + } + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&new-connections-allowed=true`); + + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + + expect(settingsJSON['new-connections-allowed']).toBe(true); + }); + + test(`Change the space name`, async () => { + let newName = "changed name"; + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&name=${newName}`); + + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + + expect(settingsJSON['name']).toBe(newName); + + // restore name to default + returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&name=${stackData.apps.app1.spaces.space1.name}`); + }); + + test(`Set space to allow unsigned tokens`, async () => { + // preset the property to ensure its state before attempting to change it + try { + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=false`); + } catch (err) { + console.log("Cannot set space to disallow unsigned tokens before testing."); + throw err; + } + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=true`); + + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + expect(settingsJSON['ignore-token-signing']).toBe(true); + }); + + test(`Set space to disallow unsigned tokens`, async () => { + // preset the property to ensure its state before attempting to change it + try { + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=true`); + } catch (err) { + console.log("Cannot set space to allow unsigned tokens signing before testing."); + throw err; + } + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${adminToken}&ignore-token-signing=false`); + + let settingsJSON: any = {}; + settingsJSON = await returnMessage.json(); + expect(settingsJSON['ignore-token-signing']).toBe(false); + }); + }); + + describe('Non admin CANNOT change space settings', () => { + test(`Change multiple settings simultaneously using 'GET'`, async () => { + let newName = "nameChangedAgain"; + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&new-connections-allowed=false&name=${newName}`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Change multiple settings simultaneously using 'POST'`, async () => { + let newName = "nameChangedAgain"; + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'name': newName, + 'new-connections-allowed': true + }) + }); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Make a space not joinable`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&new-connections-allowed=false`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + + }); + + test(`Make a space joinable`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&new-connections-allowed=true`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Change the space name`, async () => { + let newName = "changed name"; + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&name=${newName}`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Set space to ignore token signing`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&ignore-token-signing=true`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + + test(`Set space to not ignore token signing`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}&ignore-token-signing=false`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + }); + }); + }); + + describe('Kicking users', () => { + const numberTestUsers = 1; + let testUsers: Array = []; + + beforeAll(async () => { + jest.setTimeout(10000); // these tests need longer to complete }); - test(`CAN delete a space`, async () => { - // TODO ensure space already exists - let returnMessage = await fetch(`${stackURL}/api/v1/spaces/${createdSpaceJSON['space-id']}?token=${adminToken}`, { - method: 'DELETE' + afterAll(async () => { + jest.setTimeout(5000); // restore to default + }); + + beforeEach(async () => { + testUsers = []; + for (let i = 0; i < numberTestUsers; i++) { + let tokenData = TOKEN_GEN_TYPES.USER_APP1_SPACE1_SIGNED + tokenData['user_id'] = generateUUID(); + testUsers.push(new TestUser(tokenData['user_id'])); + let token = await generateJWT(tokenData); + await testUsers[i].communicator.connectToHiFiAudioAPIServer(token, stackData.url); + expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Connected); + } + }); + + afterEach(async () => { + // disconnect communicators to avoid using too many mixers + for (let i = 0; i < numberTestUsers; i++) { + await testUsers[i].communicator.disconnectFromHiFiAudioAPIServer(); + expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Disconnected); + } + }); + + describe('Admin CAN kick users', () => { + test(`Kick one user`, async () => { + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users${testUsers[0]['user_id']}?token=${adminToken}`, { + method: 'DELETE' + }); + for (let i = 0; i < numberTestUsers; i++) { + if (i === 0) expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Disconnected); + else expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Connected); + } + }); + + test(`Kick all users`, async () => { + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users?token=${adminToken}`, { + method: 'DELETE' + }); + for (let i = 0; i < numberTestUsers; i++) { + expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Disconnected); + } + }); + }); + + describe('Nonadmin CANNOT kick users', () => { + test(`Kick one user`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users/${testUsers[0]['user_id']}?token=${nonAdminToken}`, { + method: 'DELETE' + }); + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + for (let i = 0; i < numberTestUsers; i++) { + expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Connected); + } + }); + + test(`Kick all users`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users?token=${nonAdminToken}`, { + method: 'DELETE' + }); + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe("token isn't an admin token"); + for (let i = 0; i < numberTestUsers; i++) { + expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Connected); + } + }); + }); + }); + + describe('Wrong admin tokens', () => { + describe(`CANNOT read/alter App A by using a valid admin token for App B`, () => { + test(`Read settings for a space`, async () => { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings/app-id/?token=${adminTokenApp2}`); + + let returnMessageJSON: any = {}; + returnMessageJSON = await returnMessage.json(); + expect(returnMessageJSON.error).toBe('space/app mismatch'); }); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON['space-id']).toBe(returnMessageJSON['space-id']); - expect(returnMessageJSON['app-id']).toBe(stackData.apps.app1.id); }); }); -}); \ No newline at end of file +}); diff --git a/tests/smoke/serverConnections.smoke.test.ts b/tests/smoke/serverConnections.smoke.test.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/testUtilities/TestUser.ts b/tests/testUtilities/TestUser.ts new file mode 100644 index 00000000..cce2b09a --- /dev/null +++ b/tests/testUtilities/TestUser.ts @@ -0,0 +1,17 @@ +import { HiFiCommunicator, HiFiConnectionStates } from "../../src/classes/HiFiCommunicator"; + +export class TestUser { + name: string; + connectionState: HiFiConnectionStates; + communicator: HiFiCommunicator; + + constructor(name: string) { + this.name = name; + this.connectionState = HiFiConnectionStates.Disconnected; + this.communicator = new HiFiCommunicator({ onConnectionStateChanged: this.onConnectionStateChanged.bind(this) }); + } + + onConnectionStateChanged(connectionState: HiFiConnectionStates) { + this.connectionState = connectionState; + } +} \ No newline at end of file diff --git a/tests/unit/testUtilities/globalTeardown.js b/tests/testUtilities/globalTeardown.js similarity index 100% rename from tests/unit/testUtilities/globalTeardown.js rename to tests/testUtilities/globalTeardown.js diff --git a/tests/testUtilities/integrationTestUtils.ts b/tests/testUtilities/integrationTestUtils.ts deleted file mode 100644 index 59f7a1a8..00000000 --- a/tests/testUtilities/integrationTestUtils.ts +++ /dev/null @@ -1,174 +0,0 @@ -const { default: SignedJWT } = require('jose/jwt/sign'); -const { default: UnsecuredJWT } = require('jose/jwt/unsecured'); -const crypto = require('crypto'); -const stackData = require('../secrets/auth.json').stackData; - -export const TOKEN_GEN_TYPES = { - "ADMIN_ID_APP1_SPACE1_SIGNED": { - "admin": true, - "signed": true, - "user_id": "qateamAdmin", - "app_id": stackData.apps.app1.id, - "space_id": stackData.apps.app1.spaces.space1.id, - "app_secret": stackData.apps.app1.secret - }, - "NON_ADMIN_ID_APP1_SPACE1_SIGNED": { - "admin": false, - "signed": true, - "user_id": "qateamNonAdmin", - "app_id": stackData.apps.app1.id, - "space_id": stackData.apps.app1.spaces.space1.id, - "app_secret": stackData.apps.app1.secret - }, - "ADMIN_ID_APP2_SPACE1_SIGNED": { - "admin": true, - "signed": true, - "user_id": "qateamAdmin", - "app_id": stackData.apps.app2.id, - "space_id": stackData.apps.app2.spaces.space1.id, - "app_secret": stackData.apps.app2.secret - }, - "USER1_APP1_SPACE1_SIGNED": { - "admin": false, - "signed": true, - "user_id": "qa_user1", - "app_id": stackData.apps.app1.id, - "space_id": stackData.apps.app1.spaces.space1.id, - "app_secret": stackData.apps.app1.secret - }, - "USER2_APP1_SPACE1_SIGNED": { - "admin": false, - "signed": true, - "user_id": "qa_user1", - "app_id": stackData.apps.app1.id, - "space_id": stackData.apps.app1.spaces.space1.id, - "app_secret": stackData.apps.app1.secret - }, - "USER3_APP1_SPACE1_SIGNED": { - "admin": false, - "signed": true, - "user_id": "qa_user1", - "app_id": stackData.apps.app1.id, - "space_id": stackData.apps.app1.spaces.space1.id, - "app_secret": stackData.apps.app1.secret - }, - "USER4_APP1_SPACE1_SIGNED": { - "admin": false, - "signed": true, - "user_id": "qa_user1", - "app_id": stackData.apps.app1.id, - "space_id": stackData.apps.app1.spaces.space1.id, - "app_secret": stackData.apps.app1.secret - }, - "NON_ADMIN_ID_APP2_SPACE1_SIGNED": { - "admin": false, - "signed": true, - "user_id": "qateamNonAdmin", - "app_id": stackData.apps.app2.id, - "space_id": stackData.apps.app2.spaces.space1.id, - "app_secret": stackData.apps.app2.secret - }, - "NON_ADMIN_ID_APP2_SPACE1_UNSIGNED": { - "admin": false, - "signed": false, - "user_id": "qateamNonAdmin", - "app_id": stackData.apps.app2.id, - "space_id": stackData.apps.app2.spaces.space1.id, - "app_secret": stackData.apps.app2.secret - }, - "NON_ADMIN_APP2_SPACE_ID_NONEXISTANT_SIGNED": { - "admin": false, - "signed": true, - "user_id": "qateamNonAdmin", - "app_id": stackData.apps.app2.id, - "space_id": stackData.apps.app2.spaces.nonexistant.id, - "app_secret": stackData.apps.app2.secret - }, - "NON_ADMIN_APP2_SPACE_NAME_NONEXISTANT_SIGNED": { - "admin": false, - "signed": true, - "user_id": "qateamNonAdmin", - "app_id": stackData.apps.app2.id, - "space_name": stackData.apps.app2.spaces.nonexistant.name, - "app_secret": stackData.apps.app2.secret - }, - "NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED": { - "admin": false, - "signed": true, - "user_id": "qateamNonAdmin", - "app_id": stackData.apps.app2.id, - "app_secret": stackData.apps.app2.secret, - "space_name": "holding space" - }, - "NON_ADMIN_APP2_SPACE1_TIMED_SIGNED": { - "admin": false, - "signed": true, - "user_id": "qateamNonAdmin", - "app_id": stackData.apps.app2.id, - "space_id": stackData.apps.app2.spaces.space1.id, - "app_secret": stackData.apps.app2.secret, - "expired": false - }, - "NON_ADMIN_APP2_SPACE1_TIMED_EXPIRED": { - "admin": false, - "signed": true, - "user_id": "qateamNonAdmin", - "app_id": stackData.apps.app2.id, - "space_id": stackData.apps.app2.spaces.space1.id, - "app_secret": stackData.apps.app2.secret, - "expired": true - }, - "NON_ADMIN_APP2_SPACE1_DUP_SIGNED": { - "admin": false, - "signed": true, - "user_id": "qateamNonAdmin", - "app_id": stackData.apps.app2.id, - "space_name": stackData.apps.app2.spaces.space1.name, - "app_secret": stackData.apps.app2.secret - } -}; - -export async function generateJWT(tokenType: { [property: string]: any }) { - const SECRET_KEY_FOR_SIGNING = crypto.createSecretKey(Buffer.from(tokenType.app_secret, "utf8")); - try { - let data: any = {}; - let token; - data = { - "user_id": tokenType.user_id, - "app_id": tokenType.app_id - }; - if (tokenType.admin) data.admin = tokenType.admin; - if (tokenType.space_id) data.space_id = tokenType.space_id; - if (tokenType.space_name) data.space_name = tokenType.space_name; - if (tokenType.signed) { - if (tokenType.expired === true) { - token = await new SignedJWT(data) - .setProtectedHeader({ alg: 'HS256', typ: 'JWT' }) - .setIssuedAt() - .setExpirationTime(Math.round(Date.now() / 1000) - 60 * 60) - .sign(SECRET_KEY_FOR_SIGNING); - } else if (tokenType.expired === false) { - token = await new SignedJWT(data) - .setProtectedHeader({ alg: 'HS256', typ: 'JWT' }) - .setExpirationTime(Math.round(Date.now() / 1000) + 60 * 60) - .sign(SECRET_KEY_FOR_SIGNING); - } else if (tokenType.preissued === true) { - token = await new SignedJWT(data) - .setProtectedHeader({ alg: 'HS256', typ: 'JWT' }) - .setIssuedAt(Math.round(Date.now() / 1000) + 60 * 60) - .setExpirationTime(Math.round(Date.now() / 1000) + 60 * 60 * 2) - .sign(SECRET_KEY_FOR_SIGNING); - } else { - token = await new SignedJWT(data) - .setProtectedHeader({ alg: 'HS256', typ: 'JWT' }) - .sign(SECRET_KEY_FOR_SIGNING); - } - } else { - token = await new UnsecuredJWT(data).encode(); - } - return token; - } catch (error) { - console.error(`Couldn't create JWT! Error:\n${error}`); - return; - } -} \ No newline at end of file diff --git a/tests/testUtilities/testUtils.ts b/tests/testUtilities/testUtils.ts new file mode 100644 index 00000000..2035e840 --- /dev/null +++ b/tests/testUtilities/testUtils.ts @@ -0,0 +1,91 @@ +const { default: SignedJWT } = require('jose/jwt/sign'); +const { default: UnsecuredJWT } = require('jose/jwt/unsecured'); +const crypto = require('crypto'); +const stackData = require('../secrets/auth.json').stackData; + +export const TOKEN_GEN_TYPES = { + "ADMIN_ID_APP1_SPACE1_SIGNED": { + "admin": true, + "signed": true, + "user_id": "qateamAdmin", + "app_id": stackData.apps.app1.id, + "space_id": stackData.apps.app1.spaces.space1.id, + "app_secret": stackData.apps.app1.secret + }, + "NON_ADMIN_ID_APP1_SPACE1_SIGNED": { + "admin": false, + "signed": true, + "user_id": "qateamNonAdmin", + "app_id": stackData.apps.app1.id, + "space_id": stackData.apps.app1.spaces.space1.id, + "app_secret": stackData.apps.app1.secret + }, + "ADMIN_ID_APP2_SPACE1_SIGNED": { + "admin": true, + "signed": true, + "user_id": "qateamAdmin", + "app_id": stackData.apps.app2.id, + "space_id": stackData.apps.app2.spaces.space1.id, + "app_secret": stackData.apps.app2.secret + }, + "USER_APP1_SPACE1_SIGNED": { + "admin": false, + "signed": true, + "user_id": "", + "app_id": stackData.apps.app1.id, + "space_id": stackData.apps.app1.spaces.space1.id, + "app_secret": stackData.apps.app1.secret + } +}; + +export async function generateJWT(tokenType: { [property: string]: any }) { + const SECRET_KEY_FOR_SIGNING = crypto.createSecretKey(Buffer.from(tokenType.app_secret, "utf8")); + try { + let data: any = {}; + let token; + data = { + "user_id": tokenType.user_id, + "app_id": tokenType.app_id + }; + if (tokenType.admin) data.admin = tokenType.admin; + if (tokenType.space_id) data.space_id = tokenType.space_id; + if (tokenType.space_name) data.space_name = tokenType.space_name; + if (tokenType.signed) { + if (tokenType.expired === true) { + token = await new SignedJWT(data) + .setProtectedHeader({ alg: 'HS256', typ: 'JWT' }) + .setIssuedAt() + .setExpirationTime(Math.round(Date.now() / 1000) - 60 * 60) + .sign(SECRET_KEY_FOR_SIGNING); + } else if (tokenType.expired === false) { + token = await new SignedJWT(data) + .setProtectedHeader({ alg: 'HS256', typ: 'JWT' }) + .setExpirationTime(Math.round(Date.now() / 1000) + 60 * 60) + .sign(SECRET_KEY_FOR_SIGNING); + } else { + token = await new SignedJWT(data) + .setProtectedHeader({ alg: 'HS256', typ: 'JWT' }) + .sign(SECRET_KEY_FOR_SIGNING); + } + } else { + token = await new UnsecuredJWT(data).encode(); + } + return token; + } catch (error) { + console.error(`Couldn't create JWT! Error:\n${error}`); + return; + } +} + +export function generateUUID() { + let i = 0; + let generatedUUID = ""; + let baseString = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'; + + while (i++ < 38) { + let c = baseString[i - 1], r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); + generatedUUID += (c == '-' || c == '4') ? c : v.toString(16) + } + + return generatedUUID; +} \ No newline at end of file From 0c8c12748569f120add800b019a772f31c54f305 Mon Sep 17 00:00:00 2001 From: hifibuild Date: Thu, 11 Mar 2021 22:13:51 +0000 Subject: [PATCH 10/41] Bump package version to 0.5.1-4 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 66cc0934..cc427a27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-3", + "version": "0.5.1-4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 62f220f7..29c2240f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-3", + "version": "0.5.1-4", "description": "hifi-spatial-audio allows developers to integrate High Fidelity's spatial audio technology into their JavaScript projects.", "main": "./dist/index.js", "types": "./dist/index.d.ts", From a071e43d7de6abcce118e4a3c68a299f56e3f750 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 11 Mar 2021 17:33:06 -0500 Subject: [PATCH 11/41] Update issue templates --- .github/ISSUE_TEMPLATE/bug-report.md | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug-report.md diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 00000000..8645d986 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,32 @@ +--- +name: Bug Report +about: Create a bug report to help us improve +title: "[BUG]" +labels: bug +assignees: zfox23 + +--- + +## Describe the Bug + + +## Steps to Reproduce +Steps to reproduce the behavior: +1. +2. +3. See error. + +## Expected Behavior + + +## User or Developer Environment +- Environment: +- NodeJS Version: +- OS: +- Browser: + +## Screenshots + + +## Additional Information + From 6abf8d33929c96693d69787be0d51ac0a08ca1b9 Mon Sep 17 00:00:00 2001 From: hifibuild Date: Thu, 11 Mar 2021 22:33:59 +0000 Subject: [PATCH 12/41] Bump package version to 0.5.1-5 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index cc427a27..6458da4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-4", + "version": "0.5.1-5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 29c2240f..63ea93dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-4", + "version": "0.5.1-5", "description": "hifi-spatial-audio allows developers to integrate High Fidelity's spatial audio technology into their JavaScript projects.", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 8ee620fe0117d94e819d23778c2f2cc78398b30b Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 11 Mar 2021 18:26:12 -0500 Subject: [PATCH 13/41] Improve package.json and README --- README.md | 42 +- package-lock.json | 8311 +++++++++++++++++++++++++++++++- package.json | 17 +- utilities/spatialAudioLogo.svg | 86 + 4 files changed, 8438 insertions(+), 18 deletions(-) create mode 100644 utilities/spatialAudioLogo.svg diff --git a/README.md b/README.md index 922f2511..204a3c39 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,22 @@ -# High Fidelity Spatial Audio Client Library -The High Fidelity Audio Client Library allows developers to integrate High Fidelity's spatial audio technology into their projects. +High Fidelity Spatial Audio +  + +![npm](https://img.shields.io/npm/v/hifi-spatial-audio?style=flat-square) +![npm](https://img.shields.io/npm/dm/hifi-spatial-audio?style=flat-square) + +![GitHub Workflow Status (event)](https://img.shields.io/github/workflow/status/highfidelity/hifi-spatial-audio-js/Run-Jest-Unit-Tests?label=automated%20tests&style=flat-square) +![GitHub issues](https://img.shields.io/github/issues/highfidelity/hifi-spatial-audio-js?style=flat-square) + +![Discord](https://img.shields.io/discord/789545374837768242?label=discord&style=flat-square) +![Twitter Follow](https://img.shields.io/twitter/follow/HighFidelityXR?style=flat-square) + +The High Fidelity Spatial Audio Client Library allows developers to integrate High Fidelity's spatial audio technology into their projects. ## Installation ### NodeJS ``` -npm install hifi-spatial-audio +npm i hifi-spatial-audio ``` ### Plain Web JavaScript @@ -15,18 +26,22 @@ Import the latest version of the main library with: ``` +## You'll Need a Developer Account +To use the Spatial Audio API, you'll need to sign up for a High Fidelity Developer Account. Sign up for free at [account.highfidelity.com](https://account.highfidelity.com). ## Documentation API documentation is available at [docs.highfidelity.com](https://docs.highfidelity.com). - ## Examples You'll find a bunch of examples that make use of this API in the [Spatial-Audio-API-Examples GitHub Repository](https://github.com/highfidelity/Spatial-Audio-API-Examples). - ## Walkthrough Guides Walkthrough guides of sample applications written in plain Web JavaScript and written in NodeJS are available at [highfidelity.com/api/guides](https://highfidelity.com/api/guides). +## Release Notes +Release notes for the Spatial Audio Client Library are available on [the GitHub releases page](https://github.com/highfidelity/hifi-spatial-audio-js/releases). + +----- ## Super QuickStart - Web JavaScript Here's a super basic version of how to use the High Fidelity Spatial Audio Client Library in the Web JavaScript context: @@ -59,4 +74,21 @@ Here's a super basic version of how to use the High Fidelity Spatial Audio Clien return; } +``` + +--- + +## Living on the Edge +If you'd like to use an experimental version of the Spatial Audio Client library built automatically from the tip of the `main` branch: + +### NodeJS +``` +npm i hifi-spatial-audio@main +``` + +### Plain Web JavaScript +Import the experimental version of the main library with: + +```JavaScript + ``` \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index cc427a27..1ee806d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,8 +1,8301 @@ { "name": "hifi-spatial-audio", "version": "0.5.1-4", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "hifi-spatial-audio", + "version": "0.5.1-4", + "license": "MIT", + "dependencies": { + "pako": "^2.0.3", + "wrtc": "^0.4.7", + "ws": "^7.4.2" + }, + "devDependencies": { + "@types/jest": "^26.0.20", + "aws-sdk": "^2.831.0", + "clean-webpack-plugin": "^3.0.0", + "jest": "^26.6.3", + "jest-stare": "^2.0.1", + "ts-jest": "^26.4.4", + "ts-loader": "^8.0.14", + "typedoc": "^0.20.29", + "typescript": "^4.1.3", + "webpack": "^5.17.0", + "webpack-cli": "^4.4.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.12.13" + } + }, + "node_modules/@babel/core": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.13.tgz", + "integrity": "sha512-BQKE9kXkPlXHPeqissfxo0lySWJcYdEP0hdtJOH/iJfDdhOCcgtNCjftCJg3qqauB4h+lz2N6ixM++b9DN1Tcw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-module-transforms": "^7.12.13", + "@babel/helpers": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.12.13", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "node_modules/@babel/generator/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "dev": true, + "dependencies": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.13.tgz", + "integrity": "sha512-B+7nN0gIL8FZ8SvMcF+EPyB21KnCcZHQZFczCxbiNGV/O0rsrSBlWGLzmtBJ3GMjSVMIm4lpFhR+VdVBuIsUcQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz", + "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.13.tgz", + "integrity": "sha512-acKF7EjqOR67ASIlDTupwkKM1eUisNAjaSduo5Cz+793ikfnpe7p4Q7B7EWU2PCoSTPWsQkR7hRUWEIZPiVLGA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13", + "@babel/helper-simple-access": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.12.11", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13", + "lodash": "^4.17.19" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", + "dev": true + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz", + "integrity": "sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz", + "integrity": "sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, + "node_modules/@babel/helpers": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.13.tgz", + "integrity": "sha512-oohVzLRZ3GQEk4Cjhfs9YkJA4TdIDTObdBEZGrd6F/T0GPSnuV6l22eMcxlvcvzVIPH3VTtxbseudM1zIE+rPQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/highlight": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.15.tgz", + "integrity": "sha512-AQBOU2Z9kWwSZMd6lNjCX0GUgFonL1wAM1db8L8PMk9UDaGsRCArBkU4Sc+UCM3AE4hjbXx+h58Lb3QT4oRmrA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", + "integrity": "sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "node_modules/@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "node_modules/@babel/traverse": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", + "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "node_modules/@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "dependencies": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + }, + "bin": { + "watch": "cli.js" + }, + "engines": { + "node": ">=0.1.95" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz", + "integrity": "sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", + "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^26.6.2", + "jest-util": "^26.6.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/core": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", + "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/reporters": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^26.6.2", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-resolve-dependencies": "^26.6.3", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "jest-watcher": "^26.6.2", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/core/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/@jest/environment": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", + "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/fake-timers": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", + "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@sinonjs/fake-timers": "^6.0.1", + "@types/node": "*", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/globals": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", + "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", + "dev": true, + "dependencies": { + "@jest/environment": "^26.6.2", + "@jest/types": "^26.6.2", + "expect": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/reporters": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", + "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "node-notifier": "^8.0.0", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^7.0.0" + }, + "engines": { + "node": ">= 10.14.2" + }, + "optionalDependencies": { + "node-notifier": "^8.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", + "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/test-result": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", + "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", + "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/transform": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", + "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^26.6.2", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-util": "^26.6.2", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz", + "integrity": "sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@types/anymatch": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", + "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz", + "integrity": "sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz", + "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz", + "integrity": "sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.0.tgz", + "integrity": "sha512-kSjgDMZONiIfSH1Nxcr5JIRMwUetDki63FSQfpTCz8ogF3Ulqm8+mr5f78dUYs6vMiB6gBusQqfQmBvHZj/lwg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/eslint": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.6.tgz", + "integrity": "sha512-I+1sYH+NPQ3/tVqCeUSBwTE/0heyvtXqpIopUUArlBm0Kpocb8FbMa3AZ/ASKIFpN3rnEx932TTXDbt9OXsNDw==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.0.tgz", + "integrity": "sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "0.0.46", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.46.tgz", + "integrity": "sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg==", + "dev": true + }, + "node_modules/@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "dev": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz", + "integrity": "sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "26.0.20", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.20.tgz", + "integrity": "sha512-9zi2Y+5USJRxd0FsahERhBwlcvFh6D2GLQnY2FH2BzK8J9s9omvNHIbvABwIluXa0fD8XVKMLTO0aOEuUfACAA==", + "dev": true, + "dependencies": { + "jest-diff": "^26.0.0", + "pretty-format": "^26.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", + "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", + "dev": true + }, + "node_modules/@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "14.14.25", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.25.tgz", + "integrity": "sha512-EPpXLOVqDvisVxtlbvzfyqSsFeQxltFbluZNRndIb8tr9KiBnYNLzrc1N3pyKUCww2RNrfHDViqDWWE1LCJQtQ==", + "dev": true + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "node_modules/@types/prettier": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.0.tgz", + "integrity": "sha512-O3SQC6+6AySHwrspYn2UvC6tjo6jCTMMmylxZUFhE1CulVu5l3AxU6ca9lrJDTQDVllF62LIxVSx5fuYL6LiZg==", + "dev": true + }, + "node_modules/@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", + "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", + "dev": true + }, + "node_modules/@types/tapable": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.6.tgz", + "integrity": "sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA==", + "dev": true + }, + "node_modules/@types/uglify-js": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.12.0.tgz", + "integrity": "sha512-sYAF+CF9XZ5cvEBkI7RtrG9g2GtMBkviTnBxYYyq+8BWvO4QtXfwwR6a2LFwCi4evMKZfpv6U43ViYvv17Wz3Q==", + "dev": true, + "dependencies": { + "source-map": "^0.6.1" + } + }, + "node_modules/@types/webpack": { + "version": "4.41.26", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.26.tgz", + "integrity": "sha512-7ZyTfxjCRwexh+EJFwRUM+CDB2XvgHl4vfuqf1ZKrgGvcS5BrNvPQqJh3tsZ0P6h6Aa1qClVHaJZszLPzpqHeA==", + "dev": true, + "dependencies": { + "@types/anymatch": "*", + "@types/node": "*", + "@types/tapable": "*", + "@types/uglify-js": "*", + "@types/webpack-sources": "*", + "source-map": "^0.6.0" + } + }, + "node_modules/@types/webpack-sources": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-2.1.0.tgz", + "integrity": "sha512-LXn/oYIpBeucgP1EIJbKQ2/4ZmpvRl+dlrFdX7+94SKRUV3Evy3FsfMZY318vGhkWUS5MPhtOM3w1/hCOAOXcg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.7.3" + } + }, + "node_modules/@types/webpack-sources/node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/yargs": { + "version": "15.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", + "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", + "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", + "dev": true + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.0.tgz", + "integrity": "sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.0", + "@webassemblyjs/helper-wasm-bytecode": "1.11.0" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz", + "integrity": "sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz", + "integrity": "sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz", + "integrity": "sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz", + "integrity": "sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.0", + "@webassemblyjs/helper-api-error": "1.11.0", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz", + "integrity": "sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz", + "integrity": "sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/helper-buffer": "1.11.0", + "@webassemblyjs/helper-wasm-bytecode": "1.11.0", + "@webassemblyjs/wasm-gen": "1.11.0" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz", + "integrity": "sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.0.tgz", + "integrity": "sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.0.tgz", + "integrity": "sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz", + "integrity": "sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/helper-buffer": "1.11.0", + "@webassemblyjs/helper-wasm-bytecode": "1.11.0", + "@webassemblyjs/helper-wasm-section": "1.11.0", + "@webassemblyjs/wasm-gen": "1.11.0", + "@webassemblyjs/wasm-opt": "1.11.0", + "@webassemblyjs/wasm-parser": "1.11.0", + "@webassemblyjs/wast-printer": "1.11.0" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz", + "integrity": "sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/helper-wasm-bytecode": "1.11.0", + "@webassemblyjs/ieee754": "1.11.0", + "@webassemblyjs/leb128": "1.11.0", + "@webassemblyjs/utf8": "1.11.0" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz", + "integrity": "sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/helper-buffer": "1.11.0", + "@webassemblyjs/wasm-gen": "1.11.0", + "@webassemblyjs/wasm-parser": "1.11.0" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz", + "integrity": "sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/helper-api-error": "1.11.0", + "@webassemblyjs/helper-wasm-bytecode": "1.11.0", + "@webassemblyjs/ieee754": "1.11.0", + "@webassemblyjs/leb128": "1.11.0", + "@webassemblyjs/utf8": "1.11.0" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz", + "integrity": "sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.0", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.0.1.tgz", + "integrity": "sha512-B+4uBUYhpzDXmwuo3V9yBH6cISwxEI4J+NO5ggDaGEEHb0osY/R7MzeKc0bHURXQuZjMM4qD+bSJCKIuI3eNBQ==", + "dev": true + }, + "node_modules/@webpack-cli/info": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.2.2.tgz", + "integrity": "sha512-5U9kUJHnwU+FhKH4PWGZuBC1hTEPYyxGSL5jjoBI96Gx8qcYJGOikpiIpFoTq8mmgX3im2zAo2wanv/alD74KQ==", + "dev": true, + "dependencies": { + "envinfo": "^7.7.3" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.3.0.tgz", + "integrity": "sha512-k2p2VrONcYVX1wRRrf0f3X2VGltLWcv+JzXRBDmvCxGlCeESx4OXw91TsWeKOkp784uNoVQo313vxJFHXPPwfw==", + "dev": true + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "dependencies": { + "type-fest": "^0.11.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-parser": { + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/ansi-parser/-/ansi-parser-3.2.10.tgz", + "integrity": "sha512-CGKGIbd678lm15IXJXI1cTyOVAnMQw0jES+klW/yIc+GzYccsYanLMhczPIIj2hE64B79g75QfiuWrEWd6nJdg==", + "dev": true + }, + "node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "node_modules/are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/aws-sdk": { + "version": "2.840.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.840.0.tgz", + "integrity": "sha512-ngesHJqb0PXYjJNnCsAX4yLkR6JFQJB+3eDGwh3mYRjcq9voix5RfbCFQT1lwWu7bcMBPCrRIA2lJkkTMYXq+A==", + "dev": true, + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.3.2", + "xml2js": "0.4.19" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, + "node_modules/babel-jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", + "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", + "dev": true, + "dependencies": { + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/babel__core": "^7.1.7", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "slash": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", + "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "node_modules/babel-preset-jest": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", + "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^26.6.2", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/bootstrap": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.0.tgz", + "integrity": "sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz", + "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==", + "dev": true, + "dependencies": { + "caniuse-lite": "^1.0.30001181", + "colorette": "^1.2.1", + "electron-to-chromium": "^1.3.649", + "escalade": "^3.1.1", + "node-releases": "^1.1.70" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001185", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001185.tgz", + "integrity": "sha512-Fpi4kVNtNvJ15H0F6vwmXtb3tukv3Zg3qhKkOGUq7KJ1J6b9kf4dnNgtEAFXhRsJo0gNj9W60+wBvn0JcTvdTg==", + "dev": true + }, + "node_modules/capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "dependencies": { + "rsvp": "^4.8.4" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chart.js": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.4.tgz", + "integrity": "sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==", + "dev": true, + "dependencies": { + "chartjs-color": "^2.1.0", + "moment": "^2.10.2" + } + }, + "node_modules/chartjs-color": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz", + "integrity": "sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==", + "dev": true, + "dependencies": { + "chartjs-color-string": "^0.6.0", + "color-convert": "^1.9.3" + } + }, + "node_modules/chartjs-color-string": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz", + "integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0" + } + }, + "node_modules/chartjs-color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/chartjs-color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/cjs-module-lexer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", + "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==", + "dev": true + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-webpack-plugin": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-3.0.0.tgz", + "integrity": "sha512-MciirUH5r+cYLGCOL5JX/ZLzOZbVr1ot3Fw+KcvbhUb6PM+yycqd9ZhIlcigQ5gl+XhppNmw3bEFuaaMNyLj3A==", + "dev": true, + "dependencies": { + "@types/webpack": "^4.4.31", + "del": "^4.1.1" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colorette": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", + "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", + "dev": true + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "node_modules/convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz", + "integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==", + "dev": true + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "dependencies": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/diff2html": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/diff2html/-/diff2html-3.2.0.tgz", + "integrity": "sha512-XbiY9AuQNuGKq107b4PlimQJsC8f1qomPqRbvuZ/ShCJaPVkn5rvE2I62ZwQSJotoGmqQ0pdvMhQx5Fd76IrCg==", + "dev": true, + "dependencies": { + "diff": "4.0.2", + "highlight.js": "10.4.1", + "hogan.js": "3.0.2" + }, + "engines": { + "node": ">=10" + }, + "optionalDependencies": { + "highlight.js": "10.4.1" + } + }, + "node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.3.659", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.659.tgz", + "integrity": "sha512-VPc1LcvuQYGjam6k7JcB6uJFTMo2YNlJ6rSbwbxApZQdow7X81kh/vDB6LB5B8DNmvkbKnpZkLmpKmnvoKA+Gw==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", + "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/envinfo": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.7.4.tgz", + "integrity": "sha512-TQXTYFVVwwluWSFis6K2XKxgrD22jEv0FTuLCQI+OjH7rn93+iY0fSSFM5lrSxFY+H1+B0/cvvlamr3UsBivdQ==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-module-lexer": { + "version": "0.3.26", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.3.26.tgz", + "integrity": "sha512-Va0Q/xqtrss45hWzP8CZJwzGSZJjDM5/MJRE3IXXnUCcVLElR9BRaE9F62BopysASyc4nM3uwhSW7FFB9nlWAA==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dev": true + }, + "node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/expect": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", + "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-styles": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true, + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, + "node_modules/fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "dependencies": { + "minipass": "^2.6.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/gauge/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globby/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.5.tgz", + "integrity": "sha512-kBBSQbz2K0Nyn+31j/w36fUfxkBW9/gfwRWdUY1ULReH3iokVJgddZAFcD1D0xlgTmFxJCbUkUclAlc6/IDJkw==", + "dev": true + }, + "node_modules/growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true, + "optional": true + }, + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/highlight.js": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.4.1.tgz", + "integrity": "sha512-yR5lWvNz7c85OhVAEAeFhVCc/GV4C30Fjzc/rCP0aCWzc1UUOPUk55dK/qdwTZHBvMZo+eZ2jpk62ndX/xMFlg==", + "dev": true, + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/hogan.js": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/hogan.js/-/hogan.js-3.0.2.tgz", + "integrity": "sha1-TNnhq9QpQUbnZ55B14mHMrAse/0=", + "dev": true, + "dependencies": { + "mkdirp": "0.3.0", + "nopt": "1.0.10" + }, + "bin": { + "hulk": "bin/hulk" + } + }, + "node_modules/holderjs": { + "version": "2.9.9", + "resolved": "https://registry.npmjs.org/holderjs/-/holderjs-2.9.9.tgz", + "integrity": "sha512-ceWPz1MrR3dxOoZXiom+G48+l1VPG3TcjBw9fq5iwCiZAMvYX8Aia13GOxT7DoV/AcSyTH7Vvr11ygjZP9qn4w==", + "dev": true + }, + "node_modules/hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "node_modules/ignore-walk": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", + "dependencies": { + "minimatch": "^3.0.4" + } + }, + "node_modules/import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-docker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "dev": true, + "optional": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "dependencies": { + "is-path-inside": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "dependencies": { + "path-is-inside": "^1.0.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", + "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", + "dev": true + }, + "node_modules/is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "optional": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", + "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", + "dev": true, + "dependencies": { + "@jest/core": "^26.6.3", + "import-local": "^3.0.2", + "jest-cli": "^26.6.3" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-changed-files": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", + "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "execa": "^4.0.0", + "throat": "^5.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-cli": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", + "dev": true, + "dependencies": { + "@jest/core": "^26.6.3", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^26.6.3", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "prompts": "^2.0.1", + "yargs": "^15.4.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-config": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", + "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^26.6.3", + "@jest/types": "^26.6.2", + "babel-jest": "^26.6.3", + "chalk": "^4.0.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.4", + "jest-environment-jsdom": "^26.6.2", + "jest-environment-node": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-jasmine2": "^26.6.3", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-diff": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-docblock": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", + "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-each": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", + "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", + "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", + "dev": true, + "dependencies": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2", + "jsdom": "^16.4.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-environment-node": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", + "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", + "dev": true, + "dependencies": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-haste-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", + "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^26.0.0", + "jest-serializer": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" + }, + "engines": { + "node": ">= 10.14.2" + }, + "optionalDependencies": { + "fsevents": "^2.1.2" + } + }, + "node_modules/jest-jasmine2": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", + "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^26.6.2", + "is-generator-fn": "^2.0.0", + "jest-each": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2", + "throat": "^5.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", + "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", + "dev": true, + "dependencies": { + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-matcher-utils": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-message-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", + "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-mock": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", + "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/node": "*" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-regex-util": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", + "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-resolve": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.2", + "read-pkg-up": "^7.0.1", + "resolve": "^1.18.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", + "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-snapshot": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-runner": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", + "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.7.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-docblock": "^26.0.0", + "jest-haste-map": "^26.6.2", + "jest-leak-detector": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "source-map-support": "^0.5.6", + "throat": "^5.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-runtime": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", + "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/globals": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0", + "cjs-module-lexer": "^0.6.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^15.4.1" + }, + "bin": { + "jest-runtime": "bin/jest-runtime.js" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-serializer": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", + "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", + "dev": true, + "dependencies": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-snapshot": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", + "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.0.0", + "chalk": "^4.0.0", + "expect": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-haste-map": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "natural-compare": "^1.4.0", + "pretty-format": "^26.6.2", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-snapshot/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/jest-stare": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/jest-stare/-/jest-stare-2.2.1.tgz", + "integrity": "sha512-Ma+DjZeLVVPZnLUlgi/YFYMznsU36Tp06FjBU8lwf1t153+0vVltVqZoKrevklUxoItTjIMOyk/liPF/whqWvg==", + "dev": true, + "dependencies": { + "@jest/reporters": "^26.0.0", + "@jest/test-result": "^26.0.0", + "@jest/types": "^26.0.0", + "@types/jest": "^26.0.12", + "ansi-parser": "^3.2.10", + "bootstrap": "^4.5.2", + "chalk": "^4.1.0", + "chart.js": "^2.9.3", + "diff2html": "^3.1.18", + "holderjs": "^2.9.7", + "jquery": "^3.5.1", + "moment": "^2.27.0", + "mustache": "^4.0.0", + "pkg-up": "^3.0.0", + "popper.js": "^1.16.1", + "yargs": "^16.0.0" + }, + "bin": { + "jest-stare": "lib/jest-stare.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/jest-stare/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/jest-stare/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-stare/node_modules/y18n": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", + "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-stare/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-validate": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", + "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "camelcase": "^6.0.0", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "leven": "^3.1.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-watcher": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", + "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^26.6.2", + "string-length": "^4.0.1" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jmespath": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/jquery": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz", + "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "node_modules/jsdom": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", + "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", + "dev": true, + "dependencies": { + "abab": "^2.0.3", + "acorn": "^7.1.1", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.2.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.0", + "domexception": "^2.0.1", + "escodegen": "^1.14.1", + "html-encoding-sniffer": "^2.0.1", + "is-potential-custom-element-name": "^1.0.0", + "nwsapi": "^2.2.0", + "parse5": "5.1.1", + "request": "^2.88.2", + "request-promise-native": "^1.0.8", + "saxes": "^5.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^3.0.1", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0", + "ws": "^7.2.3", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "node_modules/jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "node_modules/loader-runner": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "dependencies": { + "tmpl": "1.0.x" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/marked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.1.tgz", + "integrity": "sha512-5+/fKgMv2hARmMW7DOpykr2iLhl0NgjyELk5yn92iE7z8Se1IS9n3UsFm86hFXIkvMBmVxki8+ckcpjBeyo/hw==", + "dev": true, + "bin": { + "marked": "bin/marked" + }, + "engines": { + "node": ">= 8.16.2" + } + }, + "node_modules/memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mime-db": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", + "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.28", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", + "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", + "dev": true, + "dependencies": { + "mime-db": "1.45.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "node_modules/minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dependencies": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "node_modules/minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "dependencies": { + "minipass": "^2.9.0" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/mustache": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.1.0.tgz", + "integrity": "sha512-0FsgP/WVq4mKyjolIyX+Z9Bd+3WS8GOwoUTyKXT5cTYMGeauNTi2HPCwERqseC1IHAy0Z7MDZnJBfjabd4O8GQ==", + "dev": true, + "bin": { + "mustache": "bin/mustache" + } + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/needle": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.6.0.tgz", + "integrity": "sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg==", + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/needle/node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node_modules/node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-notifier": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", + "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", + "dev": true, + "optional": true, + "dependencies": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + } + }, + "node_modules/node-notifier/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-notifier/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "optional": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-notifier/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/node-notifier/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "optional": true + }, + "node_modules/node-pre-gyp": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz", + "integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==", + "dependencies": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/node-pre-gyp/node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/node-pre-gyp/node_modules/nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "dependencies": { + "abbrev": "1", + "osenv": "^0.1.4" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/node-releases": { + "version": "1.1.70", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.70.tgz", + "integrity": "sha512-Slf2s69+2/uAD79pVVQo8uSiC34+g8GWY8UH2Qtqv34ZfhYrxpYpfzs9Js9d6O0mbDmALuxaTlplnBTnSELcrw==", + "dev": true + }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-bundled": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" + }, + "node_modules/npm-packlist": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", + "dependencies": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/onigasm": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz", + "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==", + "dev": true, + "dependencies": { + "lru-cache": "^5.1.1" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dependencies": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "node_modules/p-each-series": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pako": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.0.3.tgz", + "integrity": "sha512-WjR1hOeg+kki3ZIOjaf4b5WVcay1jaliKSYiEaB1XzwhMQZJxRdQRv0V31EKBYlxb4T7SK3hjfc/jxyU64BoSw==" + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "dependencies": { + "node-modules-regexp": "^1.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", + "dev": true + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/prompts": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", + "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/react-is": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", + "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", + "dev": true + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "node_modules/repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.19" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "dev": true, + "dependencies": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/request-promise-native/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true, + "engines": { + "node": "6.* || >= 7.*" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "dependencies": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + }, + "bin": { + "sane": "src/cli.js" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/sane/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/sane/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/sane/node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sane/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sane/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/sane/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/sane/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=", + "dev": true + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shelljs": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", + "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "dev": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true, + "optional": true + }, + "node_modules/shiki": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.2.tgz", + "integrity": "sha512-BjUCxVbxMnvjs8jC4b+BQ808vwjJ9Q8NtLqPwXShZ307HdXiDFYP968ORSVfaTNNSWYDBYdMnVKJ0fYNsoZUBA==", + "dev": true, + "dependencies": { + "onigasm": "^2.2.5", + "vscode-textmate": "^5.2.0" + } + }, + "node_modules/signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", + "dev": true + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-length": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", + "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", + "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "dependencies": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + }, + "engines": { + "node": ">=4.5" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/terser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.5.1.tgz", + "integrity": "sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ==", + "dev": true, + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.1.tgz", + "integrity": "sha512-5XNNXZiR8YO6X6KhSGXfY0QrGrCRlSwAEjIIrlRQR4W8nP69TaJUlh3bkuac6zzgspiGPfKEHcY295MMVExl5Q==", + "dev": true, + "dependencies": { + "jest-worker": "^26.6.2", + "p-limit": "^3.1.0", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1", + "source-map": "^0.6.1", + "terser": "^5.5.1" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "dev": true + }, + "node_modules/tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "dev": true, + "dependencies": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", + "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-jest": { + "version": "26.5.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.1.tgz", + "integrity": "sha512-G7Rmo3OJMvlqE79amJX8VJKDiRcd7/r61wh9fnvvG8cAjhA9edklGw/dCxRSQmfZ/z8NDums5srSVgwZos1qfg==", + "dev": true, + "dependencies": { + "@types/jest": "26.x", + "bs-logger": "0.x", + "buffer-from": "1.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^26.1.0", + "json5": "2.x", + "lodash": "4.x", + "make-error": "1.x", + "mkdirp": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/ts-jest/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/ts-loader": { + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.0.16.tgz", + "integrity": "sha512-Cr9ywsgg1n8cjGjIogHLPlqe3WJUHzuJaqwNo5I596KpIqekKzxvSENbrXeOypHcXSPPsr8hV6mglngyXvcKrg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^2.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ts-loader/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-loader/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-loader/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typedoc": { + "version": "0.20.29", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.20.29.tgz", + "integrity": "sha512-IyzrbtwNAXtylUJn41FbopQsNSQ1jcM6lUhDL/REOFo31G3Q9fsniZUQP+tIcTX5JaCntRdw3PTMZTQPV52low==", + "dev": true, + "dependencies": { + "colors": "^1.4.0", + "fs-extra": "^9.1.0", + "handlebars": "^4.7.7", + "lodash": "^4.17.21", + "lunr": "^2.3.9", + "marked": "^2.0.1", + "minimatch": "^3.0.0", + "progress": "^2.0.3", + "shelljs": "^0.8.4", + "shiki": "^0.9.2", + "typedoc-default-themes": "^0.12.8" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 10.8.0" + } + }, + "node_modules/typedoc-default-themes": { + "version": "0.12.8", + "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.8.tgz", + "integrity": "sha512-tyjyDTKy/JLnBSwvhoqd99VIjrP33SdOtwcMD32b+OqnrjZWe8HmZECbfBoacqoxjHd58gfeNw6wA7uvqWFa4w==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/typedoc/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/typescript": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", + "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uglify-js": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.0.tgz", + "integrity": "sha512-TWYSWa9T2pPN4DIJYbU9oAjQx+5qdV5RUDxwARg8fmJZrD/V27Zj0JngW5xg1DFz42G0uDYl2XhzF6alSzD62w==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/union-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "dev": true, + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", + "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.0.tgz", + "integrity": "sha512-uXUVqNUCLa0AH1vuVxzi+MI4RfxEOKt9pBgKwHbgH7st8Kv2P1m+jvWNnektzBh5QShF3ODgKmUFCf38LnVz1g==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/vscode-textmate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", + "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "dev": true + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "dependencies": { + "makeerror": "1.0.x" + } + }, + "node_modules/watchpack": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.1.1.tgz", + "integrity": "sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true, + "engines": { + "node": ">=10.4" + } + }, + "node_modules/webpack": { + "version": "5.21.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.21.2.tgz", + "integrity": "sha512-xHflCenx+AM4uWKX71SWHhxml5aMXdy2tu/vdi4lClm7PADKxlyDAFFN1rEFzNV0MAoPpHtBeJnl/+K6F4QBPg==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.46", + "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/wasm-edit": "1.11.0", + "@webassemblyjs/wasm-parser": "1.11.0", + "acorn": "^8.0.4", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.7.0", + "es-module-lexer": "^0.3.26", + "eslint-scope": "^5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.4", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.1", + "watchpack": "^2.0.0", + "webpack-sources": "^2.1.1" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-cli": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.5.0.tgz", + "integrity": "sha512-wXg/ef6Ibstl2f50mnkcHblRPN/P9J4Nlod5Hg9HGFgSeF8rsqDGHJeVe4aR26q9l62TUJi6vmvC2Qz96YJw1Q==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.0.1", + "@webpack-cli/info": "^1.2.2", + "@webpack-cli/serve": "^1.3.0", + "colorette": "^1.2.1", + "commander": "^7.0.0", + "enquirer": "^2.3.6", + "execa": "^5.0.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "v8-compile-cache": "^2.2.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.0.0.tgz", + "integrity": "sha512-ovx/7NkTrnPuIV8sqk/GjUIIM1+iUQeqA3ye2VNpq9sVoiZsooObWlQy+OPWGI17GDaEoybuAGJm6U8yC077BA==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-cli/node_modules/execa": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz", + "integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/webpack-cli/node_modules/get-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz", + "integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/webpack-cli/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/webpack-cli/node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/webpack-cli/node_modules/rechoir": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.0.tgz", + "integrity": "sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==", + "dev": true, + "dependencies": { + "resolve": "^1.9.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/webpack-merge": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.7.3.tgz", + "integrity": "sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.2.0.tgz", + "integrity": "sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==", + "dev": true, + "dependencies": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/acorn": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.0.5.tgz", + "integrity": "sha512-v+DieK/HJkJOpFBETDJioequtc3PfxsWMaxIdIwujtF7FEV/MAyDQLlm6/zPvr7Mix07mLh6ccVwIsloceodlg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack/node_modules/enhanced-resolve": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz", + "integrity": "sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/events": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/webpack/node_modules/tapable": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", + "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz", + "integrity": "sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==", + "dev": true, + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^2.0.2", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/wide-align/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/wrtc": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/wrtc/-/wrtc-0.4.7.tgz", + "integrity": "sha512-P6Hn7VT4lfSH49HxLHcHhDq+aFf/jd9dPY7lDHeFhZ22N3858EKuwm2jmnlPzpsRGEPaoF6XwkcxY5SYnt4f/g==", + "dependencies": { + "domexception": "^1.0.1", + "node-pre-gyp": "^0.13.0" + }, + "engines": { + "node": "^8.11.2 || >=10.0.0" + }, + "optionalDependencies": { + "domexception": "^1.0.1" + } + }, + "node_modules/wrtc/node_modules/domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "optional": true, + "dependencies": { + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/wrtc/node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "optional": true + }, + "node_modules/ws": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.3.tgz", + "integrity": "sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA==", + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "node_modules/xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "node_modules/xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + } + } + }, "dependencies": { "@babel/code-frame": { "version": "7.12.13", @@ -5780,6 +14073,14 @@ "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", "dev": true }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, "string-length": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", @@ -5801,14 +14102,6 @@ "strip-ansi": "^6.0.0" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", diff --git a/package.json b/package.json index 29c2240f..e008da89 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "hifi-spatial-audio", "version": "0.5.1-4", - "description": "hifi-spatial-audio allows developers to integrate High Fidelity's spatial audio technology into their JavaScript projects.", + "description": "The High Fidelity Audio Client Library allows developers to integrate High Fidelity's spatial audio technology into their projects.", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ @@ -21,12 +21,15 @@ "test:generateReport": "jest --reporters default jest-stare", "publishNewRelease": "npm run clean && npm run docs:buildAndUpload && npm run build:node && npm publish && npm run build:web:prod" }, - "repository": "git://github.com/highfidelity/hifi-spatial-audio-js.git", + "repository": { + "type": "git", + "url": "https://github.com/highfidelity/hifi-spatial-audio-js.git" + }, "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" }, - "author": "High Fidelity", + "author": "High Fidelity, Inc.", "license": "MIT", "bugs": { "url": "https://github.com/highfidelity/hifi-spatial-audio-js/issues" @@ -49,5 +52,11 @@ "pako": "^2.0.3", "wrtc": "^0.4.7", "ws": "^7.4.2" - } + }, + "keywords": ["audio", "spatial audio", "3d"], + "maintainers": [ + "Zach Fox (fox@highfidelity.com)", + "Maia Hansen (maia@highfidelity.com)", + "Joy Scharmen (joy@highfidelity.com)" + ] } diff --git a/utilities/spatialAudioLogo.svg b/utilities/spatialAudioLogo.svg new file mode 100644 index 00000000..d7a7dd5f --- /dev/null +++ b/utilities/spatialAudioLogo.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 919191a903bb8bf011049fe9e6e3313052ade1fc Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 11 Mar 2021 18:28:25 -0500 Subject: [PATCH 14/41] NPM Normalize --- package-lock.json | 2 +- package.json | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index d68ce56f..b94378ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "hifi-spatial-audio", - "version": "0.5.1-4", + "version": "0.5.1-5", "license": "MIT", "dependencies": { "pako": "^2.0.3", diff --git a/package.json b/package.json index e9d8e1f9..e5b7c85a 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,11 @@ "wrtc": "^0.4.7", "ws": "^7.4.2" }, - "keywords": ["audio", "spatial audio", "3d"], + "keywords": [ + "audio", + "spatial audio", + "3d" + ], "maintainers": [ "Zach Fox (fox@highfidelity.com)", "Maia Hansen (maia@highfidelity.com)", From ca9a1c19787381371e66f2ed3a54de487eac9703 Mon Sep 17 00:00:00 2001 From: hifibuild Date: Thu, 11 Mar 2021 23:29:17 +0000 Subject: [PATCH 15/41] Bump package version to 0.5.1-6 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b94378ae..fbe2c2db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-5", + "version": "0.5.1-6", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index e5b7c85a..e475a432 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-5", + "version": "0.5.1-6", "description": "The High Fidelity Audio Client Library allows developers to integrate High Fidelity's spatial audio technology into their projects.", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 132cdc119f98eb15baf38ee34d05a06213a7a327 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 11 Mar 2021 18:32:53 -0500 Subject: [PATCH 16/41] Make badges clickable --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 204a3c39..23c03574 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,14 @@   -![npm](https://img.shields.io/npm/v/hifi-spatial-audio?style=flat-square) -![npm](https://img.shields.io/npm/dm/hifi-spatial-audio?style=flat-square) +[![npm](https://img.shields.io/npm/v/hifi-spatial-audio?style=flat-square)](https://www.npmjs.com/package/hifi-spatial-audio) +[![npm](https://img.shields.io/npm/dm/hifi-spatial-audio?style=flat-square)](https://www.npmjs.com/package/hifi-spatial-audio) -![GitHub Workflow Status (event)](https://img.shields.io/github/workflow/status/highfidelity/hifi-spatial-audio-js/Run-Jest-Unit-Tests?label=automated%20tests&style=flat-square) -![GitHub issues](https://img.shields.io/github/issues/highfidelity/hifi-spatial-audio-js?style=flat-square) +[![GitHub Workflow Status (event)](https://img.shields.io/github/workflow/status/highfidelity/hifi-spatial-audio-js/Run-Jest-Unit-Tests?label=automated%20tests&style=flat-square)](https://github.com/highfidelity/hifi-spatial-audio-js/actions/workflows/run-unit-tests.yml) +[![GitHub issues](https://img.shields.io/github/issues/highfidelity/hifi-spatial-audio-js?style=flat-square)](https://github.com/highfidelity/hifi-spatial-audio-js/issues) -![Discord](https://img.shields.io/discord/789545374837768242?label=discord&style=flat-square) -![Twitter Follow](https://img.shields.io/twitter/follow/HighFidelityXR?style=flat-square) +[![Discord](https://img.shields.io/discord/789545374837768242?label=discord&style=flat-square)](https://discord.gg/GrhxWPrp) +[![Twitter Follow](https://img.shields.io/twitter/follow/HighFidelityXR?style=flat-square)](https://twitter.com/HighFidelityXR) The High Fidelity Spatial Audio Client Library allows developers to integrate High Fidelity's spatial audio technology into their projects. From 7761934c24f7b9508b84696c96a3d31b3c1ebaa9 Mon Sep 17 00:00:00 2001 From: hifibuild Date: Thu, 11 Mar 2021 23:33:48 +0000 Subject: [PATCH 17/41] Bump package version to 0.5.1-7 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index fbe2c2db..9bec407a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-6", + "version": "0.5.1-7", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index e475a432..ca255da8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-6", + "version": "0.5.1-7", "description": "The High Fidelity Audio Client Library allows developers to integrate High Fidelity's spatial audio technology into their projects.", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 34c8e4c7366404ce3cbccabe9cce16969b9d02bd Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 11 Mar 2021 18:38:54 -0500 Subject: [PATCH 18/41] Add stroke to logo --- utilities/spatialAudioLogo.svg | 113 +++++++++++---------------------- 1 file changed, 37 insertions(+), 76 deletions(-) diff --git a/utilities/spatialAudioLogo.svg b/utilities/spatialAudioLogo.svg index d7a7dd5f..7c83856f 100644 --- a/utilities/spatialAudioLogo.svg +++ b/utilities/spatialAudioLogo.svg @@ -1,86 +1,47 @@ - + - +image/svg+xml - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + From 8d5fb21454d77c3b376b7c43540f9049fbed62ca Mon Sep 17 00:00:00 2001 From: hifibuild Date: Thu, 11 Mar 2021 23:39:51 +0000 Subject: [PATCH 19/41] Bump package version to 0.5.1-8 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9bec407a..de98bb8b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-7", + "version": "0.5.1-8", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index ca255da8..84a205a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-7", + "version": "0.5.1-8", "description": "The High Fidelity Audio Client Library allows developers to integrate High Fidelity's spatial audio technology into their projects.", "main": "./dist/index.js", "types": "./dist/index.d.ts", From da491943c14834da858a24da7bdb63138bf247d8 Mon Sep 17 00:00:00 2001 From: RebeccaStankus Date: Thu, 11 Mar 2021 16:12:13 -0800 Subject: [PATCH 20/41] Smoke and integration tests for server connections --- .../HiFiCommunicator.integration.test.ts | 195 ++++++++++++++++++ tests/smoke/serverConnections.smoke.test.ts | 157 ++++++++++++++ tests/testUtilities/testUtils.ts | 58 ++++++ 3 files changed, 410 insertions(+) create mode 100644 tests/integration/src/classes/HiFiCommunicator.integration.test.ts create mode 100644 tests/smoke/serverConnections.smoke.test.ts diff --git a/tests/integration/src/classes/HiFiCommunicator.integration.test.ts b/tests/integration/src/classes/HiFiCommunicator.integration.test.ts new file mode 100644 index 00000000..af32142e --- /dev/null +++ b/tests/integration/src/classes/HiFiCommunicator.integration.test.ts @@ -0,0 +1,195 @@ +const fetch = require('node-fetch'); +import { HiFiCommunicator } from "../../../../src/classes/HiFiCommunicator"; +import { TOKEN_GEN_TYPES, generateJWT } from '../../../testUtilities/testUtils'; +const stackData = require('../../../secrets/auth.json').stackData; + +describe('Non admin server connections', () => { + let nonAdminSigned: string; + let adminSigned: string; + let nonAdminUnsigned: string; + let nonAdminNonexistantSpaceID: string; + let nonAdminNewSpaceName: string; + let nonAdminTimed: string; + let nonAdminExpired: string; + let nonAdminDupSpaceName: string; + let hifiCommunicator: HiFiCommunicator; + beforeAll(async () => { + try { + nonAdminSigned = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP2_SPACE1_SIGNED); + adminSigned = await generateJWT(TOKEN_GEN_TYPES.ADMIN_ID_APP2_SPACE1_SIGNED); + nonAdminUnsigned = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP2_SPACE1_UNSIGNED); + nonAdminNonexistantSpaceID = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE_ID_NONEXISTANT_SIGNED); + nonAdminNewSpaceName = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED); + nonAdminTimed = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_TIMED_SIGNED); + nonAdminExpired = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_TIMED_EXPIRED); + nonAdminDupSpaceName = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_DUP_SIGNED); + } catch (err) { + console.error("Unable to create tokens in preparation for testing server connections. Please check " + + "your 'auth.json' file for errors or discrepancies with your account data. ERR: ", err); + throw err; + } + // Make sure the space we try to create later does not already exist, delete it if it does + try { + let spaceAlreadyExistsIDs: Array = []; + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${adminSigned}`); + let spacesListJSON: any = {}; + spacesListJSON = await returnMessage.json(); + spacesListJSON.forEach(async (space: any) => { + if (space['name'] === TOKEN_GEN_TYPES.NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED["space_name"]) { spaceAlreadyExistsIDs.push(space['space-id']) } + }); + + if (spaceAlreadyExistsIDs) { + spaceAlreadyExistsIDs.forEach(async (spaceID) => { + let deleteReturnMessage = await fetch(`${stackData.url}/api/v1/spaces/${spaceID}?token=${adminSigned}`, { + method: 'DELETE' + }); + let deleteReturnMessageJSON: any = {}; + deleteReturnMessageJSON = await deleteReturnMessage.json(); + + expect(deleteReturnMessageJSON['space-id']).toBe(spaceID); + }) + } + } catch (err) { + console.error(`Unable to ensure the nonexistant space does not exist. Please check your app. ERR: ${err}`); + throw err; + } + }); + + beforeEach(() => { + hifiCommunicator = new HiFiCommunicator(); + }) + + afterEach(async () => { + await hifiCommunicator.disconnectFromHiFiAudioAPIServer(); + }); + + // TODO add checks for correct stack connections once those fetch calls are possible (Jira 435) + + test(`CAN connect to Space A on staging with signed token containing Space ID A`, async () => { + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminSigned, stackData.url) + .then(data => { + expect(data.audionetInitResponse.success).toBe(true); + }); + }); + + test(`CAN connect to Space A on staging with UNSIGNED token containing Space ID A when space does not require signing`, async () => { + // set space to allow unsigned tokens + try { + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=true`); + } catch (err) { + console.error("Unable to set space to ignore token signing. ERR: ", err); + throw err; + } + + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminUnsigned, stackData.url) + .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); + }); + + test(`CANNOT connect to Space A on staging with UNSIGNED token containing Space ID A when space does require signing`, async () => { + // set space to not allow unsigned tokens + try { + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=false`); + } catch (err) { + console.error("Unable to set space to ignore token signing. ERR: ", err); + throw err; + } + + await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminUnsigned, stackData.url)) + .rejects.toMatchObject({ error: expect.stringMatching(/Unexpected server response: 501/) }); + }); + + test(`CAN connect to Space A on staging with signed token containing Space ID A when space does not require signing`, async () => { + // set space to not allow unsigned tokens + try { + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=false`); + } catch (err) { + console.error("Unable to set space to ignore token signing. ERR: ", err); + throw err; + } + + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminSigned, stackData.url) + .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); + }); + + test(`Attempting to connect without specifying a stack will target api.highfidelity.com`, async () => { + await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminSigned)) + .rejects.toMatchObject({ error: expect.stringMatching(/api.highfidelity.com/) }); + }); + + + test(`Attempting to connect when specifying a WSS stack URL will target the specified stack`, async () => { + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminSigned, stackData.wss + "?token=") + .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); + }); + + test(`CANNOT connect to a space on staging that doesn’t exist (i.e. token contains an invalid space ID)`, async () => { + await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNonexistantSpaceID, stackData.url)) + .rejects.toMatchObject({ error: expect.stringMatching(/Unexpected server response: 501/) }); + }); + + test(`CAN create a space by trying to connect with SIGNED token with nonexistant space NAME and no space ID and correct stack URL`, async () => { + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNewSpaceName, stackData.url) + .then(data => { + expect(data.audionetInitResponse.success).toBe(true); + }); + + // confirm that space was created and get ID for deletion + let createdSpaceID: string; + try { + let spaceWasCreated = false; + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${adminSigned}`); + let spacesListJSON: any = {}; + spacesListJSON = await returnMessage.json(); + spacesListJSON.forEach((space: any) => { + if (space['name'] === TOKEN_GEN_TYPES.NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED["space_name"]) { + spaceWasCreated = true; + createdSpaceID = space['space-id']; + } + }); + + expect(spaceWasCreated).toBe(true); + } catch (err) { + console.error(`Unable to check that a new space was created. Please check your app. ERR: ${err}`); + throw err; + } + + // delete the created space for clean up + try { + await fetch(`${stackData.url}/api/v1/spaces/${createdSpaceID}?token=${adminSigned}`, { + method: 'DELETE' + }); + } catch (err) { + console.error(`Unable to delete the space with ID ${createdSpaceID} that was created for testing. Please do this manually. ERR: ${err}`); + throw err; + } + }); + + test(`CANNOT connect to a space BY NAME when multiple spaces with the same name exist in the same app`, async () => { + await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminDupSpaceName, stackData.url)) + .rejects.toMatchObject({ + error: expect.stringMatching(/Unexpected server response: 501/) + }); + }); + + test(`CAN connect to a space using a timed token before the token expires`, async () => { + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminTimed, stackData.url) + .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); + }); + + test(`CANNOT connect to a space using a timed token after the token expires`, async () => { + await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminExpired, stackData.url)) + .rejects.toMatchObject({ error: expect.stringMatching(/Unexpected server response: 501/) }); + }); + + test(`CAN disconnect once connected`, async () => { + // make sure we're connected first + try { + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminSigned, stackData.url) + } catch (err) { + console.error("Unable to connect before testing disconnect. ERR: ", err); + throw err; + } + await hifiCommunicator.disconnectFromHiFiAudioAPIServer() + .then((data) => { expect(data).toBe('Successfully disconnected.') }); + }); +}); diff --git a/tests/smoke/serverConnections.smoke.test.ts b/tests/smoke/serverConnections.smoke.test.ts new file mode 100644 index 00000000..7c35ee15 --- /dev/null +++ b/tests/smoke/serverConnections.smoke.test.ts @@ -0,0 +1,157 @@ +const fetch = require('node-fetch'); +import { HiFiCommunicator } from "../../src/classes/HiFiCommunicator"; +import { TOKEN_GEN_TYPES, generateJWT } from '../testUtilities/testUtils'; +const stackData = require('../secrets/auth.json').stackData; + +describe('Non admin server connections', () => { + let nonAdminSigned: string; + let adminSigned: string; + let nonAdminUnsigned: string; + let nonAdminNonexistantSpaceID: string; + let nonAdminNewSpaceName: string; + let nonAdminTimed: string; + let nonAdminExpired: string; + let nonAdminDupSpaceName: string; + let hifiCommunicator: HiFiCommunicator; + beforeAll(async () => { + try { + nonAdminSigned = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP2_SPACE1_SIGNED); + adminSigned = await generateJWT(TOKEN_GEN_TYPES.ADMIN_ID_APP2_SPACE1_SIGNED); + nonAdminUnsigned = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP2_SPACE1_UNSIGNED); + nonAdminNonexistantSpaceID = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE_ID_NONEXISTANT_SIGNED); + nonAdminNewSpaceName = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED); + nonAdminTimed = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_TIMED_SIGNED); + nonAdminExpired = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_TIMED_EXPIRED); + nonAdminDupSpaceName = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_DUP_SIGNED); + } catch (err) { + console.error("Unable to create tokens in preparation for testing server connections. Please check " + + "your 'auth.json' file for errors or discrepancies with your account data. ERR: ", err); + throw err; + } + // Make sure the space we try to create later does not already exist, delete it if it does + try { + let spaceAlreadyExistsIDs: Array = []; + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${adminSigned}`); + let spacesListJSON: any = {}; + spacesListJSON = await returnMessage.json(); + spacesListJSON.forEach(async (space: any) => { + if (space['name'] === TOKEN_GEN_TYPES.NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED["space_name"]) { spaceAlreadyExistsIDs.push(space['space-id']) } + }); + + if (spaceAlreadyExistsIDs) { + spaceAlreadyExistsIDs.forEach(async (spaceID) => { + let deleteReturnMessage = await fetch(`${stackData.url}/api/v1/spaces/${spaceID}?token=${adminSigned}`, { + method: 'DELETE' + }); + let deleteReturnMessageJSON: any = {}; + deleteReturnMessageJSON = await deleteReturnMessage.json(); + + expect(deleteReturnMessageJSON['space-id']).toBe(spaceID); + }) + } + } catch (err) { + console.error(`Unable to ensure the nonexistant space does not exist. Please check your app. ERR: ${err}`); + throw err; + } + }); + + beforeEach(() => { + hifiCommunicator = new HiFiCommunicator(); + }) + + afterEach(async () => { + await hifiCommunicator.disconnectFromHiFiAudioAPIServer(); + }); + + // TODO add checks for correct stack connections once those fetch calls are possible (Jira 435) + + test(`CAN connect to Space A on staging with signed token containing Space ID A`, async () => { + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminSigned, stackData.url) + .then(data => { + expect(data.audionetInitResponse.success).toBe(true); + }); + }); + + test(`CANNOT connect to Space A on staging with UNSIGNED token containing Space ID A when space does require signing`, async () => { + // set space to not allow unsigned tokens + try { + await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app2.spaces.space1.id}/settings?token=${adminSigned}&ignore-token-signing=false`); + } catch (err) { + console.error("Unable to set space to ignore token signing. ERR: ", err); + throw err; + } + + await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminUnsigned, stackData.url)) + .rejects.toMatchObject({ error: expect.stringMatching(/Unexpected server response: 501/) }); + }); + + test(`Attempting to connect without specifying a stack will target api.highfidelity.com`, async () => { + await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminSigned)) + .rejects.toMatchObject({ error: expect.stringMatching(/api.highfidelity.com/) }); + }); + + + test(`Attempting to connect when specifying a WSS stack URL will target the specified stack`, async () => { + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminSigned, stackData.wss + "?token=") + .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); + }); + + test(`CAN create a space by trying to connect with SIGNED token with nonexistant space NAME and no space ID and correct stack URL`, async () => { + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNewSpaceName, stackData.url) + .then(data => { + expect(data.audionetInitResponse.success).toBe(true); + }); + + // confirm that space was created and get ID for deletion + let createdSpaceID: string; + try { + let spaceWasCreated = false; + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${adminSigned}`); + let spacesListJSON: any = {}; + spacesListJSON = await returnMessage.json(); + spacesListJSON.forEach((space: any) => { + if (space['name'] === TOKEN_GEN_TYPES.NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED["space_name"]) { + spaceWasCreated = true; + createdSpaceID = space['space-id']; + } + }); + + expect(spaceWasCreated).toBe(true); + } catch (err) { + console.error(`Unable to check that a new space was created. Please check your app. ERR: ${err}`); + throw err; + } + + // delete the created space for clean up + try { + await fetch(`${stackData.url}/api/v1/spaces/${createdSpaceID}?token=${adminSigned}`, { + method: 'DELETE' + }); + } catch (err) { + console.error(`Unable to delete the space with ID ${createdSpaceID} that was created for testing. Please do this manually. ERR: ${err}`); + throw err; + } + }); + + test(`CAN connect to a space using a timed token before the token expires`, async () => { + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminTimed, stackData.url) + .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); + }); + + test(`CANNOT connect to a space using a timed token after the token expires`, async () => { + await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminExpired, stackData.url)) + .rejects.toMatchObject({ error: expect.stringMatching(/Unexpected server response: 501/) }); + }); + + test(`CAN disconnect once connected`, async () => { + // make sure we're connected first + try { + await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminSigned, stackData.url) + } catch (err) { + console.error("Unable to connect before testing disconnect. ERR: ", err); + throw err; + } + await hifiCommunicator.disconnectFromHiFiAudioAPIServer() + .then((data) => { expect(data).toBe('Successfully disconnected.') }); + }); +}); diff --git a/tests/testUtilities/testUtils.ts b/tests/testUtilities/testUtils.ts index 2035e840..8fd38e5d 100644 --- a/tests/testUtilities/testUtils.ts +++ b/tests/testUtilities/testUtils.ts @@ -35,6 +35,64 @@ export const TOKEN_GEN_TYPES = { "app_id": stackData.apps.app1.id, "space_id": stackData.apps.app1.spaces.space1.id, "app_secret": stackData.apps.app1.secret + }, + "NON_ADMIN_ID_APP2_SPACE1_SIGNED": { + "admin": false, + "signed": true, + "user_id": "qateamNonAdmin", + "app_id": stackData.apps.app2.id, + "space_id": stackData.apps.app2.spaces.space1.id, + "app_secret": stackData.apps.app2.secret + }, + "NON_ADMIN_ID_APP2_SPACE1_UNSIGNED": { + "admin": false, + "signed": false, + "user_id": "qateamNonAdmin", + "app_id": stackData.apps.app2.id, + "space_id": stackData.apps.app2.spaces.space1.id, + "app_secret": stackData.apps.app2.secret + }, + "NON_ADMIN_APP2_SPACE_ID_NONEXISTANT_SIGNED": { + "admin": false, + "signed": true, + "user_id": "qateamNonAdmin", + "app_id": stackData.apps.app2.id, + "space_id": stackData.apps.app2.spaces.nonexistant.id, + "app_secret": stackData.apps.app2.secret + }, + "NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED": { + "admin": false, + "signed": true, + "user_id": "qateamNonAdmin", + "app_id": stackData.apps.app2.id, + "app_secret": stackData.apps.app2.secret, + "space_name": "holding space" + }, + "NON_ADMIN_APP2_SPACE1_TIMED_SIGNED": { + "admin": false, + "signed": true, + "user_id": "qateamNonAdmin", + "app_id": stackData.apps.app2.id, + "space_id": stackData.apps.app2.spaces.space1.id, + "app_secret": stackData.apps.app2.secret, + "expired": false + }, + "NON_ADMIN_APP2_SPACE1_TIMED_EXPIRED": { + "admin": false, + "signed": true, + "user_id": "qateamNonAdmin", + "app_id": stackData.apps.app2.id, + "space_id": stackData.apps.app2.spaces.space1.id, + "app_secret": stackData.apps.app2.secret, + "expired": true + }, + "NON_ADMIN_APP2_SPACE1_DUP_SIGNED": { + "admin": false, + "signed": true, + "user_id": "qateamNonAdmin", + "app_id": stackData.apps.app2.id, + "space_name": stackData.apps.app2.spaces.space1.name, + "app_secret": stackData.apps.app2.secret } }; From b87667b743cc79d535b9165d54226e088273343a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 11 Mar 2021 19:30:22 -0500 Subject: [PATCH 21/41] Upload releases JSON to S3 --- utilities/releases.json | 132 ++++++++++++++++++++++++++++++++ utilities/uploadReleasesJSON.js | 61 +++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 utilities/releases.json create mode 100644 utilities/uploadReleasesJSON.js diff --git a/utilities/releases.json b/utilities/releases.json new file mode 100644 index 00000000..6c5ae6ea --- /dev/null +++ b/utilities/releases.json @@ -0,0 +1,132 @@ +{ + "latestRelease": { + "version": "v0.5.1", + "lastModified": "2021-03-11T20:28:50.000Z", + "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/latest/highfidelity-hifi-audio-web.zip", + "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/latest/HighFidelityAudio-latest.js", + "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/latest/HighFidelityControls-latest.js", + "npm": "npm i hifi-spatial-audio" + }, + "allReleases": [ + { + "version": "v0.1.4", + "lastModified": "2021-02-02T17:51:06.000Z", + "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.1.4/highfidelity-hifi-audio-web.zip", + "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.1.4/HighFidelityAudio-latest.js", + "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.1.4/HighFidelityControls-latest.js", + "npm": "npm i hifi-spatial-audio@0.1.4" + }, + { + "version": "v0.1.7", + "lastModified": "2021-02-03T21:54:02.000Z", + "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.1.7/highfidelity-hifi-audio-web.zip", + "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.1.7/HighFidelityAudio-latest.js", + "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.1.7/HighFidelityControls-latest.js", + "npm": "npm i hifi-spatial-audio@0.1.7" + }, + { + "version": "v0.1.8", + "lastModified": "2021-02-05T19:10:26.000Z", + "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.1.8/highfidelity-hifi-audio-web.zip", + "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.1.8/HighFidelityAudio-latest.js", + "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.1.8/HighFidelityControls-latest.js", + "npm": "npm i hifi-spatial-audio@0.1.8" + }, + { + "version": "v0.2.0", + "lastModified": "2021-02-08T17:32:28.000Z", + "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.0/highfidelity-hifi-audio-web.zip", + "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.0/HighFidelityAudio-latest.js", + "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.0/HighFidelityControls-latest.js", + "npm": "npm i hifi-spatial-audio@0.2.0" + }, + { + "version": "v0.2.1", + "lastModified": "2021-02-08T22:11:47.000Z", + "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.1/highfidelity-hifi-audio-web.zip", + "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.1/HighFidelityAudio-latest.js", + "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.1/HighFidelityControls-latest.js", + "npm": "npm i hifi-spatial-audio@0.2.1" + }, + { + "version": "v0.2.2", + "lastModified": "2021-02-09T19:25:19.000Z", + "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.2/highfidelity-hifi-audio-web.zip", + "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.2/HighFidelityAudio-latest.js", + "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.2/HighFidelityControls-latest.js", + "npm": "npm i hifi-spatial-audio@0.2.2" + }, + { + "version": "v0.2.7", + "lastModified": "2021-02-16T20:32:36.000Z", + "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.7/highfidelity-hifi-audio-web.zip", + "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.7/HighFidelityAudio-latest.js", + "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.7/HighFidelityControls-latest.js", + "npm": "npm i hifi-spatial-audio@0.2.7" + }, + { + "version": "v0.2.8", + "lastModified": "2021-02-16T20:44:41.000Z", + "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.8/highfidelity-hifi-audio-web.zip", + "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.8/HighFidelityAudio-latest.js", + "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.8/HighFidelityControls-latest.js", + "npm": "npm i hifi-spatial-audio@0.2.8" + }, + { + "version": "v0.3.0", + "lastModified": "2021-02-26T18:01:52.000Z", + "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.3.0/highfidelity-hifi-audio-web.zip", + "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.3.0/HighFidelityAudio-latest.js", + "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.3.0/HighFidelityControls-latest.js", + "npm": "npm i hifi-spatial-audio@0.3.0" + }, + { + "version": "v0.4.0", + "lastModified": "2021-03-04T20:16:25.000Z", + "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.0/highfidelity-hifi-audio-web.zip", + "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.0/HighFidelityAudio-latest.js", + "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.0/HighFidelityControls-latest.js", + "npm": "npm i hifi-spatial-audio@0.4.0" + }, + { + "version": "v0.4.1", + "lastModified": "2021-03-04T21:32:59.000Z", + "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.1/highfidelity-hifi-audio-web.zip", + "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.1/HighFidelityAudio-latest.js", + "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.1/HighFidelityControls-latest.js", + "npm": "npm i hifi-spatial-audio@0.4.1" + }, + { + "version": "v0.4.2", + "lastModified": "2021-03-05T02:50:00.000Z", + "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.2/highfidelity-hifi-audio-web.zip", + "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.2/HighFidelityAudio-latest.js", + "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.2/HighFidelityControls-latest.js", + "npm": "npm i hifi-spatial-audio@0.4.2" + }, + { + "version": "v0.4.3", + "lastModified": "2021-03-05T15:57:33.000Z", + "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.3/highfidelity-hifi-audio-web.zip", + "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.3/HighFidelityAudio-latest.js", + "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.3/HighFidelityControls-latest.js", + "npm": "npm i hifi-spatial-audio@0.4.3" + }, + { + "version": "v0.5.0", + "lastModified": "2021-03-11T20:09:19.000Z", + "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.5.0/highfidelity-hifi-audio-web.zip", + "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.5.0/HighFidelityAudio-latest.js", + "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.5.0/HighFidelityControls-latest.js", + "npm": "npm i hifi-spatial-audio@0.5.0" + }, + { + "version": "v0.5.1", + "lastModified": "2021-03-11T20:28:50.000Z", + "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.5.1/highfidelity-hifi-audio-web.zip", + "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.5.1/HighFidelityAudio-latest.js", + "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.5.1/HighFidelityControls-latest.js", + "npm": "npm i hifi-spatial-audio@0.5.1" + } + ] +} \ No newline at end of file diff --git a/utilities/uploadReleasesJSON.js b/utilities/uploadReleasesJSON.js new file mode 100644 index 00000000..d475c99e --- /dev/null +++ b/utilities/uploadReleasesJSON.js @@ -0,0 +1,61 @@ +const AWS = require('aws-sdk'); +AWS.config.update({ region: 'us-west-2' }); + +let s3 = new AWS.S3({ apiVersion: '2006-03-01' }); + +const params = { + Bucket: 'hifi-spatial-audio-api', + Prefix: 'releases' +}; +s3.listObjectsV2(params, (err, data) => { + if (err) { + console.error(`Error when listing objects:\n${error}`); + return; + } + + let releaseVersions = []; + for (data of data.Contents) { + let releaseVersion = data.Key.split('/')[1]; + if (releaseVersion !== "latest" && + releaseVersion !== "main" && + releaseVersion.length > 0 && + !releaseVersions.find((version) => { return version.version === releaseVersion; })) { + releaseVersions.push({ + version: releaseVersion, + lastModified: data.LastModified, + webJSZip: `https://hifi-spatial-audio-api.s3.amazonaws.com/releases/${releaseVersion}/highfidelity-hifi-audio-web.zip`, + webJSAudio: `https://hifi-spatial-audio-api.s3.amazonaws.com/releases/${releaseVersion}/HighFidelityAudio-latest.js`, + webJSControls: `https://hifi-spatial-audio-api.s3.amazonaws.com/releases/${releaseVersion}/HighFidelityControls-latest.js`, + npm: `npm i hifi-spatial-audio@${releaseVersion.replace('v', '')}` + }); + } + } + const latestRelease = {}; + Object.assign(latestRelease, releaseVersions[releaseVersions.length - 1]) + latestRelease.webJSZip = `https://hifi-spatial-audio-api.s3.amazonaws.com/releases/latest/highfidelity-hifi-audio-web.zip`; + latestRelease.webJSAudio = `https://hifi-spatial-audio-api.s3.amazonaws.com/releases/latest/HighFidelityAudio-latest.js`; + latestRelease.webJSControls = `https://hifi-spatial-audio-api.s3.amazonaws.com/releases/latest/HighFidelityControls-latest.js`; + latestRelease.npm = `npm i hifi-spatial-audio`; + + const allReleaseInfo = { + "latestRelease": latestRelease, + "allReleases": releaseVersions + }; + + let uploadParams = { + Bucket: 'hifi-spatial-audio-api', + Key: 'releases/releases.json', + Body: JSON.stringify(allReleaseInfo, null, 4), + ContentType: 'application/json', + ACL: 'public-read' + }; + + s3.upload(uploadParams, (err, data) => { + if (err) { + console.error(`Error uploading Releases JSON to S3:\n${err}`); + process.exit(); + } if (data) { + console.log(`Successfully uploaded Releases JSON to S3!`); + } + }); +}); \ No newline at end of file From 78bc4b38e786c38c429715d12abfc5dcd9c67c55 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 12 Mar 2021 13:28:19 -0500 Subject: [PATCH 22/41] Improvements --- .github/workflows/deploy-new-release.yml | 16 ++++++- utilities/uploadReleasesJSON.js | 55 +++++++++++++++++------- 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/.github/workflows/deploy-new-release.yml b/.github/workflows/deploy-new-release.yml index 771734e2..49872698 100644 --- a/.github/workflows/deploy-new-release.yml +++ b/.github/workflows/deploy-new-release.yml @@ -57,8 +57,17 @@ jobs: SOURCE_DIR: ${{runner.workspace}}/hifi-spatial-audio-js/dist DEST_DIR: releases/${{ github.event.release.tag_name }} - - name: Alert about manually-updating website - run: echo "New library files uploaded to S3. This Action does not automatically update links on https://www.highfidelity.com/api/download - please update the website now." + - name: Maybe Upload WebJS Version to Latest S3 Folder + uses: highfidelity/s3-sync-action@master + with: + args: --follow-symlinks --delete + env: + AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} + AWS_ACCESS_KEY_ID: ${{ secrets.IAM_SPATIAL_AUDIO_JS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.IAM_SPATIAL_AUDIO_JS_SECRET }} + AWS_REGION: 'us-west-2' # optional: defaults to us-east-1 + SOURCE_DIR: ${{runner.workspace}}/hifi-spatial-audio-js/dist + DEST_DIR: releases/latest - name: Build NodeJS Version of Library run: npm run build:node @@ -68,4 +77,7 @@ jobs: uses: JS-DevTools/npm-publish@v1 with: token: ${{ secrets.NPM_TOKEN }} + + - name: Create and Upload Releases JSON + run: node ./utilities/uploadReleasesJSON.js \ No newline at end of file diff --git a/utilities/uploadReleasesJSON.js b/utilities/uploadReleasesJSON.js index d475c99e..5c8e5984 100644 --- a/utilities/uploadReleasesJSON.js +++ b/utilities/uploadReleasesJSON.js @@ -16,32 +16,55 @@ s3.listObjectsV2(params, (err, data) => { let releaseVersions = []; for (data of data.Contents) { let releaseVersion = data.Key.split('/')[1]; - if (releaseVersion !== "latest" && - releaseVersion !== "main" && - releaseVersion.length > 0 && - !releaseVersions.find((version) => { return version.version === releaseVersion; })) { - releaseVersions.push({ - version: releaseVersion, - lastModified: data.LastModified, - webJSZip: `https://hifi-spatial-audio-api.s3.amazonaws.com/releases/${releaseVersion}/highfidelity-hifi-audio-web.zip`, - webJSAudio: `https://hifi-spatial-audio-api.s3.amazonaws.com/releases/${releaseVersion}/HighFidelityAudio-latest.js`, - webJSControls: `https://hifi-spatial-audio-api.s3.amazonaws.com/releases/${releaseVersion}/HighFidelityControls-latest.js`, - npm: `npm i hifi-spatial-audio@${releaseVersion.replace('v', '')}` - }); + if (releaseVersion !== "latest" && releaseVersion !== "releases.json" && releaseVersion.length > 0) { + let isMainVersion = releaseVersion === "main"; + + let isRecentEnough = true; + if (!isMainVersion) { + let testString = releaseVersion.replace('v', ''); + let split = testString.split('.'); + let majorVersion = parseInt(split[0]); + let minorVersion = parseInt(split[1]); + let patchVersion = parseInt(split[2]); + + if (majorVersion === 0 && minorVersion < 4) { + isRecentEnough = false; + } + } + + let versionNotAlreadyPushed = false; + if (!releaseVersions.find((version) => { return version.version === releaseVersion; })) { + versionNotAlreadyPushed = true; + } + + if ((isMainVersion || isRecentEnough) && versionNotAlreadyPushed) { + releaseVersions.push({ + version: releaseVersion, + lastModified: data.LastModified, + webJSZip: `${releaseVersion}/highfidelity-hifi-audio-web.zip`, + webJSAudio: `${releaseVersion}/HighFidelityAudio-latest.js`, + webJSControls: `${releaseVersion}/HighFidelityControls-latest.js`, + npm: `npm i hifi-spatial-audio@${releaseVersion.replace('v', '')}` + }); + } } } const latestRelease = {}; Object.assign(latestRelease, releaseVersions[releaseVersions.length - 1]) - latestRelease.webJSZip = `https://hifi-spatial-audio-api.s3.amazonaws.com/releases/latest/highfidelity-hifi-audio-web.zip`; - latestRelease.webJSAudio = `https://hifi-spatial-audio-api.s3.amazonaws.com/releases/latest/HighFidelityAudio-latest.js`; - latestRelease.webJSControls = `https://hifi-spatial-audio-api.s3.amazonaws.com/releases/latest/HighFidelityControls-latest.js`; + latestRelease.webJSZip = `latest/highfidelity-hifi-audio-web.zip`; + latestRelease.webJSAudio = `latest/HighFidelityAudio-latest.js`; + latestRelease.webJSControls = `latest/HighFidelityControls-latest.js`; latestRelease.npm = `npm i hifi-spatial-audio`; - + const allReleaseInfo = { + "releaseJSONVersion": "v1.0.0", + "baseReleaseURL": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/", "latestRelease": latestRelease, "allReleases": releaseVersions }; + console.log(allReleaseInfo) + let uploadParams = { Bucket: 'hifi-spatial-audio-api', Key: 'releases/releases.json', From 2e1901a062475eebf7a63b97e870835150e2dd09 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 12 Mar 2021 13:57:30 -0500 Subject: [PATCH 23/41] Delete extraneous releases.json --- utilities/releases.json | 132 ---------------------------------------- 1 file changed, 132 deletions(-) delete mode 100644 utilities/releases.json diff --git a/utilities/releases.json b/utilities/releases.json deleted file mode 100644 index 6c5ae6ea..00000000 --- a/utilities/releases.json +++ /dev/null @@ -1,132 +0,0 @@ -{ - "latestRelease": { - "version": "v0.5.1", - "lastModified": "2021-03-11T20:28:50.000Z", - "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/latest/highfidelity-hifi-audio-web.zip", - "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/latest/HighFidelityAudio-latest.js", - "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/latest/HighFidelityControls-latest.js", - "npm": "npm i hifi-spatial-audio" - }, - "allReleases": [ - { - "version": "v0.1.4", - "lastModified": "2021-02-02T17:51:06.000Z", - "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.1.4/highfidelity-hifi-audio-web.zip", - "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.1.4/HighFidelityAudio-latest.js", - "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.1.4/HighFidelityControls-latest.js", - "npm": "npm i hifi-spatial-audio@0.1.4" - }, - { - "version": "v0.1.7", - "lastModified": "2021-02-03T21:54:02.000Z", - "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.1.7/highfidelity-hifi-audio-web.zip", - "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.1.7/HighFidelityAudio-latest.js", - "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.1.7/HighFidelityControls-latest.js", - "npm": "npm i hifi-spatial-audio@0.1.7" - }, - { - "version": "v0.1.8", - "lastModified": "2021-02-05T19:10:26.000Z", - "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.1.8/highfidelity-hifi-audio-web.zip", - "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.1.8/HighFidelityAudio-latest.js", - "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.1.8/HighFidelityControls-latest.js", - "npm": "npm i hifi-spatial-audio@0.1.8" - }, - { - "version": "v0.2.0", - "lastModified": "2021-02-08T17:32:28.000Z", - "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.0/highfidelity-hifi-audio-web.zip", - "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.0/HighFidelityAudio-latest.js", - "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.0/HighFidelityControls-latest.js", - "npm": "npm i hifi-spatial-audio@0.2.0" - }, - { - "version": "v0.2.1", - "lastModified": "2021-02-08T22:11:47.000Z", - "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.1/highfidelity-hifi-audio-web.zip", - "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.1/HighFidelityAudio-latest.js", - "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.1/HighFidelityControls-latest.js", - "npm": "npm i hifi-spatial-audio@0.2.1" - }, - { - "version": "v0.2.2", - "lastModified": "2021-02-09T19:25:19.000Z", - "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.2/highfidelity-hifi-audio-web.zip", - "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.2/HighFidelityAudio-latest.js", - "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.2/HighFidelityControls-latest.js", - "npm": "npm i hifi-spatial-audio@0.2.2" - }, - { - "version": "v0.2.7", - "lastModified": "2021-02-16T20:32:36.000Z", - "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.7/highfidelity-hifi-audio-web.zip", - "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.7/HighFidelityAudio-latest.js", - "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.7/HighFidelityControls-latest.js", - "npm": "npm i hifi-spatial-audio@0.2.7" - }, - { - "version": "v0.2.8", - "lastModified": "2021-02-16T20:44:41.000Z", - "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.8/highfidelity-hifi-audio-web.zip", - "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.8/HighFidelityAudio-latest.js", - "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.2.8/HighFidelityControls-latest.js", - "npm": "npm i hifi-spatial-audio@0.2.8" - }, - { - "version": "v0.3.0", - "lastModified": "2021-02-26T18:01:52.000Z", - "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.3.0/highfidelity-hifi-audio-web.zip", - "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.3.0/HighFidelityAudio-latest.js", - "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.3.0/HighFidelityControls-latest.js", - "npm": "npm i hifi-spatial-audio@0.3.0" - }, - { - "version": "v0.4.0", - "lastModified": "2021-03-04T20:16:25.000Z", - "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.0/highfidelity-hifi-audio-web.zip", - "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.0/HighFidelityAudio-latest.js", - "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.0/HighFidelityControls-latest.js", - "npm": "npm i hifi-spatial-audio@0.4.0" - }, - { - "version": "v0.4.1", - "lastModified": "2021-03-04T21:32:59.000Z", - "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.1/highfidelity-hifi-audio-web.zip", - "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.1/HighFidelityAudio-latest.js", - "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.1/HighFidelityControls-latest.js", - "npm": "npm i hifi-spatial-audio@0.4.1" - }, - { - "version": "v0.4.2", - "lastModified": "2021-03-05T02:50:00.000Z", - "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.2/highfidelity-hifi-audio-web.zip", - "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.2/HighFidelityAudio-latest.js", - "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.2/HighFidelityControls-latest.js", - "npm": "npm i hifi-spatial-audio@0.4.2" - }, - { - "version": "v0.4.3", - "lastModified": "2021-03-05T15:57:33.000Z", - "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.3/highfidelity-hifi-audio-web.zip", - "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.3/HighFidelityAudio-latest.js", - "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.4.3/HighFidelityControls-latest.js", - "npm": "npm i hifi-spatial-audio@0.4.3" - }, - { - "version": "v0.5.0", - "lastModified": "2021-03-11T20:09:19.000Z", - "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.5.0/highfidelity-hifi-audio-web.zip", - "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.5.0/HighFidelityAudio-latest.js", - "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.5.0/HighFidelityControls-latest.js", - "npm": "npm i hifi-spatial-audio@0.5.0" - }, - { - "version": "v0.5.1", - "lastModified": "2021-03-11T20:28:50.000Z", - "webJSZip": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.5.1/highfidelity-hifi-audio-web.zip", - "webJSAudio": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.5.1/HighFidelityAudio-latest.js", - "webJSControls": "https://hifi-spatial-audio-api.s3.amazonaws.com/releases/v0.5.1/HighFidelityControls-latest.js", - "npm": "npm i hifi-spatial-audio@0.5.1" - } - ] -} \ No newline at end of file From 7bffc2f54ac33f071d64116f1f9b72eab3ba5253 Mon Sep 17 00:00:00 2001 From: hifibuild Date: Fri, 12 Mar 2021 18:59:35 +0000 Subject: [PATCH 24/41] Bump package version to 0.5.1-9 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index de98bb8b..f98bf7da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-8", + "version": "0.5.1-9", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 84a205a7..76611179 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-8", + "version": "0.5.1-9", "description": "The High Fidelity Audio Client Library allows developers to integrate High Fidelity's spatial audio technology into their projects.", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 05cb02a43307ab9eb62bbc22e9ad186bbad7d7ba Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 12 Mar 2021 14:04:32 -0500 Subject: [PATCH 25/41] Update RELEASE_INSTRUCTIONS with info about the downloads page auto-updating --- RELEASE_INSTRUCTIONS.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RELEASE_INSTRUCTIONS.md b/RELEASE_INSTRUCTIONS.md index 598f0909..620d15a2 100644 --- a/RELEASE_INSTRUCTIONS.md +++ b/RELEASE_INSTRUCTIONS.md @@ -32,6 +32,6 @@ After you click "Publish release", [this GitHub Action](./.github/workflows/depl 6. Create a `.zip` file containing the two Client Library files (base "Audio" library and optional "Controls" library) 7. Upload the `.zip` file and the two Client Library files to the `latest` folder inside the releases S3 bucket. 8. Upload the `.zip` file and the two Client Library files to a folder corresponding to the current version of the Library inside the releases S3 bucket. -9. Echo a message to the GHA logs about the fact that the Action does not automatically update `https://www.highfidelity.com/api/download`, which must be updated manually. -10. Build the NodeJS version of the library. -11. Publish the NodeJS library module to [npmjs.com/package/hifi-spatial-audio](https://www.npmjs.com/package/hifi-spatial-audio). \ No newline at end of file +9. Build the NodeJS version of the library. +10. Publish the NodeJS library module to [npmjs.com/package/hifi-spatial-audio](https://www.npmjs.com/package/hifi-spatial-audio). +11. Creates and uploads a new `releases.json` to [a specific place on S3](https://hifi-spatial-audio-api.s3.amazonaws.com/releases/releases.json), which will then cause [our Downloads page](https://highfidelity.com/api/download) to automatically update for visitors. From e32a1611d94c3d86635f50d47f4e21766e4f3487 Mon Sep 17 00:00:00 2001 From: hifibuild Date: Fri, 12 Mar 2021 19:05:24 +0000 Subject: [PATCH 26/41] Bump package version to 0.5.1-10 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f98bf7da..01cb5838 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-9", + "version": "0.5.1-10", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 76611179..9049a6ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-9", + "version": "0.5.1-10", "description": "The High Fidelity Audio Client Library allows developers to integrate High Fidelity's spatial audio technology into their projects.", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 801ad08e5be99416ce7abefb589f3093d4cfd223 Mon Sep 17 00:00:00 2001 From: RebeccaStankus Date: Fri, 12 Mar 2021 14:10:32 -0800 Subject: [PATCH 27/41] file structure --- .../assets/css/elements/_search.sass | 89 ++++++ .../js/src/typedoc/components/search.ts | 267 ++++++++++++++++++ .../assets/js/src/typedoc/utils/debounce.ts | 7 + typedoc-theme/partials/header.hbs | 2 - 4 files changed, 363 insertions(+), 2 deletions(-) create mode 100644 typedoc-theme/assets/css/elements/_search.sass create mode 100644 typedoc-theme/assets/js/src/typedoc/components/search.ts create mode 100644 typedoc-theme/assets/js/src/typedoc/utils/debounce.ts diff --git a/typedoc-theme/assets/css/elements/_search.sass b/typedoc-theme/assets/css/elements/_search.sass new file mode 100644 index 00000000..f15ef350 --- /dev/null +++ b/typedoc-theme/assets/css/elements/_search.sass @@ -0,0 +1,89 @@ +#tsd-search + transition: background-color 0.2s + + .title + position: relative + z-index: 2 + + .field + position: absolute + left: 0 + top: 0 + right: 40px + height: 40px + + input + box-sizing: border-box + position: relative + top: -50px + z-index: 1 + width: 100% + padding: 0 10px + opacity: 0 + outline: 0 + border: 0 + background: transparent + color: $COLOR_TEXT + + label + position: absolute + overflow: hidden + right: -40px + + .field input, + .title + transition: opacity 0.2s + + .results + position: absolute + visibility: hidden + top: 40px + width: 100% + margin: 0 + padding: 0 + list-style: none + box-shadow: 0 0 4px rgba(#000, 0.25) + + li + padding: 0 10px + background-color: purple + + li:nth-child(even) + background-color: purple + + li.state + display: none + + li.current, + li:hover + background-color: purple + + a + display: block + + &:before + top: 10px + + span.parent + color: $COLOR_TEXT_ASIDE + font-weight: normal + + &.has-focus + background-color: purple + + .field input + top: 0 + opacity: 1 + + .title + z-index: 0 + opacity: 0 + + .results + visibility: visible + + &.loading .results li.state.loading + display: block + + &.failure .results li.state.failure + display: block \ No newline at end of file diff --git a/typedoc-theme/assets/js/src/typedoc/components/search.ts b/typedoc-theme/assets/js/src/typedoc/components/search.ts new file mode 100644 index 00000000..9f968ffa --- /dev/null +++ b/typedoc-theme/assets/js/src/typedoc/components/search.ts @@ -0,0 +1,267 @@ +import { debounce } from "../utils/debounce"; +import { Index } from "lunr"; + +interface IDocument { + id: number; + kind: number; + name: string; + url: string; + classes: string; + parent?: string; +} + +interface IData { + kinds: { [kind: number]: string }; + rows: IDocument[]; + index: object; +} + +declare global { + interface Window { + searchData?: IData; + } +} + +interface SearchState { + base: string; + data?: IData; + index?: Index; +} + +export function initSearch() { + const searchEl = document.getElementById("tsd-search"); + if (!searchEl) return; + + const searchScript = document.getElementById( + "search-script" + ) as HTMLScriptElement | null; + searchEl.classList.add("loading"); + if (searchScript) { + searchScript.addEventListener("error", () => { + searchEl.classList.remove("loading"); + searchEl.classList.add("failure"); + }); + searchScript.addEventListener("load", () => { + searchEl.classList.remove("loading"); + searchEl.classList.add("ready"); + }); + if (window.searchData) { + searchEl.classList.remove("loading"); + } + } + + const field = document.querySelector("#tsd-search-field"); + const results = document.querySelector(".results"); + + if (!field || !results) { + throw new Error( + "The input field or the result list wrapper was not found" + ); + } + + let resultClicked = false; + results.addEventListener("mousedown", () => (resultClicked = true)); + results.addEventListener("mouseup", () => { + resultClicked = false; + searchEl.classList.remove("has-focus"); + }); + + field.addEventListener("focus", () => searchEl.classList.add("has-focus")); + field.addEventListener("blur", () => { + if (!resultClicked) { + resultClicked = false; + searchEl.classList.remove("has-focus"); + } + }); + + const state: SearchState = { + base: searchEl.dataset.base + "/", + }; + + bindEvents(searchEl, results, field, state); +} + +function bindEvents( + searchEl: HTMLElement, + results: HTMLElement, + field: HTMLInputElement, + state: SearchState +) { + field.addEventListener( + "input", + debounce(() => { + updateResults(searchEl, results, field, state); + }, 200) + ); + + let preventPress = false; + field.addEventListener("keydown", (e) => { + preventPress = true; + if (e.key == "Enter") { + // gotoCurrentResult(results, field); + alert("CHANGE IS GOOD"); + } else if (e.key == "Escape") { + field.blur(); + } else if (e.key == "ArrowUp") { + setCurrentResult(results, -1); + } else if (e.key === "ArrowDown") { + setCurrentResult(results, 1); + } else { + preventPress = false; + } + }); + field.addEventListener("keypress", (e) => { + if (preventPress) e.preventDefault(); + }); + + /** + * Start searching by pressing slash. + */ + document.body.addEventListener("keydown", (e) => { + if (e.altKey || e.ctrlKey || e.metaKey) return; + if (!field.matches(":focus") && e.key === "/") { + field.focus(); + e.preventDefault(); + } + }); +} + +function checkIndex(state: SearchState, searchEl: HTMLElement) { + if (state.index) return; + + if (window.searchData) { + searchEl.classList.remove("loading"); + searchEl.classList.add("ready"); + state.data = window.searchData; + state.index = Index.load(window.searchData.index); + } +} + +function updateResults( + searchEl: HTMLElement, + results: HTMLElement, + query: HTMLInputElement, + state: SearchState +) { + checkIndex(state, searchEl); + // Don't clear results if loading state is not ready, + // because loading or error message can be removed. + if (!state.index || !state.data) return; + + results.textContent = ""; + + const searchText = query.value.trim(); + + // Perform a wildcard search + let res = state.index.search(`*${searchText}*`); + + for (let i = 0, c = Math.min(10, res.length); i < c; i++) { + const row = state.data.rows[Number(res[i].ref)]; + + // Bold the matched part of the query in the search results + let name = boldMatches(row.name, searchText); + if (row.parent) { + name = `${boldMatches( + row.parent, + searchText + )}.${name}`; + } + + const item = document.createElement("li"); + item.classList.value = row.classes; + + const anchor = document.createElement("a"); + anchor.href = state.base + row.url; + anchor.classList.add("tsd-kind-icon"); + anchor.innerHTML = name; + item.append(anchor); + + results.appendChild(item); + } +} + +/** + * Move the highlight within the result set. + */ +function setCurrentResult(results: HTMLElement, dir: number) { + let current = results.querySelector(".current"); + if (!current) { + current = results.querySelector( + dir == 1 ? "li:first-child" : "li:last-child" + ); + if (current) { + current.classList.add("current"); + } + } else { + const rel = + dir == 1 + ? current.nextElementSibling + : current.previousElementSibling; + if (rel) { + current.classList.remove("current"); + rel.classList.add("current"); + } + } +} + +/** + * Navigate to the highlighted result. + */ +function gotoCurrentResult(results: HTMLElement, field: HTMLInputElement) { + let current = results.querySelector(".current"); + + if (!current) { + current = results.querySelector("li:first-child"); + } + + if (current) { + const link = current.querySelector("a"); + if (link) { + window.location.href = link.href; + } + field.blur(); + } +} + +function boldMatches(text: string, search: string) { + if (search === "") { + return text; + } + + const lowerText = text.toLocaleLowerCase(); + const lowerSearch = search.toLocaleLowerCase(); + + const parts = []; + let lastIndex = 0; + let index = lowerText.indexOf(lowerSearch); + while (index != -1) { + parts.push( + escapeHtml(text.substring(lastIndex, index)), + `${escapeHtml( + text.substring(index, index + lowerSearch.length) + )}` + ); + + lastIndex = index + lowerSearch.length; + index = lowerText.indexOf(lowerSearch, lastIndex); + } + + parts.push(escapeHtml(text.substring(lastIndex))); + + return parts.join(""); +} + +const SPECIAL_HTML = { + "&": "&", + "<": "<", + ">": ">", + "'": "'", + '"': """, +} as const; + +function escapeHtml(text: string) { + return text.replace( + /[&<>"'"]/g, + (match) => SPECIAL_HTML[match as keyof typeof SPECIAL_HTML] + ); +} diff --git a/typedoc-theme/assets/js/src/typedoc/utils/debounce.ts b/typedoc-theme/assets/js/src/typedoc/utils/debounce.ts new file mode 100644 index 00000000..796348fb --- /dev/null +++ b/typedoc-theme/assets/js/src/typedoc/utils/debounce.ts @@ -0,0 +1,7 @@ +export const debounce = (fn: Function, wait: number = 100) => { + let timeout: ReturnType; + return (...args: any[]) => { + clearTimeout(timeout) + timeout = setTimeout(() => fn(args), wait) + } +} diff --git a/typedoc-theme/partials/header.hbs b/typedoc-theme/partials/header.hbs index f9fc77b5..a475ed73 100644 --- a/typedoc-theme/partials/header.hbs +++ b/typedoc-theme/partials/header.hbs @@ -45,8 +45,6 @@
  • Preparing search index...
  • The search index is not available
  • - -
    {{project.name}}
    From f2c3b6703f68c6175b649806c767f5ab24da17a8 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 15 Mar 2021 17:02:45 -0400 Subject: [PATCH 28/41] Don't upload default root object; upload JS docs to js subdirectory --- utilities/uploadDocsToS3AndInvalidate.js | 54 ++---------------------- 1 file changed, 4 insertions(+), 50 deletions(-) diff --git a/utilities/uploadDocsToS3AndInvalidate.js b/utilities/uploadDocsToS3AndInvalidate.js index 96fbaefb..21276e56 100644 --- a/utilities/uploadDocsToS3AndInvalidate.js +++ b/utilities/uploadDocsToS3AndInvalidate.js @@ -33,10 +33,10 @@ async function uploadCurrentVersionDocs() { let folderInfo = [ { - "name": `v${VERSION}`, + "name": `js/v${VERSION}`, "numFilesUploaded": 0 }, { - "name": `latest`, + "name": `js/latest`, "numFilesUploaded": 0 } ]; @@ -88,50 +88,6 @@ async function uploadCurrentVersionDocs() { }); } -async function uploadDefaultRootObject() { - return new Promise((resolve, reject) => { - console.log(`Creating and uploading default root object to redirect to Docs version \`latest\`...`); - - let uploadBody; - let redirectURL = `https://docs.highfidelity.com/latest/index.html`; - - uploadBody = ` - - - - - - -

    Redirecting you to the latest version of the API documentation at ${redirectURL}...

    - - `; - - let uploadParams = { - Bucket: 'hifi-spatial-audio-api-docs', - ContentType: 'text/html', - Key: 'index.html', - Body: uploadBody, - CacheControl: 'no-cache', - ACL: 'public-read' - }; - - s3.upload(uploadParams, (err, data) => { - if (err) { - console.log(`Error uploading file to S3:\n${err}`); - process.exit(); - return reject(err); - } if (data) { - console.log(`Upload success: ${data.Location}`); - resolve(); - } - }); - }); -} - function invalidateOldDocs() { return new Promise((resolve, reject) => { console.log(`Invaliding old documentation for version \`v${VERSION}\`on CloudFront...`); @@ -139,9 +95,8 @@ function invalidateOldDocs() { let cloudfront = new AWS.CloudFront(); let paths = [ - '/index.html', - `/v${VERSION}/*`, - `/latest/*` + `/js/v${VERSION}/*`, + `/js/latest/*` ]; let params = { @@ -171,7 +126,6 @@ function invalidateOldDocs() { async function start() { await uploadCurrentVersionDocs(); - await uploadDefaultRootObject(); await invalidateOldDocs(); } From 2915dacd0c2aca6db5646c36ddd2926d4f806d1a Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 15 Mar 2021 17:33:01 -0700 Subject: [PATCH 29/41] Fix WebRTC feature requirements --- src/utilities/HiFiUtilities.ts | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/src/utilities/HiFiUtilities.ts b/src/utilities/HiFiUtilities.ts index a5922ad5..56a54687 100644 --- a/src/utilities/HiFiUtilities.ts +++ b/src/utilities/HiFiUtilities.ts @@ -190,26 +190,16 @@ export function preciseInterval(callback: Function, intervalMS: number): any { export function checkBrowserCompatibility(): Boolean { let requiredFeatures: Array = [ // Navigator mediaDevices - "navigator", - "navigator.permissions", - "navigator.permissions.query", - "navigator.mediaDevices.getUserMedia", - "navigator.mediaDevices.getSupportedConstraints", + "navigator", // Found on source code HiFiMixerSession.ts, RaviStreamController.ts + "navigator.permissions", // Found on source code HiFiMixerSession.ts (ln.544) + "navigator.permissions.query", // Found on source code HiFiMixerSession.ts (ln.544) + "navigator.mediaDevices.getUserMedia", // Found on source code HiFiMixerSession.ts (ln.590) + "navigator.mediaDevices.getSupportedConstraints", // Found on source code HiFiUtilities (ln. 130, 134, 138) // WebRTC - "window.MediaStream", - "window.MediaStreamTrack", - "window.RTCDataChannel", - "window.RTCDataChannelEvent", - "window.RTCDtlsTransport", - "window.RTCIceCandidate", - "window.RTCIceTransport", - "window.RTCPeerConnection", - "window.RTCPeerConnectionIceEvent", - "window.RTCRtpReceiver", - "window.RTCRtpSender", - "window.RTCRtpTransceiver", - "window.RTCSctpTransport", - "window.RTCSessionDescription" + "window.MediaStream", // Found on source code HiFiCommunicator.ts, HiFiMixerSession.ts, RaviSession.ts, RaviStreamController.ts + "window.RTCDataChannel", // Found on source code RaviCommandController.ts (ln.151) + "window.RTCPeerConnection", // Found on source code RaviSession.ts (ln.603) + "window.RTCSessionDescription" // Found on source code RaviSession.ts (ln.604) ] for (let i = 0; i < requiredFeatures.length; i++) { if (typeof(eval(requiredFeatures[i])) === "undefined") { From 0253f3e29c2781d2a8e2f519b210dda3c75602ec Mon Sep 17 00:00:00 2001 From: hifibuild Date: Tue, 16 Mar 2021 16:04:03 +0000 Subject: [PATCH 30/41] Bump package version to 0.5.1-11 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 01cb5838..69b8c823 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-10", + "version": "0.5.1-11", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 9049a6ed..a089fbbe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-10", + "version": "0.5.1-11", "description": "The High Fidelity Audio Client Library allows developers to integrate High Fidelity's spatial audio technology into their projects.", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 2e87e5626d04fcda704b8208689221d9763993d9 Mon Sep 17 00:00:00 2001 From: hifibuild Date: Tue, 16 Mar 2021 16:17:23 +0000 Subject: [PATCH 31/41] Bump package version to 0.5.1-12 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 69b8c823..b8a3b13b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-11", + "version": "0.5.1-12", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index a089fbbe..a2004434 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-11", + "version": "0.5.1-12", "description": "The High Fidelity Audio Client Library allows developers to integrate High Fidelity's spatial audio technology into their projects.", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 971c1b0911b0922b3f312eae3f1d53bcd5540d8d Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Tue, 16 Mar 2021 10:18:08 -0700 Subject: [PATCH 32/41] HIFI-439 - remove unhashedVisitID as it shouldn't be used by customers --- src/classes/HiFiMixerSession.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/classes/HiFiMixerSession.ts b/src/classes/HiFiMixerSession.ts index 3aa40b06..8672202d 100644 --- a/src/classes/HiFiMixerSession.ts +++ b/src/classes/HiFiMixerSession.ts @@ -184,7 +184,6 @@ export class HiFiMixerSession { this.mixerInfo["build_type"] = parsedResponse.build_type; this.mixerInfo["build_version"] = parsedResponse.build_version; this.mixerInfo["visit_id_hash"] = parsedResponse.visit_id_hash; - parsedResponse["unhashedVisitID"] = this._raviSession.getUUID(); resolve({ success: true, audionetInitResponse: parsedResponse From f19fb572f3eb63bbb89323fb840be54d84042b09 Mon Sep 17 00:00:00 2001 From: hifibuild Date: Tue, 16 Mar 2021 17:32:07 +0000 Subject: [PATCH 33/41] Bump package version to 0.5.1-13 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b8a3b13b..2b10654a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-12", + "version": "0.5.1-13", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index a2004434..64d522ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-12", + "version": "0.5.1-13", "description": "The High Fidelity Audio Client Library allows developers to integrate High Fidelity's spatial audio technology into their projects.", "main": "./dist/index.js", "types": "./dist/index.d.ts", From b14fd73b9b9ccbe2bb9adfe1dc416dcb043f641f Mon Sep 17 00:00:00 2001 From: RebeccaStankus Date: Tue, 16 Mar 2021 15:21:32 -0700 Subject: [PATCH 34/41] Update tests to work with changed REST responses --- tests/integration/rest.integration.test.ts | 166 +++++++-------------- tests/smoke/rest.smoke.test.ts | 161 +++++++------------- 2 files changed, 111 insertions(+), 216 deletions(-) diff --git a/tests/integration/rest.integration.test.ts b/tests/integration/rest.integration.test.ts index fae131ee..648ff11f 100644 --- a/tests/integration/rest.integration.test.ts +++ b/tests/integration/rest.integration.test.ts @@ -21,16 +21,6 @@ describe('HiFi API REST Calls', () => { } }); - beforeAll(async () => { - try { - adminToken = await generateJWT(TOKEN_GEN_TYPES.ADMIN_ID_APP1_SPACE1_SIGNED); - nonAdminToken = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP1_SPACE1_SIGNED); - } catch (err) { - console.error("Unable to create non admin token for testing REST calls. ERR: ", err); - process.exit(); - } - }); - describe('Creating and deleting spaces', () => { describe('Admin CAN create and delete a space', () => { let newSpaceName = "newSpace"; @@ -60,7 +50,8 @@ describe('HiFi API REST Calls', () => { test(`Create a space`, async () => { let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/create?token=${nonAdminToken}&name=${newSpaceName}`) let returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Delete a space`, async () => { @@ -77,7 +68,8 @@ describe('HiFi API REST Calls', () => { }); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); try { await fetch(`${stackData.url}/api/v1/spaces/${spaceToDelete}?token=${adminToken}`, { method: 'DELETE' @@ -90,6 +82,27 @@ describe('HiFi API REST Calls', () => { }); describe('Reading app spaces', () => { + beforeAll(async () => { + try { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${adminToken}`); + + let spacesListJSON: any = {}; + spacesListJSON = await returnMessage.json(); + spacesListJSON.forEach(async (space: any) => { + let match = false; + for (var key in stackData.apps.app1.spaces) { + if (stackData.apps.app1.spaces[key].id === space['space-id']) { match = true; } + } + if (!match) { + await fetch(`${stackData.url}/api/v1/spaces/${space['space-id']}?token=${adminToken}`, { + method: 'DELETE' + }); + }; + }); + } catch (err) { + console.error("Failed to remove extra spaces before test. Please manually remove them and then rerun the test."); + } + }); describe(`Admin CAN read accurate list of spaces for an app`, () => { test(`Read the list of spaces`, async () => { let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${adminToken}`); @@ -117,9 +130,10 @@ describe('HiFi API REST Calls', () => { describe(`Nonadmin CANNOT read list of spaces for an app`, () => { test(`Read the list of spaces`, async () => { - let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${adminToken}`); + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${nonAdminToken}`); let returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); }); }); @@ -179,13 +193,14 @@ describe('HiFi API REST Calls', () => { }); }); - describe(`Non admin CANNOT read settings for a space`, () => { + describe(`Nonadmin CANNOT read settings for a space`, () => { test(`Read all space settings simultaneously`, async () => { let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}`); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Read the 'space-id' setting`, async () => { @@ -193,7 +208,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Read the 'app-id' setting`, async () => { @@ -201,7 +217,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Read the 'ignore-token-signing' setting`, async () => { @@ -209,7 +226,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Read the 'name' setting`, async () => { @@ -217,7 +235,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Read the 'new-connections-allowed' setting`, async () => { @@ -225,7 +244,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); }); }); @@ -361,7 +381,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Change multiple settings simultaneously using 'POST'`, async () => { @@ -379,7 +400,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Make a space not joinable`, async () => { @@ -387,7 +409,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); @@ -396,7 +419,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Change the space name`, async () => { @@ -405,7 +429,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Set space to ignore token signing`, async () => { @@ -413,7 +438,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Set space to not ignore token signing`, async () => { @@ -421,87 +447,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); - }); - }); - }); - - describe('Kicking users', () => { - const numberTestUsers = 1; - let testUsers: Array = []; - - beforeAll(async () => { - jest.setTimeout(10000); // these tests need longer to complete - }); - - afterAll(async () => { - jest.setTimeout(5000); // restore to default - }); - - beforeEach(async () => { - testUsers = []; - for (let i = 0; i < numberTestUsers; i++) { - let tokenData = TOKEN_GEN_TYPES.USER_APP1_SPACE1_SIGNED - tokenData['user_id'] = generateUUID(); - testUsers.push(new TestUser(tokenData['user_id'])); - let token = await generateJWT(tokenData); - await testUsers[i].communicator.connectToHiFiAudioAPIServer(token, stackData.url); - expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Connected); - } - }); - - afterEach(async () => { - // disconnect communicators to avoid using too many mixers - for (let i = 0; i < numberTestUsers; i++) { - await testUsers[i].communicator.disconnectFromHiFiAudioAPIServer(); - expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Disconnected); - } - }); - - describe('Admin CAN kick users', () => { - test(`Kick one user`, async () => { - await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users${testUsers[0]['user_id']}?token=${adminToken}`, { - method: 'DELETE' - }); - for (let i = 0; i < numberTestUsers; i++) { - if (i === 0) expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Disconnected); - else expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Connected); - } - }); - - test(`Kick all users`, async () => { - await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users?token=${adminToken}`, { - method: 'DELETE' - }); - for (let i = 0; i < numberTestUsers; i++) { - expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Disconnected); - } - }); - }); - - describe('Nonadmin CANNOT kick users', () => { - test(`Kick one user`, async () => { - let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users/${testUsers[0]['user_id']}?token=${nonAdminToken}`, { - method: 'DELETE' - }); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); - for (let i = 0; i < numberTestUsers; i++) { - expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Connected); - } - }); - - test(`Kick all users`, async () => { - let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users?token=${nonAdminToken}`, { - method: 'DELETE' - }); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); - for (let i = 0; i < numberTestUsers; i++) { - expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Connected); - } + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); }); }); @@ -513,7 +460,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe('space/app mismatch'); + expect(returnMessageJSON.code).toBe(422); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/space\/app mismatch/) }); }); }); }); diff --git a/tests/smoke/rest.smoke.test.ts b/tests/smoke/rest.smoke.test.ts index c6ed0262..82e9fadd 100644 --- a/tests/smoke/rest.smoke.test.ts +++ b/tests/smoke/rest.smoke.test.ts @@ -21,16 +21,6 @@ describe('HiFi API REST Calls', () => { } }); - beforeAll(async () => { - try { - adminToken = await generateJWT(TOKEN_GEN_TYPES.ADMIN_ID_APP1_SPACE1_SIGNED); - nonAdminToken = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP1_SPACE1_SIGNED); - } catch (err) { - console.error("Unable to create non admin token for testing REST calls. ERR: ", err); - process.exit(); - } - }); - describe('Creating and deleting spaces', () => { describe('Admin CAN create and delete a space', () => { let newSpaceName = "newSpace"; @@ -60,7 +50,8 @@ describe('HiFi API REST Calls', () => { test(`Create a space`, async () => { let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/create?token=${nonAdminToken}&name=${newSpaceName}`) let returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Delete a space`, async () => { @@ -77,7 +68,8 @@ describe('HiFi API REST Calls', () => { }); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); try { await fetch(`${stackData.url}/api/v1/spaces/${spaceToDelete}?token=${adminToken}`, { method: 'DELETE' @@ -90,6 +82,27 @@ describe('HiFi API REST Calls', () => { }); describe('Reading app spaces', () => { + beforeAll(async () => { + try { + let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${adminToken}`); + + let spacesListJSON: any = {}; + spacesListJSON = await returnMessage.json(); + spacesListJSON.forEach(async (space: any) => { + let match = false; + for (var key in stackData.apps.app1.spaces) { + if (stackData.apps.app1.spaces[key].id === space['space-id']) { match = true; } + } + if (!match) { + await fetch(`${stackData.url}/api/v1/spaces/${space['space-id']}?token=${adminToken}`, { + method: 'DELETE' + }); + }; + }); + } catch (err) { + console.error("Failed to remove extra spaces before test. Please manually remove them and then rerun the test."); + } + }); describe(`Admin CAN read accurate list of spaces for an app`, () => { test(`Read the list of spaces`, async () => { let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/?token=${adminToken}`); @@ -171,13 +184,14 @@ describe('HiFi API REST Calls', () => { }); }); - describe(`Non admin CANNOT read settings for a space`, () => { + describe(`Nonadmin CANNOT read settings for a space`, () => { test(`Read all space settings simultaneously`, async () => { let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/settings?token=${nonAdminToken}`); let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Read the 'space-id' setting`, async () => { @@ -185,7 +199,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Read the 'app-id' setting`, async () => { @@ -193,7 +208,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Read the 'ignore-token-signing' setting`, async () => { @@ -201,7 +217,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Read the 'name' setting`, async () => { @@ -209,7 +226,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Read the 'new-connections-allowed' setting`, async () => { @@ -217,7 +235,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); }); }); @@ -353,7 +372,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Change multiple settings simultaneously using 'POST'`, async () => { @@ -371,7 +391,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Make a space not joinable`, async () => { @@ -379,7 +400,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); @@ -388,7 +410,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Change the space name`, async () => { @@ -397,7 +420,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Set space to ignore token signing`, async () => { @@ -405,7 +429,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); test(`Set space to not ignore token signing`, async () => { @@ -413,87 +438,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); - }); - }); - }); - - describe('Kicking users', () => { - const numberTestUsers = 1; - let testUsers: Array = []; - - beforeAll(async () => { - jest.setTimeout(10000); // these tests need longer to complete - }); - - afterAll(async () => { - jest.setTimeout(5000); // restore to default - }); - - beforeEach(async () => { - testUsers = []; - for (let i = 0; i < numberTestUsers; i++) { - let tokenData = TOKEN_GEN_TYPES.USER_APP1_SPACE1_SIGNED - tokenData['user_id'] = generateUUID(); - testUsers.push(new TestUser(tokenData['user_id'])); - let token = await generateJWT(tokenData); - await testUsers[i].communicator.connectToHiFiAudioAPIServer(token, stackData.url); - expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Connected); - } - }); - - afterEach(async () => { - // disconnect communicators to avoid using too many mixers - for (let i = 0; i < numberTestUsers; i++) { - await testUsers[i].communicator.disconnectFromHiFiAudioAPIServer(); - expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Disconnected); - } - }); - - describe('Admin CAN kick users', () => { - test(`Kick one user`, async () => { - await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users${testUsers[0]['user_id']}?token=${adminToken}`, { - method: 'DELETE' - }); - for (let i = 0; i < numberTestUsers; i++) { - if (i === 0) expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Disconnected); - else expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Connected); - } - }); - - test(`Kick all users`, async () => { - await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users?token=${adminToken}`, { - method: 'DELETE' - }); - for (let i = 0; i < numberTestUsers; i++) { - expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Disconnected); - } - }); - }); - - describe('Nonadmin CANNOT kick users', () => { - test(`Kick one user`, async () => { - let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users/${testUsers[0]['user_id']}?token=${nonAdminToken}`, { - method: 'DELETE' - }); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); - for (let i = 0; i < numberTestUsers; i++) { - expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Connected); - } - }); - - test(`Kick all users`, async () => { - let returnMessage = await fetch(`${stackData.url}/api/v1/spaces/${stackData.apps.app1.spaces.space1.id}/users?token=${nonAdminToken}`, { - method: 'DELETE' - }); - let returnMessageJSON: any = {}; - returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe("token isn't an admin token"); - for (let i = 0; i < numberTestUsers; i++) { - expect(testUsers[i].connectionState).toBe(HiFiConnectionStates.Connected); - } + expect(returnMessageJSON.code).toBe(401); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/token isn't an admin token/) }); }); }); }); @@ -505,7 +451,8 @@ describe('HiFi API REST Calls', () => { let returnMessageJSON: any = {}; returnMessageJSON = await returnMessage.json(); - expect(returnMessageJSON.error).toBe('space/app mismatch'); + expect(returnMessageJSON.code).toBe(422); + expect(returnMessageJSON.errors).toMatchObject({ description: expect.stringMatching(/space\/app mismatch/) }); }); }); }); From 3944c986ea906eda0809059364e0cdb9fcfb5e05 Mon Sep 17 00:00:00 2001 From: hifibuild Date: Tue, 16 Mar 2021 23:41:54 +0000 Subject: [PATCH 35/41] Bump package version to 0.5.1-14 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b10654a..002e5c87 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-13", + "version": "0.5.1-14", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 64d522ed..1348be10 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-13", + "version": "0.5.1-14", "description": "The High Fidelity Audio Client Library allows developers to integrate High Fidelity's spatial audio technology into their projects.", "main": "./dist/index.js", "types": "./dist/index.d.ts", From eab7d00534b0d2568c4b398e336e8db7c6f0e971 Mon Sep 17 00:00:00 2001 From: RebeccaStankus Date: Wed, 17 Mar 2021 10:18:15 -0700 Subject: [PATCH 36/41] Some tests cleanup --- .../HiFiCommunicator.integration.test.ts | 10 ++++----- tests/secrets/auth_example.json | 22 ++++++++++--------- tests/smoke/serverConnections.smoke.test.ts | 8 +++---- tests/testUtilities/testUtils.ts | 4 ++-- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/tests/integration/src/classes/HiFiCommunicator.integration.test.ts b/tests/integration/src/classes/HiFiCommunicator.integration.test.ts index af32142e..5de03e75 100644 --- a/tests/integration/src/classes/HiFiCommunicator.integration.test.ts +++ b/tests/integration/src/classes/HiFiCommunicator.integration.test.ts @@ -7,7 +7,7 @@ describe('Non admin server connections', () => { let nonAdminSigned: string; let adminSigned: string; let nonAdminUnsigned: string; - let nonAdminNonexistantSpaceID: string; + let nonAdminNonexistentSpaceID: string; let nonAdminNewSpaceName: string; let nonAdminTimed: string; let nonAdminExpired: string; @@ -18,7 +18,7 @@ describe('Non admin server connections', () => { nonAdminSigned = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP2_SPACE1_SIGNED); adminSigned = await generateJWT(TOKEN_GEN_TYPES.ADMIN_ID_APP2_SPACE1_SIGNED); nonAdminUnsigned = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP2_SPACE1_UNSIGNED); - nonAdminNonexistantSpaceID = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE_ID_NONEXISTANT_SIGNED); + nonAdminNonexistentSpaceID = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE_ID_NONEXISTENT_SIGNED); nonAdminNewSpaceName = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED); nonAdminTimed = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_TIMED_SIGNED); nonAdminExpired = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_TIMED_EXPIRED); @@ -50,7 +50,7 @@ describe('Non admin server connections', () => { }) } } catch (err) { - console.error(`Unable to ensure the nonexistant space does not exist. Please check your app. ERR: ${err}`); + console.error(`Unable to ensure the nonexistent space does not exist. Please check your app. ERR: ${err}`); throw err; } }); @@ -123,11 +123,11 @@ describe('Non admin server connections', () => { }); test(`CANNOT connect to a space on staging that doesn’t exist (i.e. token contains an invalid space ID)`, async () => { - await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNonexistantSpaceID, stackData.url)) + await expect(hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNonexistentSpaceID, stackData.url)) .rejects.toMatchObject({ error: expect.stringMatching(/Unexpected server response: 501/) }); }); - test(`CAN create a space by trying to connect with SIGNED token with nonexistant space NAME and no space ID and correct stack URL`, async () => { + test(`CAN create a space by trying to connect with SIGNED token with nonexistent space NAME and no space ID and correct stack URL`, async () => { await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNewSpaceName, stackData.url) .then(data => { expect(data.audionetInitResponse.success).toBe(true); diff --git a/tests/secrets/auth_example.json b/tests/secrets/auth_example.json index 0e440026..ed3f3489 100644 --- a/tests/secrets/auth_example.json +++ b/tests/secrets/auth_example.json @@ -2,9 +2,10 @@ "stackData": { "name": "", "url": "", + "wss": "", "apps": { "app1": { - "notes": "used for REST integration tests", + "notes": "used for REST tests", "name": "Staging_QA_App1", "id": "aaaaaaaa-1111-bbbb-2222-cccccccccccc", "secret": "aaaaaaaa-1111-bbbb-2222-cccccccccccc", @@ -14,22 +15,17 @@ "id": "aaaaaaaa-1111-bbbb-2222-cccccccccccc" }, "space2": { - "notes": "purposely duplicated name", - "name": "Staging_QA_App1_Space1", - "id": "aaaaaaaa-1111-bbbb-2222-cccccccccccc" - }, - "space3": { "name": "Staging_QA_App1_Space2", "id": "aaaaaaaa-1111-bbbb-2222-cccccccccccc" }, - "space4": { + "space3": { "name": "Staging_QA_App1_Space3", "id": "aaaaaaaa-1111-bbbb-2222-cccccccccccc" } } }, "app2": { - "notes": "used for HFiCommunicator integration tests", + "notes": "used for server connection tests", "name": "Staging_QA_App2", "id": "aaaaaaaa-1111-bbbb-2222-cccccccccccc", "secret": "aaaaaaaa-1111-bbbb-2222-cccccccccccc", @@ -38,8 +34,14 @@ "name": "Staging_QA_App2_Space1", "id": "aaaaaaaa-1111-bbbb-2222-cccccccccccc" }, - "nonexistant": { - "name": "Nonexistant", + "space2": { + "notes": "purposely duplicated name", + "name": "Staging_QA_App2_Space1", + "id": "aaaaaaaa-1111-bbbb-2222-cccccccccccc" + }, + "nonexistent": { + "notes": "use any id that is not in use", + "name": "this space does not exist", "id": "aaaaaaaa-1111-bbbb-2222-cccccccccccc" } } diff --git a/tests/smoke/serverConnections.smoke.test.ts b/tests/smoke/serverConnections.smoke.test.ts index 7c35ee15..432c4f49 100644 --- a/tests/smoke/serverConnections.smoke.test.ts +++ b/tests/smoke/serverConnections.smoke.test.ts @@ -7,7 +7,7 @@ describe('Non admin server connections', () => { let nonAdminSigned: string; let adminSigned: string; let nonAdminUnsigned: string; - let nonAdminNonexistantSpaceID: string; + let nonAdminNonexistentSpaceID: string; let nonAdminNewSpaceName: string; let nonAdminTimed: string; let nonAdminExpired: string; @@ -18,7 +18,7 @@ describe('Non admin server connections', () => { nonAdminSigned = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP2_SPACE1_SIGNED); adminSigned = await generateJWT(TOKEN_GEN_TYPES.ADMIN_ID_APP2_SPACE1_SIGNED); nonAdminUnsigned = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_ID_APP2_SPACE1_UNSIGNED); - nonAdminNonexistantSpaceID = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE_ID_NONEXISTANT_SIGNED); + nonAdminNonexistentSpaceID = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE_ID_NONEXISTENT_SIGNED); nonAdminNewSpaceName = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED); nonAdminTimed = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_TIMED_SIGNED); nonAdminExpired = await generateJWT(TOKEN_GEN_TYPES.NON_ADMIN_APP2_SPACE1_TIMED_EXPIRED); @@ -50,7 +50,7 @@ describe('Non admin server connections', () => { }) } } catch (err) { - console.error(`Unable to ensure the nonexistant space does not exist. Please check your app. ERR: ${err}`); + console.error(`Unable to ensure the nonexistent space does not exist. Please check your app. ERR: ${err}`); throw err; } }); @@ -96,7 +96,7 @@ describe('Non admin server connections', () => { .then(data => { expect(data.audionetInitResponse.success).toBe(true) }); }); - test(`CAN create a space by trying to connect with SIGNED token with nonexistant space NAME and no space ID and correct stack URL`, async () => { + test(`CAN create a space by trying to connect with SIGNED token with nonexistent space NAME and no space ID and correct stack URL`, async () => { await hifiCommunicator.connectToHiFiAudioAPIServer(nonAdminNewSpaceName, stackData.url) .then(data => { expect(data.audionetInitResponse.success).toBe(true); diff --git a/tests/testUtilities/testUtils.ts b/tests/testUtilities/testUtils.ts index 8fd38e5d..cac34f4b 100644 --- a/tests/testUtilities/testUtils.ts +++ b/tests/testUtilities/testUtils.ts @@ -52,12 +52,12 @@ export const TOKEN_GEN_TYPES = { "space_id": stackData.apps.app2.spaces.space1.id, "app_secret": stackData.apps.app2.secret }, - "NON_ADMIN_APP2_SPACE_ID_NONEXISTANT_SIGNED": { + "NON_ADMIN_APP2_SPACE_ID_NONEXISTENT_SIGNED": { "admin": false, "signed": true, "user_id": "qateamNonAdmin", "app_id": stackData.apps.app2.id, - "space_id": stackData.apps.app2.spaces.nonexistant.id, + "space_id": stackData.apps.app2.spaces.nonexistent.id, "app_secret": stackData.apps.app2.secret }, "NON_ADMIN_APP2_NEW_SPACE_NAME_SIGNED": { From 1d6de9c355ed954d20bcee02389e52c90b521131 Mon Sep 17 00:00:00 2001 From: hifibuild Date: Wed, 17 Mar 2021 19:17:45 +0000 Subject: [PATCH 37/41] Bump package version to 0.5.1-15 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 002e5c87..b6efaf62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-14", + "version": "0.5.1-15", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 1348be10..77b676da 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hifi-spatial-audio", - "version": "0.5.1-14", + "version": "0.5.1-15", "description": "The High Fidelity Audio Client Library allows developers to integrate High Fidelity's spatial audio technology into their projects.", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 254184cdd7740ce9fd73705bd171357908a71ee1 Mon Sep 17 00:00:00 2001 From: RebeccaStankus Date: Tue, 16 Mar 2021 15:45:26 -0700 Subject: [PATCH 38/41] Make search more discoverable --- .../assets/css/elements/_search.sass | 89 ------ typedoc-theme/assets/css/hifi.css | 39 +++ .../js/src/typedoc/components/search.ts | 267 ------------------ .../assets/js/src/typedoc/utils/debounce.ts | 7 - typedoc-theme/partials/header.hbs | 8 +- 5 files changed, 43 insertions(+), 367 deletions(-) delete mode 100644 typedoc-theme/assets/css/elements/_search.sass delete mode 100644 typedoc-theme/assets/js/src/typedoc/components/search.ts delete mode 100644 typedoc-theme/assets/js/src/typedoc/utils/debounce.ts diff --git a/typedoc-theme/assets/css/elements/_search.sass b/typedoc-theme/assets/css/elements/_search.sass deleted file mode 100644 index f15ef350..00000000 --- a/typedoc-theme/assets/css/elements/_search.sass +++ /dev/null @@ -1,89 +0,0 @@ -#tsd-search - transition: background-color 0.2s - - .title - position: relative - z-index: 2 - - .field - position: absolute - left: 0 - top: 0 - right: 40px - height: 40px - - input - box-sizing: border-box - position: relative - top: -50px - z-index: 1 - width: 100% - padding: 0 10px - opacity: 0 - outline: 0 - border: 0 - background: transparent - color: $COLOR_TEXT - - label - position: absolute - overflow: hidden - right: -40px - - .field input, - .title - transition: opacity 0.2s - - .results - position: absolute - visibility: hidden - top: 40px - width: 100% - margin: 0 - padding: 0 - list-style: none - box-shadow: 0 0 4px rgba(#000, 0.25) - - li - padding: 0 10px - background-color: purple - - li:nth-child(even) - background-color: purple - - li.state - display: none - - li.current, - li:hover - background-color: purple - - a - display: block - - &:before - top: 10px - - span.parent - color: $COLOR_TEXT_ASIDE - font-weight: normal - - &.has-focus - background-color: purple - - .field input - top: 0 - opacity: 1 - - .title - z-index: 0 - opacity: 0 - - .results - visibility: visible - - &.loading .results li.state.loading - display: block - - &.failure .results li.state.failure - display: block \ No newline at end of file diff --git a/typedoc-theme/assets/css/hifi.css b/typedoc-theme/assets/css/hifi.css index 4f67c738..bac954b9 100644 --- a/typedoc-theme/assets/css/hifi.css +++ b/typedoc-theme/assets/css/hifi.css @@ -10,22 +10,27 @@ font-family: Graphik; src: url('https://hifi-spatial-audio-api.s3.us-west-2.amazonaws.com/includes/fonts/Graphik-Regular.ttf') } + @font-face { font-family: Graphik-Medium; src: url('https://hifi-spatial-audio-api.s3.us-west-2.amazonaws.com/includes/fonts/Graphik-Medium.ttf') } + @font-face { font-family: Graphik-Semibold; src: url('https://hifi-spatial-audio-api.s3.us-west-2.amazonaws.com/includes/fonts/Graphik-Semibold.ttf') } + @font-face { font-family: Graphik-Bold; src: url('https://hifi-spatial-audio-api.s3.us-west-2.amazonaws.com/includes/fonts/Graphik-Bold.ttf') } + @font-face { font-family: RobotoMono; src: url('https://hifi-spatial-audio-api.s3.us-west-2.amazonaws.com/includes/fonts/RobotoMono-Regular.ttf') } + @font-face { font-family: RobotoMono-Bold; src: url('https://hifi-spatial-audio-api.s3.us-west-2.amazonaws.com/includes/fonts/RobotoMono-Bold.ttf') @@ -34,3 +39,37 @@ body { font-family: 'Graphik', "Segoe UI", sans-serif; } + +@media screen and (min-width: 600px) { + .search { + width: 500px !important; + } + .searchDropdown { + padding-right: 90px; + } +} + +.table-wrap { + width: auto !important; +} + +#tsd-search.has-focus { + background-color: white; +} + +#tsd-search .field input { + top: 0; + opacity: 1 !important; +} + +#tsd-search .field label { + left: 100%; +} + +.results li { + overflow: hidden; +} + +.results li:hover { + overflow: visible; +} \ No newline at end of file diff --git a/typedoc-theme/assets/js/src/typedoc/components/search.ts b/typedoc-theme/assets/js/src/typedoc/components/search.ts deleted file mode 100644 index 9f968ffa..00000000 --- a/typedoc-theme/assets/js/src/typedoc/components/search.ts +++ /dev/null @@ -1,267 +0,0 @@ -import { debounce } from "../utils/debounce"; -import { Index } from "lunr"; - -interface IDocument { - id: number; - kind: number; - name: string; - url: string; - classes: string; - parent?: string; -} - -interface IData { - kinds: { [kind: number]: string }; - rows: IDocument[]; - index: object; -} - -declare global { - interface Window { - searchData?: IData; - } -} - -interface SearchState { - base: string; - data?: IData; - index?: Index; -} - -export function initSearch() { - const searchEl = document.getElementById("tsd-search"); - if (!searchEl) return; - - const searchScript = document.getElementById( - "search-script" - ) as HTMLScriptElement | null; - searchEl.classList.add("loading"); - if (searchScript) { - searchScript.addEventListener("error", () => { - searchEl.classList.remove("loading"); - searchEl.classList.add("failure"); - }); - searchScript.addEventListener("load", () => { - searchEl.classList.remove("loading"); - searchEl.classList.add("ready"); - }); - if (window.searchData) { - searchEl.classList.remove("loading"); - } - } - - const field = document.querySelector("#tsd-search-field"); - const results = document.querySelector(".results"); - - if (!field || !results) { - throw new Error( - "The input field or the result list wrapper was not found" - ); - } - - let resultClicked = false; - results.addEventListener("mousedown", () => (resultClicked = true)); - results.addEventListener("mouseup", () => { - resultClicked = false; - searchEl.classList.remove("has-focus"); - }); - - field.addEventListener("focus", () => searchEl.classList.add("has-focus")); - field.addEventListener("blur", () => { - if (!resultClicked) { - resultClicked = false; - searchEl.classList.remove("has-focus"); - } - }); - - const state: SearchState = { - base: searchEl.dataset.base + "/", - }; - - bindEvents(searchEl, results, field, state); -} - -function bindEvents( - searchEl: HTMLElement, - results: HTMLElement, - field: HTMLInputElement, - state: SearchState -) { - field.addEventListener( - "input", - debounce(() => { - updateResults(searchEl, results, field, state); - }, 200) - ); - - let preventPress = false; - field.addEventListener("keydown", (e) => { - preventPress = true; - if (e.key == "Enter") { - // gotoCurrentResult(results, field); - alert("CHANGE IS GOOD"); - } else if (e.key == "Escape") { - field.blur(); - } else if (e.key == "ArrowUp") { - setCurrentResult(results, -1); - } else if (e.key === "ArrowDown") { - setCurrentResult(results, 1); - } else { - preventPress = false; - } - }); - field.addEventListener("keypress", (e) => { - if (preventPress) e.preventDefault(); - }); - - /** - * Start searching by pressing slash. - */ - document.body.addEventListener("keydown", (e) => { - if (e.altKey || e.ctrlKey || e.metaKey) return; - if (!field.matches(":focus") && e.key === "/") { - field.focus(); - e.preventDefault(); - } - }); -} - -function checkIndex(state: SearchState, searchEl: HTMLElement) { - if (state.index) return; - - if (window.searchData) { - searchEl.classList.remove("loading"); - searchEl.classList.add("ready"); - state.data = window.searchData; - state.index = Index.load(window.searchData.index); - } -} - -function updateResults( - searchEl: HTMLElement, - results: HTMLElement, - query: HTMLInputElement, - state: SearchState -) { - checkIndex(state, searchEl); - // Don't clear results if loading state is not ready, - // because loading or error message can be removed. - if (!state.index || !state.data) return; - - results.textContent = ""; - - const searchText = query.value.trim(); - - // Perform a wildcard search - let res = state.index.search(`*${searchText}*`); - - for (let i = 0, c = Math.min(10, res.length); i < c; i++) { - const row = state.data.rows[Number(res[i].ref)]; - - // Bold the matched part of the query in the search results - let name = boldMatches(row.name, searchText); - if (row.parent) { - name = `${boldMatches( - row.parent, - searchText - )}.${name}`; - } - - const item = document.createElement("li"); - item.classList.value = row.classes; - - const anchor = document.createElement("a"); - anchor.href = state.base + row.url; - anchor.classList.add("tsd-kind-icon"); - anchor.innerHTML = name; - item.append(anchor); - - results.appendChild(item); - } -} - -/** - * Move the highlight within the result set. - */ -function setCurrentResult(results: HTMLElement, dir: number) { - let current = results.querySelector(".current"); - if (!current) { - current = results.querySelector( - dir == 1 ? "li:first-child" : "li:last-child" - ); - if (current) { - current.classList.add("current"); - } - } else { - const rel = - dir == 1 - ? current.nextElementSibling - : current.previousElementSibling; - if (rel) { - current.classList.remove("current"); - rel.classList.add("current"); - } - } -} - -/** - * Navigate to the highlighted result. - */ -function gotoCurrentResult(results: HTMLElement, field: HTMLInputElement) { - let current = results.querySelector(".current"); - - if (!current) { - current = results.querySelector("li:first-child"); - } - - if (current) { - const link = current.querySelector("a"); - if (link) { - window.location.href = link.href; - } - field.blur(); - } -} - -function boldMatches(text: string, search: string) { - if (search === "") { - return text; - } - - const lowerText = text.toLocaleLowerCase(); - const lowerSearch = search.toLocaleLowerCase(); - - const parts = []; - let lastIndex = 0; - let index = lowerText.indexOf(lowerSearch); - while (index != -1) { - parts.push( - escapeHtml(text.substring(lastIndex, index)), - `${escapeHtml( - text.substring(index, index + lowerSearch.length) - )}` - ); - - lastIndex = index + lowerSearch.length; - index = lowerText.indexOf(lowerSearch, lastIndex); - } - - parts.push(escapeHtml(text.substring(lastIndex))); - - return parts.join(""); -} - -const SPECIAL_HTML = { - "&": "&", - "<": "<", - ">": ">", - "'": "'", - '"': """, -} as const; - -function escapeHtml(text: string) { - return text.replace( - /[&<>"'"]/g, - (match) => SPECIAL_HTML[match as keyof typeof SPECIAL_HTML] - ); -} diff --git a/typedoc-theme/assets/js/src/typedoc/utils/debounce.ts b/typedoc-theme/assets/js/src/typedoc/utils/debounce.ts deleted file mode 100644 index 796348fb..00000000 --- a/typedoc-theme/assets/js/src/typedoc/utils/debounce.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const debounce = (fn: Function, wait: number = 100) => { - let timeout: ReturnType; - return (...args: any[]) => { - clearTimeout(timeout) - timeout = setTimeout(() => fn(args), wait) - } -} diff --git a/typedoc-theme/partials/header.hbs b/typedoc-theme/partials/header.hbs index a475ed73..60553da1 100644 --- a/typedoc-theme/partials/header.hbs +++ b/typedoc-theme/partials/header.hbs @@ -34,11 +34,11 @@
    - -
    +
    Options
    From ebeff4044034b624e2b5cffd091d81861c5b44b9 Mon Sep 17 00:00:00 2001 From: RebeccaStankus Date: Thu, 18 Mar 2021 11:03:46 -0700 Subject: [PATCH 39/41] Center search and dropdown menu --- typedoc-theme/assets/css/hifi.css | 16 ++++++++++++++++ typedoc-theme/partials/header.hbs | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/typedoc-theme/assets/css/hifi.css b/typedoc-theme/assets/css/hifi.css index bac954b9..6ce4113a 100644 --- a/typedoc-theme/assets/css/hifi.css +++ b/typedoc-theme/assets/css/hifi.css @@ -53,6 +53,10 @@ body { width: auto !important; } +#searchTable { + margin: 0 auto; +} + #tsd-search.has-focus { background-color: white; } @@ -60,12 +64,24 @@ body { #tsd-search .field input { top: 0; opacity: 1 !important; + height: 30px; + box-shadow: 0 0 2px rgb(0 0 0 / 35%); + border-radius: 4px; +} + +#tsd-search .field input:focus { + border: solid 2px gray; + border-radius: 4px; } #tsd-search .field label { left: 100%; } +.results { + margin-left: -7px; +} + .results li { overflow: hidden; } diff --git a/typedoc-theme/partials/header.hbs b/typedoc-theme/partials/header.hbs index 60553da1..2a10fac5 100644 --- a/typedoc-theme/partials/header.hbs +++ b/typedoc-theme/partials/header.hbs @@ -33,7 +33,7 @@
    -
    +