From f3b3caa946336ffe129555735a59ef2b77563f4e Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Tue, 12 Jan 2021 15:39:49 -0500 Subject: [PATCH 001/102] chore(core): Automate Daily Integration Tests (#1130) * Automate daily integration tests * Rename to nightly * Change to 6am and 8pm PT & remove tar verification * Fix schedule comment --- .github/workflows/nightly.yml | 68 +++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 .github/workflows/nightly.yml diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 0000000000..10221dac24 --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,68 @@ +# Copyright 2021 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Nightly Builds + +on: + # Runs every day at 06:00 AM (PT) and 08:00 PM (PT) / 04:00 AM (UTC) and 02:00 PM (UTC) + schedule: + - cron: "0 4,14 * * *" + +jobs: + nightly: + + runs-on: ubuntu-latest + + steps: + - name: Checkout source for staging + uses: actions/checkout@v2 + with: + ref: ${{ github.event.client_payload.ref || github.ref }} + + - name: Set up Node.js + uses: actions/setup-node@v1 + with: + node-version: 10.x + + - name: Install and build + run: | + npm ci + npm run build + npm run build:tests + + - name: Run unit tests + run: npm test + + - name: Verify public API + run: npm run api-extractor + + - name: Run integration tests + run: ./.github/scripts/run_integration_tests.sh + env: + FIREBASE_SERVICE_ACCT_KEY: ${{ secrets.FIREBASE_SERVICE_ACCT_KEY }} + FIREBASE_API_KEY: ${{ secrets.FIREBASE_API_KEY }} + + - name: Package release artifacts + run: | + npm pack + mkdir -p dist + cp *.tgz dist/ + + # Attach the packaged artifacts to the workflow output. These can be manually + # downloaded for later inspection if necessary. + - name: Archive artifacts + uses: actions/upload-artifact@v1 + with: + name: dist + path: dist From 8ae44ce3ba944dde69cf921747650ed0b152d731 Mon Sep 17 00:00:00 2001 From: egilmorez Date: Thu, 14 Jan 2021 15:22:32 -0800 Subject: [PATCH 002/102] Updating Google Cloud naming (#1122) * Reinstating tag that devsite needs present to supress machine translation. * Updating a couple of references to GCP/Google Cloud Platform per new branding guidelines. --- CONTRIBUTING.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 42d573c035..dbb374fa14 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -146,7 +146,7 @@ following credentials from the project: 2. *Web API key*: This is displayed in the "Settings > General" tab of the console. Copy it and save to a new text file at `test/resources/apikey.txt`. -Then set up your Firebase/GCP project as follows: +Then set up your Firebase/Google Cloud project as follows: 1. Enable Firestore: Go to the Firebase Console, and select "Database" from the "Develop" menu. Click on the "Create database" button. You may choose @@ -160,15 +160,15 @@ Then set up your Firebase/GCP project as follows: https://console.developers.google.com/apis/api/firebaseml.googleapis.com/overview) and make sure your project is selected. If the API is not already enabled, click Enable. 4. Enable the IAM API: Go to the - [Google Cloud Platform Console](https://console.cloud.google.com) and make - sure your Firebase/GCP project is selected. Select "APIs & Services > + [Google Cloud Console](https://console.cloud.google.com) and make + sure your Firebase/Google Cloud project is selected. Select "APIs & Services > Dashboard" from the main menu, and click the "ENABLE APIS AND SERVICES" button. Search for and enable the "Identity and Access Management (IAM) API". 5. Grant your service account the 'Firebase Authentication Admin' role. This is required to ensure that exported user records contain the password hashes of the user accounts: - 1. Go to [Google Cloud Platform Console / IAM & admin](https://console.cloud.google.com/iam-admin). + 1. Go to [Google Cloud Console / IAM & admin](https://console.cloud.google.com/iam-admin). 2. Find your service account in the list, and click the 'pencil' icon to edit it's permissions. 3. Click 'ADD ANOTHER ROLE' and choose 'Firebase Authentication Admin'. 4. Click 'SAVE'. From 1862342636e816b61337262254cdfe0cc410c640 Mon Sep 17 00:00:00 2001 From: batuxd <9674241+suchcodemuchwow@users.noreply.github.com> Date: Fri, 22 Jan 2021 20:02:08 +0100 Subject: [PATCH 003/102] update typo in interface name (#1138) FireabseErrorInterface -> FirebaseErrorInterface --- src/utils/error.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/error.ts b/src/utils/error.ts index 232bed0981..5809a294b6 100644 --- a/src/utils/error.ts +++ b/src/utils/error.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { FirebaseError as FireabseErrorInterface } from '../firebase-namespace-api'; +import { FirebaseError as FirebaseErrorInterface } from '../firebase-namespace-api'; import { deepCopy } from '../utils/deep-copy'; /** @@ -39,7 +39,7 @@ interface ServerToClientCode { * @param {ErrorInfo} errorInfo The error information (code and message). * @constructor */ -export class FirebaseError extends Error implements FireabseErrorInterface { +export class FirebaseError extends Error implements FirebaseErrorInterface { constructor(private errorInfo: ErrorInfo) { super(errorInfo.message); From 6ce98e2bdd4e43b106238578c404a997fc2aa469 Mon Sep 17 00:00:00 2001 From: Yuchen Shi Date: Wed, 3 Feb 2021 21:20:18 -0800 Subject: [PATCH 004/102] Improve token verification logic with Auth Emulator. (#1148) * Improve token verification logic with Auth Emulator. * Clean up comments. * Fix linting issues. * Address review comments. * Use mock for auth emulator unit test. * Implement session cookies. * Call useEmulator() only once. * Update tests. * Delete unused test helper. * Add unit tests for checking revocation. * Fix typo in test comments. --- package-lock.json | 6 +- package.json | 2 +- src/auth/auth-api-request.ts | 4 - src/auth/auth.ts | 50 +++---- src/auth/token-verifier.ts | 73 ++++++----- test/integration/auth.spec.ts | 179 ++++++++++++++++++++------ test/integration/setup.ts | 78 +++++++---- test/unit/auth/auth.spec.ts | 93 +++++++++---- test/unit/auth/token-verifier.spec.ts | 9 +- 9 files changed, 321 insertions(+), 173 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5ec1476a52..f4875def9a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -176,9 +176,9 @@ "integrity": "sha512-L/ZnJRAq7F++utfuoTKX4CLBG5YR7tFO3PLzG1/oXXKEezJ0kRL3CMRoueBEmTCzVb/6SIs2Qlaw++uDgi5Xyg==" }, "@firebase/auth": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.15.2.tgz", - "integrity": "sha512-2n32PBi6x9jVhc0E/ewKLUCYYTzFEXL4PNkvrrlGKbzeTBEkkyzfgUX7OV9UF5wUOG+gurtUthuur1zspZ/9hg==", + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.16.2.tgz", + "integrity": "sha512-68TlDL0yh3kF8PiCzI8m8RWd/bf/xCLUsdz1NZ2Dwea0sp6e2WAhu0sem1GfhwuEwL+Ns4jCdX7qbe/OQlkVEA==", "dev": true, "requires": { "@firebase/auth-types": "0.10.1" diff --git a/package.json b/package.json index 37620aa245..ee75e5e196 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ }, "devDependencies": { "@firebase/app": "^0.6.13", - "@firebase/auth": "^0.15.2", + "@firebase/auth": "^0.16.2", "@firebase/auth-types": "^0.10.1", "@microsoft/api-extractor": "^7.11.2", "@types/bcrypt": "^2.0.0", diff --git a/src/auth/auth-api-request.ts b/src/auth/auth-api-request.ts index 0380debeb8..56c06c1423 100644 --- a/src/auth/auth-api-request.ts +++ b/src/auth/auth-api-request.ts @@ -2117,10 +2117,6 @@ function emulatorHost(): string | undefined { /** * When true the SDK should communicate with the Auth Emulator for all API * calls and also produce unsigned tokens. - * - * This alone does NOT short-circuit ID Token verification. - * For security reasons that must be explicitly disabled through - * setJwtVerificationEnabled(false); */ export function useEmulator(): boolean { return !!emulatorHost(); diff --git a/src/auth/auth.ts b/src/auth/auth.ts index 8e35df82c4..56f9ac64b4 100644 --- a/src/auth/auth.ts +++ b/src/auth/auth.ts @@ -29,7 +29,7 @@ import * as utils from '../utils/index'; import * as validator from '../utils/validator'; import { auth } from './index'; import { - FirebaseTokenVerifier, createSessionCookieVerifier, createIdTokenVerifier, ALGORITHM_RS256 + FirebaseTokenVerifier, createSessionCookieVerifier, createIdTokenVerifier } from './token-verifier'; import { SAMLConfig, OIDCConfig, OIDCConfigServerResponse, SAMLConfigServerResponse, @@ -115,15 +115,16 @@ export class BaseAuth implements BaseAuthI * verification. */ public verifyIdToken(idToken: string, checkRevoked = false): Promise { - return this.idTokenVerifier.verifyJWT(idToken) + const isEmulator = useEmulator(); + return this.idTokenVerifier.verifyJWT(idToken, isEmulator) .then((decodedIdToken: DecodedIdToken) => { // Whether to check if the token was revoked. - if (!checkRevoked) { - return decodedIdToken; + if (checkRevoked || isEmulator) { + return this.verifyDecodedJWTNotRevoked( + decodedIdToken, + AuthClientErrorCode.ID_TOKEN_REVOKED); } - return this.verifyDecodedJWTNotRevoked( - decodedIdToken, - AuthClientErrorCode.ID_TOKEN_REVOKED); + return decodedIdToken; }); } @@ -443,15 +444,16 @@ export class BaseAuth implements BaseAuthI */ public verifySessionCookie( sessionCookie: string, checkRevoked = false): Promise { - return this.sessionCookieVerifier.verifyJWT(sessionCookie) + const isEmulator = useEmulator(); + return this.sessionCookieVerifier.verifyJWT(sessionCookie, isEmulator) .then((decodedIdToken: DecodedIdToken) => { // Whether to check if the token was revoked. - if (!checkRevoked) { - return decodedIdToken; + if (checkRevoked || isEmulator) { + return this.verifyDecodedJWTNotRevoked( + decodedIdToken, + AuthClientErrorCode.SESSION_COOKIE_REVOKED); } - return this.verifyDecodedJWTNotRevoked( - decodedIdToken, - AuthClientErrorCode.SESSION_COOKIE_REVOKED); + return decodedIdToken; }); } @@ -675,28 +677,6 @@ export class BaseAuth implements BaseAuthI return decodedIdToken; }); } - - /** - * Enable or disable ID token verification. This is used to safely short-circuit token verification with the - * Auth emulator. When disabled ONLY unsigned tokens will pass verification, production tokens will not pass. - * - * WARNING: This is a dangerous method that will compromise your app's security and break your app in - * production. Developers should never call this method, it is for internal testing use only. - * - * @internal - */ - // @ts-expect-error: this method appears unused but is used privately. - private setJwtVerificationEnabled(enabled: boolean): void { - if (!enabled && !useEmulator()) { - // We only allow verification to be disabled in conjunction with - // the emulator environment variable. - throw new Error('This method is only available when connected to the Authentication emulator.'); - } - - const algorithm = enabled ? ALGORITHM_RS256 : 'none'; - this.idTokenVerifier.setAlgorithm(algorithm); - this.sessionCookieVerifier.setAlgorithm(algorithm); - } } diff --git a/src/auth/token-verifier.ts b/src/auth/token-verifier.ts index c39aa84fc7..cbb9991f4c 100644 --- a/src/auth/token-verifier.ts +++ b/src/auth/token-verifier.ts @@ -79,7 +79,7 @@ export class FirebaseTokenVerifier { constructor(private clientCertUrl: string, private algorithm: jwt.Algorithm, private issuer: string, private tokenInfo: FirebaseTokenInfo, private readonly app: FirebaseApp) { - + if (!validator.isURL(clientCertUrl)) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_ARGUMENT, @@ -135,10 +135,11 @@ export class FirebaseTokenVerifier { * Verifies the format and signature of a Firebase Auth JWT token. * * @param {string} jwtToken The Firebase Auth JWT token to verify. + * @param {boolean=} isEmulator Whether to accept Auth Emulator tokens. * @return {Promise} A promise fulfilled with the decoded claims of the Firebase Auth ID * token. */ - public verifyJWT(jwtToken: string): Promise { + public verifyJWT(jwtToken: string, isEmulator = false): Promise { if (!validator.isString(jwtToken)) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_ARGUMENT, @@ -148,19 +149,15 @@ export class FirebaseTokenVerifier { return util.findProjectId(this.app) .then((projectId) => { - return this.verifyJWTWithProjectId(jwtToken, projectId); + return this.verifyJWTWithProjectId(jwtToken, projectId, isEmulator); }); } - /** - * Override the JWT signing algorithm. - * @param algorithm the new signing algorithm. - */ - public setAlgorithm(algorithm: jwt.Algorithm): void { - this.algorithm = algorithm; - } - - private verifyJWTWithProjectId(jwtToken: string, projectId: string | null): Promise { + private verifyJWTWithProjectId( + jwtToken: string, + projectId: string | null, + isEmulator: boolean + ): Promise { if (!validator.isNonEmptyString(projectId)) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_CREDENTIAL, @@ -185,7 +182,7 @@ export class FirebaseTokenVerifier { if (!fullDecodedToken) { errorMessage = `Decoding ${this.tokenInfo.jwtName} failed. Make sure you passed the entire string JWT ` + `which represents ${this.shortNameArticle} ${this.tokenInfo.shortName}.` + verifyJwtTokenDocsMessage; - } else if (typeof header.kid === 'undefined' && this.algorithm !== 'none') { + } else if (!isEmulator && typeof header.kid === 'undefined') { const isCustomToken = (payload.aud === FIREBASE_AUDIENCE); const isLegacyCustomToken = (header.alg === 'HS256' && payload.v === 0 && 'd' in payload && 'uid' in payload.d); @@ -200,7 +197,7 @@ export class FirebaseTokenVerifier { } errorMessage += verifyJwtTokenDocsMessage; - } else if (header.alg !== this.algorithm) { + } else if (!isEmulator && header.alg !== this.algorithm) { errorMessage = `${this.tokenInfo.jwtName} has incorrect algorithm. Expected "` + this.algorithm + '" but got ' + '"' + header.alg + '".' + verifyJwtTokenDocsMessage; } else if (payload.aud !== projectId) { @@ -209,7 +206,7 @@ export class FirebaseTokenVerifier { verifyJwtTokenDocsMessage; } else if (payload.iss !== this.issuer + projectId) { errorMessage = `${this.tokenInfo.jwtName} has incorrect "iss" (issuer) claim. Expected ` + - `"${this.issuer}"` + projectId + '" but got "' + + `"${this.issuer}` + projectId + '" but got "' + payload.iss + '".' + projectIdMatchMessage + verifyJwtTokenDocsMessage; } else if (typeof payload.sub !== 'string') { errorMessage = `${this.tokenInfo.jwtName} has no "sub" (subject) claim.` + verifyJwtTokenDocsMessage; @@ -223,9 +220,8 @@ export class FirebaseTokenVerifier { return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, errorMessage)); } - // When the algorithm is set to 'none' there will be no signature and therefore we don't check - // the public keys. - if (this.algorithm === 'none') { + if (isEmulator) { + // Signature checks skipped for emulator; no need to fetch public keys. return this.verifyJwtSignatureWithKey(jwtToken, null); } @@ -257,26 +253,29 @@ export class FirebaseTokenVerifier { const verifyJwtTokenDocsMessage = ` See ${this.tokenInfo.url} ` + `for details on how to retrieve ${this.shortNameArticle} ${this.tokenInfo.shortName}.`; return new Promise((resolve, reject) => { - jwt.verify(jwtToken, publicKey || '', { - algorithms: [this.algorithm], - }, (error: jwt.VerifyErrors | null, decodedToken: object | undefined) => { - if (error) { - if (error.name === 'TokenExpiredError') { - const errorMessage = `${this.tokenInfo.jwtName} has expired. Get a fresh ${this.tokenInfo.shortName}` + - ` from your client app and try again (auth/${this.tokenInfo.expiredErrorCode.code}).` + - verifyJwtTokenDocsMessage; - return reject(new FirebaseAuthError(this.tokenInfo.expiredErrorCode, errorMessage)); - } else if (error.name === 'JsonWebTokenError') { - const errorMessage = `${this.tokenInfo.jwtName} has invalid signature.` + verifyJwtTokenDocsMessage; - return reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, errorMessage)); + const verifyOptions: jwt.VerifyOptions = {}; + if (publicKey !== null) { + verifyOptions.algorithms = [this.algorithm]; + } + jwt.verify(jwtToken, publicKey || '', verifyOptions, + (error: jwt.VerifyErrors | null, decodedToken: object | undefined) => { + if (error) { + if (error.name === 'TokenExpiredError') { + const errorMessage = `${this.tokenInfo.jwtName} has expired. Get a fresh ${this.tokenInfo.shortName}` + + ` from your client app and try again (auth/${this.tokenInfo.expiredErrorCode.code}).` + + verifyJwtTokenDocsMessage; + return reject(new FirebaseAuthError(this.tokenInfo.expiredErrorCode, errorMessage)); + } else if (error.name === 'JsonWebTokenError') { + const errorMessage = `${this.tokenInfo.jwtName} has invalid signature.` + verifyJwtTokenDocsMessage; + return reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, errorMessage)); + } + return reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, error.message)); + } else { + const decodedIdToken = (decodedToken as DecodedIdToken); + decodedIdToken.uid = decodedIdToken.sub; + resolve(decodedIdToken); } - return reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, error.message)); - } else { - const decodedIdToken = (decodedToken as DecodedIdToken); - decodedIdToken.uid = decodedIdToken.sub; - resolve(decodedIdToken); - } - }); + }); }); } diff --git a/test/integration/auth.spec.ts b/test/integration/auth.spec.ts index 41e278f821..a7ac8c4636 100644 --- a/test/integration/auth.spec.ts +++ b/test/integration/auth.spec.ts @@ -37,6 +37,8 @@ chai.use(chaiAsPromised); const expect = chai.expect; +const authEmulatorHost = process.env.FIREBASE_AUTH_EMULATOR_HOST; + const newUserUid = generateRandomString(20); const nonexistentUid = generateRandomString(20); const newMultiFactorUserUid = generateRandomString(20); @@ -102,6 +104,9 @@ describe('admin.auth', () => { apiKey, authDomain: projectId + '.firebaseapp.com', }); + if (authEmulatorHost) { + (clientAuth() as any).useEmulator('http://' + authEmulatorHost); + } return cleanup(); }); @@ -137,7 +142,10 @@ describe('admin.auth', () => { }); }); - it('createUser() creates a new user with enrolled second factors', () => { + it('createUser() creates a new user with enrolled second factors', function () { + if (authEmulatorHost) { + return this.skip(); // Not yet supported in Auth Emulator. + } const enrolledFactors = [ { phoneNumber: '+16505550001', @@ -353,7 +361,7 @@ describe('admin.auth', () => { const metadata = userRecord!.metadata; expect(metadata.lastRefreshTime).to.exist; - expect(isUTCString(metadata.lastRefreshTime!)); + expect(isUTCString(metadata.lastRefreshTime!)).to.be.true; const creationTime = new Date(metadata.creationTime).getTime(); const lastRefreshTime = new Date(metadata.lastRefreshTime!).getTime(); expect(creationTime).lte(lastRefreshTime); @@ -410,7 +418,7 @@ describe('admin.auth', () => { }); }); - it('revokeRefreshTokens() invalidates existing sessions and ID tokens', () => { + it('revokeRefreshTokens() invalidates existing sessions and ID tokens', async () => { let currentIdToken: string; let currentUser: User; // Sign in with an email and password account. @@ -433,9 +441,14 @@ describe('admin.auth', () => { ), 1000)); }) .then(() => { - // verifyIdToken without checking revocation should still succeed. - return admin.auth().verifyIdToken(currentIdToken) - .should.eventually.be.fulfilled; + const verifyingIdToken = admin.auth().verifyIdToken(currentIdToken) + if (authEmulatorHost) { + // Check revocation is forced in emulator-mode and this should throw. + return verifyingIdToken.should.eventually.be.rejected; + } else { + // verifyIdToken without checking revocation should still succeed. + return verifyingIdToken.should.eventually.be.fulfilled; + } }) .then(() => { // verifyIdToken while checking for revocation should fail. @@ -522,6 +535,27 @@ describe('admin.auth', () => { it('updateUser() updates the user record with the given parameters', () => { const updatedDisplayName = 'Updated User ' + newUserUid; + return admin.auth().updateUser(newUserUid, { + email: updatedEmail, + phoneNumber: updatedPhone, + emailVerified: true, + displayName: updatedDisplayName, + }) + .then((userRecord) => { + expect(userRecord.emailVerified).to.be.true; + expect(userRecord.displayName).to.equal(updatedDisplayName); + // Confirm expected email. + expect(userRecord.email).to.equal(updatedEmail); + // Confirm expected phone number. + expect(userRecord.phoneNumber).to.equal(updatedPhone); + }); + }); + + it('updateUser() creates, updates, and removes second factors', function () { + if (authEmulatorHost) { + return this.skip(); // Not yet supported in Auth Emulator. + } + const now = new Date(1476235905000).toUTCString(); // Update user with enrolled second factors. const enrolledFactors = [ @@ -541,21 +575,11 @@ describe('admin.auth', () => { }, ]; return admin.auth().updateUser(newUserUid, { - email: updatedEmail, - phoneNumber: updatedPhone, - emailVerified: true, - displayName: updatedDisplayName, multiFactor: { enrolledFactors, }, }) .then((userRecord) => { - expect(userRecord.emailVerified).to.be.true; - expect(userRecord.displayName).to.equal(updatedDisplayName); - // Confirm expected email. - expect(userRecord.email).to.equal(updatedEmail); - // Confirm expected phone number. - expect(userRecord.phoneNumber).to.equal(updatedPhone); // Confirm second factors added to user. const actualUserRecord: {[key: string]: any} = userRecord.toJSON(); expect(actualUserRecord.multiFactor.enrolledFactors.length).to.equal(2); @@ -655,6 +679,49 @@ describe('admin.auth', () => { .should.eventually.be.rejected.and.have.property('code', 'auth/argument-error'); }); + if (authEmulatorHost) { + describe('Auth emulator support', () => { + const uid = 'authEmulatorUser'; + before(() => { + return admin.auth().createUser({ + uid, + email: 'lastRefreshTimeUser@example.com', + password: 'p4ssword', + }); + }); + after(() => { + return admin.auth().deleteUser(uid); + }); + + it('verifyIdToken() succeeds when called with an unsigned token', () => { + const unsignedToken = mocks.generateIdToken({ + algorithm: 'none', + audience: projectId, + issuer: 'https://securetoken.google.com/' + projectId, + subject: uid, + }); + return admin.auth().verifyIdToken(unsignedToken); + }); + + it('verifyIdToken() fails when called with a token with wrong project', () => { + const unsignedToken = mocks.generateIdToken({ algorithm: 'none', audience: 'nosuch' }); + return admin.auth().verifyIdToken(unsignedToken) + .should.eventually.be.rejected.and.have.property('code', 'auth/argument-error'); + }); + + it('verifyIdToken() fails when called with a token that does not belong to a user', () => { + const unsignedToken = mocks.generateIdToken({ + algorithm: 'none', + audience: projectId, + issuer: 'https://securetoken.google.com/' + projectId, + subject: 'nosuch', + }); + return admin.auth().verifyIdToken(unsignedToken) + .should.eventually.be.rejected.and.have.property('code', 'auth/user-not-found'); + }); + }); + } + describe('Link operations', () => { const uid = generateRandomString(20).toLowerCase(); const email = uid + '@example.com'; @@ -1253,7 +1320,10 @@ describe('admin.auth', () => { }; // Clean up temp configurations used for test. - before(() => { + before(function () { + if (authEmulatorHost) { + return this.skip(); // Not implemented. + } return removeTempConfigs().then(() => admin.auth().createProviderConfig(authProviderConfig1)); }); @@ -1385,7 +1455,10 @@ describe('admin.auth', () => { }; // Clean up temp configurations used for test. - before(() => { + before(function () { + if (authEmulatorHost) { + return this.skip(); // Not implemented. + } return removeTempConfigs().then(() => admin.auth().createProviderConfig(authProviderConfig1)); }); @@ -1484,7 +1557,6 @@ describe('admin.auth', () => { it('deleteUser() deletes the user with the given UID', () => { return Promise.all([ admin.auth().deleteUser(newUserUid), - admin.auth().deleteUser(newMultiFactorUserUid), admin.auth().deleteUser(uidFromCreateUserWithoutUid), ]).should.eventually.be.fulfilled; }); @@ -1610,8 +1682,14 @@ describe('admin.auth', () => { ), 1000)); }) .then(() => { - return admin.auth().verifySessionCookie(currentSessionCookie) - .should.eventually.be.fulfilled; + const verifyingSessionCookie = admin.auth().verifySessionCookie(currentSessionCookie); + if (authEmulatorHost) { + // Check revocation is forced in emulator-mode and this should throw. + return verifyingSessionCookie.should.eventually.be.rejected; + } else { + // verifyIdToken without checking revocation should still succeed. + return verifyingSessionCookie.should.eventually.be.fulfilled; + } }) .then(() => { return admin.auth().verifySessionCookie(currentSessionCookie, true) @@ -1824,7 +1902,10 @@ describe('admin.auth', () => { ]; fixtures.forEach((fixture) => { - it(`successfully imports users with ${fixture.name} to Firebase Auth.`, () => { + it(`successfully imports users with ${fixture.name} to Firebase Auth.`, function () { + if (authEmulatorHost) { + return this.skip(); // Auth Emulator does not support real hashes. + } importUserRecord = { uid: randomUid, email: randomUid + '@example.com', @@ -1893,10 +1974,13 @@ describe('admin.auth', () => { expect(JSON.stringify(actualUserRecord[key])) .to.be.equal(JSON.stringify((importUserRecord as any)[key])); } - }).should.eventually.be.fulfilled; + }); }); - it('successfully imports users with enrolled second factors', () => { + it('successfully imports users with enrolled second factors', function () { + if (authEmulatorHost) { + return this.skip(); // Not yet implemented. + } const uid = generateRandomString(20).toLowerCase(); const email = uid + '@example.com'; const now = new Date(1476235905000).toUTCString(); @@ -1958,25 +2042,41 @@ describe('admin.auth', () => { it('fails when invalid users are provided', () => { const users = [ - { uid: generateRandomString(20).toLowerCase(), phoneNumber: '+1error' }, { uid: generateRandomString(20).toLowerCase(), email: 'invalid' }, - { uid: generateRandomString(20).toLowerCase(), phoneNumber: '+1invalid' }, { uid: generateRandomString(20).toLowerCase(), emailVerified: 'invalid' } as any, ]; return admin.auth().importUsers(users) .then((result) => { expect(result.successCount).to.equal(0); - expect(result.failureCount).to.equal(4); - expect(result.errors.length).to.equal(4); + expect(result.failureCount).to.equal(2); + expect(result.errors.length).to.equal(2); + expect(result.errors[0].index).to.equal(0); + expect(result.errors[0].error.code).to.equals('auth/invalid-email'); + expect(result.errors[1].index).to.equal(1); + expect(result.errors[1].error.code).to.equals('auth/invalid-email-verified'); + }); + }); + + it('fails when users with invalid phone numbers are provided', function () { + if (authEmulatorHost) { + // Auth Emulator's phoneNumber validation is also lax and won't throw. + return this.skip(); + } + const users = [ + // These phoneNumbers passes local (lax) validator but fails remotely. + { uid: generateRandomString(20).toLowerCase(), phoneNumber: '+1error' }, + { uid: generateRandomString(20).toLowerCase(), phoneNumber: '+1invalid' }, + ]; + return admin.auth().importUsers(users) + .then((result) => { + expect(result.successCount).to.equal(0); + expect(result.failureCount).to.equal(2); + expect(result.errors.length).to.equal(2); expect(result.errors[0].index).to.equal(0); expect(result.errors[0].error.code).to.equals('auth/invalid-user-import'); expect(result.errors[1].index).to.equal(1); - expect(result.errors[1].error.code).to.equals('auth/invalid-email'); - expect(result.errors[2].index).to.equal(2); - expect(result.errors[2].error.code).to.equals('auth/invalid-user-import'); - expect(result.errors[3].index).to.equal(3); - expect(result.errors[3].error.code).to.equals('auth/invalid-email-verified'); - }).should.eventually.be.fulfilled; + expect(result.errors[1].error.code).to.equals('auth/invalid-user-import'); + }); }); }); }); @@ -2132,12 +2232,11 @@ function safeDelete(uid: string): Promise { * @param {string[]} uids The list of user identifiers to delete. * @return {Promise} A promise that resolves when delete operation resolves. */ -function deleteUsersWithDelay(uids: string[]): Promise { - return new Promise((resolve) => { - setTimeout(resolve, 1000); - }).then(() => { - return admin.auth().deleteUsers(uids); - }); +async function deleteUsersWithDelay(uids: string[]): Promise { + if (!authEmulatorHost) { + await new Promise((resolve) => { setTimeout(resolve, 1000); }); + } + return admin.auth().deleteUsers(uids); } /** diff --git a/test/integration/setup.ts b/test/integration/setup.ts index c70362fa89..5880a6cd0d 100644 --- a/test/integration/setup.ts +++ b/test/integration/setup.ts @@ -36,51 +36,72 @@ export let noServiceAccountApp: admin.app.App; export let cmdArgs: any; +export const isEmulator = !!process.env.FIREBASE_EMULATOR_HUB; + before(() => { - /* tslint:disable:no-console */ - let serviceAccount: any; - try { - serviceAccount = require('../resources/key.json'); - } catch (error) { - console.log(chalk.red( - 'The integration test suite requires a service account JSON file for a ' + - 'Firebase project to be saved to `test/resources/key.json`.', - error, - )); - throw error; - } + let getCredential: () => {credential?: admin.credential.Credential}; + let serviceAccountId: string; - try { - apiKey = fs.readFileSync(path.join(__dirname, '../resources/apikey.txt')).toString().trim(); - } catch (error) { - console.log(chalk.red( - 'The integration test suite requires an API key for a ' + - 'Firebase project to be saved to `test/resources/apikey.txt`.', - error, + /* tslint:disable:no-console */ + if (isEmulator) { + console.log(chalk.yellow( + 'Running integration tests against Emulator Suite. ' + + 'Some tests may be skipped due to lack of emulator support.', )); - throw error; + getCredential = () => ({}); + projectId = process.env.GCLOUD_PROJECT!; + apiKey = 'fake-api-key'; + serviceAccountId = 'fake-client-email@example.com'; + } else { + let serviceAccount: any; + try { + serviceAccount = require('../resources/key.json'); + } catch (error) { + console.log(chalk.red( + 'The integration test suite requires a service account JSON file for a ' + + 'Firebase project to be saved to `test/resources/key.json`.', + error, + )); + throw error; + } + + try { + apiKey = fs.readFileSync(path.join(__dirname, '../resources/apikey.txt')).toString().trim(); + } catch (error) { + console.log(chalk.red( + 'The integration test suite requires an API key for a ' + + 'Firebase project to be saved to `test/resources/apikey.txt`.', + error, + )); + throw error; + } + getCredential = () => ({ credential: admin.credential.cert(serviceAccount) }); + projectId = serviceAccount.project_id; + serviceAccountId = serviceAccount.client_email; } /* tslint:enable:no-console */ - projectId = serviceAccount.project_id; databaseUrl = 'https://' + projectId + '.firebaseio.com'; storageBucket = projectId + '.appspot.com'; defaultApp = admin.initializeApp({ - credential: admin.credential.cert(serviceAccount), + ...getCredential(), + projectId, databaseURL: databaseUrl, storageBucket, }); nullApp = admin.initializeApp({ - credential: admin.credential.cert(serviceAccount), + ...getCredential(), + projectId, databaseURL: databaseUrl, databaseAuthVariableOverride: null, storageBucket, }, 'null'); nonNullApp = admin.initializeApp({ - credential: admin.credential.cert(serviceAccount), + ...getCredential(), + projectId, databaseURL: databaseUrl, databaseAuthVariableOverride: { uid: generateRandomString(20), @@ -88,9 +109,14 @@ before(() => { storageBucket, }, 'nonNull'); + const noServiceAccountAppCreds = getCredential(); + if (noServiceAccountAppCreds.credential) { + noServiceAccountAppCreds.credential = new CertificatelessCredential( + noServiceAccountAppCreds.credential) + } noServiceAccountApp = admin.initializeApp({ - credential: new CertificatelessCredential(admin.credential.cert(serviceAccount)), - serviceAccountId: serviceAccount.client_email, + ...noServiceAccountAppCreds, + serviceAccountId, projectId, }, 'noServiceAccount'); diff --git a/test/unit/auth/auth.spec.ts b/test/unit/auth/auth.spec.ts index 26521b30cd..6991cc7617 100644 --- a/test/unit/auth/auth.spec.ts +++ b/test/unit/auth/auth.spec.ts @@ -3193,14 +3193,22 @@ AUTH_CONFIGS.forEach((testConfig) => { describe('auth emulator support', () => { let mockAuth = testConfig.init(mocks.app()); + const userRecord = getValidUserRecord(getValidGetAccountInfoResponse()); + const validSince = new Date(userRecord.tokensValidAfterTime!); + + const stubs: sinon.SinonStub[] = []; + let clock: sinon.SinonFakeTimers; beforeEach(() => { - process.env.FIREBASE_AUTH_EMULATOR_HOST = 'localhost:9099'; + process.env.FIREBASE_AUTH_EMULATOR_HOST = '127.0.0.1:9099'; mockAuth = testConfig.init(mocks.app()); + clock = sinon.useFakeTimers(validSince.getTime()); }); afterEach(() => { + _.forEach(stubs, (s) => s.restore()); delete process.env.FIREBASE_AUTH_EMULATOR_HOST; + clock.restore(); }); it('createCustomToken() generates an unsigned token', async () => { @@ -3215,39 +3223,78 @@ AUTH_CONFIGS.forEach((testConfig) => { jwt.verify(token, '', { algorithms: ['none'] }); }); - it('verifyIdToken() rejects an unsigned token when only the env var is set', async () => { + it('verifyIdToken() should reject revoked ID tokens', () => { + const uid = userRecord.uid; + // One second before validSince. + const oneSecBeforeValidSince = Math.floor(validSince.getTime() / 1000 - 1); + const getUserStub = sinon.stub(testConfig.Auth.prototype, 'getUser') + .resolves(userRecord); + stubs.push(getUserStub); + const unsignedToken = mocks.generateIdToken({ - algorithm: 'none' + algorithm: 'none', + subject: uid, + }, { + iat: oneSecBeforeValidSince, + auth_time: oneSecBeforeValidSince, // eslint-disable-line @typescript-eslint/camelcase }); - await expect(mockAuth.verifyIdToken(unsignedToken)) - .to.be.rejectedWith('Firebase ID token has incorrect algorithm. Expected "RS256"'); + // verifyIdToken should force checking revocation in emulator mode, + // even if checkRevoked=false. + return mockAuth.verifyIdToken(unsignedToken, false) + .then(() => { + throw new Error('Unexpected success'); + }, (error) => { + // Confirm expected error returned. + expect(error).to.have.property('code', 'auth/id-token-revoked'); + // Confirm underlying API called with expected parameters. + expect(getUserStub).to.have.been.calledOnce.and.calledWith(uid); + }); }); - it('verifyIdToken() accepts an unsigned token when private method is called and env var is set', async () => { - (mockAuth as any).setJwtVerificationEnabled(false); + it('verifySessionCookie() should reject revoked session cookies', () => { + const uid = userRecord.uid; + // One second before validSince. + const oneSecBeforeValidSince = Math.floor(validSince.getTime() / 1000 - 1); + const getUserStub = sinon.stub(testConfig.Auth.prototype, 'getUser') + .resolves(userRecord); + stubs.push(getUserStub); + + const unsignedToken = mocks.generateIdToken({ + algorithm: 'none', + subject: uid, + issuer: 'https://session.firebase.google.com/' + mocks.projectId, + }, { + iat: oneSecBeforeValidSince, + auth_time: oneSecBeforeValidSince, // eslint-disable-line @typescript-eslint/camelcase + }); - let claims = {}; - if (testConfig.Auth === TenantAwareAuth) { - claims = { - firebase: { - tenant: TENANT_ID - } - } - } + // verifySessionCookie should force checking revocation in emulator + // mode, even if checkRevoked=false. + return mockAuth.verifySessionCookie(unsignedToken, false) + .then(() => { + throw new Error('Unexpected success'); + }, (error) => { + // Confirm expected error returned. + expect(error).to.have.property('code', 'auth/session-cookie-revoked'); + // Confirm underlying API called with expected parameters. + expect(getUserStub).to.have.been.calledOnce.and.calledWith(uid); + }); + }); + it('verifyIdToken() rejects an unsigned token if auth emulator is unreachable', async () => { const unsignedToken = mocks.generateIdToken({ algorithm: 'none' - }, claims); + }); - const decoded = await mockAuth.verifyIdToken(unsignedToken); - expect(decoded).to.be.ok; - }); + const errorMessage = 'Error while making request: connect ECONNREFUSED 127.0.0.1. Error code: ECONNREFUSED'; + const getUserStub = sinon.stub(testConfig.Auth.prototype, 'getUser').rejects(new Error(errorMessage)); + stubs.push(getUserStub); - it('private method throws when env var is unset', async () => { - delete process.env.FIREBASE_AUTH_EMULATOR_HOST; - await expect(() => (mockAuth as any).setJwtVerificationEnabled(false)) - .to.throw('This method is only available when connected to the Authentication emulator.') + // Since revocation check is forced on in emulator mode, this will call + // the getUser method and get rejected (instead of succeed locally). + await expect(mockAuth.verifyIdToken(unsignedToken)) + .to.be.rejectedWith(errorMessage); }); }); }); diff --git a/test/unit/auth/token-verifier.spec.ts b/test/unit/auth/token-verifier.spec.ts index 6b370a8bb4..d863a0e849 100644 --- a/test/unit/auth/token-verifier.spec.ts +++ b/test/unit/auth/token-verifier.spec.ts @@ -106,7 +106,7 @@ function mockFailedFetchPublicKeys(): nock.Scope { } function createTokenVerifier( - app: FirebaseApp, + app: FirebaseApp, options: { algorithm?: Algorithm } = {} ): verifier.FirebaseTokenVerifier { const algorithm = options.algorithm || 'RS256'; @@ -544,16 +544,17 @@ describe('FirebaseTokenVerifier', () => { }); }); - it('should decode an unsigned token when the algorithm is set to none (emulator)', async () => { + it('should decode an unsigned token if isEmulator=true', async () => { clock = sinon.useFakeTimers(1000); - const emulatorVerifier = createTokenVerifier(app, { algorithm: 'none' }); + const emulatorVerifier = createTokenVerifier(app); const mockIdToken = mocks.generateIdToken({ algorithm: 'none', header: {} }); - const decoded = await emulatorVerifier.verifyJWT(mockIdToken); + const isEmulator = true; + const decoded = await emulatorVerifier.verifyJWT(mockIdToken, isEmulator); expect(decoded).to.deep.equal({ one: 'uno', two: 'dos', From 01d8177650676a142474229538f0b469fc363a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Borntr=C3=A4ger?= Date: Thu, 4 Feb 2021 20:04:19 +0100 Subject: [PATCH 005/102] feat: Exporting all types of Messages so they can be used by consumers (#1147) * feat: Exporting all types of Messages so they can be used by consumers Fixes https://github.com/firebase/firebase-admin-node/issues/1146 * feat(exportMessageTypes): Testing TokenMessage * feat(exportMessageTypes): Added tests for all Message types * feat(exportMessageTypes): Fixed build * feat(exportMessageTypes): Better unit tests * feat(exportMessageTypes): Deleted unneeded separate TS test * feat(exportMessageTypes): Fixed build * feat(exportMessageTypes): Fixed linting --- src/messaging/index.ts | 6 ++--- test/unit/messaging/messaging.spec.ts | 33 +++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/messaging/index.ts b/src/messaging/index.ts index 020ee7c875..6c074cea7c 100644 --- a/src/messaging/index.ts +++ b/src/messaging/index.ts @@ -58,15 +58,15 @@ export namespace messaging { fcmOptions?: FcmOptions; } - interface TokenMessage extends BaseMessage { + export interface TokenMessage extends BaseMessage { token: string; } - interface TopicMessage extends BaseMessage { + export interface TopicMessage extends BaseMessage { topic: string; } - interface ConditionMessage extends BaseMessage { + export interface ConditionMessage extends BaseMessage { condition: string; } diff --git a/test/unit/messaging/messaging.spec.ts b/test/unit/messaging/messaging.spec.ts index 82f0973f67..236c18213a 100644 --- a/test/unit/messaging/messaging.spec.ts +++ b/test/unit/messaging/messaging.spec.ts @@ -41,6 +41,9 @@ chai.use(chaiAsPromised); const expect = chai.expect; import Message = messaging.Message; +import TokenMessage = messaging.TokenMessage; +import TopicMessage = messaging.TopicMessage; +import ConditionMessage = messaging.ConditionMessage; import MessagingOptions = messaging.MessagingOptions; import MessagingPayload = messaging.MessagingPayload; import MessagingDevicesResponse = messaging.MessagingDevicesResponse; @@ -823,6 +826,32 @@ describe('Messaging', () => { [validMessage], ).should.eventually.be.rejected.and.have.property('code', 'app/invalid-credential'); }); + + // This test was added to also verify https://github.com/firebase/firebase-admin-node/issues/1146 + it('should be fulfilled when called with different message types', () => { + const messageIds = [ + 'projects/projec_id/messages/1', + 'projects/projec_id/messages/2', + 'projects/projec_id/messages/3', + ]; + const tokenMessage: TokenMessage = { token: 'test' }; + const topicMessage: TopicMessage = { topic: 'test' }; + const conditionMessage: ConditionMessage = { condition: 'test' }; + const messages: Message[] = [tokenMessage, topicMessage, conditionMessage]; + + mockedRequests.push(mockBatchRequest(messageIds)); + + return messaging.sendAll(messages) + .then((response: BatchResponse) => { + expect(response.successCount).to.equal(3); + expect(response.failureCount).to.equal(0); + response.responses.forEach((resp, idx) => { + expect(resp.success).to.be.true; + expect(resp.messageId).to.equal(messageIds[idx]); + expect(resp.error).to.be.undefined; + }); + }); + }); }); describe('sendMulticast()', () => { @@ -887,7 +916,7 @@ describe('Messaging', () => { expect(messages.length).to.equal(3); expect(stub!.args[0][1]).to.be.undefined; messages.forEach((message, idx) => { - expect((message as any).token).to.equal(tokens[idx]); + expect((message as TokenMessage).token).to.equal(tokens[idx]); expect(message.android).to.be.undefined; expect(message.apns).to.be.undefined; expect(message.data).to.be.undefined; @@ -917,7 +946,7 @@ describe('Messaging', () => { expect(messages.length).to.equal(3); expect(stub!.args[0][1]).to.be.undefined; messages.forEach((message, idx) => { - expect((message as any).token).to.equal(tokens[idx]); + expect((message as TokenMessage).token).to.equal(tokens[idx]); expect(message.android).to.deep.equal(multicast.android); expect(message.apns).to.be.deep.equal(multicast.apns); expect(message.data).to.be.deep.equal(multicast.data); From fc2f557223ac1f44bebc039cffabab7de500063c Mon Sep 17 00:00:00 2001 From: rsgowman Date: Mon, 8 Feb 2021 15:53:05 -0500 Subject: [PATCH 006/102] feat(auth): Implement getUserByProviderId (#769) RELEASE NOTE: Added a new getUserByProviderId() to lookup user accounts by their providers. --- etc/firebase-admin.api.md | 1 + src/auth/auth-api-request.ts | 15 ++++ src/auth/auth.ts | 30 +++++++ src/auth/index.ts | 15 ++++ test/integration/auth.spec.ts | 55 ++++++++++++ test/unit/auth/auth-api-request.spec.ts | 6 ++ test/unit/auth/auth.spec.ts | 114 ++++++++++++++++++++++++ 7 files changed, 236 insertions(+) diff --git a/etc/firebase-admin.api.md b/etc/firebase-admin.api.md index b5b810a5e6..3941798ebd 100644 --- a/etc/firebase-admin.api.md +++ b/etc/firebase-admin.api.md @@ -106,6 +106,7 @@ export namespace auth { getUser(uid: string): Promise; getUserByEmail(email: string): Promise; getUserByPhoneNumber(phoneNumber: string): Promise; + getUserByProviderUid(providerId: string, uid: string): Promise; getUsers(identifiers: UserIdentifier[]): Promise; importUsers(users: UserImportRecord[], options?: UserImportOptions): Promise; listProviderConfigs(options: AuthProviderConfigFilter): Promise; diff --git a/src/auth/auth-api-request.ts b/src/auth/auth-api-request.ts index 56c06c1423..33be9da092 100644 --- a/src/auth/auth-api-request.ts +++ b/src/auth/auth-api-request.ts @@ -1079,6 +1079,21 @@ export abstract class AbstractAuthRequestHandler { return this.invokeRequestHandler(this.getAuthUrlBuilder(), FIREBASE_AUTH_GET_ACCOUNT_INFO, request); } + public getAccountInfoByFederatedUid(providerId: string, rawId: string): Promise { + if (!validator.isNonEmptyString(providerId) || !validator.isNonEmptyString(rawId)) { + throw new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID); + } + + const request = { + federatedUserId: [{ + providerId, + rawId, + }], + }; + + return this.invokeRequestHandler(this.getAuthUrlBuilder(), FIREBASE_AUTH_GET_ACCOUNT_INFO, request); + } + /** * Looks up multiple users by their identifiers (uid, email, etc). * diff --git a/src/auth/auth.ts b/src/auth/auth.ts index 56f9ac64b4..76a7cf336c 100644 --- a/src/auth/auth.ts +++ b/src/auth/auth.ts @@ -173,6 +173,36 @@ export class BaseAuth implements BaseAuthI }); } + /** + * Gets the user data for the user corresponding to a given provider id. + * + * See [Retrieve user data](/docs/auth/admin/manage-users#retrieve_user_data) + * for code samples and detailed documentation. + * + * @param providerId The provider ID, for example, "google.com" for the + * Google provider. + * @param uid The user identifier for the given provider. + * + * @return A promise fulfilled with the user data corresponding to the + * given provider id. + */ + public getUserByProviderUid(providerId: string, uid: string): Promise { + // Although we don't really advertise it, we want to also handle + // non-federated idps with this call. So if we detect one of them, we'll + // reroute this request appropriately. + if (providerId === 'phone') { + return this.getUserByPhoneNumber(uid); + } else if (providerId === 'email') { + return this.getUserByEmail(uid); + } + + return this.authRequestHandler.getAccountInfoByFederatedUid(providerId, uid) + .then((response: any) => { + // Returns the user record populated with server response. + return new UserRecord(response.users[0]); + }); + } + /** * Gets the user data corresponding to the specified identifiers. * diff --git a/src/auth/index.ts b/src/auth/index.ts index 1ba9b56af5..a1ff346b97 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -1516,6 +1516,21 @@ export namespace auth { */ getUserByPhoneNumber(phoneNumber: string): Promise; + /** + * Gets the user data for the user corresponding to a given provider ID. + * + * See [Retrieve user data](/docs/auth/admin/manage-users#retrieve_user_data) + * for code samples and detailed documentation. + * + * @param providerId The provider ID, for example, "google.com" for the + * Google provider. + * @param uid The user identifier for the given provider. + * + * @return A promise fulfilled with the user data corresponding to the + * given provider id. + */ + getUserByProviderUid(providerId: string, uid: string): Promise; + /** * Gets the user data corresponding to the specified identifiers. * diff --git a/test/integration/auth.spec.ts b/test/integration/auth.spec.ts index a7ac8c4636..1eb7bfe6a8 100644 --- a/test/integration/auth.spec.ts +++ b/test/integration/auth.spec.ts @@ -221,6 +221,56 @@ describe('admin.auth', () => { }); }); + it('getUserByProviderUid() returns a user record with the matching provider id', async () => { + // TODO(rsgowman): Once we can link a provider id with a user, just do that + // here instead of creating a new user. + const randomUid = 'import_' + generateRandomString(20).toLowerCase(); + const importUser: admin.auth.UserImportRecord = { + uid: randomUid, + email: 'user@example.com', + phoneNumber: '+15555550000', + emailVerified: true, + disabled: false, + metadata: { + lastSignInTime: 'Thu, 01 Jan 1970 00:00:00 UTC', + creationTime: 'Thu, 01 Jan 1970 00:00:00 UTC', + }, + providerData: [{ + displayName: 'User Name', + email: 'user@example.com', + phoneNumber: '+15555550000', + photoURL: 'http://example.com/user', + providerId: 'google.com', + uid: 'google_uid', + }], + }; + + await admin.auth().importUsers([importUser]); + + try { + await admin.auth().getUserByProviderUid('google.com', 'google_uid') + .then((userRecord) => { + expect(userRecord.uid).to.equal(importUser.uid); + }); + } finally { + await safeDelete(importUser.uid); + } + }); + + it('getUserByProviderUid() redirects to getUserByEmail if given an email', () => { + return admin.auth().getUserByProviderUid('email', mockUserData.email) + .then((userRecord) => { + expect(userRecord.uid).to.equal(newUserUid); + }); + }); + + it('getUserByProviderUid() redirects to getUserByPhoneNumber if given a phone number', () => { + return admin.auth().getUserByProviderUid('phone', mockUserData.phoneNumber) + .then((userRecord) => { + expect(userRecord.uid).to.equal(newUserUid); + }); + }); + describe('getUsers()', () => { /** * Filters a list of object to another list of objects that only contains @@ -623,6 +673,11 @@ describe('admin.auth', () => { .should.eventually.be.rejected.and.have.property('code', 'auth/user-not-found'); }); + it('getUserByProviderUid() fails when called with a non-existing provider id', () => { + return admin.auth().getUserByProviderUid('google.com', nonexistentUid) + .should.eventually.be.rejected.and.have.property('code', 'auth/user-not-found'); + }); + it('updateUser() fails when called with a non-existing UID', () => { return admin.auth().updateUser(nonexistentUid, { emailVerified: true, diff --git a/test/unit/auth/auth-api-request.spec.ts b/test/unit/auth/auth-api-request.spec.ts index ff154d3b2c..2df01889dd 100644 --- a/test/unit/auth/auth-api-request.spec.ts +++ b/test/unit/auth/auth-api-request.spec.ts @@ -360,6 +360,12 @@ describe('FIREBASE_AUTH_GET_ACCOUNT_INFO', () => { return requestValidator(validRequest); }).not.to.throw(); }); + it('should succeed with federatedUserId passed', () => { + const validRequest = { federatedUserId: { providerId: 'google.com', rawId: 'google_uid_1234' } }; + expect(() => { + return requestValidator(validRequest); + }).not.to.throw(); + }); it('should fail when neither localId, email or phoneNumber are passed', () => { const invalidRequest = { bla: ['1234'] }; expect(() => { diff --git a/test/unit/auth/auth.spec.ts b/test/unit/auth/auth.spec.ts index 6991cc7617..4d134e2949 100644 --- a/test/unit/auth/auth.spec.ts +++ b/test/unit/auth/auth.spec.ts @@ -1151,6 +1151,120 @@ AUTH_CONFIGS.forEach((testConfig) => { }); }); + describe('getUserByProviderUid()', () => { + const providerId = 'google.com'; + const providerUid = 'google_uid'; + const tenantId = testConfig.supportsTenantManagement ? undefined : TENANT_ID; + const expectedGetAccountInfoResult = getValidGetAccountInfoResponse(tenantId); + const expectedUserRecord = getValidUserRecord(expectedGetAccountInfoResult); + const expectedError = new FirebaseAuthError(AuthClientErrorCode.USER_NOT_FOUND); + + // Stubs used to simulate underlying api calls. + let stubs: sinon.SinonStub[] = []; + beforeEach(() => sinon.spy(validator, 'isEmail')); + afterEach(() => { + (validator.isEmail as any).restore(); + _.forEach(stubs, (stub) => stub.restore()); + stubs = []; + }); + + it('should be rejected given no provider id', () => { + expect(() => (auth as any).getUserByProviderUid()) + .to.throw(FirebaseAuthError) + .with.property('code', 'auth/invalid-provider-id'); + }); + + it('should be rejected given an invalid provider id', () => { + expect(() => auth.getUserByProviderUid('', 'uid')) + .to.throw(FirebaseAuthError) + .with.property('code', 'auth/invalid-provider-id'); + }); + + it('should be rejected given an invalid provider uid', () => { + expect(() => auth.getUserByProviderUid('id', '')) + .to.throw(FirebaseAuthError) + .with.property('code', 'auth/invalid-provider-id'); + }); + + it('should be rejected given an app which returns null access tokens', () => { + return nullAccessTokenAuth.getUserByProviderUid(providerId, providerUid) + .should.eventually.be.rejected.and.have.property('code', 'app/invalid-credential'); + }); + + it('should be rejected given an app which returns invalid access tokens', () => { + return malformedAccessTokenAuth.getUserByProviderUid(providerId, providerUid) + .should.eventually.be.rejected.and.have.property('code', 'app/invalid-credential'); + }); + + it('should be rejected given an app which fails to generate access tokens', () => { + return rejectedPromiseAccessTokenAuth.getUserByProviderUid(providerId, providerUid) + .should.eventually.be.rejected.and.have.property('code', 'app/invalid-credential'); + }); + + it('should resolve with a UserRecord on success', () => { + // Stub getAccountInfoByEmail to return expected result. + const stub = sinon.stub(testConfig.RequestHandler.prototype, 'getAccountInfoByFederatedUid') + .resolves(expectedGetAccountInfoResult); + stubs.push(stub); + return auth.getUserByProviderUid(providerId, providerUid) + .then((userRecord) => { + // Confirm underlying API called with expected parameters. + expect(stub).to.have.been.calledOnce.and.calledWith(providerId, providerUid); + // Confirm expected user record response returned. + expect(userRecord).to.deep.equal(expectedUserRecord); + }); + }); + + describe('non-federated providers', () => { + let invokeRequestHandlerStub: sinon.SinonStub; + beforeEach(() => { + invokeRequestHandlerStub = sinon.stub(testConfig.RequestHandler.prototype, 'invokeRequestHandler') + .resolves({ + // nothing here is checked; we just need enough to not crash. + users: [{ + localId: 1, + }], + }); + + }); + afterEach(() => { + invokeRequestHandlerStub.restore(); + }); + + it('phone lookups should use phoneNumber field', async () => { + await auth.getUserByProviderUid('phone', '+15555550001'); + expect(invokeRequestHandlerStub).to.have.been.calledOnce.and.calledWith( + sinon.match.any, sinon.match.any, { + phoneNumber: ['+15555550001'], + }); + }); + + it('email lookups should use email field', async () => { + await auth.getUserByProviderUid('email', 'user@example.com'); + expect(invokeRequestHandlerStub).to.have.been.calledOnce.and.calledWith( + sinon.match.any, sinon.match.any, { + email: ['user@example.com'], + }); + }); + }); + + it('should throw an error when the backend returns an error', () => { + // Stub getAccountInfoByFederatedUid to throw a backend error. + const stub = sinon.stub(testConfig.RequestHandler.prototype, 'getAccountInfoByFederatedUid') + .rejects(expectedError); + stubs.push(stub); + return auth.getUserByProviderUid(providerId, providerUid) + .then(() => { + throw new Error('Unexpected success'); + }, (error) => { + // Confirm underlying API called with expected parameters. + expect(stub).to.have.been.calledOnce.and.calledWith(providerId, providerUid); + // Confirm expected error returned. + expect(error).to.equal(expectedError); + }); + }); + }); + describe('getUsers()', () => { let stubs: sinon.SinonStub[] = []; From a00ce05ee967ae6070643435e65d64b4f41e8840 Mon Sep 17 00:00:00 2001 From: rsgowman Date: Mon, 8 Feb 2021 17:02:30 -0500 Subject: [PATCH 007/102] Allow enabling of anonymous provider via tenant configuration (#802) RELEASE NOTES: Allow enabling of anonymous provider via tenant configuration. --- etc/firebase-admin.api.md | 2 ++ src/auth/index.ts | 10 ++++++++ src/auth/tenant.ts | 9 +++++++ test/integration/auth.spec.ts | 36 +++++++++++++++++++++++++++ test/unit/auth/tenant-manager.spec.ts | 3 +++ test/unit/auth/tenant.spec.ts | 2 ++ 6 files changed, 62 insertions(+) diff --git a/etc/firebase-admin.api.md b/etc/firebase-admin.api.md index 3941798ebd..38981491e2 100644 --- a/etc/firebase-admin.api.md +++ b/etc/firebase-admin.api.md @@ -251,6 +251,7 @@ export namespace auth { expiresIn: number; } export interface Tenant { + anonymousSignInEnabled: boolean; displayName?: string; emailSignInConfig?: { enabled: boolean; @@ -301,6 +302,7 @@ export namespace auth { photoURL?: string | null; } export interface UpdateTenantRequest { + anonymousSignInEnabled?: boolean; displayName?: string; emailSignInConfig?: EmailSignInProviderConfig; multiFactorConfig?: MultiFactorConfig; diff --git a/src/auth/index.ts b/src/auth/index.ts index a1ff346b97..f3a387393d 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -1014,6 +1014,11 @@ export namespace auth { passwordRequired?: boolean; }; + /** + * Whether the anonymous provider is enabled. + */ + anonymousSignInEnabled: boolean; + /** * The multi-factor auth configuration on the current tenant. */ @@ -1089,6 +1094,11 @@ export namespace auth { */ emailSignInConfig?: EmailSignInProviderConfig; + /** + * Whether the anonymous provider is enabled. + */ + anonymousSignInEnabled?: boolean; + /** * The multi-factor auth configuration to update on the tenant. */ diff --git a/src/auth/tenant.ts b/src/auth/tenant.ts index a7b1d188f4..392494739b 100644 --- a/src/auth/tenant.ts +++ b/src/auth/tenant.ts @@ -29,6 +29,7 @@ import UpdateTenantRequest = auth.UpdateTenantRequest; /** The corresponding server side representation of a TenantOptions object. */ export interface TenantOptionsServerRequest extends EmailSignInConfigServerRequest { displayName?: string; + enableAnonymousUser?: boolean; mfaConfig?: MultiFactorAuthServerConfig; testPhoneNumbers?: {[key: string]: string}; } @@ -39,6 +40,7 @@ export interface TenantServerResponse { displayName?: string; allowPasswordSignup?: boolean; enableEmailLinkSignin?: boolean; + enableAnonymousUser?: boolean; mfaConfig?: MultiFactorAuthServerConfig; testPhoneNumbers?: {[key: string]: string}; } @@ -50,6 +52,7 @@ export class Tenant implements TenantInterface { public readonly tenantId: string; public readonly displayName?: string; public readonly emailSignInConfig?: EmailSignInConfig; + public readonly anonymousSignInEnabled: boolean; public readonly multiFactorConfig?: MultiFactorAuthConfig; public readonly testPhoneNumbers?: {[phoneNumber: string]: string}; @@ -70,6 +73,9 @@ export class Tenant implements TenantInterface { if (typeof tenantOptions.displayName !== 'undefined') { request.displayName = tenantOptions.displayName; } + if (typeof tenantOptions.anonymousSignInEnabled !== 'undefined') { + request.enableAnonymousUser = tenantOptions.anonymousSignInEnabled; + } if (typeof tenantOptions.multiFactorConfig !== 'undefined') { request.mfaConfig = MultiFactorAuthConfig.buildServerRequest(tenantOptions.multiFactorConfig); } @@ -105,6 +111,7 @@ export class Tenant implements TenantInterface { const validKeys = { displayName: true, emailSignInConfig: true, + anonymousSignInEnabled: true, multiFactorConfig: true, testPhoneNumbers: true, }; @@ -179,6 +186,7 @@ export class Tenant implements TenantInterface { allowPasswordSignup: false, }); } + this.anonymousSignInEnabled = !!response.enableAnonymousUser; if (typeof response.mfaConfig !== 'undefined') { this.multiFactorConfig = new MultiFactorAuthConfig(response.mfaConfig); } @@ -193,6 +201,7 @@ export class Tenant implements TenantInterface { tenantId: this.tenantId, displayName: this.displayName, emailSignInConfig: this.emailSignInConfig?.toJSON(), + anonymousSignInEnabled: this.anonymousSignInEnabled, multiFactorConfig: this.multiFactorConfig?.toJSON(), testPhoneNumbers: this.testPhoneNumbers, }; diff --git a/test/integration/auth.spec.ts b/test/integration/auth.spec.ts index 1eb7bfe6a8..76eee8c85e 100644 --- a/test/integration/auth.spec.ts +++ b/test/integration/auth.spec.ts @@ -886,6 +886,7 @@ describe('admin.auth', () => { enabled: true, passwordRequired: true, }, + anonymousSignInEnabled: false, multiFactorConfig: { state: 'ENABLED', factorIds: ['phone'], @@ -901,6 +902,7 @@ describe('admin.auth', () => { enabled: false, passwordRequired: true, }, + anonymousSignInEnabled: false, multiFactorConfig: { state: 'DISABLED', factorIds: [], @@ -915,6 +917,7 @@ describe('admin.auth', () => { enabled: true, passwordRequired: false, }, + anonymousSignInEnabled: false, multiFactorConfig: { state: 'ENABLED', factorIds: ['phone'], @@ -957,6 +960,20 @@ describe('admin.auth', () => { }); }); + it('createTenant() can enable anonymous users', async () => { + const tenant = await admin.auth().tenantManager().createTenant({ + displayName: 'testTenantWithAnon', + emailSignInConfig: { + enabled: false, + passwordRequired: true, + }, + anonymousSignInEnabled: true, + }); + createdTenants.push(tenant.tenantId); + + expect(tenant.anonymousSignInEnabled).to.be.true; + }); + // Sanity check user management + email link generation + custom attribute APIs. // TODO: Confirm behavior in client SDK when it starts supporting it. describe('supports user management, email link generation, custom attribute and token revocation APIs', () => { @@ -1300,6 +1317,25 @@ describe('admin.auth', () => { }); }); + it('updateTenant() should be able to enable/disable anon provider', async () => { + const tenantManager = admin.auth().tenantManager(); + let tenant = await tenantManager.createTenant({ + displayName: 'testTenantUpdateAnon', + }); + createdTenants.push(tenant.tenantId); + expect(tenant.anonymousSignInEnabled).to.be.false; + + tenant = await tenantManager.updateTenant(tenant.tenantId, { + anonymousSignInEnabled: true, + }); + expect(tenant.anonymousSignInEnabled).to.be.true; + + tenant = await tenantManager.updateTenant(tenant.tenantId, { + anonymousSignInEnabled: false, + }); + expect(tenant.anonymousSignInEnabled).to.be.false; + }); + it('listTenants() should resolve with expected number of tenants', () => { const allTenantIds: string[] = []; const tenantOptions2 = deepCopy(tenantOptions); diff --git a/test/unit/auth/tenant-manager.spec.ts b/test/unit/auth/tenant-manager.spec.ts index 9c7ef80be1..52bf955ccd 100644 --- a/test/unit/auth/tenant-manager.spec.ts +++ b/test/unit/auth/tenant-manager.spec.ts @@ -52,6 +52,7 @@ describe('TenantManager', () => { displayName: 'TENANT-DISPLAY-NAME', allowPasswordSignup: true, enableEmailLinkSignin: false, + enableAnonymousUser: true, }; before(() => { @@ -388,6 +389,7 @@ describe('TenantManager', () => { enabled: true, passwordRequired: true, }, + anonymousSignInEnabled: true, }; const expectedTenant = new Tenant(GET_TENANT_RESPONSE); const expectedError = new FirebaseAuthError( @@ -480,6 +482,7 @@ describe('TenantManager', () => { enabled: true, passwordRequired: true, }, + anonymousSignInEnabled: true, }; const expectedTenant = new Tenant(GET_TENANT_RESPONSE); const expectedError = new FirebaseAuthError( diff --git a/test/unit/auth/tenant.spec.ts b/test/unit/auth/tenant.spec.ts index db090b3279..b2ebe6a5d1 100644 --- a/test/unit/auth/tenant.spec.ts +++ b/test/unit/auth/tenant.spec.ts @@ -351,6 +351,7 @@ describe('Tenant', () => { enabled: true, passwordRequired: false, }, + anonymousSignInEnabled: false, multiFactorConfig: deepCopy(clientRequest.multiFactorConfig), testPhoneNumbers: deepCopy(clientRequest.testPhoneNumbers), }); @@ -368,6 +369,7 @@ describe('Tenant', () => { enabled: true, passwordRequired: false, }, + anonymousSignInEnabled: false, }); }); }); From bea66a90b12408313c3f1b54b67e19cc32c25421 Mon Sep 17 00:00:00 2001 From: rsgowman Date: Tue, 9 Feb 2021 18:07:39 -0500 Subject: [PATCH 008/102] feat(auth): Add ability to link a federated ID with the `updateUser()` method. (#770) --- etc/firebase-admin.api.md | 11 ++ src/auth/auth-api-request.ts | 57 ++++++++- src/auth/auth.ts | 45 +++++++ src/auth/index.ts | 56 ++++++++ test/integration/auth.spec.ts | 234 +++++++++++++++++++++++---------- test/unit/auth/auth.spec.ts | 235 ++++++++++++++++++++++++++++++---- 6 files changed, 543 insertions(+), 95 deletions(-) diff --git a/etc/firebase-admin.api.md b/etc/firebase-admin.api.md index 38981491e2..f90136685f 100644 --- a/etc/firebase-admin.api.md +++ b/etc/firebase-admin.api.md @@ -300,6 +300,8 @@ export namespace auth { password?: string; phoneNumber?: string | null; photoURL?: string | null; + providersToUnlink?: string[]; + providerToLink?: UserProvider; } export interface UpdateTenantRequest { anonymousSignInEnabled?: boolean; @@ -365,6 +367,14 @@ export namespace auth { creationTime?: string; lastSignInTime?: string; } + export interface UserProvider { + displayName?: string; + email?: string; + phoneNumber?: string; + photoURL?: string; + providerId?: string; + uid?: string; + } export interface UserProviderRequest { displayName?: string; email?: string; @@ -393,6 +403,7 @@ export namespace auth { tokensValidAfterTime?: string; uid: string; } + {}; } // @public (undocumented) diff --git a/src/auth/auth-api-request.ts b/src/auth/auth-api-request.ts index 33be9da092..d3ecf73fc7 100644 --- a/src/auth/auth-api-request.ts +++ b/src/auth/auth-api-request.ts @@ -403,6 +403,8 @@ function validateCreateEditRequest(request: any, writeOperationType: WriteOperat phoneNumber: true, customAttributes: true, validSince: true, + // Pass linkProviderUserInfo only for updates (i.e. not for uploads.) + linkProviderUserInfo: !uploadAccountRequest, // Pass tenantId only for uploadAccount requests. tenantId: uploadAccountRequest, passwordHash: uploadAccountRequest, @@ -551,6 +553,12 @@ function validateCreateEditRequest(request: any, writeOperationType: WriteOperat validateProviderUserInfo(providerUserInfoEntry); }); } + + // linkProviderUserInfo must be a (single) UserProvider value. + if (typeof request.linkProviderUserInfo !== 'undefined') { + validateProviderUserInfo(request.linkProviderUserInfo); + } + // mfaInfo is used for importUsers. // mfa.enrollments is used for setAccountInfo. // enrollments has to be an array of valid AuthFactorInfo requests. @@ -1306,6 +1314,33 @@ export abstract class AbstractAuthRequestHandler { 'Properties argument must be a non-null object.', ), ); + } else if (validator.isNonNullObject(properties.providerToLink)) { + // TODO(rsgowman): These checks overlap somewhat with + // validateProviderUserInfo. It may be possible to refactor a bit. + if (!validator.isNonEmptyString(properties.providerToLink.providerId)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, + 'providerToLink.providerId of properties argument must be a non-empty string.'); + } + if (!validator.isNonEmptyString(properties.providerToLink.uid)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, + 'providerToLink.uid of properties argument must be a non-empty string.'); + } + } else if (typeof properties.providersToUnlink !== 'undefined') { + if (!validator.isArray(properties.providersToUnlink)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, + 'providersToUnlink of properties argument must be an array of strings.'); + } + + properties.providersToUnlink.forEach((providerId) => { + if (!validator.isNonEmptyString(providerId)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, + 'providersToUnlink of properties argument must be an array of strings.'); + } + }); } // Build the setAccountInfo request. @@ -1340,13 +1375,25 @@ export abstract class AbstractAuthRequestHandler { // It will be removed from the backend request and an additional parameter // deleteProvider: ['phone'] with an array of providerIds (phone in this case), // will be passed. - // Currently this applies to phone provider only. if (request.phoneNumber === null) { - request.deleteProvider = ['phone']; + request.deleteProvider ? request.deleteProvider.push('phone') : request.deleteProvider = ['phone']; delete request.phoneNumber; - } else { - // Doesn't apply to other providers in admin SDK. - delete request.deleteProvider; + } + + if (typeof(request.providerToLink) !== 'undefined') { + request.linkProviderUserInfo = deepCopy(request.providerToLink); + delete request.providerToLink; + + request.linkProviderUserInfo.rawId = request.linkProviderUserInfo.uid; + delete request.linkProviderUserInfo.uid; + } + + if (typeof(request.providersToUnlink) !== 'undefined') { + if (!validator.isArray(request.deleteProvider)) { + request.deleteProvider = []; + } + request.deleteProvider = request.deleteProvider.concat(request.providersToUnlink); + delete request.providersToUnlink; } // Rewrite photoURL to photoUrl. diff --git a/src/auth/auth.ts b/src/auth/auth.ts index 76a7cf336c..aa5d7b11ef 100644 --- a/src/auth/auth.ts +++ b/src/auth/auth.ts @@ -15,6 +15,7 @@ * limitations under the License. */ +import { deepCopy } from '../utils/deep-copy'; import { UserRecord } from './user-record'; import { isUidIdentifier, isEmailIdentifier, isPhoneIdentifier, isProviderIdentifier, @@ -381,6 +382,50 @@ export class BaseAuth implements BaseAuthI * @return {Promise} A promise that resolves with the modified user record. */ public updateUser(uid: string, properties: UpdateRequest): Promise { + // Although we don't really advertise it, we want to also handle linking of + // non-federated idps with this call. So if we detect one of them, we'll + // adjust the properties parameter appropriately. This *does* imply that a + // conflict could arise, e.g. if the user provides a phoneNumber property, + // but also provides a providerToLink with a 'phone' provider id. In that + // case, we'll throw an error. + properties = deepCopy(properties); + + if (properties?.providerToLink) { + if (properties.providerToLink.providerId === 'email') { + if (typeof properties.email !== 'undefined') { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, + "Both UpdateRequest.email and UpdateRequest.providerToLink.providerId='email' were set. To " + + 'link to the email/password provider, only specify the UpdateRequest.email field.'); + } + properties.email = properties.providerToLink.uid; + delete properties.providerToLink; + } else if (properties.providerToLink.providerId === 'phone') { + if (typeof properties.phoneNumber !== 'undefined') { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, + "Both UpdateRequest.phoneNumber and UpdateRequest.providerToLink.providerId='phone' were set. To " + + 'link to a phone provider, only specify the UpdateRequest.phoneNumber field.'); + } + properties.phoneNumber = properties.providerToLink.uid; + delete properties.providerToLink; + } + } + if (properties?.providersToUnlink) { + if (properties.providersToUnlink.indexOf('phone') !== -1) { + // If we've been told to unlink the phone provider both via setting + // phoneNumber to null *and* by setting providersToUnlink to include + // 'phone', then we'll reject that. Though it might also be reasonable + // to relax this restriction and just unlink it. + if (properties.phoneNumber === null) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, + "Both UpdateRequest.phoneNumber=null and UpdateRequest.providersToUnlink=['phone'] were set. To " + + 'unlink from a phone provider, only specify the UpdateRequest.phoneNumber=null field.'); + } + } + } + return this.authRequestHandler.updateExistingAccount(uid, properties) .then((existingUid) => { // Return the corresponding user record. diff --git a/src/auth/index.ts b/src/auth/index.ts index f3a387393d..7817435c14 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -153,6 +153,42 @@ export namespace auth { phoneNumber: string; } + /** + * Represents a user identity provider that can be associated with a Firebase user. + */ + interface UserProvider { + + /** + * The user identifier for the linked provider. + */ + uid?: string; + + /** + * The display name for the linked provider. + */ + displayName?: string; + + /** + * The email for the linked provider. + */ + email?: string; + + /** + * The phone number for the linked provider. + */ + phoneNumber?: string; + + /** + * The photo URL for the linked provider. + */ + photoURL?: string; + + /** + * The linked provider ID (for example, "google.com" for the Google provider). + */ + providerId?: string; + } + /** * Interface representing a user. */ @@ -384,6 +420,26 @@ export namespace auth { * The user's updated multi-factor related properties. */ multiFactor?: MultiFactorUpdateSettings; + + /** + * Links this user to the specified provider. + * + * Linking a provider to an existing user account does not invalidate the + * refresh token of that account. In other words, the existing account + * would continue to be able to access resources, despite not having used + * the newly linked provider to log in. If you wish to force the user to + * authenticate with this new provider, you need to (a) revoke their + * refresh token (see + * https://firebase.google.com/docs/auth/admin/manage-sessions#revoke_refresh_tokens), + * and (b) ensure no other authentication methods are present on this + * account. + */ + providerToLink?: UserProvider; + + /** + * Unlinks this user from the specified providers. + */ + providersToUnlink?: string[]; } /** diff --git a/test/integration/auth.spec.ts b/test/integration/auth.spec.ts index 76eee8c85e..bf9f7f134d 100644 --- a/test/integration/auth.spec.ts +++ b/test/integration/auth.spec.ts @@ -583,79 +583,177 @@ describe('admin.auth', () => { }); }); - it('updateUser() updates the user record with the given parameters', () => { - const updatedDisplayName = 'Updated User ' + newUserUid; - return admin.auth().updateUser(newUserUid, { - email: updatedEmail, - phoneNumber: updatedPhone, - emailVerified: true, - displayName: updatedDisplayName, - }) - .then((userRecord) => { - expect(userRecord.emailVerified).to.be.true; - expect(userRecord.displayName).to.equal(updatedDisplayName); - // Confirm expected email. - expect(userRecord.email).to.equal(updatedEmail); - // Confirm expected phone number. - expect(userRecord.phoneNumber).to.equal(updatedPhone); + describe('updateUser()', () => { + /** + * Creates a new user for testing purposes. The user's uid will be + * '$name_$tenRandomChars' and email will be + * '$name_$tenRandomChars@example.com'. + */ + // TODO(rsgowman): This function could usefully be employed throughout this file. + function createTestUser(name: string): Promise { + const tenRandomChars = generateRandomString(10); + return admin.auth().createUser({ + uid: name + '_' + tenRandomChars, + displayName: name, + email: name + '_' + tenRandomChars + '@example.com', }); - }); - - it('updateUser() creates, updates, and removes second factors', function () { - if (authEmulatorHost) { - return this.skip(); // Not yet supported in Auth Emulator. } - const now = new Date(1476235905000).toUTCString(); - // Update user with enrolled second factors. - const enrolledFactors = [ - { - uid: 'mfaUid1', - phoneNumber: '+16505550001', - displayName: 'Work phone number', - factorId: 'phone', - enrollmentTime: now, - }, - { - uid: 'mfaUid2', - phoneNumber: '+16505550002', - displayName: 'Personal phone number', - factorId: 'phone', - enrollmentTime: now, - }, - ]; - return admin.auth().updateUser(newUserUid, { - multiFactor: { - enrolledFactors, - }, - }) - .then((userRecord) => { - // Confirm second factors added to user. - const actualUserRecord: {[key: string]: any} = userRecord.toJSON(); - expect(actualUserRecord.multiFactor.enrolledFactors.length).to.equal(2); - expect(actualUserRecord.multiFactor.enrolledFactors).to.deep.equal(enrolledFactors); - // Update list of second factors. - return admin.auth().updateUser(newUserUid, { - multiFactor: { - enrolledFactors: [enrolledFactors[0]], - }, - }); + let updateUser: admin.auth.UserRecord; + before(async () => { + updateUser = await createTestUser('UpdateUser'); + }); + + after(() => { + return safeDelete(updateUser.uid); + }); + + it('updates the user record with the given parameters', () => { + const updatedDisplayName = 'Updated User ' + updateUser.uid; + return admin.auth().updateUser(updateUser.uid, { + email: updatedEmail, + phoneNumber: updatedPhone, + emailVerified: true, + displayName: updatedDisplayName, }) - .then((userRecord) => { - expect(userRecord.multiFactor!.enrolledFactors.length).to.equal(1); - const actualUserRecord: {[key: string]: any} = userRecord.toJSON(); - expect(actualUserRecord.multiFactor.enrolledFactors[0]).to.deep.equal(enrolledFactors[0]); - // Remove all second factors. - return admin.auth().updateUser(newUserUid, { - multiFactor: { - enrolledFactors: null, - }, + .then((userRecord) => { + expect(userRecord.emailVerified).to.be.true; + expect(userRecord.displayName).to.equal(updatedDisplayName); + // Confirm expected email. + expect(userRecord.email).to.equal(updatedEmail); + // Confirm expected phone number. + expect(userRecord.phoneNumber).to.equal(updatedPhone); }); + }); + + it('creates, updates, and removes second factors', function () { + if (authEmulatorHost) { + return this.skip(); // Not yet supported in Auth Emulator. + } + + const now = new Date(1476235905000).toUTCString(); + // Update user with enrolled second factors. + const enrolledFactors = [ + { + uid: 'mfaUid1', + phoneNumber: '+16505550001', + displayName: 'Work phone number', + factorId: 'phone', + enrollmentTime: now, + }, + { + uid: 'mfaUid2', + phoneNumber: '+16505550002', + displayName: 'Personal phone number', + factorId: 'phone', + enrollmentTime: now, + }, + ]; + return admin.auth().updateUser(updateUser.uid, { + multiFactor: { + enrolledFactors, + }, }) - .then((userRecord) => { - // Confirm all second factors removed. - expect(userRecord.multiFactor).to.be.undefined; + .then((userRecord) => { + // Confirm second factors added to user. + const actualUserRecord: {[key: string]: any} = userRecord.toJSON(); + expect(actualUserRecord.multiFactor.enrolledFactors.length).to.equal(2); + expect(actualUserRecord.multiFactor.enrolledFactors).to.deep.equal(enrolledFactors); + // Update list of second factors. + return admin.auth().updateUser(updateUser.uid, { + multiFactor: { + enrolledFactors: [enrolledFactors[0]], + }, + }); + }) + .then((userRecord) => { + expect(userRecord.multiFactor!.enrolledFactors.length).to.equal(1); + const actualUserRecord: {[key: string]: any} = userRecord.toJSON(); + expect(actualUserRecord.multiFactor.enrolledFactors[0]).to.deep.equal(enrolledFactors[0]); + // Remove all second factors. + return admin.auth().updateUser(updateUser.uid, { + multiFactor: { + enrolledFactors: null, + }, + }); + }) + .then((userRecord) => { + // Confirm all second factors removed. + expect(userRecord.multiFactor).to.be.undefined; + }); + }); + + it('can link/unlink with a federated provider', async () => { + const googleFederatedUid = 'google_uid_' + generateRandomString(10); + let userRecord = await admin.auth().updateUser(updateUser.uid, { + providerToLink: { + providerId: 'google.com', + uid: googleFederatedUid, + }, + }); + + let providerUids = userRecord.providerData.map((userInfo) => userInfo.uid); + let providerIds = userRecord.providerData.map((userInfo) => userInfo.providerId); + expect(providerUids).to.deep.include(googleFederatedUid); + expect(providerIds).to.deep.include('google.com'); + + userRecord = await admin.auth().updateUser(updateUser.uid, { + providersToUnlink: ['google.com'], + }); + + providerUids = userRecord.providerData.map((userInfo) => userInfo.uid); + providerIds = userRecord.providerData.map((userInfo) => userInfo.providerId); + expect(providerUids).to.not.deep.include(googleFederatedUid); + expect(providerIds).to.not.deep.include('google.com'); + }); + + it('can unlink multiple providers at once, incl a non-federated provider', async () => { + await deletePhoneNumberUser('+15555550001'); + + const googleFederatedUid = 'google_uid_' + generateRandomString(10); + const facebookFederatedUid = 'facebook_uid_' + generateRandomString(10); + + let userRecord = await admin.auth().updateUser(updateUser.uid, { + phoneNumber: '+15555550001', + providerToLink: { + providerId: 'google.com', + uid: googleFederatedUid, + }, + }); + userRecord = await admin.auth().updateUser(updateUser.uid, { + providerToLink: { + providerId: 'facebook.com', + uid: facebookFederatedUid, + }, + }); + + let providerUids = userRecord.providerData.map((userInfo) => userInfo.uid); + let providerIds = userRecord.providerData.map((userInfo) => userInfo.providerId); + expect(providerUids).to.deep.include.members([googleFederatedUid, facebookFederatedUid, '+15555550001']); + expect(providerIds).to.deep.include.members(['google.com', 'facebook.com', 'phone']); + + userRecord = await admin.auth().updateUser(updateUser.uid, { + providersToUnlink: ['google.com', 'facebook.com', 'phone'], }); + + providerUids = userRecord.providerData.map((userInfo) => userInfo.uid); + providerIds = userRecord.providerData.map((userInfo) => userInfo.providerId); + expect(providerUids).to.not.deep.include.members([googleFederatedUid, facebookFederatedUid, '+15555550001']); + expect(providerIds).to.not.deep.include.members(['google.com', 'facebook.com', 'phone']); + }); + + it('noops successfully when given an empty providersToUnlink list', async () => { + const userRecord = await createTestUser('NoopWithEmptyProvidersToDeleteUser'); + try { + const updatedUserRecord = await admin.auth().updateUser(userRecord.uid, { + providersToUnlink: [], + }); + + expect(updatedUserRecord).to.deep.equal(userRecord); + } finally { + safeDelete(userRecord.uid); + } + }); }); it('getUser() fails when called with a non-existing UID', () => { @@ -2208,8 +2306,8 @@ function testImportAndSignInUser( /** * Helper function that deletes the user with the specified phone number * if it exists. - * @param {string} phoneNumber The phone number of the user to delete. - * @return {Promise} A promise that resolves when the user is deleted + * @param phoneNumber The phone number of the user to delete. + * @return A promise that resolves when the user is deleted * or is found not to exist. */ function deletePhoneNumberUser(phoneNumber: string): Promise { diff --git a/test/unit/auth/auth.spec.ts b/test/unit/auth/auth.spec.ts index 4d134e2949..71957bc4b4 100644 --- a/test/unit/auth/auth.spec.ts +++ b/test/unit/auth/auth.spec.ts @@ -241,6 +241,8 @@ function getSAMLConfigServerResponse(providerId: string): SAMLConfigServerRespon } +const INVALID_PROVIDER_IDS = [ + undefined, null, NaN, 0, 1, true, false, '', [], [1, 'a'], {}, { a: 1 }, _.noop]; const TENANT_ID = 'tenantId'; const AUTH_CONFIGS: AuthTest[] = [ { @@ -1628,6 +1630,10 @@ AUTH_CONFIGS.forEach((testConfig) => { emailVerified: expectedUserRecord.emailVerified, password: 'password', phoneNumber: expectedUserRecord.phoneNumber, + providerToLink: { + providerId: 'google.com', + uid: 'google_uid', + }, }; // Stubs used to simulate underlying api calls. let stubs: sinon.SinonStub[] = []; @@ -1671,8 +1677,193 @@ AUTH_CONFIGS.forEach((testConfig) => { }) .catch((error) => { expect(error).to.have.property('code', 'auth/argument-error'); - expect(validator.isNonNullObject).to.have.been.calledOnce.and.calledWith(null); + expect(validator.isNonNullObject).to.have.been.calledWith(null); + }); + }); + + const invalidUpdateRequests: UpdateRequest[] = [ + { providerToLink: { uid: 'google_uid' } }, + { providerToLink: { providerId: 'google.com' } }, + { providerToLink: { providerId: 'google.com', uid: '' } }, + { providerToLink: { providerId: '', uid: 'google_uid' } }, + ]; + invalidUpdateRequests.forEach((invalidUpdateRequest) => { + it('should be rejected given an UpdateRequest with an invalid providerToLink parameter', () => { + expect(() => { + auth.updateUser(uid, invalidUpdateRequest); + }).to.throw(FirebaseAuthError).with.property('code', 'auth/argument-error'); + }); + }); + + it('should rename providerToLink property to linkProviderUserInfo', async () => { + const invokeRequestHandlerStub = sinon.stub(testConfig.RequestHandler.prototype, 'invokeRequestHandler') + .resolves({ + localId: uid, + }); + + // Stub getAccountInfoByUid to return a valid result (unchecked; we + // just need it to be valid so as to not crash.) + const getUserStub = sinon.stub(testConfig.RequestHandler.prototype, 'getAccountInfoByUid') + .resolves(expectedGetAccountInfoResult); + + stubs.push(invokeRequestHandlerStub); + stubs.push(getUserStub); + + await auth.updateUser(uid, { + providerToLink: { + providerId: 'google.com', + uid: 'google_uid', + }, + }); + + expect(invokeRequestHandlerStub).to.have.been.calledOnce.and.calledWith( + sinon.match.any, sinon.match.any, { + localId: uid, + linkProviderUserInfo: { + providerId: 'google.com', + rawId: 'google_uid', + }, + }); + }); + + INVALID_PROVIDER_IDS.forEach((invalidProviderId) => { + it('should be rejected given a deleteProvider list with an invalid provider ID ' + + JSON.stringify(invalidProviderId), () => { + expect(() => { + auth.updateUser(uid, { + providersToUnlink: [ invalidProviderId as any ], + }); + }).to.throw(FirebaseAuthError).with.property('code', 'auth/argument-error'); + }); + }); + + it('should merge deletion of phone provider with the providersToUnlink list', async () => { + const invokeRequestHandlerStub = sinon.stub(testConfig.RequestHandler.prototype, 'invokeRequestHandler') + .resolves({ + localId: uid, + }); + + // Stub getAccountInfoByUid to return a valid result (unchecked; we + // just need it to be valid so as to not crash.) + const getUserStub = sinon.stub(testConfig.RequestHandler.prototype, 'getAccountInfoByUid') + .resolves(expectedGetAccountInfoResult); + + stubs.push(invokeRequestHandlerStub); + stubs.push(getUserStub); + + await auth.updateUser(uid, { + phoneNumber: null, + providersToUnlink: [ 'google.com' ], + }); + + expect(invokeRequestHandlerStub).to.have.been.calledOnce.and.calledWith( + sinon.match.any, sinon.match.any, { + localId: uid, + deleteProvider: [ 'phone', 'google.com' ], + }); + }); + + describe('non-federated providers', () => { + let invokeRequestHandlerStub: sinon.SinonStub; + let getAccountInfoByUidStub: sinon.SinonStub; + beforeEach(() => { + invokeRequestHandlerStub = sinon.stub(testConfig.RequestHandler.prototype, 'invokeRequestHandler') + .resolves({ + // nothing here is checked; we just need enough to not crash. + users: [{ + localId: 1, + }], + }); + + getAccountInfoByUidStub = sinon.stub(testConfig.RequestHandler.prototype, 'getAccountInfoByUid') + .resolves({ + // nothing here is checked; we just need enough to not crash. + users: [{ + localId: 1, + }], + }); + }); + afterEach(() => { + invokeRequestHandlerStub.restore(); + getAccountInfoByUidStub.restore(); + }); + + it('specifying both email and providerId=email should be rejected', () => { + expect(() => { + auth.updateUser(uid, { + email: 'user@example.com', + providerToLink: { + providerId: 'email', + uid: 'user@example.com', + }, + }); + }).to.throw(FirebaseAuthError).with.property('code', 'auth/argument-error'); + }); + + it('specifying both phoneNumber and providerId=phone should be rejected', () => { + expect(() => { + auth.updateUser(uid, { + phoneNumber: '+15555550001', + providerToLink: { + providerId: 'phone', + uid: '+15555550001', + }, + }); + }).to.throw(FirebaseAuthError).with.property('code', 'auth/argument-error'); + }); + + it('email linking should use email field', async () => { + await auth.updateUser(uid, { + providerToLink: { + providerId: 'email', + uid: 'user@example.com', + }, + }); + expect(invokeRequestHandlerStub).to.have.been.calledOnce.and.calledWith( + sinon.match.any, sinon.match.any, { + localId: uid, + email: 'user@example.com', + }); + }); + + it('phone linking should use phoneNumber field', async () => { + await auth.updateUser(uid, { + providerToLink: { + providerId: 'phone', + uid: '+15555550001', + }, }); + expect(invokeRequestHandlerStub).to.have.been.calledOnce.and.calledWith( + sinon.match.any, sinon.match.any, { + localId: uid, + phoneNumber: '+15555550001', + }); + }); + + it('specifying both phoneNumber=null and providersToUnlink=phone should be rejected', () => { + expect(() => { + auth.updateUser(uid, { + phoneNumber: null, + providersToUnlink: ['phone'], + }); + }).to.throw(FirebaseAuthError).with.property('code', 'auth/argument-error'); + }); + + it('doesnt mutate the properties parameter', async () => { + const properties: UpdateRequest = { + providerToLink: { + providerId: 'email', + uid: 'user@example.com', + }, + }; + await auth.updateUser(uid, properties); + expect(properties).to.deep.equal({ + providerToLink: { + providerId: 'email', + uid: 'user@example.com', + }, + }); + }); }); it('should be rejected given an app which returns null access tokens', () => { @@ -2487,9 +2678,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .should.eventually.be.rejected.and.have.property('code', 'auth/invalid-provider-id'); }); - const invalidProviderIds = [ - undefined, null, NaN, 0, 1, true, false, '', [], [1, 'a'], {}, { a: 1 }, _.noop]; - invalidProviderIds.forEach((invalidProviderId) => { + INVALID_PROVIDER_IDS.forEach((invalidProviderId) => { it(`should be rejected given an invalid provider ID "${JSON.stringify(invalidProviderId)}"`, () => { return (auth as Auth).getProviderConfig(invalidProviderId as any) .then(() => { @@ -2860,15 +3049,16 @@ AUTH_CONFIGS.forEach((testConfig) => { .should.eventually.be.rejected.and.have.property('code', 'auth/invalid-provider-id'); }); - it('should be rejected given an invalid provider ID', () => { - const invalidProviderId = ''; - return (auth as Auth).deleteProviderConfig(invalidProviderId) - .then(() => { - throw new Error('Unexpected success'); - }) - .catch((error) => { - expect(error).to.have.property('code', 'auth/invalid-provider-id'); - }); + INVALID_PROVIDER_IDS.forEach((invalidProviderId) => { + it(`should be rejected given an invalid provider ID "${JSON.stringify(invalidProviderId)}"`, () => { + return (auth as Auth).deleteProviderConfig(invalidProviderId as any) + .then(() => { + throw new Error('Unexpected success'); + }) + .catch((error) => { + expect(error).to.have.property('code', 'auth/invalid-provider-id'); + }); + }); }); it('should be rejected given an app which returns null access tokens', () => { @@ -2979,15 +3169,16 @@ AUTH_CONFIGS.forEach((testConfig) => { .should.eventually.be.rejected.and.have.property('code', 'auth/invalid-provider-id'); }); - it('should be rejected given an invalid provider ID', () => { - const invalidProviderId = ''; - return (auth as Auth).updateProviderConfig(invalidProviderId, oidcConfigOptions) - .then(() => { - throw new Error('Unexpected success'); - }) - .catch((error) => { - expect(error).to.have.property('code', 'auth/invalid-provider-id'); - }); + INVALID_PROVIDER_IDS.forEach((invalidProviderId) => { + it(`should be rejected given an invalid provider ID "${JSON.stringify(invalidProviderId)}"`, () => { + return (auth as Auth).updateProviderConfig(invalidProviderId as any, oidcConfigOptions) + .then(() => { + throw new Error('Unexpected success'); + }) + .catch((error) => { + expect(error).to.have.property('code', 'auth/invalid-provider-id'); + }); + }); }); it('should be rejected given no options', () => { From 5c60cc4a9f7bb669fe26032c9badeeb7dac4436e Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Wed, 10 Feb 2021 15:08:18 -0500 Subject: [PATCH 009/102] (chore): Export UserProvider type and add it to toc.yaml (#1165) - Export UserProvider type - Add UserProvider to toc.yaml --- docgen/content-sources/node/toc.yaml | 2 ++ etc/firebase-admin.api.md | 1 - src/auth/index.ts | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docgen/content-sources/node/toc.yaml b/docgen/content-sources/node/toc.yaml index a563b7417a..487d3fcc39 100644 --- a/docgen/content-sources/node/toc.yaml +++ b/docgen/content-sources/node/toc.yaml @@ -94,6 +94,8 @@ toc: path: /docs/reference/admin/node/admin.auth.UserProviderRequest - title: "UserRecord" path: /docs/reference/admin/node/admin.auth.UserRecord + - title: "UserProvider" + path: /docs/reference/admin/node/admin.auth.UserProvider - title: "SessionCookieOptions" path: /docs/reference/admin/node/admin.auth.SessionCookieOptions - title: "BaseAuth" diff --git a/etc/firebase-admin.api.md b/etc/firebase-admin.api.md index f90136685f..eaf280aec8 100644 --- a/etc/firebase-admin.api.md +++ b/etc/firebase-admin.api.md @@ -403,7 +403,6 @@ export namespace auth { tokensValidAfterTime?: string; uid: string; } - {}; } // @public (undocumented) diff --git a/src/auth/index.ts b/src/auth/index.ts index 7817435c14..b1bc47f725 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -156,7 +156,7 @@ export namespace auth { /** * Represents a user identity provider that can be associated with a Firebase user. */ - interface UserProvider { + export interface UserProvider { /** * The user identifier for the linked provider. From 95857754bdaf52d401837567ac6692702d1bd355 Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Wed, 10 Feb 2021 17:41:58 -0500 Subject: [PATCH 010/102] [chore] Release 9.5.0 (#1167) Release 9.5.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ee75e5e196..aa1c323a75 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-admin", - "version": "9.4.2", + "version": "9.5.0", "description": "Firebase admin SDK for Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", From 93362d5721fc45f3fc541543813d5739eda8ba2d Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Thu, 11 Feb 2021 11:12:30 -0800 Subject: [PATCH 011/102] chore: Updated doc generator for typedoc 0.19.0 (#1166) --- docgen/generate-docs.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/docgen/generate-docs.js b/docgen/generate-docs.js index 4f8b8df736..12f93706b7 100644 --- a/docgen/generate-docs.js +++ b/docgen/generate-docs.js @@ -46,7 +46,11 @@ const contentPath = path.resolve(`${__dirname}/content-sources/node`); const tempHomePath = path.resolve(`${contentPath}/HOME_TEMP.md`); const devsitePath = `/docs/reference/admin/node/`; -const firestoreExcludes = ['v1', 'v1beta1', 'setLogFunction','DocumentData']; +const firestoreExcludes = [ + 'v1', 'v1beta1', 'setLogFunction','DocumentData', + 'BulkWriterOptions', 'DocumentChangeType', 'FirestoreDataConverter', + 'GrpcStatus', 'Precondition', 'ReadOptions', 'UpdateData', 'Settings', +]; const firestoreHtmlPath = `${docPath}/admin.firestore.html`; const firestoreHeader = `

Type aliases

@@ -278,6 +282,18 @@ function updateHtml(htmlPath, contentBlock) { const dom = new jsdom.JSDOM(fs.readFileSync(htmlPath)); const contentNode = dom.window.document.body.querySelector('.col-12'); + // Recent versions of Typedoc generates an additional index section and a variables + // section for namespaces with re-exports. We iterate through these nodes and remove + // them from the output. + const sections = []; + contentNode.childNodes.forEach((child) => { + if (child.nodeName === 'SECTION') { + sections.push(child); + } + }); + contentNode.removeChild(sections[1]); + contentNode.removeChild(sections[2]); + const newSection = new jsdom.JSDOM(contentBlock); contentNode.appendChild(newSection.window.document.body.firstChild); fs.writeFileSync(htmlPath, dom.window.document.documentElement.outerHTML); From 6bcffa2fdca8ee9b9fb49a0109ddc72f4f6d4c2e Mon Sep 17 00:00:00 2001 From: egilmorez Date: Fri, 26 Feb 2021 13:53:01 -0700 Subject: [PATCH 012/102] Update HOME.md (#1181) Quick addition of a little bit of clarifying verbiage per an internal bug report. Thanks! --- docgen/content-sources/node/HOME.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docgen/content-sources/node/HOME.md b/docgen/content-sources/node/HOME.md index bf22c94da8..4e253f7733 100644 --- a/docgen/content-sources/node/HOME.md +++ b/docgen/content-sources/node/HOME.md @@ -1,6 +1,6 @@ # Firebase Admin Node.js SDK Reference -The Admin SDK lets you interact with Firebase from privileged environments. +The Admin SDK is a set of server libraries that lets you interact with Firebase from privileged environments. You can install it via our [npm package](https://www.npmjs.com/package/firebase-admin). To get started using the Firebase Admin Node.js SDK, see From 994fd43e36c0824deba176763010dc141e9bb0f4 Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Wed, 10 Mar 2021 12:10:26 -0800 Subject: [PATCH 013/102] feat(rtdb): Support emulator mode for rules management operations (#1190) * feat(rtdb): Support emulator mode for rules management operations * fix: Adding namespace to emulated URL string * fix: Consolidated unit testing * fix: Removed extra whitespace --- src/database/database-internal.ts | 23 +++++- test/unit/database/database.spec.ts | 117 +++++++++++++++++++++++++--- 2 files changed, 125 insertions(+), 15 deletions(-) diff --git a/src/database/database-internal.ts b/src/database/database-internal.ts index a469a41773..b77f536b97 100644 --- a/src/database/database-internal.ts +++ b/src/database/database-internal.ts @@ -63,7 +63,7 @@ export class DatabaseService { /** * Returns the app associated with this DatabaseService instance. * - * @return {FirebaseApp} The app associated with this DatabaseService instance. + * @return The app associated with this DatabaseService instance. */ get app(): FirebaseApp { return this.appInternal; @@ -123,7 +123,13 @@ class DatabaseRulesClient { private readonly httpClient: AuthorizedHttpClient; constructor(app: FirebaseApp, dbUrl: string) { - const parsedUrl = new URL(dbUrl); + let parsedUrl = new URL(dbUrl); + const emulatorHost = process.env.FIREBASE_DATABASE_EMULATOR_HOST; + if (emulatorHost) { + const namespace = extractNamespace(parsedUrl); + parsedUrl = new URL(`http://${emulatorHost}?ns=${namespace}`); + } + parsedUrl.pathname = path.join(parsedUrl.pathname, RULES_URL_PATH); this.dbUrl = parsedUrl.toString(); this.httpClient = new AuthorizedHttpClient(app); @@ -133,7 +139,7 @@ class DatabaseRulesClient { * Gets the currently applied security rules as a string. The return value consists of * the rules source including comments. * - * @return {Promise} A promise fulfilled with the rules as a raw string. + * @return A promise fulfilled with the rules as a raw string. */ public getRules(): Promise { const req: HttpRequestConfig = { @@ -233,3 +239,14 @@ class DatabaseRulesClient { return `${intro}: ${err.response.text}`; } } + +function extractNamespace(parsedUrl: URL): string { + const ns = parsedUrl.searchParams.get('ns'); + if (ns) { + return ns; + } + + const hostname = parsedUrl.hostname; + const dotIndex = hostname.indexOf('.'); + return hostname.substring(0, dotIndex).toLowerCase(); +} diff --git a/test/unit/database/database.spec.ts b/test/unit/database/database.spec.ts index 4a4f969b1e..3979719ab6 100644 --- a/test/unit/database/database.spec.ts +++ b/test/unit/database/database.spec.ts @@ -48,7 +48,7 @@ describe('Database', () => { describe('Constructor', () => { const invalidApps = [null, NaN, 0, 1, true, false, '', 'a', [], [1, 'a'], {}, { a: 1 }, _.noop]; invalidApps.forEach((invalidApp) => { - it(`should throw given invalid app: ${ JSON.stringify(invalidApp) }`, () => { + it(`should throw given invalid app: ${JSON.stringify(invalidApp)}`, () => { expect(() => { const databaseAny: any = DatabaseService; return new databaseAny(invalidApp); @@ -154,11 +154,8 @@ describe('Database', () => { }`; const rulesPath = '.settings/rules.json'; - function callParamsForGet( - strict = false, - url = `https://databasename.firebaseio.com/${rulesPath}`, - ): HttpRequestConfig { - + function callParamsForGet(options?: { strict?: boolean; url?: string }): HttpRequestConfig { + const url = options?.url || `https://databasename.firebaseio.com/${rulesPath}`; const params: HttpRequestConfig = { method: 'GET', url, @@ -167,7 +164,7 @@ describe('Database', () => { }, }; - if (strict) { + if (options?.strict) { params.data = { format: 'strict' }; } @@ -215,7 +212,7 @@ describe('Database', () => { return db.getRules().then((result) => { expect(result).to.equal(rulesString); return expect(stub).to.have.been.calledOnce.and.calledWith( - callParamsForGet(false, `https://custom.firebaseio.com/${rulesPath}`)); + callParamsForGet({ url: `https://custom.firebaseio.com/${rulesPath}` })); }); }); @@ -225,7 +222,7 @@ describe('Database', () => { return db.getRules().then((result) => { expect(result).to.equal(rulesString); return expect(stub).to.have.been.calledOnce.and.calledWith( - callParamsForGet(false, `http://localhost:9000/${rulesPath}?ns=foo`)); + callParamsForGet({ url: `http://localhost:9000/${rulesPath}?ns=foo` })); }); }); @@ -259,7 +256,7 @@ describe('Database', () => { return db.getRulesJSON().then((result) => { expect(result).to.deep.equal(rules); return expect(stub).to.have.been.calledOnce.and.calledWith( - callParamsForGet(true)); + callParamsForGet({ strict: true })); }); }); @@ -269,7 +266,7 @@ describe('Database', () => { return db.getRulesJSON().then((result) => { expect(result).to.deep.equal(rules); return expect(stub).to.have.been.calledOnce.and.calledWith( - callParamsForGet(true, `https://custom.firebaseio.com/${rulesPath}`)); + callParamsForGet({ strict: true, url: `https://custom.firebaseio.com/${rulesPath}` })); }); }); @@ -279,7 +276,7 @@ describe('Database', () => { return db.getRulesJSON().then((result) => { expect(result).to.deep.equal(rules); return expect(stub).to.have.been.calledOnce.and.calledWith( - callParamsForGet(true, `http://localhost:9000/${rulesPath}?ns=foo`)); + callParamsForGet({ strict: true, url: `http://localhost:9000/${rulesPath}?ns=foo` })); }); }); @@ -409,5 +406,101 @@ describe('Database', () => { return db.setRules(rules).should.eventually.be.rejectedWith('network error'); }); }); + + describe('emulator mode', () => { + interface EmulatorTestConfig { + name: string; + setUp: () => FirebaseApp; + tearDown?: () => void; + url: string; + } + + const configs: EmulatorTestConfig[] = [ + { + name: 'with environment variable', + setUp: () => { + process.env.FIREBASE_DATABASE_EMULATOR_HOST = 'localhost:9090'; + return mocks.app(); + }, + tearDown: () => { + delete process.env.FIREBASE_DATABASE_EMULATOR_HOST; + }, + url: `http://localhost:9090/${rulesPath}?ns=databasename`, + }, + { + name: 'with app options', + setUp: () => { + return mocks.appWithOptions({ + databaseURL: 'http://localhost:9091?ns=databasename', + }); + }, + url: `http://localhost:9091/${rulesPath}?ns=databasename`, + }, + { + name: 'with environment variable overriding app options', + setUp: () => { + process.env.FIREBASE_DATABASE_EMULATOR_HOST = 'localhost:9090'; + return mocks.appWithOptions({ + databaseURL: 'http://localhost:9091?ns=databasename', + }); + }, + tearDown: () => { + delete process.env.FIREBASE_DATABASE_EMULATOR_HOST; + }, + url: `http://localhost:9090/${rulesPath}?ns=databasename`, + }, + ]; + + configs.forEach((config) => { + describe(config.name, () => { + let emulatorApp: FirebaseApp; + let emulatorDatabase: DatabaseService; + + before(() => { + emulatorApp = config.setUp(); + emulatorDatabase = new DatabaseService(emulatorApp); + }); + + after(() => { + if (config.tearDown) { + config.tearDown(); + } + + return emulatorDatabase.delete().then(() => { + return emulatorApp.delete(); + }); + }); + + it('getRules should connect to the emulator', () => { + const db: Database = emulatorDatabase.getDatabase(); + const stub = stubSuccessfulResponse(rules); + return db.getRules().then((result) => { + expect(result).to.equal(rulesString); + return expect(stub).to.have.been.calledOnce.and.calledWith( + callParamsForGet({ url: config.url })); + }); + }); + + it('getRulesJSON should connect to the emulator', () => { + const db: Database = emulatorDatabase.getDatabase(); + const stub = stubSuccessfulResponse(rules); + return db.getRulesJSON().then((result) => { + expect(result).to.equal(rules); + return expect(stub).to.have.been.calledOnce.and.calledWith( + callParamsForGet({ strict: true, url: config.url })); + }); + }); + + it('setRules should connect to the emulator', () => { + const db: Database = emulatorDatabase.getDatabase(); + const stub = stubSuccessfulResponse({}); + return db.setRules(rulesString).then(() => { + return expect(stub).to.have.been.calledOnce.and.calledWith( + callParamsForPut(rulesString, config.url)); + }); + }); + }); + }); + }); }); }); From bf4bacb18dc2e500a54ae7aa93b2db334c6ad4db Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Thu, 18 Mar 2021 16:20:46 -0700 Subject: [PATCH 014/102] fix: Decoupled proactive token refresh from FirebaseApp (#1194) * fix: Decoupled proactive token refresh from FirebaseApp * fix: Defined constants for duration values * fix: Logging errors encountered while scheduling a refresh * fix: Renamed some variables for clarity --- src/database/database-internal.ts | 44 ++++++ src/firebase-app.ts | 210 +++++++++------------------- test/unit/database/database.spec.ts | 174 +++++++++++++++++++++++ test/unit/firebase-app.spec.ts | 201 -------------------------- 4 files changed, 283 insertions(+), 346 deletions(-) diff --git a/src/database/database-internal.ts b/src/database/database-internal.ts index b77f536b97..dd28bfd919 100644 --- a/src/database/database-internal.ts +++ b/src/database/database-internal.ts @@ -28,9 +28,13 @@ import { getSdkVersion } from '../utils/index'; import Database = database.Database; +const TOKEN_REFRESH_THRESHOLD_MILLIS = 5 * 60 * 1000; + export class DatabaseService { private readonly appInternal: FirebaseApp; + private tokenListenerRegistered: boolean; + private tokenRefreshTimeout: NodeJS.Timeout; private databases: { [dbUrl: string]: Database; @@ -50,6 +54,12 @@ export class DatabaseService { * @internal */ public delete(): Promise { + if (this.tokenListenerRegistered) { + this.appInternal.INTERNAL.removeAuthTokenListener(this.onTokenChange); + clearTimeout(this.tokenRefreshTimeout); + this.tokenListenerRegistered = false; + } + const promises = []; for (const dbUrl of Object.keys(this.databases)) { const db: DatabaseImpl = ((this.databases[dbUrl] as any) as DatabaseImpl); @@ -96,9 +106,43 @@ export class DatabaseService { this.databases[dbUrl] = db; } + + if (!this.tokenListenerRegistered) { + this.tokenListenerRegistered = true; + this.appInternal.INTERNAL.addAuthTokenListener(this.onTokenChange); + } + return db; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private onTokenChange(_: string): void { + this.appInternal.INTERNAL.getToken() + .then((token) => { + const delayMillis = token.expirationTime - TOKEN_REFRESH_THRESHOLD_MILLIS - Date.now(); + // If the new token is set to expire soon (unlikely), do nothing. Somebody will eventually + // notice and refresh the token, at which point this callback will fire again. + if (delayMillis > 0) { + this.scheduleTokenRefresh(delayMillis); + } + }) + .catch((err) => { + console.error('Unexpected error while attempting to schedule a token refresh:', err); + }); + } + + private scheduleTokenRefresh(delayMillis: number): void { + clearTimeout(this.tokenRefreshTimeout); + this.tokenRefreshTimeout = setTimeout(() => { + this.appInternal.INTERNAL.getToken(/*forceRefresh=*/ true) + .catch(() => { + // Ignore the error since this might just be an intermittent failure. If we really cannot + // refresh the token, an error will be logged once the existing token expires and we try + // to fetch a fresh one. + }); + }, delayMillis); + } + private ensureUrl(url?: string): string { if (typeof url !== 'undefined') { return url; diff --git a/src/firebase-app.ts b/src/firebase-app.ts index fb8ad8b0f5..88947bec4c 100644 --- a/src/firebase-app.ts +++ b/src/firebase-app.ts @@ -16,7 +16,7 @@ */ import { AppOptions, app } from './firebase-namespace-api'; -import { credential, GoogleOAuthAccessToken } from './credential/index'; +import { credential } from './credential/index'; import { getApplicationDefault } from './credential/credential-internal'; import * as validator from './utils/validator'; import { deepCopy } from './utils/deep-copy'; @@ -39,6 +39,8 @@ import { RemoteConfig } from './remote-config/remote-config'; import Credential = credential.Credential; import Database = database.Database; +const TOKEN_EXPIRY_THRESHOLD_MILLIS = 5 * 60 * 1000; + /** * Type representing a callback which is called every time an app lifecycle event occurs. */ @@ -57,129 +59,80 @@ export interface FirebaseAccessToken { * Internals of a FirebaseApp instance. */ export class FirebaseAppInternals { - private isDeleted_ = false; private cachedToken_: FirebaseAccessToken; - private cachedTokenPromise_: Promise | null; private tokenListeners_: Array<(token: string) => void>; - private tokenRefreshTimeout_: NodeJS.Timer; constructor(private credential_: Credential) { this.tokenListeners_ = []; } - /** - * Gets an auth token for the associated app. - * - * @param {boolean} forceRefresh Whether or not to force a token refresh. - * @return {Promise} A Promise that will be fulfilled with the current or - * new token. - */ - public getToken(forceRefresh?: boolean): Promise { - const expired = this.cachedToken_ && this.cachedToken_.expirationTime < Date.now(); - if (this.cachedTokenPromise_ && !forceRefresh && !expired) { - return this.cachedTokenPromise_ - .catch((error) => { - // Update the cached token promise to avoid caching errors. Set it to resolve with the - // cached token if we have one (and return that promise since the token has still not - // expired). - if (this.cachedToken_) { - this.cachedTokenPromise_ = Promise.resolve(this.cachedToken_); - return this.cachedTokenPromise_; - } - - // Otherwise, set the cached token promise to null so that it will force a refresh next - // time getToken() is called. - this.cachedTokenPromise_ = null; - - // And re-throw the caught error. - throw error; - }); - } else { - // Clear the outstanding token refresh timeout. This is a noop if the timeout is undefined. - clearTimeout(this.tokenRefreshTimeout_); - - // this.credential_ may be an external class; resolving it in a promise helps us - // protect against exceptions and upgrades the result to a promise in all cases. - this.cachedTokenPromise_ = Promise.resolve(this.credential_.getAccessToken()) - .then((result: GoogleOAuthAccessToken) => { - // Since the developer can provide the credential implementation, we want to weakly verify - // the return type until the type is properly exported. - if (!validator.isNonNullObject(result) || - typeof result.expires_in !== 'number' || - typeof result.access_token !== 'string') { - throw new FirebaseAppError( - AppErrorCodes.INVALID_CREDENTIAL, - `Invalid access token generated: "${JSON.stringify(result)}". Valid access ` + - 'tokens must be an object with the "expires_in" (number) and "access_token" ' + - '(string) properties.', - ); - } - - const token: FirebaseAccessToken = { - accessToken: result.access_token, - expirationTime: Date.now() + (result.expires_in * 1000), - }; - - const hasAccessTokenChanged = (this.cachedToken_ && this.cachedToken_.accessToken !== token.accessToken); - const hasExpirationChanged = (this.cachedToken_ && this.cachedToken_.expirationTime !== token.expirationTime); - if (!this.cachedToken_ || hasAccessTokenChanged || hasExpirationChanged) { - this.cachedToken_ = token; - this.tokenListeners_.forEach((listener) => { - listener(token.accessToken); - }); - } - - // Establish a timeout to proactively refresh the token every minute starting at five - // minutes before it expires. Once a token refresh succeeds, no further retries are - // needed; if it fails, retry every minute until the token expires (resulting in a total - // of four retries: at 4, 3, 2, and 1 minutes). - let refreshTimeInSeconds = (result.expires_in - (5 * 60)); - let numRetries = 4; - - // In the rare cases the token is short-lived (that is, it expires in less than five - // minutes from when it was fetched), establish the timeout to refresh it after the - // current minute ends and update the number of retries that should be attempted before - // the token expires. - if (refreshTimeInSeconds <= 0) { - refreshTimeInSeconds = result.expires_in % 60; - numRetries = Math.floor(result.expires_in / 60) - 1; - } - - // The token refresh timeout keeps the Node.js process alive, so only create it if this - // instance has not already been deleted. - if (numRetries && !this.isDeleted_) { - this.setTokenRefreshTimeout(refreshTimeInSeconds * 1000, numRetries); - } - - return token; - }) - .catch((error) => { - let errorMessage = (typeof error === 'string') ? error : error.message; - - errorMessage = 'Credential implementation provided to initializeApp() via the ' + - '"credential" property failed to fetch a valid Google OAuth2 access token with the ' + - `following error: "${errorMessage}".`; - - if (errorMessage.indexOf('invalid_grant') !== -1) { - errorMessage += ' There are two likely causes: (1) your server time is not properly ' + - 'synced or (2) your certificate key file has been revoked. To solve (1), re-sync the ' + - 'time on your server. To solve (2), make sure the key ID for your key file is still ' + - 'present at https://console.firebase.google.com/iam-admin/serviceaccounts/project. If ' + - 'not, generate a new key file at ' + - 'https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk.'; - } - - throw new FirebaseAppError(AppErrorCodes.INVALID_CREDENTIAL, errorMessage); - }); - - return this.cachedTokenPromise_; + public getToken(forceRefresh = false): Promise { + if (forceRefresh || this.shouldRefresh()) { + return this.refreshToken(); } + + return Promise.resolve(this.cachedToken_); + } + + private refreshToken(): Promise { + return Promise.resolve(this.credential_.getAccessToken()) + .then((result) => { + // Since the developer can provide the credential implementation, we want to weakly verify + // the return type until the type is properly exported. + if (!validator.isNonNullObject(result) || + typeof result.expires_in !== 'number' || + typeof result.access_token !== 'string') { + throw new FirebaseAppError( + AppErrorCodes.INVALID_CREDENTIAL, + `Invalid access token generated: "${JSON.stringify(result)}". Valid access ` + + 'tokens must be an object with the "expires_in" (number) and "access_token" ' + + '(string) properties.', + ); + } + + const token = { + accessToken: result.access_token, + expirationTime: Date.now() + (result.expires_in * 1000), + }; + if (!this.cachedToken_ + || this.cachedToken_.accessToken !== token.accessToken + || this.cachedToken_.expirationTime !== token.expirationTime) { + this.cachedToken_ = token; + this.tokenListeners_.forEach((listener) => { + listener(token.accessToken); + }); + } + + return token; + }) + .catch((error) => { + let errorMessage = (typeof error === 'string') ? error : error.message; + + errorMessage = 'Credential implementation provided to initializeApp() via the ' + + '"credential" property failed to fetch a valid Google OAuth2 access token with the ' + + `following error: "${errorMessage}".`; + + if (errorMessage.indexOf('invalid_grant') !== -1) { + errorMessage += ' There are two likely causes: (1) your server time is not properly ' + + 'synced or (2) your certificate key file has been revoked. To solve (1), re-sync the ' + + 'time on your server. To solve (2), make sure the key ID for your key file is still ' + + 'present at https://console.firebase.google.com/iam-admin/serviceaccounts/project. If ' + + 'not, generate a new key file at ' + + 'https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk.'; + } + + throw new FirebaseAppError(AppErrorCodes.INVALID_CREDENTIAL, errorMessage); + }); + } + + private shouldRefresh(): boolean { + return !this.cachedToken_ || (this.cachedToken_.expirationTime - Date.now()) <= TOKEN_EXPIRY_THRESHOLD_MILLIS; } /** * Adds a listener that is called each time a token changes. * - * @param {function(string)} listener The listener that will be called with each new token. + * @param listener The listener that will be called with each new token. */ public addAuthTokenListener(listener: (token: string) => void): void { this.tokenListeners_.push(listener); @@ -191,42 +144,11 @@ export class FirebaseAppInternals { /** * Removes a token listener. * - * @param {function(string)} listener The listener to remove. + * @param listener The listener to remove. */ public removeAuthTokenListener(listener: (token: string) => void): void { this.tokenListeners_ = this.tokenListeners_.filter((other) => other !== listener); } - - /** - * Deletes the FirebaseAppInternals instance. - */ - public delete(): void { - this.isDeleted_ = true; - - // Clear the token refresh timeout so it doesn't keep the Node.js process alive. - clearTimeout(this.tokenRefreshTimeout_); - } - - /** - * Establishes timeout to refresh the Google OAuth2 access token used by the SDK. - * - * @param {number} delayInMilliseconds The delay to use for the timeout. - * @param {number} numRetries The number of times to retry fetching a new token if the prior fetch - * failed. - */ - private setTokenRefreshTimeout(delayInMilliseconds: number, numRetries: number): void { - this.tokenRefreshTimeout_ = setTimeout(() => { - this.getToken(/* forceRefresh */ true) - .catch(() => { - // Ignore the error since this might just be an intermittent failure. If we really cannot - // refresh the token, an error will be logged once the existing token expires and we try - // to fetch a fresh one. - if (numRetries > 0) { - this.setTokenRefreshTimeout(60 * 1000, numRetries - 1); - } - }); - }, delayInMilliseconds); - } } /** @@ -419,8 +341,6 @@ export class FirebaseApp implements app.App { this.checkDestroyed_(); this.firebaseInternals_.removeApp(this.name_); - this.INTERNAL.delete(); - return Promise.all(Object.keys(this.services_).map((serviceName) => { const service = this.services_[serviceName]; if (isStateful(service)) { diff --git a/test/unit/database/database.spec.ts b/test/unit/database/database.spec.ts index 3979719ab6..7f7626321b 100644 --- a/test/unit/database/database.spec.ts +++ b/test/unit/database/database.spec.ts @@ -25,6 +25,7 @@ import * as mocks from '../../resources/mocks'; import { FirebaseApp } from '../../../src/firebase-app'; import { DatabaseService } from '../../../src/database/database-internal'; import { database } from '../../../src/database/index'; +import { ServiceAccountCredential } from '../../../src/credential/credential-internal'; import * as utils from '../utils'; import { HttpClient, HttpRequestConfig } from '../../../src/utils/api-request'; @@ -118,6 +119,179 @@ describe('Database', () => { }); }); + describe('Token refresh', () => { + const MINUTE_IN_MILLIS = 60 * 1000; + + let clock: sinon.SinonFakeTimers; + let getTokenStub: sinon.SinonStub; + + beforeEach(() => { + getTokenStub = stubCredentials(); + clock = sinon.useFakeTimers(1000); + }); + + afterEach(() => { + getTokenStub.restore(); + clock.restore(); + }); + + function stubCredentials(options?: { + accessToken?: string; + expiresIn?: number; + err?: any; + }): sinon.SinonStub { + if (options?.err) { + return sinon.stub(ServiceAccountCredential.prototype, 'getAccessToken') + .rejects(options.err); + } + + return sinon.stub(ServiceAccountCredential.prototype, 'getAccessToken') + .resolves({ + access_token: options?.accessToken || 'mock-access-token', // eslint-disable-line @typescript-eslint/camelcase + expires_in: options?.expiresIn || 3600, // eslint-disable-line @typescript-eslint/camelcase + }); + } + + it('should refresh the token 5 minutes before expiration', () => { + database.getDatabase(mockApp.options.databaseURL); + expect(getTokenStub).to.have.not.been.called; + mockApp.INTERNAL.getToken() + .then((token) => { + expect(getTokenStub).to.have.been.calledOnce; + + const expiryInMillis = token.expirationTime - Date.now(); + clock.tick(expiryInMillis - (5 * MINUTE_IN_MILLIS) - 1000); + expect(getTokenStub).to.have.been.calledOnce; + + clock.tick(1000); + expect(getTokenStub).to.have.been.calledTwice; + }); + }); + + it('should not start multiple token refresher tasks', () => { + database.getDatabase(mockApp.options.databaseURL); + database.getDatabase('https://other-database.firebaseio.com'); + expect(getTokenStub).to.have.not.been.called; + mockApp.INTERNAL.getToken() + .then((token) => { + expect(getTokenStub).to.have.been.calledOnce; + + const expiryInMillis = token.expirationTime - Date.now(); + clock.tick(expiryInMillis - (5 * MINUTE_IN_MILLIS)); + expect(getTokenStub).to.have.been.calledTwice; + }); + }); + + it('should reschedule the token refresher when the underlying token changes', () => { + database.getDatabase(mockApp.options.databaseURL); + mockApp.INTERNAL.getToken() + .then((token1) => { + expect(getTokenStub).to.have.been.calledOnce; + + // Forward the clock to 30 minutes before expiry. + const expiryInMillis = token1.expirationTime - Date.now(); + clock.tick(expiryInMillis - (30 * MINUTE_IN_MILLIS)); + + // Force a token refresh + return mockApp.INTERNAL.getToken(true) + .then((token2) => { + expect(getTokenStub).to.have.been.calledTwice; + // Forward the clock to 5 minutes before old expiry time. + clock.tick(25 * MINUTE_IN_MILLIS); + expect(getTokenStub).to.have.been.calledTwice; + + // Forward the clock 1 second past old expiry time. + clock.tick(5 * MINUTE_IN_MILLIS + 1000); + expect(getTokenStub).to.have.been.calledTwice; + + const newExpiryTimeInMillis = token2.expirationTime - Date.now(); + clock.tick(newExpiryTimeInMillis - (5 * MINUTE_IN_MILLIS)); + expect(getTokenStub).to.have.been.calledThrice; + }); + }); + }); + + it('should not reschedule when the token is about to expire in 5 minutes', () => { + database.getDatabase(mockApp.options.databaseURL); + mockApp.INTERNAL.getToken() + .then((token1) => { + expect(getTokenStub).to.have.been.calledOnce; + + // Forward the clock to 30 minutes before expiry. + const expiryInMillis = token1.expirationTime - Date.now(); + clock.tick(expiryInMillis - (30 * MINUTE_IN_MILLIS)); + + getTokenStub.restore(); + getTokenStub = stubCredentials({ expiresIn: 5 * 60 }); + // Force a token refresh + return mockApp.INTERNAL.getToken(true); + }) + .then((token2) => { + expect(getTokenStub).to.have.been.calledTwice; + + const newExpiryTimeInMillis = token2.expirationTime - Date.now(); + clock.tick(newExpiryTimeInMillis); + expect(getTokenStub).to.have.been.calledTwice; + + getTokenStub.restore(); + getTokenStub = stubCredentials({ expiresIn: 60 * 60 }); + // Force a token refresh + return mockApp.INTERNAL.getToken(true); + }) + .then((token3) => { + expect(getTokenStub).to.have.been.calledThrice; + + const newExpiryTimeInMillis = token3.expirationTime - Date.now(); + clock.tick(newExpiryTimeInMillis - (5 * MINUTE_IN_MILLIS)); + expect(getTokenStub).to.have.callCount(4); + }); + }); + + it('should gracefully handle errors during token refresh', () => { + database.getDatabase(mockApp.options.databaseURL); + mockApp.INTERNAL.getToken() + .then((token1) => { + expect(getTokenStub).to.have.been.calledOnce; + + getTokenStub.restore(); + getTokenStub = stubCredentials({ err: new Error('Test error') }); + expect(getTokenStub).to.have.not.been.called; + + const expiryInMillis = token1.expirationTime - Date.now(); + clock.tick(expiryInMillis); + expect(getTokenStub).to.have.been.calledOnce; + + getTokenStub.restore(); + getTokenStub = stubCredentials(); + expect(getTokenStub).to.have.not.been.called; + // Force a token refresh + return mockApp.INTERNAL.getToken(true); + }) + .then((token2) => { + expect(getTokenStub).to.have.been.calledOnce; + + const newExpiryTimeInMillis = token2.expirationTime - Date.now(); + clock.tick(newExpiryTimeInMillis - (5 * MINUTE_IN_MILLIS)); + expect(getTokenStub).to.have.been.calledTwice; + }); + }); + + it('should stop the token refresher task at delete', () => { + database.getDatabase(mockApp.options.databaseURL); + mockApp.INTERNAL.getToken() + .then((token) => { + expect(getTokenStub).to.have.been.calledOnce; + return database.delete() + .then(() => { + // Forward the clock to five minutes before expiry. + const expiryInMillis = token.expirationTime - Date.now(); + clock.tick(expiryInMillis - (5 * MINUTE_IN_MILLIS)); + expect(getTokenStub).to.have.been.calledOnce; + }); + }); + }); + }); + describe('Rules', () => { const mockAccessToken: string = utils.generateRandomAccessToken(); let getTokenStub: sinon.SinonStub; diff --git a/test/unit/firebase-app.spec.ts b/test/unit/firebase-app.spec.ts index 49da6736c5..6edf73b7ef 100644 --- a/test/unit/firebase-app.spec.ts +++ b/test/unit/firebase-app.spec.ts @@ -90,9 +90,6 @@ describe('FirebaseApp', () => { }); clock = sinon.useFakeTimers(1000); - - mockApp = mocks.app(); - firebaseConfigVar = process.env[FIREBASE_CONFIG_VAR]; delete process.env[FIREBASE_CONFIG_VAR]; firebaseNamespace = new FirebaseNamespace(); @@ -767,204 +764,6 @@ describe('FirebaseApp', () => { }); }); - it('retries to proactively refresh the token if a proactive refresh attempt fails', () => { - // Force a token refresh. - return mockApp.INTERNAL.getToken(true).then((token1) => { - // Stub the getToken() method to return a rejected promise. - getTokenStub.restore(); - expect(mockApp.options.credential).to.exist; - getTokenStub = sinon.stub(mockApp.options.credential!, 'getAccessToken') - .rejects(new Error('Intentionally rejected')); - - // Forward the clock to exactly five minutes before expiry. - const expiryInMilliseconds = token1.expirationTime - Date.now(); - clock.tick(expiryInMilliseconds - (5 * ONE_MINUTE_IN_MILLISECONDS)); - - // Forward the clock to exactly four minutes before expiry. - clock.tick(60 * 1000); - - // Restore the stubbed getAccessToken() method. - getTokenStub.restore(); - getTokenStub = sinon.stub(ServiceAccountCredential.prototype, 'getAccessToken').resolves({ - access_token: 'mock-access-token', // eslint-disable-line @typescript-eslint/camelcase - expires_in: 3600, // eslint-disable-line @typescript-eslint/camelcase - }); - - return mockApp.INTERNAL.getToken().then((token2) => { - // Ensure the token has not been proactively refreshed. - expect(token1).to.deep.equal(token2); - expect(getTokenStub).to.have.not.been.called; - - // Forward the clock to exactly three minutes before expiry. - clock.tick(60 * 1000); - - return mockApp.INTERNAL.getToken().then((token3) => { - // Ensure the token was proactively refreshed. - expect(token1).to.not.deep.equal(token3); - expect(getTokenStub).to.have.been.calledOnce; - }); - }); - }); - }); - - it('stops retrying to proactively refresh the token after five attempts', () => { - // Force a token refresh. - let originalToken: FirebaseAccessToken; - return mockApp.INTERNAL.getToken(true).then((token) => { - originalToken = token; - - // Stub the credential's getAccessToken() method to always return a rejected promise. - getTokenStub.restore(); - expect(mockApp.options.credential).to.exist; - getTokenStub = sinon.stub(mockApp.options.credential!, 'getAccessToken') - .rejects(new Error('Intentionally rejected')); - - // Expect the call count to initially be zero. - expect(getTokenStub.callCount).to.equal(0); - - // Forward the clock to exactly five minutes before expiry. - const expiryInMilliseconds = token.expirationTime - Date.now(); - clock.tick(expiryInMilliseconds - (5 * ONE_MINUTE_IN_MILLISECONDS)); - - // Due to synchronous timing issues when the timer is mocked, make a call to getToken() - // without forcing a refresh to ensure there is enough time for the underlying token refresh - // timeout to fire and complete. - return mockApp.INTERNAL.getToken(); - }).then((token) => { - // Ensure the token was attempted to be proactively refreshed one time. - expect(getTokenStub.callCount).to.equal(1); - - // Ensure the proactive refresh failed. - expect(token).to.deep.equal(originalToken); - - // Forward the clock to four minutes before expiry. - clock.tick(ONE_MINUTE_IN_MILLISECONDS); - - // See note above about calling getToken(). - return mockApp.INTERNAL.getToken(); - }).then((token) => { - // Ensure the token was attempted to be proactively refreshed two times. - expect(getTokenStub.callCount).to.equal(2); - - // Ensure the proactive refresh failed. - expect(token).to.deep.equal(originalToken); - - // Forward the clock to three minutes before expiry. - clock.tick(ONE_MINUTE_IN_MILLISECONDS); - - // See note above about calling getToken(). - return mockApp.INTERNAL.getToken(); - }).then((token) => { - // Ensure the token was attempted to be proactively refreshed three times. - expect(getTokenStub.callCount).to.equal(3); - - // Ensure the proactive refresh failed. - expect(token).to.deep.equal(originalToken); - - // Forward the clock to two minutes before expiry. - clock.tick(ONE_MINUTE_IN_MILLISECONDS); - - // See note above about calling getToken(). - return mockApp.INTERNAL.getToken(); - }).then((token) => { - // Ensure the token was attempted to be proactively refreshed four times. - expect(getTokenStub.callCount).to.equal(4); - - // Ensure the proactive refresh failed. - expect(token).to.deep.equal(originalToken); - - // Forward the clock to one minute before expiry. - clock.tick(ONE_MINUTE_IN_MILLISECONDS); - - // See note above about calling getToken(). - return mockApp.INTERNAL.getToken(); - }).then((token) => { - // Ensure the token was attempted to be proactively refreshed five times. - expect(getTokenStub.callCount).to.equal(5); - - // Ensure the proactive refresh failed. - expect(token).to.deep.equal(originalToken); - - // Forward the clock to expiry. - clock.tick(ONE_MINUTE_IN_MILLISECONDS); - - // See note above about calling getToken(). - return mockApp.INTERNAL.getToken(); - }).then((token) => { - // Ensure the token was not attempted to be proactively refreshed a sixth time. - expect(getTokenStub.callCount).to.equal(5); - - // Ensure the token has never been refresh. - expect(token).to.deep.equal(originalToken); - }); - }); - - it('resets the proactive refresh timeout upon a force refresh', () => { - // Force a token refresh. - return mockApp.INTERNAL.getToken(true).then((token1) => { - // Forward the clock to five minutes and one second before expiry. - let expiryInMilliseconds = token1.expirationTime - Date.now(); - clock.tick(expiryInMilliseconds - (5 * ONE_MINUTE_IN_MILLISECONDS) - 1000); - - // Force a token refresh. - return mockApp.INTERNAL.getToken(true).then((token2) => { - // Ensure the token was force refreshed. - expect(token1).to.not.deep.equal(token2); - expect(getTokenStub).to.have.been.calledTwice; - - // Forward the clock to exactly five minutes before the original token's expiry. - clock.tick(1000); - - return mockApp.INTERNAL.getToken().then((token3) => { - // Ensure the token hasn't changed, meaning the proactive refresh was canceled. - expect(token2).to.deep.equal(token3); - expect(getTokenStub).to.have.been.calledTwice; - - // Forward the clock to exactly five minutes before the refreshed token's expiry. - expiryInMilliseconds = token3.expirationTime - Date.now(); - clock.tick(expiryInMilliseconds - (5 * ONE_MINUTE_IN_MILLISECONDS)); - - return mockApp.INTERNAL.getToken().then((token4) => { - // Ensure the token was proactively refreshed. - expect(token3).to.not.deep.equal(token4); - expect(getTokenStub).to.have.been.calledThrice; - }); - }); - }); - }); - }); - - it('proactively refreshes the token at the next full minute if it expires in five minutes or less', () => { - // Turn off default mocking of one hour access tokens and replace it with a short-lived token. - getTokenStub.restore(); - expect(mockApp.options.credential).to.exist; - getTokenStub = sinon.stub(mockApp.options.credential!, 'getAccessToken').resolves({ - access_token: utils.generateRandomAccessToken(), // eslint-disable-line @typescript-eslint/camelcase - expires_in: 3 * 60 + 10, // eslint-disable-line @typescript-eslint/camelcase - }); - // Expect the call count to initially be zero. - expect(getTokenStub.callCount).to.equal(0); - - // Force a token refresh. - return mockApp.INTERNAL.getToken(true).then((token1) => { - - // Move the clock forward to three minutes and one second before expiry. - clock.tick(9 * 1000); - expect(getTokenStub.callCount).to.equal(1); - - // Move the clock forward to exactly three minutes before expiry. - clock.tick(1000); - - // Expect the underlying getAccessToken() method to have been called once. - expect(getTokenStub.callCount).to.equal(2); - - return mockApp.INTERNAL.getToken().then((token2) => { - // Ensure the token was proactively refreshed. - expect(token1).to.not.deep.equal(token2); - }); - }); - }); - it('Includes the original error in exception', () => { getTokenStub.restore(); const mockError = new FirebaseAppError( From 738eba78a15756706d92d4844074d71d915eb585 Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Fri, 19 Mar 2021 15:24:31 -0700 Subject: [PATCH 015/102] fix(rtdb): Fixing the RTDB token listener callback (#1203) --- src/database/database-internal.ts | 13 +++++------ test/unit/database/database.spec.ts | 36 +++++++++++++++-------------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/database/database-internal.ts b/src/database/database-internal.ts index dd28bfd919..66d8db883c 100644 --- a/src/database/database-internal.ts +++ b/src/database/database-internal.ts @@ -33,7 +33,7 @@ const TOKEN_REFRESH_THRESHOLD_MILLIS = 5 * 60 * 1000; export class DatabaseService { private readonly appInternal: FirebaseApp; - private tokenListenerRegistered: boolean; + private tokenListener: (token: string) => void; private tokenRefreshTimeout: NodeJS.Timeout; private databases: { @@ -54,10 +54,9 @@ export class DatabaseService { * @internal */ public delete(): Promise { - if (this.tokenListenerRegistered) { - this.appInternal.INTERNAL.removeAuthTokenListener(this.onTokenChange); + if (this.tokenListener) { + this.appInternal.INTERNAL.removeAuthTokenListener(this.tokenListener); clearTimeout(this.tokenRefreshTimeout); - this.tokenListenerRegistered = false; } const promises = []; @@ -107,9 +106,9 @@ export class DatabaseService { this.databases[dbUrl] = db; } - if (!this.tokenListenerRegistered) { - this.tokenListenerRegistered = true; - this.appInternal.INTERNAL.addAuthTokenListener(this.onTokenChange); + if (!this.tokenListener) { + this.tokenListener = this.onTokenChange.bind(this); + this.appInternal.INTERNAL.addAuthTokenListener(this.tokenListener); } return db; diff --git a/test/unit/database/database.spec.ts b/test/unit/database/database.spec.ts index 7f7626321b..739dcbccfc 100644 --- a/test/unit/database/database.spec.ts +++ b/test/unit/database/database.spec.ts @@ -153,9 +153,9 @@ describe('Database', () => { } it('should refresh the token 5 minutes before expiration', () => { - database.getDatabase(mockApp.options.databaseURL); + database.getDatabase(); expect(getTokenStub).to.have.not.been.called; - mockApp.INTERNAL.getToken() + return mockApp.INTERNAL.getToken() .then((token) => { expect(getTokenStub).to.have.been.calledOnce; @@ -169,10 +169,10 @@ describe('Database', () => { }); it('should not start multiple token refresher tasks', () => { - database.getDatabase(mockApp.options.databaseURL); + database.getDatabase(); database.getDatabase('https://other-database.firebaseio.com'); expect(getTokenStub).to.have.not.been.called; - mockApp.INTERNAL.getToken() + return mockApp.INTERNAL.getToken() .then((token) => { expect(getTokenStub).to.have.been.calledOnce; @@ -183,8 +183,8 @@ describe('Database', () => { }); it('should reschedule the token refresher when the underlying token changes', () => { - database.getDatabase(mockApp.options.databaseURL); - mockApp.INTERNAL.getToken() + database.getDatabase(); + return mockApp.INTERNAL.getToken() .then((token1) => { expect(getTokenStub).to.have.been.calledOnce; @@ -211,9 +211,11 @@ describe('Database', () => { }); }); - it('should not reschedule when the token is about to expire in 5 minutes', () => { - database.getDatabase(mockApp.options.databaseURL); - mockApp.INTERNAL.getToken() + // Currently doesn't work as expected since onTokenChange() can force a token refresh + // by calling getToken(). Skipping for now. + xit('should not reschedule when the token is about to expire in 5 minutes', () => { + database.getDatabase(); + return mockApp.INTERNAL.getToken() .then((token1) => { expect(getTokenStub).to.have.been.calledOnce; @@ -227,11 +229,11 @@ describe('Database', () => { return mockApp.INTERNAL.getToken(true); }) .then((token2) => { - expect(getTokenStub).to.have.been.calledTwice; + expect(getTokenStub).to.have.been.calledOnce; const newExpiryTimeInMillis = token2.expirationTime - Date.now(); clock.tick(newExpiryTimeInMillis); - expect(getTokenStub).to.have.been.calledTwice; + expect(getTokenStub).to.have.been.calledOnce; getTokenStub.restore(); getTokenStub = stubCredentials({ expiresIn: 60 * 60 }); @@ -239,17 +241,17 @@ describe('Database', () => { return mockApp.INTERNAL.getToken(true); }) .then((token3) => { - expect(getTokenStub).to.have.been.calledThrice; + expect(getTokenStub).to.have.been.calledOnce; const newExpiryTimeInMillis = token3.expirationTime - Date.now(); clock.tick(newExpiryTimeInMillis - (5 * MINUTE_IN_MILLIS)); - expect(getTokenStub).to.have.callCount(4); + expect(getTokenStub).to.have.been.calledTwice; }); }); it('should gracefully handle errors during token refresh', () => { - database.getDatabase(mockApp.options.databaseURL); - mockApp.INTERNAL.getToken() + database.getDatabase(); + return mockApp.INTERNAL.getToken() .then((token1) => { expect(getTokenStub).to.have.been.calledOnce; @@ -277,8 +279,8 @@ describe('Database', () => { }); it('should stop the token refresher task at delete', () => { - database.getDatabase(mockApp.options.databaseURL); - mockApp.INTERNAL.getToken() + database.getDatabase(); + return mockApp.INTERNAL.getToken() .then((token) => { expect(getTokenStub).to.have.been.calledOnce; return database.delete() From 97d382352542c4774e7c55b6fdfa29c3bc848d79 Mon Sep 17 00:00:00 2001 From: Yuchen Shi Date: Mon, 22 Mar 2021 17:47:57 -0700 Subject: [PATCH 016/102] Add emulator-based integration tests. (#1155) * Add emulator-based integration tests. * Move emulator stuff out of package.json. * Update CONTRIBUTING.md too. * Add npx. * Skip new unsupported tests. * Inline commands in ci.yml. --- .github/workflows/ci.yml | 13 ++++++++++--- CONTRIBUTING.md | 23 ++++++++++++++++++++++- test/integration/auth.spec.ts | 10 ++++++++-- test/integration/database.spec.ts | 22 ++++++++++++++++++---- 4 files changed, 58 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b5810ed895..244d123dd6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,10 +17,17 @@ jobs: uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - - name: Install, build and test + - name: Install and build run: | npm ci npm run build npm run build:tests - npm test - npm run api-extractor + - name: Lint and run unit tests + run: npm test + - name: Run api-extractor + run: npm run api-extractor + - name: Run emulator-based integration tests + run: | + npm install -g firebase-tools + firebase emulators:exec --project fake-project-id --only auth,database,firestore \ + 'npx mocha \"test/integration/{auth,database,firestore}.spec.ts\" --slow 5000 --timeout 20000 --require ts-node/register' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dbb374fa14..7ac6a71cb1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -123,6 +123,8 @@ There are two test suites: unit and integration. The unit test suite is intended development, and the integration test suite is intended to be run before packaging up release candidates. +#### Unit Tests + To run the unit test suite: ```bash @@ -135,7 +137,26 @@ If you wish to skip the linter, and only run the unit tests: $ npm run test:unit ``` -The integration tests run against an actual Firebase project. Create a new +#### Integration Tests with Emulator Suite + +Some of the integration tests work with the Emulator Suite and you can run them +without an actual Firebase project. + +First, make sure to [install Firebase CLI](https://firebase.google.com/docs/cli#install_the_firebase_cli). +And then: + +```bash + firebase emulators:exec --project fake-project-id --only auth,database,firestore \ + 'npx mocha \"test/integration/{auth,database,firestore}.spec.ts\" --slow 5000 --timeout 20000 --require ts-node/register' +``` + +Currently, only the Auth, Database, and Firestore test suites work. Some test +cases will be automatically skipped due to lack of emulator support. The section +below covers how to run the full test suite against an actual Firebase project. + +#### Integration Tests with an actual Firebase project + +Other integration tests require an actual Firebase project. Create a new project in the [Firebase Console](https://console.firebase.google.com), if you do not already have one suitable for running the tests against. Then obtain the following credentials from the project: diff --git a/test/integration/auth.spec.ts b/test/integration/auth.spec.ts index bf9f7f134d..29060b4cdd 100644 --- a/test/integration/auth.spec.ts +++ b/test/integration/auth.spec.ts @@ -683,7 +683,10 @@ describe('admin.auth', () => { }); }); - it('can link/unlink with a federated provider', async () => { + it('can link/unlink with a federated provider', async function () { + if (authEmulatorHost) { + return this.skip(); // Not yet supported in Auth Emulator. + } const googleFederatedUid = 'google_uid_' + generateRandomString(10); let userRecord = await admin.auth().updateUser(updateUser.uid, { providerToLink: { @@ -707,7 +710,10 @@ describe('admin.auth', () => { expect(providerIds).to.not.deep.include('google.com'); }); - it('can unlink multiple providers at once, incl a non-federated provider', async () => { + it('can unlink multiple providers at once, incl a non-federated provider', async function () { + if (authEmulatorHost) { + return this.skip(); // Not yet supported in Auth Emulator. + } await deletePhoneNumberUser('+15555550001'); const googleFederatedUid = 'google_uid_' + generateRandomString(10); diff --git a/test/integration/database.spec.ts b/test/integration/database.spec.ts index 708c3a153a..77204a1839 100644 --- a/test/integration/database.spec.ts +++ b/test/integration/database.spec.ts @@ -17,7 +17,7 @@ import * as admin from '../../lib/index'; import * as chai from 'chai'; import * as chaiAsPromised from 'chai-as-promised'; -import { defaultApp, nullApp, nonNullApp, cmdArgs, databaseUrl } from './setup'; +import { defaultApp, nullApp, nonNullApp, cmdArgs, databaseUrl, isEmulator } from './setup'; // eslint-disable-next-line @typescript-eslint/no-var-requires const chalk = require('chalk'); @@ -64,7 +64,13 @@ describe('admin.database', () => { .should.eventually.be.fulfilled; }); - it('App with null auth overrides is blocked by security rules', () => { + it('App with null auth overrides is blocked by security rules', function () { + if (isEmulator) { + // RTDB emulator has open security rules by default and won't block this. + // TODO(https://github.com/firebase/firebase-admin-node/issues/1149): + // remove this once updating security rules through admin is in place. + return this.skip(); + } return nullApp.database().ref('blocked').set(admin.database.ServerValue.TIMESTAMP) .should.eventually.be.rejectedWith('PERMISSION_DENIED: Permission denied'); }); @@ -157,13 +163,21 @@ describe('admin.database', () => { }); }); - it('admin.database().getRules() returns currently defined rules as a string', () => { + it('admin.database().getRules() returns currently defined rules as a string', function () { + if (isEmulator) { + // https://github.com/firebase/firebase-admin-node/issues/1149 + return this.skip(); + } return admin.database().getRules().then((result) => { return expect(result).to.be.not.empty; }); }); - it('admin.database().getRulesJSON() returns currently defined rules as an object', () => { + it('admin.database().getRulesJSON() returns currently defined rules as an object', function () { + if (isEmulator) { + // https://github.com/firebase/firebase-admin-node/issues/1149 + return this.skip(); + } return admin.database().getRulesJSON().then((result) => { return expect(result).to.be.not.undefined; }); From 19660d921d20732857bf54393a09e8b5bce15d63 Mon Sep 17 00:00:00 2001 From: Yuchen Shi Date: Thu, 25 Mar 2021 10:56:53 -0700 Subject: [PATCH 017/102] Disable one flaky tests in emulator. (#1205) --- test/integration/auth.spec.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/integration/auth.spec.ts b/test/integration/auth.spec.ts index 29060b4cdd..53174f2ff5 100644 --- a/test/integration/auth.spec.ts +++ b/test/integration/auth.spec.ts @@ -441,8 +441,12 @@ describe('admin.auth', () => { .then((listUsersResult) => { // Confirm expected number of users. expect(listUsersResult.users.length).to.equal(2); - // Confirm next page token present. - expect(typeof listUsersResult.pageToken).to.equal('string'); + // TODO(yuchenshi): Investigate on why this is flaky in emulator. + if (!authEmulatorHost) { + // Confirm next page token present. + expect(typeof listUsersResult.pageToken).to.equal('string'); + } + // Confirm each user's uid and the hashed passwords. expect(listUsersResult.users[0].uid).to.equal(uids[1]); From 2a5b7f6366bda8802f1636dadcfa69c4e25ec8c0 Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Mon, 29 Mar 2021 10:25:09 -0700 Subject: [PATCH 018/102] [chore] Release 9.6.0 (#1209) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aa1c323a75..240abe3cd7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-admin", - "version": "9.5.0", + "version": "9.6.0", "description": "Firebase admin SDK for Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", From 60b4e2933d4bc7c2d7066ea0eae12e793c558549 Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Tue, 30 Mar 2021 13:57:10 -0400 Subject: [PATCH 019/102] (chore): Add JWT Decoder and Signature Verifier (#1204) * (chore): Add JWT Decoder * Add signature verifier and key fetcher abstractions * Add unit tests for utils/jwt --- src/auth/token-verifier.ts | 260 +++++-------- src/utils/jwt.ts | 275 +++++++++++++ test/unit/auth/token-verifier.spec.ts | 339 ++++------------ test/unit/index.spec.ts | 1 + test/unit/utils/jwt.spec.ts | 541 ++++++++++++++++++++++++++ 5 files changed, 1000 insertions(+), 416 deletions(-) create mode 100644 src/utils/jwt.ts create mode 100644 test/unit/utils/jwt.spec.ts diff --git a/src/auth/token-verifier.ts b/src/auth/token-verifier.ts index cbb9991f4c..e100cc25f6 100644 --- a/src/auth/token-verifier.ts +++ b/src/auth/token-verifier.ts @@ -17,8 +17,10 @@ import { AuthClientErrorCode, FirebaseAuthError, ErrorInfo } from '../utils/error'; import * as util from '../utils/index'; import * as validator from '../utils/validator'; -import * as jwt from 'jsonwebtoken'; -import { HttpClient, HttpRequestConfig, HttpError } from '../utils/api-request'; +import { + DecodedToken, decodeJwt, JwtError, JwtErrorCode, + EmulatorSignatureVerifier, PublicKeySignatureVerifier, ALGORITHM_RS256, SignatureVerifier, +} from '../utils/jwt'; import { FirebaseApp } from '../firebase-app'; import { auth } from './index'; @@ -27,8 +29,6 @@ import DecodedIdToken = auth.DecodedIdToken; // Audience to use for Firebase Auth Custom tokens const FIREBASE_AUDIENCE = 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit'; -export const ALGORITHM_RS256 = 'RS256'; - // URL containing the public keys for the Google certs (whose private keys are used to sign Firebase // Auth ID tokens) const CLIENT_CERT_URL = 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com'; @@ -36,6 +36,8 @@ const CLIENT_CERT_URL = 'https://www.googleapis.com/robot/v1/metadata/x509/secur // URL containing the public keys for Firebase session cookies. This will be updated to a different URL soon. const SESSION_COOKIE_CERT_URL = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/publicKeys'; +const EMULATOR_VERIFIER = new EmulatorSignatureVerifier(); + /** User facing token information related to the Firebase ID token. */ export const ID_TOKEN_INFO: FirebaseTokenInfo = { url: 'https://firebase.google.com/docs/auth/admin/verify-id-tokens', @@ -69,15 +71,13 @@ export interface FirebaseTokenInfo { } /** - * Class for verifying general purpose Firebase JWTs. This verifies ID tokens and session cookies. + * Class for verifying ID tokens and session cookies. */ export class FirebaseTokenVerifier { - private publicKeys: {[key: string]: string}; - private publicKeysExpireAt: number; private readonly shortNameArticle: string; + private readonly signatureVerifier: SignatureVerifier; - constructor(private clientCertUrl: string, private algorithm: jwt.Algorithm, - private issuer: string, private tokenInfo: FirebaseTokenInfo, + constructor(clientCertUrl: string, private issuer: string, private tokenInfo: FirebaseTokenInfo, private readonly app: FirebaseApp) { if (!validator.isURL(clientCertUrl)) { @@ -85,11 +85,6 @@ export class FirebaseTokenVerifier { AuthClientErrorCode.INVALID_ARGUMENT, 'The provided public client certificate URL is an invalid URL.', ); - } else if (!validator.isNonEmptyString(algorithm)) { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, - 'The provided JWT algorithm is an empty string.', - ); } else if (!validator.isURL(issuer)) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_ARGUMENT, @@ -128,16 +123,18 @@ export class FirebaseTokenVerifier { } this.shortNameArticle = tokenInfo.shortName.charAt(0).match(/[aeiou]/i) ? 'an' : 'a'; + this.signatureVerifier = + PublicKeySignatureVerifier.withCertificateUrl(clientCertUrl, app.options.httpAgent); + // For backward compatibility, the project ID is validated in the verification call. } /** * Verifies the format and signature of a Firebase Auth JWT token. * - * @param {string} jwtToken The Firebase Auth JWT token to verify. - * @param {boolean=} isEmulator Whether to accept Auth Emulator tokens. - * @return {Promise} A promise fulfilled with the decoded claims of the Firebase Auth ID - * token. + * @param jwtToken The Firebase Auth JWT token to verify. + * @param isEmulator Whether to accept Auth Emulator tokens. + * @return A promise fulfilled with the decoded claims of the Firebase Auth ID token. */ public verifyJWT(jwtToken: string, isEmulator = false): Promise { if (!validator.isString(jwtToken)) { @@ -147,29 +144,68 @@ export class FirebaseTokenVerifier { ); } - return util.findProjectId(this.app) + return this.ensureProjectId() .then((projectId) => { - return this.verifyJWTWithProjectId(jwtToken, projectId, isEmulator); + return this.decodeAndVerify(jwtToken, projectId, isEmulator); + }) + .then((decoded) => { + const decodedIdToken = decoded.payload as DecodedIdToken; + decodedIdToken.uid = decodedIdToken.sub; + return decodedIdToken; }); } - private verifyJWTWithProjectId( - jwtToken: string, - projectId: string | null, - isEmulator: boolean - ): Promise { - if (!validator.isNonEmptyString(projectId)) { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CREDENTIAL, - 'Must initialize app with a cert credential or set your Firebase project ID as the ' + - `GOOGLE_CLOUD_PROJECT environment variable to call ${this.tokenInfo.verifyApiName}.`, - ); - } + private ensureProjectId(): Promise { + return util.findProjectId(this.app) + .then((projectId) => { + if (!validator.isNonEmptyString(projectId)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CREDENTIAL, + 'Must initialize app with a cert credential or set your Firebase project ID as the ' + + `GOOGLE_CLOUD_PROJECT environment variable to call ${this.tokenInfo.verifyApiName}.`, + ); + } + return Promise.resolve(projectId); + }) + } - const fullDecodedToken: any = jwt.decode(jwtToken, { - complete: true, - }); + private decodeAndVerify(token: string, projectId: string, isEmulator: boolean): Promise { + return this.safeDecode(token) + .then((decodedToken) => { + this.verifyContent(decodedToken, projectId, isEmulator); + return this.verifySignature(token, isEmulator) + .then(() => decodedToken); + }); + } + private safeDecode(jwtToken: string): Promise { + return decodeJwt(jwtToken) + .catch((err: JwtError) => { + if (err.code == JwtErrorCode.INVALID_ARGUMENT) { + const verifyJwtTokenDocsMessage = ` See ${this.tokenInfo.url} ` + + `for details on how to retrieve ${this.shortNameArticle} ${this.tokenInfo.shortName}.`; + const errorMessage = `Decoding ${this.tokenInfo.jwtName} failed. Make sure you passed ` + + `the entire string JWT which represents ${this.shortNameArticle} ` + + `${this.tokenInfo.shortName}.` + verifyJwtTokenDocsMessage; + throw new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, + errorMessage); + } + throw new FirebaseAuthError(AuthClientErrorCode.INTERNAL_ERROR, err.message); + }); + } + + /** + * Verifies the content of a Firebase Auth JWT. + * + * @param fullDecodedToken The decoded JWT. + * @param projectId The Firebase Project Id. + * @param isEmulator Whether the token is an Emulator token. + */ + private verifyContent( + fullDecodedToken: DecodedToken, + projectId: string | null, + isEmulator: boolean): void { + const header = fullDecodedToken && fullDecodedToken.header; const payload = fullDecodedToken && fullDecodedToken.payload; @@ -179,10 +215,7 @@ export class FirebaseTokenVerifier { `for details on how to retrieve ${this.shortNameArticle} ${this.tokenInfo.shortName}.`; let errorMessage: string | undefined; - if (!fullDecodedToken) { - errorMessage = `Decoding ${this.tokenInfo.jwtName} failed. Make sure you passed the entire string JWT ` + - `which represents ${this.shortNameArticle} ${this.tokenInfo.shortName}.` + verifyJwtTokenDocsMessage; - } else if (!isEmulator && typeof header.kid === 'undefined') { + if (!isEmulator && typeof header.kid === 'undefined') { const isCustomToken = (payload.aud === FIREBASE_AUDIENCE); const isLegacyCustomToken = (header.alg === 'HS256' && payload.v === 0 && 'd' in payload && 'uid' in payload.d); @@ -197,8 +230,8 @@ export class FirebaseTokenVerifier { } errorMessage += verifyJwtTokenDocsMessage; - } else if (!isEmulator && header.alg !== this.algorithm) { - errorMessage = `${this.tokenInfo.jwtName} has incorrect algorithm. Expected "` + this.algorithm + '" but got ' + + } else if (!isEmulator && header.alg !== ALGORITHM_RS256) { + errorMessage = `${this.tokenInfo.jwtName} has incorrect algorithm. Expected "` + ALGORITHM_RS256 + '" but got ' + '"' + header.alg + '".' + verifyJwtTokenDocsMessage; } else if (payload.aud !== projectId) { errorMessage = `${this.tokenInfo.jwtName} has incorrect "aud" (audience) claim. Expected "` + @@ -217,135 +250,55 @@ export class FirebaseTokenVerifier { verifyJwtTokenDocsMessage; } if (errorMessage) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, errorMessage)); + throw new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, errorMessage); } + } - if (isEmulator) { - // Signature checks skipped for emulator; no need to fetch public keys. - return this.verifyJwtSignatureWithKey(jwtToken, null); - } - - return this.fetchPublicKeys().then((publicKeys) => { - if (!Object.prototype.hasOwnProperty.call(publicKeys, header.kid)) { - return Promise.reject( - new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, - `${this.tokenInfo.jwtName} has "kid" claim which does not correspond to a known public key. ` + - `Most likely the ${this.tokenInfo.shortName} is expired, so get a fresh token from your ` + - 'client app and try again.', - ), - ); - } else { - return this.verifyJwtSignatureWithKey(jwtToken, publicKeys[header.kid]); - } - - }); + private verifySignature(jwtToken: string, isEmulator: boolean): + Promise { + const verifier = isEmulator ? EMULATOR_VERIFIER : this.signatureVerifier; + return verifier.verify(jwtToken) + .catch((error) => { + throw this.mapJwtErrorToAuthError(error); + }); } /** - * Verifies the JWT signature using the provided public key. - * @param {string} jwtToken The JWT token to verify. - * @param {string} publicKey The public key certificate. - * @return {Promise} A promise that resolves with the decoded JWT claims on successful - * verification. + * Maps JwtError to FirebaseAuthError + * + * @param error JwtError to be mapped. + * @returns FirebaseAuthError or Error instance. */ - private verifyJwtSignatureWithKey(jwtToken: string, publicKey: string | null): Promise { + private mapJwtErrorToAuthError(error: JwtError): Error { const verifyJwtTokenDocsMessage = ` See ${this.tokenInfo.url} ` + `for details on how to retrieve ${this.shortNameArticle} ${this.tokenInfo.shortName}.`; - return new Promise((resolve, reject) => { - const verifyOptions: jwt.VerifyOptions = {}; - if (publicKey !== null) { - verifyOptions.algorithms = [this.algorithm]; - } - jwt.verify(jwtToken, publicKey || '', verifyOptions, - (error: jwt.VerifyErrors | null, decodedToken: object | undefined) => { - if (error) { - if (error.name === 'TokenExpiredError') { - const errorMessage = `${this.tokenInfo.jwtName} has expired. Get a fresh ${this.tokenInfo.shortName}` + - ` from your client app and try again (auth/${this.tokenInfo.expiredErrorCode.code}).` + - verifyJwtTokenDocsMessage; - return reject(new FirebaseAuthError(this.tokenInfo.expiredErrorCode, errorMessage)); - } else if (error.name === 'JsonWebTokenError') { - const errorMessage = `${this.tokenInfo.jwtName} has invalid signature.` + verifyJwtTokenDocsMessage; - return reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, errorMessage)); - } - return reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, error.message)); - } else { - const decodedIdToken = (decodedToken as DecodedIdToken); - decodedIdToken.uid = decodedIdToken.sub; - resolve(decodedIdToken); - } - }); - }); - } - - /** - * Fetches the public keys for the Google certs. - * - * @return {Promise} A promise fulfilled with public keys for the Google certs. - */ - private fetchPublicKeys(): Promise<{[key: string]: string}> { - const publicKeysExist = (typeof this.publicKeys !== 'undefined'); - const publicKeysExpiredExists = (typeof this.publicKeysExpireAt !== 'undefined'); - const publicKeysStillValid = (publicKeysExpiredExists && Date.now() < this.publicKeysExpireAt); - if (publicKeysExist && publicKeysStillValid) { - return Promise.resolve(this.publicKeys); + if (error.code === JwtErrorCode.TOKEN_EXPIRED) { + const errorMessage = `${this.tokenInfo.jwtName} has expired. Get a fresh ${this.tokenInfo.shortName}` + + ` from your client app and try again (auth/${this.tokenInfo.expiredErrorCode.code}).` + + verifyJwtTokenDocsMessage; + return new FirebaseAuthError(this.tokenInfo.expiredErrorCode, errorMessage); + } else if (error.code === JwtErrorCode.INVALID_SIGNATURE) { + const errorMessage = `${this.tokenInfo.jwtName} has invalid signature.` + verifyJwtTokenDocsMessage; + return new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, errorMessage); + } else if (error.code === JwtErrorCode.NO_MATCHING_KID) { + const errorMessage = `${this.tokenInfo.jwtName} has "kid" claim which does not ` + + `correspond to a known public key. Most likely the ${this.tokenInfo.shortName} ` + + 'is expired, so get a fresh token from your client app and try again.'; + return new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, errorMessage); } - - const client = new HttpClient(); - const request: HttpRequestConfig = { - method: 'GET', - url: this.clientCertUrl, - httpAgent: this.app.options.httpAgent, - }; - return client.send(request).then((resp) => { - if (!resp.isJson() || resp.data.error) { - // Treat all non-json messages and messages with an 'error' field as - // error responses. - throw new HttpError(resp); - } - if (Object.prototype.hasOwnProperty.call(resp.headers, 'cache-control')) { - const cacheControlHeader: string = resp.headers['cache-control']; - const parts = cacheControlHeader.split(','); - parts.forEach((part) => { - const subParts = part.trim().split('='); - if (subParts[0] === 'max-age') { - const maxAge: number = +subParts[1]; - this.publicKeysExpireAt = Date.now() + (maxAge * 1000); - } - }); - } - this.publicKeys = resp.data; - return resp.data; - }).catch((err) => { - if (err instanceof HttpError) { - let errorMessage = 'Error fetching public keys for Google certs: '; - const resp = err.response; - if (resp.isJson() && resp.data.error) { - errorMessage += `${resp.data.error}`; - if (resp.data.error_description) { - errorMessage += ' (' + resp.data.error_description + ')'; - } - } else { - errorMessage += `${resp.text}`; - } - throw new FirebaseAuthError(AuthClientErrorCode.INTERNAL_ERROR, errorMessage); - } - throw err; - }); + return new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, error.message); } } /** * Creates a new FirebaseTokenVerifier to verify Firebase ID tokens. * - * @param {FirebaseApp} app Firebase app instance. - * @return {FirebaseTokenVerifier} + * @param app Firebase app instance. + * @return FirebaseTokenVerifier */ export function createIdTokenVerifier(app: FirebaseApp): FirebaseTokenVerifier { return new FirebaseTokenVerifier( CLIENT_CERT_URL, - ALGORITHM_RS256, 'https://securetoken.google.com/', ID_TOKEN_INFO, app @@ -355,13 +308,12 @@ export function createIdTokenVerifier(app: FirebaseApp): FirebaseTokenVerifier { /** * Creates a new FirebaseTokenVerifier to verify Firebase session cookies. * - * @param {FirebaseApp} app Firebase app instance. - * @return {FirebaseTokenVerifier} + * @param app Firebase app instance. + * @return FirebaseTokenVerifier */ export function createSessionCookieVerifier(app: FirebaseApp): FirebaseTokenVerifier { return new FirebaseTokenVerifier( SESSION_COOKIE_CERT_URL, - ALGORITHM_RS256, 'https://session.firebase.google.com/', SESSION_COOKIE_INFO, app diff --git a/src/utils/jwt.ts b/src/utils/jwt.ts new file mode 100644 index 0000000000..d048567061 --- /dev/null +++ b/src/utils/jwt.ts @@ -0,0 +1,275 @@ +/*! + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as validator from './validator'; +import * as jwt from 'jsonwebtoken'; +import { HttpClient, HttpRequestConfig, HttpError } from '../utils/api-request'; +import { Agent } from 'http'; + +export const ALGORITHM_RS256: jwt.Algorithm = 'RS256' as const; + +// `jsonwebtoken` converts errors from the `getKey` callback to its own `JsonWebTokenError` type +// and prefixes the error message with the following. Use the prefix to identify errors thrown +// from the key provider callback. +// https://github.com/auth0/node-jsonwebtoken/blob/d71e383862fc735991fd2e759181480f066bf138/verify.js#L96 +const JWT_CALLBACK_ERROR_PREFIX = 'error in secret or public key callback: '; + +const NO_MATCHING_KID_ERROR_MESSAGE = 'no-matching-kid-error'; + +export type Dictionary = { [key: string]: any } + +export type DecodedToken = { + header: Dictionary; + payload: Dictionary; +} + +export interface SignatureVerifier { + verify(token: string): Promise; +} + +interface KeyFetcher { + fetchPublicKeys(): Promise<{ [key: string]: string }>; +} + +/** + * Class to fetch public keys from a client certificates URL. + */ +export class UrlKeyFetcher implements KeyFetcher { + private publicKeys: { [key: string]: string }; + private publicKeysExpireAt = 0; + + constructor(private clientCertUrl: string, private readonly httpAgent?: Agent) { + if (!validator.isURL(clientCertUrl)) { + throw new Error( + 'The provided public client certificate URL is not a valid URL.', + ); + } + } + + /** + * Fetches the public keys for the Google certs. + * + * @return A promise fulfilled with public keys for the Google certs. + */ + public fetchPublicKeys(): Promise<{ [key: string]: string }> { + if (this.shouldRefresh()) { + return this.refresh(); + } + return Promise.resolve(this.publicKeys); + } + + /** + * Checks if the cached public keys need to be refreshed. + * + * @returns Whether the keys should be fetched from the client certs url or not. + */ + private shouldRefresh(): boolean { + return !this.publicKeys || this.publicKeysExpireAt <= Date.now(); + } + + private refresh(): Promise<{ [key: string]: string }> { + const client = new HttpClient(); + const request: HttpRequestConfig = { + method: 'GET', + url: this.clientCertUrl, + httpAgent: this.httpAgent, + }; + return client.send(request).then((resp) => { + if (!resp.isJson() || resp.data.error) { + // Treat all non-json messages and messages with an 'error' field as + // error responses. + throw new HttpError(resp); + } + // reset expire at from previous set of keys. + this.publicKeysExpireAt = 0; + if (Object.prototype.hasOwnProperty.call(resp.headers, 'cache-control')) { + const cacheControlHeader: string = resp.headers['cache-control']; + const parts = cacheControlHeader.split(','); + parts.forEach((part) => { + const subParts = part.trim().split('='); + if (subParts[0] === 'max-age') { + const maxAge: number = +subParts[1]; + this.publicKeysExpireAt = Date.now() + (maxAge * 1000); + } + }); + } + this.publicKeys = resp.data; + return resp.data; + }).catch((err) => { + if (err instanceof HttpError) { + let errorMessage = 'Error fetching public keys for Google certs: '; + const resp = err.response; + if (resp.isJson() && resp.data.error) { + errorMessage += `${resp.data.error}`; + if (resp.data.error_description) { + errorMessage += ' (' + resp.data.error_description + ')'; + } + } else { + errorMessage += `${resp.text}`; + } + throw new Error(errorMessage); + } + throw err; + }); + } +} + +/** + * Class for verifing JWT signature with a public key. + */ +export class PublicKeySignatureVerifier implements SignatureVerifier { + constructor(private keyFetcher: KeyFetcher) { + if (!validator.isNonNullObject(keyFetcher)) { + throw new Error('The provided key fetcher is not an object or null.'); + } + } + + public static withCertificateUrl(clientCertUrl: string, httpAgent?: Agent): PublicKeySignatureVerifier { + return new PublicKeySignatureVerifier(new UrlKeyFetcher(clientCertUrl, httpAgent)); + } + + public verify(token: string): Promise { + if (!validator.isString(token)) { + return Promise.reject(new JwtError(JwtErrorCode.INVALID_ARGUMENT, + 'The provided token must be a string.')); + } + + return verifyJwtSignature(token, getKeyCallback(this.keyFetcher), { algorithms: [ALGORITHM_RS256] }); + } +} + +/** + * Class for verifing unsigned (emulator) JWTs. + */ +export class EmulatorSignatureVerifier implements SignatureVerifier { + public verify(token: string): Promise { + // Signature checks skipped for emulator; no need to fetch public keys. + return verifyJwtSignature(token, ''); + } +} + +/** + * Provides a callback to fetch public keys. + * + * @param fetcher KeyFetcher to fetch the keys from. + * @returns A callback function that can be used to get keys in `jsonwebtoken`. + */ +function getKeyCallback(fetcher: KeyFetcher): jwt.GetPublicKeyOrSecret { + return (header: jwt.JwtHeader, callback: jwt.SigningKeyCallback) => { + const kid = header.kid || ''; + fetcher.fetchPublicKeys().then((publicKeys) => { + if (!Object.prototype.hasOwnProperty.call(publicKeys, kid)) { + callback(new Error(NO_MATCHING_KID_ERROR_MESSAGE)); + } else { + callback(null, publicKeys[kid]); + } + }) + .catch(error => { + callback(error); + }); + } +} + +/** + * Verifies the signature of a JWT using the provided secret or a function to fetch + * the secret or public key. + * + * @param token The JWT to be verfied. + * @param secretOrPublicKey The secret or a function to fetch the secret or public key. + * @param options JWT verification options. + * @returns A Promise resolving for a token with a valid signature. + */ +export function verifyJwtSignature(token: string, secretOrPublicKey: jwt.Secret | jwt.GetPublicKeyOrSecret, + options?: jwt.VerifyOptions): Promise { + if (!validator.isString(token)) { + return Promise.reject(new JwtError(JwtErrorCode.INVALID_ARGUMENT, + 'The provided token must be a string.')); + } + + return new Promise((resolve, reject) => { + jwt.verify(token, secretOrPublicKey, options, + (error: jwt.VerifyErrors | null) => { + if (!error) { + return resolve(); + } + if (error.name === 'TokenExpiredError') { + return reject(new JwtError(JwtErrorCode.TOKEN_EXPIRED, + 'The provided token has expired. Get a fresh token from your ' + + 'client app and try again.')); + } else if (error.name === 'JsonWebTokenError') { + if (error.message && error.message.includes(JWT_CALLBACK_ERROR_PREFIX)) { + const message = error.message.split(JWT_CALLBACK_ERROR_PREFIX).pop() || 'Error fetching public keys.'; + const code = (message === NO_MATCHING_KID_ERROR_MESSAGE) ? JwtErrorCode.NO_MATCHING_KID : + JwtErrorCode.KEY_FETCH_ERROR; + return reject(new JwtError(code, message)); + } + } + return reject(new JwtError(JwtErrorCode.INVALID_SIGNATURE, error.message)); + }); + }); +} + +/** + * Decodes general purpose Firebase JWTs. + * + * @param jwtToken JWT token to be decoded. + * @returns Decoded token containing the header and payload. + */ +export function decodeJwt(jwtToken: string): Promise { + if (!validator.isString(jwtToken)) { + return Promise.reject(new JwtError(JwtErrorCode.INVALID_ARGUMENT, + 'The provided token must be a string.')); + } + + const fullDecodedToken: any = jwt.decode(jwtToken, { + complete: true, + }); + + if (!fullDecodedToken) { + return Promise.reject(new JwtError(JwtErrorCode.INVALID_ARGUMENT, + 'Decoding token failed.')); + } + + const header = fullDecodedToken?.header; + const payload = fullDecodedToken?.payload; + return Promise.resolve({ header, payload }); +} + +/** + * Jwt error code structure. + * + * @param code The error code. + * @param message The error message. + * @constructor + */ +export class JwtError extends Error { + constructor(readonly code: JwtErrorCode, readonly message: string) { + super(message); + (this as any).__proto__ = JwtError.prototype; + } +} + +/** + * JWT error codes. + */ +export enum JwtErrorCode { + INVALID_ARGUMENT = 'invalid-argument', + INVALID_CREDENTIAL = 'invalid-credential', + TOKEN_EXPIRED = 'token-expired', + INVALID_SIGNATURE = 'invalid-token', + NO_MATCHING_KID = 'no-matching-kid-error', + KEY_FETCH_ERROR = 'key-fetch-error', +} diff --git a/test/unit/auth/token-verifier.spec.ts b/test/unit/auth/token-verifier.spec.ts index d863a0e849..1f7e3546f8 100644 --- a/test/unit/auth/token-verifier.spec.ts +++ b/test/unit/auth/token-verifier.spec.ts @@ -16,15 +16,14 @@ 'use strict'; -// Use untyped import syntax for Node built-ins -import https = require('https'); - import * as _ from 'lodash'; import * as chai from 'chai'; import * as nock from 'nock'; import * as sinon from 'sinon'; import * as sinonChai from 'sinon-chai'; import * as chaiAsPromised from 'chai-as-promised'; +import { Agent } from 'http'; + import LegacyFirebaseTokenGenerator = require('firebase-token-generator'); import * as mocks from '../../resources/mocks'; @@ -34,7 +33,7 @@ import * as verifier from '../../../src/auth/token-verifier'; import { ServiceAccountCredential } from '../../../src/credential/credential-internal'; import { AuthClientErrorCode } from '../../../src/utils/error'; import { FirebaseApp } from '../../../src/firebase-app'; -import { Algorithm } from 'jsonwebtoken'; +import { JwtError, JwtErrorCode, PublicKeySignatureVerifier } from '../../../src/utils/jwt'; chai.should(); chai.use(sinonChai); @@ -43,76 +42,12 @@ chai.use(chaiAsPromised); const expect = chai.expect; const ONE_HOUR_IN_SECONDS = 60 * 60; -const idTokenPublicCertPath = '/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com'; - -/** - * Returns a mocked out success response from the URL containing the public keys for the Google certs. - * - * @param {string=} path URL path to which the mock request should be made. If not specified, defaults - * to the URL path of ID token public key certificates. - * @return {Object} A nock response object. - */ -function mockFetchPublicKeys(path: string = idTokenPublicCertPath): nock.Scope { - const mockedResponse: {[key: string]: string} = {}; - mockedResponse[mocks.certificateObject.private_key_id] = mocks.keyPairs[0].public; - return nock('https://www.googleapis.com') - .get(path) - .reply(200, mockedResponse, { - 'cache-control': 'public, max-age=1, must-revalidate, no-transform', - }); -} - -/** - * Returns a mocked out success response from the URL containing the public keys for the Google certs - * which contains a public key which won't match the mocked token. - * - * @return {Object} A nock response object. - */ -function mockFetchWrongPublicKeys(): nock.Scope { - const mockedResponse: {[key: string]: string} = {}; - mockedResponse[mocks.certificateObject.private_key_id] = mocks.keyPairs[1].public; - return nock('https://www.googleapis.com') - .get('/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com') - .reply(200, mockedResponse, { - 'cache-control': 'public, max-age=1, must-revalidate, no-transform', - }); -} - -/** - * Returns a mocked out error response from the URL containing the public keys for the Google certs. - * The status code is 200 but the response itself will contain an 'error' key. - * - * @return {Object} A nock response object. - */ -function mockFetchPublicKeysWithErrorResponse(): nock.Scope { - return nock('https://www.googleapis.com') - .get('/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com') - .reply(200, { - error: 'message', - error_description: 'description', // eslint-disable-line @typescript-eslint/camelcase - }); -} - -/** - * Returns a mocked out failed response from the URL containing the public keys for the Google certs. - * The status code is non-200 and the response itself will fail. - * - * @return {Object} A nock response object. - */ -function mockFailedFetchPublicKeys(): nock.Scope { - return nock('https://www.googleapis.com') - .get('/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com') - .replyWithError('message'); -} function createTokenVerifier( - app: FirebaseApp, - options: { algorithm?: Algorithm } = {} + app: FirebaseApp ): verifier.FirebaseTokenVerifier { - const algorithm = options.algorithm || 'RS256'; return new verifier.FirebaseTokenVerifier( 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com', - algorithm, 'https://securetoken.google.com/', verifier.ID_TOKEN_INFO, app @@ -125,14 +60,12 @@ describe('FirebaseTokenVerifier', () => { let tokenVerifier: verifier.FirebaseTokenVerifier; let tokenGenerator: FirebaseTokenGenerator; let clock: sinon.SinonFakeTimers | undefined; - let httpsSpy: sinon.SinonSpy; beforeEach(() => { // Needed to generate custom token for testing. app = mocks.app(); const cert = new ServiceAccountCredential(mocks.certificateObject); tokenGenerator = new FirebaseTokenGenerator(new ServiceAccountSigner(cert)); tokenVerifier = createTokenVerifier(app); - httpsSpy = sinon.spy(https, 'request'); }); afterEach(() => { @@ -140,7 +73,6 @@ describe('FirebaseTokenVerifier', () => { clock.restore(); clock = undefined; } - httpsSpy.restore(); }); after(() => { @@ -152,7 +84,6 @@ describe('FirebaseTokenVerifier', () => { expect(() => { tokenVerifier = new verifier.FirebaseTokenVerifier( 'https://www.example.com/publicKeys', - 'RS256', 'https://www.example.com/issuer/', { url: 'https://docs.example.com/verify-tokens', @@ -172,7 +103,6 @@ describe('FirebaseTokenVerifier', () => { expect(() => { new verifier.FirebaseTokenVerifier( invalidCertUrl as any, - 'RS256', 'https://www.example.com/issuer/', verifier.ID_TOKEN_INFO, app, @@ -181,27 +111,12 @@ describe('FirebaseTokenVerifier', () => { }); }); - const invalidAlgorithms = [null, NaN, 0, 1, true, false, [], {}, { a: 1 }, _.noop, '']; - invalidAlgorithms.forEach((invalidAlgorithm) => { - it('should throw given an invalid algorithm: ' + JSON.stringify(invalidAlgorithm), () => { - expect(() => { - new verifier.FirebaseTokenVerifier( - 'https://www.example.com/publicKeys', - invalidAlgorithm as any, - 'https://www.example.com/issuer/', - verifier.ID_TOKEN_INFO, - app); - }).to.throw('The provided JWT algorithm is an empty string.'); - }); - }); - const invalidIssuers = [null, NaN, 0, 1, true, false, [], {}, { a: 1 }, _.noop, 'file://invalid']; invalidIssuers.forEach((invalidIssuer) => { it('should throw given a non-URL issuer: ' + JSON.stringify(invalidIssuer), () => { expect(() => { new verifier.FirebaseTokenVerifier( 'https://www.example.com/publicKeys', - 'RS256', invalidIssuer as any, verifier.ID_TOKEN_INFO, app, @@ -216,7 +131,6 @@ describe('FirebaseTokenVerifier', () => { expect(() => { new verifier.FirebaseTokenVerifier( 'https://www.example.com/publicKeys', - 'RS256', 'https://www.example.com/issuer/', { url: 'https://docs.example.com/verify-tokens', @@ -237,7 +151,6 @@ describe('FirebaseTokenVerifier', () => { expect(() => { new verifier.FirebaseTokenVerifier( 'https://www.example.com/publicKeys', - 'RS256', 'https://www.example.com/issuer/', { url: 'https://docs.example.com/verify-tokens', @@ -258,7 +171,6 @@ describe('FirebaseTokenVerifier', () => { expect(() => { new verifier.FirebaseTokenVerifier( 'https://www.example.com/publicKeys', - 'RS256', 'https://www.example.com/issuer/', { url: 'https://docs.example.com/verify-tokens', @@ -279,7 +191,6 @@ describe('FirebaseTokenVerifier', () => { expect(() => { new verifier.FirebaseTokenVerifier( 'https://www.example.com/publicKeys', - 'RS256', 'https://www.example.com/issuer/', { url: 'https://docs.example.com/verify-tokens', @@ -297,10 +208,14 @@ describe('FirebaseTokenVerifier', () => { describe('verifyJWT()', () => { let mockedRequests: nock.Scope[] = []; + let stubs: sinon.SinonStub[] = []; afterEach(() => { _.forEach(mockedRequests, (mockedRequest) => mockedRequest.done()); mockedRequests = []; + + _.forEach(stubs, (stub) => stub.restore()); + stubs = []; }); it('should throw given no Firebase JWT token', () => { @@ -331,7 +246,6 @@ describe('FirebaseTokenVerifier', () => { it('should throw if the token verifier was initialized with no "project_id"', () => { const tokenVerifierWithNoProjectId = new verifier.FirebaseTokenVerifier( 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com', - 'RS256', 'https://securetoken.google.com/', verifier.ID_TOKEN_INFO, mocks.mockCredentialApp(), @@ -351,21 +265,6 @@ describe('FirebaseTokenVerifier', () => { .should.eventually.be.rejectedWith('Firebase ID token has no "kid" claim'); }); - it('should be rejected given a Firebase JWT token with a kid which does not match any of the ' + - 'actual public keys', () => { - mockedRequests.push(mockFetchPublicKeys()); - - const mockIdToken = mocks.generateIdToken({ - header: { - kid: 'wrongkid', - }, - }); - - return tokenVerifier.verifyJWT(mockIdToken) - .should.eventually.be.rejectedWith('Firebase ID token has "kid" claim which does not ' + - 'correspond to a known public key'); - }); - it('should be rejected given a Firebase JWT token with an incorrect algorithm', () => { const mockIdToken = mocks.generateIdToken({ algorithm: 'HS256', @@ -392,8 +291,26 @@ describe('FirebaseTokenVerifier', () => { .should.eventually.be.rejectedWith('Firebase ID token has incorrect "iss" (issuer) claim'); }); + it('should be rejected when the verifier throws no maching kid error', () => { + const verifierStub = sinon.stub(PublicKeySignatureVerifier.prototype, 'verify') + .rejects(new JwtError(JwtErrorCode.NO_MATCHING_KID, 'No matching key ID.')); + stubs.push(verifierStub); + + const mockIdToken = mocks.generateIdToken({ + header: { + kid: 'wrongkid', + }, + }); + + return tokenVerifier.verifyJWT(mockIdToken) + .should.eventually.be.rejectedWith('Firebase ID token has "kid" claim which does not ' + + 'correspond to a known public key'); + }); + it('should be rejected given a Firebase JWT token with a subject with greater than 128 characters', () => { - mockedRequests.push(mockFetchPublicKeys()); + const verifierStub = sinon.stub(PublicKeySignatureVerifier.prototype, 'verify') + .resolves(); + stubs.push(verifierStub); // uid of length 128 should be fulfilled let uid = Array(129).join('a'); @@ -414,62 +331,59 @@ describe('FirebaseTokenVerifier', () => { }); }); - it('should be rejected given an expired Firebase JWT token', () => { - mockedRequests.push(mockFetchPublicKeys()); - - clock = sinon.useFakeTimers(1000); + it('should be rejected when the verifier throws for expired Firebase JWT token', () => { + const verifierStub = sinon.stub(PublicKeySignatureVerifier.prototype, 'verify') + .rejects(new JwtError(JwtErrorCode.TOKEN_EXPIRED, 'Expired token.')); + stubs.push(verifierStub); const mockIdToken = mocks.generateIdToken(); - clock.tick((ONE_HOUR_IN_SECONDS * 1000) - 1); - - // Token should still be valid - return tokenVerifier.verifyJWT(mockIdToken).then(() => { - clock!.tick(1); - - // Token should now be invalid - return tokenVerifier.verifyJWT(mockIdToken) - .should.eventually.be.rejectedWith('Firebase ID token has expired. Get a fresh ID token from your client ' + - 'app and try again (auth/id-token-expired)') - .and.have.property('code', 'auth/id-token-expired'); - }); + return tokenVerifier.verifyJWT(mockIdToken) + .should.eventually.be.rejectedWith('Firebase ID token has expired. Get a fresh ID token from your client ' + + 'app and try again (auth/id-token-expired)') + .and.have.property('code', 'auth/id-token-expired'); }); - it('should be rejected given an expired Firebase session cookie', () => { + it('should be rejected when the verifier throws for expired Firebase session cookie', () => { + const verifierStub = sinon.stub(PublicKeySignatureVerifier.prototype, 'verify') + .rejects(new JwtError(JwtErrorCode.TOKEN_EXPIRED, 'Expired token.')); + stubs.push(verifierStub); + const tokenVerifierSessionCookie = new verifier.FirebaseTokenVerifier( 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/publicKeys', - 'RS256', 'https://session.firebase.google.com/', verifier.SESSION_COOKIE_INFO, app, ); - mockedRequests.push(mockFetchPublicKeys('/identitytoolkit/v3/relyingparty/publicKeys')); - - clock = sinon.useFakeTimers(1000); const mockSessionCookie = mocks.generateSessionCookie(); - clock.tick((ONE_HOUR_IN_SECONDS * 1000) - 1); + return tokenVerifierSessionCookie.verifyJWT(mockSessionCookie) + .should.eventually.be.rejectedWith('Firebase session cookie has expired. Get a fresh session cookie from ' + + 'your client app and try again (auth/session-cookie-expired).') + .and.have.property('code', 'auth/session-cookie-expired'); + }); - // Cookie should still be valid - return tokenVerifierSessionCookie.verifyJWT(mockSessionCookie).then(() => { - clock!.tick(1); + it('should be rejected when the verifier throws invalid signature for a Firebase JWT token.', () => { + const verifierStub = sinon.stub(PublicKeySignatureVerifier.prototype, 'verify') + .rejects(new JwtError(JwtErrorCode.INVALID_SIGNATURE, 'invalid signature.')); + stubs.push(verifierStub); - // Cookie should now be invalid - return tokenVerifierSessionCookie.verifyJWT(mockSessionCookie) - .should.eventually.be.rejectedWith('Firebase session cookie has expired. Get a fresh session cookie from ' + - 'your client app and try again (auth/session-cookie-expired).') - .and.have.property('code', 'auth/session-cookie-expired'); - }); + const mockIdToken = mocks.generateIdToken(); + + return tokenVerifier.verifyJWT(mockIdToken) + .should.eventually.be.rejectedWith('Firebase ID token has invalid signature'); }); - it('should be rejected given a Firebase JWT token which was not signed with the kid it specifies', () => { - mockedRequests.push(mockFetchWrongPublicKeys()); + it('should be rejected when the verifier throws key fetch error.', () => { + const verifierStub = sinon.stub(PublicKeySignatureVerifier.prototype, 'verify') + .rejects(new JwtError(JwtErrorCode.KEY_FETCH_ERROR, 'Error fetching public keys.')); + stubs.push(verifierStub); const mockIdToken = mocks.generateIdToken(); return tokenVerifier.verifyJWT(mockIdToken) - .should.eventually.be.rejectedWith('Firebase ID token has invalid signature'); + .should.eventually.be.rejectedWith('Error fetching public keys.'); }); it('should be rejected given a custom token with error using article "an" before JWT short name', () => { @@ -483,7 +397,6 @@ describe('FirebaseTokenVerifier', () => { it('should be rejected given a custom token with error using article "a" before JWT short name', () => { const tokenVerifierSessionCookie = new verifier.FirebaseTokenVerifier( 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/publicKeys', - 'RS256', 'https://session.firebase.google.com/', verifier.SESSION_COOKIE_INFO, app, @@ -509,7 +422,6 @@ describe('FirebaseTokenVerifier', () => { it('should be rejected given a legacy custom token with error using article "a" before JWT short name', () => { const tokenVerifierSessionCookie = new verifier.FirebaseTokenVerifier( 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/publicKeys', - 'RS256', 'https://session.firebase.google.com/', verifier.SESSION_COOKIE_INFO, app, @@ -524,8 +436,26 @@ describe('FirebaseTokenVerifier', () => { 'verifySessionCookie() expects a session cookie, but was given a legacy custom token'); }); + it('AppOptions.httpAgent should be passed to the verifier', () => { + const mockAppWithAgent = mocks.appWithOptions({ + httpAgent: new Agent() + }); + const agentForApp = mockAppWithAgent.options.httpAgent; + const verifierSpy = sinon.spy(PublicKeySignatureVerifier, 'withCertificateUrl'); + + expect(verifierSpy.args).to.be.empty; + + createTokenVerifier(mockAppWithAgent); + + expect(verifierSpy.args[0][1]).to.equal(agentForApp); + + verifierSpy.restore(); + }); + it('should be fulfilled with decoded claims given a valid Firebase JWT token', () => { - mockedRequests.push(mockFetchPublicKeys()); + const verifierStub = sinon.stub(PublicKeySignatureVerifier.prototype, 'verify') + .resolves(); + stubs.push(verifierStub); clock = sinon.useFakeTimers(1000); @@ -567,16 +497,6 @@ describe('FirebaseTokenVerifier', () => { }); }); - it('should not decode a signed token when the algorithm is set to none (emulator)', async () => { - clock = sinon.useFakeTimers(1000); - - const emulatorVerifier = createTokenVerifier(app, { algorithm: 'none' }); - const mockIdToken = mocks.generateIdToken(); - - await emulatorVerifier.verifyJWT(mockIdToken) - .should.eventually.be.rejectedWith('Firebase ID token has incorrect algorithm. Expected "none"'); - }); - it('should not decode an unsigned token when the algorithm is not overridden (emulator)', async () => { clock = sinon.useFakeTimers(1000); @@ -593,110 +513,5 @@ describe('FirebaseTokenVerifier', () => { await tokenVerifier.verifyJWT(idTokenNoHeader) .should.eventually.be.rejectedWith('Firebase ID token has no "kid" claim.'); }); - - it('should use the given HTTP Agent', () => { - const agent = new https.Agent(); - const appWithAgent = mocks.appWithOptions({ - credential: mocks.credential, - httpAgent: agent, - }); - tokenVerifier = new verifier.FirebaseTokenVerifier( - 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com', - 'RS256', - 'https://securetoken.google.com/', - verifier.ID_TOKEN_INFO, - appWithAgent, - ); - mockedRequests.push(mockFetchPublicKeys()); - - clock = sinon.useFakeTimers(1000); - - const mockIdToken = mocks.generateIdToken(); - - return tokenVerifier.verifyJWT(mockIdToken) - .then(() => { - expect(https.request).to.have.been.calledOnce; - expect(httpsSpy.args[0][0].agent).to.equal(agent); - }); - }); - - it('should not fetch the Google cert public keys until the first time verifyJWT() is called', () => { - mockedRequests.push(mockFetchPublicKeys()); - - const testTokenVerifier = new verifier.FirebaseTokenVerifier( - 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com', - 'RS256', - 'https://securetoken.google.com/', - verifier.ID_TOKEN_INFO, - app, - ); - expect(https.request).not.to.have.been.called; - - const mockIdToken = mocks.generateIdToken(); - - return testTokenVerifier.verifyJWT(mockIdToken) - .then(() => expect(https.request).to.have.been.calledOnce); - }); - - it('should not re-fetch the Google cert public keys every time verifyJWT() is called', () => { - mockedRequests.push(mockFetchPublicKeys()); - - const mockIdToken = mocks.generateIdToken(); - - return tokenVerifier.verifyJWT(mockIdToken).then(() => { - expect(https.request).to.have.been.calledOnce; - return tokenVerifier.verifyJWT(mockIdToken); - }).then(() => expect(https.request).to.have.been.calledOnce); - }); - - it('should refresh the Google cert public keys after the "max-age" on the request expires', () => { - mockedRequests.push(mockFetchPublicKeys()); - mockedRequests.push(mockFetchPublicKeys()); - mockedRequests.push(mockFetchPublicKeys()); - - clock = sinon.useFakeTimers(1000); - - const mockIdToken = mocks.generateIdToken(); - - return tokenVerifier.verifyJWT(mockIdToken).then(() => { - expect(https.request).to.have.been.calledOnce; - clock!.tick(999); - return tokenVerifier.verifyJWT(mockIdToken); - }).then(() => { - expect(https.request).to.have.been.calledOnce; - clock!.tick(1); - return tokenVerifier.verifyJWT(mockIdToken); - }).then(() => { - // One second has passed - expect(https.request).to.have.been.calledTwice; - clock!.tick(999); - return tokenVerifier.verifyJWT(mockIdToken); - }).then(() => { - expect(https.request).to.have.been.calledTwice; - clock!.tick(1); - return tokenVerifier.verifyJWT(mockIdToken); - }).then(() => { - // Two seconds have passed - expect(https.request).to.have.been.calledThrice; - }); - }); - - it('should be rejected if fetching the Google public keys fails', () => { - mockedRequests.push(mockFailedFetchPublicKeys()); - - const mockIdToken = mocks.generateIdToken(); - - return tokenVerifier.verifyJWT(mockIdToken) - .should.eventually.be.rejectedWith('message'); - }); - - it('should be rejected if fetching the Google public keys returns a response with an error message', () => { - mockedRequests.push(mockFetchPublicKeysWithErrorResponse()); - - const mockIdToken = mocks.generateIdToken(); - - return tokenVerifier.verifyJWT(mockIdToken) - .should.eventually.be.rejectedWith('Error fetching public keys for Google certs: message (description)'); - }); }); }); diff --git a/test/unit/index.spec.ts b/test/unit/index.spec.ts index efbe059e96..e8c5a6d17d 100644 --- a/test/unit/index.spec.ts +++ b/test/unit/index.spec.ts @@ -25,6 +25,7 @@ import './utils/index.spec'; import './utils/error.spec'; import './utils/validator.spec'; import './utils/api-request.spec'; +import './utils/jwt.spec'; // Auth import './auth/auth.spec'; diff --git a/test/unit/utils/jwt.spec.ts b/test/unit/utils/jwt.spec.ts new file mode 100644 index 0000000000..525608feef --- /dev/null +++ b/test/unit/utils/jwt.spec.ts @@ -0,0 +1,541 @@ +/*! + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +// Use untyped import syntax for Node built-ins +import https = require('https'); + +import * as _ from 'lodash'; +import * as chai from 'chai'; +import * as nock from 'nock'; +import * as sinon from 'sinon'; +//import * as sinonChai from 'sinon-chai'; +//import * as chaiAsPromised from 'chai-as-promised'; + +import * as mocks from '../../resources/mocks'; +import * as jwtUtil from '../../../src/utils/jwt'; + +const expect = chai.expect; + +const ONE_HOUR_IN_SECONDS = 60 * 60; +const publicCertPath = '/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com'; + +/** + * Returns a mocked out success response from the URL containing the public keys for the Google certs. + * + * @param {string=} path URL path to which the mock request should be made. If not specified, defaults + * to the URL path of ID token public key certificates. + * @return {Object} A nock response object. + */ +function mockFetchPublicKeys(path: string = publicCertPath): nock.Scope { + const mockedResponse: { [key: string]: string } = {}; + mockedResponse[mocks.certificateObject.private_key_id] = mocks.keyPairs[0].public; + return nock('https://www.googleapis.com') + .get(path) + .reply(200, mockedResponse, { + 'cache-control': 'public, max-age=1, must-revalidate, no-transform', + }); +} + +/** + * Returns a mocked out error response from the URL containing the public keys for the Google certs. + * The status code is 200 but the response itself will contain an 'error' key. + * + * @return {Object} A nock response object. + */ + +function mockFetchPublicKeysWithErrorResponse(): nock.Scope { + return nock('https://www.googleapis.com') + .get('/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com') + .reply(200, { + error: 'message', + error_description: 'description', // eslint-disable-line @typescript-eslint/camelcase + }); +} + +/** + * Returns a mocked out failed response from the URL containing the public keys for the Google certs. + * The status code is non-200 and the response itself will fail. + * + * @return {Object} A nock response object. + */ + +function mockFailedFetchPublicKeys(): nock.Scope { + return nock('https://www.googleapis.com') + .get('/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com') + .replyWithError('message'); +} + + +const TOKEN_PAYLOAD = { + one: 'uno', + two: 'dos', + iat: 1, + exp: ONE_HOUR_IN_SECONDS + 1, + aud: mocks.projectId, + iss: 'https://securetoken.google.com/' + mocks.projectId, + sub: mocks.uid, +}; + +const DECODED_SIGNED_TOKEN: jwtUtil.DecodedToken = { + header: { + alg: 'RS256', + kid: 'aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd', + typ: 'JWT', + }, + payload: TOKEN_PAYLOAD +}; + +const DECODED_UNSIGNED_TOKEN: jwtUtil.DecodedToken = { + header: { + alg: 'none', + typ: 'JWT', + }, + payload: TOKEN_PAYLOAD +}; + +const VALID_PUBLIC_KEYS_RESPONSE: { [key: string]: string } = {}; +VALID_PUBLIC_KEYS_RESPONSE[mocks.certificateObject.private_key_id] = mocks.keyPairs[0].public; + +describe('decodeJwt', () => { + let clock: sinon.SinonFakeTimers | undefined; + + afterEach(() => { + if (clock) { + clock.restore(); + clock = undefined; + } + }); + + it('should reject given no token', () => { + return (jwtUtil.decodeJwt as any)() + .should.eventually.be.rejectedWith('The provided token must be a string.'); + }); + + const invalidIdTokens = [null, NaN, 0, 1, true, false, [], {}, { a: 1 }, _.noop]; + invalidIdTokens.forEach((invalidIdToken) => { + it('should reject given a non-string token: ' + JSON.stringify(invalidIdToken), () => { + return jwtUtil.decodeJwt(invalidIdToken as any) + .should.eventually.be.rejectedWith('The provided token must be a string.'); + }); + }); + + it('should reject given an empty string token', () => { + return jwtUtil.decodeJwt('') + .should.eventually.be.rejectedWith('Decoding token failed.'); + }); + + it('should reject given an invalid token', () => { + return jwtUtil.decodeJwt('invalid-token') + .should.eventually.be.rejectedWith('Decoding token failed.'); + }); + + it('should be fulfilled with decoded claims given a valid signed token', () => { + clock = sinon.useFakeTimers(1000); + + const mockIdToken = mocks.generateIdToken(); + + return jwtUtil.decodeJwt(mockIdToken) + .should.eventually.be.fulfilled.and.deep.equal(DECODED_SIGNED_TOKEN); + }); + + it('should be fulfilled with decoded claims given a valid unsigned token', () => { + clock = sinon.useFakeTimers(1000); + + const mockIdToken = mocks.generateIdToken({ + algorithm: 'none', + header: {} + }); + + return jwtUtil.decodeJwt(mockIdToken) + .should.eventually.be.fulfilled.and.deep.equal(DECODED_UNSIGNED_TOKEN); + }); +}); + + +describe('verifyJwtSignature', () => { + let clock: sinon.SinonFakeTimers | undefined; + + afterEach(() => { + if (clock) { + clock.restore(); + clock = undefined; + } + }); + + it('should throw given no token', () => { + return (jwtUtil.verifyJwtSignature as any)() + .should.eventually.be.rejectedWith('The provided token must be a string.'); + }); + + const invalidIdTokens = [null, NaN, 0, 1, true, false, [], {}, { a: 1 }, _.noop]; + invalidIdTokens.forEach((invalidIdToken) => { + it('should reject given a non-string token: ' + JSON.stringify(invalidIdToken), () => { + return jwtUtil.verifyJwtSignature(invalidIdToken as any, mocks.keyPairs[0].public) + .should.eventually.be.rejectedWith('The provided token must be a string.'); + }); + }); + + it('should reject given an empty string token', () => { + return jwtUtil.verifyJwtSignature('', mocks.keyPairs[0].public) + .should.eventually.be.rejectedWith('jwt must be provided'); + }); + + it('should be fulfilled given a valid signed token and public key', () => { + const mockIdToken = mocks.generateIdToken(); + + return jwtUtil.verifyJwtSignature(mockIdToken, mocks.keyPairs[0].public, + { algorithms: [jwtUtil.ALGORITHM_RS256] }) + .should.eventually.be.fulfilled; + }); + + it('should be fulfilled given a valid unsigned (emulator) token and no public key', () => { + const mockIdToken = mocks.generateIdToken({ + algorithm: 'none', + header: {} + }); + + return jwtUtil.verifyJwtSignature(mockIdToken, '') + .should.eventually.be.fulfilled; + }); + + it('should be fulfilled given a valid signed token and a function to provide public keys', () => { + const mockIdToken = mocks.generateIdToken(); + const getKeyCallback = (_: any, callback: any): void => callback(null, mocks.keyPairs[0].public); + + return jwtUtil.verifyJwtSignature(mockIdToken, getKeyCallback, + { algorithms: [jwtUtil.ALGORITHM_RS256] }) + .should.eventually.be.fulfilled; + }); + + it('should be rejected when the given algorithm does not match the token', () => { + const mockIdToken = mocks.generateIdToken(); + + return jwtUtil.verifyJwtSignature(mockIdToken, mocks.keyPairs[0].public, + { algorithms: ['RS384'] }) + .should.eventually.be.rejectedWith('invalid algorithm') + .with.property('code', jwtUtil.JwtErrorCode.INVALID_SIGNATURE); + }); + + it('should be rejected given an expired token', () => { + clock = sinon.useFakeTimers(1000); + const mockIdToken = mocks.generateIdToken(); + clock.tick((ONE_HOUR_IN_SECONDS * 1000) - 1); + + // token should still be valid + return jwtUtil.verifyJwtSignature(mockIdToken, mocks.keyPairs[0].public, + { algorithms: [jwtUtil.ALGORITHM_RS256] }) + .then(() => { + clock!.tick(1); + + // token should now be invalid + return jwtUtil.verifyJwtSignature(mockIdToken, mocks.keyPairs[0].public, + { algorithms: [jwtUtil.ALGORITHM_RS256] }) + .should.eventually.be.rejectedWith( + 'The provided token has expired. Get a fresh token from your client app and try again.' + ) + .with.property('code', jwtUtil.JwtErrorCode.TOKEN_EXPIRED); + }); + }); + + it('should be rejected with correct public key fetch error.', () => { + const mockIdToken = mocks.generateIdToken(); + const getKeyCallback = (_: any, callback: any): void => + callback(new Error('key fetch failed.')); + + return jwtUtil.verifyJwtSignature(mockIdToken, getKeyCallback, + { algorithms: [jwtUtil.ALGORITHM_RS256] }) + .should.eventually.be.rejectedWith('key fetch failed.') + .with.property('code', jwtUtil.JwtErrorCode.KEY_FETCH_ERROR); + }); + + it('should be rejected with correct no matching key id found error.', () => { + const mockIdToken = mocks.generateIdToken(); + const getKeyCallback = (_: any, callback: any): void => + callback(new Error('no-matching-kid-error')); + + return jwtUtil.verifyJwtSignature(mockIdToken, getKeyCallback, + { algorithms: [jwtUtil.ALGORITHM_RS256] }) + .should.eventually.be.rejectedWith('no-matching-kid-error') + .with.property('code', jwtUtil.JwtErrorCode.NO_MATCHING_KID); + }); + + it('should be rejected given a public key that does not match the token.', () => { + const mockIdToken = mocks.generateIdToken(); + + return jwtUtil.verifyJwtSignature(mockIdToken, mocks.keyPairs[1].public, + { algorithms: [jwtUtil.ALGORITHM_RS256] }) + .should.eventually.be.rejectedWith('invalid signature') + .with.property('code', jwtUtil.JwtErrorCode.INVALID_SIGNATURE); + }); + + it('should be rejected given an invalid JWT.', () => { + return jwtUtil.verifyJwtSignature('invalid-token', mocks.keyPairs[0].public) + .should.eventually.be.rejectedWith('jwt malformed') + .with.property('code', jwtUtil.JwtErrorCode.INVALID_SIGNATURE); + }); +}); + +describe('PublicKeySignatureVerifier', () => { + let stubs: sinon.SinonStub[] = []; + const verifier = new jwtUtil.PublicKeySignatureVerifier( + new jwtUtil.UrlKeyFetcher('https://www.example.com/publicKeys')); + + afterEach(() => { + _.forEach(stubs, (stub) => stub.restore()); + stubs = []; + }); + + describe('Constructor', () => { + it('should not throw when valid key fetcher is provided', () => { + expect(() => { + new jwtUtil.PublicKeySignatureVerifier( + new jwtUtil.UrlKeyFetcher('https://www.example.com/publicKeys')); + }).not.to.throw(); + }); + + const invalidKeyFetchers = [null, NaN, 0, 1, true, false, [], ['a'], _.noop, '', 'a']; + invalidKeyFetchers.forEach((invalidKeyFetcher) => { + it('should throw given an invalid key fetcher: ' + JSON.stringify(invalidKeyFetcher), () => { + expect(() => { + new jwtUtil.PublicKeySignatureVerifier(invalidKeyFetchers as any); + }).to.throw('The provided key fetcher is not an object or null.'); + }); + }); + }); + + describe('withCertificateUrl', () => { + it('should return a PublicKeySignatureVerifier instance when a valid cert url is provided', () => { + expect( + jwtUtil.PublicKeySignatureVerifier.withCertificateUrl('https://www.example.com/publicKeys') + ).to.be.an.instanceOf(jwtUtil.PublicKeySignatureVerifier); + }); + }); + + describe('verify', () => { + it('should throw given no token', () => { + return (verifier.verify as any)() + .should.eventually.be.rejectedWith('The provided token must be a string.'); + }); + + const invalidIdTokens = [null, NaN, 0, 1, true, false, [], {}, { a: 1 }, _.noop]; + invalidIdTokens.forEach((invalidIdToken) => { + it('should reject given a non-string token: ' + JSON.stringify(invalidIdToken), () => { + return verifier.verify(invalidIdToken as any) + .should.eventually.be.rejectedWith('The provided token must be a string.'); + }); + }); + + it('should reject given an empty string token', () => { + return verifier.verify('') + .should.eventually.be.rejectedWith('jwt must be provided'); + }); + + it('should be fullfilled given a valid token', () => { + const keyFetcherStub = sinon.stub(jwtUtil.UrlKeyFetcher.prototype, 'fetchPublicKeys') + .resolves(VALID_PUBLIC_KEYS_RESPONSE); + stubs.push(keyFetcherStub); + const mockIdToken = mocks.generateIdToken(); + + return verifier.verify(mockIdToken).should.eventually.be.fulfilled; + }); + + it('should be rejected given a token with an incorrect algorithm', () => { + const keyFetcherStub = sinon.stub(jwtUtil.UrlKeyFetcher.prototype, 'fetchPublicKeys') + .resolves(VALID_PUBLIC_KEYS_RESPONSE); + stubs.push(keyFetcherStub); + const mockIdToken = mocks.generateIdToken({ + algorithm: 'HS256', + }); + + return verifier.verify(mockIdToken).should.eventually.be + .rejectedWith('invalid algorithm') + .with.property('code', jwtUtil.JwtErrorCode.INVALID_SIGNATURE); + }); + + // tests to cover the private getKeyCallback function. + it('should reject when no matching kid found', () => { + const keyFetcherStub = sinon.stub(jwtUtil.UrlKeyFetcher.prototype, 'fetchPublicKeys') + .resolves({ 'not-a-matching-key': 'public-key' }); + stubs.push(keyFetcherStub); + const mockIdToken = mocks.generateIdToken(); + + return verifier.verify(mockIdToken).should.eventually.be + .rejectedWith('no-matching-kid-error') + .with.property('code', jwtUtil.JwtErrorCode.NO_MATCHING_KID); + }); + + it('should reject when an error occurs while fetching the keys', () => { + const keyFetcherStub = sinon.stub(jwtUtil.UrlKeyFetcher.prototype, 'fetchPublicKeys') + .rejects(new Error('Error fetching public keys.')); + stubs.push(keyFetcherStub); + const mockIdToken = mocks.generateIdToken(); + + return verifier.verify(mockIdToken).should.eventually.be + .rejectedWith('Error fetching public keys.') + .with.property('code', jwtUtil.JwtErrorCode.KEY_FETCH_ERROR); + }); + }); +}); + +describe('EmulatorSignatureVerifier', () => { + const emulatorVerifier = new jwtUtil.EmulatorSignatureVerifier(); + + describe('verify', () => { + it('should be fullfilled given a valid unsigned (emulator) token', () => { + const mockIdToken = mocks.generateIdToken({ + algorithm: 'none', + header: {} + }); + + return emulatorVerifier.verify(mockIdToken).should.eventually.be.fulfilled; + }); + + it('should be rejected given a valid signed (non-emulator) token', () => { + const mockIdToken = mocks.generateIdToken(); + + return emulatorVerifier.verify(mockIdToken).should.eventually.be.rejected; + }); + }); +}); + +describe('UrlKeyFetcher', () => { + const agent = new https.Agent(); + let keyFetcher: jwtUtil.UrlKeyFetcher; + let clock: sinon.SinonFakeTimers | undefined; + let httpsSpy: sinon.SinonSpy; + + beforeEach(() => { + keyFetcher = new jwtUtil.UrlKeyFetcher( + 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com', + agent); + httpsSpy = sinon.spy(https, 'request'); + }); + + afterEach(() => { + if (clock) { + clock.restore(); + clock = undefined; + } + httpsSpy.restore(); + }); + + after(() => { + nock.cleanAll(); + }); + + describe('Constructor', () => { + it('should not throw when valid key parameters are provided', () => { + expect(() => { + new jwtUtil.UrlKeyFetcher('https://www.example.com/publicKeys', agent); + }).not.to.throw(); + }); + + const invalidCertURLs = [null, NaN, 0, 1, true, false, [], {}, { a: 1 }, _.noop, 'file://invalid']; + invalidCertURLs.forEach((invalidCertUrl) => { + it('should throw given a non-URL public cert: ' + JSON.stringify(invalidCertUrl), () => { + expect(() => { + new jwtUtil.UrlKeyFetcher(invalidCertUrl as any, agent); + }).to.throw('The provided public client certificate URL is not a valid URL.'); + }); + }); + }); + + describe('fetchPublicKeys', () => { + let mockedRequests: nock.Scope[] = []; + + afterEach(() => { + _.forEach(mockedRequests, (mockedRequest) => mockedRequest.done()); + mockedRequests = []; + }); + + it('should use the given HTTP Agent', () => { + const agent = new https.Agent(); + const urlKeyFetcher = new jwtUtil.UrlKeyFetcher('https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com', agent); + mockedRequests.push(mockFetchPublicKeys()); + + return urlKeyFetcher.fetchPublicKeys() + .then(() => { + expect(https.request).to.have.been.calledOnce; + expect(httpsSpy.args[0][0].agent).to.equal(agent); + }); + }); + + it('should not fetch the public keys until the first time fetchPublicKeys() is called', () => { + mockedRequests.push(mockFetchPublicKeys()); + + const urlKeyFetcher = new jwtUtil.UrlKeyFetcher('https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com', agent); + expect(https.request).not.to.have.been.called; + + return urlKeyFetcher.fetchPublicKeys() + .then(() => expect(https.request).to.have.been.calledOnce); + }); + + it('should not re-fetch the public keys every time fetchPublicKeys() is called', () => { + mockedRequests.push(mockFetchPublicKeys()); + + return keyFetcher.fetchPublicKeys().then(() => { + expect(https.request).to.have.been.calledOnce; + return keyFetcher.fetchPublicKeys(); + }).then(() => expect(https.request).to.have.been.calledOnce); + }); + + it('should refresh the public keys after the "max-age" on the request expires', () => { + mockedRequests.push(mockFetchPublicKeys()); + mockedRequests.push(mockFetchPublicKeys()); + mockedRequests.push(mockFetchPublicKeys()); + + clock = sinon.useFakeTimers(1000); + + return keyFetcher.fetchPublicKeys().then(() => { + expect(https.request).to.have.been.calledOnce; + clock!.tick(999); + return keyFetcher.fetchPublicKeys(); + }).then(() => { + expect(https.request).to.have.been.calledOnce; + clock!.tick(1); + return keyFetcher.fetchPublicKeys(); + }).then(() => { + // One second has passed + expect(https.request).to.have.been.calledTwice; + clock!.tick(999); + return keyFetcher.fetchPublicKeys(); + }).then(() => { + expect(https.request).to.have.been.calledTwice; + clock!.tick(1); + return keyFetcher.fetchPublicKeys(); + }).then(() => { + // Two seconds have passed + expect(https.request).to.have.been.calledThrice; + }); + }); + + it('should be rejected if fetching the public keys fails', () => { + mockedRequests.push(mockFailedFetchPublicKeys()); + + return keyFetcher.fetchPublicKeys() + .should.eventually.be.rejectedWith('message'); + }); + + it('should be rejected if fetching the public keys returns a response with an error message', () => { + mockedRequests.push(mockFetchPublicKeysWithErrorResponse()); + + return keyFetcher.fetchPublicKeys() + .should.eventually.be.rejectedWith('Error fetching public keys for Google certs: message (description)'); + }); + }); +}); From 1254850f9b86367afe3b864933e8741db3e7ed54 Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Wed, 31 Mar 2021 18:22:56 -0400 Subject: [PATCH 020/102] chore: Add Mailgun send email action (#1210) * Add Mailgun send email github action * Add send email action in nightly workflow --- .github/actions/send-email/README.md | 59 + .github/actions/send-email/action.yml | 44 + .github/actions/send-email/dist/index.js | 2098 ++++++++++++++++++ .github/actions/send-email/index.js | 75 + .github/actions/send-email/package-lock.json | 173 ++ .github/actions/send-email/package.json | 23 + .github/workflows/nightly.yml | 30 + test/integration/remote-config.spec.ts | 6 + 8 files changed, 2508 insertions(+) create mode 100644 .github/actions/send-email/README.md create mode 100644 .github/actions/send-email/action.yml create mode 100644 .github/actions/send-email/dist/index.js create mode 100644 .github/actions/send-email/index.js create mode 100644 .github/actions/send-email/package-lock.json create mode 100644 .github/actions/send-email/package.json diff --git a/.github/actions/send-email/README.md b/.github/actions/send-email/README.md new file mode 100644 index 0000000000..ab3f93a154 --- /dev/null +++ b/.github/actions/send-email/README.md @@ -0,0 +1,59 @@ +# Send Email GitHub Action + +This is a minimalistic GitHub Action for sending emails using the Mailgun API. +Specify the Mailgun API key along with the Mailgun domain and message to +be sent. + +## Inputs + +### `api-key` + +**Required** Mailgun API key. + +### `domain` + +**Required** Mailgun domain name. + +### `from` + +**Required** Sender's email address. Ex: 'Hello User ' (defaults to 'user@{domain}`). + +### `to` + +**Required** Recipient's email address. You can use commas to separate multiple recipients. + +### `cc` + +Email addresses to Cc. + +### `subject` + +**Required** Message subject. + +### `text` + +Text body of the message. + +### `html` + +HTML body of the message. + +## Example usage + +``` +- name: Send Email + uses: firebase/firebase-admin-node/.github/actions/send-email + with: + api-key: ${{ secrets.MAILGUN_API_KEY }} + domain: ${{ secrets.MAILGUN_DOMAIN }} + from: 'User ' + html: '

Testing some Mailgun awesomness!

' + to: 'foo@example.com' +``` + +## Implementation + +This Action uses the `mailgun.js` NPM package to send Emails. + +When making a code change remember to run `npm run pack` to rebuild the +`dist/index.js` file which is the executable of this Action. diff --git a/.github/actions/send-email/action.yml b/.github/actions/send-email/action.yml new file mode 100644 index 0000000000..65956f9a12 --- /dev/null +++ b/.github/actions/send-email/action.yml @@ -0,0 +1,44 @@ +# Copyright 2021 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: 'Send email Action' +description: 'Send emails using Mailgun from GitHub Actions workflows.' +inputs: + api-key: + description: Mailgun API key. + required: true + domain: + description: Mailgun domain name. + required: true + from: + description: Senders name and email address. + required: true + to: + description: Recipient's email address. You can use commas to separate multiple recipients. + required: true + cc: + description: Email addresses to Cc. + required: false + subject: + description: Message subject. + required: true + text: + description: Text body of the message. + required: false + html: + description: HTML body of the message. + required: false +runs: + using: 'node12' + main: 'dist/index.js' diff --git a/.github/actions/send-email/dist/index.js b/.github/actions/send-email/dist/index.js new file mode 100644 index 0000000000..22a6566c3f --- /dev/null +++ b/.github/actions/send-email/dist/index.js @@ -0,0 +1,2098 @@ +module.exports = +/******/ (function(modules, runtime) { // webpackBootstrap +/******/ "use strict"; +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ __webpack_require__.ab = __dirname + "/"; +/******/ +/******/ // the startup function +/******/ function startup() { +/******/ // Load entry module and return exports +/******/ return __webpack_require__(104); +/******/ }; +/******/ +/******/ // run startup +/******/ return startup(); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ 2: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var abort = __webpack_require__(762) + , async = __webpack_require__(792) + ; + +// API +module.exports = terminator; + +/** + * Terminates jobs in the attached state context + * + * @this AsyncKitState# + * @param {function} callback - final callback to invoke after termination + */ +function terminator(callback) +{ + if (!Object.keys(this.jobs).length) + { + return; + } + + // fast forward iteration index + this.index = this.size; + + // abort jobs + abort(this); + + // send back results we have so far + async(callback)(null, this.results); +} + + +/***/ }), + +/***/ 70: +/***/ (function(module) { + +// populates missing values +module.exports = function(dst, src) { + + Object.keys(src).forEach(function(prop) + { + dst[prop] = dst[prop] || src[prop]; + }); + + return dst; +}; + + +/***/ }), + +/***/ 82: +/***/ (function(__unusedmodule, exports) { + +"use strict"; + +// We use any as a valid input type +/* eslint-disable @typescript-eslint/no-explicit-any */ +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * Sanitizes an input into a string so it can be passed into issueCommand safely + * @param input input to sanitize into a string + */ +function toCommandValue(input) { + if (input === null || input === undefined) { + return ''; + } + else if (typeof input === 'string' || input instanceof String) { + return input; + } + return JSON.stringify(input); +} +exports.toCommandValue = toCommandValue; +//# sourceMappingURL=utils.js.map + +/***/ }), + +/***/ 87: +/***/ (function(module) { + +module.exports = require("os"); + +/***/ }), + +/***/ 89: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var iterate = __webpack_require__(258) + , initState = __webpack_require__(125) + , terminator = __webpack_require__(2) + ; + +// Public API +module.exports = parallel; + +/** + * Runs iterator over provided array elements in parallel + * + * @param {array|object} list - array or object (named list) to iterate over + * @param {function} iterator - iterator to run + * @param {function} callback - invoked when all elements processed + * @returns {function} - jobs terminator + */ +function parallel(list, iterator, callback) +{ + var state = initState(list); + + while (state.index < (state['keyedList'] || list).length) + { + iterate(list, iterator, state, function(error, result) + { + if (error) + { + callback(error, result); + return; + } + + // looks like it's the last one + if (Object.keys(state.jobs).length === 0) + { + callback(null, state.results); + return; + } + }); + + state.index++; + } + + return terminator.bind(state, callback); +} + + +/***/ }), + +/***/ 102: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +// For internal use, subject to change. +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; + result["default"] = mod; + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +// We use any as a valid input type +/* eslint-disable @typescript-eslint/no-explicit-any */ +const fs = __importStar(__webpack_require__(747)); +const os = __importStar(__webpack_require__(87)); +const utils_1 = __webpack_require__(82); +function issueCommand(command, message) { + const filePath = process.env[`GITHUB_${command}`]; + if (!filePath) { + throw new Error(`Unable to find environment variable for file command ${command}`); + } + if (!fs.existsSync(filePath)) { + throw new Error(`Missing file at path: ${filePath}`); + } + fs.appendFileSync(filePath, `${utils_1.toCommandValue(message)}${os.EOL}`, { + encoding: 'utf8' + }); +} +exports.issueCommand = issueCommand; +//# sourceMappingURL=file-command.js.map + +/***/ }), + +/***/ 104: +/***/ (function(__unusedmodule, __unusedexports, __webpack_require__) { + +/*! + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const core = __webpack_require__(470); +const formData = __webpack_require__(416); +const Mailgun = __webpack_require__(618); + +const mailgun = new Mailgun(formData); +const optionalFields = ['cc', 'text', 'html']; + +function loadConfig() { + return { + apiKey: core.getInput('api-key'), + domain: core.getInput('domain'), + to: core.getInput('to'), + from: core.getInput('from'), + cc: core.getInput('cc'), + subject: core.getInput('subject'), + text: core.getInput('text'), + html: core.getInput('html'), + } +} + +function validate(config) { + for (param in config) { + if (optionalFields.includes(param)) { + continue; + } + validateRequiredParameter(config[param], `'${param}'`); + } +} + +function validateRequiredParameter(value, name) { + if (!isNonEmptyString(value)) { + throw new Error(`${name} must be a non-empty string.`); + } +} + +function sendEmail(config) { + const mg = mailgun.client({ + username: 'api', + key: config.apiKey, + }); + + return mg.messages + .create(domain, config) + .then((resp) => { + core.setOutput('response', resp.message); + return; + }) + .catch((err) => { + core.setFailed(err.message); + }); +} + +function isNonEmptyString(value) { + return typeof value === 'string' && value !== ''; +} + +const config = loadConfig(); +validate(config); +sendEmail(config); + + +/***/ }), + +/***/ 118: +/***/ (function(module) { + +module.exports = {"application/1d-interleaved-parityfec":{"source":"iana"},"application/3gpdash-qoe-report+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/3gpp-ims+xml":{"source":"iana","compressible":true},"application/a2l":{"source":"iana"},"application/activemessage":{"source":"iana"},"application/activity+json":{"source":"iana","compressible":true},"application/alto-costmap+json":{"source":"iana","compressible":true},"application/alto-costmapfilter+json":{"source":"iana","compressible":true},"application/alto-directory+json":{"source":"iana","compressible":true},"application/alto-endpointcost+json":{"source":"iana","compressible":true},"application/alto-endpointcostparams+json":{"source":"iana","compressible":true},"application/alto-endpointprop+json":{"source":"iana","compressible":true},"application/alto-endpointpropparams+json":{"source":"iana","compressible":true},"application/alto-error+json":{"source":"iana","compressible":true},"application/alto-networkmap+json":{"source":"iana","compressible":true},"application/alto-networkmapfilter+json":{"source":"iana","compressible":true},"application/alto-updatestreamcontrol+json":{"source":"iana","compressible":true},"application/alto-updatestreamparams+json":{"source":"iana","compressible":true},"application/aml":{"source":"iana"},"application/andrew-inset":{"source":"iana","extensions":["ez"]},"application/applefile":{"source":"iana"},"application/applixware":{"source":"apache","extensions":["aw"]},"application/atf":{"source":"iana"},"application/atfx":{"source":"iana"},"application/atom+xml":{"source":"iana","compressible":true,"extensions":["atom"]},"application/atomcat+xml":{"source":"iana","compressible":true,"extensions":["atomcat"]},"application/atomdeleted+xml":{"source":"iana","compressible":true,"extensions":["atomdeleted"]},"application/atomicmail":{"source":"iana"},"application/atomsvc+xml":{"source":"iana","compressible":true,"extensions":["atomsvc"]},"application/atsc-dwd+xml":{"source":"iana","compressible":true,"extensions":["dwd"]},"application/atsc-dynamic-event-message":{"source":"iana"},"application/atsc-held+xml":{"source":"iana","compressible":true,"extensions":["held"]},"application/atsc-rdt+json":{"source":"iana","compressible":true},"application/atsc-rsat+xml":{"source":"iana","compressible":true,"extensions":["rsat"]},"application/atxml":{"source":"iana"},"application/auth-policy+xml":{"source":"iana","compressible":true},"application/bacnet-xdd+zip":{"source":"iana","compressible":false},"application/batch-smtp":{"source":"iana"},"application/bdoc":{"compressible":false,"extensions":["bdoc"]},"application/beep+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/calendar+json":{"source":"iana","compressible":true},"application/calendar+xml":{"source":"iana","compressible":true,"extensions":["xcs"]},"application/call-completion":{"source":"iana"},"application/cals-1840":{"source":"iana"},"application/cap+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/cbor":{"source":"iana"},"application/cbor-seq":{"source":"iana"},"application/cccex":{"source":"iana"},"application/ccmp+xml":{"source":"iana","compressible":true},"application/ccxml+xml":{"source":"iana","compressible":true,"extensions":["ccxml"]},"application/cdfx+xml":{"source":"iana","compressible":true,"extensions":["cdfx"]},"application/cdmi-capability":{"source":"iana","extensions":["cdmia"]},"application/cdmi-container":{"source":"iana","extensions":["cdmic"]},"application/cdmi-domain":{"source":"iana","extensions":["cdmid"]},"application/cdmi-object":{"source":"iana","extensions":["cdmio"]},"application/cdmi-queue":{"source":"iana","extensions":["cdmiq"]},"application/cdni":{"source":"iana"},"application/cea":{"source":"iana"},"application/cea-2018+xml":{"source":"iana","compressible":true},"application/cellml+xml":{"source":"iana","compressible":true},"application/cfw":{"source":"iana"},"application/clue+xml":{"source":"iana","compressible":true},"application/clue_info+xml":{"source":"iana","compressible":true},"application/cms":{"source":"iana"},"application/cnrp+xml":{"source":"iana","compressible":true},"application/coap-group+json":{"source":"iana","compressible":true},"application/coap-payload":{"source":"iana"},"application/commonground":{"source":"iana"},"application/conference-info+xml":{"source":"iana","compressible":true},"application/cose":{"source":"iana"},"application/cose-key":{"source":"iana"},"application/cose-key-set":{"source":"iana"},"application/cpl+xml":{"source":"iana","compressible":true},"application/csrattrs":{"source":"iana"},"application/csta+xml":{"source":"iana","compressible":true},"application/cstadata+xml":{"source":"iana","compressible":true},"application/csvm+json":{"source":"iana","compressible":true},"application/cu-seeme":{"source":"apache","extensions":["cu"]},"application/cwt":{"source":"iana"},"application/cybercash":{"source":"iana"},"application/dart":{"compressible":true},"application/dash+xml":{"source":"iana","compressible":true,"extensions":["mpd"]},"application/dashdelta":{"source":"iana"},"application/davmount+xml":{"source":"iana","compressible":true,"extensions":["davmount"]},"application/dca-rft":{"source":"iana"},"application/dcd":{"source":"iana"},"application/dec-dx":{"source":"iana"},"application/dialog-info+xml":{"source":"iana","compressible":true},"application/dicom":{"source":"iana"},"application/dicom+json":{"source":"iana","compressible":true},"application/dicom+xml":{"source":"iana","compressible":true},"application/dii":{"source":"iana"},"application/dit":{"source":"iana"},"application/dns":{"source":"iana"},"application/dns+json":{"source":"iana","compressible":true},"application/dns-message":{"source":"iana"},"application/docbook+xml":{"source":"apache","compressible":true,"extensions":["dbk"]},"application/dots+cbor":{"source":"iana"},"application/dskpp+xml":{"source":"iana","compressible":true},"application/dssc+der":{"source":"iana","extensions":["dssc"]},"application/dssc+xml":{"source":"iana","compressible":true,"extensions":["xdssc"]},"application/dvcs":{"source":"iana"},"application/ecmascript":{"source":"iana","compressible":true,"extensions":["ecma","es"]},"application/edi-consent":{"source":"iana"},"application/edi-x12":{"source":"iana","compressible":false},"application/edifact":{"source":"iana","compressible":false},"application/efi":{"source":"iana"},"application/emergencycalldata.comment+xml":{"source":"iana","compressible":true},"application/emergencycalldata.control+xml":{"source":"iana","compressible":true},"application/emergencycalldata.deviceinfo+xml":{"source":"iana","compressible":true},"application/emergencycalldata.ecall.msd":{"source":"iana"},"application/emergencycalldata.providerinfo+xml":{"source":"iana","compressible":true},"application/emergencycalldata.serviceinfo+xml":{"source":"iana","compressible":true},"application/emergencycalldata.subscriberinfo+xml":{"source":"iana","compressible":true},"application/emergencycalldata.veds+xml":{"source":"iana","compressible":true},"application/emma+xml":{"source":"iana","compressible":true,"extensions":["emma"]},"application/emotionml+xml":{"source":"iana","compressible":true,"extensions":["emotionml"]},"application/encaprtp":{"source":"iana"},"application/epp+xml":{"source":"iana","compressible":true},"application/epub+zip":{"source":"iana","compressible":false,"extensions":["epub"]},"application/eshop":{"source":"iana"},"application/exi":{"source":"iana","extensions":["exi"]},"application/expect-ct-report+json":{"source":"iana","compressible":true},"application/fastinfoset":{"source":"iana"},"application/fastsoap":{"source":"iana"},"application/fdt+xml":{"source":"iana","compressible":true,"extensions":["fdt"]},"application/fhir+json":{"source":"iana","charset":"UTF-8","compressible":true},"application/fhir+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/fido.trusted-apps+json":{"compressible":true},"application/fits":{"source":"iana"},"application/flexfec":{"source":"iana"},"application/font-sfnt":{"source":"iana"},"application/font-tdpfr":{"source":"iana","extensions":["pfr"]},"application/font-woff":{"source":"iana","compressible":false},"application/framework-attributes+xml":{"source":"iana","compressible":true},"application/geo+json":{"source":"iana","compressible":true,"extensions":["geojson"]},"application/geo+json-seq":{"source":"iana"},"application/geopackage+sqlite3":{"source":"iana"},"application/geoxacml+xml":{"source":"iana","compressible":true},"application/gltf-buffer":{"source":"iana"},"application/gml+xml":{"source":"iana","compressible":true,"extensions":["gml"]},"application/gpx+xml":{"source":"apache","compressible":true,"extensions":["gpx"]},"application/gxf":{"source":"apache","extensions":["gxf"]},"application/gzip":{"source":"iana","compressible":false,"extensions":["gz"]},"application/h224":{"source":"iana"},"application/held+xml":{"source":"iana","compressible":true},"application/hjson":{"extensions":["hjson"]},"application/http":{"source":"iana"},"application/hyperstudio":{"source":"iana","extensions":["stk"]},"application/ibe-key-request+xml":{"source":"iana","compressible":true},"application/ibe-pkg-reply+xml":{"source":"iana","compressible":true},"application/ibe-pp-data":{"source":"iana"},"application/iges":{"source":"iana"},"application/im-iscomposing+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/index":{"source":"iana"},"application/index.cmd":{"source":"iana"},"application/index.obj":{"source":"iana"},"application/index.response":{"source":"iana"},"application/index.vnd":{"source":"iana"},"application/inkml+xml":{"source":"iana","compressible":true,"extensions":["ink","inkml"]},"application/iotp":{"source":"iana"},"application/ipfix":{"source":"iana","extensions":["ipfix"]},"application/ipp":{"source":"iana"},"application/isup":{"source":"iana"},"application/its+xml":{"source":"iana","compressible":true,"extensions":["its"]},"application/java-archive":{"source":"apache","compressible":false,"extensions":["jar","war","ear"]},"application/java-serialized-object":{"source":"apache","compressible":false,"extensions":["ser"]},"application/java-vm":{"source":"apache","compressible":false,"extensions":["class"]},"application/javascript":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["js","mjs"]},"application/jf2feed+json":{"source":"iana","compressible":true},"application/jose":{"source":"iana"},"application/jose+json":{"source":"iana","compressible":true},"application/jrd+json":{"source":"iana","compressible":true},"application/json":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["json","map"]},"application/json-patch+json":{"source":"iana","compressible":true},"application/json-seq":{"source":"iana"},"application/json5":{"extensions":["json5"]},"application/jsonml+json":{"source":"apache","compressible":true,"extensions":["jsonml"]},"application/jwk+json":{"source":"iana","compressible":true},"application/jwk-set+json":{"source":"iana","compressible":true},"application/jwt":{"source":"iana"},"application/kpml-request+xml":{"source":"iana","compressible":true},"application/kpml-response+xml":{"source":"iana","compressible":true},"application/ld+json":{"source":"iana","compressible":true,"extensions":["jsonld"]},"application/lgr+xml":{"source":"iana","compressible":true,"extensions":["lgr"]},"application/link-format":{"source":"iana"},"application/load-control+xml":{"source":"iana","compressible":true},"application/lost+xml":{"source":"iana","compressible":true,"extensions":["lostxml"]},"application/lostsync+xml":{"source":"iana","compressible":true},"application/lpf+zip":{"source":"iana","compressible":false},"application/lxf":{"source":"iana"},"application/mac-binhex40":{"source":"iana","extensions":["hqx"]},"application/mac-compactpro":{"source":"apache","extensions":["cpt"]},"application/macwriteii":{"source":"iana"},"application/mads+xml":{"source":"iana","compressible":true,"extensions":["mads"]},"application/manifest+json":{"charset":"UTF-8","compressible":true,"extensions":["webmanifest"]},"application/marc":{"source":"iana","extensions":["mrc"]},"application/marcxml+xml":{"source":"iana","compressible":true,"extensions":["mrcx"]},"application/mathematica":{"source":"iana","extensions":["ma","nb","mb"]},"application/mathml+xml":{"source":"iana","compressible":true,"extensions":["mathml"]},"application/mathml-content+xml":{"source":"iana","compressible":true},"application/mathml-presentation+xml":{"source":"iana","compressible":true},"application/mbms-associated-procedure-description+xml":{"source":"iana","compressible":true},"application/mbms-deregister+xml":{"source":"iana","compressible":true},"application/mbms-envelope+xml":{"source":"iana","compressible":true},"application/mbms-msk+xml":{"source":"iana","compressible":true},"application/mbms-msk-response+xml":{"source":"iana","compressible":true},"application/mbms-protection-description+xml":{"source":"iana","compressible":true},"application/mbms-reception-report+xml":{"source":"iana","compressible":true},"application/mbms-register+xml":{"source":"iana","compressible":true},"application/mbms-register-response+xml":{"source":"iana","compressible":true},"application/mbms-schedule+xml":{"source":"iana","compressible":true},"application/mbms-user-service-description+xml":{"source":"iana","compressible":true},"application/mbox":{"source":"iana","extensions":["mbox"]},"application/media-policy-dataset+xml":{"source":"iana","compressible":true},"application/media_control+xml":{"source":"iana","compressible":true},"application/mediaservercontrol+xml":{"source":"iana","compressible":true,"extensions":["mscml"]},"application/merge-patch+json":{"source":"iana","compressible":true},"application/metalink+xml":{"source":"apache","compressible":true,"extensions":["metalink"]},"application/metalink4+xml":{"source":"iana","compressible":true,"extensions":["meta4"]},"application/mets+xml":{"source":"iana","compressible":true,"extensions":["mets"]},"application/mf4":{"source":"iana"},"application/mikey":{"source":"iana"},"application/mipc":{"source":"iana"},"application/mmt-aei+xml":{"source":"iana","compressible":true,"extensions":["maei"]},"application/mmt-usd+xml":{"source":"iana","compressible":true,"extensions":["musd"]},"application/mods+xml":{"source":"iana","compressible":true,"extensions":["mods"]},"application/moss-keys":{"source":"iana"},"application/moss-signature":{"source":"iana"},"application/mosskey-data":{"source":"iana"},"application/mosskey-request":{"source":"iana"},"application/mp21":{"source":"iana","extensions":["m21","mp21"]},"application/mp4":{"source":"iana","extensions":["mp4s","m4p"]},"application/mpeg4-generic":{"source":"iana"},"application/mpeg4-iod":{"source":"iana"},"application/mpeg4-iod-xmt":{"source":"iana"},"application/mrb-consumer+xml":{"source":"iana","compressible":true,"extensions":["xdf"]},"application/mrb-publish+xml":{"source":"iana","compressible":true,"extensions":["xdf"]},"application/msc-ivr+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/msc-mixer+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/msword":{"source":"iana","compressible":false,"extensions":["doc","dot"]},"application/mud+json":{"source":"iana","compressible":true},"application/multipart-core":{"source":"iana"},"application/mxf":{"source":"iana","extensions":["mxf"]},"application/n-quads":{"source":"iana","extensions":["nq"]},"application/n-triples":{"source":"iana","extensions":["nt"]},"application/nasdata":{"source":"iana"},"application/news-checkgroups":{"source":"iana","charset":"US-ASCII"},"application/news-groupinfo":{"source":"iana","charset":"US-ASCII"},"application/news-transmission":{"source":"iana"},"application/nlsml+xml":{"source":"iana","compressible":true},"application/node":{"source":"iana","extensions":["cjs"]},"application/nss":{"source":"iana"},"application/ocsp-request":{"source":"iana"},"application/ocsp-response":{"source":"iana"},"application/octet-stream":{"source":"iana","compressible":false,"extensions":["bin","dms","lrf","mar","so","dist","distz","pkg","bpk","dump","elc","deploy","exe","dll","deb","dmg","iso","img","msi","msp","msm","buffer"]},"application/oda":{"source":"iana","extensions":["oda"]},"application/odm+xml":{"source":"iana","compressible":true},"application/odx":{"source":"iana"},"application/oebps-package+xml":{"source":"iana","compressible":true,"extensions":["opf"]},"application/ogg":{"source":"iana","compressible":false,"extensions":["ogx"]},"application/omdoc+xml":{"source":"apache","compressible":true,"extensions":["omdoc"]},"application/onenote":{"source":"apache","extensions":["onetoc","onetoc2","onetmp","onepkg"]},"application/oscore":{"source":"iana"},"application/oxps":{"source":"iana","extensions":["oxps"]},"application/p2p-overlay+xml":{"source":"iana","compressible":true,"extensions":["relo"]},"application/parityfec":{"source":"iana"},"application/passport":{"source":"iana"},"application/patch-ops-error+xml":{"source":"iana","compressible":true,"extensions":["xer"]},"application/pdf":{"source":"iana","compressible":false,"extensions":["pdf"]},"application/pdx":{"source":"iana"},"application/pem-certificate-chain":{"source":"iana"},"application/pgp-encrypted":{"source":"iana","compressible":false,"extensions":["pgp"]},"application/pgp-keys":{"source":"iana"},"application/pgp-signature":{"source":"iana","extensions":["asc","sig"]},"application/pics-rules":{"source":"apache","extensions":["prf"]},"application/pidf+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/pidf-diff+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/pkcs10":{"source":"iana","extensions":["p10"]},"application/pkcs12":{"source":"iana"},"application/pkcs7-mime":{"source":"iana","extensions":["p7m","p7c"]},"application/pkcs7-signature":{"source":"iana","extensions":["p7s"]},"application/pkcs8":{"source":"iana","extensions":["p8"]},"application/pkcs8-encrypted":{"source":"iana"},"application/pkix-attr-cert":{"source":"iana","extensions":["ac"]},"application/pkix-cert":{"source":"iana","extensions":["cer"]},"application/pkix-crl":{"source":"iana","extensions":["crl"]},"application/pkix-pkipath":{"source":"iana","extensions":["pkipath"]},"application/pkixcmp":{"source":"iana","extensions":["pki"]},"application/pls+xml":{"source":"iana","compressible":true,"extensions":["pls"]},"application/poc-settings+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/postscript":{"source":"iana","compressible":true,"extensions":["ai","eps","ps"]},"application/ppsp-tracker+json":{"source":"iana","compressible":true},"application/problem+json":{"source":"iana","compressible":true},"application/problem+xml":{"source":"iana","compressible":true},"application/provenance+xml":{"source":"iana","compressible":true,"extensions":["provx"]},"application/prs.alvestrand.titrax-sheet":{"source":"iana"},"application/prs.cww":{"source":"iana","extensions":["cww"]},"application/prs.hpub+zip":{"source":"iana","compressible":false},"application/prs.nprend":{"source":"iana"},"application/prs.plucker":{"source":"iana"},"application/prs.rdf-xml-crypt":{"source":"iana"},"application/prs.xsf+xml":{"source":"iana","compressible":true},"application/pskc+xml":{"source":"iana","compressible":true,"extensions":["pskcxml"]},"application/pvd+json":{"source":"iana","compressible":true},"application/qsig":{"source":"iana"},"application/raml+yaml":{"compressible":true,"extensions":["raml"]},"application/raptorfec":{"source":"iana"},"application/rdap+json":{"source":"iana","compressible":true},"application/rdf+xml":{"source":"iana","compressible":true,"extensions":["rdf","owl"]},"application/reginfo+xml":{"source":"iana","compressible":true,"extensions":["rif"]},"application/relax-ng-compact-syntax":{"source":"iana","extensions":["rnc"]},"application/remote-printing":{"source":"iana"},"application/reputon+json":{"source":"iana","compressible":true},"application/resource-lists+xml":{"source":"iana","compressible":true,"extensions":["rl"]},"application/resource-lists-diff+xml":{"source":"iana","compressible":true,"extensions":["rld"]},"application/rfc+xml":{"source":"iana","compressible":true},"application/riscos":{"source":"iana"},"application/rlmi+xml":{"source":"iana","compressible":true},"application/rls-services+xml":{"source":"iana","compressible":true,"extensions":["rs"]},"application/route-apd+xml":{"source":"iana","compressible":true,"extensions":["rapd"]},"application/route-s-tsid+xml":{"source":"iana","compressible":true,"extensions":["sls"]},"application/route-usd+xml":{"source":"iana","compressible":true,"extensions":["rusd"]},"application/rpki-ghostbusters":{"source":"iana","extensions":["gbr"]},"application/rpki-manifest":{"source":"iana","extensions":["mft"]},"application/rpki-publication":{"source":"iana"},"application/rpki-roa":{"source":"iana","extensions":["roa"]},"application/rpki-updown":{"source":"iana"},"application/rsd+xml":{"source":"apache","compressible":true,"extensions":["rsd"]},"application/rss+xml":{"source":"apache","compressible":true,"extensions":["rss"]},"application/rtf":{"source":"iana","compressible":true,"extensions":["rtf"]},"application/rtploopback":{"source":"iana"},"application/rtx":{"source":"iana"},"application/samlassertion+xml":{"source":"iana","compressible":true},"application/samlmetadata+xml":{"source":"iana","compressible":true},"application/sbe":{"source":"iana"},"application/sbml+xml":{"source":"iana","compressible":true,"extensions":["sbml"]},"application/scaip+xml":{"source":"iana","compressible":true},"application/scim+json":{"source":"iana","compressible":true},"application/scvp-cv-request":{"source":"iana","extensions":["scq"]},"application/scvp-cv-response":{"source":"iana","extensions":["scs"]},"application/scvp-vp-request":{"source":"iana","extensions":["spq"]},"application/scvp-vp-response":{"source":"iana","extensions":["spp"]},"application/sdp":{"source":"iana","extensions":["sdp"]},"application/secevent+jwt":{"source":"iana"},"application/senml+cbor":{"source":"iana"},"application/senml+json":{"source":"iana","compressible":true},"application/senml+xml":{"source":"iana","compressible":true,"extensions":["senmlx"]},"application/senml-etch+cbor":{"source":"iana"},"application/senml-etch+json":{"source":"iana","compressible":true},"application/senml-exi":{"source":"iana"},"application/sensml+cbor":{"source":"iana"},"application/sensml+json":{"source":"iana","compressible":true},"application/sensml+xml":{"source":"iana","compressible":true,"extensions":["sensmlx"]},"application/sensml-exi":{"source":"iana"},"application/sep+xml":{"source":"iana","compressible":true},"application/sep-exi":{"source":"iana"},"application/session-info":{"source":"iana"},"application/set-payment":{"source":"iana"},"application/set-payment-initiation":{"source":"iana","extensions":["setpay"]},"application/set-registration":{"source":"iana"},"application/set-registration-initiation":{"source":"iana","extensions":["setreg"]},"application/sgml":{"source":"iana"},"application/sgml-open-catalog":{"source":"iana"},"application/shf+xml":{"source":"iana","compressible":true,"extensions":["shf"]},"application/sieve":{"source":"iana","extensions":["siv","sieve"]},"application/simple-filter+xml":{"source":"iana","compressible":true},"application/simple-message-summary":{"source":"iana"},"application/simplesymbolcontainer":{"source":"iana"},"application/sipc":{"source":"iana"},"application/slate":{"source":"iana"},"application/smil":{"source":"iana"},"application/smil+xml":{"source":"iana","compressible":true,"extensions":["smi","smil"]},"application/smpte336m":{"source":"iana"},"application/soap+fastinfoset":{"source":"iana"},"application/soap+xml":{"source":"iana","compressible":true},"application/sparql-query":{"source":"iana","extensions":["rq"]},"application/sparql-results+xml":{"source":"iana","compressible":true,"extensions":["srx"]},"application/spirits-event+xml":{"source":"iana","compressible":true},"application/sql":{"source":"iana"},"application/srgs":{"source":"iana","extensions":["gram"]},"application/srgs+xml":{"source":"iana","compressible":true,"extensions":["grxml"]},"application/sru+xml":{"source":"iana","compressible":true,"extensions":["sru"]},"application/ssdl+xml":{"source":"apache","compressible":true,"extensions":["ssdl"]},"application/ssml+xml":{"source":"iana","compressible":true,"extensions":["ssml"]},"application/stix+json":{"source":"iana","compressible":true},"application/swid+xml":{"source":"iana","compressible":true,"extensions":["swidtag"]},"application/tamp-apex-update":{"source":"iana"},"application/tamp-apex-update-confirm":{"source":"iana"},"application/tamp-community-update":{"source":"iana"},"application/tamp-community-update-confirm":{"source":"iana"},"application/tamp-error":{"source":"iana"},"application/tamp-sequence-adjust":{"source":"iana"},"application/tamp-sequence-adjust-confirm":{"source":"iana"},"application/tamp-status-query":{"source":"iana"},"application/tamp-status-response":{"source":"iana"},"application/tamp-update":{"source":"iana"},"application/tamp-update-confirm":{"source":"iana"},"application/tar":{"compressible":true},"application/taxii+json":{"source":"iana","compressible":true},"application/td+json":{"source":"iana","compressible":true},"application/tei+xml":{"source":"iana","compressible":true,"extensions":["tei","teicorpus"]},"application/tetra_isi":{"source":"iana"},"application/thraud+xml":{"source":"iana","compressible":true,"extensions":["tfi"]},"application/timestamp-query":{"source":"iana"},"application/timestamp-reply":{"source":"iana"},"application/timestamped-data":{"source":"iana","extensions":["tsd"]},"application/tlsrpt+gzip":{"source":"iana"},"application/tlsrpt+json":{"source":"iana","compressible":true},"application/tnauthlist":{"source":"iana"},"application/toml":{"compressible":true,"extensions":["toml"]},"application/trickle-ice-sdpfrag":{"source":"iana"},"application/trig":{"source":"iana"},"application/ttml+xml":{"source":"iana","compressible":true,"extensions":["ttml"]},"application/tve-trigger":{"source":"iana"},"application/tzif":{"source":"iana"},"application/tzif-leap":{"source":"iana"},"application/ulpfec":{"source":"iana"},"application/urc-grpsheet+xml":{"source":"iana","compressible":true},"application/urc-ressheet+xml":{"source":"iana","compressible":true,"extensions":["rsheet"]},"application/urc-targetdesc+xml":{"source":"iana","compressible":true},"application/urc-uisocketdesc+xml":{"source":"iana","compressible":true},"application/vcard+json":{"source":"iana","compressible":true},"application/vcard+xml":{"source":"iana","compressible":true},"application/vemmi":{"source":"iana"},"application/vividence.scriptfile":{"source":"apache"},"application/vnd.1000minds.decision-model+xml":{"source":"iana","compressible":true,"extensions":["1km"]},"application/vnd.3gpp-prose+xml":{"source":"iana","compressible":true},"application/vnd.3gpp-prose-pc3ch+xml":{"source":"iana","compressible":true},"application/vnd.3gpp-v2x-local-service-information":{"source":"iana"},"application/vnd.3gpp.access-transfer-events+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.bsf+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.gmop+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mc-signalling-ear":{"source":"iana"},"application/vnd.3gpp.mcdata-affiliation-command+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcdata-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcdata-payload":{"source":"iana"},"application/vnd.3gpp.mcdata-service-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcdata-signalling":{"source":"iana"},"application/vnd.3gpp.mcdata-ue-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcdata-user-profile+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-affiliation-command+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-floor-request+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-location-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-mbms-usage-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-service-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-signed+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-ue-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-ue-init-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-user-profile+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-affiliation-command+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-affiliation-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-location-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-mbms-usage-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-service-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-transmission-request+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-ue-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-user-profile+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mid-call+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.pic-bw-large":{"source":"iana","extensions":["plb"]},"application/vnd.3gpp.pic-bw-small":{"source":"iana","extensions":["psb"]},"application/vnd.3gpp.pic-bw-var":{"source":"iana","extensions":["pvb"]},"application/vnd.3gpp.sms":{"source":"iana"},"application/vnd.3gpp.sms+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.srvcc-ext+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.srvcc-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.state-and-event-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.ussd+xml":{"source":"iana","compressible":true},"application/vnd.3gpp2.bcmcsinfo+xml":{"source":"iana","compressible":true},"application/vnd.3gpp2.sms":{"source":"iana"},"application/vnd.3gpp2.tcap":{"source":"iana","extensions":["tcap"]},"application/vnd.3lightssoftware.imagescal":{"source":"iana"},"application/vnd.3m.post-it-notes":{"source":"iana","extensions":["pwn"]},"application/vnd.accpac.simply.aso":{"source":"iana","extensions":["aso"]},"application/vnd.accpac.simply.imp":{"source":"iana","extensions":["imp"]},"application/vnd.acucobol":{"source":"iana","extensions":["acu"]},"application/vnd.acucorp":{"source":"iana","extensions":["atc","acutc"]},"application/vnd.adobe.air-application-installer-package+zip":{"source":"apache","compressible":false,"extensions":["air"]},"application/vnd.adobe.flash.movie":{"source":"iana"},"application/vnd.adobe.formscentral.fcdt":{"source":"iana","extensions":["fcdt"]},"application/vnd.adobe.fxp":{"source":"iana","extensions":["fxp","fxpl"]},"application/vnd.adobe.partial-upload":{"source":"iana"},"application/vnd.adobe.xdp+xml":{"source":"iana","compressible":true,"extensions":["xdp"]},"application/vnd.adobe.xfdf":{"source":"iana","extensions":["xfdf"]},"application/vnd.aether.imp":{"source":"iana"},"application/vnd.afpc.afplinedata":{"source":"iana"},"application/vnd.afpc.afplinedata-pagedef":{"source":"iana"},"application/vnd.afpc.foca-charset":{"source":"iana"},"application/vnd.afpc.foca-codedfont":{"source":"iana"},"application/vnd.afpc.foca-codepage":{"source":"iana"},"application/vnd.afpc.modca":{"source":"iana"},"application/vnd.afpc.modca-formdef":{"source":"iana"},"application/vnd.afpc.modca-mediummap":{"source":"iana"},"application/vnd.afpc.modca-objectcontainer":{"source":"iana"},"application/vnd.afpc.modca-overlay":{"source":"iana"},"application/vnd.afpc.modca-pagesegment":{"source":"iana"},"application/vnd.ah-barcode":{"source":"iana"},"application/vnd.ahead.space":{"source":"iana","extensions":["ahead"]},"application/vnd.airzip.filesecure.azf":{"source":"iana","extensions":["azf"]},"application/vnd.airzip.filesecure.azs":{"source":"iana","extensions":["azs"]},"application/vnd.amadeus+json":{"source":"iana","compressible":true},"application/vnd.amazon.ebook":{"source":"apache","extensions":["azw"]},"application/vnd.amazon.mobi8-ebook":{"source":"iana"},"application/vnd.americandynamics.acc":{"source":"iana","extensions":["acc"]},"application/vnd.amiga.ami":{"source":"iana","extensions":["ami"]},"application/vnd.amundsen.maze+xml":{"source":"iana","compressible":true},"application/vnd.android.ota":{"source":"iana"},"application/vnd.android.package-archive":{"source":"apache","compressible":false,"extensions":["apk"]},"application/vnd.anki":{"source":"iana"},"application/vnd.anser-web-certificate-issue-initiation":{"source":"iana","extensions":["cii"]},"application/vnd.anser-web-funds-transfer-initiation":{"source":"apache","extensions":["fti"]},"application/vnd.antix.game-component":{"source":"iana","extensions":["atx"]},"application/vnd.apache.thrift.binary":{"source":"iana"},"application/vnd.apache.thrift.compact":{"source":"iana"},"application/vnd.apache.thrift.json":{"source":"iana"},"application/vnd.api+json":{"source":"iana","compressible":true},"application/vnd.aplextor.warrp+json":{"source":"iana","compressible":true},"application/vnd.apothekende.reservation+json":{"source":"iana","compressible":true},"application/vnd.apple.installer+xml":{"source":"iana","compressible":true,"extensions":["mpkg"]},"application/vnd.apple.keynote":{"source":"iana","extensions":["keynote"]},"application/vnd.apple.mpegurl":{"source":"iana","extensions":["m3u8"]},"application/vnd.apple.numbers":{"source":"iana","extensions":["numbers"]},"application/vnd.apple.pages":{"source":"iana","extensions":["pages"]},"application/vnd.apple.pkpass":{"compressible":false,"extensions":["pkpass"]},"application/vnd.arastra.swi":{"source":"iana"},"application/vnd.aristanetworks.swi":{"source":"iana","extensions":["swi"]},"application/vnd.artisan+json":{"source":"iana","compressible":true},"application/vnd.artsquare":{"source":"iana"},"application/vnd.astraea-software.iota":{"source":"iana","extensions":["iota"]},"application/vnd.audiograph":{"source":"iana","extensions":["aep"]},"application/vnd.autopackage":{"source":"iana"},"application/vnd.avalon+json":{"source":"iana","compressible":true},"application/vnd.avistar+xml":{"source":"iana","compressible":true},"application/vnd.balsamiq.bmml+xml":{"source":"iana","compressible":true,"extensions":["bmml"]},"application/vnd.balsamiq.bmpr":{"source":"iana"},"application/vnd.banana-accounting":{"source":"iana"},"application/vnd.bbf.usp.error":{"source":"iana"},"application/vnd.bbf.usp.msg":{"source":"iana"},"application/vnd.bbf.usp.msg+json":{"source":"iana","compressible":true},"application/vnd.bekitzur-stech+json":{"source":"iana","compressible":true},"application/vnd.bint.med-content":{"source":"iana"},"application/vnd.biopax.rdf+xml":{"source":"iana","compressible":true},"application/vnd.blink-idb-value-wrapper":{"source":"iana"},"application/vnd.blueice.multipass":{"source":"iana","extensions":["mpm"]},"application/vnd.bluetooth.ep.oob":{"source":"iana"},"application/vnd.bluetooth.le.oob":{"source":"iana"},"application/vnd.bmi":{"source":"iana","extensions":["bmi"]},"application/vnd.bpf":{"source":"iana"},"application/vnd.bpf3":{"source":"iana"},"application/vnd.businessobjects":{"source":"iana","extensions":["rep"]},"application/vnd.byu.uapi+json":{"source":"iana","compressible":true},"application/vnd.cab-jscript":{"source":"iana"},"application/vnd.canon-cpdl":{"source":"iana"},"application/vnd.canon-lips":{"source":"iana"},"application/vnd.capasystems-pg+json":{"source":"iana","compressible":true},"application/vnd.cendio.thinlinc.clientconf":{"source":"iana"},"application/vnd.century-systems.tcp_stream":{"source":"iana"},"application/vnd.chemdraw+xml":{"source":"iana","compressible":true,"extensions":["cdxml"]},"application/vnd.chess-pgn":{"source":"iana"},"application/vnd.chipnuts.karaoke-mmd":{"source":"iana","extensions":["mmd"]},"application/vnd.ciedi":{"source":"iana"},"application/vnd.cinderella":{"source":"iana","extensions":["cdy"]},"application/vnd.cirpack.isdn-ext":{"source":"iana"},"application/vnd.citationstyles.style+xml":{"source":"iana","compressible":true,"extensions":["csl"]},"application/vnd.claymore":{"source":"iana","extensions":["cla"]},"application/vnd.cloanto.rp9":{"source":"iana","extensions":["rp9"]},"application/vnd.clonk.c4group":{"source":"iana","extensions":["c4g","c4d","c4f","c4p","c4u"]},"application/vnd.cluetrust.cartomobile-config":{"source":"iana","extensions":["c11amc"]},"application/vnd.cluetrust.cartomobile-config-pkg":{"source":"iana","extensions":["c11amz"]},"application/vnd.coffeescript":{"source":"iana"},"application/vnd.collabio.xodocuments.document":{"source":"iana"},"application/vnd.collabio.xodocuments.document-template":{"source":"iana"},"application/vnd.collabio.xodocuments.presentation":{"source":"iana"},"application/vnd.collabio.xodocuments.presentation-template":{"source":"iana"},"application/vnd.collabio.xodocuments.spreadsheet":{"source":"iana"},"application/vnd.collabio.xodocuments.spreadsheet-template":{"source":"iana"},"application/vnd.collection+json":{"source":"iana","compressible":true},"application/vnd.collection.doc+json":{"source":"iana","compressible":true},"application/vnd.collection.next+json":{"source":"iana","compressible":true},"application/vnd.comicbook+zip":{"source":"iana","compressible":false},"application/vnd.comicbook-rar":{"source":"iana"},"application/vnd.commerce-battelle":{"source":"iana"},"application/vnd.commonspace":{"source":"iana","extensions":["csp"]},"application/vnd.contact.cmsg":{"source":"iana","extensions":["cdbcmsg"]},"application/vnd.coreos.ignition+json":{"source":"iana","compressible":true},"application/vnd.cosmocaller":{"source":"iana","extensions":["cmc"]},"application/vnd.crick.clicker":{"source":"iana","extensions":["clkx"]},"application/vnd.crick.clicker.keyboard":{"source":"iana","extensions":["clkk"]},"application/vnd.crick.clicker.palette":{"source":"iana","extensions":["clkp"]},"application/vnd.crick.clicker.template":{"source":"iana","extensions":["clkt"]},"application/vnd.crick.clicker.wordbank":{"source":"iana","extensions":["clkw"]},"application/vnd.criticaltools.wbs+xml":{"source":"iana","compressible":true,"extensions":["wbs"]},"application/vnd.cryptii.pipe+json":{"source":"iana","compressible":true},"application/vnd.crypto-shade-file":{"source":"iana"},"application/vnd.ctc-posml":{"source":"iana","extensions":["pml"]},"application/vnd.ctct.ws+xml":{"source":"iana","compressible":true},"application/vnd.cups-pdf":{"source":"iana"},"application/vnd.cups-postscript":{"source":"iana"},"application/vnd.cups-ppd":{"source":"iana","extensions":["ppd"]},"application/vnd.cups-raster":{"source":"iana"},"application/vnd.cups-raw":{"source":"iana"},"application/vnd.curl":{"source":"iana"},"application/vnd.curl.car":{"source":"apache","extensions":["car"]},"application/vnd.curl.pcurl":{"source":"apache","extensions":["pcurl"]},"application/vnd.cyan.dean.root+xml":{"source":"iana","compressible":true},"application/vnd.cybank":{"source":"iana"},"application/vnd.d2l.coursepackage1p0+zip":{"source":"iana","compressible":false},"application/vnd.dart":{"source":"iana","compressible":true,"extensions":["dart"]},"application/vnd.data-vision.rdz":{"source":"iana","extensions":["rdz"]},"application/vnd.datapackage+json":{"source":"iana","compressible":true},"application/vnd.dataresource+json":{"source":"iana","compressible":true},"application/vnd.dbf":{"source":"iana"},"application/vnd.debian.binary-package":{"source":"iana"},"application/vnd.dece.data":{"source":"iana","extensions":["uvf","uvvf","uvd","uvvd"]},"application/vnd.dece.ttml+xml":{"source":"iana","compressible":true,"extensions":["uvt","uvvt"]},"application/vnd.dece.unspecified":{"source":"iana","extensions":["uvx","uvvx"]},"application/vnd.dece.zip":{"source":"iana","extensions":["uvz","uvvz"]},"application/vnd.denovo.fcselayout-link":{"source":"iana","extensions":["fe_launch"]},"application/vnd.desmume.movie":{"source":"iana"},"application/vnd.dir-bi.plate-dl-nosuffix":{"source":"iana"},"application/vnd.dm.delegation+xml":{"source":"iana","compressible":true},"application/vnd.dna":{"source":"iana","extensions":["dna"]},"application/vnd.document+json":{"source":"iana","compressible":true},"application/vnd.dolby.mlp":{"source":"apache","extensions":["mlp"]},"application/vnd.dolby.mobile.1":{"source":"iana"},"application/vnd.dolby.mobile.2":{"source":"iana"},"application/vnd.doremir.scorecloud-binary-document":{"source":"iana"},"application/vnd.dpgraph":{"source":"iana","extensions":["dpg"]},"application/vnd.dreamfactory":{"source":"iana","extensions":["dfac"]},"application/vnd.drive+json":{"source":"iana","compressible":true},"application/vnd.ds-keypoint":{"source":"apache","extensions":["kpxx"]},"application/vnd.dtg.local":{"source":"iana"},"application/vnd.dtg.local.flash":{"source":"iana"},"application/vnd.dtg.local.html":{"source":"iana"},"application/vnd.dvb.ait":{"source":"iana","extensions":["ait"]},"application/vnd.dvb.dvbisl+xml":{"source":"iana","compressible":true},"application/vnd.dvb.dvbj":{"source":"iana"},"application/vnd.dvb.esgcontainer":{"source":"iana"},"application/vnd.dvb.ipdcdftnotifaccess":{"source":"iana"},"application/vnd.dvb.ipdcesgaccess":{"source":"iana"},"application/vnd.dvb.ipdcesgaccess2":{"source":"iana"},"application/vnd.dvb.ipdcesgpdd":{"source":"iana"},"application/vnd.dvb.ipdcroaming":{"source":"iana"},"application/vnd.dvb.iptv.alfec-base":{"source":"iana"},"application/vnd.dvb.iptv.alfec-enhancement":{"source":"iana"},"application/vnd.dvb.notif-aggregate-root+xml":{"source":"iana","compressible":true},"application/vnd.dvb.notif-container+xml":{"source":"iana","compressible":true},"application/vnd.dvb.notif-generic+xml":{"source":"iana","compressible":true},"application/vnd.dvb.notif-ia-msglist+xml":{"source":"iana","compressible":true},"application/vnd.dvb.notif-ia-registration-request+xml":{"source":"iana","compressible":true},"application/vnd.dvb.notif-ia-registration-response+xml":{"source":"iana","compressible":true},"application/vnd.dvb.notif-init+xml":{"source":"iana","compressible":true},"application/vnd.dvb.pfr":{"source":"iana"},"application/vnd.dvb.service":{"source":"iana","extensions":["svc"]},"application/vnd.dxr":{"source":"iana"},"application/vnd.dynageo":{"source":"iana","extensions":["geo"]},"application/vnd.dzr":{"source":"iana"},"application/vnd.easykaraoke.cdgdownload":{"source":"iana"},"application/vnd.ecdis-update":{"source":"iana"},"application/vnd.ecip.rlp":{"source":"iana"},"application/vnd.ecowin.chart":{"source":"iana","extensions":["mag"]},"application/vnd.ecowin.filerequest":{"source":"iana"},"application/vnd.ecowin.fileupdate":{"source":"iana"},"application/vnd.ecowin.series":{"source":"iana"},"application/vnd.ecowin.seriesrequest":{"source":"iana"},"application/vnd.ecowin.seriesupdate":{"source":"iana"},"application/vnd.efi.img":{"source":"iana"},"application/vnd.efi.iso":{"source":"iana"},"application/vnd.emclient.accessrequest+xml":{"source":"iana","compressible":true},"application/vnd.enliven":{"source":"iana","extensions":["nml"]},"application/vnd.enphase.envoy":{"source":"iana"},"application/vnd.eprints.data+xml":{"source":"iana","compressible":true},"application/vnd.epson.esf":{"source":"iana","extensions":["esf"]},"application/vnd.epson.msf":{"source":"iana","extensions":["msf"]},"application/vnd.epson.quickanime":{"source":"iana","extensions":["qam"]},"application/vnd.epson.salt":{"source":"iana","extensions":["slt"]},"application/vnd.epson.ssf":{"source":"iana","extensions":["ssf"]},"application/vnd.ericsson.quickcall":{"source":"iana"},"application/vnd.espass-espass+zip":{"source":"iana","compressible":false},"application/vnd.eszigno3+xml":{"source":"iana","compressible":true,"extensions":["es3","et3"]},"application/vnd.etsi.aoc+xml":{"source":"iana","compressible":true},"application/vnd.etsi.asic-e+zip":{"source":"iana","compressible":false},"application/vnd.etsi.asic-s+zip":{"source":"iana","compressible":false},"application/vnd.etsi.cug+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvcommand+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvdiscovery+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvprofile+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvsad-bc+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvsad-cod+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvsad-npvr+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvservice+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvsync+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvueprofile+xml":{"source":"iana","compressible":true},"application/vnd.etsi.mcid+xml":{"source":"iana","compressible":true},"application/vnd.etsi.mheg5":{"source":"iana"},"application/vnd.etsi.overload-control-policy-dataset+xml":{"source":"iana","compressible":true},"application/vnd.etsi.pstn+xml":{"source":"iana","compressible":true},"application/vnd.etsi.sci+xml":{"source":"iana","compressible":true},"application/vnd.etsi.simservs+xml":{"source":"iana","compressible":true},"application/vnd.etsi.timestamp-token":{"source":"iana"},"application/vnd.etsi.tsl+xml":{"source":"iana","compressible":true},"application/vnd.etsi.tsl.der":{"source":"iana"},"application/vnd.eudora.data":{"source":"iana"},"application/vnd.evolv.ecig.profile":{"source":"iana"},"application/vnd.evolv.ecig.settings":{"source":"iana"},"application/vnd.evolv.ecig.theme":{"source":"iana"},"application/vnd.exstream-empower+zip":{"source":"iana","compressible":false},"application/vnd.exstream-package":{"source":"iana"},"application/vnd.ezpix-album":{"source":"iana","extensions":["ez2"]},"application/vnd.ezpix-package":{"source":"iana","extensions":["ez3"]},"application/vnd.f-secure.mobile":{"source":"iana"},"application/vnd.fastcopy-disk-image":{"source":"iana"},"application/vnd.fdf":{"source":"iana","extensions":["fdf"]},"application/vnd.fdsn.mseed":{"source":"iana","extensions":["mseed"]},"application/vnd.fdsn.seed":{"source":"iana","extensions":["seed","dataless"]},"application/vnd.ffsns":{"source":"iana"},"application/vnd.ficlab.flb+zip":{"source":"iana","compressible":false},"application/vnd.filmit.zfc":{"source":"iana"},"application/vnd.fints":{"source":"iana"},"application/vnd.firemonkeys.cloudcell":{"source":"iana"},"application/vnd.flographit":{"source":"iana","extensions":["gph"]},"application/vnd.fluxtime.clip":{"source":"iana","extensions":["ftc"]},"application/vnd.font-fontforge-sfd":{"source":"iana"},"application/vnd.framemaker":{"source":"iana","extensions":["fm","frame","maker","book"]},"application/vnd.frogans.fnc":{"source":"iana","extensions":["fnc"]},"application/vnd.frogans.ltf":{"source":"iana","extensions":["ltf"]},"application/vnd.fsc.weblaunch":{"source":"iana","extensions":["fsc"]},"application/vnd.fujitsu.oasys":{"source":"iana","extensions":["oas"]},"application/vnd.fujitsu.oasys2":{"source":"iana","extensions":["oa2"]},"application/vnd.fujitsu.oasys3":{"source":"iana","extensions":["oa3"]},"application/vnd.fujitsu.oasysgp":{"source":"iana","extensions":["fg5"]},"application/vnd.fujitsu.oasysprs":{"source":"iana","extensions":["bh2"]},"application/vnd.fujixerox.art-ex":{"source":"iana"},"application/vnd.fujixerox.art4":{"source":"iana"},"application/vnd.fujixerox.ddd":{"source":"iana","extensions":["ddd"]},"application/vnd.fujixerox.docuworks":{"source":"iana","extensions":["xdw"]},"application/vnd.fujixerox.docuworks.binder":{"source":"iana","extensions":["xbd"]},"application/vnd.fujixerox.docuworks.container":{"source":"iana"},"application/vnd.fujixerox.hbpl":{"source":"iana"},"application/vnd.fut-misnet":{"source":"iana"},"application/vnd.futoin+cbor":{"source":"iana"},"application/vnd.futoin+json":{"source":"iana","compressible":true},"application/vnd.fuzzysheet":{"source":"iana","extensions":["fzs"]},"application/vnd.genomatix.tuxedo":{"source":"iana","extensions":["txd"]},"application/vnd.gentics.grd+json":{"source":"iana","compressible":true},"application/vnd.geo+json":{"source":"iana","compressible":true},"application/vnd.geocube+xml":{"source":"iana","compressible":true},"application/vnd.geogebra.file":{"source":"iana","extensions":["ggb"]},"application/vnd.geogebra.tool":{"source":"iana","extensions":["ggt"]},"application/vnd.geometry-explorer":{"source":"iana","extensions":["gex","gre"]},"application/vnd.geonext":{"source":"iana","extensions":["gxt"]},"application/vnd.geoplan":{"source":"iana","extensions":["g2w"]},"application/vnd.geospace":{"source":"iana","extensions":["g3w"]},"application/vnd.gerber":{"source":"iana"},"application/vnd.globalplatform.card-content-mgt":{"source":"iana"},"application/vnd.globalplatform.card-content-mgt-response":{"source":"iana"},"application/vnd.gmx":{"source":"iana","extensions":["gmx"]},"application/vnd.google-apps.document":{"compressible":false,"extensions":["gdoc"]},"application/vnd.google-apps.presentation":{"compressible":false,"extensions":["gslides"]},"application/vnd.google-apps.spreadsheet":{"compressible":false,"extensions":["gsheet"]},"application/vnd.google-earth.kml+xml":{"source":"iana","compressible":true,"extensions":["kml"]},"application/vnd.google-earth.kmz":{"source":"iana","compressible":false,"extensions":["kmz"]},"application/vnd.gov.sk.e-form+xml":{"source":"iana","compressible":true},"application/vnd.gov.sk.e-form+zip":{"source":"iana","compressible":false},"application/vnd.gov.sk.xmldatacontainer+xml":{"source":"iana","compressible":true},"application/vnd.grafeq":{"source":"iana","extensions":["gqf","gqs"]},"application/vnd.gridmp":{"source":"iana"},"application/vnd.groove-account":{"source":"iana","extensions":["gac"]},"application/vnd.groove-help":{"source":"iana","extensions":["ghf"]},"application/vnd.groove-identity-message":{"source":"iana","extensions":["gim"]},"application/vnd.groove-injector":{"source":"iana","extensions":["grv"]},"application/vnd.groove-tool-message":{"source":"iana","extensions":["gtm"]},"application/vnd.groove-tool-template":{"source":"iana","extensions":["tpl"]},"application/vnd.groove-vcard":{"source":"iana","extensions":["vcg"]},"application/vnd.hal+json":{"source":"iana","compressible":true},"application/vnd.hal+xml":{"source":"iana","compressible":true,"extensions":["hal"]},"application/vnd.handheld-entertainment+xml":{"source":"iana","compressible":true,"extensions":["zmm"]},"application/vnd.hbci":{"source":"iana","extensions":["hbci"]},"application/vnd.hc+json":{"source":"iana","compressible":true},"application/vnd.hcl-bireports":{"source":"iana"},"application/vnd.hdt":{"source":"iana"},"application/vnd.heroku+json":{"source":"iana","compressible":true},"application/vnd.hhe.lesson-player":{"source":"iana","extensions":["les"]},"application/vnd.hp-hpgl":{"source":"iana","extensions":["hpgl"]},"application/vnd.hp-hpid":{"source":"iana","extensions":["hpid"]},"application/vnd.hp-hps":{"source":"iana","extensions":["hps"]},"application/vnd.hp-jlyt":{"source":"iana","extensions":["jlt"]},"application/vnd.hp-pcl":{"source":"iana","extensions":["pcl"]},"application/vnd.hp-pclxl":{"source":"iana","extensions":["pclxl"]},"application/vnd.httphone":{"source":"iana"},"application/vnd.hydrostatix.sof-data":{"source":"iana","extensions":["sfd-hdstx"]},"application/vnd.hyper+json":{"source":"iana","compressible":true},"application/vnd.hyper-item+json":{"source":"iana","compressible":true},"application/vnd.hyperdrive+json":{"source":"iana","compressible":true},"application/vnd.hzn-3d-crossword":{"source":"iana"},"application/vnd.ibm.afplinedata":{"source":"iana"},"application/vnd.ibm.electronic-media":{"source":"iana"},"application/vnd.ibm.minipay":{"source":"iana","extensions":["mpy"]},"application/vnd.ibm.modcap":{"source":"iana","extensions":["afp","listafp","list3820"]},"application/vnd.ibm.rights-management":{"source":"iana","extensions":["irm"]},"application/vnd.ibm.secure-container":{"source":"iana","extensions":["sc"]},"application/vnd.iccprofile":{"source":"iana","extensions":["icc","icm"]},"application/vnd.ieee.1905":{"source":"iana"},"application/vnd.igloader":{"source":"iana","extensions":["igl"]},"application/vnd.imagemeter.folder+zip":{"source":"iana","compressible":false},"application/vnd.imagemeter.image+zip":{"source":"iana","compressible":false},"application/vnd.immervision-ivp":{"source":"iana","extensions":["ivp"]},"application/vnd.immervision-ivu":{"source":"iana","extensions":["ivu"]},"application/vnd.ims.imsccv1p1":{"source":"iana"},"application/vnd.ims.imsccv1p2":{"source":"iana"},"application/vnd.ims.imsccv1p3":{"source":"iana"},"application/vnd.ims.lis.v2.result+json":{"source":"iana","compressible":true},"application/vnd.ims.lti.v2.toolconsumerprofile+json":{"source":"iana","compressible":true},"application/vnd.ims.lti.v2.toolproxy+json":{"source":"iana","compressible":true},"application/vnd.ims.lti.v2.toolproxy.id+json":{"source":"iana","compressible":true},"application/vnd.ims.lti.v2.toolsettings+json":{"source":"iana","compressible":true},"application/vnd.ims.lti.v2.toolsettings.simple+json":{"source":"iana","compressible":true},"application/vnd.informedcontrol.rms+xml":{"source":"iana","compressible":true},"application/vnd.informix-visionary":{"source":"iana"},"application/vnd.infotech.project":{"source":"iana"},"application/vnd.infotech.project+xml":{"source":"iana","compressible":true},"application/vnd.innopath.wamp.notification":{"source":"iana"},"application/vnd.insors.igm":{"source":"iana","extensions":["igm"]},"application/vnd.intercon.formnet":{"source":"iana","extensions":["xpw","xpx"]},"application/vnd.intergeo":{"source":"iana","extensions":["i2g"]},"application/vnd.intertrust.digibox":{"source":"iana"},"application/vnd.intertrust.nncp":{"source":"iana"},"application/vnd.intu.qbo":{"source":"iana","extensions":["qbo"]},"application/vnd.intu.qfx":{"source":"iana","extensions":["qfx"]},"application/vnd.iptc.g2.catalogitem+xml":{"source":"iana","compressible":true},"application/vnd.iptc.g2.conceptitem+xml":{"source":"iana","compressible":true},"application/vnd.iptc.g2.knowledgeitem+xml":{"source":"iana","compressible":true},"application/vnd.iptc.g2.newsitem+xml":{"source":"iana","compressible":true},"application/vnd.iptc.g2.newsmessage+xml":{"source":"iana","compressible":true},"application/vnd.iptc.g2.packageitem+xml":{"source":"iana","compressible":true},"application/vnd.iptc.g2.planningitem+xml":{"source":"iana","compressible":true},"application/vnd.ipunplugged.rcprofile":{"source":"iana","extensions":["rcprofile"]},"application/vnd.irepository.package+xml":{"source":"iana","compressible":true,"extensions":["irp"]},"application/vnd.is-xpr":{"source":"iana","extensions":["xpr"]},"application/vnd.isac.fcs":{"source":"iana","extensions":["fcs"]},"application/vnd.iso11783-10+zip":{"source":"iana","compressible":false},"application/vnd.jam":{"source":"iana","extensions":["jam"]},"application/vnd.japannet-directory-service":{"source":"iana"},"application/vnd.japannet-jpnstore-wakeup":{"source":"iana"},"application/vnd.japannet-payment-wakeup":{"source":"iana"},"application/vnd.japannet-registration":{"source":"iana"},"application/vnd.japannet-registration-wakeup":{"source":"iana"},"application/vnd.japannet-setstore-wakeup":{"source":"iana"},"application/vnd.japannet-verification":{"source":"iana"},"application/vnd.japannet-verification-wakeup":{"source":"iana"},"application/vnd.jcp.javame.midlet-rms":{"source":"iana","extensions":["rms"]},"application/vnd.jisp":{"source":"iana","extensions":["jisp"]},"application/vnd.joost.joda-archive":{"source":"iana","extensions":["joda"]},"application/vnd.jsk.isdn-ngn":{"source":"iana"},"application/vnd.kahootz":{"source":"iana","extensions":["ktz","ktr"]},"application/vnd.kde.karbon":{"source":"iana","extensions":["karbon"]},"application/vnd.kde.kchart":{"source":"iana","extensions":["chrt"]},"application/vnd.kde.kformula":{"source":"iana","extensions":["kfo"]},"application/vnd.kde.kivio":{"source":"iana","extensions":["flw"]},"application/vnd.kde.kontour":{"source":"iana","extensions":["kon"]},"application/vnd.kde.kpresenter":{"source":"iana","extensions":["kpr","kpt"]},"application/vnd.kde.kspread":{"source":"iana","extensions":["ksp"]},"application/vnd.kde.kword":{"source":"iana","extensions":["kwd","kwt"]},"application/vnd.kenameaapp":{"source":"iana","extensions":["htke"]},"application/vnd.kidspiration":{"source":"iana","extensions":["kia"]},"application/vnd.kinar":{"source":"iana","extensions":["kne","knp"]},"application/vnd.koan":{"source":"iana","extensions":["skp","skd","skt","skm"]},"application/vnd.kodak-descriptor":{"source":"iana","extensions":["sse"]},"application/vnd.las":{"source":"iana"},"application/vnd.las.las+json":{"source":"iana","compressible":true},"application/vnd.las.las+xml":{"source":"iana","compressible":true,"extensions":["lasxml"]},"application/vnd.laszip":{"source":"iana"},"application/vnd.leap+json":{"source":"iana","compressible":true},"application/vnd.liberty-request+xml":{"source":"iana","compressible":true},"application/vnd.llamagraphics.life-balance.desktop":{"source":"iana","extensions":["lbd"]},"application/vnd.llamagraphics.life-balance.exchange+xml":{"source":"iana","compressible":true,"extensions":["lbe"]},"application/vnd.logipipe.circuit+zip":{"source":"iana","compressible":false},"application/vnd.loom":{"source":"iana"},"application/vnd.lotus-1-2-3":{"source":"iana","extensions":["123"]},"application/vnd.lotus-approach":{"source":"iana","extensions":["apr"]},"application/vnd.lotus-freelance":{"source":"iana","extensions":["pre"]},"application/vnd.lotus-notes":{"source":"iana","extensions":["nsf"]},"application/vnd.lotus-organizer":{"source":"iana","extensions":["org"]},"application/vnd.lotus-screencam":{"source":"iana","extensions":["scm"]},"application/vnd.lotus-wordpro":{"source":"iana","extensions":["lwp"]},"application/vnd.macports.portpkg":{"source":"iana","extensions":["portpkg"]},"application/vnd.mapbox-vector-tile":{"source":"iana"},"application/vnd.marlin.drm.actiontoken+xml":{"source":"iana","compressible":true},"application/vnd.marlin.drm.conftoken+xml":{"source":"iana","compressible":true},"application/vnd.marlin.drm.license+xml":{"source":"iana","compressible":true},"application/vnd.marlin.drm.mdcf":{"source":"iana"},"application/vnd.mason+json":{"source":"iana","compressible":true},"application/vnd.maxmind.maxmind-db":{"source":"iana"},"application/vnd.mcd":{"source":"iana","extensions":["mcd"]},"application/vnd.medcalcdata":{"source":"iana","extensions":["mc1"]},"application/vnd.mediastation.cdkey":{"source":"iana","extensions":["cdkey"]},"application/vnd.meridian-slingshot":{"source":"iana"},"application/vnd.mfer":{"source":"iana","extensions":["mwf"]},"application/vnd.mfmp":{"source":"iana","extensions":["mfm"]},"application/vnd.micro+json":{"source":"iana","compressible":true},"application/vnd.micrografx.flo":{"source":"iana","extensions":["flo"]},"application/vnd.micrografx.igx":{"source":"iana","extensions":["igx"]},"application/vnd.microsoft.portable-executable":{"source":"iana"},"application/vnd.microsoft.windows.thumbnail-cache":{"source":"iana"},"application/vnd.miele+json":{"source":"iana","compressible":true},"application/vnd.mif":{"source":"iana","extensions":["mif"]},"application/vnd.minisoft-hp3000-save":{"source":"iana"},"application/vnd.mitsubishi.misty-guard.trustweb":{"source":"iana"},"application/vnd.mobius.daf":{"source":"iana","extensions":["daf"]},"application/vnd.mobius.dis":{"source":"iana","extensions":["dis"]},"application/vnd.mobius.mbk":{"source":"iana","extensions":["mbk"]},"application/vnd.mobius.mqy":{"source":"iana","extensions":["mqy"]},"application/vnd.mobius.msl":{"source":"iana","extensions":["msl"]},"application/vnd.mobius.plc":{"source":"iana","extensions":["plc"]},"application/vnd.mobius.txf":{"source":"iana","extensions":["txf"]},"application/vnd.mophun.application":{"source":"iana","extensions":["mpn"]},"application/vnd.mophun.certificate":{"source":"iana","extensions":["mpc"]},"application/vnd.motorola.flexsuite":{"source":"iana"},"application/vnd.motorola.flexsuite.adsi":{"source":"iana"},"application/vnd.motorola.flexsuite.fis":{"source":"iana"},"application/vnd.motorola.flexsuite.gotap":{"source":"iana"},"application/vnd.motorola.flexsuite.kmr":{"source":"iana"},"application/vnd.motorola.flexsuite.ttc":{"source":"iana"},"application/vnd.motorola.flexsuite.wem":{"source":"iana"},"application/vnd.motorola.iprm":{"source":"iana"},"application/vnd.mozilla.xul+xml":{"source":"iana","compressible":true,"extensions":["xul"]},"application/vnd.ms-3mfdocument":{"source":"iana"},"application/vnd.ms-artgalry":{"source":"iana","extensions":["cil"]},"application/vnd.ms-asf":{"source":"iana"},"application/vnd.ms-cab-compressed":{"source":"iana","extensions":["cab"]},"application/vnd.ms-color.iccprofile":{"source":"apache"},"application/vnd.ms-excel":{"source":"iana","compressible":false,"extensions":["xls","xlm","xla","xlc","xlt","xlw"]},"application/vnd.ms-excel.addin.macroenabled.12":{"source":"iana","extensions":["xlam"]},"application/vnd.ms-excel.sheet.binary.macroenabled.12":{"source":"iana","extensions":["xlsb"]},"application/vnd.ms-excel.sheet.macroenabled.12":{"source":"iana","extensions":["xlsm"]},"application/vnd.ms-excel.template.macroenabled.12":{"source":"iana","extensions":["xltm"]},"application/vnd.ms-fontobject":{"source":"iana","compressible":true,"extensions":["eot"]},"application/vnd.ms-htmlhelp":{"source":"iana","extensions":["chm"]},"application/vnd.ms-ims":{"source":"iana","extensions":["ims"]},"application/vnd.ms-lrm":{"source":"iana","extensions":["lrm"]},"application/vnd.ms-office.activex+xml":{"source":"iana","compressible":true},"application/vnd.ms-officetheme":{"source":"iana","extensions":["thmx"]},"application/vnd.ms-opentype":{"source":"apache","compressible":true},"application/vnd.ms-outlook":{"compressible":false,"extensions":["msg"]},"application/vnd.ms-package.obfuscated-opentype":{"source":"apache"},"application/vnd.ms-pki.seccat":{"source":"apache","extensions":["cat"]},"application/vnd.ms-pki.stl":{"source":"apache","extensions":["stl"]},"application/vnd.ms-playready.initiator+xml":{"source":"iana","compressible":true},"application/vnd.ms-powerpoint":{"source":"iana","compressible":false,"extensions":["ppt","pps","pot"]},"application/vnd.ms-powerpoint.addin.macroenabled.12":{"source":"iana","extensions":["ppam"]},"application/vnd.ms-powerpoint.presentation.macroenabled.12":{"source":"iana","extensions":["pptm"]},"application/vnd.ms-powerpoint.slide.macroenabled.12":{"source":"iana","extensions":["sldm"]},"application/vnd.ms-powerpoint.slideshow.macroenabled.12":{"source":"iana","extensions":["ppsm"]},"application/vnd.ms-powerpoint.template.macroenabled.12":{"source":"iana","extensions":["potm"]},"application/vnd.ms-printdevicecapabilities+xml":{"source":"iana","compressible":true},"application/vnd.ms-printing.printticket+xml":{"source":"apache","compressible":true},"application/vnd.ms-printschematicket+xml":{"source":"iana","compressible":true},"application/vnd.ms-project":{"source":"iana","extensions":["mpp","mpt"]},"application/vnd.ms-tnef":{"source":"iana"},"application/vnd.ms-windows.devicepairing":{"source":"iana"},"application/vnd.ms-windows.nwprinting.oob":{"source":"iana"},"application/vnd.ms-windows.printerpairing":{"source":"iana"},"application/vnd.ms-windows.wsd.oob":{"source":"iana"},"application/vnd.ms-wmdrm.lic-chlg-req":{"source":"iana"},"application/vnd.ms-wmdrm.lic-resp":{"source":"iana"},"application/vnd.ms-wmdrm.meter-chlg-req":{"source":"iana"},"application/vnd.ms-wmdrm.meter-resp":{"source":"iana"},"application/vnd.ms-word.document.macroenabled.12":{"source":"iana","extensions":["docm"]},"application/vnd.ms-word.template.macroenabled.12":{"source":"iana","extensions":["dotm"]},"application/vnd.ms-works":{"source":"iana","extensions":["wps","wks","wcm","wdb"]},"application/vnd.ms-wpl":{"source":"iana","extensions":["wpl"]},"application/vnd.ms-xpsdocument":{"source":"iana","compressible":false,"extensions":["xps"]},"application/vnd.msa-disk-image":{"source":"iana"},"application/vnd.mseq":{"source":"iana","extensions":["mseq"]},"application/vnd.msign":{"source":"iana"},"application/vnd.multiad.creator":{"source":"iana"},"application/vnd.multiad.creator.cif":{"source":"iana"},"application/vnd.music-niff":{"source":"iana"},"application/vnd.musician":{"source":"iana","extensions":["mus"]},"application/vnd.muvee.style":{"source":"iana","extensions":["msty"]},"application/vnd.mynfc":{"source":"iana","extensions":["taglet"]},"application/vnd.ncd.control":{"source":"iana"},"application/vnd.ncd.reference":{"source":"iana"},"application/vnd.nearst.inv+json":{"source":"iana","compressible":true},"application/vnd.nervana":{"source":"iana"},"application/vnd.netfpx":{"source":"iana"},"application/vnd.neurolanguage.nlu":{"source":"iana","extensions":["nlu"]},"application/vnd.nimn":{"source":"iana"},"application/vnd.nintendo.nitro.rom":{"source":"iana"},"application/vnd.nintendo.snes.rom":{"source":"iana"},"application/vnd.nitf":{"source":"iana","extensions":["ntf","nitf"]},"application/vnd.noblenet-directory":{"source":"iana","extensions":["nnd"]},"application/vnd.noblenet-sealer":{"source":"iana","extensions":["nns"]},"application/vnd.noblenet-web":{"source":"iana","extensions":["nnw"]},"application/vnd.nokia.catalogs":{"source":"iana"},"application/vnd.nokia.conml+wbxml":{"source":"iana"},"application/vnd.nokia.conml+xml":{"source":"iana","compressible":true},"application/vnd.nokia.iptv.config+xml":{"source":"iana","compressible":true},"application/vnd.nokia.isds-radio-presets":{"source":"iana"},"application/vnd.nokia.landmark+wbxml":{"source":"iana"},"application/vnd.nokia.landmark+xml":{"source":"iana","compressible":true},"application/vnd.nokia.landmarkcollection+xml":{"source":"iana","compressible":true},"application/vnd.nokia.n-gage.ac+xml":{"source":"iana","compressible":true,"extensions":["ac"]},"application/vnd.nokia.n-gage.data":{"source":"iana","extensions":["ngdat"]},"application/vnd.nokia.n-gage.symbian.install":{"source":"iana","extensions":["n-gage"]},"application/vnd.nokia.ncd":{"source":"iana"},"application/vnd.nokia.pcd+wbxml":{"source":"iana"},"application/vnd.nokia.pcd+xml":{"source":"iana","compressible":true},"application/vnd.nokia.radio-preset":{"source":"iana","extensions":["rpst"]},"application/vnd.nokia.radio-presets":{"source":"iana","extensions":["rpss"]},"application/vnd.novadigm.edm":{"source":"iana","extensions":["edm"]},"application/vnd.novadigm.edx":{"source":"iana","extensions":["edx"]},"application/vnd.novadigm.ext":{"source":"iana","extensions":["ext"]},"application/vnd.ntt-local.content-share":{"source":"iana"},"application/vnd.ntt-local.file-transfer":{"source":"iana"},"application/vnd.ntt-local.ogw_remote-access":{"source":"iana"},"application/vnd.ntt-local.sip-ta_remote":{"source":"iana"},"application/vnd.ntt-local.sip-ta_tcp_stream":{"source":"iana"},"application/vnd.oasis.opendocument.chart":{"source":"iana","extensions":["odc"]},"application/vnd.oasis.opendocument.chart-template":{"source":"iana","extensions":["otc"]},"application/vnd.oasis.opendocument.database":{"source":"iana","extensions":["odb"]},"application/vnd.oasis.opendocument.formula":{"source":"iana","extensions":["odf"]},"application/vnd.oasis.opendocument.formula-template":{"source":"iana","extensions":["odft"]},"application/vnd.oasis.opendocument.graphics":{"source":"iana","compressible":false,"extensions":["odg"]},"application/vnd.oasis.opendocument.graphics-template":{"source":"iana","extensions":["otg"]},"application/vnd.oasis.opendocument.image":{"source":"iana","extensions":["odi"]},"application/vnd.oasis.opendocument.image-template":{"source":"iana","extensions":["oti"]},"application/vnd.oasis.opendocument.presentation":{"source":"iana","compressible":false,"extensions":["odp"]},"application/vnd.oasis.opendocument.presentation-template":{"source":"iana","extensions":["otp"]},"application/vnd.oasis.opendocument.spreadsheet":{"source":"iana","compressible":false,"extensions":["ods"]},"application/vnd.oasis.opendocument.spreadsheet-template":{"source":"iana","extensions":["ots"]},"application/vnd.oasis.opendocument.text":{"source":"iana","compressible":false,"extensions":["odt"]},"application/vnd.oasis.opendocument.text-master":{"source":"iana","extensions":["odm"]},"application/vnd.oasis.opendocument.text-template":{"source":"iana","extensions":["ott"]},"application/vnd.oasis.opendocument.text-web":{"source":"iana","extensions":["oth"]},"application/vnd.obn":{"source":"iana"},"application/vnd.ocf+cbor":{"source":"iana"},"application/vnd.oci.image.manifest.v1+json":{"source":"iana","compressible":true},"application/vnd.oftn.l10n+json":{"source":"iana","compressible":true},"application/vnd.oipf.contentaccessdownload+xml":{"source":"iana","compressible":true},"application/vnd.oipf.contentaccessstreaming+xml":{"source":"iana","compressible":true},"application/vnd.oipf.cspg-hexbinary":{"source":"iana"},"application/vnd.oipf.dae.svg+xml":{"source":"iana","compressible":true},"application/vnd.oipf.dae.xhtml+xml":{"source":"iana","compressible":true},"application/vnd.oipf.mippvcontrolmessage+xml":{"source":"iana","compressible":true},"application/vnd.oipf.pae.gem":{"source":"iana"},"application/vnd.oipf.spdiscovery+xml":{"source":"iana","compressible":true},"application/vnd.oipf.spdlist+xml":{"source":"iana","compressible":true},"application/vnd.oipf.ueprofile+xml":{"source":"iana","compressible":true},"application/vnd.oipf.userprofile+xml":{"source":"iana","compressible":true},"application/vnd.olpc-sugar":{"source":"iana","extensions":["xo"]},"application/vnd.oma-scws-config":{"source":"iana"},"application/vnd.oma-scws-http-request":{"source":"iana"},"application/vnd.oma-scws-http-response":{"source":"iana"},"application/vnd.oma.bcast.associated-procedure-parameter+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.drm-trigger+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.imd+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.ltkm":{"source":"iana"},"application/vnd.oma.bcast.notification+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.provisioningtrigger":{"source":"iana"},"application/vnd.oma.bcast.sgboot":{"source":"iana"},"application/vnd.oma.bcast.sgdd+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.sgdu":{"source":"iana"},"application/vnd.oma.bcast.simple-symbol-container":{"source":"iana"},"application/vnd.oma.bcast.smartcard-trigger+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.sprov+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.stkm":{"source":"iana"},"application/vnd.oma.cab-address-book+xml":{"source":"iana","compressible":true},"application/vnd.oma.cab-feature-handler+xml":{"source":"iana","compressible":true},"application/vnd.oma.cab-pcc+xml":{"source":"iana","compressible":true},"application/vnd.oma.cab-subs-invite+xml":{"source":"iana","compressible":true},"application/vnd.oma.cab-user-prefs+xml":{"source":"iana","compressible":true},"application/vnd.oma.dcd":{"source":"iana"},"application/vnd.oma.dcdc":{"source":"iana"},"application/vnd.oma.dd2+xml":{"source":"iana","compressible":true,"extensions":["dd2"]},"application/vnd.oma.drm.risd+xml":{"source":"iana","compressible":true},"application/vnd.oma.group-usage-list+xml":{"source":"iana","compressible":true},"application/vnd.oma.lwm2m+json":{"source":"iana","compressible":true},"application/vnd.oma.lwm2m+tlv":{"source":"iana"},"application/vnd.oma.pal+xml":{"source":"iana","compressible":true},"application/vnd.oma.poc.detailed-progress-report+xml":{"source":"iana","compressible":true},"application/vnd.oma.poc.final-report+xml":{"source":"iana","compressible":true},"application/vnd.oma.poc.groups+xml":{"source":"iana","compressible":true},"application/vnd.oma.poc.invocation-descriptor+xml":{"source":"iana","compressible":true},"application/vnd.oma.poc.optimized-progress-report+xml":{"source":"iana","compressible":true},"application/vnd.oma.push":{"source":"iana"},"application/vnd.oma.scidm.messages+xml":{"source":"iana","compressible":true},"application/vnd.oma.xcap-directory+xml":{"source":"iana","compressible":true},"application/vnd.omads-email+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/vnd.omads-file+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/vnd.omads-folder+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/vnd.omaloc-supl-init":{"source":"iana"},"application/vnd.onepager":{"source":"iana"},"application/vnd.onepagertamp":{"source":"iana"},"application/vnd.onepagertamx":{"source":"iana"},"application/vnd.onepagertat":{"source":"iana"},"application/vnd.onepagertatp":{"source":"iana"},"application/vnd.onepagertatx":{"source":"iana"},"application/vnd.openblox.game+xml":{"source":"iana","compressible":true,"extensions":["obgx"]},"application/vnd.openblox.game-binary":{"source":"iana"},"application/vnd.openeye.oeb":{"source":"iana"},"application/vnd.openofficeorg.extension":{"source":"apache","extensions":["oxt"]},"application/vnd.openstreetmap.data+xml":{"source":"iana","compressible":true,"extensions":["osm"]},"application/vnd.openxmlformats-officedocument.custom-properties+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.customxmlproperties+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawing+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawingml.chart+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawingml.diagramdata+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.extended-properties+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.commentauthors+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.comments+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.notesmaster+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.notesslide+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.presentation":{"source":"iana","compressible":false,"extensions":["pptx"]},"application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.presprops+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.slide":{"source":"iana","extensions":["sldx"]},"application/vnd.openxmlformats-officedocument.presentationml.slide+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.slidelayout+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.slidemaster+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.slideshow":{"source":"iana","extensions":["ppsx"]},"application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.tablestyles+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.tags+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.template":{"source":"iana","extensions":["potx"]},"application/vnd.openxmlformats-officedocument.presentationml.template.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.viewprops+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":{"source":"iana","compressible":false,"extensions":["xlsx"]},"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.template":{"source":"iana","extensions":["xltx"]},"application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.theme+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.themeoverride+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.vmldrawing":{"source":"iana"},"application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.document":{"source":"iana","compressible":false,"extensions":["docx"]},"application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.template":{"source":"iana","extensions":["dotx"]},"application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-package.core-properties+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-package.relationships+xml":{"source":"iana","compressible":true},"application/vnd.oracle.resource+json":{"source":"iana","compressible":true},"application/vnd.orange.indata":{"source":"iana"},"application/vnd.osa.netdeploy":{"source":"iana"},"application/vnd.osgeo.mapguide.package":{"source":"iana","extensions":["mgp"]},"application/vnd.osgi.bundle":{"source":"iana"},"application/vnd.osgi.dp":{"source":"iana","extensions":["dp"]},"application/vnd.osgi.subsystem":{"source":"iana","extensions":["esa"]},"application/vnd.otps.ct-kip+xml":{"source":"iana","compressible":true},"application/vnd.oxli.countgraph":{"source":"iana"},"application/vnd.pagerduty+json":{"source":"iana","compressible":true},"application/vnd.palm":{"source":"iana","extensions":["pdb","pqa","oprc"]},"application/vnd.panoply":{"source":"iana"},"application/vnd.paos.xml":{"source":"iana"},"application/vnd.patentdive":{"source":"iana"},"application/vnd.patientecommsdoc":{"source":"iana"},"application/vnd.pawaafile":{"source":"iana","extensions":["paw"]},"application/vnd.pcos":{"source":"iana"},"application/vnd.pg.format":{"source":"iana","extensions":["str"]},"application/vnd.pg.osasli":{"source":"iana","extensions":["ei6"]},"application/vnd.piaccess.application-licence":{"source":"iana"},"application/vnd.picsel":{"source":"iana","extensions":["efif"]},"application/vnd.pmi.widget":{"source":"iana","extensions":["wg"]},"application/vnd.poc.group-advertisement+xml":{"source":"iana","compressible":true},"application/vnd.pocketlearn":{"source":"iana","extensions":["plf"]},"application/vnd.powerbuilder6":{"source":"iana","extensions":["pbd"]},"application/vnd.powerbuilder6-s":{"source":"iana"},"application/vnd.powerbuilder7":{"source":"iana"},"application/vnd.powerbuilder7-s":{"source":"iana"},"application/vnd.powerbuilder75":{"source":"iana"},"application/vnd.powerbuilder75-s":{"source":"iana"},"application/vnd.preminet":{"source":"iana"},"application/vnd.previewsystems.box":{"source":"iana","extensions":["box"]},"application/vnd.proteus.magazine":{"source":"iana","extensions":["mgz"]},"application/vnd.psfs":{"source":"iana"},"application/vnd.publishare-delta-tree":{"source":"iana","extensions":["qps"]},"application/vnd.pvi.ptid1":{"source":"iana","extensions":["ptid"]},"application/vnd.pwg-multiplexed":{"source":"iana"},"application/vnd.pwg-xhtml-print+xml":{"source":"iana","compressible":true},"application/vnd.qualcomm.brew-app-res":{"source":"iana"},"application/vnd.quarantainenet":{"source":"iana"},"application/vnd.quark.quarkxpress":{"source":"iana","extensions":["qxd","qxt","qwd","qwt","qxl","qxb"]},"application/vnd.quobject-quoxdocument":{"source":"iana"},"application/vnd.radisys.moml+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-audit+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-audit-conf+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-audit-conn+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-audit-dialog+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-audit-stream+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-conf+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog-base+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog-fax-detect+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog-fax-sendrecv+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog-group+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog-speech+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog-transform+xml":{"source":"iana","compressible":true},"application/vnd.rainstor.data":{"source":"iana"},"application/vnd.rapid":{"source":"iana"},"application/vnd.rar":{"source":"iana"},"application/vnd.realvnc.bed":{"source":"iana","extensions":["bed"]},"application/vnd.recordare.musicxml":{"source":"iana","extensions":["mxl"]},"application/vnd.recordare.musicxml+xml":{"source":"iana","compressible":true,"extensions":["musicxml"]},"application/vnd.renlearn.rlprint":{"source":"iana"},"application/vnd.restful+json":{"source":"iana","compressible":true},"application/vnd.rig.cryptonote":{"source":"iana","extensions":["cryptonote"]},"application/vnd.rim.cod":{"source":"apache","extensions":["cod"]},"application/vnd.rn-realmedia":{"source":"apache","extensions":["rm"]},"application/vnd.rn-realmedia-vbr":{"source":"apache","extensions":["rmvb"]},"application/vnd.route66.link66+xml":{"source":"iana","compressible":true,"extensions":["link66"]},"application/vnd.rs-274x":{"source":"iana"},"application/vnd.ruckus.download":{"source":"iana"},"application/vnd.s3sms":{"source":"iana"},"application/vnd.sailingtracker.track":{"source":"iana","extensions":["st"]},"application/vnd.sar":{"source":"iana"},"application/vnd.sbm.cid":{"source":"iana"},"application/vnd.sbm.mid2":{"source":"iana"},"application/vnd.scribus":{"source":"iana"},"application/vnd.sealed.3df":{"source":"iana"},"application/vnd.sealed.csf":{"source":"iana"},"application/vnd.sealed.doc":{"source":"iana"},"application/vnd.sealed.eml":{"source":"iana"},"application/vnd.sealed.mht":{"source":"iana"},"application/vnd.sealed.net":{"source":"iana"},"application/vnd.sealed.ppt":{"source":"iana"},"application/vnd.sealed.tiff":{"source":"iana"},"application/vnd.sealed.xls":{"source":"iana"},"application/vnd.sealedmedia.softseal.html":{"source":"iana"},"application/vnd.sealedmedia.softseal.pdf":{"source":"iana"},"application/vnd.seemail":{"source":"iana","extensions":["see"]},"application/vnd.sema":{"source":"iana","extensions":["sema"]},"application/vnd.semd":{"source":"iana","extensions":["semd"]},"application/vnd.semf":{"source":"iana","extensions":["semf"]},"application/vnd.shade-save-file":{"source":"iana"},"application/vnd.shana.informed.formdata":{"source":"iana","extensions":["ifm"]},"application/vnd.shana.informed.formtemplate":{"source":"iana","extensions":["itp"]},"application/vnd.shana.informed.interchange":{"source":"iana","extensions":["iif"]},"application/vnd.shana.informed.package":{"source":"iana","extensions":["ipk"]},"application/vnd.shootproof+json":{"source":"iana","compressible":true},"application/vnd.shopkick+json":{"source":"iana","compressible":true},"application/vnd.shp":{"source":"iana"},"application/vnd.shx":{"source":"iana"},"application/vnd.sigrok.session":{"source":"iana"},"application/vnd.simtech-mindmapper":{"source":"iana","extensions":["twd","twds"]},"application/vnd.siren+json":{"source":"iana","compressible":true},"application/vnd.smaf":{"source":"iana","extensions":["mmf"]},"application/vnd.smart.notebook":{"source":"iana"},"application/vnd.smart.teacher":{"source":"iana","extensions":["teacher"]},"application/vnd.snesdev-page-table":{"source":"iana"},"application/vnd.software602.filler.form+xml":{"source":"iana","compressible":true,"extensions":["fo"]},"application/vnd.software602.filler.form-xml-zip":{"source":"iana"},"application/vnd.solent.sdkm+xml":{"source":"iana","compressible":true,"extensions":["sdkm","sdkd"]},"application/vnd.spotfire.dxp":{"source":"iana","extensions":["dxp"]},"application/vnd.spotfire.sfs":{"source":"iana","extensions":["sfs"]},"application/vnd.sqlite3":{"source":"iana"},"application/vnd.sss-cod":{"source":"iana"},"application/vnd.sss-dtf":{"source":"iana"},"application/vnd.sss-ntf":{"source":"iana"},"application/vnd.stardivision.calc":{"source":"apache","extensions":["sdc"]},"application/vnd.stardivision.draw":{"source":"apache","extensions":["sda"]},"application/vnd.stardivision.impress":{"source":"apache","extensions":["sdd"]},"application/vnd.stardivision.math":{"source":"apache","extensions":["smf"]},"application/vnd.stardivision.writer":{"source":"apache","extensions":["sdw","vor"]},"application/vnd.stardivision.writer-global":{"source":"apache","extensions":["sgl"]},"application/vnd.stepmania.package":{"source":"iana","extensions":["smzip"]},"application/vnd.stepmania.stepchart":{"source":"iana","extensions":["sm"]},"application/vnd.street-stream":{"source":"iana"},"application/vnd.sun.wadl+xml":{"source":"iana","compressible":true,"extensions":["wadl"]},"application/vnd.sun.xml.calc":{"source":"apache","extensions":["sxc"]},"application/vnd.sun.xml.calc.template":{"source":"apache","extensions":["stc"]},"application/vnd.sun.xml.draw":{"source":"apache","extensions":["sxd"]},"application/vnd.sun.xml.draw.template":{"source":"apache","extensions":["std"]},"application/vnd.sun.xml.impress":{"source":"apache","extensions":["sxi"]},"application/vnd.sun.xml.impress.template":{"source":"apache","extensions":["sti"]},"application/vnd.sun.xml.math":{"source":"apache","extensions":["sxm"]},"application/vnd.sun.xml.writer":{"source":"apache","extensions":["sxw"]},"application/vnd.sun.xml.writer.global":{"source":"apache","extensions":["sxg"]},"application/vnd.sun.xml.writer.template":{"source":"apache","extensions":["stw"]},"application/vnd.sus-calendar":{"source":"iana","extensions":["sus","susp"]},"application/vnd.svd":{"source":"iana","extensions":["svd"]},"application/vnd.swiftview-ics":{"source":"iana"},"application/vnd.symbian.install":{"source":"apache","extensions":["sis","sisx"]},"application/vnd.syncml+xml":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["xsm"]},"application/vnd.syncml.dm+wbxml":{"source":"iana","charset":"UTF-8","extensions":["bdm"]},"application/vnd.syncml.dm+xml":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["xdm"]},"application/vnd.syncml.dm.notification":{"source":"iana"},"application/vnd.syncml.dmddf+wbxml":{"source":"iana"},"application/vnd.syncml.dmddf+xml":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["ddf"]},"application/vnd.syncml.dmtnds+wbxml":{"source":"iana"},"application/vnd.syncml.dmtnds+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/vnd.syncml.ds.notification":{"source":"iana"},"application/vnd.tableschema+json":{"source":"iana","compressible":true},"application/vnd.tao.intent-module-archive":{"source":"iana","extensions":["tao"]},"application/vnd.tcpdump.pcap":{"source":"iana","extensions":["pcap","cap","dmp"]},"application/vnd.think-cell.ppttc+json":{"source":"iana","compressible":true},"application/vnd.tmd.mediaflex.api+xml":{"source":"iana","compressible":true},"application/vnd.tml":{"source":"iana"},"application/vnd.tmobile-livetv":{"source":"iana","extensions":["tmo"]},"application/vnd.tri.onesource":{"source":"iana"},"application/vnd.trid.tpt":{"source":"iana","extensions":["tpt"]},"application/vnd.triscape.mxs":{"source":"iana","extensions":["mxs"]},"application/vnd.trueapp":{"source":"iana","extensions":["tra"]},"application/vnd.truedoc":{"source":"iana"},"application/vnd.ubisoft.webplayer":{"source":"iana"},"application/vnd.ufdl":{"source":"iana","extensions":["ufd","ufdl"]},"application/vnd.uiq.theme":{"source":"iana","extensions":["utz"]},"application/vnd.umajin":{"source":"iana","extensions":["umj"]},"application/vnd.unity":{"source":"iana","extensions":["unityweb"]},"application/vnd.uoml+xml":{"source":"iana","compressible":true,"extensions":["uoml"]},"application/vnd.uplanet.alert":{"source":"iana"},"application/vnd.uplanet.alert-wbxml":{"source":"iana"},"application/vnd.uplanet.bearer-choice":{"source":"iana"},"application/vnd.uplanet.bearer-choice-wbxml":{"source":"iana"},"application/vnd.uplanet.cacheop":{"source":"iana"},"application/vnd.uplanet.cacheop-wbxml":{"source":"iana"},"application/vnd.uplanet.channel":{"source":"iana"},"application/vnd.uplanet.channel-wbxml":{"source":"iana"},"application/vnd.uplanet.list":{"source":"iana"},"application/vnd.uplanet.list-wbxml":{"source":"iana"},"application/vnd.uplanet.listcmd":{"source":"iana"},"application/vnd.uplanet.listcmd-wbxml":{"source":"iana"},"application/vnd.uplanet.signal":{"source":"iana"},"application/vnd.uri-map":{"source":"iana"},"application/vnd.valve.source.material":{"source":"iana"},"application/vnd.vcx":{"source":"iana","extensions":["vcx"]},"application/vnd.vd-study":{"source":"iana"},"application/vnd.vectorworks":{"source":"iana"},"application/vnd.vel+json":{"source":"iana","compressible":true},"application/vnd.verimatrix.vcas":{"source":"iana"},"application/vnd.veryant.thin":{"source":"iana"},"application/vnd.ves.encrypted":{"source":"iana"},"application/vnd.vidsoft.vidconference":{"source":"iana"},"application/vnd.visio":{"source":"iana","extensions":["vsd","vst","vss","vsw"]},"application/vnd.visionary":{"source":"iana","extensions":["vis"]},"application/vnd.vividence.scriptfile":{"source":"iana"},"application/vnd.vsf":{"source":"iana","extensions":["vsf"]},"application/vnd.wap.sic":{"source":"iana"},"application/vnd.wap.slc":{"source":"iana"},"application/vnd.wap.wbxml":{"source":"iana","charset":"UTF-8","extensions":["wbxml"]},"application/vnd.wap.wmlc":{"source":"iana","extensions":["wmlc"]},"application/vnd.wap.wmlscriptc":{"source":"iana","extensions":["wmlsc"]},"application/vnd.webturbo":{"source":"iana","extensions":["wtb"]},"application/vnd.wfa.p2p":{"source":"iana"},"application/vnd.wfa.wsc":{"source":"iana"},"application/vnd.windows.devicepairing":{"source":"iana"},"application/vnd.wmc":{"source":"iana"},"application/vnd.wmf.bootstrap":{"source":"iana"},"application/vnd.wolfram.mathematica":{"source":"iana"},"application/vnd.wolfram.mathematica.package":{"source":"iana"},"application/vnd.wolfram.player":{"source":"iana","extensions":["nbp"]},"application/vnd.wordperfect":{"source":"iana","extensions":["wpd"]},"application/vnd.wqd":{"source":"iana","extensions":["wqd"]},"application/vnd.wrq-hp3000-labelled":{"source":"iana"},"application/vnd.wt.stf":{"source":"iana","extensions":["stf"]},"application/vnd.wv.csp+wbxml":{"source":"iana"},"application/vnd.wv.csp+xml":{"source":"iana","compressible":true},"application/vnd.wv.ssp+xml":{"source":"iana","compressible":true},"application/vnd.xacml+json":{"source":"iana","compressible":true},"application/vnd.xara":{"source":"iana","extensions":["xar"]},"application/vnd.xfdl":{"source":"iana","extensions":["xfdl"]},"application/vnd.xfdl.webform":{"source":"iana"},"application/vnd.xmi+xml":{"source":"iana","compressible":true},"application/vnd.xmpie.cpkg":{"source":"iana"},"application/vnd.xmpie.dpkg":{"source":"iana"},"application/vnd.xmpie.plan":{"source":"iana"},"application/vnd.xmpie.ppkg":{"source":"iana"},"application/vnd.xmpie.xlim":{"source":"iana"},"application/vnd.yamaha.hv-dic":{"source":"iana","extensions":["hvd"]},"application/vnd.yamaha.hv-script":{"source":"iana","extensions":["hvs"]},"application/vnd.yamaha.hv-voice":{"source":"iana","extensions":["hvp"]},"application/vnd.yamaha.openscoreformat":{"source":"iana","extensions":["osf"]},"application/vnd.yamaha.openscoreformat.osfpvg+xml":{"source":"iana","compressible":true,"extensions":["osfpvg"]},"application/vnd.yamaha.remote-setup":{"source":"iana"},"application/vnd.yamaha.smaf-audio":{"source":"iana","extensions":["saf"]},"application/vnd.yamaha.smaf-phrase":{"source":"iana","extensions":["spf"]},"application/vnd.yamaha.through-ngn":{"source":"iana"},"application/vnd.yamaha.tunnel-udpencap":{"source":"iana"},"application/vnd.yaoweme":{"source":"iana"},"application/vnd.yellowriver-custom-menu":{"source":"iana","extensions":["cmp"]},"application/vnd.youtube.yt":{"source":"iana"},"application/vnd.zul":{"source":"iana","extensions":["zir","zirz"]},"application/vnd.zzazz.deck+xml":{"source":"iana","compressible":true,"extensions":["zaz"]},"application/voicexml+xml":{"source":"iana","compressible":true,"extensions":["vxml"]},"application/voucher-cms+json":{"source":"iana","compressible":true},"application/vq-rtcpxr":{"source":"iana"},"application/wasm":{"compressible":true,"extensions":["wasm"]},"application/watcherinfo+xml":{"source":"iana","compressible":true},"application/webpush-options+json":{"source":"iana","compressible":true},"application/whoispp-query":{"source":"iana"},"application/whoispp-response":{"source":"iana"},"application/widget":{"source":"iana","extensions":["wgt"]},"application/winhlp":{"source":"apache","extensions":["hlp"]},"application/wita":{"source":"iana"},"application/wordperfect5.1":{"source":"iana"},"application/wsdl+xml":{"source":"iana","compressible":true,"extensions":["wsdl"]},"application/wspolicy+xml":{"source":"iana","compressible":true,"extensions":["wspolicy"]},"application/x-7z-compressed":{"source":"apache","compressible":false,"extensions":["7z"]},"application/x-abiword":{"source":"apache","extensions":["abw"]},"application/x-ace-compressed":{"source":"apache","extensions":["ace"]},"application/x-amf":{"source":"apache"},"application/x-apple-diskimage":{"source":"apache","extensions":["dmg"]},"application/x-arj":{"compressible":false,"extensions":["arj"]},"application/x-authorware-bin":{"source":"apache","extensions":["aab","x32","u32","vox"]},"application/x-authorware-map":{"source":"apache","extensions":["aam"]},"application/x-authorware-seg":{"source":"apache","extensions":["aas"]},"application/x-bcpio":{"source":"apache","extensions":["bcpio"]},"application/x-bdoc":{"compressible":false,"extensions":["bdoc"]},"application/x-bittorrent":{"source":"apache","extensions":["torrent"]},"application/x-blorb":{"source":"apache","extensions":["blb","blorb"]},"application/x-bzip":{"source":"apache","compressible":false,"extensions":["bz"]},"application/x-bzip2":{"source":"apache","compressible":false,"extensions":["bz2","boz"]},"application/x-cbr":{"source":"apache","extensions":["cbr","cba","cbt","cbz","cb7"]},"application/x-cdlink":{"source":"apache","extensions":["vcd"]},"application/x-cfs-compressed":{"source":"apache","extensions":["cfs"]},"application/x-chat":{"source":"apache","extensions":["chat"]},"application/x-chess-pgn":{"source":"apache","extensions":["pgn"]},"application/x-chrome-extension":{"extensions":["crx"]},"application/x-cocoa":{"source":"nginx","extensions":["cco"]},"application/x-compress":{"source":"apache"},"application/x-conference":{"source":"apache","extensions":["nsc"]},"application/x-cpio":{"source":"apache","extensions":["cpio"]},"application/x-csh":{"source":"apache","extensions":["csh"]},"application/x-deb":{"compressible":false},"application/x-debian-package":{"source":"apache","extensions":["deb","udeb"]},"application/x-dgc-compressed":{"source":"apache","extensions":["dgc"]},"application/x-director":{"source":"apache","extensions":["dir","dcr","dxr","cst","cct","cxt","w3d","fgd","swa"]},"application/x-doom":{"source":"apache","extensions":["wad"]},"application/x-dtbncx+xml":{"source":"apache","compressible":true,"extensions":["ncx"]},"application/x-dtbook+xml":{"source":"apache","compressible":true,"extensions":["dtb"]},"application/x-dtbresource+xml":{"source":"apache","compressible":true,"extensions":["res"]},"application/x-dvi":{"source":"apache","compressible":false,"extensions":["dvi"]},"application/x-envoy":{"source":"apache","extensions":["evy"]},"application/x-eva":{"source":"apache","extensions":["eva"]},"application/x-font-bdf":{"source":"apache","extensions":["bdf"]},"application/x-font-dos":{"source":"apache"},"application/x-font-framemaker":{"source":"apache"},"application/x-font-ghostscript":{"source":"apache","extensions":["gsf"]},"application/x-font-libgrx":{"source":"apache"},"application/x-font-linux-psf":{"source":"apache","extensions":["psf"]},"application/x-font-pcf":{"source":"apache","extensions":["pcf"]},"application/x-font-snf":{"source":"apache","extensions":["snf"]},"application/x-font-speedo":{"source":"apache"},"application/x-font-sunos-news":{"source":"apache"},"application/x-font-type1":{"source":"apache","extensions":["pfa","pfb","pfm","afm"]},"application/x-font-vfont":{"source":"apache"},"application/x-freearc":{"source":"apache","extensions":["arc"]},"application/x-futuresplash":{"source":"apache","extensions":["spl"]},"application/x-gca-compressed":{"source":"apache","extensions":["gca"]},"application/x-glulx":{"source":"apache","extensions":["ulx"]},"application/x-gnumeric":{"source":"apache","extensions":["gnumeric"]},"application/x-gramps-xml":{"source":"apache","extensions":["gramps"]},"application/x-gtar":{"source":"apache","extensions":["gtar"]},"application/x-gzip":{"source":"apache"},"application/x-hdf":{"source":"apache","extensions":["hdf"]},"application/x-httpd-php":{"compressible":true,"extensions":["php"]},"application/x-install-instructions":{"source":"apache","extensions":["install"]},"application/x-iso9660-image":{"source":"apache","extensions":["iso"]},"application/x-java-archive-diff":{"source":"nginx","extensions":["jardiff"]},"application/x-java-jnlp-file":{"source":"apache","compressible":false,"extensions":["jnlp"]},"application/x-javascript":{"compressible":true},"application/x-keepass2":{"extensions":["kdbx"]},"application/x-latex":{"source":"apache","compressible":false,"extensions":["latex"]},"application/x-lua-bytecode":{"extensions":["luac"]},"application/x-lzh-compressed":{"source":"apache","extensions":["lzh","lha"]},"application/x-makeself":{"source":"nginx","extensions":["run"]},"application/x-mie":{"source":"apache","extensions":["mie"]},"application/x-mobipocket-ebook":{"source":"apache","extensions":["prc","mobi"]},"application/x-mpegurl":{"compressible":false},"application/x-ms-application":{"source":"apache","extensions":["application"]},"application/x-ms-shortcut":{"source":"apache","extensions":["lnk"]},"application/x-ms-wmd":{"source":"apache","extensions":["wmd"]},"application/x-ms-wmz":{"source":"apache","extensions":["wmz"]},"application/x-ms-xbap":{"source":"apache","extensions":["xbap"]},"application/x-msaccess":{"source":"apache","extensions":["mdb"]},"application/x-msbinder":{"source":"apache","extensions":["obd"]},"application/x-mscardfile":{"source":"apache","extensions":["crd"]},"application/x-msclip":{"source":"apache","extensions":["clp"]},"application/x-msdos-program":{"extensions":["exe"]},"application/x-msdownload":{"source":"apache","extensions":["exe","dll","com","bat","msi"]},"application/x-msmediaview":{"source":"apache","extensions":["mvb","m13","m14"]},"application/x-msmetafile":{"source":"apache","extensions":["wmf","wmz","emf","emz"]},"application/x-msmoney":{"source":"apache","extensions":["mny"]},"application/x-mspublisher":{"source":"apache","extensions":["pub"]},"application/x-msschedule":{"source":"apache","extensions":["scd"]},"application/x-msterminal":{"source":"apache","extensions":["trm"]},"application/x-mswrite":{"source":"apache","extensions":["wri"]},"application/x-netcdf":{"source":"apache","extensions":["nc","cdf"]},"application/x-ns-proxy-autoconfig":{"compressible":true,"extensions":["pac"]},"application/x-nzb":{"source":"apache","extensions":["nzb"]},"application/x-perl":{"source":"nginx","extensions":["pl","pm"]},"application/x-pilot":{"source":"nginx","extensions":["prc","pdb"]},"application/x-pkcs12":{"source":"apache","compressible":false,"extensions":["p12","pfx"]},"application/x-pkcs7-certificates":{"source":"apache","extensions":["p7b","spc"]},"application/x-pkcs7-certreqresp":{"source":"apache","extensions":["p7r"]},"application/x-pki-message":{"source":"iana"},"application/x-rar-compressed":{"source":"apache","compressible":false,"extensions":["rar"]},"application/x-redhat-package-manager":{"source":"nginx","extensions":["rpm"]},"application/x-research-info-systems":{"source":"apache","extensions":["ris"]},"application/x-sea":{"source":"nginx","extensions":["sea"]},"application/x-sh":{"source":"apache","compressible":true,"extensions":["sh"]},"application/x-shar":{"source":"apache","extensions":["shar"]},"application/x-shockwave-flash":{"source":"apache","compressible":false,"extensions":["swf"]},"application/x-silverlight-app":{"source":"apache","extensions":["xap"]},"application/x-sql":{"source":"apache","extensions":["sql"]},"application/x-stuffit":{"source":"apache","compressible":false,"extensions":["sit"]},"application/x-stuffitx":{"source":"apache","extensions":["sitx"]},"application/x-subrip":{"source":"apache","extensions":["srt"]},"application/x-sv4cpio":{"source":"apache","extensions":["sv4cpio"]},"application/x-sv4crc":{"source":"apache","extensions":["sv4crc"]},"application/x-t3vm-image":{"source":"apache","extensions":["t3"]},"application/x-tads":{"source":"apache","extensions":["gam"]},"application/x-tar":{"source":"apache","compressible":true,"extensions":["tar"]},"application/x-tcl":{"source":"apache","extensions":["tcl","tk"]},"application/x-tex":{"source":"apache","extensions":["tex"]},"application/x-tex-tfm":{"source":"apache","extensions":["tfm"]},"application/x-texinfo":{"source":"apache","extensions":["texinfo","texi"]},"application/x-tgif":{"source":"apache","extensions":["obj"]},"application/x-ustar":{"source":"apache","extensions":["ustar"]},"application/x-virtualbox-hdd":{"compressible":true,"extensions":["hdd"]},"application/x-virtualbox-ova":{"compressible":true,"extensions":["ova"]},"application/x-virtualbox-ovf":{"compressible":true,"extensions":["ovf"]},"application/x-virtualbox-vbox":{"compressible":true,"extensions":["vbox"]},"application/x-virtualbox-vbox-extpack":{"compressible":false,"extensions":["vbox-extpack"]},"application/x-virtualbox-vdi":{"compressible":true,"extensions":["vdi"]},"application/x-virtualbox-vhd":{"compressible":true,"extensions":["vhd"]},"application/x-virtualbox-vmdk":{"compressible":true,"extensions":["vmdk"]},"application/x-wais-source":{"source":"apache","extensions":["src"]},"application/x-web-app-manifest+json":{"compressible":true,"extensions":["webapp"]},"application/x-www-form-urlencoded":{"source":"iana","compressible":true},"application/x-x509-ca-cert":{"source":"iana","extensions":["der","crt","pem"]},"application/x-x509-ca-ra-cert":{"source":"iana"},"application/x-x509-next-ca-cert":{"source":"iana"},"application/x-xfig":{"source":"apache","extensions":["fig"]},"application/x-xliff+xml":{"source":"apache","compressible":true,"extensions":["xlf"]},"application/x-xpinstall":{"source":"apache","compressible":false,"extensions":["xpi"]},"application/x-xz":{"source":"apache","extensions":["xz"]},"application/x-zmachine":{"source":"apache","extensions":["z1","z2","z3","z4","z5","z6","z7","z8"]},"application/x400-bp":{"source":"iana"},"application/xacml+xml":{"source":"iana","compressible":true},"application/xaml+xml":{"source":"apache","compressible":true,"extensions":["xaml"]},"application/xcap-att+xml":{"source":"iana","compressible":true,"extensions":["xav"]},"application/xcap-caps+xml":{"source":"iana","compressible":true,"extensions":["xca"]},"application/xcap-diff+xml":{"source":"iana","compressible":true,"extensions":["xdf"]},"application/xcap-el+xml":{"source":"iana","compressible":true,"extensions":["xel"]},"application/xcap-error+xml":{"source":"iana","compressible":true,"extensions":["xer"]},"application/xcap-ns+xml":{"source":"iana","compressible":true,"extensions":["xns"]},"application/xcon-conference-info+xml":{"source":"iana","compressible":true},"application/xcon-conference-info-diff+xml":{"source":"iana","compressible":true},"application/xenc+xml":{"source":"iana","compressible":true,"extensions":["xenc"]},"application/xhtml+xml":{"source":"iana","compressible":true,"extensions":["xhtml","xht"]},"application/xhtml-voice+xml":{"source":"apache","compressible":true},"application/xliff+xml":{"source":"iana","compressible":true,"extensions":["xlf"]},"application/xml":{"source":"iana","compressible":true,"extensions":["xml","xsl","xsd","rng"]},"application/xml-dtd":{"source":"iana","compressible":true,"extensions":["dtd"]},"application/xml-external-parsed-entity":{"source":"iana"},"application/xml-patch+xml":{"source":"iana","compressible":true},"application/xmpp+xml":{"source":"iana","compressible":true},"application/xop+xml":{"source":"iana","compressible":true,"extensions":["xop"]},"application/xproc+xml":{"source":"apache","compressible":true,"extensions":["xpl"]},"application/xslt+xml":{"source":"iana","compressible":true,"extensions":["xslt"]},"application/xspf+xml":{"source":"apache","compressible":true,"extensions":["xspf"]},"application/xv+xml":{"source":"iana","compressible":true,"extensions":["mxml","xhvml","xvml","xvm"]},"application/yang":{"source":"iana","extensions":["yang"]},"application/yang-data+json":{"source":"iana","compressible":true},"application/yang-data+xml":{"source":"iana","compressible":true},"application/yang-patch+json":{"source":"iana","compressible":true},"application/yang-patch+xml":{"source":"iana","compressible":true},"application/yin+xml":{"source":"iana","compressible":true,"extensions":["yin"]},"application/zip":{"source":"iana","compressible":false,"extensions":["zip"]},"application/zlib":{"source":"iana"},"application/zstd":{"source":"iana"},"audio/1d-interleaved-parityfec":{"source":"iana"},"audio/32kadpcm":{"source":"iana"},"audio/3gpp":{"source":"iana","compressible":false,"extensions":["3gpp"]},"audio/3gpp2":{"source":"iana"},"audio/aac":{"source":"iana"},"audio/ac3":{"source":"iana"},"audio/adpcm":{"source":"apache","extensions":["adp"]},"audio/amr":{"source":"iana"},"audio/amr-wb":{"source":"iana"},"audio/amr-wb+":{"source":"iana"},"audio/aptx":{"source":"iana"},"audio/asc":{"source":"iana"},"audio/atrac-advanced-lossless":{"source":"iana"},"audio/atrac-x":{"source":"iana"},"audio/atrac3":{"source":"iana"},"audio/basic":{"source":"iana","compressible":false,"extensions":["au","snd"]},"audio/bv16":{"source":"iana"},"audio/bv32":{"source":"iana"},"audio/clearmode":{"source":"iana"},"audio/cn":{"source":"iana"},"audio/dat12":{"source":"iana"},"audio/dls":{"source":"iana"},"audio/dsr-es201108":{"source":"iana"},"audio/dsr-es202050":{"source":"iana"},"audio/dsr-es202211":{"source":"iana"},"audio/dsr-es202212":{"source":"iana"},"audio/dv":{"source":"iana"},"audio/dvi4":{"source":"iana"},"audio/eac3":{"source":"iana"},"audio/encaprtp":{"source":"iana"},"audio/evrc":{"source":"iana"},"audio/evrc-qcp":{"source":"iana"},"audio/evrc0":{"source":"iana"},"audio/evrc1":{"source":"iana"},"audio/evrcb":{"source":"iana"},"audio/evrcb0":{"source":"iana"},"audio/evrcb1":{"source":"iana"},"audio/evrcnw":{"source":"iana"},"audio/evrcnw0":{"source":"iana"},"audio/evrcnw1":{"source":"iana"},"audio/evrcwb":{"source":"iana"},"audio/evrcwb0":{"source":"iana"},"audio/evrcwb1":{"source":"iana"},"audio/evs":{"source":"iana"},"audio/flexfec":{"source":"iana"},"audio/fwdred":{"source":"iana"},"audio/g711-0":{"source":"iana"},"audio/g719":{"source":"iana"},"audio/g722":{"source":"iana"},"audio/g7221":{"source":"iana"},"audio/g723":{"source":"iana"},"audio/g726-16":{"source":"iana"},"audio/g726-24":{"source":"iana"},"audio/g726-32":{"source":"iana"},"audio/g726-40":{"source":"iana"},"audio/g728":{"source":"iana"},"audio/g729":{"source":"iana"},"audio/g7291":{"source":"iana"},"audio/g729d":{"source":"iana"},"audio/g729e":{"source":"iana"},"audio/gsm":{"source":"iana"},"audio/gsm-efr":{"source":"iana"},"audio/gsm-hr-08":{"source":"iana"},"audio/ilbc":{"source":"iana"},"audio/ip-mr_v2.5":{"source":"iana"},"audio/isac":{"source":"apache"},"audio/l16":{"source":"iana"},"audio/l20":{"source":"iana"},"audio/l24":{"source":"iana","compressible":false},"audio/l8":{"source":"iana"},"audio/lpc":{"source":"iana"},"audio/melp":{"source":"iana"},"audio/melp1200":{"source":"iana"},"audio/melp2400":{"source":"iana"},"audio/melp600":{"source":"iana"},"audio/mhas":{"source":"iana"},"audio/midi":{"source":"apache","extensions":["mid","midi","kar","rmi"]},"audio/mobile-xmf":{"source":"iana","extensions":["mxmf"]},"audio/mp3":{"compressible":false,"extensions":["mp3"]},"audio/mp4":{"source":"iana","compressible":false,"extensions":["m4a","mp4a"]},"audio/mp4a-latm":{"source":"iana"},"audio/mpa":{"source":"iana"},"audio/mpa-robust":{"source":"iana"},"audio/mpeg":{"source":"iana","compressible":false,"extensions":["mpga","mp2","mp2a","mp3","m2a","m3a"]},"audio/mpeg4-generic":{"source":"iana"},"audio/musepack":{"source":"apache"},"audio/ogg":{"source":"iana","compressible":false,"extensions":["oga","ogg","spx"]},"audio/opus":{"source":"iana"},"audio/parityfec":{"source":"iana"},"audio/pcma":{"source":"iana"},"audio/pcma-wb":{"source":"iana"},"audio/pcmu":{"source":"iana"},"audio/pcmu-wb":{"source":"iana"},"audio/prs.sid":{"source":"iana"},"audio/qcelp":{"source":"iana"},"audio/raptorfec":{"source":"iana"},"audio/red":{"source":"iana"},"audio/rtp-enc-aescm128":{"source":"iana"},"audio/rtp-midi":{"source":"iana"},"audio/rtploopback":{"source":"iana"},"audio/rtx":{"source":"iana"},"audio/s3m":{"source":"apache","extensions":["s3m"]},"audio/silk":{"source":"apache","extensions":["sil"]},"audio/smv":{"source":"iana"},"audio/smv-qcp":{"source":"iana"},"audio/smv0":{"source":"iana"},"audio/sp-midi":{"source":"iana"},"audio/speex":{"source":"iana"},"audio/t140c":{"source":"iana"},"audio/t38":{"source":"iana"},"audio/telephone-event":{"source":"iana"},"audio/tetra_acelp":{"source":"iana"},"audio/tetra_acelp_bb":{"source":"iana"},"audio/tone":{"source":"iana"},"audio/uemclip":{"source":"iana"},"audio/ulpfec":{"source":"iana"},"audio/usac":{"source":"iana"},"audio/vdvi":{"source":"iana"},"audio/vmr-wb":{"source":"iana"},"audio/vnd.3gpp.iufp":{"source":"iana"},"audio/vnd.4sb":{"source":"iana"},"audio/vnd.audiokoz":{"source":"iana"},"audio/vnd.celp":{"source":"iana"},"audio/vnd.cisco.nse":{"source":"iana"},"audio/vnd.cmles.radio-events":{"source":"iana"},"audio/vnd.cns.anp1":{"source":"iana"},"audio/vnd.cns.inf1":{"source":"iana"},"audio/vnd.dece.audio":{"source":"iana","extensions":["uva","uvva"]},"audio/vnd.digital-winds":{"source":"iana","extensions":["eol"]},"audio/vnd.dlna.adts":{"source":"iana"},"audio/vnd.dolby.heaac.1":{"source":"iana"},"audio/vnd.dolby.heaac.2":{"source":"iana"},"audio/vnd.dolby.mlp":{"source":"iana"},"audio/vnd.dolby.mps":{"source":"iana"},"audio/vnd.dolby.pl2":{"source":"iana"},"audio/vnd.dolby.pl2x":{"source":"iana"},"audio/vnd.dolby.pl2z":{"source":"iana"},"audio/vnd.dolby.pulse.1":{"source":"iana"},"audio/vnd.dra":{"source":"iana","extensions":["dra"]},"audio/vnd.dts":{"source":"iana","extensions":["dts"]},"audio/vnd.dts.hd":{"source":"iana","extensions":["dtshd"]},"audio/vnd.dts.uhd":{"source":"iana"},"audio/vnd.dvb.file":{"source":"iana"},"audio/vnd.everad.plj":{"source":"iana"},"audio/vnd.hns.audio":{"source":"iana"},"audio/vnd.lucent.voice":{"source":"iana","extensions":["lvp"]},"audio/vnd.ms-playready.media.pya":{"source":"iana","extensions":["pya"]},"audio/vnd.nokia.mobile-xmf":{"source":"iana"},"audio/vnd.nortel.vbk":{"source":"iana"},"audio/vnd.nuera.ecelp4800":{"source":"iana","extensions":["ecelp4800"]},"audio/vnd.nuera.ecelp7470":{"source":"iana","extensions":["ecelp7470"]},"audio/vnd.nuera.ecelp9600":{"source":"iana","extensions":["ecelp9600"]},"audio/vnd.octel.sbc":{"source":"iana"},"audio/vnd.presonus.multitrack":{"source":"iana"},"audio/vnd.qcelp":{"source":"iana"},"audio/vnd.rhetorex.32kadpcm":{"source":"iana"},"audio/vnd.rip":{"source":"iana","extensions":["rip"]},"audio/vnd.rn-realaudio":{"compressible":false},"audio/vnd.sealedmedia.softseal.mpeg":{"source":"iana"},"audio/vnd.vmx.cvsd":{"source":"iana"},"audio/vnd.wave":{"compressible":false},"audio/vorbis":{"source":"iana","compressible":false},"audio/vorbis-config":{"source":"iana"},"audio/wav":{"compressible":false,"extensions":["wav"]},"audio/wave":{"compressible":false,"extensions":["wav"]},"audio/webm":{"source":"apache","compressible":false,"extensions":["weba"]},"audio/x-aac":{"source":"apache","compressible":false,"extensions":["aac"]},"audio/x-aiff":{"source":"apache","extensions":["aif","aiff","aifc"]},"audio/x-caf":{"source":"apache","compressible":false,"extensions":["caf"]},"audio/x-flac":{"source":"apache","extensions":["flac"]},"audio/x-m4a":{"source":"nginx","extensions":["m4a"]},"audio/x-matroska":{"source":"apache","extensions":["mka"]},"audio/x-mpegurl":{"source":"apache","extensions":["m3u"]},"audio/x-ms-wax":{"source":"apache","extensions":["wax"]},"audio/x-ms-wma":{"source":"apache","extensions":["wma"]},"audio/x-pn-realaudio":{"source":"apache","extensions":["ram","ra"]},"audio/x-pn-realaudio-plugin":{"source":"apache","extensions":["rmp"]},"audio/x-realaudio":{"source":"nginx","extensions":["ra"]},"audio/x-tta":{"source":"apache"},"audio/x-wav":{"source":"apache","extensions":["wav"]},"audio/xm":{"source":"apache","extensions":["xm"]},"chemical/x-cdx":{"source":"apache","extensions":["cdx"]},"chemical/x-cif":{"source":"apache","extensions":["cif"]},"chemical/x-cmdf":{"source":"apache","extensions":["cmdf"]},"chemical/x-cml":{"source":"apache","extensions":["cml"]},"chemical/x-csml":{"source":"apache","extensions":["csml"]},"chemical/x-pdb":{"source":"apache"},"chemical/x-xyz":{"source":"apache","extensions":["xyz"]},"font/collection":{"source":"iana","extensions":["ttc"]},"font/otf":{"source":"iana","compressible":true,"extensions":["otf"]},"font/sfnt":{"source":"iana"},"font/ttf":{"source":"iana","compressible":true,"extensions":["ttf"]},"font/woff":{"source":"iana","extensions":["woff"]},"font/woff2":{"source":"iana","extensions":["woff2"]},"image/aces":{"source":"iana","extensions":["exr"]},"image/apng":{"compressible":false,"extensions":["apng"]},"image/avci":{"source":"iana"},"image/avcs":{"source":"iana"},"image/bmp":{"source":"iana","compressible":true,"extensions":["bmp"]},"image/cgm":{"source":"iana","extensions":["cgm"]},"image/dicom-rle":{"source":"iana","extensions":["drle"]},"image/emf":{"source":"iana","extensions":["emf"]},"image/fits":{"source":"iana","extensions":["fits"]},"image/g3fax":{"source":"iana","extensions":["g3"]},"image/gif":{"source":"iana","compressible":false,"extensions":["gif"]},"image/heic":{"source":"iana","extensions":["heic"]},"image/heic-sequence":{"source":"iana","extensions":["heics"]},"image/heif":{"source":"iana","extensions":["heif"]},"image/heif-sequence":{"source":"iana","extensions":["heifs"]},"image/hej2k":{"source":"iana","extensions":["hej2"]},"image/hsj2":{"source":"iana","extensions":["hsj2"]},"image/ief":{"source":"iana","extensions":["ief"]},"image/jls":{"source":"iana","extensions":["jls"]},"image/jp2":{"source":"iana","compressible":false,"extensions":["jp2","jpg2"]},"image/jpeg":{"source":"iana","compressible":false,"extensions":["jpeg","jpg","jpe"]},"image/jph":{"source":"iana","extensions":["jph"]},"image/jphc":{"source":"iana","extensions":["jhc"]},"image/jpm":{"source":"iana","compressible":false,"extensions":["jpm"]},"image/jpx":{"source":"iana","compressible":false,"extensions":["jpx","jpf"]},"image/jxr":{"source":"iana","extensions":["jxr"]},"image/jxra":{"source":"iana","extensions":["jxra"]},"image/jxrs":{"source":"iana","extensions":["jxrs"]},"image/jxs":{"source":"iana","extensions":["jxs"]},"image/jxsc":{"source":"iana","extensions":["jxsc"]},"image/jxsi":{"source":"iana","extensions":["jxsi"]},"image/jxss":{"source":"iana","extensions":["jxss"]},"image/ktx":{"source":"iana","extensions":["ktx"]},"image/naplps":{"source":"iana"},"image/pjpeg":{"compressible":false},"image/png":{"source":"iana","compressible":false,"extensions":["png"]},"image/prs.btif":{"source":"iana","extensions":["btif"]},"image/prs.pti":{"source":"iana","extensions":["pti"]},"image/pwg-raster":{"source":"iana"},"image/sgi":{"source":"apache","extensions":["sgi"]},"image/svg+xml":{"source":"iana","compressible":true,"extensions":["svg","svgz"]},"image/t38":{"source":"iana","extensions":["t38"]},"image/tiff":{"source":"iana","compressible":false,"extensions":["tif","tiff"]},"image/tiff-fx":{"source":"iana","extensions":["tfx"]},"image/vnd.adobe.photoshop":{"source":"iana","compressible":true,"extensions":["psd"]},"image/vnd.airzip.accelerator.azv":{"source":"iana","extensions":["azv"]},"image/vnd.cns.inf2":{"source":"iana"},"image/vnd.dece.graphic":{"source":"iana","extensions":["uvi","uvvi","uvg","uvvg"]},"image/vnd.djvu":{"source":"iana","extensions":["djvu","djv"]},"image/vnd.dvb.subtitle":{"source":"iana","extensions":["sub"]},"image/vnd.dwg":{"source":"iana","extensions":["dwg"]},"image/vnd.dxf":{"source":"iana","extensions":["dxf"]},"image/vnd.fastbidsheet":{"source":"iana","extensions":["fbs"]},"image/vnd.fpx":{"source":"iana","extensions":["fpx"]},"image/vnd.fst":{"source":"iana","extensions":["fst"]},"image/vnd.fujixerox.edmics-mmr":{"source":"iana","extensions":["mmr"]},"image/vnd.fujixerox.edmics-rlc":{"source":"iana","extensions":["rlc"]},"image/vnd.globalgraphics.pgb":{"source":"iana"},"image/vnd.microsoft.icon":{"source":"iana","extensions":["ico"]},"image/vnd.mix":{"source":"iana"},"image/vnd.mozilla.apng":{"source":"iana"},"image/vnd.ms-dds":{"extensions":["dds"]},"image/vnd.ms-modi":{"source":"iana","extensions":["mdi"]},"image/vnd.ms-photo":{"source":"apache","extensions":["wdp"]},"image/vnd.net-fpx":{"source":"iana","extensions":["npx"]},"image/vnd.radiance":{"source":"iana"},"image/vnd.sealed.png":{"source":"iana"},"image/vnd.sealedmedia.softseal.gif":{"source":"iana"},"image/vnd.sealedmedia.softseal.jpg":{"source":"iana"},"image/vnd.svf":{"source":"iana"},"image/vnd.tencent.tap":{"source":"iana","extensions":["tap"]},"image/vnd.valve.source.texture":{"source":"iana","extensions":["vtf"]},"image/vnd.wap.wbmp":{"source":"iana","extensions":["wbmp"]},"image/vnd.xiff":{"source":"iana","extensions":["xif"]},"image/vnd.zbrush.pcx":{"source":"iana","extensions":["pcx"]},"image/webp":{"source":"apache","extensions":["webp"]},"image/wmf":{"source":"iana","extensions":["wmf"]},"image/x-3ds":{"source":"apache","extensions":["3ds"]},"image/x-cmu-raster":{"source":"apache","extensions":["ras"]},"image/x-cmx":{"source":"apache","extensions":["cmx"]},"image/x-freehand":{"source":"apache","extensions":["fh","fhc","fh4","fh5","fh7"]},"image/x-icon":{"source":"apache","compressible":true,"extensions":["ico"]},"image/x-jng":{"source":"nginx","extensions":["jng"]},"image/x-mrsid-image":{"source":"apache","extensions":["sid"]},"image/x-ms-bmp":{"source":"nginx","compressible":true,"extensions":["bmp"]},"image/x-pcx":{"source":"apache","extensions":["pcx"]},"image/x-pict":{"source":"apache","extensions":["pic","pct"]},"image/x-portable-anymap":{"source":"apache","extensions":["pnm"]},"image/x-portable-bitmap":{"source":"apache","extensions":["pbm"]},"image/x-portable-graymap":{"source":"apache","extensions":["pgm"]},"image/x-portable-pixmap":{"source":"apache","extensions":["ppm"]},"image/x-rgb":{"source":"apache","extensions":["rgb"]},"image/x-tga":{"source":"apache","extensions":["tga"]},"image/x-xbitmap":{"source":"apache","extensions":["xbm"]},"image/x-xcf":{"compressible":false},"image/x-xpixmap":{"source":"apache","extensions":["xpm"]},"image/x-xwindowdump":{"source":"apache","extensions":["xwd"]},"message/cpim":{"source":"iana"},"message/delivery-status":{"source":"iana"},"message/disposition-notification":{"source":"iana","extensions":["disposition-notification"]},"message/external-body":{"source":"iana"},"message/feedback-report":{"source":"iana"},"message/global":{"source":"iana","extensions":["u8msg"]},"message/global-delivery-status":{"source":"iana","extensions":["u8dsn"]},"message/global-disposition-notification":{"source":"iana","extensions":["u8mdn"]},"message/global-headers":{"source":"iana","extensions":["u8hdr"]},"message/http":{"source":"iana","compressible":false},"message/imdn+xml":{"source":"iana","compressible":true},"message/news":{"source":"iana"},"message/partial":{"source":"iana","compressible":false},"message/rfc822":{"source":"iana","compressible":true,"extensions":["eml","mime"]},"message/s-http":{"source":"iana"},"message/sip":{"source":"iana"},"message/sipfrag":{"source":"iana"},"message/tracking-status":{"source":"iana"},"message/vnd.si.simp":{"source":"iana"},"message/vnd.wfa.wsc":{"source":"iana","extensions":["wsc"]},"model/3mf":{"source":"iana","extensions":["3mf"]},"model/gltf+json":{"source":"iana","compressible":true,"extensions":["gltf"]},"model/gltf-binary":{"source":"iana","compressible":true,"extensions":["glb"]},"model/iges":{"source":"iana","compressible":false,"extensions":["igs","iges"]},"model/mesh":{"source":"iana","compressible":false,"extensions":["msh","mesh","silo"]},"model/mtl":{"source":"iana","extensions":["mtl"]},"model/obj":{"source":"iana","extensions":["obj"]},"model/stl":{"source":"iana","extensions":["stl"]},"model/vnd.collada+xml":{"source":"iana","compressible":true,"extensions":["dae"]},"model/vnd.dwf":{"source":"iana","extensions":["dwf"]},"model/vnd.flatland.3dml":{"source":"iana"},"model/vnd.gdl":{"source":"iana","extensions":["gdl"]},"model/vnd.gs-gdl":{"source":"apache"},"model/vnd.gs.gdl":{"source":"iana"},"model/vnd.gtw":{"source":"iana","extensions":["gtw"]},"model/vnd.moml+xml":{"source":"iana","compressible":true},"model/vnd.mts":{"source":"iana","extensions":["mts"]},"model/vnd.opengex":{"source":"iana","extensions":["ogex"]},"model/vnd.parasolid.transmit.binary":{"source":"iana","extensions":["x_b"]},"model/vnd.parasolid.transmit.text":{"source":"iana","extensions":["x_t"]},"model/vnd.rosette.annotated-data-model":{"source":"iana"},"model/vnd.usdz+zip":{"source":"iana","compressible":false,"extensions":["usdz"]},"model/vnd.valve.source.compiled-map":{"source":"iana","extensions":["bsp"]},"model/vnd.vtu":{"source":"iana","extensions":["vtu"]},"model/vrml":{"source":"iana","compressible":false,"extensions":["wrl","vrml"]},"model/x3d+binary":{"source":"apache","compressible":false,"extensions":["x3db","x3dbz"]},"model/x3d+fastinfoset":{"source":"iana","extensions":["x3db"]},"model/x3d+vrml":{"source":"apache","compressible":false,"extensions":["x3dv","x3dvz"]},"model/x3d+xml":{"source":"iana","compressible":true,"extensions":["x3d","x3dz"]},"model/x3d-vrml":{"source":"iana","extensions":["x3dv"]},"multipart/alternative":{"source":"iana","compressible":false},"multipart/appledouble":{"source":"iana"},"multipart/byteranges":{"source":"iana"},"multipart/digest":{"source":"iana"},"multipart/encrypted":{"source":"iana","compressible":false},"multipart/form-data":{"source":"iana","compressible":false},"multipart/header-set":{"source":"iana"},"multipart/mixed":{"source":"iana"},"multipart/multilingual":{"source":"iana"},"multipart/parallel":{"source":"iana"},"multipart/related":{"source":"iana","compressible":false},"multipart/report":{"source":"iana"},"multipart/signed":{"source":"iana","compressible":false},"multipart/vnd.bint.med-plus":{"source":"iana"},"multipart/voice-message":{"source":"iana"},"multipart/x-mixed-replace":{"source":"iana"},"text/1d-interleaved-parityfec":{"source":"iana"},"text/cache-manifest":{"source":"iana","compressible":true,"extensions":["appcache","manifest"]},"text/calendar":{"source":"iana","extensions":["ics","ifb"]},"text/calender":{"compressible":true},"text/cmd":{"compressible":true},"text/coffeescript":{"extensions":["coffee","litcoffee"]},"text/css":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["css"]},"text/csv":{"source":"iana","compressible":true,"extensions":["csv"]},"text/csv-schema":{"source":"iana"},"text/directory":{"source":"iana"},"text/dns":{"source":"iana"},"text/ecmascript":{"source":"iana"},"text/encaprtp":{"source":"iana"},"text/enriched":{"source":"iana"},"text/flexfec":{"source":"iana"},"text/fwdred":{"source":"iana"},"text/grammar-ref-list":{"source":"iana"},"text/html":{"source":"iana","compressible":true,"extensions":["html","htm","shtml"]},"text/jade":{"extensions":["jade"]},"text/javascript":{"source":"iana","compressible":true},"text/jcr-cnd":{"source":"iana"},"text/jsx":{"compressible":true,"extensions":["jsx"]},"text/less":{"compressible":true,"extensions":["less"]},"text/markdown":{"source":"iana","compressible":true,"extensions":["markdown","md"]},"text/mathml":{"source":"nginx","extensions":["mml"]},"text/mdx":{"compressible":true,"extensions":["mdx"]},"text/mizar":{"source":"iana"},"text/n3":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["n3"]},"text/parameters":{"source":"iana","charset":"UTF-8"},"text/parityfec":{"source":"iana"},"text/plain":{"source":"iana","compressible":true,"extensions":["txt","text","conf","def","list","log","in","ini"]},"text/provenance-notation":{"source":"iana","charset":"UTF-8"},"text/prs.fallenstein.rst":{"source":"iana"},"text/prs.lines.tag":{"source":"iana","extensions":["dsc"]},"text/prs.prop.logic":{"source":"iana"},"text/raptorfec":{"source":"iana"},"text/red":{"source":"iana"},"text/rfc822-headers":{"source":"iana"},"text/richtext":{"source":"iana","compressible":true,"extensions":["rtx"]},"text/rtf":{"source":"iana","compressible":true,"extensions":["rtf"]},"text/rtp-enc-aescm128":{"source":"iana"},"text/rtploopback":{"source":"iana"},"text/rtx":{"source":"iana"},"text/sgml":{"source":"iana","extensions":["sgml","sgm"]},"text/shex":{"extensions":["shex"]},"text/slim":{"extensions":["slim","slm"]},"text/strings":{"source":"iana"},"text/stylus":{"extensions":["stylus","styl"]},"text/t140":{"source":"iana"},"text/tab-separated-values":{"source":"iana","compressible":true,"extensions":["tsv"]},"text/troff":{"source":"iana","extensions":["t","tr","roff","man","me","ms"]},"text/turtle":{"source":"iana","charset":"UTF-8","extensions":["ttl"]},"text/ulpfec":{"source":"iana"},"text/uri-list":{"source":"iana","compressible":true,"extensions":["uri","uris","urls"]},"text/vcard":{"source":"iana","compressible":true,"extensions":["vcard"]},"text/vnd.a":{"source":"iana"},"text/vnd.abc":{"source":"iana"},"text/vnd.ascii-art":{"source":"iana"},"text/vnd.curl":{"source":"iana","extensions":["curl"]},"text/vnd.curl.dcurl":{"source":"apache","extensions":["dcurl"]},"text/vnd.curl.mcurl":{"source":"apache","extensions":["mcurl"]},"text/vnd.curl.scurl":{"source":"apache","extensions":["scurl"]},"text/vnd.debian.copyright":{"source":"iana","charset":"UTF-8"},"text/vnd.dmclientscript":{"source":"iana"},"text/vnd.dvb.subtitle":{"source":"iana","extensions":["sub"]},"text/vnd.esmertec.theme-descriptor":{"source":"iana","charset":"UTF-8"},"text/vnd.ficlab.flt":{"source":"iana"},"text/vnd.fly":{"source":"iana","extensions":["fly"]},"text/vnd.fmi.flexstor":{"source":"iana","extensions":["flx"]},"text/vnd.gml":{"source":"iana"},"text/vnd.graphviz":{"source":"iana","extensions":["gv"]},"text/vnd.hgl":{"source":"iana"},"text/vnd.in3d.3dml":{"source":"iana","extensions":["3dml"]},"text/vnd.in3d.spot":{"source":"iana","extensions":["spot"]},"text/vnd.iptc.newsml":{"source":"iana"},"text/vnd.iptc.nitf":{"source":"iana"},"text/vnd.latex-z":{"source":"iana"},"text/vnd.motorola.reflex":{"source":"iana"},"text/vnd.ms-mediapackage":{"source":"iana"},"text/vnd.net2phone.commcenter.command":{"source":"iana"},"text/vnd.radisys.msml-basic-layout":{"source":"iana"},"text/vnd.senx.warpscript":{"source":"iana"},"text/vnd.si.uricatalogue":{"source":"iana"},"text/vnd.sosi":{"source":"iana"},"text/vnd.sun.j2me.app-descriptor":{"source":"iana","charset":"UTF-8","extensions":["jad"]},"text/vnd.trolltech.linguist":{"source":"iana","charset":"UTF-8"},"text/vnd.wap.si":{"source":"iana"},"text/vnd.wap.sl":{"source":"iana"},"text/vnd.wap.wml":{"source":"iana","extensions":["wml"]},"text/vnd.wap.wmlscript":{"source":"iana","extensions":["wmls"]},"text/vtt":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["vtt"]},"text/x-asm":{"source":"apache","extensions":["s","asm"]},"text/x-c":{"source":"apache","extensions":["c","cc","cxx","cpp","h","hh","dic"]},"text/x-component":{"source":"nginx","extensions":["htc"]},"text/x-fortran":{"source":"apache","extensions":["f","for","f77","f90"]},"text/x-gwt-rpc":{"compressible":true},"text/x-handlebars-template":{"extensions":["hbs"]},"text/x-java-source":{"source":"apache","extensions":["java"]},"text/x-jquery-tmpl":{"compressible":true},"text/x-lua":{"extensions":["lua"]},"text/x-markdown":{"compressible":true,"extensions":["mkd"]},"text/x-nfo":{"source":"apache","extensions":["nfo"]},"text/x-opml":{"source":"apache","extensions":["opml"]},"text/x-org":{"compressible":true,"extensions":["org"]},"text/x-pascal":{"source":"apache","extensions":["p","pas"]},"text/x-processing":{"compressible":true,"extensions":["pde"]},"text/x-sass":{"extensions":["sass"]},"text/x-scss":{"extensions":["scss"]},"text/x-setext":{"source":"apache","extensions":["etx"]},"text/x-sfv":{"source":"apache","extensions":["sfv"]},"text/x-suse-ymp":{"compressible":true,"extensions":["ymp"]},"text/x-uuencode":{"source":"apache","extensions":["uu"]},"text/x-vcalendar":{"source":"apache","extensions":["vcs"]},"text/x-vcard":{"source":"apache","extensions":["vcf"]},"text/xml":{"source":"iana","compressible":true,"extensions":["xml"]},"text/xml-external-parsed-entity":{"source":"iana"},"text/yaml":{"extensions":["yaml","yml"]},"video/1d-interleaved-parityfec":{"source":"iana"},"video/3gpp":{"source":"iana","extensions":["3gp","3gpp"]},"video/3gpp-tt":{"source":"iana"},"video/3gpp2":{"source":"iana","extensions":["3g2"]},"video/bmpeg":{"source":"iana"},"video/bt656":{"source":"iana"},"video/celb":{"source":"iana"},"video/dv":{"source":"iana"},"video/encaprtp":{"source":"iana"},"video/flexfec":{"source":"iana"},"video/h261":{"source":"iana","extensions":["h261"]},"video/h263":{"source":"iana","extensions":["h263"]},"video/h263-1998":{"source":"iana"},"video/h263-2000":{"source":"iana"},"video/h264":{"source":"iana","extensions":["h264"]},"video/h264-rcdo":{"source":"iana"},"video/h264-svc":{"source":"iana"},"video/h265":{"source":"iana"},"video/iso.segment":{"source":"iana"},"video/jpeg":{"source":"iana","extensions":["jpgv"]},"video/jpeg2000":{"source":"iana"},"video/jpm":{"source":"apache","extensions":["jpm","jpgm"]},"video/mj2":{"source":"iana","extensions":["mj2","mjp2"]},"video/mp1s":{"source":"iana"},"video/mp2p":{"source":"iana"},"video/mp2t":{"source":"iana","extensions":["ts"]},"video/mp4":{"source":"iana","compressible":false,"extensions":["mp4","mp4v","mpg4"]},"video/mp4v-es":{"source":"iana"},"video/mpeg":{"source":"iana","compressible":false,"extensions":["mpeg","mpg","mpe","m1v","m2v"]},"video/mpeg4-generic":{"source":"iana"},"video/mpv":{"source":"iana"},"video/nv":{"source":"iana"},"video/ogg":{"source":"iana","compressible":false,"extensions":["ogv"]},"video/parityfec":{"source":"iana"},"video/pointer":{"source":"iana"},"video/quicktime":{"source":"iana","compressible":false,"extensions":["qt","mov"]},"video/raptorfec":{"source":"iana"},"video/raw":{"source":"iana"},"video/rtp-enc-aescm128":{"source":"iana"},"video/rtploopback":{"source":"iana"},"video/rtx":{"source":"iana"},"video/smpte291":{"source":"iana"},"video/smpte292m":{"source":"iana"},"video/ulpfec":{"source":"iana"},"video/vc1":{"source":"iana"},"video/vc2":{"source":"iana"},"video/vnd.cctv":{"source":"iana"},"video/vnd.dece.hd":{"source":"iana","extensions":["uvh","uvvh"]},"video/vnd.dece.mobile":{"source":"iana","extensions":["uvm","uvvm"]},"video/vnd.dece.mp4":{"source":"iana"},"video/vnd.dece.pd":{"source":"iana","extensions":["uvp","uvvp"]},"video/vnd.dece.sd":{"source":"iana","extensions":["uvs","uvvs"]},"video/vnd.dece.video":{"source":"iana","extensions":["uvv","uvvv"]},"video/vnd.directv.mpeg":{"source":"iana"},"video/vnd.directv.mpeg-tts":{"source":"iana"},"video/vnd.dlna.mpeg-tts":{"source":"iana"},"video/vnd.dvb.file":{"source":"iana","extensions":["dvb"]},"video/vnd.fvt":{"source":"iana","extensions":["fvt"]},"video/vnd.hns.video":{"source":"iana"},"video/vnd.iptvforum.1dparityfec-1010":{"source":"iana"},"video/vnd.iptvforum.1dparityfec-2005":{"source":"iana"},"video/vnd.iptvforum.2dparityfec-1010":{"source":"iana"},"video/vnd.iptvforum.2dparityfec-2005":{"source":"iana"},"video/vnd.iptvforum.ttsavc":{"source":"iana"},"video/vnd.iptvforum.ttsmpeg2":{"source":"iana"},"video/vnd.motorola.video":{"source":"iana"},"video/vnd.motorola.videop":{"source":"iana"},"video/vnd.mpegurl":{"source":"iana","extensions":["mxu","m4u"]},"video/vnd.ms-playready.media.pyv":{"source":"iana","extensions":["pyv"]},"video/vnd.nokia.interleaved-multimedia":{"source":"iana"},"video/vnd.nokia.mp4vr":{"source":"iana"},"video/vnd.nokia.videovoip":{"source":"iana"},"video/vnd.objectvideo":{"source":"iana"},"video/vnd.radgamettools.bink":{"source":"iana"},"video/vnd.radgamettools.smacker":{"source":"iana"},"video/vnd.sealed.mpeg1":{"source":"iana"},"video/vnd.sealed.mpeg4":{"source":"iana"},"video/vnd.sealed.swf":{"source":"iana"},"video/vnd.sealedmedia.softseal.mov":{"source":"iana"},"video/vnd.uvvu.mp4":{"source":"iana","extensions":["uvu","uvvu"]},"video/vnd.vivo":{"source":"iana","extensions":["viv"]},"video/vnd.youtube.yt":{"source":"iana"},"video/vp8":{"source":"iana"},"video/webm":{"source":"apache","compressible":false,"extensions":["webm"]},"video/x-f4v":{"source":"apache","extensions":["f4v"]},"video/x-fli":{"source":"apache","extensions":["fli"]},"video/x-flv":{"source":"apache","compressible":false,"extensions":["flv"]},"video/x-m4v":{"source":"apache","extensions":["m4v"]},"video/x-matroska":{"source":"apache","compressible":false,"extensions":["mkv","mk3d","mks"]},"video/x-mng":{"source":"apache","extensions":["mng"]},"video/x-ms-asf":{"source":"apache","extensions":["asf","asx"]},"video/x-ms-vob":{"source":"apache","extensions":["vob"]},"video/x-ms-wm":{"source":"apache","extensions":["wm"]},"video/x-ms-wmv":{"source":"apache","compressible":false,"extensions":["wmv"]},"video/x-ms-wmx":{"source":"apache","extensions":["wmx"]},"video/x-ms-wvx":{"source":"apache","extensions":["wvx"]},"video/x-msvideo":{"source":"apache","extensions":["avi"]},"video/x-sgi-movie":{"source":"apache","extensions":["movie"]},"video/x-smv":{"source":"apache","extensions":["smv"]},"x-conference/x-cooltalk":{"source":"apache","extensions":["ice"]},"x-shader/x-fragment":{"compressible":true},"x-shader/x-vertex":{"compressible":true}}; + +/***/ }), + +/***/ 125: +/***/ (function(module) { + +// API +module.exports = state; + +/** + * Creates initial state object + * for iteration over list + * + * @param {array|object} list - list to iterate over + * @param {function|null} sortMethod - function to use for keys sort, + * or `null` to keep them as is + * @returns {object} - initial state object + */ +function state(list, sortMethod) +{ + var isNamedList = !Array.isArray(list) + , initState = + { + index : 0, + keyedList: isNamedList || sortMethod ? Object.keys(list) : null, + jobs : {}, + results : isNamedList ? {} : [], + size : isNamedList ? Object.keys(list).length : list.length + } + ; + + if (sortMethod) + { + // sort array keys based on it's values + // sort object's keys just on own merit + initState.keyedList.sort(isNamedList ? sortMethod : function(a, b) + { + return sortMethod(list[a], list[b]); + }); + } + + return initState; +} + + +/***/ }), + +/***/ 128: +/***/ (function(module, __unusedexports, __webpack_require__) { + +/*! + * mime-db + * Copyright(c) 2014 Jonathan Ong + * MIT Licensed + */ + +/** + * Module exports. + */ + +module.exports = __webpack_require__(118) + + +/***/ }), + +/***/ 154: +/***/ (function(module) { + +module.exports = defer; + +/** + * Runs provided function on next iteration of the event loop + * + * @param {function} fn - function to run + */ +function defer(fn) +{ + var nextTick = typeof setImmediate == 'function' + ? setImmediate + : ( + typeof process == 'object' && typeof process.nextTick == 'function' + ? process.nextTick + : null + ); + + if (nextTick) + { + nextTick(fn); + } + else + { + setTimeout(fn, 0); + } +} + + +/***/ }), + +/***/ 164: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var Stream = __webpack_require__(413).Stream; +var util = __webpack_require__(669); + +module.exports = DelayedStream; +function DelayedStream() { + this.source = null; + this.dataSize = 0; + this.maxDataSize = 1024 * 1024; + this.pauseStream = true; + + this._maxDataSizeExceeded = false; + this._released = false; + this._bufferedEvents = []; +} +util.inherits(DelayedStream, Stream); + +DelayedStream.create = function(source, options) { + var delayedStream = new this(); + + options = options || {}; + for (var option in options) { + delayedStream[option] = options[option]; + } + + delayedStream.source = source; + + var realEmit = source.emit; + source.emit = function() { + delayedStream._handleEmit(arguments); + return realEmit.apply(source, arguments); + }; + + source.on('error', function() {}); + if (delayedStream.pauseStream) { + source.pause(); + } + + return delayedStream; +}; + +Object.defineProperty(DelayedStream.prototype, 'readable', { + configurable: true, + enumerable: true, + get: function() { + return this.source.readable; + } +}); + +DelayedStream.prototype.setEncoding = function() { + return this.source.setEncoding.apply(this.source, arguments); +}; + +DelayedStream.prototype.resume = function() { + if (!this._released) { + this.release(); + } + + this.source.resume(); +}; + +DelayedStream.prototype.pause = function() { + this.source.pause(); +}; + +DelayedStream.prototype.release = function() { + this._released = true; + + this._bufferedEvents.forEach(function(args) { + this.emit.apply(this, args); + }.bind(this)); + this._bufferedEvents = []; +}; + +DelayedStream.prototype.pipe = function() { + var r = Stream.prototype.pipe.apply(this, arguments); + this.resume(); + return r; +}; + +DelayedStream.prototype._handleEmit = function(args) { + if (this._released) { + this.emit.apply(this, args); + return; + } + + if (args[0] === 'data') { + this.dataSize += args[1].length; + this._checkIfMaxDataSizeExceeded(); + } + + this._bufferedEvents.push(args); +}; + +DelayedStream.prototype._checkIfMaxDataSizeExceeded = function() { + if (this._maxDataSizeExceeded) { + return; + } + + if (this.dataSize <= this.maxDataSize) { + return; + } + + this._maxDataSizeExceeded = true; + var message = + 'DelayedStream#maxDataSize of ' + this.maxDataSize + ' bytes exceeded.' + this.emit('error', new Error(message)); +}; + + +/***/ }), + +/***/ 176: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var serialOrdered = __webpack_require__(275); + +// Public API +module.exports = serial; + +/** + * Runs iterator over provided array elements in series + * + * @param {array|object} list - array or object (named list) to iterate over + * @param {function} iterator - iterator to run + * @param {function} callback - invoked when all elements processed + * @returns {function} - jobs terminator + */ +function serial(list, iterator, callback) +{ + return serialOrdered(list, iterator, null, callback); +} + + +/***/ }), + +/***/ 211: +/***/ (function(module) { + +module.exports = require("https"); + +/***/ }), + +/***/ 213: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var util = __webpack_require__(669); +var Stream = __webpack_require__(413).Stream; +var DelayedStream = __webpack_require__(164); + +module.exports = CombinedStream; +function CombinedStream() { + this.writable = false; + this.readable = true; + this.dataSize = 0; + this.maxDataSize = 2 * 1024 * 1024; + this.pauseStreams = true; + + this._released = false; + this._streams = []; + this._currentStream = null; + this._insideLoop = false; + this._pendingNext = false; +} +util.inherits(CombinedStream, Stream); + +CombinedStream.create = function(options) { + var combinedStream = new this(); + + options = options || {}; + for (var option in options) { + combinedStream[option] = options[option]; + } + + return combinedStream; +}; + +CombinedStream.isStreamLike = function(stream) { + return (typeof stream !== 'function') + && (typeof stream !== 'string') + && (typeof stream !== 'boolean') + && (typeof stream !== 'number') + && (!Buffer.isBuffer(stream)); +}; + +CombinedStream.prototype.append = function(stream) { + var isStreamLike = CombinedStream.isStreamLike(stream); + + if (isStreamLike) { + if (!(stream instanceof DelayedStream)) { + var newStream = DelayedStream.create(stream, { + maxDataSize: Infinity, + pauseStream: this.pauseStreams, + }); + stream.on('data', this._checkDataSize.bind(this)); + stream = newStream; + } + + this._handleErrors(stream); + + if (this.pauseStreams) { + stream.pause(); + } + } + + this._streams.push(stream); + return this; +}; + +CombinedStream.prototype.pipe = function(dest, options) { + Stream.prototype.pipe.call(this, dest, options); + this.resume(); + return dest; +}; + +CombinedStream.prototype._getNext = function() { + this._currentStream = null; + + if (this._insideLoop) { + this._pendingNext = true; + return; // defer call + } + + this._insideLoop = true; + try { + do { + this._pendingNext = false; + this._realGetNext(); + } while (this._pendingNext); + } finally { + this._insideLoop = false; + } +}; + +CombinedStream.prototype._realGetNext = function() { + var stream = this._streams.shift(); + + + if (typeof stream == 'undefined') { + this.end(); + return; + } + + if (typeof stream !== 'function') { + this._pipeNext(stream); + return; + } + + var getStream = stream; + getStream(function(stream) { + var isStreamLike = CombinedStream.isStreamLike(stream); + if (isStreamLike) { + stream.on('data', this._checkDataSize.bind(this)); + this._handleErrors(stream); + } + + this._pipeNext(stream); + }.bind(this)); +}; + +CombinedStream.prototype._pipeNext = function(stream) { + this._currentStream = stream; + + var isStreamLike = CombinedStream.isStreamLike(stream); + if (isStreamLike) { + stream.on('end', this._getNext.bind(this)); + stream.pipe(this, {end: false}); + return; + } + + var value = stream; + this.write(value); + this._getNext(); +}; + +CombinedStream.prototype._handleErrors = function(stream) { + var self = this; + stream.on('error', function(err) { + self._emitError(err); + }); +}; + +CombinedStream.prototype.write = function(data) { + this.emit('data', data); +}; + +CombinedStream.prototype.pause = function() { + if (!this.pauseStreams) { + return; + } + + if(this.pauseStreams && this._currentStream && typeof(this._currentStream.pause) == 'function') this._currentStream.pause(); + this.emit('pause'); +}; + +CombinedStream.prototype.resume = function() { + if (!this._released) { + this._released = true; + this.writable = true; + this._getNext(); + } + + if(this.pauseStreams && this._currentStream && typeof(this._currentStream.resume) == 'function') this._currentStream.resume(); + this.emit('resume'); +}; + +CombinedStream.prototype.end = function() { + this._reset(); + this.emit('end'); +}; + +CombinedStream.prototype.destroy = function() { + this._reset(); + this.emit('close'); +}; + +CombinedStream.prototype._reset = function() { + this.writable = false; + this._streams = []; + this._currentStream = null; +}; + +CombinedStream.prototype._checkDataSize = function() { + this._updateDataSize(); + if (this.dataSize <= this.maxDataSize) { + return; + } + + var message = + 'DelayedStream#maxDataSize of ' + this.maxDataSize + ' bytes exceeded.'; + this._emitError(new Error(message)); +}; + +CombinedStream.prototype._updateDataSize = function() { + this.dataSize = 0; + + var self = this; + this._streams.forEach(function(stream) { + if (!stream.dataSize) { + return; + } + + self.dataSize += stream.dataSize; + }); + + if (this._currentStream && this._currentStream.dataSize) { + this.dataSize += this._currentStream.dataSize; + } +}; + +CombinedStream.prototype._emitError = function(err) { + this._reset(); + this.emit('error', err); +}; + + +/***/ }), + +/***/ 258: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var async = __webpack_require__(792) + , abort = __webpack_require__(762) + ; + +// API +module.exports = iterate; + +/** + * Iterates over each job object + * + * @param {array|object} list - array or object (named list) to iterate over + * @param {function} iterator - iterator to run + * @param {object} state - current job status + * @param {function} callback - invoked when all elements processed + */ +function iterate(list, iterator, state, callback) +{ + // store current index + var key = state['keyedList'] ? state['keyedList'][state.index] : state.index; + + state.jobs[key] = runJob(iterator, key, list[key], function(error, output) + { + // don't repeat yourself + // skip secondary callbacks + if (!(key in state.jobs)) + { + return; + } + + // clean up jobs + delete state.jobs[key]; + + if (error) + { + // don't process rest of the results + // stop still active jobs + // and reset the list + abort(state); + } + else + { + state.results[key] = output; + } + + // return salvaged results + callback(error, state.results); + }); +} + +/** + * Runs iterator over provided job element + * + * @param {function} iterator - iterator to invoke + * @param {string|number} key - key/index of the element in the list of jobs + * @param {mixed} item - job description + * @param {function} callback - invoked after iterator is done with the job + * @returns {function|mixed} - job abort function or something else + */ +function runJob(iterator, key, item, callback) +{ + var aborter; + + // allow shortcut if iterator expects only two arguments + if (iterator.length == 2) + { + aborter = iterator(item, async(callback)); + } + // otherwise go with full three arguments + else + { + aborter = iterator(item, key, async(callback)); + } + + return aborter; +} + + +/***/ }), + +/***/ 275: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var iterate = __webpack_require__(258) + , initState = __webpack_require__(125) + , terminator = __webpack_require__(2) + ; + +// Public API +module.exports = serialOrdered; +// sorting helpers +module.exports.ascending = ascending; +module.exports.descending = descending; + +/** + * Runs iterator over provided sorted array elements in series + * + * @param {array|object} list - array or object (named list) to iterate over + * @param {function} iterator - iterator to run + * @param {function} sortMethod - custom sort function + * @param {function} callback - invoked when all elements processed + * @returns {function} - jobs terminator + */ +function serialOrdered(list, iterator, sortMethod, callback) +{ + var state = initState(list, sortMethod); + + iterate(list, iterator, state, function iteratorHandler(error, result) + { + if (error) + { + callback(error, result); + return; + } + + state.index++; + + // are we there yet? + if (state.index < (state['keyedList'] || list).length) + { + iterate(list, iterator, state, iteratorHandler); + return; + } + + // done here + callback(null, state.results); + }); + + return terminator.bind(state, callback); +} + +/* + * -- Sort methods + */ + +/** + * sort helper to sort array elements in ascending order + * + * @param {mixed} a - an item to compare + * @param {mixed} b - an item to compare + * @returns {number} - comparison result + */ +function ascending(a, b) +{ + return a < b ? -1 : a > b ? 1 : 0; +} + +/** + * sort helper to sort array elements in descending order + * + * @param {mixed} a - an item to compare + * @param {mixed} b - an item to compare + * @returns {number} - comparison result + */ +function descending(a, b) +{ + return -1 * ascending(a, b); +} + + +/***/ }), + +/***/ 294: +/***/ (function(module, __unusedexports, __webpack_require__) { + +module.exports = +{ + parallel : __webpack_require__(89), + serial : __webpack_require__(176), + serialOrdered : __webpack_require__(275) +}; + + +/***/ }), + +/***/ 413: +/***/ (function(module) { + +module.exports = require("stream"); + +/***/ }), + +/***/ 416: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var CombinedStream = __webpack_require__(213); +var util = __webpack_require__(669); +var path = __webpack_require__(622); +var http = __webpack_require__(605); +var https = __webpack_require__(211); +var parseUrl = __webpack_require__(835).parse; +var fs = __webpack_require__(747); +var mime = __webpack_require__(769); +var asynckit = __webpack_require__(294); +var populate = __webpack_require__(70); + +// Public API +module.exports = FormData; + +// make it a Stream +util.inherits(FormData, CombinedStream); + +/** + * Create readable "multipart/form-data" streams. + * Can be used to submit forms + * and file uploads to other web applications. + * + * @constructor + * @param {Object} options - Properties to be added/overriden for FormData and CombinedStream + */ +function FormData(options) { + if (!(this instanceof FormData)) { + return new FormData(); + } + + this._overheadLength = 0; + this._valueLength = 0; + this._valuesToMeasure = []; + + CombinedStream.call(this); + + options = options || {}; + for (var option in options) { + this[option] = options[option]; + } +} + +FormData.LINE_BREAK = '\r\n'; +FormData.DEFAULT_CONTENT_TYPE = 'application/octet-stream'; + +FormData.prototype.append = function(field, value, options) { + + options = options || {}; + + // allow filename as single option + if (typeof options == 'string') { + options = {filename: options}; + } + + var append = CombinedStream.prototype.append.bind(this); + + // all that streamy business can't handle numbers + if (typeof value == 'number') { + value = '' + value; + } + + // https://github.com/felixge/node-form-data/issues/38 + if (util.isArray(value)) { + // Please convert your array into string + // the way web server expects it + this._error(new Error('Arrays are not supported.')); + return; + } + + var header = this._multiPartHeader(field, value, options); + var footer = this._multiPartFooter(); + + append(header); + append(value); + append(footer); + + // pass along options.knownLength + this._trackLength(header, value, options); +}; + +FormData.prototype._trackLength = function(header, value, options) { + var valueLength = 0; + + // used w/ getLengthSync(), when length is known. + // e.g. for streaming directly from a remote server, + // w/ a known file a size, and not wanting to wait for + // incoming file to finish to get its size. + if (options.knownLength != null) { + valueLength += +options.knownLength; + } else if (Buffer.isBuffer(value)) { + valueLength = value.length; + } else if (typeof value === 'string') { + valueLength = Buffer.byteLength(value); + } + + this._valueLength += valueLength; + + // @check why add CRLF? does this account for custom/multiple CRLFs? + this._overheadLength += + Buffer.byteLength(header) + + FormData.LINE_BREAK.length; + + // empty or either doesn't have path or not an http response + if (!value || ( !value.path && !(value.readable && value.hasOwnProperty('httpVersion')) )) { + return; + } + + // no need to bother with the length + if (!options.knownLength) { + this._valuesToMeasure.push(value); + } +}; + +FormData.prototype._lengthRetriever = function(value, callback) { + + if (value.hasOwnProperty('fd')) { + + // take read range into a account + // `end` = Infinity –> read file till the end + // + // TODO: Looks like there is bug in Node fs.createReadStream + // it doesn't respect `end` options without `start` options + // Fix it when node fixes it. + // https://github.com/joyent/node/issues/7819 + if (value.end != undefined && value.end != Infinity && value.start != undefined) { + + // when end specified + // no need to calculate range + // inclusive, starts with 0 + callback(null, value.end + 1 - (value.start ? value.start : 0)); + + // not that fast snoopy + } else { + // still need to fetch file size from fs + fs.stat(value.path, function(err, stat) { + + var fileSize; + + if (err) { + callback(err); + return; + } + + // update final size based on the range options + fileSize = stat.size - (value.start ? value.start : 0); + callback(null, fileSize); + }); + } + + // or http response + } else if (value.hasOwnProperty('httpVersion')) { + callback(null, +value.headers['content-length']); + + // or request stream http://github.com/mikeal/request + } else if (value.hasOwnProperty('httpModule')) { + // wait till response come back + value.on('response', function(response) { + value.pause(); + callback(null, +response.headers['content-length']); + }); + value.resume(); + + // something else + } else { + callback('Unknown stream'); + } +}; + +FormData.prototype._multiPartHeader = function(field, value, options) { + // custom header specified (as string)? + // it becomes responsible for boundary + // (e.g. to handle extra CRLFs on .NET servers) + if (typeof options.header == 'string') { + return options.header; + } + + var contentDisposition = this._getContentDisposition(value, options); + var contentType = this._getContentType(value, options); + + var contents = ''; + var headers = { + // add custom disposition as third element or keep it two elements if not + 'Content-Disposition': ['form-data', 'name="' + field + '"'].concat(contentDisposition || []), + // if no content type. allow it to be empty array + 'Content-Type': [].concat(contentType || []) + }; + + // allow custom headers. + if (typeof options.header == 'object') { + populate(headers, options.header); + } + + var header; + for (var prop in headers) { + if (!headers.hasOwnProperty(prop)) continue; + header = headers[prop]; + + // skip nullish headers. + if (header == null) { + continue; + } + + // convert all headers to arrays. + if (!Array.isArray(header)) { + header = [header]; + } + + // add non-empty headers. + if (header.length) { + contents += prop + ': ' + header.join('; ') + FormData.LINE_BREAK; + } + } + + return '--' + this.getBoundary() + FormData.LINE_BREAK + contents + FormData.LINE_BREAK; +}; + +FormData.prototype._getContentDisposition = function(value, options) { + + var filename + , contentDisposition + ; + + if (typeof options.filepath === 'string') { + // custom filepath for relative paths + filename = path.normalize(options.filepath).replace(/\\/g, '/'); + } else if (options.filename || value.name || value.path) { + // custom filename take precedence + // formidable and the browser add a name property + // fs- and request- streams have path property + filename = path.basename(options.filename || value.name || value.path); + } else if (value.readable && value.hasOwnProperty('httpVersion')) { + // or try http response + filename = path.basename(value.client._httpMessage.path || ''); + } + + if (filename) { + contentDisposition = 'filename="' + filename + '"'; + } + + return contentDisposition; +}; + +FormData.prototype._getContentType = function(value, options) { + + // use custom content-type above all + var contentType = options.contentType; + + // or try `name` from formidable, browser + if (!contentType && value.name) { + contentType = mime.lookup(value.name); + } + + // or try `path` from fs-, request- streams + if (!contentType && value.path) { + contentType = mime.lookup(value.path); + } + + // or if it's http-reponse + if (!contentType && value.readable && value.hasOwnProperty('httpVersion')) { + contentType = value.headers['content-type']; + } + + // or guess it from the filepath or filename + if (!contentType && (options.filepath || options.filename)) { + contentType = mime.lookup(options.filepath || options.filename); + } + + // fallback to the default content type if `value` is not simple value + if (!contentType && typeof value == 'object') { + contentType = FormData.DEFAULT_CONTENT_TYPE; + } + + return contentType; +}; + +FormData.prototype._multiPartFooter = function() { + return function(next) { + var footer = FormData.LINE_BREAK; + + var lastPart = (this._streams.length === 0); + if (lastPart) { + footer += this._lastBoundary(); + } + + next(footer); + }.bind(this); +}; + +FormData.prototype._lastBoundary = function() { + return '--' + this.getBoundary() + '--' + FormData.LINE_BREAK; +}; + +FormData.prototype.getHeaders = function(userHeaders) { + var header; + var formHeaders = { + 'content-type': 'multipart/form-data; boundary=' + this.getBoundary() + }; + + for (header in userHeaders) { + if (userHeaders.hasOwnProperty(header)) { + formHeaders[header.toLowerCase()] = userHeaders[header]; + } + } + + return formHeaders; +}; + +FormData.prototype.getBoundary = function() { + if (!this._boundary) { + this._generateBoundary(); + } + + return this._boundary; +}; + +FormData.prototype.getBuffer = function() { + var dataBuffer = new Buffer.alloc( 0 ); + var boundary = this.getBoundary(); + + // Create the form content. Add Line breaks to the end of data. + for (var i = 0, len = this._streams.length; i < len; i++) { + if (typeof this._streams[i] !== 'function') { + + // Add content to the buffer. + if(Buffer.isBuffer(this._streams[i])) { + dataBuffer = Buffer.concat( [dataBuffer, this._streams[i]]); + }else { + dataBuffer = Buffer.concat( [dataBuffer, Buffer.from(this._streams[i])]); + } + + // Add break after content. + if (typeof this._streams[i] !== 'string' || this._streams[i].substring( 2, boundary.length + 2 ) !== boundary) { + dataBuffer = Buffer.concat( [dataBuffer, Buffer.from(FormData.LINE_BREAK)] ); + } + } + } + + // Add the footer and return the Buffer object. + return Buffer.concat( [dataBuffer, Buffer.from(this._lastBoundary())] ); +}; + +FormData.prototype._generateBoundary = function() { + // This generates a 50 character boundary similar to those used by Firefox. + // They are optimized for boyer-moore parsing. + var boundary = '--------------------------'; + for (var i = 0; i < 24; i++) { + boundary += Math.floor(Math.random() * 10).toString(16); + } + + this._boundary = boundary; +}; + +// Note: getLengthSync DOESN'T calculate streams length +// As workaround one can calculate file size manually +// and add it as knownLength option +FormData.prototype.getLengthSync = function() { + var knownLength = this._overheadLength + this._valueLength; + + // Don't get confused, there are 3 "internal" streams for each keyval pair + // so it basically checks if there is any value added to the form + if (this._streams.length) { + knownLength += this._lastBoundary().length; + } + + // https://github.com/form-data/form-data/issues/40 + if (!this.hasKnownLength()) { + // Some async length retrievers are present + // therefore synchronous length calculation is false. + // Please use getLength(callback) to get proper length + this._error(new Error('Cannot calculate proper length in synchronous way.')); + } + + return knownLength; +}; + +// Public API to check if length of added values is known +// https://github.com/form-data/form-data/issues/196 +// https://github.com/form-data/form-data/issues/262 +FormData.prototype.hasKnownLength = function() { + var hasKnownLength = true; + + if (this._valuesToMeasure.length) { + hasKnownLength = false; + } + + return hasKnownLength; +}; + +FormData.prototype.getLength = function(cb) { + var knownLength = this._overheadLength + this._valueLength; + + if (this._streams.length) { + knownLength += this._lastBoundary().length; + } + + if (!this._valuesToMeasure.length) { + process.nextTick(cb.bind(this, null, knownLength)); + return; + } + + asynckit.parallel(this._valuesToMeasure, this._lengthRetriever, function(err, values) { + if (err) { + cb(err); + return; + } + + values.forEach(function(length) { + knownLength += length; + }); + + cb(null, knownLength); + }); +}; + +FormData.prototype.submit = function(params, cb) { + var request + , options + , defaults = {method: 'post'} + ; + + // parse provided url if it's string + // or treat it as options object + if (typeof params == 'string') { + + params = parseUrl(params); + options = populate({ + port: params.port, + path: params.pathname, + host: params.hostname, + protocol: params.protocol + }, defaults); + + // use custom params + } else { + + options = populate(params, defaults); + // if no port provided use default one + if (!options.port) { + options.port = options.protocol == 'https:' ? 443 : 80; + } + } + + // put that good code in getHeaders to some use + options.headers = this.getHeaders(params.headers); + + // https if specified, fallback to http in any other case + if (options.protocol == 'https:') { + request = https.request(options); + } else { + request = http.request(options); + } + + // get content length and fire away + this.getLength(function(err, length) { + if (err) { + this._error(err); + return; + } + + // add content length + request.setHeader('Content-Length', length); + + this.pipe(request); + if (cb) { + request.on('error', cb); + request.on('response', cb.bind(this, null)); + } + }.bind(this)); + + return request; +}; + +FormData.prototype._error = function(err) { + if (!this.error) { + this.error = err; + this.pause(); + this.emit('error', err); + } +}; + +FormData.prototype.toString = function () { + return '[object FormData]'; +}; + + +/***/ }), + +/***/ 417: +/***/ (function(module) { + +module.exports = require("crypto"); + +/***/ }), + +/***/ 431: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; + result["default"] = mod; + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const os = __importStar(__webpack_require__(87)); +const utils_1 = __webpack_require__(82); +/** + * Commands + * + * Command Format: + * ::name key=value,key=value::message + * + * Examples: + * ::warning::This is the message + * ::set-env name=MY_VAR::some value + */ +function issueCommand(command, properties, message) { + const cmd = new Command(command, properties, message); + process.stdout.write(cmd.toString() + os.EOL); +} +exports.issueCommand = issueCommand; +function issue(name, message = '') { + issueCommand(name, {}, message); +} +exports.issue = issue; +const CMD_STRING = '::'; +class Command { + constructor(command, properties, message) { + if (!command) { + command = 'missing.command'; + } + this.command = command; + this.properties = properties; + this.message = message; + } + toString() { + let cmdStr = CMD_STRING + this.command; + if (this.properties && Object.keys(this.properties).length > 0) { + cmdStr += ' '; + let first = true; + for (const key in this.properties) { + if (this.properties.hasOwnProperty(key)) { + const val = this.properties[key]; + if (val) { + if (first) { + first = false; + } + else { + cmdStr += ','; + } + cmdStr += `${key}=${escapeProperty(val)}`; + } + } + } + } + cmdStr += `${CMD_STRING}${escapeData(this.message)}`; + return cmdStr; + } +} +function escapeData(s) { + return utils_1.toCommandValue(s) + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A'); +} +function escapeProperty(s) { + return utils_1.toCommandValue(s) + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A') + .replace(/:/g, '%3A') + .replace(/,/g, '%2C'); +} +//# sourceMappingURL=command.js.map + +/***/ }), + +/***/ 470: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; + result["default"] = mod; + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const command_1 = __webpack_require__(431); +const file_command_1 = __webpack_require__(102); +const utils_1 = __webpack_require__(82); +const os = __importStar(__webpack_require__(87)); +const path = __importStar(__webpack_require__(622)); +/** + * The code to exit an action + */ +var ExitCode; +(function (ExitCode) { + /** + * A code indicating that the action was successful + */ + ExitCode[ExitCode["Success"] = 0] = "Success"; + /** + * A code indicating that the action was a failure + */ + ExitCode[ExitCode["Failure"] = 1] = "Failure"; +})(ExitCode = exports.ExitCode || (exports.ExitCode = {})); +//----------------------------------------------------------------------- +// Variables +//----------------------------------------------------------------------- +/** + * Sets env variable for this action and future actions in the job + * @param name the name of the variable to set + * @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function exportVariable(name, val) { + const convertedVal = utils_1.toCommandValue(val); + process.env[name] = convertedVal; + const filePath = process.env['GITHUB_ENV'] || ''; + if (filePath) { + const delimiter = '_GitHubActionsFileCommandDelimeter_'; + const commandValue = `${name}<<${delimiter}${os.EOL}${convertedVal}${os.EOL}${delimiter}`; + file_command_1.issueCommand('ENV', commandValue); + } + else { + command_1.issueCommand('set-env', { name }, convertedVal); + } +} +exports.exportVariable = exportVariable; +/** + * Registers a secret which will get masked from logs + * @param secret value of the secret + */ +function setSecret(secret) { + command_1.issueCommand('add-mask', {}, secret); +} +exports.setSecret = setSecret; +/** + * Prepends inputPath to the PATH (for this action and future actions) + * @param inputPath + */ +function addPath(inputPath) { + const filePath = process.env['GITHUB_PATH'] || ''; + if (filePath) { + file_command_1.issueCommand('PATH', inputPath); + } + else { + command_1.issueCommand('add-path', {}, inputPath); + } + process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`; +} +exports.addPath = addPath; +/** + * Gets the value of an input. The value is also trimmed. + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns string + */ +function getInput(name, options) { + const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || ''; + if (options && options.required && !val) { + throw new Error(`Input required and not supplied: ${name}`); + } + return val.trim(); +} +exports.getInput = getInput; +/** + * Sets the value of an output. + * + * @param name name of the output to set + * @param value value to store. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function setOutput(name, value) { + command_1.issueCommand('set-output', { name }, value); +} +exports.setOutput = setOutput; +/** + * Enables or disables the echoing of commands into stdout for the rest of the step. + * Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set. + * + */ +function setCommandEcho(enabled) { + command_1.issue('echo', enabled ? 'on' : 'off'); +} +exports.setCommandEcho = setCommandEcho; +//----------------------------------------------------------------------- +// Results +//----------------------------------------------------------------------- +/** + * Sets the action status to failed. + * When the action exits it will be with an exit code of 1 + * @param message add error issue message + */ +function setFailed(message) { + process.exitCode = ExitCode.Failure; + error(message); +} +exports.setFailed = setFailed; +//----------------------------------------------------------------------- +// Logging Commands +//----------------------------------------------------------------------- +/** + * Gets whether Actions Step Debug is on or not + */ +function isDebug() { + return process.env['RUNNER_DEBUG'] === '1'; +} +exports.isDebug = isDebug; +/** + * Writes debug message to user log + * @param message debug message + */ +function debug(message) { + command_1.issueCommand('debug', {}, message); +} +exports.debug = debug; +/** + * Adds an error issue + * @param message error issue message. Errors will be converted to string via toString() + */ +function error(message) { + command_1.issue('error', message instanceof Error ? message.toString() : message); +} +exports.error = error; +/** + * Adds an warning issue + * @param message warning issue message. Errors will be converted to string via toString() + */ +function warning(message) { + command_1.issue('warning', message instanceof Error ? message.toString() : message); +} +exports.warning = warning; +/** + * Writes info to log with console.log. + * @param message info message + */ +function info(message) { + process.stdout.write(message + os.EOL); +} +exports.info = info; +/** + * Begin an output group. + * + * Output until the next `groupEnd` will be foldable in this group + * + * @param name The name of the output group + */ +function startGroup(name) { + command_1.issue('group', name); +} +exports.startGroup = startGroup; +/** + * End an output group. + */ +function endGroup() { + command_1.issue('endgroup'); +} +exports.endGroup = endGroup; +/** + * Wrap an asynchronous function call in a group. + * + * Returns the same type as the function itself. + * + * @param name The name of the group + * @param fn The function to wrap in the group + */ +function group(name, fn) { + return __awaiter(this, void 0, void 0, function* () { + startGroup(name); + let result; + try { + result = yield fn(); + } + finally { + endGroup(); + } + return result; + }); +} +exports.group = group; +//----------------------------------------------------------------------- +// Wrapper action state +//----------------------------------------------------------------------- +/** + * Saves state for current action, the state can only be retrieved by this action's post job execution. + * + * @param name name of the state to store + * @param value value to store. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function saveState(name, value) { + command_1.issueCommand('save-state', { name }, value); +} +exports.saveState = saveState; +/** + * Gets the value of an state set by this action's main execution. + * + * @param name name of the state to get + * @returns string + */ +function getState(name) { + return process.env[`STATE_${name}`] || ''; +} +exports.getState = getState; +//# sourceMappingURL=core.js.map + +/***/ }), + +/***/ 605: +/***/ (function(module) { + +module.exports = require("http"); + +/***/ }), + +/***/ 618: +/***/ (function(module, __unusedexports, __webpack_require__) { + +/*! For license information please see mailgun.js.LICENSE.txt */ +!function(e,t){ true?module.exports=t():undefined}(this,(function(){return(()=>{var e={271:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=r(185);class n extends o.EventTarget{constructor(){throw super(),new TypeError("AbortSignal cannot be constructed directly")}get aborted(){const e=s.get(this);if("boolean"!=typeof e)throw new TypeError("Expected 'this' to be an 'AbortSignal' object, but got "+(null===this?"null":typeof this));return e}}o.defineEventAttribute(n.prototype,"abort");const s=new WeakMap;Object.defineProperties(n.prototype,{aborted:{enumerable:!0}}),"function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag&&Object.defineProperty(n.prototype,Symbol.toStringTag,{configurable:!0,value:"AbortSignal"});class i{constructor(){a.set(this,function(){const e=Object.create(n.prototype);return o.EventTarget.call(e),s.set(e,!1),e}())}get signal(){return u(this)}abort(){var e;e=u(this),!1===s.get(e)&&(s.set(e,!0),e.dispatchEvent({type:"abort"}))}}const a=new WeakMap;function u(e){const t=a.get(e);if(null==t)throw new TypeError("Expected 'this' to be an 'AbortController' object, but got "+(null===e?"null":typeof e));return t}Object.defineProperties(i.prototype,{signal:{enumerable:!0},abort:{enumerable:!0}}),"function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag&&Object.defineProperty(i.prototype,Symbol.toStringTag,{configurable:!0,value:"AbortController"}),t.AbortController=i,t.AbortSignal=n,t.default=i,e.exports=i,e.exports.AbortController=e.exports.default=i,e.exports.AbortSignal=n},990:function(e,t,r){"use strict";var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var n=o(r(765)),s=function(){function e(e){this.formData=e}return e.prototype.client=function(e){return new n.default(e,this.formData)},e}();t.default=s},765:function(e,t,r){"use strict";var o=this&&this.__assign||function(){return(o=Object.assign||function(e){for(var t,r=1,o=arguments.length;r{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=r(78),n=(r(955),function(){function e(e){this.request=e}return e.prototype._parsePageNumber=function(e){return e.split("/").pop()},e.prototype._parsePage=function(e,t){return{id:e,number:this._parsePageNumber(t),url:t}},e.prototype._parsePageLinks=function(e){var t=this;return Object.entries(e.body.paging).reduce((function(e,r){var o=r[0],n=r[1];return e[o]=t._parsePage(o,n),e}),{})},e.prototype._parseEventList=function(e){return{items:e.body.items,pages:this._parsePageLinks(e)}},e.prototype.get=function(e,t){var r,n=this;return t&&t.page?(r=o("/v2",e,"events",t.page),delete t.page):r=o("/v2",e,"events"),this.request.get(r,t).then((function(e){return n._parseEventList(e)}))},e}());t.default=n},853:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),r(955);var o=function(){function e(e){this.request=e}return e.prototype.list=function(e){var t=this;return this.request.get("/v1/ip_pools",e).then((function(e){return t.parseIpPoolsResponse(e)}))},e.prototype.create=function(e){return this.request.post("/v1/ip_pools",e).then((function(e){return null==e?void 0:e.body}))},e.prototype.update=function(e,t){return this.request.patch("/v1/ip_pools/"+e,t).then((function(e){return null==e?void 0:e.body}))},e.prototype.delete=function(e,t){return this.request.delete("/v1/ip_pools/"+e,t).then((function(e){return null==e?void 0:e.body}))},e.prototype.parseIpPoolsResponse=function(e){return e.body.ip_pools},e}();t.default=o},580:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),r(955);var o=function(){function e(e){this.request=e}return e.prototype.list=function(e){var t=this;return this.request.get("/v3/ips",e).then((function(e){return t.parseIpsResponse(e)}))},e.prototype.get=function(e){var t=this;return this.request.get("/v3/ips/"+e).then((function(e){return t.parseIpsResponse(e)}))},e.prototype.parseIpsResponse=function(e){return e.body},e}();t.default=o},616:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e){this.request=e}return e.prototype._parseResponse=function(e){return e.body?e.body:e},e.prototype.create=function(e,t){return t.message?this.request.postMulti("/v3/"+e+"/messages.mime",t).then(this._parseResponse):this.request.postMulti("/v3/"+e+"/messages",t).then(this._parseResponse)},e}();t.default=r},726:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e){this.request=e}return e.prototype.get=function(e,t){var r={};return Array.isArray(e)&&(e=e.join(",")),r.addresses=e,t&&(r.syntax_only=!1),this.request.get("/v3/address/parse",r).then((function(e){return e.body}))},e}();t.default=r},955:function(e,t,r){"use strict";var o=this&&this.__assign||function(){return(o=Object.assign||function(e){for(var t,r=1,o=arguments.length;r0&&n[n.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!n||s[1]>n[0]&&s[1]0&&(f.searchParams=r.query,delete f.query),[4,l.default(u.default(this.url,t),o({method:e.toLocaleUpperCase(),headers:i,throwHttpErrors:!1},f))];case 1:return(null==(p=s.sent())?void 0:p.ok)?[3,6]:(null==p?void 0:p.body)&&d(p.body)?[4,(m=p.body,_=[],new Promise((function(e,t){m.on("data",(function(e){return _.push(e)})),m.on("error",t),m.on("end",(function(){return e(Buffer.concat(_).toString("utf8"))}))})))]:[3,3];case 2:return y=s.sent(),[3,5];case 3:return[4,null==p?void 0:p.json()];case 4:y=s.sent(),s.label=5;case 5:throw h=y,new c.default({status:null==p?void 0:p.status,statusText:null==p?void 0:p.statusText,body:{message:h}});case 6:return b={},[4,null==p?void 0:p.json()];case 7:return[2,(b.body=s.sent(),b.status=null==p?void 0:p.status,b)]}var m,_}))}))},e.prototype.query=function(e,t,r,n){return this.request(e,t,o({query:r},n))},e.prototype.command=function(e,t,r,n){return this.request(e,t,o({headers:{"Content-Type":"application/x-www-form-urlencoded"},body:r},n))},e.prototype.get=function(e,t,r){return this.query("get",e,t,r)},e.prototype.head=function(e,t,r){return this.query("head",e,t,r)},e.prototype.options=function(e,t,r){return this.query("options",e,t,r)},e.prototype.post=function(e,t,r){return this.command("post",e,t,r)},e.prototype.postMulti=function(e,t){var r=new this.formData;return Object.keys(t).filter((function(e){return t[e]})).forEach((function(e){if("attachment"!==e)Array.isArray(t[e])?t[e].forEach((function(t){r.append(e,t)})):r.append(e,t[e]);else{var o=t.attachment;if(Array.isArray(o))o.forEach((function(t){var o=t.data?t.data:t,n=f(t);r.append(e,o,n)}));else{var n=d(o)?o:o.data,s=f(o);r.append(e,n,s)}}})),this.command("post",e,r,{headers:{"Content-Type":null}})},e.prototype.put=function(e,t,r){return this.command("put",e,t,r)},e.prototype.patch=function(e,t,r){return this.command("patch",e,t,r)},e.prototype.delete=function(e,t,r){return this.command("delete",e,t,r)},e}();t.default=p},893:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e){this.request=e}return e.prototype.list=function(e){return this.request.get("/v3/routes",e).then((function(e){return e.body.items}))},e.prototype.get=function(e){return this.request.get("/v3/routes/"+e).then((function(e){return e.body.route}))},e.prototype.create=function(e){return this.request.post("/v3/routes",e).then((function(e){return e.body.route}))},e.prototype.update=function(e,t){return this.request.put("/v3/routes/"+e,t).then((function(e){return e.body}))},e.prototype.destroy=function(e){return this.request.delete("/v3/routes/"+e).then((function(e){return e.body}))},e}();t.default=r},154:function(e,t,r){"use strict";var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var n=o(r(78)),s=function(e){this.start=new Date(e.start),this.end=new Date(e.end),this.resolution=e.resolution,this.stats=e.stats.map((function(e){return e.time=new Date(e.time),e}))},i=function(){function e(e){this.request=e}return e.prototype._parseStats=function(e){return new s(e.body)},e.prototype.getDomain=function(e,t){return this.request.get(n.default("/v3",e,"stats/total"),t).then(this._parseStats)},e.prototype.getAccount=function(e){return this.request.get("/v3/stats/total",e).then(this._parseStats)},e}();t.default=i},526:function(e,t,r){"use strict";var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var n=o(r(835)),s=o(r(78)),i={headers:{"Content-Type":"application/json"}},a=function(e){this.type="bounces",this.address=e.address,this.code=+e.code,this.error=e.error,this.created_at=new Date(e.created_at)},u=function(e){this.type="complaints",this.address=e.address,this.created_at=new Date(e.created_at)},l=function(e){this.type="unsubscribes",this.address=e.address,this.tags=e.tags,this.created_at=new Date(e.created_at)},c=function(){function e(e){this.request=e,this.models={bounces:a,complaints:u,unsubscribes:l}}return e.prototype._parsePage=function(e,t){var r=n.default.parse(t,!0).query;return{id:e,page:r.page,address:r.address,url:t}},e.prototype._parsePageLinks=function(e){var t=this;return Object.entries(e.body.paging).reduce((function(e,r){var o=r[0],n=r[1];return e[o]=t._parsePage(o,n),e}),{})},e.prototype._parseList=function(e,t){var r={};return r.items=e.body.items.map((function(e){return new t(e)})),r.pages=this._parsePageLinks(e),r},e.prototype._parseItem=function(e,t){return new t(e.body)},e.prototype.list=function(e,t,r){var o=this,n=this.models[t];return this.request.get(s.default("v3",e,t),r).then((function(e){return o._parseList(e,n)}))},e.prototype.get=function(e,t,r){var o=this,n=this.models[t];return this.request.get(s.default("v3",e,t,encodeURIComponent(r))).then((function(e){return o._parseItem(e,n)}))},e.prototype.create=function(e,t,r){return Array.isArray(r)||(r=[r]),this.request.post(s.default("v3",e,t),r,i).then((function(e){return e.body}))},e.prototype.destroy=function(e,t,r){return this.request.delete(s.default("v3",e,t,encodeURIComponent(r))).then((function(e){return e.body}))},e}();t.default=c,e.exports=c},335:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e){this.request=e}return e.prototype.get=function(e){return this.request.get("/v3/address/validate",{address:e}).then((function(e){return e.body}))},e}();t.default=r},632:function(e,t,r){"use strict";var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var n=o(r(78)),s=function(e,t){this.id=e,this.url=t.url},i=function(){function e(e){this.request=e}return e.prototype._parseWebhookList=function(e){return e.body.webhooks},e.prototype._parseWebhookWithID=function(e){return function(t){return new s(e,t.body.webhook)}},e.prototype._parseWebhookTest=function(e){return{code:e.body.code,message:e.body.message}},e.prototype.list=function(e,t){return this.request.get(n.default("/v2/domains",e,"webhooks"),t).then(this._parseWebhookList)},e.prototype.get=function(e,t){return this.request.get(n.default("/v2/domains",e,"webhooks",t)).then(this._parseWebhookWithID(t))},e.prototype.create=function(e,t,r,o){return o?this.request.put(n.default("/v2/domains",e,"webhooks",t,"test"),{url:r}).then(this._parseWebhookTest):this.request.post(n.default("/v2/domains",e,"webhooks"),{id:t,url:r}).then(this._parseWebhookWithID(t))},e.prototype.update=function(e,t,r){return this.request.put(n.default("/v2/domains",e,"webhooks",t),{url:r}).then(this._parseWebhookWithID(t))},e.prototype.destroy=function(e,t){return this.request.delete(n.default("/v2/domains",e,"webhooks",t)).then(this._parseWebhookWithID(t))},e}();t.default=i},706:e=>{!function(){"use strict";e.exports=function(e){return(e instanceof Buffer?e:Buffer.from(e.toString(),"binary")).toString("base64")}}()},175:e=>{"use strict";e.exports=function(e){if(!/^data:/i.test(e))throw new TypeError('`uri` does not appear to be a Data URI (must begin with "data:")');const t=(e=e.replace(/\r?\n/g,"")).indexOf(",");if(-1===t||t<=4)throw new TypeError("malformed data: URI");const r=e.substring(5,t).split(";");let o="",n=!1;const s=r[0]||"text/plain";let i=s;for(let e=1;e{"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=new WeakMap,o=new WeakMap;function n(e){const t=r.get(e);return console.assert(null!=t,"'this' is expected an Event object, but got",e),t}function s(e){null==e.passiveListener?e.event.cancelable&&(e.canceled=!0,"function"==typeof e.event.preventDefault&&e.event.preventDefault()):"undefined"!=typeof console&&"function"==typeof console.error&&console.error("Unable to preventDefault inside passive event listener invocation.",e.passiveListener)}function i(e,t){r.set(this,{eventTarget:e,event:t,eventPhase:2,currentTarget:e,canceled:!1,stopped:!1,immediateStopped:!1,passiveListener:null,timeStamp:t.timeStamp||Date.now()}),Object.defineProperty(this,"isTrusted",{value:!1,enumerable:!0});const o=Object.keys(t);for(let e=0;e0){const e=new Array(arguments.length);for(let t=0;t{const{Readable:o}=r(413),n=new WeakMap;class s{constructor(e=[],t={type:""}){let r=0;const o=e.map((e=>{let t;return t=e instanceof Buffer?e:ArrayBuffer.isView(e)?Buffer.from(e.buffer,e.byteOffset,e.byteLength):e instanceof ArrayBuffer?Buffer.from(e):e instanceof s?e:Buffer.from("string"==typeof e?e:String(e)),r+=t.length||t.size||0,t})),i=void 0===t.type?"":String(t.type).toLowerCase();n.set(this,{type:/[^\u0020-\u007E]/.test(i)?"":i,size:r,parts:o})}get size(){return n.get(this).size}get type(){return n.get(this).type}async text(){return Buffer.from(await this.arrayBuffer()).toString()}async arrayBuffer(){const e=new Uint8Array(this.size);let t=0;for await(const r of this.stream())e.set(r,t),t+=r.length;return e.buffer}stream(){return o.from(async function*(e){for(const t of e)"stream"in t?yield*t.stream():yield t}(n.get(this).parts))}slice(e=0,t=this.size,r=""){const{size:o}=this;let i=e<0?Math.max(o+e,0):Math.min(e,o),a=t<0?Math.max(o+t,0):Math.min(t,o);const u=Math.max(a-i,0),l=n.get(this).parts.values(),c=[];let d=0;for(const e of l){const t=ArrayBuffer.isView(e)?e.byteLength:e.size;if(i&&t<=i)i-=t,a-=t;else{const r=e.slice(i,Math.min(t,a));if(c.push(r),d+=ArrayBuffer.isView(r)?r.byteLength:r.size,i=0,d>=u)break}}const f=new s([],{type:r});return Object.assign(n.get(f),{size:u,parts:c}),f}get[Symbol.toStringTag](){return"Blob"}static[Symbol.hasInstance](e){return"object"==typeof e&&"function"==typeof e.stream&&0===e.stream.length&&"function"==typeof e.constructor&&/^(Blob|File)$/.test(e[Symbol.toStringTag])}}Object.defineProperties(s.prototype,{size:{enumerable:!0},type:{enumerable:!0},slice:{enumerable:!0}}),e.exports=s},556:(e,t,r)=>{"use strict";const o=r(711),n=r(271);if(global.fetch||(global.fetch=(e,t)=>o(e,{highWaterMark:1e7,...t})),global.Headers||(global.Headers=o.Headers),global.Request||(global.Request=o.Request),global.Response||(global.Response=o.Response),global.AbortController||(global.AbortController=n),!global.ReadableStream)try{global.ReadableStream=r(377)}catch(e){}e.exports=r(721)},721:function(e){var t;t=function(){"use strict";const e={},t=e=>"undefined"!=typeof self&&self&&e in self?self:"undefined"!=typeof window&&window&&e in window?window:"undefined"!=typeof global&&global&&e in global?global:"undefined"!=typeof globalThis&&globalThis?globalThis:void 0,r=["Headers","Request","Response","ReadableStream","fetch","AbortController","FormData"];for(const o of r)Object.defineProperty(e,o,{get(){const e=t(o),r=e&&e[o];return"function"==typeof r?r.bind(e):r}});const o=e=>null!==e&&"object"==typeof e,n="function"==typeof e.AbortController,s="function"==typeof e.ReadableStream,i="function"==typeof e.FormData,a=(t,r)=>{const o=new e.Headers(t||{}),n=r instanceof e.Headers,s=new e.Headers(r||{});for(const[e,t]of s)n&&"undefined"===t||void 0===t?o.delete(e):o.set(e,t);return o},u=(...e)=>{let t={},r={};for(const n of e){if(Array.isArray(n))Array.isArray(t)||(t=[]),t=[...t,...n];else if(o(n)){for(let[e,r]of Object.entries(n))o(r)&&e in t&&(r=u(t[e],r)),t={...t,[e]:r};o(n.headers)&&(r=a(r,n.headers))}t.headers=r}return t},l=["get","post","put","patch","head","delete"],c={json:"application/json",text:"text/*",formData:"multipart/form-data",arrayBuffer:"*/*",blob:"*/*"},d=[413,429,503],f=Symbol("stop");class p extends Error{constructor(e){super(e.statusText||String(0===e.status||e.status?e.status:"Unknown response error")),this.name="HTTPError",this.response=e}}class h extends Error{constructor(e){super("Request timed out"),this.name="TimeoutError",this.request=e}}const y=e=>new Promise((t=>setTimeout(t,e))),b=e=>l.includes(e)?e.toUpperCase():e,m={limit:2,methods:["get","put","head","delete","options","trace"],statusCodes:[408,413,429,500,502,503,504],afterStatusCodes:d},_=(e={})=>{if("number"==typeof e)return{...m,limit:e};if(e.methods&&!Array.isArray(e.methods))throw new Error("retry.methods must be an array");if(e.statusCodes&&!Array.isArray(e.statusCodes))throw new Error("retry.statusCodes must be an array");return{...m,...e,afterStatusCodes:d}},g=2147483647;class w{constructor(t,r={}){if(this._retryCount=0,this._input=t,this._options={credentials:this._input.credentials||"same-origin",...r,headers:a(this._input.headers,r.headers),hooks:u({beforeRequest:[],beforeRetry:[],afterResponse:[]},r.hooks),method:b(r.method||this._input.method),prefixUrl:String(r.prefixUrl||""),retry:_(r.retry),throwHttpErrors:!1!==r.throwHttpErrors,timeout:void 0===r.timeout?1e4:r.timeout,fetch:r.fetch||e.fetch},"string"!=typeof this._input&&!(this._input instanceof URL||this._input instanceof e.Request))throw new TypeError("`input` must be a string, URL, or Request");if(this._options.prefixUrl&&"string"==typeof this._input){if(this._input.startsWith("/"))throw new Error("`input` must not begin with a slash when using `prefixUrl`");this._options.prefixUrl.endsWith("/")||(this._options.prefixUrl+="/"),this._input=this._options.prefixUrl+this._input}if(n&&(this.abortController=new e.AbortController,this._options.signal&&this._options.signal.addEventListener("abort",(()=>{this.abortController.abort()})),this._options.signal=this.abortController.signal),this.request=new e.Request(this._input,this._options),this._options.searchParams){const t="?"+new URLSearchParams(this._options.searchParams).toString(),r=this.request.url.replace(/(?:\?.*?)?(?=#|$)/,t);!(i&&this._options.body instanceof e.FormData||this._options.body instanceof URLSearchParams)||this._options.headers&&this._options.headers["content-type"]||this.request.headers.delete("content-type"),this.request=new e.Request(new e.Request(r,this.request),this._options)}void 0!==this._options.json&&(this._options.body=JSON.stringify(this._options.json),this.request.headers.set("content-type","application/json"),this.request=new e.Request(this.request,{body:this._options.body}));const o=async()=>{if(this._options.timeout>g)throw new RangeError("The `timeout` option cannot be greater than 2147483647");await y(1);let t=await this._fetch();for(const r of this._options.hooks.afterResponse){const o=await r(this.request,this._options,this._decorateResponse(t.clone()));o instanceof e.Response&&(t=o)}if(this._decorateResponse(t),!t.ok&&this._options.throwHttpErrors)throw new p(t);if(this._options.onDownloadProgress){if("function"!=typeof this._options.onDownloadProgress)throw new TypeError("The `onDownloadProgress` option must be a function");if(!s)throw new Error("Streams are not supported in your environment. `ReadableStream` is missing.");return this._stream(t.clone(),this._options.onDownloadProgress)}return t},l=this._options.retry.methods.includes(this.request.method.toLowerCase())?this._retry(o):o();for(const[e,t]of Object.entries(c))l[e]=async()=>{this.request.headers.set("accept",this.request.headers.get("accept")||t);const o=(await l).clone();if("json"===e){if(204===o.status)return"";if(r.parseJson)return r.parseJson(await o.text())}return o[e]()};return l}_calculateRetryDelay(e){if(this._retryCount++,this._retryCountthis._options.retry.maxRetryAfter?0:e}if(413===e.response.status)return 0}return.3*2**(this._retryCount-1)*1e3}return 0}_decorateResponse(e){return this._options.parseJson&&(e.json=async()=>this._options.parseJson(await e.text())),e}async _retry(e){try{return await e()}catch(t){const r=Math.min(this._calculateRetryDelay(t),g);if(0!==r&&this._retryCount>0){await y(r);for(const e of this._options.hooks.beforeRetry)if(await e({request:this.request,options:this._options,error:t,retryCount:this._retryCount})===f)return;return this._retry(e)}if(this._options.throwHttpErrors)throw t}}async _fetch(){for(const e of this._options.hooks.beforeRequest){const t=await e(this.request,this._options);if(t instanceof Request){this.request=t;break}if(t instanceof Response)return t}return!1===this._options.timeout?this._options.fetch(this.request.clone()):(e=this.request.clone(),t=this.abortController,r=this._options,new Promise(((o,n)=>{const s=setTimeout((()=>{t&&t.abort(),n(new h(e))}),r.timeout);r.fetch(e).then(o).catch(n).then((()=>{clearTimeout(s)}))})));var e,t,r}_stream(t,r){const o=Number(t.headers.get("content-length"))||0;let n=0;return new e.Response(new e.ReadableStream({start(e){const s=t.body.getReader();r&&r({percent:0,transferredBytes:0,totalBytes:o},new Uint8Array),async function t(){const{done:i,value:a}=await s.read();i?e.close():(r&&(n+=a.byteLength,r({percent:0===o?0:n/o,transferredBytes:n,totalBytes:o},a)),e.enqueue(a),t())}()}}))}}const v=(...e)=>{for(const t of e)if((!o(t)||Array.isArray(t))&&void 0!==t)throw new TypeError("The `options` argument must be an object");return u({},...e)},S=e=>{const t=(t,r)=>new w(t,v(e,r));for(const r of l)t[r]=(t,o)=>new w(t,v(e,o,{method:r}));return t.HTTPError=p,t.TimeoutError=h,t.create=e=>S(v(e)),t.extend=t=>S(v(e,t)),t.stop=f,t};return S()},e.exports=t()},711:(e,t,r)=>{"use strict";t=e.exports=I;const o=r(605),n=r(211),s=r(761),i=r(413),a=r(175),u=r(669),l=r(30),c=r(417),d=r(835);class f extends Error{constructor(e,t){super(e),Error.captureStackTrace(this,this.constructor),this.type=t}get name(){return this.constructor.name}get[Symbol.toStringTag](){return this.constructor.name}}class p extends f{constructor(e,t,r){super(e,t),r&&(this.code=this.errno=r.code,this.erroredSysCall=r.syscall)}}const h=Symbol.toStringTag,y=e=>"object"==typeof e&&"function"==typeof e.append&&"function"==typeof e.delete&&"function"==typeof e.get&&"function"==typeof e.getAll&&"function"==typeof e.has&&"function"==typeof e.set&&"function"==typeof e.sort&&"URLSearchParams"===e[h],b=e=>"object"==typeof e&&"function"==typeof e.arrayBuffer&&"string"==typeof e.type&&"function"==typeof e.stream&&"function"==typeof e.constructor&&/^(Blob|File)$/.test(e[h]);function m(e){return"object"==typeof e&&"function"==typeof e.append&&"function"==typeof e.set&&"function"==typeof e.get&&"function"==typeof e.getAll&&"function"==typeof e.delete&&"function"==typeof e.keys&&"function"==typeof e.values&&"function"==typeof e.entries&&"function"==typeof e.constructor&&"FormData"===e[h]}const _="\r\n",g="-".repeat(2),w=Buffer.byteLength(_),v=e=>`${g}${e}${g}${_.repeat(2)}`;function S(e,t,r){let o="";return o+=`${g}${e}\r\n`,o+=`Content-Disposition: form-data; name="${t}"`,b(r)&&(o+=`; filename="${r.name}"\r\n`,o+=`Content-Type: ${r.type||"application/octet-stream"}`),`${o}${_.repeat(2)}`}const T=Symbol("Body internals");class R{constructor(e,{size:t=0}={}){let r=null;null===e?e=null:y(e)?e=Buffer.from(e.toString()):b(e)||Buffer.isBuffer(e)||(u.types.isAnyArrayBuffer(e)?e=Buffer.from(e):ArrayBuffer.isView(e)?e=Buffer.from(e.buffer,e.byteOffset,e.byteLength):e instanceof i||(m(e)?(r=`NodeFetchFormDataBoundary${c.randomBytes(8).toString("hex")}`,e=i.Readable.from(async function*(e,t){for(const[r,o]of e)yield S(t,r,o),b(o)?yield*o.stream():yield o,yield _;yield v(t)}(e,r))):e=Buffer.from(String(e)))),this[T]={body:e,boundary:r,disturbed:!1,error:null},this.size=t,e instanceof i&&e.on("error",(e=>{const t=e instanceof f?e:new p(`Invalid response body while trying to fetch ${this.url}: ${e.message}`,"system",e);this[T].error=t}))}get body(){return this[T].body}get bodyUsed(){return this[T].disturbed}async arrayBuffer(){const{buffer:e,byteOffset:t,byteLength:r}=await q(this);return e.slice(t,t+r)}async blob(){const e=this.headers&&this.headers.get("content-type")||this[T].body&&this[T].body.type||"",t=await this.buffer();return new l([t],{type:e})}async json(){const e=await q(this);return JSON.parse(e.toString())}async text(){return(await q(this)).toString()}buffer(){return q(this)}}async function q(e){if(e[T].disturbed)throw new TypeError(`body used already for: ${e.url}`);if(e[T].disturbed=!0,e[T].error)throw e[T].error;let{body:t}=e;if(null===t)return Buffer.alloc(0);if(b(t)&&(t=t.stream()),Buffer.isBuffer(t))return t;if(!(t instanceof i))return Buffer.alloc(0);const r=[];let o=0;try{for await(const n of t){if(e.size>0&&o+n.length>e.size){const r=new p(`content size at ${e.url} over limit: ${e.size}`,"max-size");throw t.destroy(r),r}o+=n.length,r.push(n)}}catch(t){throw t instanceof f?t:new p(`Invalid response body while trying to fetch ${e.url}: ${t.message}`,"system",t)}if(!0!==t.readableEnded&&!0!==t._readableState.ended)throw new p(`Premature close of server response while trying to fetch ${e.url}`);try{return r.every((e=>"string"==typeof e))?Buffer.from(r.join("")):Buffer.concat(r,o)}catch(t){throw new p(`Could not create Buffer from response body for ${e.url}: ${t.message}`,"system",t)}}Object.defineProperties(R.prototype,{body:{enumerable:!0},bodyUsed:{enumerable:!0},arrayBuffer:{enumerable:!0},blob:{enumerable:!0},json:{enumerable:!0},text:{enumerable:!0}});const P=(e,t)=>{let r,o,{body:n}=e;if(e.bodyUsed)throw new Error("cannot clone body after it is used");return n instanceof i&&"function"!=typeof n.getBoundary&&(r=new i.PassThrough({highWaterMark:t}),o=new i.PassThrough({highWaterMark:t}),n.pipe(r),n.pipe(o),e[T].body=r,n=o),n},E=(e,t)=>null===e?null:"string"==typeof e?"text/plain;charset=UTF-8":y(e)?"application/x-www-form-urlencoded;charset=UTF-8":b(e)?e.type||null:Buffer.isBuffer(e)||u.types.isAnyArrayBuffer(e)||ArrayBuffer.isView(e)?null:e&&"function"==typeof e.getBoundary?`multipart/form-data;boundary=${e.getBoundary()}`:m(e)?`multipart/form-data; boundary=${t[T].boundary}`:e instanceof i?null:"text/plain;charset=UTF-8",j="function"==typeof o.validateHeaderName?o.validateHeaderName:e=>{if(!/^[\^`\-\w!#$%&'*+.|~]+$/.test(e)){const t=new TypeError(`Header name must be a valid HTTP token [${e}]`);throw Object.defineProperty(t,"code",{value:"ERR_INVALID_HTTP_TOKEN"}),t}},k="function"==typeof o.validateHeaderValue?o.validateHeaderValue:(e,t)=>{if(/[^\t\u0020-\u007E\u0080-\u00FF]/.test(t)){const t=new TypeError(`Invalid character in header content ["${e}"]`);throw Object.defineProperty(t,"code",{value:"ERR_INVALID_CHAR"}),t}};class O extends URLSearchParams{constructor(e){let t=[];if(e instanceof O){const r=e.raw();for(const[e,o]of Object.entries(r))t.push(...o.map((t=>[e,t])))}else if(null==e);else{if("object"!=typeof e||u.types.isBoxedPrimitive(e))throw new TypeError("Failed to construct 'Headers': The provided value is not of type '(sequence> or record)");{const r=e[Symbol.iterator];if(null==r)t.push(...Object.entries(e));else{if("function"!=typeof r)throw new TypeError("Header pairs must be iterable");t=[...e].map((e=>{if("object"!=typeof e||u.types.isBoxedPrimitive(e))throw new TypeError("Each header pair must be an iterable object");return[...e]})).map((e=>{if(2!==e.length)throw new TypeError("Each header pair must be a name/value tuple");return[...e]}))}}}return t=t.length>0?t.map((([e,t])=>(j(e),k(e,String(t)),[String(e).toLowerCase(),String(t)]))):void 0,super(t),new Proxy(this,{get(e,t,r){switch(t){case"append":case"set":return(e,o)=>(j(e),k(e,String(o)),URLSearchParams.prototype[t].call(r,String(e).toLowerCase(),String(o)));case"delete":case"has":case"getAll":return e=>(j(e),URLSearchParams.prototype[t].call(r,String(e).toLowerCase()));case"keys":return()=>(e.sort(),new Set(URLSearchParams.prototype.keys.call(e)).keys());default:return Reflect.get(e,t,r)}}})}get[Symbol.toStringTag](){return this.constructor.name}toString(){return Object.prototype.toString.call(this)}get(e){const t=this.getAll(e);if(0===t.length)return null;let r=t.join(", ");return/^content-encoding$/i.test(e)&&(r=r.toLowerCase()),r}forEach(e){for(const t of this.keys())e(this.get(t),t)}*values(){for(const e of this.keys())yield this.get(e)}*entries(){for(const e of this.keys())yield[e,this.get(e)]}[Symbol.iterator](){return this.entries()}raw(){return[...this.keys()].reduce(((e,t)=>(e[t]=this.getAll(t),e)),{})}[Symbol.for("nodejs.util.inspect.custom")](){return[...this.keys()].reduce(((e,t)=>{const r=this.getAll(t);return e[t]="host"===t?r[0]:r.length>1?r:r[0],e}),{})}}Object.defineProperties(O.prototype,["get","entries","forEach","values"].reduce(((e,t)=>(e[t]={enumerable:!0},e)),{}));const C=new Set([301,302,303,307,308]),A=e=>C.has(e),B=Symbol("Response internals");class x extends R{constructor(e=null,t={}){super(e,t);const r=t.status||200,o=new O(t.headers);if(null!==e&&!o.has("Content-Type")){const t=E(e);t&&o.append("Content-Type",t)}this[B]={url:t.url,status:r,statusText:t.statusText||"",headers:o,counter:t.counter,highWaterMark:t.highWaterMark}}get url(){return this[B].url||""}get status(){return this[B].status}get ok(){return this[B].status>=200&&this[B].status<300}get redirected(){return this[B].counter>0}get statusText(){return this[B].statusText}get headers(){return this[B].headers}get highWaterMark(){return this[B].highWaterMark}clone(){return new x(P(this,this.highWaterMark),{url:this.url,status:this.status,statusText:this.statusText,headers:this.headers,ok:this.ok,redirected:this.redirected,size:this.size})}static redirect(e,t=302){if(!A(t))throw new RangeError('Failed to execute "redirect" on "response": Invalid status code');return new x(null,{headers:{location:new URL(e).toString()},status:t})}get[Symbol.toStringTag](){return"Response"}}Object.defineProperties(x.prototype,{url:{enumerable:!0},status:{enumerable:!0},ok:{enumerable:!0},redirected:{enumerable:!0},statusText:{enumerable:!0},headers:{enumerable:!0},clone:{enumerable:!0}});const W=Symbol("Request internals"),L=e=>"object"==typeof e&&"object"==typeof e[W];class z extends R{constructor(e,t={}){let r;L(e)?r=new URL(e.url):(r=new URL(e),e={});let o=t.method||e.method||"GET";if(o=o.toUpperCase(),(null!=t.body||L(e))&&null!==e.body&&("GET"===o||"HEAD"===o))throw new TypeError("Request with GET/HEAD method cannot have body");const n=t.body?t.body:L(e)&&null!==e.body?P(e):null;super(n,{size:t.size||e.size||0});const s=new O(t.headers||e.headers||{});if(null!==n&&!s.has("Content-Type")){const e=E(n,this);e&&s.append("Content-Type",e)}let i=L(e)?e.signal:null;if("signal"in t&&(i=t.signal),null!==i&&("object"!=typeof(a=i)||"AbortSignal"!==a[h]))throw new TypeError("Expected signal to be an instanceof AbortSignal");var a;this[W]={method:o,redirect:t.redirect||e.redirect||"follow",headers:s,parsedURL:r,signal:i},this.follow=void 0===t.follow?void 0===e.follow?20:e.follow:t.follow,this.compress=void 0===t.compress?void 0===e.compress||e.compress:t.compress,this.counter=t.counter||e.counter||0,this.agent=t.agent||e.agent,this.highWaterMark=t.highWaterMark||e.highWaterMark||16384,this.insecureHTTPParser=t.insecureHTTPParser||e.insecureHTTPParser||!1}get method(){return this[W].method}get url(){return d.format(this[W].parsedURL)}get headers(){return this[W].headers}get redirect(){return this[W].redirect}get signal(){return this[W].signal}clone(){return new z(this)}get[Symbol.toStringTag](){return"Request"}}Object.defineProperties(z.prototype,{method:{enumerable:!0},url:{enumerable:!0},headers:{enumerable:!0},redirect:{enumerable:!0},clone:{enumerable:!0},signal:{enumerable:!0}});class M extends f{constructor(e,t="aborted"){super(e,t)}}const $=new Set(["data:","http:","https:"]);async function I(e,t){return new Promise(((r,u)=>{const l=new z(e,t),c=(e=>{const{parsedURL:t}=e[W],r=new O(e[W].headers);r.has("Accept")||r.set("Accept","*/*");let o=null;if(null===e.body&&/^(post|put)$/i.test(e.method)&&(o="0"),null!==e.body){const t=(e=>{const{body:t}=e;return null===t?0:b(t)?t.size:Buffer.isBuffer(t)?t.length:t&&"function"==typeof t.getLengthSync?t.hasKnownLength&&t.hasKnownLength()?t.getLengthSync():null:m(t)?function(e,t){let r=0;for(const[o,n]of e)r+=Buffer.byteLength(S(t,o,n)),b(n)?r+=n.size:r+=Buffer.byteLength(String(n)),r+=w;return r+=Buffer.byteLength(v(t)),r}(e[T].boundary):null})(e);"number"!=typeof t||Number.isNaN(t)||(o=String(t))}o&&r.set("Content-Length",o),r.has("User-Agent")||r.set("User-Agent","node-fetch"),e.compress&&!r.has("Accept-Encoding")&&r.set("Accept-Encoding","gzip,deflate,br");let{agent:n}=e;"function"==typeof n&&(n=n(t)),r.has("Connection")||n||r.set("Connection","close");const s=(e=>{if(e.search)return e.search;const t=e.href.length-1,r=e.hash||("#"===e.href[t]?"#":"");return"?"===e.href[t-r.length]?"?":""})(t);return{path:t.pathname+s,pathname:t.pathname,hostname:t.hostname,protocol:t.protocol,port:t.port,hash:t.hash,search:t.search,query:t.query,href:t.href,method:e.method,headers:r[Symbol.for("nodejs.util.inspect.custom")](),insecureHTTPParser:e.insecureHTTPParser,agent:n}})(l);if(!$.has(c.protocol))throw new TypeError(`node-fetch cannot load ${e}. URL scheme "${c.protocol.replace(/:$/,"")}" is not supported.`);if("data:"===c.protocol){const e=a(l.url),t=new x(e,{headers:{"Content-Type":e.typeFull}});return void r(t)}const d=("https:"===c.protocol?n:o).request,{signal:f}=l;let h=null;const y=()=>{const e=new M("The operation was aborted.");u(e),l.body&&l.body instanceof i.Readable&&l.body.destroy(e),h&&h.body&&h.body.emit("error",e)};if(f&&f.aborted)return void y();const _=()=>{y(),R()},g=d(c);f&&f.addEventListener("abort",_);const R=()=>{g.abort(),f&&f.removeEventListener("abort",_)};g.on("error",(e=>{u(new p(`request to ${l.url} failed, reason: ${e.message}`,"system",e)),R()})),g.on("response",(e=>{g.setTimeout(0);const o=function(e=[]){return new O(e.reduce(((e,t,r,o)=>(r%2==0&&e.push(o.slice(r,r+2)),e)),[]).filter((([e,t])=>{try{return j(e),k(e,String(t)),!0}catch{return!1}})))}(e.rawHeaders);if(A(e.statusCode)){const n=o.get("Location"),s=null===n?null:new URL(n,l.url);switch(l.redirect){case"error":return u(new p(`uri requested responds with a redirect, redirect mode is set to error: ${l.url}`,"no-redirect")),void R();case"manual":if(null!==s)try{o.set("Location",s)}catch(e){u(e)}break;case"follow":{if(null===s)break;if(l.counter>=l.follow)return u(new p(`maximum redirect reached at: ${l.url}`,"max-redirect")),void R();const o={headers:new O(l.headers),follow:l.follow,counter:l.counter+1,agent:l.agent,compress:l.compress,method:l.method,body:l.body,signal:l.signal,size:l.size};return 303!==e.statusCode&&l.body&&t.body instanceof i.Readable?(u(new p("Cannot follow redirect with body being a readable stream","unsupported-redirect")),void R()):(303!==e.statusCode&&(301!==e.statusCode&&302!==e.statusCode||"POST"!==l.method)||(o.method="GET",o.body=void 0,o.headers.delete("content-length")),r(I(new z(s,o))),void R())}}}e.once("end",(()=>{f&&f.removeEventListener("abort",_)}));let n=i.pipeline(e,new i.PassThrough,(e=>{u(e)}));process.version<"v12.10"&&e.on("aborted",_);const a={url:l.url,status:e.statusCode,statusText:e.statusMessage,headers:o,size:l.size,counter:l.counter,highWaterMark:l.highWaterMark},c=o.get("Content-Encoding");if(!l.compress||"HEAD"===l.method||null===c||204===e.statusCode||304===e.statusCode)return h=new x(n,a),void r(h);const d={flush:s.Z_SYNC_FLUSH,finishFlush:s.Z_SYNC_FLUSH};if("gzip"===c||"x-gzip"===c)return n=i.pipeline(n,s.createGunzip(d),(e=>{u(e)})),h=new x(n,a),void r(h);if("deflate"!==c&&"x-deflate"!==c){if("br"===c)return n=i.pipeline(n,s.createBrotliDecompress(),(e=>{u(e)})),h=new x(n,a),void r(h);h=new x(n,a),r(h)}else i.pipeline(e,new i.PassThrough,(e=>{u(e)})).once("data",(e=>{n=8==(15&e[0])?i.pipeline(n,s.createInflate(),(e=>{u(e)})):i.pipeline(n,s.createInflateRaw(),(e=>{u(e)})),h=new x(n,a),r(h)}))})),((e,{body:t})=>{null===t?e.end():b(t)?t.stream().pipe(e):Buffer.isBuffer(t)?(e.write(t),e.end()):t.pipe(e)})(g,l)}))}t.AbortError=M,t.FetchError=p,t.Headers=O,t.Request=z,t.Response=x,t.default=I,t.isRedirect=A},78:e=>{function t(e){return e.replace(/[\/]+/g,"/").replace(/\/\?/g,"?").replace(/\/\#/g,"#").replace(/\:\//g,"://")}e.exports=function(){var e=[].slice.call(arguments,0).join("/");return t(e)}},377:(e,t,r)=>{"use strict";r.r(t),r.d(t,{ByteLengthQueuingStrategy:()=>pr,CountQueuingStrategy:()=>mr,ReadableByteStreamController:()=>ye,ReadableStream:()=>rr,ReadableStreamBYOBReader:()=>ze,ReadableStreamBYOBRequest:()=>he,ReadableStreamDefaultController:()=>Mt,ReadableStreamDefaultReader:()=>X,TransformStream:()=>Tr,TransformStreamDefaultController:()=>jr,WritableStream:()=>Ge,WritableStreamDefaultController:()=>yt,WritableStreamDefaultWriter:()=>ut});const o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?Symbol:e=>`Symbol(${e})`;function n(){}const s="undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:void 0;function i(e){return"object"==typeof e&&null!==e||"function"==typeof e}const a=n,u=Promise,l=Promise.prototype.then,c=Promise.resolve.bind(u),d=Promise.reject.bind(u);function f(e){return new u(e)}function p(e){return c(e)}function h(e){return d(e)}function y(e,t,r){return l.call(e,t,r)}function b(e,t,r){y(y(e,t,r),void 0,a)}function m(e,t){b(e,t)}function _(e,t){b(e,void 0,t)}function g(e,t,r){return y(e,t,r)}function w(e){y(e,void 0,a)}const v=(()=>{const e=s&&s.queueMicrotask;if("function"==typeof e)return e;const t=p(void 0);return e=>y(t,e)})();function S(e,t,r){if("function"!=typeof e)throw new TypeError("Argument is not a function");return Function.prototype.apply.call(e,t,r)}function T(e,t,r){try{return p(S(e,t,r))}catch(e){return h(e)}}class R{constructor(){this._cursor=0,this._size=0,this._front={_elements:[],_next:void 0},this._back=this._front,this._cursor=0,this._size=0}get length(){return this._size}push(e){const t=this._back;let r=t;16383===t._elements.length&&(r={_elements:[],_next:void 0}),t._elements.push(e),r!==t&&(this._back=r,t._next=r),++this._size}shift(){const e=this._front;let t=e;const r=this._cursor;let o=r+1;const n=e._elements,s=n[r];return 16384===o&&(t=e._next,o=0),--this._size,this._cursor=o,e!==t&&(this._front=t),n[r]=void 0,s}forEach(e){let t=this._cursor,r=this._front,o=r._elements;for(;!(t===o.length&&void 0===r._next||t===o.length&&(r=r._next,o=r._elements,t=0,0===o.length));)e(o[t]),++t}peek(){const e=this._front,t=this._cursor;return e._elements[t]}}function q(e,t){e._ownerReadableStream=t,t._reader=e,"readable"===t._state?k(e):"closed"===t._state?function(e){k(e),A(e)}(e):O(e,t._storedError)}function P(e,t){return ar(e._ownerReadableStream,t)}function E(e){"readable"===e._ownerReadableStream._state?C(e,new TypeError("Reader was released and can no longer be used to monitor the stream's closedness")):function(e,t){O(e,new TypeError("Reader was released and can no longer be used to monitor the stream's closedness"))}(e),e._ownerReadableStream._reader=void 0,e._ownerReadableStream=void 0}function j(e){return new TypeError("Cannot "+e+" a stream using a released reader")}function k(e){e._closedPromise=f(((t,r)=>{e._closedPromise_resolve=t,e._closedPromise_reject=r}))}function O(e,t){k(e),C(e,t)}function C(e,t){void 0!==e._closedPromise_reject&&(w(e._closedPromise),e._closedPromise_reject(t),e._closedPromise_resolve=void 0,e._closedPromise_reject=void 0)}function A(e){void 0!==e._closedPromise_resolve&&(e._closedPromise_resolve(void 0),e._closedPromise_resolve=void 0,e._closedPromise_reject=void 0)}const B=o("[[AbortSteps]]"),x=o("[[ErrorSteps]]"),W=o("[[CancelSteps]]"),L=o("[[PullSteps]]"),z=Number.isFinite||function(e){return"number"==typeof e&&isFinite(e)},M=Math.trunc||function(e){return e<0?Math.ceil(e):Math.floor(e)};function $(e,t){if(void 0!==e&&"object"!=typeof(r=e)&&"function"!=typeof r)throw new TypeError(`${t} is not an object.`);var r}function I(e,t){if("function"!=typeof e)throw new TypeError(`${t} is not a function.`)}function D(e,t){if(!function(e){return"object"==typeof e&&null!==e||"function"==typeof e}(e))throw new TypeError(`${t} is not an object.`)}function F(e,t,r){if(void 0===e)throw new TypeError(`Parameter ${t} is required in '${r}'.`)}function U(e,t,r){if(void 0===e)throw new TypeError(`${t} is required in '${r}'.`)}function H(e){return Number(e)}function N(e){return 0===e?0:e}function V(e,t){const r=Number.MAX_SAFE_INTEGER;let o=Number(e);if(o=N(o),!z(o))throw new TypeError(`${t} is not a finite number`);if(o=function(e){return N(M(e))}(o),o<0||o>r)throw new TypeError(`${t} is outside the accepted range of 0 to ${r}, inclusive`);return z(o)&&0!==o?o:0}function Q(e,t){if(!sr(e))throw new TypeError(`${t} is not a ReadableStream.`)}function Y(e){return new X(e)}function G(e,t){e._reader._readRequests.push(t)}function J(e,t,r){const o=e._reader._readRequests.shift();r?o._closeSteps():o._chunkSteps(t)}function K(e){return e._reader._readRequests.length}function Z(e){const t=e._reader;return void 0!==t&&!!ee(t)}class X{constructor(e){if(F(e,1,"ReadableStreamDefaultReader"),Q(e,"First parameter"),ir(e))throw new TypeError("This stream has already been locked for exclusive reading by another reader");q(this,e),this._readRequests=new R}get closed(){return ee(this)?this._closedPromise:h(re("closed"))}cancel(e){return ee(this)?void 0===this._ownerReadableStream?h(j("cancel")):P(this,e):h(re("cancel"))}read(){if(!ee(this))return h(re("read"));if(void 0===this._ownerReadableStream)return h(j("read from"));let e,t;const r=f(((r,o)=>{e=r,t=o}));return te(this,{_chunkSteps:t=>e({value:t,done:!1}),_closeSteps:()=>e({value:void 0,done:!0}),_errorSteps:e=>t(e)}),r}releaseLock(){if(!ee(this))throw re("releaseLock");if(void 0!==this._ownerReadableStream){if(this._readRequests.length>0)throw new TypeError("Tried to release a reader lock when that reader has pending read() calls un-settled");E(this)}}}function ee(e){return!!i(e)&&!!Object.prototype.hasOwnProperty.call(e,"_readRequests")}function te(e,t){const r=e._ownerReadableStream;r._disturbed=!0,"closed"===r._state?t._closeSteps():"errored"===r._state?t._errorSteps(r._storedError):r._readableStreamController[L](t)}function re(e){return new TypeError(`ReadableStreamDefaultReader.prototype.${e} can only be used on a ReadableStreamDefaultReader`)}Object.defineProperties(X.prototype,{cancel:{enumerable:!0},read:{enumerable:!0},releaseLock:{enumerable:!0},closed:{enumerable:!0}}),"symbol"==typeof o.toStringTag&&Object.defineProperty(X.prototype,o.toStringTag,{value:"ReadableStreamDefaultReader",configurable:!0});const oe=Object.getPrototypeOf(Object.getPrototypeOf((async function*(){})).prototype);class ne{constructor(e,t){this._ongoingPromise=void 0,this._isFinished=!1,this._reader=e,this._preventCancel=t}next(){const e=()=>this._nextSteps();return this._ongoingPromise=this._ongoingPromise?g(this._ongoingPromise,e,e):e(),this._ongoingPromise}return(e){const t=()=>this._returnSteps(e);return this._ongoingPromise?g(this._ongoingPromise,t,t):t()}_nextSteps(){if(this._isFinished)return Promise.resolve({value:void 0,done:!0});const e=this._reader;if(void 0===e._ownerReadableStream)return h(j("iterate"));let t,r;const o=f(((e,o)=>{t=e,r=o}));return te(e,{_chunkSteps:e=>{this._ongoingPromise=void 0,v((()=>t({value:e,done:!1})))},_closeSteps:()=>{this._ongoingPromise=void 0,this._isFinished=!0,E(e),t({value:void 0,done:!0})},_errorSteps:t=>{this._ongoingPromise=void 0,this._isFinished=!0,E(e),r(t)}}),o}_returnSteps(e){if(this._isFinished)return Promise.resolve({value:e,done:!0});this._isFinished=!0;const t=this._reader;if(void 0===t._ownerReadableStream)return h(j("finish iterating"));if(!this._preventCancel){const r=P(t,e);return E(t),g(r,(()=>({value:e,done:!0})))}return E(t),p({value:e,done:!0})}}const se={next(){return ie(this)?this._asyncIteratorImpl.next():h(ae("next"))},return(e){return ie(this)?this._asyncIteratorImpl.return(e):h(ae("return"))}};function ie(e){return!!i(e)&&!!Object.prototype.hasOwnProperty.call(e,"_asyncIteratorImpl")}function ae(e){return new TypeError(`ReadableStreamAsyncIterator.${e} can only be used on a ReadableSteamAsyncIterator`)}void 0!==oe&&Object.setPrototypeOf(se,oe);const ue=Number.isNaN||function(e){return e!=e};function le(e){return!!function(e){return"number"==typeof e&&(!ue(e)&&!(e<0))}(e)&&e!==1/0}function ce(e){const t=e._queue.shift();return e._queueTotalSize-=t.size,e._queueTotalSize<0&&(e._queueTotalSize=0),t.value}function de(e,t,r){if(!le(r=Number(r)))throw new RangeError("Size must be a finite, non-NaN, non-negative number.");e._queue.push({value:t,size:r}),e._queueTotalSize+=r}function fe(e){e._queue=new R,e._queueTotalSize=0}function pe(e){return e.slice()}class he{constructor(){throw new TypeError("Illegal constructor")}get view(){if(!me(this))throw Ae("view");return this._view}respond(e){if(!me(this))throw Ae("respond");if(F(e,1,"respond"),e=V(e,"First parameter"),void 0===this._associatedReadableByteStreamController)throw new TypeError("This BYOB request has been invalidated");this._view.buffer,function(e,t){if(!le(t=Number(t)))throw new RangeError("bytesWritten must be a finite");Ee(e,t)}(this._associatedReadableByteStreamController,e)}respondWithNewView(e){if(!me(this))throw Ae("respondWithNewView");if(F(e,1,"respondWithNewView"),!ArrayBuffer.isView(e))throw new TypeError("You can only respond with array buffer views");if(0===e.byteLength)throw new TypeError("chunk must have non-zero byteLength");if(0===e.buffer.byteLength)throw new TypeError("chunk's buffer must have non-zero byteLength");if(void 0===this._associatedReadableByteStreamController)throw new TypeError("This BYOB request has been invalidated");!function(e,t){const r=e._pendingPullIntos.peek();if(r.byteOffset+r.bytesFilled!==t.byteOffset)throw new RangeError("The region specified by view does not match byobRequest");if(r.byteLength!==t.byteLength)throw new RangeError("The buffer of view has different capacity than byobRequest");r.buffer=t.buffer,Ee(e,t.byteLength)}(this._associatedReadableByteStreamController,e)}}Object.defineProperties(he.prototype,{respond:{enumerable:!0},respondWithNewView:{enumerable:!0},view:{enumerable:!0}}),"symbol"==typeof o.toStringTag&&Object.defineProperty(he.prototype,o.toStringTag,{value:"ReadableStreamBYOBRequest",configurable:!0});class ye{constructor(){throw new TypeError("Illegal constructor")}get byobRequest(){if(!be(this))throw Be("byobRequest");if(null===this._byobRequest&&this._pendingPullIntos.length>0){const e=this._pendingPullIntos.peek(),t=new Uint8Array(e.buffer,e.byteOffset+e.bytesFilled,e.byteLength-e.bytesFilled),r=Object.create(he.prototype);!function(e,t,r){e._associatedReadableByteStreamController=t,e._view=r}(r,this,t),this._byobRequest=r}return this._byobRequest}get desiredSize(){if(!be(this))throw Be("desiredSize");return Ce(this)}close(){if(!be(this))throw Be("close");if(this._closeRequested)throw new TypeError("The stream has already been closed; do not close it again!");const e=this._controlledReadableByteStream._state;if("readable"!==e)throw new TypeError(`The stream (in ${e} state) is not in the readable state and cannot be closed`);!function(e){const t=e._controlledReadableByteStream;if(!e._closeRequested&&"readable"===t._state)if(e._queueTotalSize>0)e._closeRequested=!0;else{if(e._pendingPullIntos.length>0&&e._pendingPullIntos.peek().bytesFilled>0){const t=new TypeError("Insufficient bytes to fill elements in the given buffer");throw Oe(e,t),t}ke(e),ur(t)}}(this)}enqueue(e){if(!be(this))throw Be("enqueue");if(F(e,1,"enqueue"),!ArrayBuffer.isView(e))throw new TypeError("chunk must be an array buffer view");if(0===e.byteLength)throw new TypeError("chunk must have non-zero byteLength");if(0===e.buffer.byteLength)throw new TypeError("chunk's buffer must have non-zero byteLength");if(this._closeRequested)throw new TypeError("stream is closed or draining");const t=this._controlledReadableByteStream._state;if("readable"!==t)throw new TypeError(`The stream (in ${t} state) is not in the readable state and cannot be enqueued to`);!function(e,t){const r=e._controlledReadableByteStream;if(e._closeRequested||"readable"!==r._state)return;const o=t.buffer,n=t.byteOffset,s=t.byteLength,i=o;Z(r)?0===K(r)?ve(e,i,n,s):J(r,new Uint8Array(i,n,s),!1):Le(r)?(ve(e,i,n,s),Pe(e)):ve(e,i,n,s),_e(e)}(this,e)}error(e){if(!be(this))throw Be("error");Oe(this,e)}[W](e){this._pendingPullIntos.length>0&&(this._pendingPullIntos.peek().bytesFilled=0),fe(this);const t=this._cancelAlgorithm(e);return ke(this),t}[L](e){const t=this._controlledReadableByteStream;if(this._queueTotalSize>0){const t=this._queue.shift();this._queueTotalSize-=t.byteLength,Re(this);const r=new Uint8Array(t.buffer,t.byteOffset,t.byteLength);return void e._chunkSteps(r)}const r=this._autoAllocateChunkSize;if(void 0!==r){let t;try{t=new ArrayBuffer(r)}catch(t){return void e._errorSteps(t)}const o={buffer:t,byteOffset:0,byteLength:r,bytesFilled:0,elementSize:1,viewConstructor:Uint8Array,readerType:"default"};this._pendingPullIntos.push(o)}G(t,e),_e(this)}}function be(e){return!!i(e)&&!!Object.prototype.hasOwnProperty.call(e,"_controlledReadableByteStream")}function me(e){return!!i(e)&&!!Object.prototype.hasOwnProperty.call(e,"_associatedReadableByteStreamController")}function _e(e){(function(e){const t=e._controlledReadableByteStream;return"readable"===t._state&&(!e._closeRequested&&(!!e._started&&(!!(Z(t)&&K(t)>0)||(!!(Le(t)&&We(t)>0)||Ce(e)>0))))})(e)&&(e._pulling?e._pullAgain=!0:(e._pulling=!0,b(e._pullAlgorithm(),(()=>{e._pulling=!1,e._pullAgain&&(e._pullAgain=!1,_e(e))}),(t=>{Oe(e,t)}))))}function ge(e,t){let r=!1;"closed"===e._state&&(r=!0);const o=we(t);"default"===t.readerType?J(e,o,r):function(e,t,r){const o=e._reader._readIntoRequests.shift();r?o._closeSteps(t):o._chunkSteps(t)}(e,o,r)}function we(e){const t=e.bytesFilled,r=e.elementSize;return new e.viewConstructor(e.buffer,e.byteOffset,t/r)}function ve(e,t,r,o){e._queue.push({buffer:t,byteOffset:r,byteLength:o}),e._queueTotalSize+=o}function Se(e,t){const r=t.elementSize,o=t.bytesFilled-t.bytesFilled%r,n=Math.min(e._queueTotalSize,t.byteLength-t.bytesFilled),s=t.bytesFilled+n,i=s-s%r;let a=n,u=!1;i>o&&(a=i-t.bytesFilled,u=!0);const l=e._queue;for(;a>0;){const r=l.peek(),o=Math.min(a,r.byteLength),n=t.byteOffset+t.bytesFilled;c=t.buffer,d=n,f=r.buffer,p=r.byteOffset,h=o,new Uint8Array(c).set(new Uint8Array(f,p,h),d),r.byteLength===o?l.shift():(r.byteOffset+=o,r.byteLength-=o),e._queueTotalSize-=o,Te(e,o,t),a-=o}var c,d,f,p,h;return u}function Te(e,t,r){qe(e),r.bytesFilled+=t}function Re(e){0===e._queueTotalSize&&e._closeRequested?(ke(e),ur(e._controlledReadableByteStream)):_e(e)}function qe(e){null!==e._byobRequest&&(e._byobRequest._associatedReadableByteStreamController=void 0,e._byobRequest._view=null,e._byobRequest=null)}function Pe(e){for(;e._pendingPullIntos.length>0;){if(0===e._queueTotalSize)return;const t=e._pendingPullIntos.peek();Se(e,t)&&(je(e),ge(e._controlledReadableByteStream,t))}}function Ee(e,t){const r=e._pendingPullIntos.peek();if("closed"===e._controlledReadableByteStream._state){if(0!==t)throw new TypeError("bytesWritten must be 0 when calling respond() on a closed stream");!function(e,t){t.buffer=t.buffer;const r=e._controlledReadableByteStream;if(Le(r))for(;We(r)>0;)ge(r,je(e))}(e,r)}else!function(e,t,r){if(r.bytesFilled+t>r.byteLength)throw new RangeError("bytesWritten out of range");if(Te(e,t,r),r.bytesFilled0){const t=r.byteOffset+r.bytesFilled,n=r.buffer.slice(t-o,t);ve(e,n,0,n.byteLength)}r.buffer=r.buffer,r.bytesFilled-=o,ge(e._controlledReadableByteStream,r),Pe(e)}(e,t,r);_e(e)}function je(e){const t=e._pendingPullIntos.shift();return qe(e),t}function ke(e){e._pullAlgorithm=void 0,e._cancelAlgorithm=void 0}function Oe(e,t){const r=e._controlledReadableByteStream;"readable"===r._state&&(function(e){qe(e),e._pendingPullIntos=new R}(e),fe(e),ke(e),lr(r,t))}function Ce(e){const t=e._controlledReadableByteStream._state;return"errored"===t?null:"closed"===t?0:e._strategyHWM-e._queueTotalSize}function Ae(e){return new TypeError(`ReadableStreamBYOBRequest.prototype.${e} can only be used on a ReadableStreamBYOBRequest`)}function Be(e){return new TypeError(`ReadableByteStreamController.prototype.${e} can only be used on a ReadableByteStreamController`)}function xe(e,t){e._reader._readIntoRequests.push(t)}function We(e){return e._reader._readIntoRequests.length}function Le(e){const t=e._reader;return void 0!==t&&!!Me(t)}Object.defineProperties(ye.prototype,{close:{enumerable:!0},enqueue:{enumerable:!0},error:{enumerable:!0},byobRequest:{enumerable:!0},desiredSize:{enumerable:!0}}),"symbol"==typeof o.toStringTag&&Object.defineProperty(ye.prototype,o.toStringTag,{value:"ReadableByteStreamController",configurable:!0});class ze{constructor(e){if(F(e,1,"ReadableStreamBYOBReader"),Q(e,"First parameter"),ir(e))throw new TypeError("This stream has already been locked for exclusive reading by another reader");if(!be(e._readableStreamController))throw new TypeError("Cannot construct a ReadableStreamBYOBReader for a stream not constructed with a byte source");q(this,e),this._readIntoRequests=new R}get closed(){return Me(this)?this._closedPromise:h($e("closed"))}cancel(e){return Me(this)?void 0===this._ownerReadableStream?h(j("cancel")):P(this,e):h($e("cancel"))}read(e){if(!Me(this))return h($e("read"));if(!ArrayBuffer.isView(e))return h(new TypeError("view must be an array buffer view"));if(0===e.byteLength)return h(new TypeError("view must have non-zero byteLength"));if(0===e.buffer.byteLength)return h(new TypeError("view's buffer must have non-zero byteLength"));if(void 0===this._ownerReadableStream)return h(j("read from"));let t,r;const o=f(((e,o)=>{t=e,r=o}));return function(e,t,r){const o=e._ownerReadableStream;o._disturbed=!0,"errored"===o._state?r._errorSteps(o._storedError):function(e,t,r){const o=e._controlledReadableByteStream;let n=1;t.constructor!==DataView&&(n=t.constructor.BYTES_PER_ELEMENT);const s=t.constructor,i={buffer:t.buffer,byteOffset:t.byteOffset,byteLength:t.byteLength,bytesFilled:0,elementSize:n,viewConstructor:s,readerType:"byob"};if(e._pendingPullIntos.length>0)return e._pendingPullIntos.push(i),void xe(o,r);if("closed"!==o._state){if(e._queueTotalSize>0){if(Se(e,i)){const t=we(i);return Re(e),void r._chunkSteps(t)}if(e._closeRequested){const t=new TypeError("Insufficient bytes to fill elements in the given buffer");return Oe(e,t),void r._errorSteps(t)}}e._pendingPullIntos.push(i),xe(o,r),_e(e)}else{const e=new s(i.buffer,i.byteOffset,0);r._closeSteps(e)}}(o._readableStreamController,t,r)}(this,e,{_chunkSteps:e=>t({value:e,done:!1}),_closeSteps:e=>t({value:e,done:!0}),_errorSteps:e=>r(e)}),o}releaseLock(){if(!Me(this))throw $e("releaseLock");if(void 0!==this._ownerReadableStream){if(this._readIntoRequests.length>0)throw new TypeError("Tried to release a reader lock when that reader has pending read() calls un-settled");E(this)}}}function Me(e){return!!i(e)&&!!Object.prototype.hasOwnProperty.call(e,"_readIntoRequests")}function $e(e){return new TypeError(`ReadableStreamBYOBReader.prototype.${e} can only be used on a ReadableStreamBYOBReader`)}function Ie(e,t){const{highWaterMark:r}=e;if(void 0===r)return t;if(ue(r)||r<0)throw new RangeError("Invalid highWaterMark");return r}function De(e){const{size:t}=e;return t||(()=>1)}function Fe(e,t){$(e,t);const r=null==e?void 0:e.highWaterMark,o=null==e?void 0:e.size;return{highWaterMark:void 0===r?void 0:H(r),size:void 0===o?void 0:Ue(o,`${t} has member 'size' that`)}}function Ue(e,t){return I(e,t),t=>H(e(t))}function He(e,t,r){return I(e,r),r=>T(e,t,[r])}function Ne(e,t,r){return I(e,r),()=>T(e,t,[])}function Ve(e,t,r){return I(e,r),r=>S(e,t,[r])}function Qe(e,t,r){return I(e,r),(r,o)=>T(e,t,[r,o])}function Ye(e,t){if(!Ze(e))throw new TypeError(`${t} is not a WritableStream.`)}Object.defineProperties(ze.prototype,{cancel:{enumerable:!0},read:{enumerable:!0},releaseLock:{enumerable:!0},closed:{enumerable:!0}}),"symbol"==typeof o.toStringTag&&Object.defineProperty(ze.prototype,o.toStringTag,{value:"ReadableStreamBYOBReader",configurable:!0});class Ge{constructor(e={},t={}){void 0===e?e=null:D(e,"First parameter");const r=Fe(t,"Second parameter"),o=function(e,t){$(e,t);const r=null==e?void 0:e.abort,o=null==e?void 0:e.close,n=null==e?void 0:e.start,s=null==e?void 0:e.type,i=null==e?void 0:e.write;return{abort:void 0===r?void 0:He(r,e,`${t} has member 'abort' that`),close:void 0===o?void 0:Ne(o,e,`${t} has member 'close' that`),start:void 0===n?void 0:Ve(n,e,`${t} has member 'start' that`),write:void 0===i?void 0:Qe(i,e,`${t} has member 'write' that`),type:s}}(e,"First parameter");if(Ke(this),void 0!==o.type)throw new RangeError("Invalid type is specified");const n=De(r);!function(e,t,r,o){const n=Object.create(yt.prototype);let s=()=>{},i=()=>p(void 0),a=()=>p(void 0),u=()=>p(void 0);void 0!==t.start&&(s=()=>t.start(n)),void 0!==t.write&&(i=e=>t.write(e,n)),void 0!==t.close&&(a=()=>t.close()),void 0!==t.abort&&(u=e=>t.abort(e)),bt(e,n,s,i,a,u,r,o)}(this,o,Ie(r,1),n)}get locked(){if(!Ze(this))throw Tt("locked");return Xe(this)}abort(e){return Ze(this)?Xe(this)?h(new TypeError("Cannot abort a stream that already has a writer")):et(this,e):h(Tt("abort"))}close(){return Ze(this)?Xe(this)?h(new TypeError("Cannot close a stream that already has a writer")):st(this)?h(new TypeError("Cannot close an already-closing stream")):tt(this):h(Tt("close"))}getWriter(){if(!Ze(this))throw Tt("getWriter");return Je(this)}}function Je(e){return new ut(e)}function Ke(e){e._state="writable",e._storedError=void 0,e._writer=void 0,e._writableStreamController=void 0,e._writeRequests=new R,e._inFlightWriteRequest=void 0,e._closeRequest=void 0,e._inFlightCloseRequest=void 0,e._pendingAbortRequest=void 0,e._backpressure=!1}function Ze(e){return!!i(e)&&!!Object.prototype.hasOwnProperty.call(e,"_writableStreamController")}function Xe(e){return void 0!==e._writer}function et(e,t){const r=e._state;if("closed"===r||"errored"===r)return p(void 0);if(void 0!==e._pendingAbortRequest)return e._pendingAbortRequest._promise;let o=!1;"erroring"===r&&(o=!0,t=void 0);const n=f(((r,n)=>{e._pendingAbortRequest={_promise:void 0,_resolve:r,_reject:n,_reason:t,_wasAlreadyErroring:o}}));return e._pendingAbortRequest._promise=n,o||ot(e,t),n}function tt(e){const t=e._state;if("closed"===t||"errored"===t)return h(new TypeError(`The stream (in ${t} state) is not in the writable state and cannot be closed`));const r=f(((t,r)=>{const o={_resolve:t,_reject:r};e._closeRequest=o})),o=e._writer;var n;return void 0!==o&&e._backpressure&&"writable"===t&&xt(o),de(n=e._writableStreamController,ht,0),gt(n),r}function rt(e,t){"writable"!==e._state?nt(e):ot(e,t)}function ot(e,t){const r=e._writableStreamController;e._state="erroring",e._storedError=t;const o=e._writer;void 0!==o&&dt(o,t),!function(e){return void 0!==e._inFlightWriteRequest||void 0!==e._inFlightCloseRequest}(e)&&r._started&&nt(e)}function nt(e){e._state="errored",e._writableStreamController[x]();const t=e._storedError;if(e._writeRequests.forEach((e=>{e._reject(t)})),e._writeRequests=new R,void 0===e._pendingAbortRequest)return void it(e);const r=e._pendingAbortRequest;if(e._pendingAbortRequest=void 0,r._wasAlreadyErroring)return r._reject(t),void it(e);b(e._writableStreamController[B](r._reason),(()=>{r._resolve(),it(e)}),(t=>{r._reject(t),it(e)}))}function st(e){return void 0!==e._closeRequest||void 0!==e._inFlightCloseRequest}function it(e){void 0!==e._closeRequest&&(e._closeRequest._reject(e._storedError),e._closeRequest=void 0);const t=e._writer;void 0!==t&&jt(t,e._storedError)}function at(e,t){const r=e._writer;void 0!==r&&t!==e._backpressure&&(t?function(e){Ot(e)}(r):xt(r)),e._backpressure=t}Object.defineProperties(Ge.prototype,{abort:{enumerable:!0},close:{enumerable:!0},getWriter:{enumerable:!0},locked:{enumerable:!0}}),"symbol"==typeof o.toStringTag&&Object.defineProperty(Ge.prototype,o.toStringTag,{value:"WritableStream",configurable:!0});class ut{constructor(e){if(F(e,1,"WritableStreamDefaultWriter"),Ye(e,"First parameter"),Xe(e))throw new TypeError("This stream has already been locked for exclusive writing by another writer");this._ownerWritableStream=e,e._writer=this;const t=e._state;if("writable"===t)!st(e)&&e._backpressure?Ot(this):At(this),Pt(this);else if("erroring"===t)Ct(this,e._storedError),Pt(this);else if("closed"===t)At(this),Pt(this),kt(this);else{const t=e._storedError;Ct(this,t),Et(this,t)}}get closed(){return lt(this)?this._closedPromise:h(Rt("closed"))}get desiredSize(){if(!lt(this))throw Rt("desiredSize");if(void 0===this._ownerWritableStream)throw qt("desiredSize");return function(e){const t=e._ownerWritableStream,r=t._state;return"errored"===r||"erroring"===r?null:"closed"===r?0:_t(t._writableStreamController)}(this)}get ready(){return lt(this)?this._readyPromise:h(Rt("ready"))}abort(e){return lt(this)?void 0===this._ownerWritableStream?h(qt("abort")):function(e,t){return et(e._ownerWritableStream,t)}(this,e):h(Rt("abort"))}close(){if(!lt(this))return h(Rt("close"));const e=this._ownerWritableStream;return void 0===e?h(qt("close")):st(e)?h(new TypeError("Cannot close an already-closing stream")):ct(this)}releaseLock(){if(!lt(this))throw Rt("releaseLock");void 0!==this._ownerWritableStream&&ft(this)}write(e){return lt(this)?void 0===this._ownerWritableStream?h(qt("write to")):pt(this,e):h(Rt("write"))}}function lt(e){return!!i(e)&&!!Object.prototype.hasOwnProperty.call(e,"_ownerWritableStream")}function ct(e){return tt(e._ownerWritableStream)}function dt(e,t){"pending"===e._readyPromiseState?Bt(e,t):function(e,t){Ct(e,t)}(e,t)}function ft(e){const t=e._ownerWritableStream,r=new TypeError("Writer was released and can no longer be used to monitor the stream's closedness");dt(e,r),function(e,t){"pending"===e._closedPromiseState?jt(e,t):function(e,t){Et(e,t)}(e,t)}(e,r),t._writer=void 0,e._ownerWritableStream=void 0}function pt(e,t){const r=e._ownerWritableStream,o=r._writableStreamController,n=function(e,t){try{return e._strategySizeAlgorithm(t)}catch(t){return wt(e,t),1}}(o,t);if(r!==e._ownerWritableStream)return h(qt("write to"));const s=r._state;if("errored"===s)return h(r._storedError);if(st(r)||"closed"===s)return h(new TypeError("The stream is closing or closed and cannot be written to"));if("erroring"===s)return h(r._storedError);const i=function(e){return f(((t,r)=>{const o={_resolve:t,_reject:r};e._writeRequests.push(o)}))}(r);return function(e,t,r){try{de(e,t,r)}catch(t){return void wt(e,t)}const o=e._controlledWritableStream;st(o)||"writable"!==o._state||at(o,vt(e)),gt(e)}(o,t,n),i}Object.defineProperties(ut.prototype,{abort:{enumerable:!0},close:{enumerable:!0},releaseLock:{enumerable:!0},write:{enumerable:!0},closed:{enumerable:!0},desiredSize:{enumerable:!0},ready:{enumerable:!0}}),"symbol"==typeof o.toStringTag&&Object.defineProperty(ut.prototype,o.toStringTag,{value:"WritableStreamDefaultWriter",configurable:!0});const ht={};class yt{constructor(){throw new TypeError("Illegal constructor")}error(e){if(!i(t=this)||!Object.prototype.hasOwnProperty.call(t,"_controlledWritableStream"))throw new TypeError("WritableStreamDefaultController.prototype.error can only be used on a WritableStreamDefaultController");var t;"writable"===this._controlledWritableStream._state&&St(this,e)}[B](e){const t=this._abortAlgorithm(e);return mt(this),t}[x](){fe(this)}}function bt(e,t,r,o,n,s,i,a){t._controlledWritableStream=e,e._writableStreamController=t,t._queue=void 0,t._queueTotalSize=void 0,fe(t),t._started=!1,t._strategySizeAlgorithm=a,t._strategyHWM=i,t._writeAlgorithm=o,t._closeAlgorithm=n,t._abortAlgorithm=s;const u=vt(t);at(e,u),b(p(r()),(()=>{t._started=!0,gt(t)}),(r=>{t._started=!0,rt(e,r)}))}function mt(e){e._writeAlgorithm=void 0,e._closeAlgorithm=void 0,e._abortAlgorithm=void 0,e._strategySizeAlgorithm=void 0}function _t(e){return e._strategyHWM-e._queueTotalSize}function gt(e){const t=e._controlledWritableStream;if(!e._started)return;if(void 0!==t._inFlightWriteRequest)return;if("erroring"===t._state)return void nt(t);if(0===e._queue.length)return;const r=e._queue.peek().value;r===ht?function(e){const t=e._controlledWritableStream;(function(e){e._inFlightCloseRequest=e._closeRequest,e._closeRequest=void 0})(t),ce(e);const r=e._closeAlgorithm();mt(e),b(r,(()=>{!function(e){e._inFlightCloseRequest._resolve(void 0),e._inFlightCloseRequest=void 0,"erroring"===e._state&&(e._storedError=void 0,void 0!==e._pendingAbortRequest&&(e._pendingAbortRequest._resolve(),e._pendingAbortRequest=void 0)),e._state="closed";const t=e._writer;void 0!==t&&kt(t)}(t)}),(e=>{!function(e,t){e._inFlightCloseRequest._reject(t),e._inFlightCloseRequest=void 0,void 0!==e._pendingAbortRequest&&(e._pendingAbortRequest._reject(t),e._pendingAbortRequest=void 0),rt(e,t)}(t,e)}))}(e):function(e,t){const r=e._controlledWritableStream;!function(e){e._inFlightWriteRequest=e._writeRequests.shift()}(r),b(e._writeAlgorithm(t),(()=>{!function(e){e._inFlightWriteRequest._resolve(void 0),e._inFlightWriteRequest=void 0}(r);const t=r._state;if(ce(e),!st(r)&&"writable"===t){const t=vt(e);at(r,t)}gt(e)}),(t=>{"writable"===r._state&&mt(e),function(e,t){e._inFlightWriteRequest._reject(t),e._inFlightWriteRequest=void 0,rt(e,t)}(r,t)}))}(e,r)}function wt(e,t){"writable"===e._controlledWritableStream._state&&St(e,t)}function vt(e){return _t(e)<=0}function St(e,t){const r=e._controlledWritableStream;mt(e),ot(r,t)}function Tt(e){return new TypeError(`WritableStream.prototype.${e} can only be used on a WritableStream`)}function Rt(e){return new TypeError(`WritableStreamDefaultWriter.prototype.${e} can only be used on a WritableStreamDefaultWriter`)}function qt(e){return new TypeError("Cannot "+e+" a stream using a released writer")}function Pt(e){e._closedPromise=f(((t,r)=>{e._closedPromise_resolve=t,e._closedPromise_reject=r,e._closedPromiseState="pending"}))}function Et(e,t){Pt(e),jt(e,t)}function jt(e,t){void 0!==e._closedPromise_reject&&(w(e._closedPromise),e._closedPromise_reject(t),e._closedPromise_resolve=void 0,e._closedPromise_reject=void 0,e._closedPromiseState="rejected")}function kt(e){void 0!==e._closedPromise_resolve&&(e._closedPromise_resolve(void 0),e._closedPromise_resolve=void 0,e._closedPromise_reject=void 0,e._closedPromiseState="resolved")}function Ot(e){e._readyPromise=f(((t,r)=>{e._readyPromise_resolve=t,e._readyPromise_reject=r})),e._readyPromiseState="pending"}function Ct(e,t){Ot(e),Bt(e,t)}function At(e){Ot(e),xt(e)}function Bt(e,t){void 0!==e._readyPromise_reject&&(w(e._readyPromise),e._readyPromise_reject(t),e._readyPromise_resolve=void 0,e._readyPromise_reject=void 0,e._readyPromiseState="rejected")}function xt(e){void 0!==e._readyPromise_resolve&&(e._readyPromise_resolve(void 0),e._readyPromise_resolve=void 0,e._readyPromise_reject=void 0,e._readyPromiseState="fulfilled")}Object.defineProperties(yt.prototype,{error:{enumerable:!0}}),"symbol"==typeof o.toStringTag&&Object.defineProperty(yt.prototype,o.toStringTag,{value:"WritableStreamDefaultController",configurable:!0});const Wt="undefined"!=typeof DOMException?DOMException:void 0,Lt=function(e){if("function"!=typeof e&&"object"!=typeof e)return!1;try{return new e,!0}catch(e){return!1}}(Wt)?Wt:function(){const e=function(e,t){this.message=e||"",this.name=t||"Error",Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)};return e.prototype=Object.create(Error.prototype),Object.defineProperty(e.prototype,"constructor",{value:e,writable:!0,configurable:!0}),e}();function zt(e,t,r,o,s,i){const a=Y(e),u=Je(t);e._disturbed=!0;let l=!1,c=p(void 0);return f(((d,g)=>{let v;if(void 0!==i){if(v=()=>{const r=new Lt("Aborted","AbortError"),n=[];o||n.push((()=>"writable"===t._state?et(t,r):p(void 0))),s||n.push((()=>"readable"===e._state?ar(e,r):p(void 0))),j((()=>Promise.all(n.map((e=>e())))),!0,r)},i.aborted)return void v();i.addEventListener("abort",v)}var S,T,R;if(P(e,a._closedPromise,(e=>{o?k(!0,e):j((()=>et(t,e)),!0,e)})),P(t,u._closedPromise,(t=>{s?k(!0,t):j((()=>ar(e,t)),!0,t)})),S=e,T=a._closedPromise,R=()=>{r?k():j((()=>function(e){const t=e._ownerWritableStream,r=t._state;return st(t)||"closed"===r?p(void 0):"errored"===r?h(t._storedError):ct(e)}(u)))},"closed"===S._state?R():m(T,R),st(t)||"closed"===t._state){const t=new TypeError("the destination writable stream closed before all data could be piped to it");s?k(!0,t):j((()=>ar(e,t)),!0,t)}function q(){const e=c;return y(c,(()=>e!==c?q():void 0))}function P(e,t,r){"errored"===e._state?r(e._storedError):_(t,r)}function j(e,r,o){function n(){b(e(),(()=>O(r,o)),(e=>O(!0,e)))}l||(l=!0,"writable"!==t._state||st(t)?n():m(q(),n))}function k(e,r){l||(l=!0,"writable"!==t._state||st(t)?O(e,r):m(q(),(()=>O(e,r))))}function O(e,t){ft(u),E(a),void 0!==i&&i.removeEventListener("abort",v),e?g(t):d(void 0)}w(f(((e,t)=>{!function r(o){o?e():y(l?p(!0):y(u._readyPromise,(()=>f(((e,t)=>{te(a,{_chunkSteps:t=>{c=y(pt(u,t),void 0,n),e(!1)},_closeSteps:()=>e(!0),_errorSteps:t})})))),r,t)}(!1)})))}))}class Mt{constructor(){throw new TypeError("Illegal constructor")}get desiredSize(){if(!$t(this))throw Gt("desiredSize");return Vt(this)}close(){if(!$t(this))throw Gt("close");if(!Qt(this))throw new TypeError("The stream is not in a state that permits close");Ut(this)}enqueue(e){if(!$t(this))throw Gt("enqueue");if(!Qt(this))throw new TypeError("The stream is not in a state that permits enqueue");return Ht(this,e)}error(e){if(!$t(this))throw Gt("error");Nt(this,e)}[W](e){fe(this);const t=this._cancelAlgorithm(e);return Ft(this),t}[L](e){const t=this._controlledReadableStream;if(this._queue.length>0){const r=ce(this);this._closeRequested&&0===this._queue.length?(Ft(this),ur(t)):It(this),e._chunkSteps(r)}else G(t,e),It(this)}}function $t(e){return!!i(e)&&!!Object.prototype.hasOwnProperty.call(e,"_controlledReadableStream")}function It(e){Dt(e)&&(e._pulling?e._pullAgain=!0:(e._pulling=!0,b(e._pullAlgorithm(),(()=>{e._pulling=!1,e._pullAgain&&(e._pullAgain=!1,It(e))}),(t=>{Nt(e,t)}))))}function Dt(e){const t=e._controlledReadableStream;return!!Qt(e)&&(!!e._started&&(!!(ir(t)&&K(t)>0)||Vt(e)>0))}function Ft(e){e._pullAlgorithm=void 0,e._cancelAlgorithm=void 0,e._strategySizeAlgorithm=void 0}function Ut(e){if(!Qt(e))return;const t=e._controlledReadableStream;e._closeRequested=!0,0===e._queue.length&&(Ft(e),ur(t))}function Ht(e,t){if(!Qt(e))return;const r=e._controlledReadableStream;if(ir(r)&&K(r)>0)J(r,t,!1);else{let r;try{r=e._strategySizeAlgorithm(t)}catch(t){throw Nt(e,t),t}try{de(e,t,r)}catch(t){throw Nt(e,t),t}}It(e)}function Nt(e,t){const r=e._controlledReadableStream;"readable"===r._state&&(fe(e),Ft(e),lr(r,t))}function Vt(e){const t=e._controlledReadableStream._state;return"errored"===t?null:"closed"===t?0:e._strategyHWM-e._queueTotalSize}function Qt(e){const t=e._controlledReadableStream._state;return!e._closeRequested&&"readable"===t}function Yt(e,t,r,o,n,s,i){t._controlledReadableStream=e,t._queue=void 0,t._queueTotalSize=void 0,fe(t),t._started=!1,t._closeRequested=!1,t._pullAgain=!1,t._pulling=!1,t._strategySizeAlgorithm=i,t._strategyHWM=s,t._pullAlgorithm=o,t._cancelAlgorithm=n,e._readableStreamController=t,b(p(r()),(()=>{t._started=!0,It(t)}),(e=>{Nt(t,e)}))}function Gt(e){return new TypeError(`ReadableStreamDefaultController.prototype.${e} can only be used on a ReadableStreamDefaultController`)}function Jt(e,t,r){return I(e,r),r=>T(e,t,[r])}function Kt(e,t,r){return I(e,r),r=>T(e,t,[r])}function Zt(e,t,r){return I(e,r),r=>S(e,t,[r])}function Xt(e,t){if("bytes"!=(e=`${e}`))throw new TypeError(`${t} '${e}' is not a valid enumeration value for ReadableStreamType`);return e}function er(e,t){if("byob"!=(e=`${e}`))throw new TypeError(`${t} '${e}' is not a valid enumeration value for ReadableStreamReaderMode`);return e}function tr(e,t){$(e,t);const r=null==e?void 0:e.preventAbort,o=null==e?void 0:e.preventCancel,n=null==e?void 0:e.preventClose,s=null==e?void 0:e.signal;return void 0!==s&&function(e,t){if(!function(e){if("object"!=typeof e||null===e)return!1;try{return"boolean"==typeof e.aborted}catch(e){return!1}}(e))throw new TypeError(`${t} is not an AbortSignal.`)}(s,`${t} has member 'signal' that`),{preventAbort:Boolean(r),preventCancel:Boolean(o),preventClose:Boolean(n),signal:s}}Object.defineProperties(Mt.prototype,{close:{enumerable:!0},enqueue:{enumerable:!0},error:{enumerable:!0},desiredSize:{enumerable:!0}}),"symbol"==typeof o.toStringTag&&Object.defineProperty(Mt.prototype,o.toStringTag,{value:"ReadableStreamDefaultController",configurable:!0});class rr{constructor(e={},t={}){void 0===e?e=null:D(e,"First parameter");const r=Fe(t,"Second parameter"),o=function(e,t){$(e,t);const r=e,o=null==r?void 0:r.autoAllocateChunkSize,n=null==r?void 0:r.cancel,s=null==r?void 0:r.pull,i=null==r?void 0:r.start,a=null==r?void 0:r.type;return{autoAllocateChunkSize:void 0===o?void 0:V(o,`${t} has member 'autoAllocateChunkSize' that`),cancel:void 0===n?void 0:Jt(n,r,`${t} has member 'cancel' that`),pull:void 0===s?void 0:Kt(s,r,`${t} has member 'pull' that`),start:void 0===i?void 0:Zt(i,r,`${t} has member 'start' that`),type:void 0===a?void 0:Xt(a,`${t} has member 'type' that`)}}(e,"First parameter");if(nr(this),"bytes"===o.type){if(void 0!==r.size)throw new RangeError("The strategy for a byte stream cannot have a size function");!function(e,t,r){const o=Object.create(ye.prototype);let n=()=>{},s=()=>p(void 0),i=()=>p(void 0);void 0!==t.start&&(n=()=>t.start(o)),void 0!==t.pull&&(s=()=>t.pull(o)),void 0!==t.cancel&&(i=e=>t.cancel(e));const a=t.autoAllocateChunkSize;!function(e,t,r,o,n,s,i){t._controlledReadableByteStream=e,t._pullAgain=!1,t._pulling=!1,t._byobRequest=null,t._queue=t._queueTotalSize=void 0,fe(t),t._closeRequested=!1,t._started=!1,t._strategyHWM=s,t._pullAlgorithm=o,t._cancelAlgorithm=n,t._autoAllocateChunkSize=i,t._pendingPullIntos=new R,e._readableStreamController=t,b(p(r()),(()=>{t._started=!0,_e(t)}),(e=>{Oe(t,e)}))}(e,o,n,s,i,r,a)}(this,o,Ie(r,0))}else{const e=De(r);!function(e,t,r,o){const n=Object.create(Mt.prototype);let s=()=>{},i=()=>p(void 0),a=()=>p(void 0);void 0!==t.start&&(s=()=>t.start(n)),void 0!==t.pull&&(i=()=>t.pull(n)),void 0!==t.cancel&&(a=e=>t.cancel(e)),Yt(e,n,s,i,a,r,o)}(this,o,Ie(r,1),e)}}get locked(){if(!sr(this))throw cr("locked");return ir(this)}cancel(e){return sr(this)?ir(this)?h(new TypeError("Cannot cancel a stream that already has a reader")):ar(this,e):h(cr("cancel"))}getReader(e){if(!sr(this))throw cr("getReader");return void 0===function(e,t){$(e,t);const r=null==e?void 0:e.mode;return{mode:void 0===r?void 0:er(r,`${t} has member 'mode' that`)}}(e,"First parameter").mode?Y(this):new ze(this)}pipeThrough(e,t={}){if(!sr(this))throw cr("pipeThrough");F(e,1,"pipeThrough");const r=function(e,t){$(e,t);const r=null==e?void 0:e.readable;U(r,"readable","ReadableWritablePair"),Q(r,`${t} has member 'readable' that`);const o=null==e?void 0:e.writable;return U(o,"writable","ReadableWritablePair"),Ye(o,`${t} has member 'writable' that`),{readable:r,writable:o}}(e,"First parameter"),o=tr(t,"Second parameter");if(ir(this))throw new TypeError("ReadableStream.prototype.pipeThrough cannot be used on a locked ReadableStream");if(Xe(r.writable))throw new TypeError("ReadableStream.prototype.pipeThrough cannot be used on a locked WritableStream");return w(zt(this,r.writable,o.preventClose,o.preventAbort,o.preventCancel,o.signal)),r.readable}pipeTo(e,t={}){if(!sr(this))return h(cr("pipeTo"));if(void 0===e)return h("Parameter 1 is required in 'pipeTo'.");if(!Ze(e))return h(new TypeError("ReadableStream.prototype.pipeTo's first argument must be a WritableStream"));let r;try{r=tr(t,"Second parameter")}catch(e){return h(e)}return ir(this)?h(new TypeError("ReadableStream.prototype.pipeTo cannot be used on a locked ReadableStream")):Xe(e)?h(new TypeError("ReadableStream.prototype.pipeTo cannot be used on a locked WritableStream")):zt(this,e,r.preventClose,r.preventAbort,r.preventCancel,r.signal)}tee(){if(!sr(this))throw cr("tee");const e=function(e,t){const r=Y(e);let o,n,s,i,a,u=!1,l=!1,c=!1;const d=f((e=>{a=e}));function h(){return u||(u=!0,te(r,{_chunkSteps:e=>{v((()=>{u=!1;const t=e,r=e;l||Ht(s._readableStreamController,t),c||Ht(i._readableStreamController,r),a(void 0)}))},_closeSteps:()=>{u=!1,l||Ut(s._readableStreamController),c||Ut(i._readableStreamController)},_errorSteps:()=>{u=!1}})),p(void 0)}function y(){}return s=or(y,h,(function(t){if(l=!0,o=t,c){const t=pe([o,n]),r=ar(e,t);a(r)}return d})),i=or(y,h,(function(t){if(c=!0,n=t,l){const t=pe([o,n]),r=ar(e,t);a(r)}return d})),_(r._closedPromise,(e=>{Nt(s._readableStreamController,e),Nt(i._readableStreamController,e),a(void 0)})),[s,i]}(this);return pe(e)}values(e){if(!sr(this))throw cr("values");return function(e,t){const r=Y(e),o=new ne(r,t),n=Object.create(se);return n._asyncIteratorImpl=o,n}(this,function(e,t){$(e,"First parameter");const r=null==e?void 0:e.preventCancel;return{preventCancel:Boolean(r)}}(e).preventCancel)}}function or(e,t,r,o=1,n=(()=>1)){const s=Object.create(rr.prototype);return nr(s),Yt(s,Object.create(Mt.prototype),e,t,r,o,n),s}function nr(e){e._state="readable",e._reader=void 0,e._storedError=void 0,e._disturbed=!1}function sr(e){return!!i(e)&&!!Object.prototype.hasOwnProperty.call(e,"_readableStreamController")}function ir(e){return void 0!==e._reader}function ar(e,t){return e._disturbed=!0,"closed"===e._state?p(void 0):"errored"===e._state?h(e._storedError):(ur(e),g(e._readableStreamController[W](t),n))}function ur(e){e._state="closed";const t=e._reader;void 0!==t&&(ee(t)&&(t._readRequests.forEach((e=>{e._closeSteps()})),t._readRequests=new R),A(t))}function lr(e,t){e._state="errored",e._storedError=t;const r=e._reader;void 0!==r&&(ee(r)?(r._readRequests.forEach((e=>{e._errorSteps(t)})),r._readRequests=new R):(r._readIntoRequests.forEach((e=>{e._errorSteps(t)})),r._readIntoRequests=new R),C(r,t))}function cr(e){return new TypeError(`ReadableStream.prototype.${e} can only be used on a ReadableStream`)}function dr(e,t){$(e,t);const r=null==e?void 0:e.highWaterMark;return U(r,"highWaterMark","QueuingStrategyInit"),{highWaterMark:H(r)}}Object.defineProperties(rr.prototype,{cancel:{enumerable:!0},getReader:{enumerable:!0},pipeThrough:{enumerable:!0},pipeTo:{enumerable:!0},tee:{enumerable:!0},values:{enumerable:!0},locked:{enumerable:!0}}),"symbol"==typeof o.toStringTag&&Object.defineProperty(rr.prototype,o.toStringTag,{value:"ReadableStream",configurable:!0}),"symbol"==typeof o.asyncIterator&&Object.defineProperty(rr.prototype,o.asyncIterator,{value:rr.prototype.values,writable:!0,configurable:!0});const fr=function(e){return e.byteLength};class pr{constructor(e){F(e,1,"ByteLengthQueuingStrategy"),e=dr(e,"First parameter"),this._byteLengthQueuingStrategyHighWaterMark=e.highWaterMark}get highWaterMark(){if(!yr(this))throw hr("highWaterMark");return this._byteLengthQueuingStrategyHighWaterMark}get size(){if(!yr(this))throw hr("size");return fr}}function hr(e){return new TypeError(`ByteLengthQueuingStrategy.prototype.${e} can only be used on a ByteLengthQueuingStrategy`)}function yr(e){return!!i(e)&&!!Object.prototype.hasOwnProperty.call(e,"_byteLengthQueuingStrategyHighWaterMark")}Object.defineProperties(pr.prototype,{highWaterMark:{enumerable:!0},size:{enumerable:!0}}),"symbol"==typeof o.toStringTag&&Object.defineProperty(pr.prototype,o.toStringTag,{value:"ByteLengthQueuingStrategy",configurable:!0});const br=function(){return 1};class mr{constructor(e){F(e,1,"CountQueuingStrategy"),e=dr(e,"First parameter"),this._countQueuingStrategyHighWaterMark=e.highWaterMark}get highWaterMark(){if(!gr(this))throw _r("highWaterMark");return this._countQueuingStrategyHighWaterMark}get size(){if(!gr(this))throw _r("size");return br}}function _r(e){return new TypeError(`CountQueuingStrategy.prototype.${e} can only be used on a CountQueuingStrategy`)}function gr(e){return!!i(e)&&!!Object.prototype.hasOwnProperty.call(e,"_countQueuingStrategyHighWaterMark")}function wr(e,t,r){return I(e,r),r=>T(e,t,[r])}function vr(e,t,r){return I(e,r),r=>S(e,t,[r])}function Sr(e,t,r){return I(e,r),(r,o)=>T(e,t,[r,o])}Object.defineProperties(mr.prototype,{highWaterMark:{enumerable:!0},size:{enumerable:!0}}),"symbol"==typeof o.toStringTag&&Object.defineProperty(mr.prototype,o.toStringTag,{value:"CountQueuingStrategy",configurable:!0});class Tr{constructor(e={},t={},r={}){void 0===e&&(e=null);const o=Fe(t,"Second parameter"),n=Fe(r,"Third parameter"),s=function(e,t){$(e,t);const r=null==e?void 0:e.flush,o=null==e?void 0:e.readableType,n=null==e?void 0:e.start,s=null==e?void 0:e.transform,i=null==e?void 0:e.writableType;return{flush:void 0===r?void 0:wr(r,e,`${t} has member 'flush' that`),readableType:o,start:void 0===n?void 0:vr(n,e,`${t} has member 'start' that`),transform:void 0===s?void 0:Sr(s,e,`${t} has member 'transform' that`),writableType:i}}(e,"First parameter");if(void 0!==s.readableType)throw new RangeError("Invalid readableType specified");if(void 0!==s.writableType)throw new RangeError("Invalid writableType specified");const i=Ie(n,0),a=De(n),u=Ie(o,1),l=De(o);let c;!function(e,t,r,o,n,s){function i(){return t}e._writable=function(e,t,r,o,n=1,s=(()=>1)){const i=Object.create(Ge.prototype);return Ke(i),bt(i,Object.create(yt.prototype),e,t,r,o,n,s),i}(i,(function(t){return function(e,t){const r=e._transformStreamController;return e._backpressure?g(e._backpressureChangePromise,(()=>{const o=e._writable;if("erroring"===o._state)throw o._storedError;return Ar(r,t)})):Ar(r,t)}(e,t)}),(function(){return function(e){const t=e._readable,r=e._transformStreamController,o=r._flushAlgorithm();return Or(r),g(o,(()=>{if("errored"===t._state)throw t._storedError;Ut(t._readableStreamController)}),(r=>{throw qr(e,r),t._storedError}))}(e)}),(function(t){return function(e,t){return qr(e,t),p(void 0)}(e,t)}),r,o),e._readable=or(i,(function(){return function(e){return Er(e,!1),e._backpressureChangePromise}(e)}),(function(t){return Pr(e,t),p(void 0)}),n,s),e._backpressure=void 0,e._backpressureChangePromise=void 0,e._backpressureChangePromise_resolve=void 0,Er(e,!0),e._transformStreamController=void 0}(this,f((e=>{c=e})),u,l,i,a),function(e,t){const r=Object.create(jr.prototype);let o=e=>{try{return Cr(r,e),p(void 0)}catch(e){return h(e)}},n=()=>p(void 0);void 0!==t.transform&&(o=e=>t.transform(e,r)),void 0!==t.flush&&(n=()=>t.flush(r)),function(e,t,r,o){t._controlledTransformStream=e,e._transformStreamController=t,t._transformAlgorithm=r,t._flushAlgorithm=o}(e,r,o,n)}(this,s),void 0!==s.start?c(s.start(this._transformStreamController)):c(void 0)}get readable(){if(!Rr(this))throw xr("readable");return this._readable}get writable(){if(!Rr(this))throw xr("writable");return this._writable}}function Rr(e){return!!i(e)&&!!Object.prototype.hasOwnProperty.call(e,"_transformStreamController")}function qr(e,t){Nt(e._readable._readableStreamController,t),Pr(e,t)}function Pr(e,t){Or(e._transformStreamController),wt(e._writable._writableStreamController,t),e._backpressure&&Er(e,!1)}function Er(e,t){void 0!==e._backpressureChangePromise&&e._backpressureChangePromise_resolve(),e._backpressureChangePromise=f((t=>{e._backpressureChangePromise_resolve=t})),e._backpressure=t}Object.defineProperties(Tr.prototype,{readable:{enumerable:!0},writable:{enumerable:!0}}),"symbol"==typeof o.toStringTag&&Object.defineProperty(Tr.prototype,o.toStringTag,{value:"TransformStream",configurable:!0});class jr{constructor(){throw new TypeError("Illegal constructor")}get desiredSize(){if(!kr(this))throw Br("desiredSize");return Vt(this._controlledTransformStream._readable._readableStreamController)}enqueue(e){if(!kr(this))throw Br("enqueue");Cr(this,e)}error(e){if(!kr(this))throw Br("error");var t;t=e,qr(this._controlledTransformStream,t)}terminate(){if(!kr(this))throw Br("terminate");!function(e){const t=e._controlledTransformStream;Ut(t._readable._readableStreamController);Pr(t,new TypeError("TransformStream terminated"))}(this)}}function kr(e){return!!i(e)&&!!Object.prototype.hasOwnProperty.call(e,"_controlledTransformStream")}function Or(e){e._transformAlgorithm=void 0,e._flushAlgorithm=void 0}function Cr(e,t){const r=e._controlledTransformStream,o=r._readable._readableStreamController;if(!Qt(o))throw new TypeError("Readable side is not in a state that permits enqueue");try{Ht(o,t)}catch(e){throw Pr(r,e),r._readable._storedError}(function(e){return!Dt(e)})(o)!==r._backpressure&&Er(r,!0)}function Ar(e,t){return g(e._transformAlgorithm(t),void 0,(t=>{throw qr(e._controlledTransformStream,t),t}))}function Br(e){return new TypeError(`TransformStreamDefaultController.prototype.${e} can only be used on a TransformStreamDefaultController`)}function xr(e){return new TypeError(`TransformStream.prototype.${e} can only be used on a TransformStream`)}Object.defineProperties(jr.prototype,{enqueue:{enumerable:!0},error:{enumerable:!0},terminate:{enumerable:!0},desiredSize:{enumerable:!0}}),"symbol"==typeof o.toStringTag&&Object.defineProperty(jr.prototype,o.toStringTag,{value:"TransformStreamDefaultController",configurable:!0})},417:e=>{"use strict";e.exports=__webpack_require__(417)},605:e=>{"use strict";e.exports=__webpack_require__(605)},211:e=>{"use strict";e.exports=__webpack_require__(211)},413:e=>{"use strict";e.exports=__webpack_require__(413)},835:e=>{"use strict";e.exports=__webpack_require__(835)},669:e=>{"use strict";e.exports=__webpack_require__(669)},761:e=>{"use strict";e.exports=__webpack_require__(761)}},t={};function r(o){if(t[o])return t[o].exports;var n=t[o]={exports:{}};return e[o].call(n.exports,n,n.exports,r),n.exports}return r.d=(e,t)=>{for(var o in t)r.o(t,o)&&!r.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:t[o]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r(990)})().default})); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9tYWlsZ3VuL3dlYnBhY2svdW5pdmVyc2FsTW9kdWxlRGVmaW5pdGlvbiIsIndlYnBhY2s6Ly9tYWlsZ3VuLy4vbm9kZV9tb2R1bGVzL2Fib3J0LWNvbnRyb2xsZXIvZGlzdC9hYm9ydC1jb250cm9sbGVyLmpzIiwid2VicGFjazovL21haWxndW4vLi9pbmRleC50cyIsIndlYnBhY2s6Ly9tYWlsZ3VuLy4vbGliL2NsaWVudC50cyIsIndlYnBhY2s6Ly9tYWlsZ3VuLy4vbGliL2RvbWFpbnMudHMiLCJ3ZWJwYWNrOi8vbWFpbGd1bi8uL2xpYi9lcnJvci50cyIsIndlYnBhY2s6Ly9tYWlsZ3VuLy4vbGliL2V2ZW50cy50cyIsIndlYnBhY2s6Ly9tYWlsZ3VuLy4vbGliL2lwLXBvb2xzLnRzIiwid2VicGFjazovL21haWxndW4vLi9saWIvaXBzLnRzIiwid2VicGFjazovL21haWxndW4vLi9saWIvbWVzc2FnZXMudHMiLCJ3ZWJwYWNrOi8vbWFpbGd1bi8uL2xpYi9wYXJzZS50cyIsIndlYnBhY2s6Ly9tYWlsZ3VuLy4vbGliL3JlcXVlc3QudHMiLCJ3ZWJwYWNrOi8vbWFpbGd1bi8uL2xpYi9yb3V0ZXMudHMiLCJ3ZWJwYWNrOi8vbWFpbGd1bi8uL2xpYi9zdGF0cy50cyIsIndlYnBhY2s6Ly9tYWlsZ3VuLy4vbGliL3N1cHByZXNzaW9ucy50cyIsIndlYnBhY2s6Ly9tYWlsZ3VuLy4vbGliL3ZhbGlkYXRlLnRzIiwid2VicGFjazovL21haWxndW4vLi9saWIvd2ViaG9va3MudHMiLCJ3ZWJwYWNrOi8vbWFpbGd1bi8uL25vZGVfbW9kdWxlcy9idG9hL2luZGV4LmpzIiwid2VicGFjazovL21haWxndW4vLi9ub2RlX21vZHVsZXMvZGF0YS11cmktdG8tYnVmZmVyL2Rpc3Qvc3JjL2luZGV4LmpzIiwid2VicGFjazovL21haWxndW4vLi9ub2RlX21vZHVsZXMvZXZlbnQtdGFyZ2V0LXNoaW0vZGlzdC9ldmVudC10YXJnZXQtc2hpbS5qcyIsIndlYnBhY2s6Ly9tYWlsZ3VuLy4vbm9kZV9tb2R1bGVzL2ZldGNoLWJsb2IvaW5kZXguanMiLCJ3ZWJwYWNrOi8vbWFpbGd1bi8uL25vZGVfbW9kdWxlcy9reS11bml2ZXJzYWwvaW5kZXguanMiLCJ3ZWJwYWNrOi8vbWFpbGd1bi8uL25vZGVfbW9kdWxlcy9reS91bWQuanMiLCJ3ZWJwYWNrOi8vbWFpbGd1bi8uL25vZGVfbW9kdWxlcy9ub2RlLWZldGNoL2Rpc3QvaW5kZXguY2pzIiwid2VicGFjazovL21haWxndW4vLi9ub2RlX21vZHVsZXMvdXJsLWpvaW4vbGliL3VybC1qb2luLmpzIiwid2VicGFjazovL21haWxndW4vLi9ub2RlX21vZHVsZXMvd2ViLXN0cmVhbXMtcG9seWZpbGwvZGlzdC9wb255ZmlsbC5lczIwMTgubWpzIiwid2VicGFjazovL21haWxndW4vZXh0ZXJuYWwgXCJjcnlwdG9cIiIsIndlYnBhY2s6Ly9tYWlsZ3VuL2V4dGVybmFsIFwiaHR0cFwiIiwid2VicGFjazovL21haWxndW4vZXh0ZXJuYWwgXCJodHRwc1wiIiwid2VicGFjazovL21haWxndW4vZXh0ZXJuYWwgXCJzdHJlYW1cIiIsIndlYnBhY2s6Ly9tYWlsZ3VuL2V4dGVybmFsIFwidXJsXCIiLCJ3ZWJwYWNrOi8vbWFpbGd1bi9leHRlcm5hbCBcInV0aWxcIiIsIndlYnBhY2s6Ly9tYWlsZ3VuL2V4dGVybmFsIFwiemxpYlwiIiwid2VicGFjazovL21haWxndW4vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vbWFpbGd1bi93ZWJwYWNrL3N0YXJ0dXAiLCJ3ZWJwYWNrOi8vbWFpbGd1bi93ZWJwYWNrL3J1bnRpbWUvZGVmaW5lIHByb3BlcnR5IGdldHRlcnMiLCJ3ZWJwYWNrOi8vbWFpbGd1bi93ZWJwYWNrL3J1bnRpbWUvaGFzT3duUHJvcGVydHkgc2hvcnRoYW5kIiwid2VicGFjazovL21haWxndW4vd2VicGFjay9ydW50aW1lL21ha2UgbmFtZXNwYWNlIG9iamVjdCJdLCJuYW1lcyI6WyJyb290IiwiZmFjdG9yeSIsImV4cG9ydHMiLCJtb2R1bGUiLCJkZWZpbmUiLCJhbWQiLCJ0aGlzIiwiT2JqZWN0IiwiZGVmaW5lUHJvcGVydHkiLCJ2YWx1ZSIsImV2ZW50VGFyZ2V0U2hpbSIsIkFib3J0U2lnbmFsIiwiRXZlbnRUYXJnZXQiLCJzdXBlciIsIlR5cGVFcnJvciIsImFib3J0ZWQiLCJhYm9ydGVkRmxhZ3MiLCJnZXQiLCJkZWZpbmVFdmVudEF0dHJpYnV0ZSIsInByb3RvdHlwZSIsIldlYWtNYXAiLCJkZWZpbmVQcm9wZXJ0aWVzIiwiZW51bWVyYWJsZSIsIlN5bWJvbCIsInRvU3RyaW5nVGFnIiwiY29uZmlndXJhYmxlIiwiQWJvcnRDb250cm9sbGVyIiwic2lnbmFscyIsInNldCIsInNpZ25hbCIsImNyZWF0ZSIsImNhbGwiLCJjcmVhdGVBYm9ydFNpZ25hbCIsImdldFNpZ25hbCIsImRpc3BhdGNoRXZlbnQiLCJ0eXBlIiwiY29udHJvbGxlciIsImFib3J0IiwiZGVmYXVsdCIsIkZvcm1EYXRhIiwiZm9ybURhdGEiLCJjbGllbnQiLCJvcHRpb25zIiwiY29uZmlnIiwidXJsIiwidXNlcm5hbWUiLCJFcnJvciIsImtleSIsInJlcXVlc3QiLCJkb21haW5zIiwid2ViaG9va3MiLCJldmVudHMiLCJzdGF0cyIsInN1cHByZXNzaW9ucyIsIm1lc3NhZ2VzIiwicm91dGVzIiwiaXBzIiwiaXBfcG9vbHMiLCJwdWJsaWNfa2V5IiwicHVibGljX3JlcXVlc3QiLCJ2YWxpZGF0ZSIsInBhcnNlIiwiZGF0YSIsInJlY2VpdmluZyIsInNlbmRpbmciLCJuYW1lIiwicmVxdWlyZV90bHMiLCJza2lwX3ZlcmlmaWNhdGlvbiIsInN0YXRlIiwid2lsZGNhcmQiLCJzcGFtX2FjdGlvbiIsImNyZWF0ZWRfYXQiLCJzbXRwX3Bhc3N3b3JkIiwic210cF9sb2dpbiIsInJlY2VpdmluZ19kbnNfcmVjb3JkcyIsInNlbmRpbmdfZG5zX3JlY29yZHMiLCJfcGFyc2VNZXNzYWdlIiwicmVzcG9uc2UiLCJib2R5IiwiX3BhcnNlRG9tYWluTGlzdCIsIml0ZW1zIiwibWFwIiwiaXRlbSIsIkRvbWFpbiIsIl9wYXJzZURvbWFpbiIsImRvbWFpbiIsIl9wYXJzZVRyYWNraW5nU2V0dGluZ3MiLCJ0cmFja2luZyIsIl9wYXJzZVRyYWNraW5nVXBkYXRlIiwibGlzdCIsInF1ZXJ5IiwidGhlbiIsInBvc3QiLCJkZXN0cm95IiwiZGVsZXRlIiwiZ2V0VHJhY2tpbmciLCJ1cGRhdGVUcmFja2luZyIsInB1dCIsImdldElwcyIsImFzc2lnbklwIiwiaXAiLCJkZWxldGVJcCIsImxpbmtJcFBvb2wiLCJwb29sX2lkIiwidW5saW5rSXBQb2xsIiwic3RhdHVzIiwic3RhdHVzVGV4dCIsIm1lc3NhZ2UiLCJib2R5TWVzc2FnZSIsImVycm9yIiwic3RhY2siLCJkZXRhaWxzIiwidXJsam9pbiIsIl9wYXJzZVBhZ2VOdW1iZXIiLCJzcGxpdCIsInBvcCIsIl9wYXJzZVBhZ2UiLCJpZCIsIm51bWJlciIsIl9wYXJzZVBhZ2VMaW5rcyIsImVudHJpZXMiLCJwYWdpbmciLCJyZWR1Y2UiLCJhY2MiLCJfcGFyc2VFdmVudExpc3QiLCJwYWdlcyIsInBhZ2UiLCJwYXJzZUlwUG9vbHNSZXNwb25zZSIsInVwZGF0ZSIsInBvb2xJZCIsInBhdGNoIiwicGFyc2VJcHNSZXNwb25zZSIsIl9wYXJzZVJlc3BvbnNlIiwicG9zdE11bHRpIiwiYWRkcmVzc2VzIiwiZW5hYmxlRG5zRXNwQ2hlY2tzIiwiQXJyYXkiLCJpc0FycmF5Iiwiam9pbiIsInN5bnRheF9vbmx5IiwiaXNTdHJlYW0iLCJhdHRhY2htZW50IiwicGlwZSIsImdldEF0dGFjaG1lbnRPcHRpb25zIiwiZmlsZW5hbWUiLCJjb250ZW50VHlwZSIsImtub3duTGVuZ3RoIiwiaGVhZGVycyIsIm1ldGhvZCIsImJhc2ljIiwiQXV0aG9yaXphdGlvbiIsInBhcmFtcyIsImdldE93blByb3BlcnR5TmFtZXMiLCJsZW5ndGgiLCJzZWFyY2hQYXJhbXMiLCJ0b0xvY2FsZVVwcGVyQ2FzZSIsInRocm93SHR0cEVycm9ycyIsIm9rIiwic3RyZWFtIiwiY2h1bmtzIiwiUHJvbWlzZSIsInJlc29sdmUiLCJyZWplY3QiLCJvbiIsImNodW5rIiwicHVzaCIsIkJ1ZmZlciIsImNvbmNhdCIsInRvU3RyaW5nIiwianNvbiIsImNvbW1hbmQiLCJoZWFkIiwia2V5cyIsImZpbHRlciIsImZvckVhY2giLCJhcHBlbmQiLCJvYmoiLCJSZXF1ZXN0Iiwic3RhcnQiLCJEYXRlIiwiZW5kIiwicmVzb2x1dGlvbiIsInN0YXQiLCJ0aW1lIiwiX3BhcnNlU3RhdHMiLCJTdGF0cyIsImdldERvbWFpbiIsImdldEFjY291bnQiLCJjcmVhdGVPcHRpb25zIiwiYWRkcmVzcyIsImNvZGUiLCJ0YWdzIiwibW9kZWxzIiwiYm91bmNlcyIsIkJvdW5jZSIsImNvbXBsYWludHMiLCJDb21wbGFpbnQiLCJ1bnN1YnNjcmliZXMiLCJVbnN1YnNjcmliZSIsInBhZ2VVcmwiLCJfcGFyc2VMaXN0IiwiTW9kZWwiLCJkIiwiX3BhcnNlSXRlbSIsIm1vZGVsIiwiZW5jb2RlVVJJQ29tcG9uZW50IiwiU3VwcHJlc3Npb25DbGllbnQiLCJfcGFyc2VXZWJob29rTGlzdCIsIl9wYXJzZVdlYmhvb2tXaXRoSUQiLCJXZWJob29rIiwid2ViaG9vayIsIl9wYXJzZVdlYmhvb2tUZXN0IiwidGVzdCIsInN0ciIsImZyb20iLCJ1cmkiLCJmaXJzdENvbW1hIiwicmVwbGFjZSIsImluZGV4T2YiLCJtZXRhIiwic3Vic3RyaW5nIiwiY2hhcnNldCIsImJhc2U2NCIsInR5cGVGdWxsIiwiaSIsImVuY29kaW5nIiwidW5lc2NhcGUiLCJidWZmZXIiLCJwcml2YXRlRGF0YSIsIndyYXBwZXJzIiwicGQiLCJldmVudCIsInJldHYiLCJjb25zb2xlIiwiYXNzZXJ0Iiwic2V0Q2FuY2VsRmxhZyIsInBhc3NpdmVMaXN0ZW5lciIsImNhbmNlbGFibGUiLCJjYW5jZWxlZCIsInByZXZlbnREZWZhdWx0IiwiRXZlbnQiLCJldmVudFRhcmdldCIsImV2ZW50UGhhc2UiLCJjdXJyZW50VGFyZ2V0Iiwic3RvcHBlZCIsImltbWVkaWF0ZVN0b3BwZWQiLCJ0aW1lU3RhbXAiLCJub3ciLCJkZWZpbmVSZWRpcmVjdERlc2NyaXB0b3IiLCJkZWZpbmVDYWxsRGVzY3JpcHRvciIsImFwcGx5IiwiYXJndW1lbnRzIiwiZ2V0V3JhcHBlciIsInByb3RvIiwid3JhcHBlciIsIkJhc2VFdmVudCIsIkN1c3RvbUV2ZW50IiwiY29uc3RydWN0b3IiLCJ3cml0YWJsZSIsImlzRnVuYyIsImdldE93blByb3BlcnR5RGVzY3JpcHRvciIsImRlZmluZVdyYXBwZXIiLCJnZXRQcm90b3R5cGVPZiIsImlzU3RvcHBlZCIsInNldFBhc3NpdmVMaXN0ZW5lciIsInN0b3BQcm9wYWdhdGlvbiIsInN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbiIsIkJvb2xlYW4iLCJidWJibGVzIiwiY29tcG9zZWQiLCJjYW5jZWxCdWJibGUiLCJ3aW5kb3ciLCJzZXRQcm90b3R5cGVPZiIsImxpc3RlbmVyc01hcCIsImlzT2JqZWN0IiwieCIsImdldExpc3RlbmVycyIsImxpc3RlbmVycyIsImV2ZW50VGFyZ2V0UHJvdG90eXBlIiwiZXZlbnROYW1lIiwibm9kZSIsImxpc3RlbmVyVHlwZSIsImxpc3RlbmVyIiwibmV4dCIsInByZXYiLCJuZXdOb2RlIiwicGFzc2l2ZSIsIm9uY2UiLCJkZWZpbmVFdmVudEF0dHJpYnV0ZURlc2NyaXB0b3IiLCJkZWZpbmVDdXN0b21FdmVudFRhcmdldCIsImV2ZW50TmFtZXMiLCJDdXN0b21FdmVudFRhcmdldCIsInR5cGVzIiwiTWFwIiwib3B0aW9uc0lzT2JqIiwiY2FwdHVyZSIsInVuZGVmaW5lZCIsIndyYXBwZWRFdmVudCIsIndyYXBFdmVudCIsImVyciIsImhhbmRsZUV2ZW50Iiwic2V0RXZlbnRQaGFzZSIsInNldEN1cnJlbnRUYXJnZXQiLCJkZWZhdWx0UHJldmVudGVkIiwiUmVhZGFibGUiLCJ3bSIsIkJsb2IiLCJibG9iUGFydHMiLCJzaXplIiwicGFydHMiLCJlbGVtZW50IiwiQXJyYXlCdWZmZXIiLCJpc1ZpZXciLCJieXRlT2Zmc2V0IiwiYnl0ZUxlbmd0aCIsIlN0cmluZyIsInRvTG93ZXJDYXNlIiwiYXJyYXlCdWZmZXIiLCJVaW50OEFycmF5Iiwib2Zmc2V0IiwiYXN5bmMiLCJwYXJ0IiwicmVhZCIsInJlbGF0aXZlU3RhcnQiLCJNYXRoIiwibWF4IiwibWluIiwicmVsYXRpdmVFbmQiLCJzcGFuIiwidmFsdWVzIiwiYWRkZWQiLCJzbGljZSIsImJsb2IiLCJhc3NpZ24iLCJoYXNJbnN0YW5jZSIsIm9iamVjdCIsImZldGNoIiwiZ2xvYmFsIiwiaGlnaFdhdGVyTWFyayIsIkhlYWRlcnMiLCJSZXNwb25zZSIsIlJlYWRhYmxlU3RyZWFtIiwiXyIsImdsb2JhbHMiLCJnZXRHbG9iYWwiLCJwcm9wZXJ0eSIsInNlbGYiLCJnbG9iYWxUaGlzIiwiZ2xvYmFsUHJvcGVydGllcyIsImdsb2JhbE9iamVjdCIsImJpbmQiLCJzdXBwb3J0c0Fib3J0Q29udHJvbGxlciIsInN1cHBvcnRzU3RyZWFtcyIsInN1cHBvcnRzRm9ybURhdGEiLCJtZXJnZUhlYWRlcnMiLCJzb3VyY2UxIiwic291cmNlMiIsInJlc3VsdCIsImlzSGVhZGVyc0luc3RhbmNlIiwic291cmNlIiwiZGVlcE1lcmdlIiwic291cmNlcyIsInJldHVyblZhbHVlIiwicmVxdWVzdE1ldGhvZHMiLCJyZXNwb25zZVR5cGVzIiwidGV4dCIsInJldHJ5QWZ0ZXJTdGF0dXNDb2RlcyIsInN0b3AiLCJIVFRQRXJyb3IiLCJUaW1lb3V0RXJyb3IiLCJkZWxheSIsIm1zIiwic2V0VGltZW91dCIsIm5vcm1hbGl6ZVJlcXVlc3RNZXRob2QiLCJpbnB1dCIsImluY2x1ZGVzIiwidG9VcHBlckNhc2UiLCJkZWZhdWx0UmV0cnlPcHRpb25zIiwibGltaXQiLCJtZXRob2RzIiwic3RhdHVzQ29kZXMiLCJhZnRlclN0YXR1c0NvZGVzIiwibm9ybWFsaXplUmV0cnlPcHRpb25zIiwicmV0cnkiLCJtYXhTYWZlVGltZW91dCIsIkt5IiwiX3JldHJ5Q291bnQiLCJfaW5wdXQiLCJfb3B0aW9ucyIsImNyZWRlbnRpYWxzIiwiaG9va3MiLCJiZWZvcmVSZXF1ZXN0IiwiYmVmb3JlUmV0cnkiLCJhZnRlclJlc3BvbnNlIiwicHJlZml4VXJsIiwidGltZW91dCIsIlVSTCIsInN0YXJ0c1dpdGgiLCJlbmRzV2l0aCIsImFib3J0Q29udHJvbGxlciIsImFkZEV2ZW50TGlzdGVuZXIiLCJVUkxTZWFyY2hQYXJhbXMiLCJKU09OIiwic3RyaW5naWZ5IiwiZm4iLCJSYW5nZUVycm9yIiwiX2ZldGNoIiwiaG9vayIsIm1vZGlmaWVkUmVzcG9uc2UiLCJfZGVjb3JhdGVSZXNwb25zZSIsImNsb25lIiwib25Eb3dubG9hZFByb2dyZXNzIiwiX3N0cmVhbSIsIl9yZXRyeSIsIm1pbWVUeXBlIiwicGFyc2VKc29uIiwicmV0cnlBZnRlciIsImFmdGVyIiwiTnVtYmVyIiwiaXNOYU4iLCJtYXhSZXRyeUFmdGVyIiwiX2NhbGN1bGF0ZVJldHJ5RGVsYXkiLCJyZXRyeUNvdW50IiwidGltZW91dElEIiwiY2F0Y2giLCJjbGVhclRpbWVvdXQiLCJ0b3RhbEJ5dGVzIiwidHJhbnNmZXJyZWRCeXRlcyIsInJlYWRlciIsImdldFJlYWRlciIsInBlcmNlbnQiLCJkb25lIiwiY2xvc2UiLCJlbnF1ZXVlIiwidmFsaWRhdGVBbmRNZXJnZSIsImNyZWF0ZUluc3RhbmNlIiwiZGVmYXVsdHMiLCJreSIsIm5ld0RlZmF1bHRzIiwiZXh0ZW5kIiwiaHR0cCIsImh0dHBzIiwiemxpYiIsIlN0cmVhbSIsImRhdGFVcmlUb0J1ZmZlciIsInV0aWwiLCJjcnlwdG8iLCJGZXRjaEJhc2VFcnJvciIsImNhcHR1cmVTdGFja1RyYWNlIiwiRmV0Y2hFcnJvciIsInN5c3RlbUVycm9yIiwiZXJybm8iLCJlcnJvcmVkU3lzQ2FsbCIsInN5c2NhbGwiLCJOQU1FIiwiaXNVUkxTZWFyY2hQYXJhbWV0ZXJzIiwiZ2V0QWxsIiwiaGFzIiwic29ydCIsImlzQmxvYiIsImlzRm9ybURhdGEiLCJjYXJyaWFnZSIsImRhc2hlcyIsInJlcGVhdCIsImNhcnJpYWdlTGVuZ3RoIiwiZ2V0Rm9vdGVyIiwiYm91bmRhcnkiLCJnZXRIZWFkZXIiLCJmaWVsZCIsImhlYWRlciIsIklOVEVSTkFMUyIsIkJvZHkiLCJpc0J1ZmZlciIsImlzQW55QXJyYXlCdWZmZXIiLCJyYW5kb21CeXRlcyIsImZvcm0iLCJmb3JtRGF0YUl0ZXJhdG9yIiwiZGlzdHVyYmVkIiwiY29uc3VtZUJvZHkiLCJjdCIsImJ1ZiIsImFsbG9jIiwiYWNjdW0iLCJhY2N1bUJ5dGVzIiwicmVhZGFibGVFbmRlZCIsIl9yZWFkYWJsZVN0YXRlIiwiZW5kZWQiLCJldmVyeSIsImMiLCJib2R5VXNlZCIsImluc3RhbmNlIiwicDEiLCJwMiIsImdldEJvdW5kYXJ5IiwiUGFzc1Rocm91Z2giLCJleHRyYWN0Q29udGVudFR5cGUiLCJ2YWxpZGF0ZUhlYWRlck5hbWUiLCJ2YWxpZGF0ZUhlYWRlclZhbHVlIiwiaW5pdCIsInJhdyIsImlzQm94ZWRQcmltaXRpdmUiLCJpdGVyYXRvciIsInBhaXIiLCJQcm94eSIsInRhcmdldCIsInAiLCJyZWNlaXZlciIsIlNldCIsIlJlZmxlY3QiLCJjYWxsYmFjayIsImZvciIsInJlZGlyZWN0U3RhdHVzIiwiaXNSZWRpcmVjdCIsIklOVEVSTkFMUyQxIiwiY291bnRlciIsInJlZGlyZWN0ZWQiLCJsb2NhdGlvbiIsIklOVEVSTkFMUyQyIiwiaXNSZXF1ZXN0IiwicGFyc2VkVVJMIiwiaW5wdXRCb2R5IiwicmVkaXJlY3QiLCJmb2xsb3ciLCJjb21wcmVzcyIsImFnZW50IiwiaW5zZWN1cmVIVFRQUGFyc2VyIiwiZm9ybWF0IiwiQWJvcnRFcnJvciIsInN1cHBvcnRlZFNjaGVtYXMiLCJvcHRpb25zXyIsImNvbnRlbnRMZW5ndGhWYWx1ZSIsImdldExlbmd0aFN5bmMiLCJoYXNLbm93bkxlbmd0aCIsImdldEZvcm1EYXRhTGVuZ3RoIiwiZ2V0VG90YWxCeXRlcyIsInNlYXJjaCIsImxhc3RPZmZzZXQiLCJocmVmIiwiaGFzaCIsImdldFNlYXJjaCIsInBhdGgiLCJwYXRobmFtZSIsImhvc3RuYW1lIiwicHJvdG9jb2wiLCJwb3J0IiwiZ2V0Tm9kZVJlcXVlc3RPcHRpb25zIiwic2VuZCIsImVtaXQiLCJhYm9ydEFuZEZpbmFsaXplIiwiZmluYWxpemUiLCJyZXF1ZXN0XyIsInJlbW92ZUV2ZW50TGlzdGVuZXIiLCJyZXNwb25zZV8iLCJpbmRleCIsImFycmF5IiwiZnJvbVJhd0hlYWRlcnMiLCJyYXdIZWFkZXJzIiwic3RhdHVzQ29kZSIsImxvY2F0aW9uVVJMIiwicmVxdWVzdE9wdGlvbnMiLCJwaXBlbGluZSIsInByb2Nlc3MiLCJ2ZXJzaW9uIiwicmVzcG9uc2VPcHRpb25zIiwic3RhdHVzTWVzc2FnZSIsImNvZGluZ3MiLCJ6bGliT3B0aW9ucyIsImZsdXNoIiwiWl9TWU5DX0ZMVVNIIiwiZmluaXNoRmx1c2giLCJjcmVhdGVHdW56aXAiLCJjcmVhdGVCcm90bGlEZWNvbXByZXNzIiwiY3JlYXRlSW5mbGF0ZSIsImNyZWF0ZUluZmxhdGVSYXciLCJkZXN0Iiwid3JpdGUiLCJ3cml0ZVRvU3RyZWFtIiwibm9ybWFsaXplIiwiam9pbmVkIiwiU3ltYm9sUG9seWZpbGwiLCJkZXNjcmlwdGlvbiIsIm5vb3AiLCJ0eXBlSXNPYmplY3QiLCJyZXRocm93QXNzZXJ0aW9uRXJyb3JSZWplY3Rpb24iLCJvcmlnaW5hbFByb21pc2UiLCJvcmlnaW5hbFByb21pc2VUaGVuIiwib3JpZ2luYWxQcm9taXNlUmVzb2x2ZSIsIm9yaWdpbmFsUHJvbWlzZVJlamVjdCIsIm5ld1Byb21pc2UiLCJleGVjdXRvciIsInByb21pc2VSZXNvbHZlZFdpdGgiLCJwcm9taXNlUmVqZWN0ZWRXaXRoIiwicmVhc29uIiwiUGVyZm9ybVByb21pc2VUaGVuIiwicHJvbWlzZSIsIm9uRnVsZmlsbGVkIiwib25SZWplY3RlZCIsInVwb25Qcm9taXNlIiwidXBvbkZ1bGZpbGxtZW50IiwidXBvblJlamVjdGlvbiIsInRyYW5zZm9ybVByb21pc2VXaXRoIiwiZnVsZmlsbG1lbnRIYW5kbGVyIiwicmVqZWN0aW9uSGFuZGxlciIsInNldFByb21pc2VJc0hhbmRsZWRUb1RydWUiLCJxdWV1ZU1pY3JvdGFzayIsImdsb2JhbFF1ZXVlTWljcm90YXNrIiwicmVzb2x2ZWRQcm9taXNlIiwicmVmbGVjdENhbGwiLCJGIiwiViIsImFyZ3MiLCJGdW5jdGlvbiIsInByb21pc2VDYWxsIiwiU2ltcGxlUXVldWUiLCJfY3Vyc29yIiwiX3NpemUiLCJfZnJvbnQiLCJfZWxlbWVudHMiLCJfbmV4dCIsIl9iYWNrIiwib2xkQmFjayIsIm5ld0JhY2siLCJRVUVVRV9NQVhfQVJSQVlfU0laRSIsIm9sZEZyb250IiwibmV3RnJvbnQiLCJvbGRDdXJzb3IiLCJuZXdDdXJzb3IiLCJlbGVtZW50cyIsImZyb250IiwiY3Vyc29yIiwiUmVhZGFibGVTdHJlYW1SZWFkZXJHZW5lcmljSW5pdGlhbGl6ZSIsIl9vd25lclJlYWRhYmxlU3RyZWFtIiwiX3JlYWRlciIsIl9zdGF0ZSIsImRlZmF1bHRSZWFkZXJDbG9zZWRQcm9taXNlSW5pdGlhbGl6ZSIsImRlZmF1bHRSZWFkZXJDbG9zZWRQcm9taXNlUmVzb2x2ZSIsImRlZmF1bHRSZWFkZXJDbG9zZWRQcm9taXNlSW5pdGlhbGl6ZUFzUmVzb2x2ZWQiLCJkZWZhdWx0UmVhZGVyQ2xvc2VkUHJvbWlzZUluaXRpYWxpemVBc1JlamVjdGVkIiwiX3N0b3JlZEVycm9yIiwiUmVhZGFibGVTdHJlYW1SZWFkZXJHZW5lcmljQ2FuY2VsIiwiUmVhZGFibGVTdHJlYW1DYW5jZWwiLCJSZWFkYWJsZVN0cmVhbVJlYWRlckdlbmVyaWNSZWxlYXNlIiwiZGVmYXVsdFJlYWRlckNsb3NlZFByb21pc2VSZWplY3QiLCJkZWZhdWx0UmVhZGVyQ2xvc2VkUHJvbWlzZVJlc2V0VG9SZWplY3RlZCIsInJlYWRlckxvY2tFeGNlcHRpb24iLCJfY2xvc2VkUHJvbWlzZSIsIl9jbG9zZWRQcm9taXNlX3Jlc29sdmUiLCJfY2xvc2VkUHJvbWlzZV9yZWplY3QiLCJBYm9ydFN0ZXBzIiwiRXJyb3JTdGVwcyIsIkNhbmNlbFN0ZXBzIiwiUHVsbFN0ZXBzIiwiTnVtYmVySXNGaW5pdGUiLCJpc0Zpbml0ZSIsIk1hdGhUcnVuYyIsInRydW5jIiwidiIsImNlaWwiLCJmbG9vciIsImFzc2VydERpY3Rpb25hcnkiLCJjb250ZXh0IiwiYXNzZXJ0RnVuY3Rpb24iLCJhc3NlcnRPYmplY3QiLCJhc3NlcnRSZXF1aXJlZEFyZ3VtZW50IiwicG9zaXRpb24iLCJhc3NlcnRSZXF1aXJlZEZpZWxkIiwiY29udmVydFVucmVzdHJpY3RlZERvdWJsZSIsImNlbnNvck5lZ2F0aXZlWmVybyIsImNvbnZlcnRVbnNpZ25lZExvbmdMb25nV2l0aEVuZm9yY2VSYW5nZSIsInVwcGVyQm91bmQiLCJNQVhfU0FGRV9JTlRFR0VSIiwiaW50ZWdlclBhcnQiLCJhc3NlcnRSZWFkYWJsZVN0cmVhbSIsIklzUmVhZGFibGVTdHJlYW0iLCJBY3F1aXJlUmVhZGFibGVTdHJlYW1EZWZhdWx0UmVhZGVyIiwiUmVhZGFibGVTdHJlYW1EZWZhdWx0UmVhZGVyIiwiUmVhZGFibGVTdHJlYW1BZGRSZWFkUmVxdWVzdCIsInJlYWRSZXF1ZXN0IiwiX3JlYWRSZXF1ZXN0cyIsIlJlYWRhYmxlU3RyZWFtRnVsZmlsbFJlYWRSZXF1ZXN0Iiwic2hpZnQiLCJfY2xvc2VTdGVwcyIsIl9jaHVua1N0ZXBzIiwiUmVhZGFibGVTdHJlYW1HZXROdW1SZWFkUmVxdWVzdHMiLCJSZWFkYWJsZVN0cmVhbUhhc0RlZmF1bHRSZWFkZXIiLCJJc1JlYWRhYmxlU3RyZWFtRGVmYXVsdFJlYWRlciIsIklzUmVhZGFibGVTdHJlYW1Mb2NrZWQiLCJkZWZhdWx0UmVhZGVyQnJhbmRDaGVja0V4Y2VwdGlvbiIsInJlc29sdmVQcm9taXNlIiwicmVqZWN0UHJvbWlzZSIsIlJlYWRhYmxlU3RyZWFtRGVmYXVsdFJlYWRlclJlYWQiLCJfZXJyb3JTdGVwcyIsImUiLCJoYXNPd25Qcm9wZXJ0eSIsIl9kaXN0dXJiZWQiLCJfcmVhZGFibGVTdHJlYW1Db250cm9sbGVyIiwiY2FuY2VsIiwicmVsZWFzZUxvY2siLCJjbG9zZWQiLCJBc3luY0l0ZXJhdG9yUHJvdG90eXBlIiwiUmVhZGFibGVTdHJlYW1Bc3luY0l0ZXJhdG9ySW1wbCIsInByZXZlbnRDYW5jZWwiLCJfb25nb2luZ1Byb21pc2UiLCJfaXNGaW5pc2hlZCIsIl9wcmV2ZW50Q2FuY2VsIiwibmV4dFN0ZXBzIiwiX25leHRTdGVwcyIsInJldHVyblN0ZXBzIiwiX3JldHVyblN0ZXBzIiwiUmVhZGFibGVTdHJlYW1Bc3luY0l0ZXJhdG9yUHJvdG90eXBlIiwiSXNSZWFkYWJsZVN0cmVhbUFzeW5jSXRlcmF0b3IiLCJfYXN5bmNJdGVyYXRvckltcGwiLCJzdHJlYW1Bc3luY0l0ZXJhdG9yQnJhbmRDaGVja0V4Y2VwdGlvbiIsInJldHVybiIsIk51bWJlcklzTmFOIiwiSXNGaW5pdGVOb25OZWdhdGl2ZU51bWJlciIsIklzTm9uTmVnYXRpdmVOdW1iZXIiLCJJbmZpbml0eSIsIkRlcXVldWVWYWx1ZSIsImNvbnRhaW5lciIsIl9xdWV1ZSIsIl9xdWV1ZVRvdGFsU2l6ZSIsIkVucXVldWVWYWx1ZVdpdGhTaXplIiwiUmVzZXRRdWV1ZSIsIkNyZWF0ZUFycmF5RnJvbUxpc3QiLCJSZWFkYWJsZVN0cmVhbUJZT0JSZXF1ZXN0IiwiSXNSZWFkYWJsZVN0cmVhbUJZT0JSZXF1ZXN0IiwiYnlvYlJlcXVlc3RCcmFuZENoZWNrRXhjZXB0aW9uIiwiX3ZpZXciLCJieXRlc1dyaXR0ZW4iLCJfYXNzb2NpYXRlZFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXIiLCJSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyUmVzcG9uZEludGVybmFsIiwiUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlclJlc3BvbmQiLCJ2aWV3IiwiZmlyc3REZXNjcmlwdG9yIiwiX3BlbmRpbmdQdWxsSW50b3MiLCJwZWVrIiwiYnl0ZXNGaWxsZWQiLCJSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyUmVzcG9uZFdpdGhOZXdWaWV3IiwicmVzcG9uZCIsInJlc3BvbmRXaXRoTmV3VmlldyIsIlJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXIiLCJJc1JlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXIiLCJieXRlU3RyZWFtQ29udHJvbGxlckJyYW5kQ2hlY2tFeGNlcHRpb24iLCJfYnlvYlJlcXVlc3QiLCJieW9iUmVxdWVzdCIsIlNldFVwUmVhZGFibGVTdHJlYW1CWU9CUmVxdWVzdCIsIlJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJHZXREZXNpcmVkU2l6ZSIsIl9jbG9zZVJlcXVlc3RlZCIsIl9jb250cm9sbGVkUmVhZGFibGVCeXRlU3RyZWFtIiwiUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckVycm9yIiwiUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckNsZWFyQWxnb3JpdGhtcyIsIlJlYWRhYmxlU3RyZWFtQ2xvc2UiLCJSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyQ2xvc2UiLCJ0cmFuc2ZlcnJlZEJ1ZmZlciIsIlJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJFbnF1ZXVlQ2h1bmtUb1F1ZXVlIiwiUmVhZGFibGVTdHJlYW1IYXNCWU9CUmVhZGVyIiwiUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlclByb2Nlc3NQdWxsSW50b0Rlc2NyaXB0b3JzVXNpbmdRdWV1ZSIsIlJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJDYWxsUHVsbElmTmVlZGVkIiwiUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckVucXVldWUiLCJfY2FuY2VsQWxnb3JpdGhtIiwiZW50cnkiLCJSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVySGFuZGxlUXVldWVEcmFpbiIsImF1dG9BbGxvY2F0ZUNodW5rU2l6ZSIsIl9hdXRvQWxsb2NhdGVDaHVua1NpemUiLCJidWZmZXJFIiwicHVsbEludG9EZXNjcmlwdG9yIiwiZWxlbWVudFNpemUiLCJ2aWV3Q29uc3RydWN0b3IiLCJyZWFkZXJUeXBlIiwiX3N0YXJ0ZWQiLCJSZWFkYWJsZVN0cmVhbUdldE51bVJlYWRJbnRvUmVxdWVzdHMiLCJSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyU2hvdWxkQ2FsbFB1bGwiLCJfcHVsbGluZyIsIl9wdWxsQWdhaW4iLCJfcHVsbEFsZ29yaXRobSIsIlJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJDb21taXRQdWxsSW50b0Rlc2NyaXB0b3IiLCJmaWxsZWRWaWV3IiwiUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckNvbnZlcnRQdWxsSW50b0Rlc2NyaXB0b3IiLCJyZWFkSW50b1JlcXVlc3QiLCJfcmVhZEludG9SZXF1ZXN0cyIsIlJlYWRhYmxlU3RyZWFtRnVsZmlsbFJlYWRJbnRvUmVxdWVzdCIsIlJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJGaWxsUHVsbEludG9EZXNjcmlwdG9yRnJvbVF1ZXVlIiwiY3VycmVudEFsaWduZWRCeXRlcyIsIm1heEJ5dGVzVG9Db3B5IiwibWF4Qnl0ZXNGaWxsZWQiLCJtYXhBbGlnbmVkQnl0ZXMiLCJ0b3RhbEJ5dGVzVG9Db3B5UmVtYWluaW5nIiwicmVhZHkiLCJxdWV1ZSIsImhlYWRPZlF1ZXVlIiwiYnl0ZXNUb0NvcHkiLCJkZXN0U3RhcnQiLCJkZXN0T2Zmc2V0Iiwic3JjIiwic3JjT2Zmc2V0IiwibiIsIlJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJGaWxsSGVhZFB1bGxJbnRvRGVzY3JpcHRvciIsIlJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJJbnZhbGlkYXRlQllPQlJlcXVlc3QiLCJSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyU2hpZnRQZW5kaW5nUHVsbEludG8iLCJSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyUmVzcG9uZEluQ2xvc2VkU3RhdGUiLCJyZW1haW5kZXJTaXplIiwicmVtYWluZGVyIiwiUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlclJlc3BvbmRJblJlYWRhYmxlU3RhdGUiLCJkZXNjcmlwdG9yIiwiUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckNsZWFyUGVuZGluZ1B1bGxJbnRvcyIsIlJlYWRhYmxlU3RyZWFtRXJyb3IiLCJfc3RyYXRlZ3lIV00iLCJSZWFkYWJsZVN0cmVhbUFkZFJlYWRJbnRvUmVxdWVzdCIsIklzUmVhZGFibGVTdHJlYW1CWU9CUmVhZGVyIiwiZGVzaXJlZFNpemUiLCJSZWFkYWJsZVN0cmVhbUJZT0JSZWFkZXIiLCJieW9iUmVhZGVyQnJhbmRDaGVja0V4Y2VwdGlvbiIsIkRhdGFWaWV3IiwiQllURVNfUEVSX0VMRU1FTlQiLCJjdG9yIiwiZW1wdHlWaWV3IiwiUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlclB1bGxJbnRvIiwiUmVhZGFibGVTdHJlYW1CWU9CUmVhZGVyUmVhZCIsIkV4dHJhY3RIaWdoV2F0ZXJNYXJrIiwic3RyYXRlZ3kiLCJkZWZhdWx0SFdNIiwiRXh0cmFjdFNpemVBbGdvcml0aG0iLCJjb252ZXJ0UXVldWluZ1N0cmF0ZWd5IiwiY29udmVydFF1ZXVpbmdTdHJhdGVneVNpemUiLCJjb252ZXJ0VW5kZXJseWluZ1NpbmtBYm9ydENhbGxiYWNrIiwib3JpZ2luYWwiLCJjb252ZXJ0VW5kZXJseWluZ1NpbmtDbG9zZUNhbGxiYWNrIiwiY29udmVydFVuZGVybHlpbmdTaW5rU3RhcnRDYWxsYmFjayIsImNvbnZlcnRVbmRlcmx5aW5nU2lua1dyaXRlQ2FsbGJhY2siLCJhc3NlcnRXcml0YWJsZVN0cmVhbSIsIklzV3JpdGFibGVTdHJlYW0iLCJXcml0YWJsZVN0cmVhbSIsInJhd1VuZGVybHlpbmdTaW5rIiwicmF3U3RyYXRlZ3kiLCJ1bmRlcmx5aW5nU2luayIsImNvbnZlcnRVbmRlcmx5aW5nU2luayIsIkluaXRpYWxpemVXcml0YWJsZVN0cmVhbSIsInNpemVBbGdvcml0aG0iLCJXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyIiwic3RhcnRBbGdvcml0aG0iLCJ3cml0ZUFsZ29yaXRobSIsImNsb3NlQWxnb3JpdGhtIiwiYWJvcnRBbGdvcml0aG0iLCJTZXRVcFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXIiLCJTZXRVcFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJGcm9tVW5kZXJseWluZ1NpbmsiLCJzdHJlYW1CcmFuZENoZWNrRXhjZXB0aW9uIiwiSXNXcml0YWJsZVN0cmVhbUxvY2tlZCIsIldyaXRhYmxlU3RyZWFtQWJvcnQiLCJXcml0YWJsZVN0cmVhbUNsb3NlUXVldWVkT3JJbkZsaWdodCIsIldyaXRhYmxlU3RyZWFtQ2xvc2UiLCJBY3F1aXJlV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyIiwiV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyIiwiX3dyaXRlciIsIl93cml0YWJsZVN0cmVhbUNvbnRyb2xsZXIiLCJfd3JpdGVSZXF1ZXN0cyIsIl9pbkZsaWdodFdyaXRlUmVxdWVzdCIsIl9jbG9zZVJlcXVlc3QiLCJfaW5GbGlnaHRDbG9zZVJlcXVlc3QiLCJfcGVuZGluZ0Fib3J0UmVxdWVzdCIsIl9iYWNrcHJlc3N1cmUiLCJfcHJvbWlzZSIsIndhc0FscmVhZHlFcnJvcmluZyIsIl9yZXNvbHZlIiwiX3JlamVjdCIsIl9yZWFzb24iLCJfd2FzQWxyZWFkeUVycm9yaW5nIiwiV3JpdGFibGVTdHJlYW1TdGFydEVycm9yaW5nIiwiY2xvc2VSZXF1ZXN0Iiwid3JpdGVyIiwiZGVmYXVsdFdyaXRlclJlYWR5UHJvbWlzZVJlc29sdmUiLCJjbG9zZVNlbnRpbmVsIiwiV3JpdGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckFkdmFuY2VRdWV1ZUlmTmVlZGVkIiwiV3JpdGFibGVTdHJlYW1EZWFsV2l0aFJlamVjdGlvbiIsIldyaXRhYmxlU3RyZWFtRmluaXNoRXJyb3JpbmciLCJXcml0YWJsZVN0cmVhbURlZmF1bHRXcml0ZXJFbnN1cmVSZWFkeVByb21pc2VSZWplY3RlZCIsIldyaXRhYmxlU3RyZWFtSGFzT3BlcmF0aW9uTWFya2VkSW5GbGlnaHQiLCJzdG9yZWRFcnJvciIsIndyaXRlUmVxdWVzdCIsIldyaXRhYmxlU3RyZWFtUmVqZWN0Q2xvc2VBbmRDbG9zZWRQcm9taXNlSWZOZWVkZWQiLCJhYm9ydFJlcXVlc3QiLCJkZWZhdWx0V3JpdGVyQ2xvc2VkUHJvbWlzZVJlamVjdCIsIldyaXRhYmxlU3RyZWFtVXBkYXRlQmFja3ByZXNzdXJlIiwiYmFja3ByZXNzdXJlIiwiZGVmYXVsdFdyaXRlclJlYWR5UHJvbWlzZUluaXRpYWxpemUiLCJkZWZhdWx0V3JpdGVyUmVhZHlQcm9taXNlUmVzZXQiLCJnZXRXcml0ZXIiLCJsb2NrZWQiLCJfb3duZXJXcml0YWJsZVN0cmVhbSIsImRlZmF1bHRXcml0ZXJSZWFkeVByb21pc2VJbml0aWFsaXplQXNSZXNvbHZlZCIsImRlZmF1bHRXcml0ZXJDbG9zZWRQcm9taXNlSW5pdGlhbGl6ZSIsImRlZmF1bHRXcml0ZXJSZWFkeVByb21pc2VJbml0aWFsaXplQXNSZWplY3RlZCIsImRlZmF1bHRXcml0ZXJDbG9zZWRQcm9taXNlUmVzb2x2ZSIsImRlZmF1bHRXcml0ZXJDbG9zZWRQcm9taXNlSW5pdGlhbGl6ZUFzUmVqZWN0ZWQiLCJJc1dyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlciIsImRlZmF1bHRXcml0ZXJCcmFuZENoZWNrRXhjZXB0aW9uIiwiZGVmYXVsdFdyaXRlckxvY2tFeGNlcHRpb24iLCJXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyR2V0RGVzaXJlZFNpemUiLCJXcml0YWJsZVN0cmVhbURlZmF1bHRXcml0ZXJHZXREZXNpcmVkU2l6ZSIsIl9yZWFkeVByb21pc2UiLCJXcml0YWJsZVN0cmVhbURlZmF1bHRXcml0ZXJBYm9ydCIsIldyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlckNsb3NlIiwiV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyUmVsZWFzZSIsIldyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlcldyaXRlIiwiX3JlYWR5UHJvbWlzZVN0YXRlIiwiZGVmYXVsdFdyaXRlclJlYWR5UHJvbWlzZVJlamVjdCIsImRlZmF1bHRXcml0ZXJSZWFkeVByb21pc2VSZXNldFRvUmVqZWN0ZWQiLCJyZWxlYXNlZEVycm9yIiwiX2Nsb3NlZFByb21pc2VTdGF0ZSIsImRlZmF1bHRXcml0ZXJDbG9zZWRQcm9taXNlUmVzZXRUb1JlamVjdGVkIiwiV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyRW5zdXJlQ2xvc2VkUHJvbWlzZVJlamVjdGVkIiwiY2h1bmtTaXplIiwiX3N0cmF0ZWd5U2l6ZUFsZ29yaXRobSIsImNodW5rU2l6ZUUiLCJXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyRXJyb3JJZk5lZWRlZCIsIldyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJHZXRDaHVua1NpemUiLCJXcml0YWJsZVN0cmVhbUFkZFdyaXRlUmVxdWVzdCIsImVucXVldWVFIiwiX2NvbnRyb2xsZWRXcml0YWJsZVN0cmVhbSIsIldyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJHZXRCYWNrcHJlc3N1cmUiLCJXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyV3JpdGUiLCJXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyRXJyb3IiLCJfYWJvcnRBbGdvcml0aG0iLCJXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyQ2xlYXJBbGdvcml0aG1zIiwiX3dyaXRlQWxnb3JpdGhtIiwiX2Nsb3NlQWxnb3JpdGhtIiwiciIsIldyaXRhYmxlU3RyZWFtTWFya0Nsb3NlUmVxdWVzdEluRmxpZ2h0Iiwic2lua0Nsb3NlUHJvbWlzZSIsIldyaXRhYmxlU3RyZWFtRmluaXNoSW5GbGlnaHRDbG9zZSIsIldyaXRhYmxlU3RyZWFtRmluaXNoSW5GbGlnaHRDbG9zZVdpdGhFcnJvciIsIldyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJQcm9jZXNzQ2xvc2UiLCJXcml0YWJsZVN0cmVhbU1hcmtGaXJzdFdyaXRlUmVxdWVzdEluRmxpZ2h0IiwiV3JpdGFibGVTdHJlYW1GaW5pc2hJbkZsaWdodFdyaXRlIiwiV3JpdGFibGVTdHJlYW1GaW5pc2hJbkZsaWdodFdyaXRlV2l0aEVycm9yIiwiV3JpdGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlclByb2Nlc3NXcml0ZSIsIl9yZWFkeVByb21pc2VfcmVzb2x2ZSIsIl9yZWFkeVByb21pc2VfcmVqZWN0IiwiTmF0aXZlRE9NRXhjZXB0aW9uIiwiRE9NRXhjZXB0aW9uIiwiRE9NRXhjZXB0aW9uJDEiLCJfYSIsImlzRE9NRXhjZXB0aW9uQ29uc3RydWN0b3IiLCJjcmVhdGVET01FeGNlcHRpb25Qb2x5ZmlsbCIsIlJlYWRhYmxlU3RyZWFtUGlwZVRvIiwicHJldmVudENsb3NlIiwicHJldmVudEFib3J0Iiwic2h1dHRpbmdEb3duIiwiY3VycmVudFdyaXRlIiwiYWN0aW9ucyIsInNodXRkb3duV2l0aEFjdGlvbiIsImFsbCIsImFjdGlvbiIsImlzT3JCZWNvbWVzRXJyb3JlZCIsInNodXRkb3duIiwiV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyQ2xvc2VXaXRoRXJyb3JQcm9wYWdhdGlvbiIsImRlc3RDbG9zZWQiLCJ3YWl0Rm9yV3JpdGVzVG9GaW5pc2giLCJvbGRDdXJyZW50V3JpdGUiLCJvcmlnaW5hbElzRXJyb3IiLCJvcmlnaW5hbEVycm9yIiwiZG9UaGVSZXN0IiwibmV3RXJyb3IiLCJpc0Vycm9yIiwicmVzb2x2ZUxvb3AiLCJyZWplY3RMb29wIiwicmVzb2x2ZVJlYWQiLCJyZWplY3RSZWFkIiwiUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlciIsIklzUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlciIsImRlZmF1bHRDb250cm9sbGVyQnJhbmRDaGVja0V4Y2VwdGlvbiIsIlJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJHZXREZXNpcmVkU2l6ZSIsIlJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJDYW5DbG9zZU9yRW5xdWV1ZSIsIlJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJDbG9zZSIsIlJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJFbnF1ZXVlIiwiUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckVycm9yIiwiUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckNsZWFyQWxnb3JpdGhtcyIsIl9jb250cm9sbGVkUmVhZGFibGVTdHJlYW0iLCJSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyQ2FsbFB1bGxJZk5lZWRlZCIsIlJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJTaG91bGRDYWxsUHVsbCIsIlNldFVwUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlciIsInB1bGxBbGdvcml0aG0iLCJjYW5jZWxBbGdvcml0aG0iLCJjb252ZXJ0VW5kZXJseWluZ1NvdXJjZUNhbmNlbENhbGxiYWNrIiwiY29udmVydFVuZGVybHlpbmdTb3VyY2VQdWxsQ2FsbGJhY2siLCJjb252ZXJ0VW5kZXJseWluZ1NvdXJjZVN0YXJ0Q2FsbGJhY2siLCJjb252ZXJ0UmVhZGFibGVTdHJlYW1UeXBlIiwiY29udmVydFJlYWRhYmxlU3RyZWFtUmVhZGVyTW9kZSIsIm1vZGUiLCJjb252ZXJ0UGlwZU9wdGlvbnMiLCJpc0Fib3J0U2lnbmFsIiwiYXNzZXJ0QWJvcnRTaWduYWwiLCJyYXdVbmRlcmx5aW5nU291cmNlIiwidW5kZXJseWluZ1NvdXJjZSIsInB1bGwiLCJjb252ZXJ0VW5kZXJseWluZ0RlZmF1bHRPckJ5dGVTb3VyY2UiLCJJbml0aWFsaXplUmVhZGFibGVTdHJlYW0iLCJ1bmRlcmx5aW5nQnl0ZVNvdXJjZSIsIlNldFVwUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlciIsIlNldFVwUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckZyb21VbmRlcmx5aW5nU291cmNlIiwiU2V0VXBSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyRnJvbVVuZGVybHlpbmdTb3VyY2UiLCJzdHJlYW1CcmFuZENoZWNrRXhjZXB0aW9uJDEiLCJyYXdPcHRpb25zIiwiY29udmVydFJlYWRlck9wdGlvbnMiLCJyYXdUcmFuc2Zvcm0iLCJ0cmFuc2Zvcm0iLCJyZWFkYWJsZSIsImNvbnZlcnRSZWFkYWJsZVdyaXRhYmxlUGFpciIsImRlc3RpbmF0aW9uIiwiYnJhbmNoZXMiLCJjbG9uZUZvckJyYW5jaDIiLCJyZWFzb24xIiwicmVhc29uMiIsImJyYW5jaDEiLCJicmFuY2gyIiwicmVzb2x2ZUNhbmNlbFByb21pc2UiLCJyZWFkaW5nIiwiY2FuY2VsZWQxIiwiY2FuY2VsZWQyIiwiY2FuY2VsUHJvbWlzZSIsInZhbHVlMSIsInZhbHVlMiIsIkNyZWF0ZVJlYWRhYmxlU3RyZWFtIiwiY29tcG9zaXRlUmVhc29uIiwiY2FuY2VsUmVzdWx0IiwiUmVhZGFibGVTdHJlYW1UZWUiLCJpbXBsIiwiQWNxdWlyZVJlYWRhYmxlU3RyZWFtQXN5bmNJdGVyYXRvciIsImNvbnZlcnRJdGVyYXRvck9wdGlvbnMiLCJjb252ZXJ0UXVldWluZ1N0cmF0ZWd5SW5pdCIsInBpcGVUaHJvdWdoIiwicGlwZVRvIiwidGVlIiwiYXN5bmNJdGVyYXRvciIsImJ5dGVMZW5ndGhTaXplRnVuY3Rpb24iLCJCeXRlTGVuZ3RoUXVldWluZ1N0cmF0ZWd5IiwiX2J5dGVMZW5ndGhRdWV1aW5nU3RyYXRlZ3lIaWdoV2F0ZXJNYXJrIiwiSXNCeXRlTGVuZ3RoUXVldWluZ1N0cmF0ZWd5IiwiYnl0ZUxlbmd0aEJyYW5kQ2hlY2tFeGNlcHRpb24iLCJjb3VudFNpemVGdW5jdGlvbiIsIkNvdW50UXVldWluZ1N0cmF0ZWd5IiwiX2NvdW50UXVldWluZ1N0cmF0ZWd5SGlnaFdhdGVyTWFyayIsIklzQ291bnRRdWV1aW5nU3RyYXRlZ3kiLCJjb3VudEJyYW5kQ2hlY2tFeGNlcHRpb24iLCJjb252ZXJ0VHJhbnNmb3JtZXJGbHVzaENhbGxiYWNrIiwiY29udmVydFRyYW5zZm9ybWVyU3RhcnRDYWxsYmFjayIsImNvbnZlcnRUcmFuc2Zvcm1lclRyYW5zZm9ybUNhbGxiYWNrIiwiVHJhbnNmb3JtU3RyZWFtIiwicmF3VHJhbnNmb3JtZXIiLCJyYXdXcml0YWJsZVN0cmF0ZWd5IiwicmF3UmVhZGFibGVTdHJhdGVneSIsIndyaXRhYmxlU3RyYXRlZ3kiLCJyZWFkYWJsZVN0cmF0ZWd5IiwidHJhbnNmb3JtZXIiLCJyZWFkYWJsZVR5cGUiLCJ3cml0YWJsZVR5cGUiLCJjb252ZXJ0VHJhbnNmb3JtZXIiLCJyZWFkYWJsZUhpZ2hXYXRlck1hcmsiLCJyZWFkYWJsZVNpemVBbGdvcml0aG0iLCJ3cml0YWJsZUhpZ2hXYXRlck1hcmsiLCJ3cml0YWJsZVNpemVBbGdvcml0aG0iLCJzdGFydFByb21pc2VfcmVzb2x2ZSIsInN0YXJ0UHJvbWlzZSIsIl93cml0YWJsZSIsIkNyZWF0ZVdyaXRhYmxlU3RyZWFtIiwiX3RyYW5zZm9ybVN0cmVhbUNvbnRyb2xsZXIiLCJfYmFja3ByZXNzdXJlQ2hhbmdlUHJvbWlzZSIsIlRyYW5zZm9ybVN0cmVhbURlZmF1bHRDb250cm9sbGVyUGVyZm9ybVRyYW5zZm9ybSIsIlRyYW5zZm9ybVN0cmVhbURlZmF1bHRTaW5rV3JpdGVBbGdvcml0aG0iLCJfcmVhZGFibGUiLCJmbHVzaFByb21pc2UiLCJfZmx1c2hBbGdvcml0aG0iLCJUcmFuc2Zvcm1TdHJlYW1EZWZhdWx0Q29udHJvbGxlckNsZWFyQWxnb3JpdGhtcyIsIlRyYW5zZm9ybVN0cmVhbUVycm9yIiwiVHJhbnNmb3JtU3RyZWFtRGVmYXVsdFNpbmtDbG9zZUFsZ29yaXRobSIsIlRyYW5zZm9ybVN0cmVhbURlZmF1bHRTaW5rQWJvcnRBbGdvcml0aG0iLCJUcmFuc2Zvcm1TdHJlYW1TZXRCYWNrcHJlc3N1cmUiLCJUcmFuc2Zvcm1TdHJlYW1EZWZhdWx0U291cmNlUHVsbEFsZ29yaXRobSIsIlRyYW5zZm9ybVN0cmVhbUVycm9yV3JpdGFibGVBbmRVbmJsb2NrV3JpdGUiLCJfYmFja3ByZXNzdXJlQ2hhbmdlUHJvbWlzZV9yZXNvbHZlIiwiSW5pdGlhbGl6ZVRyYW5zZm9ybVN0cmVhbSIsIlRyYW5zZm9ybVN0cmVhbURlZmF1bHRDb250cm9sbGVyIiwidHJhbnNmb3JtQWxnb3JpdGhtIiwiVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJFbnF1ZXVlIiwidHJhbnNmb3JtUmVzdWx0RSIsImZsdXNoQWxnb3JpdGhtIiwiX2NvbnRyb2xsZWRUcmFuc2Zvcm1TdHJlYW0iLCJfdHJhbnNmb3JtQWxnb3JpdGhtIiwiU2V0VXBUcmFuc2Zvcm1TdHJlYW1EZWZhdWx0Q29udHJvbGxlciIsIlNldFVwVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJGcm9tVHJhbnNmb3JtZXIiLCJJc1RyYW5zZm9ybVN0cmVhbSIsInN0cmVhbUJyYW5kQ2hlY2tFeGNlcHRpb24kMiIsIklzVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXIiLCJkZWZhdWx0Q29udHJvbGxlckJyYW5kQ2hlY2tFeGNlcHRpb24kMSIsIlRyYW5zZm9ybVN0cmVhbURlZmF1bHRDb250cm9sbGVyVGVybWluYXRlIiwicmVhZGFibGVDb250cm9sbGVyIiwiUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckhhc0JhY2twcmVzc3VyZSIsInRlcm1pbmF0ZSIsInJlcXVpcmUiLCJfX3dlYnBhY2tfbW9kdWxlX2NhY2hlX18iLCJfX3dlYnBhY2tfcmVxdWlyZV9fIiwibW9kdWxlSWQiLCJfX3dlYnBhY2tfbW9kdWxlc19fIiwiZGVmaW5pdGlvbiIsIm8iLCJwcm9wIl0sIm1hcHBpbmdzIjoiO0NBQUEsU0FBMkNBLEVBQU1DLEdBQzFCLGlCQUFaQyxTQUEwQyxpQkFBWEMsT0FDeENBLE9BQU9ELFFBQVVELElBQ1EsbUJBQVhHLFFBQXlCQSxPQUFPQyxJQUM5Q0QsT0FBTyxHQUFJSCxHQUNlLGlCQUFaQyxRQUNkQSxRQUFpQixRQUFJRCxJQUVyQkQsRUFBYyxRQUFJQyxJQVJwQixDQVNHSyxNQUFNLFdBQ1QsTSx3Q0NKQUMsT0FBT0MsZUFBZU4sRUFBUyxhQUEvQixDQUErQ08sT0FBTyxJQUV0RCxJQUFJQyxFQUFrQixFQUFRLEtBTTlCLE1BQU1DLFVBQW9CRCxFQUFnQkUsWUFJdEMs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T0FBT1ksVUFBVWtxQixlQUFldHBCLEtBQUtpSixFQUFLaTZCLEdDQ2pGTCxFQUFvQjlLLEVBQUs1NUIsSUFDSCxvQkFBWHFCLFFBQTBCQSxPQUFPQyxhQUMxQ2pCLE9BQU9DLGVBQWVOLEVBQVNxQixPQUFPQyxZQUFhLENBQUVmLE1BQU8sV0FFN0RGLE9BQU9DLGVBQWVOLEVBQVMsYUFBYyxDQUFFTyxPQUFPLEtIRmhEbWtDLEVBQW9CLE0iLCJmaWxlIjoibWFpbGd1bi5qcyIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiB3ZWJwYWNrVW5pdmVyc2FsTW9kdWxlRGVmaW5pdGlvbihyb290LCBmYWN0b3J5KSB7XG5cdGlmKHR5cGVvZiBleHBvcnRzID09PSAnb2JqZWN0JyAmJiB0eXBlb2YgbW9kdWxlID09PSAnb2JqZWN0Jylcblx0XHRtb2R1bGUuZXhwb3J0cyA9IGZhY3RvcnkoKTtcblx0ZWxzZSBpZih0eXBlb2YgZGVmaW5lID09PSAnZnVuY3Rpb24nICYmIGRlZmluZS5hbWQpXG5cdFx0ZGVmaW5lKFtdLCBmYWN0b3J5KTtcblx0ZWxzZSBpZih0eXBlb2YgZXhwb3J0cyA9PT0gJ29iamVjdCcpXG5cdFx0ZXhwb3J0c1tcIm1haWxndW5cIl0gPSBmYWN0b3J5KCk7XG5cdGVsc2Vcblx0XHRyb290W1wibWFpbGd1blwiXSA9IGZhY3RvcnkoKTtcbn0pKHRoaXMsIGZ1bmN0aW9uKCkge1xucmV0dXJuICIsIi8qKlxuICogQGF1dGhvciBUb3J1IE5hZ2FzaGltYSA8aHR0cHM6Ly9naXRodWIuY29tL215c3RpY2F0ZWE+XG4gKiBTZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZGlyZWN0b3J5IGZvciBmdWxsIGxpY2Vuc2UuXG4gKi9cbid1c2Ugc3RyaWN0JztcblxuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcblxudmFyIGV2ZW50VGFyZ2V0U2hpbSA9IHJlcXVpcmUoJ2V2ZW50LXRhcmdldC1zaGltJyk7XG5cbi8qKlxuICogVGhlIHNpZ25hbCBjbGFzcy5cbiAqIEBzZWUgaHR0cHM6Ly9kb20uc3BlYy53aGF0d2cub3JnLyNhYm9ydHNpZ25hbFxuICovXG5jbGFzcyBBYm9ydFNpZ25hbCBleHRlbmRzIGV2ZW50VGFyZ2V0U2hpbS5FdmVudFRhcmdldCB7XG4gICAgLyoqXG4gICAgICogQWJvcnRTaWduYWwgY2Fubm90IGJlIGNvbnN0cnVjdGVkIGRpcmVjdGx5LlxuICAgICAqL1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICBzdXBlcigpO1xuICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKFwiQWJvcnRTaWduYWwgY2Fubm90IGJlIGNvbnN0cnVjdGVkIGRpcmVjdGx5XCIpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIGB0cnVlYCBpZiB0aGlzIGBBYm9ydFNpZ25hbGAncyBgQWJvcnRDb250cm9sbGVyYCBoYXMgc2lnbmFsZWQgdG8gYWJvcnQsIGFuZCBgZmFsc2VgIG90aGVyd2lzZS5cbiAgICAgKi9cbiAgICBnZXQgYWJvcnRlZCgpIHtcbiAgICAgICAgY29uc3QgYWJvcnRlZCA9IGFib3J0ZWRGbGFncy5nZXQodGhpcyk7XG4gICAgICAgIGlmICh0eXBlb2YgYWJvcnRlZCAhPT0gXCJib29sZWFuXCIpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoYEV4cGVjdGVkICd0aGlzJyB0byBiZSBhbiAnQWJvcnRTaWduYWwnIG9iamVjdCwgYnV0IGdvdCAke3RoaXMgPT09IG51bGwgPyBcIm51bGxcIiA6IHR5cGVvZiB0aGlzfWApO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBhYm9ydGVkO1xuICAgIH1cbn1cbmV2ZW50VGFyZ2V0U2hpbS5kZWZpbmVFdmVudEF0dHJpYnV0ZShBYm9ydFNpZ25hbC5wcm90b3R5cGUsIFwiYWJvcnRcIik7XG4vKipcbiAqIENyZWF0ZSBhbiBBYm9ydFNpZ25hbCBvYmplY3QuXG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZUFib3J0U2lnbmFsKCkge1xuICAgIGNvbnN0IHNpZ25hbCA9IE9iamVjdC5jcmVhdGUoQWJvcnRTaWduYWwucHJvdG90eXBlKTtcbiAgICBldmVudFRhcmdldFNoaW0uRXZlbnRUYXJnZXQuY2FsbChzaWduYWwpO1xuICAgIGFib3J0ZWRGbGFncy5zZXQoc2lnbmFsLCBmYWxzZSk7XG4gICAgcmV0dXJuIHNpZ25hbDtcbn1cbi8qKlxuICogQWJvcnQgYSBnaXZlbiBzaWduYWwuXG4gKi9cbmZ1bmN0aW9uIGFib3J0U2lnbmFsKHNpZ25hbCkge1xuICAgIGlmIChhYm9ydGVkRmxhZ3MuZ2V0KHNpZ25hbCkgIT09IGZhbHNlKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgYWJvcnRlZEZsYWdzLnNldChzaWduYWwsIHRydWUpO1xuICAgIHNpZ25hbC5kaXNwYXRjaEV2ZW50KHsgdHlwZTogXCJhYm9ydFwiIH0pO1xufVxuLyoqXG4gKiBBYm9ydGVkIGZsYWcgZm9yIGVhY2ggaW5zdGFuY2VzLlxuICovXG5jb25zdCBhYm9ydGVkRmxhZ3MgPSBuZXcgV2Vha01hcCgpO1xuLy8gUHJvcGVydGllcyBzaG91bGQgYmUgZW51bWVyYWJsZS5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKEFib3J0U2lnbmFsLnByb3RvdHlwZSwge1xuICAgIGFib3J0ZWQ6IHsgZW51bWVyYWJsZTogdHJ1ZSB9LFxufSk7XG4vLyBgdG9TdHJpbmcoKWAgc2hvdWxkIHJldHVybiBgXCJbb2JqZWN0IEFib3J0U2lnbmFsXVwiYFxuaWYgKHR5cGVvZiBTeW1ib2wgPT09IFwiZnVuY3Rpb25cIiAmJiB0eXBlb2YgU3ltYm9sLnRvU3RyaW5nVGFnID09PSBcInN5bWJvbFwiKSB7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KEFib3J0U2lnbmFsLnByb3RvdHlwZSwgU3ltYm9sLnRvU3RyaW5nVGFnLCB7XG4gICAgICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZSxcbiAgICAgICAgdmFsdWU6IFwiQWJvcnRTaWduYWxcIixcbiAgICB9KTtcbn1cblxuLyoqXG4gKiBUaGUgQWJvcnRDb250cm9sbGVyLlxuICogQHNlZSBodHRwczovL2RvbS5zcGVjLndoYXR3Zy5vcmcvI2Fib3J0Y29udHJvbGxlclxuICovXG5jbGFzcyBBYm9ydENvbnRyb2xsZXIge1xuICAgIC8qKlxuICAgICAqIEluaXRpYWxpemUgdGhpcyBjb250cm9sbGVyLlxuICAgICAqL1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICBzaWduYWxzLnNldCh0aGlzLCBjcmVhdGVBYm9ydFNpZ25hbCgpKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgYEFib3J0U2lnbmFsYCBvYmplY3QgYXNzb2NpYXRlZCB3aXRoIHRoaXMgb2JqZWN0LlxuICAgICAqL1xuICAgIGdldCBzaWduYWwoKSB7XG4gICAgICAgIHJldHVybiBnZXRTaWduYWwodGhpcyk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEFib3J0IGFuZCBzaWduYWwgdG8gYW55IG9ic2VydmVycyB0aGF0IHRoZSBhc3NvY2lhdGVkIGFjdGl2aXR5IGlzIHRvIGJlIGFib3J0ZWQuXG4gICAgICovXG4gICAgYWJvcnQoKSB7XG4gICAgICAgIGFib3J0U2lnbmFsKGdldFNpZ25hbCh0aGlzKSk7XG4gICAgfVxufVxuLyoqXG4gKiBBc3NvY2lhdGVkIHNpZ25hbHMuXG4gKi9cbmNvbnN0IHNpZ25hbHMgPSBuZXcgV2Vha01hcCgpO1xuLyoqXG4gKiBHZXQgdGhlIGFzc29jaWF0ZWQgc2lnbmFsIG9mIGEgZ2l2ZW4gY29udHJvbGxlci5cbiAqL1xuZnVuY3Rpb24gZ2V0U2lnbmFsKGNvbnRyb2xsZXIpIHtcbiAgICBjb25zdCBzaWduYWwgPSBzaWduYWxzLmdldChjb250cm9sbGVyKTtcbiAgICBpZiAoc2lnbmFsID09IG51bGwpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihgRXhwZWN0ZWQgJ3RoaXMnIHRvIGJlIGFuICdBYm9ydENvbnRyb2xsZXInIG9iamVjdCwgYnV0IGdvdCAke2NvbnRyb2xsZXIgPT09IG51bGwgPyBcIm51bGxcIiA6IHR5cGVvZiBjb250cm9sbGVyfWApO1xuICAgIH1cbiAgICByZXR1cm4gc2lnbmFsO1xufVxuLy8gUHJvcGVydGllcyBzaG91bGQgYmUgZW51bWVyYWJsZS5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKEFib3J0Q29udHJvbGxlci5wcm90b3R5cGUsIHtcbiAgICBzaWduYWw6IHsgZW51bWVyYWJsZTogdHJ1ZSB9LFxuICAgIGFib3J0OiB7IGVudW1lcmFibGU6IHRydWUgfSxcbn0pO1xuaWYgKHR5cGVvZiBTeW1ib2wgPT09IFwiZnVuY3Rpb25cIiAmJiB0eXBlb2YgU3ltYm9sLnRvU3RyaW5nVGFnID09PSBcInN5bWJvbFwiKSB7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KEFib3J0Q29udHJvbGxlci5wcm90b3R5cGUsIFN5bWJvbC50b1N0cmluZ1RhZywge1xuICAgICAgICBjb25maWd1cmFibGU6IHRydWUsXG4gICAgICAgIHZhbHVlOiBcIkFib3J0Q29udHJvbGxlclwiLFxuICAgIH0pO1xufVxuXG5leHBvcnRzLkFib3J0Q29udHJvbGxlciA9IEFib3J0Q29udHJvbGxlcjtcbmV4cG9ydHMuQWJvcnRTaWduYWwgPSBBYm9ydFNpZ25hbDtcbmV4cG9ydHMuZGVmYXVsdCA9IEFib3J0Q29udHJvbGxlcjtcblxubW9kdWxlLmV4cG9ydHMgPSBBYm9ydENvbnRyb2xsZXJcbm1vZHVsZS5leHBvcnRzLkFib3J0Q29udHJvbGxlciA9IG1vZHVsZS5leHBvcnRzW1wiZGVmYXVsdFwiXSA9IEFib3J0Q29udHJvbGxlclxubW9kdWxlLmV4cG9ydHMuQWJvcnRTaWduYWwgPSBBYm9ydFNpZ25hbFxuLy8jIHNvdXJjZU1hcHBpbmdVUkw9YWJvcnQtY29udHJvbGxlci5qcy5tYXBcbiIsImltcG9ydCBDbGllbnQgZnJvbSAnLi9saWIvY2xpZW50J1xuaW1wb3J0IE9wdGlvbnMgZnJvbSAnLi9saWIvaW50ZXJmYWNlcy9PcHRpb25zJztcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgTWFpbGd1biB7XG4gIHByaXZhdGUgZm9ybURhdGE6IG5ldyAoKSA9PiBGb3JtRGF0YVxuXG4gIGNvbnN0cnVjdG9yKEZvcm1EYXRhOiBuZXcgKCkgPT4gRm9ybURhdGEpIHtcbiAgICB0aGlzLmZvcm1EYXRhID0gRm9ybURhdGE7XG4gIH1cblxuICBjbGllbnQob3B0aW9uczogT3B0aW9ucykge1xuICAgIHJldHVybiBuZXcgQ2xpZW50KG9wdGlvbnMsIHRoaXMuZm9ybURhdGEpXG4gIH1cbn07IiwiaW1wb3J0IFJlcXVlc3QgZnJvbSAnLi9yZXF1ZXN0JztcbmltcG9ydCBPcHRpb25zIGZyb20gJy4vaW50ZXJmYWNlcy9PcHRpb25zJztcbmltcG9ydCBSZXF1ZXN0T3B0aW9ucyBmcm9tICcuL2ludGVyZmFjZXMvUmVxdWVzdE9wdGlvbnMnO1xuXG5pbXBvcnQgRG9tYWluQ2xpZW50IGZyb20gJy4vZG9tYWlucyc7XG5pbXBvcnQgRXZlbnRDbGllbnQgZnJvbSAnLi9ldmVudHMnO1xuaW1wb3J0IFN0YXRzQ2xpZW50IGZyb20gJy4vc3RhdHMnO1xuaW1wb3J0IFN1cHByZXNzaW9uQ2xpZW50IGZyb20gJy4vc3VwcHJlc3Npb25zJztcbmltcG9ydCBXZWJob29rQ2xpZW50IGZyb20gJy4vd2ViaG9va3MnO1xuaW1wb3J0IE1lc3NhZ2VzQ2xpZW50IGZyb20gJy4vbWVzc2FnZXMnO1xuaW1wb3J0IFJvdXRlc0NsaWVudCBmcm9tICcuL3JvdXRlcyc7XG5pbXBvcnQgVmFsaWRhdGVDbGllbnQgZnJvbSAnLi92YWxpZGF0ZSc7XG5pbXBvcnQgUGFyc2VDbGllbnQgZnJvbSAnLi9wYXJzZSc7XG5pbXBvcnQgSXBzQ2xpZW50IGZyb20gJy4vaXBzJztcbmltcG9ydCBJcFBvb2xzQ2xpZW50IGZyb20gJy4vaXAtcG9vbHMnO1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBDbGllbnQge1xuICBwcml2YXRlIHJlcXVlc3Q7XG5cbiAgcHVibGljIGRvbWFpbnM7XG4gIHB1YmxpYyB3ZWJob29rcztcbiAgcHVibGljIGV2ZW50cztcbiAgcHVibGljIHN0YXRzO1xuICBwdWJsaWMgc3VwcHJlc3Npb25zO1xuICBwdWJsaWMgbWVzc2FnZXM7XG4gIHB1YmxpYyByb3V0ZXM7XG4gIHB1YmxpYyBwdWJsaWNfcmVxdWVzdDtcbiAgcHVibGljIHZhbGlkYXRlO1xuICBwdWJsaWMgcGFyc2U7XG4gIHB1YmxpYyBpcHM7XG4gIHB1YmxpYyBpcF9wb29scztcblxuICBjb25zdHJ1Y3RvcihvcHRpb25zOiBPcHRpb25zLCBmb3JtRGF0YTogbmV3ICgpID0+IEZvcm1EYXRhKSB7XG4gICAgY29uc3QgY29uZmlnOiBSZXF1ZXN0T3B0aW9ucyA9IHsgLi4ub3B0aW9ucyB9IGFzIFJlcXVlc3RPcHRpb25zO1xuXG4gICAgaWYgKCFjb25maWcudXJsKSB7XG4gICAgICBjb25maWcudXJsID0gJ2h0dHBzOi8vYXBpLm1haWxndW4ubmV0J1xuICAgIH1cblxuICAgIGlmICghY29uZmlnLnVzZXJuYW1lKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1BhcmFtZXRlciBcInVzZXJuYW1lXCIgaXMgcmVxdWlyZWQnKTtcbiAgICB9XG5cbiAgICBpZiAoIWNvbmZpZy5rZXkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignUGFyYW1ldGVyIFwia2V5XCIgaXMgcmVxdWlyZWQnKTtcbiAgICB9XG5cbiAgICAvKiogQGludGVybmFsICovXG4gICAgdGhpcy5yZXF1ZXN0ID0gbmV3IFJlcXVlc3QoY29uZmlnLCBmb3JtRGF0YSk7XG5cbiAgICB0aGlzLmRvbWFpbnMgPSBuZXcgRG9tYWluQ2xpZW50KHRoaXMucmVxdWVzdCk7XG4gICAgdGhpcy53ZWJob29rcyA9IG5ldyBXZWJob29rQ2xpZW50KHRoaXMucmVxdWVzdCk7XG4gICAgdGhpcy5ldmVudHMgPSBuZXcgRXZlbnRDbGllbnQodGhpcy5yZXF1ZXN0KTtcbiAgICB0aGlzLnN0YXRzID0gbmV3IFN0YXRzQ2xpZW50KHRoaXMucmVxdWVzdCk7XG4gICAgdGhpcy5zdXBwcmVzc2lvbnMgPSBuZXcgU3VwcHJlc3Npb25DbGllbnQodGhpcy5yZXF1ZXN0KTtcbiAgICB0aGlzLm1lc3NhZ2VzID0gbmV3IE1lc3NhZ2VzQ2xpZW50KHRoaXMucmVxdWVzdCk7XG4gICAgdGhpcy5yb3V0ZXMgPSBuZXcgUm91dGVzQ2xpZW50KHRoaXMucmVxdWVzdCk7XG4gICAgdGhpcy5pcHMgPSBuZXcgSXBzQ2xpZW50KHRoaXMucmVxdWVzdCk7XG4gICAgdGhpcy5pcF9wb29scyA9IG5ldyBJcFBvb2xzQ2xpZW50KHRoaXMucmVxdWVzdCk7XG5cbiAgICBpZiAoY29uZmlnLnB1YmxpY19rZXkpIHtcbiAgICAgIGNvbmZpZy5rZXkgPSBjb25maWcucHVibGljX2tleTtcblxuICAgICAgdGhpcy5wdWJsaWNfcmVxdWVzdCA9IG5ldyBSZXF1ZXN0KGNvbmZpZywgZm9ybURhdGEpO1xuICAgICAgdGhpcy52YWxpZGF0ZSA9IG5ldyBWYWxpZGF0ZUNsaWVudCh0aGlzLnB1YmxpY19yZXF1ZXN0KTtcbiAgICAgIHRoaXMucGFyc2UgPSBuZXcgUGFyc2VDbGllbnQodGhpcy5wdWJsaWNfcmVxdWVzdCk7XG4gICAgfVxuICB9XG59XG4iLCJpbXBvcnQgdXJsam9pbiBmcm9tICd1cmwtam9pbic7XG5pbXBvcnQgUmVxdWVzdCBmcm9tICcuL3JlcXVlc3QnO1xuXG5pbnRlcmZhY2UgRG9tYWluRGF0YSB7XG4gIG5hbWU6IHN0cmluZztcbiAgcmVxdWlyZV90bHM6IGFueTtcbiAgc2tpcF92ZXJpZmljYXRpb246IGFueTtcbiAgc3RhdGU6IGFueTtcbiAgd2lsZGNhcmQ6IGFueTtcbiAgc3BhbV9hY3Rpb246IGFueTtcbiAgY3JlYXRlZF9hdDogc3RyaW5nIHwgRGF0ZTtcbiAgc210cF9wYXNzd29yZDogc3RyaW5nO1xuICBzbXRwX2xvZ2luOiBzdHJpbmc7XG4gIHR5cGU6IHN0cmluZztcbn1cblxuY2xhc3MgRG9tYWluIHtcbiAgbmFtZTogYW55O1xuICByZXF1aXJlX3RsczogYW55O1xuICBza2lwX3ZlcmlmaWNhdGlvbjogYW55O1xuICBzdGF0ZTogYW55O1xuICB3aWxkY2FyZDogYW55O1xuICBzcGFtX2FjdGlvbjogYW55O1xuICBjcmVhdGVkX2F0OiBhbnk7XG4gIHNtdHBfcGFzc3dvcmQ6IGFueTtcbiAgc210cF9sb2dpbjogYW55O1xuICB0eXBlOiBhbnk7XG4gIHJlY2VpdmluZ19kbnNfcmVjb3JkczogYW55O1xuICBzZW5kaW5nX2Ruc19yZWNvcmRzOiBhbnk7XG5cbiAgY29uc3RydWN0b3IoZGF0YTogRG9tYWluRGF0YSwgcmVjZWl2aW5nPzogYW55LCBzZW5kaW5nPzogYW55KSB7XG4gICAgdGhpcy5uYW1lID0gZGF0YS5uYW1lO1xuICAgIHRoaXMucmVxdWlyZV90bHMgPSBkYXRhLnJlcXVpcmVfdGxzO1xuICAgIHRoaXMuc2tpcF92ZXJpZmljYXRpb24gPSBkYXRhLnNraXBfdmVyaWZpY2F0aW9uO1xuICAgIHRoaXMuc3RhdGUgPSBkYXRhLnN0YXRlO1xuICAgIHRoaXMud2lsZGNhcmQgPSBkYXRhLndpbGRjYXJkO1xuICAgIHRoaXMuc3BhbV9hY3Rpb24gPSBkYXRhLnNwYW1fYWN0aW9uO1xuICAgIHRoaXMuY3JlYXRlZF9hdCA9IGRhdGEuY3JlYXRlZF9hdDtcbiAgICB0aGlzLnNtdHBfcGFzc3dvcmQgPSBkYXRhLnNtdHBfcGFzc3dvcmQ7XG4gICAgdGhpcy5zbXRwX2xvZ2luID0gZGF0YS5zbXRwX2xvZ2luO1xuICAgIHRoaXMudHlwZSA9IGRhdGEudHlwZTtcblxuICAgIHRoaXMucmVjZWl2aW5nX2Ruc19yZWNvcmRzID0gcmVjZWl2aW5nIHx8IG51bGw7XG4gICAgdGhpcy5zZW5kaW5nX2Ruc19yZWNvcmRzID0gc2VuZGluZyB8fCBudWxsO1xuICB9XG59XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIERvbWFpbkNsaWVudCB7XG4gIHJlcXVlc3Q6IFJlcXVlc3Q7XG5cbiAgY29uc3RydWN0b3IocmVxdWVzdDogUmVxdWVzdCkge1xuICAgIHRoaXMucmVxdWVzdCA9IHJlcXVlc3Q7XG4gIH1cblxuICBfcGFyc2VNZXNzYWdlKHJlc3BvbnNlOiB7IGJvZHk6IGFueSB9KSB7XG4gICAgcmV0dXJuIHJlc3BvbnNlLmJvZHk7XG4gIH1cblxuICBfcGFyc2VEb21haW5MaXN0KHJlc3BvbnNlOiB7IGJvZHk6IHsgaXRlbXM6IERvbWFpbkRhdGFbXSB9IH0pIHtcbiAgICByZXR1cm4gcmVzcG9uc2UuYm9keS5pdGVtcy5tYXAoZnVuY3Rpb24gKGl0ZW0pIHtcbiAgICAgIHJldHVybiBuZXcgRG9tYWluKGl0ZW0pO1xuICAgIH0pO1xuICB9XG5cbiAgX3BhcnNlRG9tYWluKHJlc3BvbnNlOiB7XG4gICAgYm9keToge1xuICAgICAgZG9tYWluOiBhbnksXG4gICAgICByZWNlaXZpbmdfZG5zX3JlY29yZHM6IGFueSxcbiAgICAgIHNlbmRpbmdfZG5zX3JlY29yZHM6IGFueVxuICAgIH1cbiAgfSkge1xuICAgIHJldHVybiBuZXcgRG9tYWluKFxuICAgICAgcmVzcG9uc2UuYm9keS5kb21haW4sXG4gICAgICByZXNwb25zZS5ib2R5LnJlY2VpdmluZ19kbnNfcmVjb3JkcyxcbiAgICAgIHJlc3BvbnNlLmJvZHkuc2VuZGluZ19kbnNfcmVjb3Jkc1xuICAgICk7XG4gIH1cblxuICBfcGFyc2VUcmFja2luZ1NldHRpbmdzKHJlc3BvbnNlOiB7IGJvZHk6IHsgdHJhY2tpbmc6IGFueSB9IH0pIHtcbiAgICByZXR1cm4gcmVzcG9uc2UuYm9keS50cmFja2luZztcbiAgfVxuXG4gIF9wYXJzZVRyYWNraW5nVXBkYXRlKHJlc3BvbnNlOiB7IGJvZHk6IGFueSB9KSB7XG4gICAgcmV0dXJuIHJlc3BvbnNlLmJvZHk7XG4gIH1cblxuICBsaXN0KHF1ZXJ5OiBhbnkpIHtcbiAgICByZXR1cm4gdGhpcy5yZXF1ZXN0LmdldCgnL3YyL2RvbWFpbnMnLCBxdWVyeSlcbiAgICAgIC50aGVuKHRoaXMuX3BhcnNlRG9tYWluTGlzdCk7XG4gIH1cblxuICBnZXQoZG9tYWluOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gdGhpcy5yZXF1ZXN0LmdldChgL3YyL2RvbWFpbnMvJHtkb21haW59YClcbiAgICAgIC50aGVuKHRoaXMuX3BhcnNlRG9tYWluKTtcbiAgfVxuXG4gIGNyZWF0ZShkYXRhOiBhbnkpIHtcbiAgICByZXR1cm4gdGhpcy5yZXF1ZXN0LnBvc3QoJy92Mi9kb21haW5zJywgZGF0YSlcbiAgICAgIC50aGVuKHRoaXMuX3BhcnNlRG9tYWluKTtcbiAgfVxuXG4gIGRlc3Ryb3koZG9tYWluOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gdGhpcy5yZXF1ZXN0LmRlbGV0ZShgL3YyL2RvbWFpbnMvJHtkb21haW59YClcbiAgICAgIC50aGVuKHRoaXMuX3BhcnNlTWVzc2FnZSk7XG4gIH1cblxuICAvLyBUcmFja2luZ1xuXG4gIGdldFRyYWNraW5nKGRvbWFpbjogc3RyaW5nKSB7XG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdC5nZXQodXJsam9pbignL3YyL2RvbWFpbnMnLCBkb21haW4sICd0cmFja2luZycpKVxuICAgICAgLnRoZW4odGhpcy5fcGFyc2VUcmFja2luZ1NldHRpbmdzKTtcbiAgfVxuXG4gIHVwZGF0ZVRyYWNraW5nKGRvbWFpbjogc3RyaW5nLCB0eXBlOiBzdHJpbmcsIGRhdGE6IGFueSkge1xuICAgIHJldHVybiB0aGlzLnJlcXVlc3QucHV0KHVybGpvaW4oJy92Mi9kb21haW5zJywgZG9tYWluLCAndHJhY2tpbmcnLCB0eXBlKSwgZGF0YSlcbiAgICAgIC50aGVuKHRoaXMuX3BhcnNlVHJhY2tpbmdVcGRhdGUpO1xuICB9XG5cbiAgLy8gSVBzXG5cbiAgZ2V0SXBzKGRvbWFpbjogc3RyaW5nKSB7XG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdC5nZXQodXJsam9pbignL3YyL2RvbWFpbnMnLCBkb21haW4sICdpcHMnKSlcbiAgICAgIC50aGVuKChyZXNwb25zZTogeyBib2R5OiB7IGl0ZW1zOiBzdHJpbmdbXSB9IH0pID0+IHJlc3BvbnNlPy5ib2R5Py5pdGVtcyk7XG4gIH1cblxuICBhc3NpZ25JcChkb21haW46IHN0cmluZywgaXA6IHN0cmluZykge1xuICAgIHJldHVybiB0aGlzLnJlcXVlc3QucG9zdCh1cmxqb2luKCcvdjIvZG9tYWlucycsIGRvbWFpbiwgJ2lwcycpLCB7IGlwIH0pO1xuICB9XG5cbiAgZGVsZXRlSXAoZG9tYWluOiBzdHJpbmcsIGlwOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gdGhpcy5yZXF1ZXN0LmRlbGV0ZSh1cmxqb2luKCcvdjIvZG9tYWlucycsIGRvbWFpbiwgJ2lwcycsIGlwKSk7XG4gIH1cblxuICBsaW5rSXBQb29sKGRvbWFpbjogc3RyaW5nLCBwb29sX2lkOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gdGhpcy5yZXF1ZXN0LnBvc3QodXJsam9pbignL3YyL2RvbWFpbnMnLCBkb21haW4sICdpcHMnKSwgeyBwb29sX2lkIH0pO1xuICB9XG5cbiAgdW5saW5rSXBQb2xsKGRvbWFpbjogc3RyaW5nLCBwb29sX2lkOiBzdHJpbmcsIGlwOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gdGhpcy5yZXF1ZXN0LmRlbGV0ZSh1cmxqb2luKCcvdjIvZG9tYWlucycsIGRvbWFpbiwgJ2lwcycsICdpcF9wb29sJyksIHsgcG9vbF9pZCwgaXAgfSk7XG4gIH1cbn1cbiIsImltcG9ydCBBUElFcnJvck9wdGlvbnMgZnJvbSAnLi9pbnRlcmZhY2VzL0FQSUVycm9yT3B0aW9ucyc7XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEFQSUVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICBzdGF0dXM6IG51bWJlciB8IHN0cmluZztcbiAgc3RhY2s6IHN0cmluZztcbiAgZGV0YWlsczogc3RyaW5nO1xuXG4gIGNvbnN0cnVjdG9yKHtcbiAgICBzdGF0dXMsXG4gICAgc3RhdHVzVGV4dCxcbiAgICBtZXNzYWdlLFxuICAgIGJvZHkgPSB7fVxuICB9OiBBUElFcnJvck9wdGlvbnMpIHtcbiAgICBjb25zdCB7IG1lc3NhZ2U6IGJvZHlNZXNzYWdlLCBlcnJvciB9ID0gYm9keTtcbiAgICBzdXBlcigpO1xuXG4gICAgdGhpcy5zdGFjayA9IG51bGw7XG4gICAgdGhpcy5zdGF0dXMgPSBzdGF0dXM7XG4gICAgdGhpcy5tZXNzYWdlID0gbWVzc2FnZSB8fCBlcnJvciB8fCBzdGF0dXNUZXh0O1xuICAgIHRoaXMuZGV0YWlscyA9IGJvZHlNZXNzYWdlO1xuICB9XG59XG4iLCJjb25zdCB1cmxqb2luID0gcmVxdWlyZSgndXJsLWpvaW4nKTtcblxuY29uc3QgTWdSZXF1ZXN0ID0gcmVxdWlyZSgnLi9yZXF1ZXN0Jyk7XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEV2ZW50Q2xpZW50IHtcbiAgcmVxdWVzdDogdHlwZW9mIE1nUmVxdWVzdDtcblxuICBjb25zdHJ1Y3RvcihyZXF1ZXN0OiB0eXBlb2YgTWdSZXF1ZXN0KSB7XG4gICAgdGhpcy5yZXF1ZXN0ID0gcmVxdWVzdDtcbiAgfVxuXG4gIF9wYXJzZVBhZ2VOdW1iZXIodXJsOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gdXJsLnNwbGl0KCcvJykucG9wKCk7XG4gIH1cblxuICBfcGFyc2VQYWdlKGlkOiBzdHJpbmcsIHVybDogc3RyaW5nKSB7XG4gICAgcmV0dXJuIHsgaWQsIG51bWJlcjogdGhpcy5fcGFyc2VQYWdlTnVtYmVyKHVybCksIHVybCB9O1xuICB9XG5cbiAgX3BhcnNlUGFnZUxpbmtzKHJlc3BvbnNlOiB7IGJvZHk6IHsgcGFnaW5nOiBhbnkgfSB9KSB7XG4gICAgY29uc3QgcGFnZXMgPSBPYmplY3QuZW50cmllcyhyZXNwb25zZS5ib2R5LnBhZ2luZyk7XG4gICAgcmV0dXJuIHBhZ2VzLnJlZHVjZShcbiAgICAgIChhY2M6IGFueSwgW2lkLCB1cmxdOiBbdXJsOiBzdHJpbmcsIGlkOiBzdHJpbmddKSA9PiB7XG4gICAgICAgIGFjY1tpZF0gPSB0aGlzLl9wYXJzZVBhZ2UoaWQsIHVybClcbiAgICAgICAgcmV0dXJuIGFjY1xuICAgICAgfSwge30pO1xuICB9XG5cbiAgX3BhcnNlRXZlbnRMaXN0KHJlc3BvbnNlOiB7IGJvZHk6IHsgaXRlbXM6IGFueSwgcGFnaW5nOiBhbnkgfSAgfSkge1xuICAgIHJldHVybiB7XG4gICAgICBpdGVtczogcmVzcG9uc2UuYm9keS5pdGVtcyxcbiAgICAgIHBhZ2VzOiB0aGlzLl9wYXJzZVBhZ2VMaW5rcyhyZXNwb25zZSlcbiAgICB9O1xuICB9XG5cbiAgZ2V0KGRvbWFpbjogc3RyaW5nLCBxdWVyeTogeyBwYWdlOiBhbnkgfSkge1xuICAgIGxldCB1cmw7XG5cbiAgICBpZiAocXVlcnkgJiYgcXVlcnkucGFnZSkge1xuICAgICAgdXJsID0gdXJsam9pbignL3YyJywgZG9tYWluLCAnZXZlbnRzJywgcXVlcnkucGFnZSk7XG4gICAgICBkZWxldGUgcXVlcnkucGFnZTtcbiAgICB9IGVsc2Uge1xuICAgICAgdXJsID0gdXJsam9pbignL3YyJywgZG9tYWluLCAnZXZlbnRzJyk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdC5nZXQodXJsLCBxdWVyeSlcbiAgICAgIC50aGVuKChyZXNwb25zZTogeyBib2R5OiB7IGl0ZW1zOiBhbnksIHBhZ2luZzogYW55IH0gfSkgPT4gdGhpcy5fcGFyc2VFdmVudExpc3QocmVzcG9uc2UpKTtcbiAgfVxufVxuIiwiY29uc3QgTWdSZXF1ZXN0ID0gcmVxdWlyZSgnLi9yZXF1ZXN0Jyk7XG5cbmltcG9ydCB7IElwUG9vbCB9IGZyb20gXCIuL2ludGVyZmFjZXMvSXBQb29sc1wiO1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBJcFBvb2xzQ2xpZW50IHtcbiAgcmVxdWVzdDogdHlwZW9mIE1nUmVxdWVzdDtcblxuICBjb25zdHJ1Y3RvcihyZXF1ZXN0OiB0eXBlb2YgTWdSZXF1ZXN0KSB7XG4gICAgdGhpcy5yZXF1ZXN0ID0gcmVxdWVzdDtcbiAgfVxuXG4gIGxpc3QocXVlcnk6IGFueSk6IElwUG9vbFtdIHtcbiAgICByZXR1cm4gdGhpcy5yZXF1ZXN0LmdldCgnL3YxL2lwX3Bvb2xzJywgcXVlcnkpXG4gICAgICAudGhlbigocmVzcG9uc2U6IHsgYm9keTogeyBpcF9wb29sczogSXBQb29sLCBtZXNzYWdlOiBzdHJpbmcgfSB9KSA9PiB0aGlzLnBhcnNlSXBQb29sc1Jlc3BvbnNlKHJlc3BvbnNlKSk7XG4gIH1cblxuICBjcmVhdGUoZGF0YTogeyBuYW1lOiBzdHJpbmcsIGRlc2NyaXB0aW9uPzogc3RyaW5nLCBpcHM/OiBzdHJpbmdbXSB9KSB7XG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdC5wb3N0KCcvdjEvaXBfcG9vbHMnLCBkYXRhKVxuICAgICAgLnRoZW4oKHJlc3BvbnNlOiB7IGJvZHk6IHsgbWVzc2FnZTogc3RyaW5nLCBwb29sX2lkOiBzdHJpbmcgfSB9KSA9PiByZXNwb25zZT8uYm9keSk7XG4gIH1cblxuICB1cGRhdGUocG9vbElkOiBzdHJpbmcsIGRhdGE6IHsgbmFtZTogc3RyaW5nLCBkZXNjcmlwdGlvbjogc3RyaW5nLCBhZGRfaXA6IHN0cmluZywgcmVtb3ZlX2lwOiBzdHJpbmcgfSkge1xuICAgIHJldHVybiB0aGlzLnJlcXVlc3QucGF0Y2goYC92MS9pcF9wb29scy8ke3Bvb2xJZH1gLCBkYXRhKVxuICAgICAgLnRoZW4oKHJlc3BvbnNlOiB7IGJvZHk6IGFueSB9KSA9PiByZXNwb25zZT8uYm9keSk7XG4gIH1cblxuICBkZWxldGUocG9vbElkOiBzdHJpbmcsIGRhdGE6IHsgaWQ6IHN0cmluZywgcG9vbF9pZDogc3RyaW5nIH0pIHtcbiAgICByZXR1cm4gdGhpcy5yZXF1ZXN0LmRlbGV0ZShgL3YxL2lwX3Bvb2xzLyR7cG9vbElkfWAsIGRhdGEpXG4gICAgICAudGhlbigocmVzcG9uc2U6IHsgYm9keTogYW55IH0pID0+IHJlc3BvbnNlPy5ib2R5KTtcbiAgfVxuXG4gIHByaXZhdGUgcGFyc2VJcFBvb2xzUmVzcG9uc2UocmVzcG9uc2U6IHsgYm9keTogYW55IHwgYW55IH0pIHtcbiAgICByZXR1cm4gcmVzcG9uc2UuYm9keS5pcF9wb29scztcbiAgfVxufVxuIiwiY29uc3QgTWdSZXF1ZXN0ID0gcmVxdWlyZSgnLi9yZXF1ZXN0Jyk7XG5pbXBvcnQgeyBJcERhdGEsIElwc0xpc3RSZXNwb25zZUJvZHkgfSBmcm9tICcuL2ludGVyZmFjZXMvSXBzJztcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgSXBzQ2xpZW50IHtcbiAgcmVxdWVzdDogdHlwZW9mIE1nUmVxdWVzdDtcblxuICBjb25zdHJ1Y3RvcihyZXF1ZXN0OiB0eXBlb2YgTWdSZXF1ZXN0KSB7XG4gICAgdGhpcy5yZXF1ZXN0ID0gcmVxdWVzdDtcbiAgfVxuXG4gIGxpc3QocXVlcnk6IGFueSkge1xuICAgIHJldHVybiB0aGlzLnJlcXVlc3QuZ2V0KCcvdjMvaXBzJywgcXVlcnkpXG4gICAgICAudGhlbigocmVzcG9uc2U6IHsgYm9keTogSXBzTGlzdFJlc3BvbnNlQm9keSB9KSA9PiB0aGlzLnBhcnNlSXBzUmVzcG9uc2UocmVzcG9uc2UpKTtcbiAgfVxuXG4gIGdldChpcDogc3RyaW5nKSB7XG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdC5nZXQoYC92My9pcHMvJHtpcH1gKVxuICAgICAgLnRoZW4oKHJlc3BvbnNlOiB7IGJvZHk6IElwRGF0YSB9KSA9PiB0aGlzLnBhcnNlSXBzUmVzcG9uc2UocmVzcG9uc2UpKTtcbiAgfVxuXG4gIHByaXZhdGUgcGFyc2VJcHNSZXNwb25zZShyZXNwb25zZTogeyBib2R5OiBJcHNMaXN0UmVzcG9uc2VCb2R5IHwgSXBEYXRhIH0pIHtcbiAgICByZXR1cm4gcmVzcG9uc2UuYm9keTtcbiAgfVxufVxuIiwiaW1wb3J0IFJlcXVlc3QgZnJvbSBcIi4vcmVxdWVzdFwiO1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBNZXNzYWdlc0NsaWVudCB7XG4gIHJlcXVlc3Q6IFJlcXVlc3Q7XG5cbiAgY29uc3RydWN0b3IocmVxdWVzdDogUmVxdWVzdCkge1xuICAgIHRoaXMucmVxdWVzdCA9IHJlcXVlc3Q7XG4gIH1cblxuICBfcGFyc2VSZXNwb25zZShyZXNwb25zZTogeyBib2R5OiBhbnkgfSkge1xuICAgIGlmIChyZXNwb25zZS5ib2R5KSB7XG4gICAgICByZXR1cm4gcmVzcG9uc2UuYm9keTtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzcG9uc2U7XG4gIH1cblxuICBjcmVhdGUoZG9tYWluOiBzdHJpbmcsIGRhdGE6IGFueSkge1xuICAgIGlmIChkYXRhLm1lc3NhZ2UpIHtcbiAgICAgIHJldHVybiB0aGlzLnJlcXVlc3QucG9zdE11bHRpKGAvdjMvJHtkb21haW59L21lc3NhZ2VzLm1pbWVgLCBkYXRhKVxuICAgICAgLnRoZW4odGhpcy5fcGFyc2VSZXNwb25zZSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdC5wb3N0TXVsdGkoYC92My8ke2RvbWFpbn0vbWVzc2FnZXNgLCBkYXRhKVxuICAgICAgLnRoZW4odGhpcy5fcGFyc2VSZXNwb25zZSk7XG4gIH1cbn1cbiIsImltcG9ydCBSZXF1ZXN0IGZyb20gJy4vcmVxdWVzdCc7XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFBhcnNlQ2xpZW50IHtcbiAgcmVxdWVzdDogUmVxdWVzdDtcblxuICBjb25zdHJ1Y3RvcihyZXF1ZXN0OiBSZXF1ZXN0KSB7XG4gICAgdGhpcy5yZXF1ZXN0ID0gcmVxdWVzdDtcbiAgfVxuXG4gIGdldChhZGRyZXNzZXM6IHN0cmluZ1tdIHwgc3RyaW5nLCBlbmFibGVEbnNFc3BDaGVja3M6IGJvb2xlYW4pIHtcbiAgICBjb25zdCBxdWVyeSA9IHt9IGFzIHsgYWRkcmVzc2VzOiBzdHJpbmcsIHN5bnRheF9vbmx5OiBib29sZWFuIH07XG5cbiAgICBpZiAoQXJyYXkuaXNBcnJheShhZGRyZXNzZXMpKSB7XG4gICAgICBhZGRyZXNzZXMgPSBhZGRyZXNzZXMuam9pbignLCcpO1xuICAgIH1cblxuICAgIHF1ZXJ5LmFkZHJlc3NlcyA9IGFkZHJlc3NlcztcblxuICAgIGlmIChlbmFibGVEbnNFc3BDaGVja3MpIHtcbiAgICAgIHF1ZXJ5LnN5bnRheF9vbmx5ID0gZmFsc2U7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdC5nZXQoJy92My9hZGRyZXNzL3BhcnNlJywgcXVlcnkpXG4gICAgICAudGhlbigocmVzcG9uc2UpID0+IHJlc3BvbnNlLmJvZHkpO1xuICB9XG59XG4iLCJcbmltcG9ydCBCdG9hIGZyb20gJ2J0b2EnO1xuaW1wb3J0IHVybGpvaW4gZnJvbSAndXJsLWpvaW4nO1xuaW1wb3J0IGt5IGZyb20gJ2t5LXVuaXZlcnNhbCc7XG5cbmltcG9ydCBBUElFcnJvciBmcm9tICcuL2Vycm9yJztcbmltcG9ydCBSZXF1ZXN0T3B0aW9ucyBmcm9tICcuL2ludGVyZmFjZXMvUmVxdWVzdE9wdGlvbnMnO1xuaW1wb3J0IEFQSUVycm9yT3B0aW9ucyBmcm9tICcuL2ludGVyZmFjZXMvQVBJRXJyb3JPcHRpb25zJztcblxuY29uc3QgaXNTdHJlYW0gPSAoYXR0YWNobWVudDogYW55KSA9PiB0eXBlb2YgYXR0YWNobWVudCA9PT0gJ29iamVjdCcgJiYgdHlwZW9mIGF0dGFjaG1lbnQucGlwZSA9PT0gJ2Z1bmN0aW9uJztcblxuY29uc3QgZ2V0QXR0YWNobWVudE9wdGlvbnMgPSAoaXRlbTogYW55KTogeyBmaWxlbmFtZT86IHN0cmluZywgY29udGVudFR5cGU/OiBzdHJpbmcsIGtub3duTGVuZ3RoPzogbnVtYmVyIH0gPT4ge1xuICBpZiAodHlwZW9mIGl0ZW0gIT09ICdvYmplY3QnIHx8IGlzU3RyZWFtKGl0ZW0pKSByZXR1cm4ge307XG5cbiAgY29uc3Qge1xuICAgIGZpbGVuYW1lLFxuICAgIGNvbnRlbnRUeXBlLFxuICAgIGtub3duTGVuZ3RoXG4gIH0gPSBpdGVtO1xuXG4gIHJldHVybiB7XG4gICAgLi4uKGZpbGVuYW1lID8geyBmaWxlbmFtZSB9IDogeyBmaWxlbmFtZTogJ2ZpbGUnIH0pLFxuICAgIC4uLihjb250ZW50VHlwZSAmJiB7IGNvbnRlbnRUeXBlIH0pLFxuICAgIC4uLihrbm93bkxlbmd0aCAmJiB7IGtub3duTGVuZ3RoIH0pXG4gIH07XG59XG5cbmNvbnN0IHN0cmVhbVRvU3RyaW5nID0gKHN0cmVhbTogYW55KSA9PiB7XG4gIGNvbnN0IGNodW5rczogYW55ID0gW11cbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICBzdHJlYW0ub24oJ2RhdGEnLCAoY2h1bms6IGFueSkgPT4gY2h1bmtzLnB1c2goY2h1bmspKVxuICAgIHN0cmVhbS5vbignZXJyb3InLCByZWplY3QpXG4gICAgc3RyZWFtLm9uKCdlbmQnLCAoKSA9PiByZXNvbHZlKEJ1ZmZlci5jb25jYXQoY2h1bmtzKS50b1N0cmluZygndXRmOCcpKSlcbiAgfSlcbn1cblxuY2xhc3MgUmVxdWVzdCB7XG4gIHByaXZhdGUgdXNlcm5hbWU7XG4gIHByaXZhdGUga2V5O1xuICBwcml2YXRlIHVybDtcbiAgcHJpdmF0ZSBoZWFkZXJzOiBhbnk7XG4gIHByaXZhdGUgZm9ybURhdGE6IG5ldyAoKSA9PiBGb3JtRGF0YTtcblxuICBjb25zdHJ1Y3RvcihvcHRpb25zOiBSZXF1ZXN0T3B0aW9ucywgZm9ybURhdGE6IG5ldyAoKSA9PiBGb3JtRGF0YSkge1xuICAgIHRoaXMudXNlcm5hbWUgPSBvcHRpb25zLnVzZXJuYW1lO1xuICAgIHRoaXMua2V5ID0gb3B0aW9ucy5rZXk7XG4gICAgdGhpcy51cmwgPSBvcHRpb25zLnVybDtcbiAgICB0aGlzLmhlYWRlcnMgPSBvcHRpb25zLmhlYWRlcnMgfHwge307XG4gICAgdGhpcy5mb3JtRGF0YSA9IGZvcm1EYXRhO1xuICB9XG5cbiAgYXN5bmMgcmVxdWVzdChtZXRob2Q6IHN0cmluZywgdXJsOiBzdHJpbmcsIG9wdGlvbnM/OiBhbnkpIHtcbiAgICBjb25zdCBiYXNpYyA9IEJ0b2EoYCR7dGhpcy51c2VybmFtZX06JHt0aGlzLmtleX1gKTtcbiAgICBjb25zdCBoZWFkZXJzID0ge1xuICAgICAgQXV0aG9yaXphdGlvbjogYEJhc2ljICR7YmFzaWN9YCxcbiAgICAgIC4uLnRoaXMuaGVhZGVycyxcbiAgICAgIC4uLm9wdGlvbnM/LmhlYWRlcnNcbiAgICB9O1xuXG4gICAgZGVsZXRlIG9wdGlvbnM/LmhlYWRlcnM7XG5cbiAgICBpZiAoIWhlYWRlcnNbJ0NvbnRlbnQtVHlwZSddKSB7XG4gICAgICAvLyBmb3IgZm9ybS1kYXRhIGl0IHdpbGwgYmUgTnVsbCBzbyB3ZSBuZWVkIHRvIHJlbW92ZSBpdFxuICAgICAgZGVsZXRlIGhlYWRlcnNbJ0NvbnRlbnQtVHlwZSddO1xuICAgIH1cblxuICAgIGNvbnN0IHBhcmFtcyA9IHsgLi4ub3B0aW9ucyB9O1xuXG4gICAgaWYgKG9wdGlvbnM/LnF1ZXJ5ICYmIE9iamVjdC5nZXRPd25Qcm9wZXJ0eU5hbWVzKG9wdGlvbnM/LnF1ZXJ5KS5sZW5ndGggPiAwKSB7XG4gICAgICBwYXJhbXMuc2VhcmNoUGFyYW1zID0gb3B0aW9ucy5xdWVyeTtcbiAgICAgIGRlbGV0ZSBwYXJhbXMucXVlcnlcbiAgICB9XG5cbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGt5KFxuICAgICAgdXJsam9pbih0aGlzLnVybCwgdXJsKSxcbiAgICAgIHtcbiAgICAgICAgbWV0aG9kOiBtZXRob2QudG9Mb2NhbGVVcHBlckNhc2UoKSxcbiAgICAgICAgaGVhZGVycyxcbiAgICAgICAgdGhyb3dIdHRwRXJyb3JzOiBmYWxzZSxcbiAgICAgICAgLi4ucGFyYW1zXG4gICAgICB9XG4gICAgKTtcblxuICAgIGlmICghcmVzcG9uc2U/Lm9rKSB7XG4gICAgICBjb25zdCBtZXNzYWdlID0gcmVzcG9uc2U/LmJvZHkgJiYgaXNTdHJlYW0ocmVzcG9uc2UuYm9keSlcbiAgICAgICAgPyBhd2FpdCBzdHJlYW1Ub1N0cmluZyhyZXNwb25zZS5ib2R5KVxuICAgICAgICA6IGF3YWl0IHJlc3BvbnNlPy5qc29uKCk7XG5cbiAgICAgIHRocm93IG5ldyBBUElFcnJvcih7XG4gICAgICAgIHN0YXR1czogcmVzcG9uc2U/LnN0YXR1cyxcbiAgICAgICAgc3RhdHVzVGV4dDogcmVzcG9uc2U/LnN0YXR1c1RleHQsXG4gICAgICAgIGJvZHk6IHsgbWVzc2FnZSB9XG4gICAgICB9IGFzIEFQSUVycm9yT3B0aW9ucyk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGJvZHk6IGF3YWl0IHJlc3BvbnNlPy5qc29uKCksXG4gICAgICBzdGF0dXM6IHJlc3BvbnNlPy5zdGF0dXNcbiAgICB9O1xuICB9XG5cbiAgcXVlcnkobWV0aG9kOiBzdHJpbmcsIHVybDogc3RyaW5nLCBxdWVyeTogYW55LCBvcHRpb25zPzogYW55KSB7XG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdChtZXRob2QsIHVybCwgeyBxdWVyeSwgLi4ub3B0aW9ucyB9KTtcbiAgfVxuXG4gIGNvbW1hbmQobWV0aG9kOiBzdHJpbmcsIHVybDogc3RyaW5nLCBkYXRhOiBhbnksIG9wdGlvbnM/OiBhbnkpIHtcbiAgICByZXR1cm4gdGhpcy5yZXF1ZXN0KG1ldGhvZCwgdXJsLCB7XG4gICAgICBoZWFkZXJzOiB7ICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJyB9LFxuICAgICAgYm9keTogZGF0YSxcbiAgICAgIC4uLm9wdGlvbnNcbiAgICB9KTtcbiAgfVxuXG4gIGdldCh1cmw6IHN0cmluZywgcXVlcnk/OiBhbnksIG9wdGlvbnM/OiBhbnkpIHtcbiAgICByZXR1cm4gdGhpcy5xdWVyeSgnZ2V0JywgdXJsLCBxdWVyeSwgb3B0aW9ucyk7XG4gIH1cblxuICBoZWFkKHVybDogc3RyaW5nLCBxdWVyeTogYW55LCBvcHRpb25zOiBhbnkpIHtcbiAgICByZXR1cm4gdGhpcy5xdWVyeSgnaGVhZCcsIHVybCwgcXVlcnksIG9wdGlvbnMpO1xuICB9XG5cbiAgb3B0aW9ucyh1cmw6IHN0cmluZywgcXVlcnk6IGFueSwgb3B0aW9uczogYW55KSB7XG4gICAgcmV0dXJuIHRoaXMucXVlcnkoJ29wdGlvbnMnLCB1cmwsIHF1ZXJ5LCBvcHRpb25zKTtcbiAgfVxuXG4gIHBvc3QodXJsOiBzdHJpbmcsIGRhdGE6IGFueSwgb3B0aW9ucz86IGFueSkge1xuICAgIHJldHVybiB0aGlzLmNvbW1hbmQoJ3Bvc3QnLCB1cmwsIGRhdGEsIG9wdGlvbnMpO1xuICB9XG5cbiAgcG9zdE11bHRpKHVybDogc3RyaW5nLCBkYXRhOiBhbnkpIHtcblxuICAgIGNvbnN0IGZvcm1EYXRhOiBGb3JtRGF0YSA9IG5ldyB0aGlzLmZvcm1EYXRhKCk7XG4gICAgY29uc3QgcGFyYW1zOiBhbnkgPSB7XG4gICAgICBoZWFkZXJzOiB7ICdDb250ZW50LVR5cGUnOiBudWxsIH1cbiAgICB9O1xuXG4gICAgT2JqZWN0LmtleXMoZGF0YSlcbiAgICAgIC5maWx0ZXIoZnVuY3Rpb24gKGtleSkgeyByZXR1cm4gZGF0YVtrZXldOyB9KVxuICAgICAgLmZvckVhY2goZnVuY3Rpb24gKGtleSkge1xuICAgICAgICBpZiAoa2V5ID09PSAnYXR0YWNobWVudCcpIHtcbiAgICAgICAgICBjb25zdCBvYmogPSBkYXRhLmF0dGFjaG1lbnQ7XG5cbiAgICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShvYmopKSB7XG4gICAgICAgICAgICBvYmouZm9yRWFjaChmdW5jdGlvbiAoaXRlbSkge1xuICAgICAgICAgICAgICBjb25zdCBkYXRhID0gaXRlbS5kYXRhID8gaXRlbS5kYXRhIDogaXRlbTtcbiAgICAgICAgICAgICAgY29uc3Qgb3B0aW9ucyA9IGdldEF0dGFjaG1lbnRPcHRpb25zKGl0ZW0pO1xuICAgICAgICAgICAgICAoZm9ybURhdGEgYXMgYW55KS5hcHBlbmQoa2V5LCBkYXRhLCBvcHRpb25zKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zdCBkYXRhID0gaXNTdHJlYW0ob2JqKSA/IG9iaiA6IG9iai5kYXRhO1xuICAgICAgICAgICAgY29uc3Qgb3B0aW9ucyA9IGdldEF0dGFjaG1lbnRPcHRpb25zKG9iaik7XG4gICAgICAgICAgICAoZm9ybURhdGEgYXMgYW55KS5hcHBlbmQoa2V5LCBkYXRhLCBvcHRpb25zKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShkYXRhW2tleV0pKSB7XG4gICAgICAgICAgZGF0YVtrZXldLmZvckVhY2goZnVuY3Rpb24gKGl0ZW06IGFueSkge1xuICAgICAgICAgICAgZm9ybURhdGEuYXBwZW5kKGtleSwgaXRlbSk7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZm9ybURhdGEuYXBwZW5kKGtleSwgZGF0YVtrZXldKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG5cbiAgICByZXR1cm4gdGhpcy5jb21tYW5kKCdwb3N0JywgdXJsLCBmb3JtRGF0YSwgcGFyYW1zKTtcbiAgfVxuXG4gIHB1dCh1cmw6IHN0cmluZywgZGF0YTogYW55LCBvcHRpb25zPzogYW55KSB7XG4gICAgcmV0dXJuIHRoaXMuY29tbWFuZCgncHV0JywgdXJsLCBkYXRhLCBvcHRpb25zKTtcbiAgfVxuXG4gIHBhdGNoKHVybDogc3RyaW5nLCBkYXRhOiBhbnksIG9wdGlvbnM/OiBhbnkpIHtcbiAgICByZXR1cm4gdGhpcy5jb21tYW5kKCdwYXRjaCcsIHVybCwgZGF0YSwgb3B0aW9ucyk7XG4gIH1cblxuICBkZWxldGUodXJsOiBzdHJpbmcsIGRhdGE/OiBhbnksIG9wdGlvbnM/OiBhbnkpIHtcbiAgICByZXR1cm4gdGhpcy5jb21tYW5kKCdkZWxldGUnLCB1cmwsIGRhdGEsIG9wdGlvbnMpO1xuICB9XG59XG5cbmV4cG9ydCBkZWZhdWx0IFJlcXVlc3QiLCJpbXBvcnQgUmVxdWVzdCBmcm9tICcuL3JlcXVlc3QnO1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBSb3V0ZXNDbGllbnQge1xuICByZXF1ZXN0OiBSZXF1ZXN0O1xuXG4gIGNvbnN0cnVjdG9yKHJlcXVlc3Q6IFJlcXVlc3QpIHtcbiAgICB0aGlzLnJlcXVlc3QgPSByZXF1ZXN0O1xuICB9XG5cbiAgbGlzdChxdWVyeTogYW55KSB7XG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdC5nZXQoJy92My9yb3V0ZXMnLCBxdWVyeSlcbiAgICAgIC50aGVuKChyZXNwb25zZSkgPT4gcmVzcG9uc2UuYm9keS5pdGVtcyk7XG4gIH1cblxuICBnZXQoaWQ6IHN0cmluZykge1xuICAgIHJldHVybiB0aGlzLnJlcXVlc3QuZ2V0KGAvdjMvcm91dGVzLyR7aWR9YClcbiAgICAgIC50aGVuKChyZXNwb25zZSkgPT4gcmVzcG9uc2UuYm9keS5yb3V0ZSk7XG4gIH1cblxuICBjcmVhdGUoZGF0YTogYW55KSB7XG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdC5wb3N0KCcvdjMvcm91dGVzJywgZGF0YSlcbiAgICAgIC50aGVuKChyZXNwb25zZSkgPT4gcmVzcG9uc2UuYm9keS5yb3V0ZSk7XG4gIH1cblxuICB1cGRhdGUoaWQ6IHN0cmluZywgZGF0YTogYW55KSB7XG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdC5wdXQoYC92My9yb3V0ZXMvJHtpZH1gLCBkYXRhKVxuICAgICAgLnRoZW4oKHJlc3BvbnNlKSA9PiByZXNwb25zZS5ib2R5KTtcbiAgfVxuXG4gIGRlc3Ryb3koaWQ6IHN0cmluZykge1xuICAgIHJldHVybiB0aGlzLnJlcXVlc3QuZGVsZXRlKGAvdjMvcm91dGVzLyR7aWR9YClcbiAgICAgIC50aGVuKChyZXNwb25zZSkgPT4gcmVzcG9uc2UuYm9keSk7XG4gIH1cbn1cbiIsImltcG9ydCB1cmxqb2luIGZyb20gJ3VybC1qb2luJztcbmltcG9ydCBSZXF1ZXN0IGZyb20gJy4vcmVxdWVzdCc7XG5pbXBvcnQgU3RhdHNPcHRpb25zIGZyb20gJy4vaW50ZXJmYWNlcy9TdGF0c09wdGlvbnMnO1xuXG5jbGFzcyBTdGF0cyB7XG4gIHN0YXJ0OiBEYXRlO1xuICBlbmQ6IERhdGU7XG4gIHJlc29sdXRpb246IHN0cmluZztcbiAgc3RhdHM6IGFueTtcblxuICBjb25zdHJ1Y3RvcihkYXRhOiBTdGF0c09wdGlvbnMpIHtcbiAgICB0aGlzLnN0YXJ0ID0gbmV3IERhdGUoZGF0YS5zdGFydCk7XG4gICAgdGhpcy5lbmQgPSBuZXcgRGF0ZShkYXRhLmVuZCk7XG4gICAgdGhpcy5yZXNvbHV0aW9uID0gZGF0YS5yZXNvbHV0aW9uO1xuICAgIHRoaXMuc3RhdHMgPSBkYXRhLnN0YXRzLm1hcChmdW5jdGlvbiAoc3RhdDogeyB0aW1lOiBzdHJpbmcgfCBEYXRlIH0pIHtcbiAgICAgIHN0YXQudGltZSA9IG5ldyBEYXRlKHN0YXQudGltZSk7XG4gICAgICByZXR1cm4gc3RhdDtcbiAgICB9KTtcbiAgfVxufVxuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBTdGF0c0NsaWVudCB7XG4gIHJlcXVlc3Q6IFJlcXVlc3Q7XG5cbiAgY29uc3RydWN0b3IocmVxdWVzdDogUmVxdWVzdCkge1xuICAgIHRoaXMucmVxdWVzdCA9IHJlcXVlc3Q7XG4gIH1cblxuICBfcGFyc2VTdGF0cyhyZXNwb25zZTogeyBib2R5OiBTdGF0c09wdGlvbnMgfSkge1xuICAgIHJldHVybiBuZXcgU3RhdHMocmVzcG9uc2UuYm9keSk7XG4gIH1cblxuICBnZXREb21haW4oZG9tYWluOiBzdHJpbmcsIHF1ZXJ5OiBhbnkpIHtcbiAgICByZXR1cm4gdGhpcy5yZXF1ZXN0LmdldCh1cmxqb2luKCcvdjMnLCBkb21haW4sICdzdGF0cy90b3RhbCcpLCBxdWVyeSlcbiAgICAgIC50aGVuKHRoaXMuX3BhcnNlU3RhdHMpO1xuICB9XG5cbiAgZ2V0QWNjb3VudChxdWVyeTogYW55KSB7XG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdC5nZXQoJy92My9zdGF0cy90b3RhbCcsIHF1ZXJ5KVxuICAgICAgLnRoZW4odGhpcy5fcGFyc2VTdGF0cyk7XG4gIH1cbn1cbiIsImltcG9ydCB1cmwgZnJvbSAndXJsJztcbmltcG9ydCB1cmxqb2luIGZyb20gJ3VybC1qb2luJztcblxuaW1wb3J0IFJlcXVlc3QgZnJvbSAnLi9yZXF1ZXN0JztcbmltcG9ydCB7IEJvdW5jZURhdGEsIENvbXBsYWludERhdGEsIFVuc3Vic2NyaWJlRGF0YSB9IGZyb20gJy4vaW50ZXJmYWNlcy9TdXByZXNzaW9ucyc7XG5cbnR5cGUgVE1vZGVsID0gdHlwZW9mIEJvdW5jZSB8IHR5cGVvZiBDb21wbGFpbnQgfCB0eXBlb2YgVW5zdWJzY3JpYmU7XG5cbmNvbnN0IGNyZWF0ZU9wdGlvbnMgPSB7XG4gIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyB9XG59O1xuXG5jbGFzcyBCb3VuY2Uge1xuICB0eXBlOiBzdHJpbmc7XG4gIGFkZHJlc3M6IHN0cmluZztcbiAgY29kZTogbnVtYmVyO1xuICBlcnJvcjogc3RyaW5nO1xuICBjcmVhdGVkX2F0OiBEYXRlO1xuXG4gIGNvbnN0cnVjdG9yKGRhdGE6IEJvdW5jZURhdGEpIHtcbiAgICB0aGlzLnR5cGUgPSAnYm91bmNlcyc7XG4gICAgdGhpcy5hZGRyZXNzID0gZGF0YS5hZGRyZXNzO1xuICAgIHRoaXMuY29kZSA9ICtkYXRhLmNvZGU7XG4gICAgdGhpcy5lcnJvciA9IGRhdGEuZXJyb3I7XG4gICAgdGhpcy5jcmVhdGVkX2F0ID0gbmV3IERhdGUoZGF0YS5jcmVhdGVkX2F0KTtcbiAgfVxufVxuXG5jbGFzcyBDb21wbGFpbnQge1xuICB0eXBlOiBzdHJpbmc7XG4gIGFkZHJlc3M6IGFueTtcbiAgY3JlYXRlZF9hdDogRGF0ZTtcblxuICBjb25zdHJ1Y3RvcihkYXRhOiBDb21wbGFpbnREYXRhKSB7XG4gICAgdGhpcy50eXBlID0gJ2NvbXBsYWludHMnO1xuICAgIHRoaXMuYWRkcmVzcyA9IGRhdGEuYWRkcmVzcztcbiAgICB0aGlzLmNyZWF0ZWRfYXQgPSBuZXcgRGF0ZShkYXRhLmNyZWF0ZWRfYXQpO1xuICB9XG59XG5cbmNsYXNzIFVuc3Vic2NyaWJlIHtcbiAgdHlwZTogc3RyaW5nO1xuICBhZGRyZXNzOiBzdHJpbmc7XG4gIHRhZ3M6IGFueTtcbiAgY3JlYXRlZF9hdDogRGF0ZTtcblxuICBjb25zdHJ1Y3RvcihkYXRhOiBVbnN1YnNjcmliZURhdGEpIHtcbiAgICB0aGlzLnR5cGUgPSAndW5zdWJzY3JpYmVzJztcbiAgICB0aGlzLmFkZHJlc3MgPSBkYXRhLmFkZHJlc3M7XG4gICAgdGhpcy50YWdzID0gZGF0YS50YWdzO1xuICAgIHRoaXMuY3JlYXRlZF9hdCA9IG5ldyBEYXRlKGRhdGEuY3JlYXRlZF9hdCk7XG4gIH1cbn1cblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgU3VwcHJlc3Npb25DbGllbnQge1xuICByZXF1ZXN0OiBhbnk7XG4gIG1vZGVsczoge1xuICAgIGJvdW5jZXM6IHR5cGVvZiBCb3VuY2U7XG4gICAgY29tcGxhaW50czogdHlwZW9mIENvbXBsYWludDtcbiAgICB1bnN1YnNjcmliZXM6IHR5cGVvZiBVbnN1YnNjcmliZTtcbiAgfTtcblxuICBjb25zdHJ1Y3RvcihyZXF1ZXN0OiBSZXF1ZXN0KSB7XG4gICAgdGhpcy5yZXF1ZXN0ID0gcmVxdWVzdDtcbiAgICB0aGlzLm1vZGVscyA9IHtcbiAgICAgIGJvdW5jZXM6IEJvdW5jZSxcbiAgICAgIGNvbXBsYWludHM6IENvbXBsYWludCxcbiAgICAgIHVuc3Vic2NyaWJlczogVW5zdWJzY3JpYmVcbiAgICB9O1xuICB9XG5cbiAgX3BhcnNlUGFnZShpZDogc3RyaW5nLCBwYWdlVXJsOiBzdHJpbmcpIHtcbiAgICBjb25zdCBwYXJzZWRVcmwgPSB1cmwucGFyc2UocGFnZVVybCwgdHJ1ZSk7XG4gICAgY29uc3QgeyBxdWVyeSB9ID0gcGFyc2VkVXJsO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIGlkLFxuICAgICAgcGFnZTogcXVlcnkucGFnZSxcbiAgICAgIGFkZHJlc3M6IHF1ZXJ5LmFkZHJlc3MsXG4gICAgICB1cmw6IHBhZ2VVcmxcbiAgICB9O1xuICB9XG5cbiAgX3BhcnNlUGFnZUxpbmtzKHJlc3BvbnNlOiB7IGJvZHk6IHsgcGFnaW5nOiBhbnkgfSB9KSB7XG4gICAgY29uc3QgcGFnZXMgPSBPYmplY3QuZW50cmllcyhyZXNwb25zZS5ib2R5LnBhZ2luZyk7XG4gICAgcmV0dXJuIHBhZ2VzLnJlZHVjZShcbiAgICAgIChhY2M6IGFueSwgW2lkLCB1cmxdOiBbdXJsOiBzdHJpbmcsIGlkOiBzdHJpbmddKSA9PiB7XG4gICAgICAgIGFjY1tpZF0gPSB0aGlzLl9wYXJzZVBhZ2UoaWQsIHVybClcbiAgICAgICAgcmV0dXJuIGFjY1xuICAgICAgfSwge30pO1xuICB9XG5cbiAgX3BhcnNlTGlzdChyZXNwb25zZTogeyBib2R5OiB7IGl0ZW1zOiBhbnksIHBhZ2luZzogYW55IH0gfSwgTW9kZWw6IFRNb2RlbCkge1xuICAgIGNvbnN0IGRhdGEgPSB7fSBhcyBhbnk7XG5cbiAgICBkYXRhLml0ZW1zID0gcmVzcG9uc2UuYm9keS5pdGVtcy5tYXAoKGQ6IGFueSkgPT4gbmV3IE1vZGVsKGQpKTtcblxuICAgIGRhdGEucGFnZXMgPSB0aGlzLl9wYXJzZVBhZ2VMaW5rcyhyZXNwb25zZSk7XG5cbiAgICByZXR1cm4gZGF0YTtcbiAgfVxuXG4gIF9wYXJzZUl0ZW0ocmVzcG9uc2U6IHsgYm9keTogYW55IH0sIE1vZGVsOiBUTW9kZWwpIHtcbiAgICByZXR1cm4gbmV3IE1vZGVsKHJlc3BvbnNlLmJvZHkpO1xuICB9XG5cbiAgbGlzdChkb21haW46IHN0cmluZywgdHlwZTogc3RyaW5nLCBxdWVyeTogYW55KSB7XG4gICAgY29uc3QgbW9kZWwgPSAodGhpcy5tb2RlbHMgYXMgYW55KVt0eXBlXTtcblxuICAgIHJldHVybiB0aGlzLnJlcXVlc3RcbiAgICAgIC5nZXQodXJsam9pbigndjMnLCBkb21haW4sIHR5cGUpLCBxdWVyeSlcbiAgICAgIC50aGVuKChyZXNwb25zZTogeyBib2R5OiB7IGl0ZW1zOiBhbnksIHBhZ2luZzogYW55IH0gfSkgPT4gdGhpcy5fcGFyc2VMaXN0KHJlc3BvbnNlLCBtb2RlbCkpO1xuICB9XG5cbiAgZ2V0KGRvbWFpbjogc3RyaW5nLCB0eXBlOiBzdHJpbmcsIGFkZHJlc3M6IHN0cmluZykge1xuICAgIGNvbnN0IG1vZGVsID0gKHRoaXMubW9kZWxzIGFzIGFueSlbdHlwZV07XG5cbiAgICByZXR1cm4gdGhpcy5yZXF1ZXN0XG4gICAgICAuZ2V0KHVybGpvaW4oJ3YzJywgZG9tYWluLCB0eXBlLCBlbmNvZGVVUklDb21wb25lbnQoYWRkcmVzcykpKVxuICAgICAgLnRoZW4oKHJlc3BvbnNlOiB7IGJvZHk6IGFueSB9KSA9PiB0aGlzLl9wYXJzZUl0ZW0ocmVzcG9uc2UsIG1vZGVsKSk7XG4gIH1cblxuICBjcmVhdGUoZG9tYWluOiBzdHJpbmcsIHR5cGU6IHN0cmluZywgZGF0YTogYW55KSB7XG4gICAgLy8gc3VwcG9ydHMgYWRkaW5nIG11bHRpcGxlIHN1cHByZXNzaW9ucyBieSBkZWZhdWx0XG4gICAgaWYgKCFBcnJheS5pc0FycmF5KGRhdGEpKSB7XG4gICAgICBkYXRhID0gW2RhdGFdO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLnJlcXVlc3RcbiAgICAucG9zdCh1cmxqb2luKCd2MycsIGRvbWFpbiwgdHlwZSksIGRhdGEsIGNyZWF0ZU9wdGlvbnMpXG4gICAgLnRoZW4oKHJlc3BvbnNlOiB7IGJvZHk6IGFueSB9KSA9PiByZXNwb25zZS5ib2R5KTtcbiAgfVxuXG4gIGRlc3Ryb3koZG9tYWluOiBzdHJpbmcsIHR5cGU6IHN0cmluZywgYWRkcmVzczogc3RyaW5nKSB7XG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdFxuICAgIC5kZWxldGUodXJsam9pbigndjMnLCBkb21haW4sIHR5cGUsIGVuY29kZVVSSUNvbXBvbmVudChhZGRyZXNzKSkpXG4gICAgLnRoZW4oKHJlc3BvbnNlOiB7IGJvZHk6IGFueSB9KSA9PiByZXNwb25zZS5ib2R5KTtcbiAgfVxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IFN1cHByZXNzaW9uQ2xpZW50O1xuIiwiXG5pbXBvcnQgUmVxdWVzdCBmcm9tICcuL3JlcXVlc3QnO1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBWYWxpZGF0ZUNsaWVudCB7XG4gIHJlcXVlc3Q6IFJlcXVlc3Q7XG5cbiAgY29uc3RydWN0b3IocmVxdWVzdDogUmVxdWVzdCkge1xuICAgIHRoaXMucmVxdWVzdCA9IHJlcXVlc3Q7XG4gIH1cblxuICBnZXQoYWRkcmVzczogc3RyaW5nKSB7XG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdC5nZXQoJy92My9hZGRyZXNzL3ZhbGlkYXRlJywgeyBhZGRyZXNzIH0pXG4gICAgICAudGhlbigocmVzcG9uc2UpID0+IHJlc3BvbnNlLmJvZHkpO1xuICB9XG59XG4iLCJpbXBvcnQgdXJsam9pbiBmcm9tICd1cmwtam9pbic7XG5pbXBvcnQgUmVxdWVzdCBmcm9tICcuL3JlcXVlc3QnO1xuXG5jbGFzcyBXZWJob29rIHtcbiAgaWQ6IHN0cmluZztcbiAgdXJsOiBzdHJpbmc7XG5cbiAgY29uc3RydWN0b3IoaWQ6IHN0cmluZywgZGF0YTogeyB1cmw6IHN0cmluZyB9KSB7XG4gICAgdGhpcy5pZCA9IGlkO1xuICAgIHRoaXMudXJsID0gZGF0YS51cmw7XG4gIH1cbn1cblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgV2ViaG9va0NsaWVudCB7XG4gIHJlcXVlc3Q6IGFueTtcblxuICBjb25zdHJ1Y3RvcihyZXF1ZXN0OiBSZXF1ZXN0KSB7XG4gICAgdGhpcy5yZXF1ZXN0ID0gcmVxdWVzdDtcbiAgfVxuXG4gIF9wYXJzZVdlYmhvb2tMaXN0KHJlc3BvbnNlOiB7IGJvZHk6IHsgd2ViaG9va3M6IGFueSB9IH0pIHtcbiAgICByZXR1cm4gcmVzcG9uc2UuYm9keS53ZWJob29rcztcbiAgfVxuXG4gIF9wYXJzZVdlYmhvb2tXaXRoSUQoaWQ6IHN0cmluZykge1xuICAgIHJldHVybiBmdW5jdGlvbiAocmVzcG9uc2U6IHsgYm9keTogeyB3ZWJob29rOiBhbnkgfSB9KSB7XG4gICAgICByZXR1cm4gbmV3IFdlYmhvb2soaWQsIHJlc3BvbnNlLmJvZHkud2ViaG9vayk7XG4gICAgfTtcbiAgfVxuXG4gIF9wYXJzZVdlYmhvb2tUZXN0KHJlc3BvbnNlOiB7IGJvZHk6IHsgY29kZTogbnVtYmVyLCBtZXNzYWdlOiBzdHJpbmcgfSB9KSB7XG4gICAgcmV0dXJuIHsgY29kZTogcmVzcG9uc2UuYm9keS5jb2RlLCBtZXNzYWdlOiByZXNwb25zZS5ib2R5Lm1lc3NhZ2UgfTtcbiAgfVxuXG4gIGxpc3QoZG9tYWluOiBzdHJpbmcsIHF1ZXJ5OiBhbnkpIHtcbiAgICByZXR1cm4gdGhpcy5yZXF1ZXN0LmdldCh1cmxqb2luKCcvdjIvZG9tYWlucycsIGRvbWFpbiwgJ3dlYmhvb2tzJyksIHF1ZXJ5KVxuICAgICAgLnRoZW4odGhpcy5fcGFyc2VXZWJob29rTGlzdCk7XG4gIH1cblxuICBnZXQoZG9tYWluOiBzdHJpbmcsIGlkOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gdGhpcy5yZXF1ZXN0LmdldCh1cmxqb2luKCcvdjIvZG9tYWlucycsIGRvbWFpbiwgJ3dlYmhvb2tzJywgaWQpKVxuICAgICAgLnRoZW4odGhpcy5fcGFyc2VXZWJob29rV2l0aElEKGlkKSk7XG4gIH1cblxuICBjcmVhdGUoZG9tYWluOiBzdHJpbmcsIGlkOiBzdHJpbmcsIHVybDogc3RyaW5nLCB0ZXN0OiBib29sZWFuKSB7XG4gICAgaWYgKHRlc3QpIHtcbiAgICAgIHJldHVybiB0aGlzLnJlcXVlc3QucHV0KHVybGpvaW4oJy92Mi9kb21haW5zJywgZG9tYWluLCAnd2ViaG9va3MnLCBpZCwgJ3Rlc3QnKSwgeyB1cmwgfSlcbiAgICAgICAgLnRoZW4odGhpcy5fcGFyc2VXZWJob29rVGVzdCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdC5wb3N0KHVybGpvaW4oJy92Mi9kb21haW5zJywgZG9tYWluLCAnd2ViaG9va3MnKSwgeyBpZCwgdXJsIH0pXG4gICAgICAudGhlbih0aGlzLl9wYXJzZVdlYmhvb2tXaXRoSUQoaWQpKTtcbiAgfVxuXG4gIHVwZGF0ZShkb21haW46IHN0cmluZywgaWQ6IHN0cmluZywgdXJsOiBzdHJpbmcsKSB7XG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdC5wdXQodXJsam9pbignL3YyL2RvbWFpbnMnLCBkb21haW4sICd3ZWJob29rcycsIGlkKSwgeyB1cmwgfSlcbiAgICAgIC50aGVuKHRoaXMuX3BhcnNlV2ViaG9va1dpdGhJRChpZCkpO1xuICB9XG5cbiAgZGVzdHJveShkb21haW46IHN0cmluZywgaWQ6IHN0cmluZykge1xuICAgIHJldHVybiB0aGlzLnJlcXVlc3QuZGVsZXRlKHVybGpvaW4oJy92Mi9kb21haW5zJywgZG9tYWluLCAnd2ViaG9va3MnLCBpZCkpXG4gICAgICAudGhlbih0aGlzLl9wYXJzZVdlYmhvb2tXaXRoSUQoaWQpKTtcbiAgfVxufVxuIiwiKGZ1bmN0aW9uICgpIHtcbiAgXCJ1c2Ugc3RyaWN0XCI7XG5cbiAgZnVuY3Rpb24gYnRvYShzdHIpIHtcbiAgICB2YXIgYnVmZmVyO1xuXG4gICAgaWYgKHN0ciBpbnN0YW5jZW9mIEJ1ZmZlcikge1xuICAgICAgYnVmZmVyID0gc3RyO1xuICAgIH0gZWxzZSB7XG4gICAgICBidWZmZXIgPSBCdWZmZXIuZnJvbShzdHIudG9TdHJpbmcoKSwgJ2JpbmFyeScpO1xuICAgIH1cblxuICAgIHJldHVybiBidWZmZXIudG9TdHJpbmcoJ2Jhc2U2NCcpO1xuICB9XG5cbiAgbW9kdWxlLmV4cG9ydHMgPSBidG9hO1xufSgpKTtcbiIsIlwidXNlIHN0cmljdFwiO1xuLyoqXG4gKiBSZXR1cm5zIGEgYEJ1ZmZlcmAgaW5zdGFuY2UgZnJvbSB0aGUgZ2l2ZW4gZGF0YSBVUkkgYHVyaWAuXG4gKlxuICogQHBhcmFtIHtTdHJpbmd9IHVyaSBEYXRhIFVSSSB0byB0dXJuIGludG8gYSBCdWZmZXIgaW5zdGFuY2VcbiAqIEByZXR1cm4ge0J1ZmZlcn0gQnVmZmVyIGluc3RhbmNlIGZyb20gRGF0YSBVUklcbiAqIEBhcGkgcHVibGljXG4gKi9cbmZ1bmN0aW9uIGRhdGFVcmlUb0J1ZmZlcih1cmkpIHtcbiAgICBpZiAoIS9eZGF0YTovaS50ZXN0KHVyaSkpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignYHVyaWAgZG9lcyBub3QgYXBwZWFyIHRvIGJlIGEgRGF0YSBVUkkgKG11c3QgYmVnaW4gd2l0aCBcImRhdGE6XCIpJyk7XG4gICAgfVxuICAgIC8vIHN0cmlwIG5ld2xpbmVzXG4gICAgdXJpID0gdXJpLnJlcGxhY2UoL1xccj9cXG4vZywgJycpO1xuICAgIC8vIHNwbGl0IHRoZSBVUkkgdXAgaW50byB0aGUgXCJtZXRhZGF0YVwiIGFuZCB0aGUgXCJkYXRhXCIgcG9ydGlvbnNcbiAgICBjb25zdCBmaXJzdENvbW1hID0gdXJpLmluZGV4T2YoJywnKTtcbiAgICBpZiAoZmlyc3RDb21tYSA9PT0gLTEgfHwgZmlyc3RDb21tYSA8PSA0KSB7XG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ21hbGZvcm1lZCBkYXRhOiBVUkknKTtcbiAgICB9XG4gICAgLy8gcmVtb3ZlIHRoZSBcImRhdGE6XCIgc2NoZW1lIGFuZCBwYXJzZSB0aGUgbWV0YWRhdGFcbiAgICBjb25zdCBtZXRhID0gdXJpLnN1YnN0cmluZyg1LCBmaXJzdENvbW1hKS5zcGxpdCgnOycpO1xuICAgIGxldCBjaGFyc2V0ID0gJyc7XG4gICAgbGV0IGJhc2U2NCA9IGZhbHNlO1xuICAgIGNvbnN0IHR5cGUgPSBtZXRhWzBdIHx8ICd0ZXh0L3BsYWluJztcbiAgICBsZXQgdHlwZUZ1bGwgPSB0eXBlO1xuICAgIGZvciAobGV0IGkgPSAxOyBpIDwgbWV0YS5sZW5ndGg7IGkrKykge1xuICAgICAgICBpZiAobWV0YVtpXSA9PT0gJ2Jhc2U2NCcpIHtcbiAgICAgICAgICAgIGJhc2U2NCA9IHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICB0eXBlRnVsbCArPSBgOyR7bWV0YVtpXX1gO1xuICAgICAgICAgICAgaWYgKG1ldGFbaV0uaW5kZXhPZignY2hhcnNldD0nKSA9PT0gMCkge1xuICAgICAgICAgICAgICAgIGNoYXJzZXQgPSBtZXRhW2ldLnN1YnN0cmluZyg4KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cbiAgICAvLyBkZWZhdWx0cyB0byBVUy1BU0NJSSBvbmx5IGlmIHR5cGUgaXMgbm90IHByb3ZpZGVkXG4gICAgaWYgKCFtZXRhWzBdICYmICFjaGFyc2V0Lmxlbmd0aCkge1xuICAgICAgICB0eXBlRnVsbCArPSAnO2NoYXJzZXQ9VVMtQVNDSUknO1xuICAgICAgICBjaGFyc2V0ID0gJ1VTLUFTQ0lJJztcbiAgICB9XG4gICAgLy8gZ2V0IHRoZSBlbmNvZGVkIGRhdGEgcG9ydGlvbiBhbmQgZGVjb2RlIFVSSS1lbmNvZGVkIGNoYXJzXG4gICAgY29uc3QgZW5jb2RpbmcgPSBiYXNlNjQgPyAnYmFzZTY0JyA6ICdhc2NpaSc7XG4gICAgY29uc3QgZGF0YSA9IHVuZXNjYXBlKHVyaS5zdWJzdHJpbmcoZmlyc3RDb21tYSArIDEpKTtcbiAgICBjb25zdCBidWZmZXIgPSBCdWZmZXIuZnJvbShkYXRhLCBlbmNvZGluZyk7XG4gICAgLy8gc2V0IGAudHlwZWAgYW5kIGAudHlwZUZ1bGxgIHByb3BlcnRpZXMgdG8gTUlNRSB0eXBlXG4gICAgYnVmZmVyLnR5cGUgPSB0eXBlO1xuICAgIGJ1ZmZlci50eXBlRnVsbCA9IHR5cGVGdWxsO1xuICAgIC8vIHNldCB0aGUgYC5jaGFyc2V0YCBwcm9wZXJ0eVxuICAgIGJ1ZmZlci5jaGFyc2V0ID0gY2hhcnNldDtcbiAgICByZXR1cm4gYnVmZmVyO1xufVxubW9kdWxlLmV4cG9ydHMgPSBkYXRhVXJpVG9CdWZmZXI7XG4vLyMgc291cmNlTWFwcGluZ1VSTD1pbmRleC5qcy5tYXAiLCIvKipcbiAqIEBhdXRob3IgVG9ydSBOYWdhc2hpbWEgPGh0dHBzOi8vZ2l0aHViLmNvbS9teXN0aWNhdGVhPlxuICogQGNvcHlyaWdodCAyMDE1IFRvcnUgTmFnYXNoaW1hLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGRpcmVjdG9yeSBmb3IgZnVsbCBsaWNlbnNlLlxuICovXG4ndXNlIHN0cmljdCc7XG5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG5cbi8qKlxuICogQHR5cGVkZWYge29iamVjdH0gUHJpdmF0ZURhdGFcbiAqIEBwcm9wZXJ0eSB7RXZlbnRUYXJnZXR9IGV2ZW50VGFyZ2V0IFRoZSBldmVudCB0YXJnZXQuXG4gKiBAcHJvcGVydHkge3t0eXBlOnN0cmluZ319IGV2ZW50IFRoZSBvcmlnaW5hbCBldmVudCBvYmplY3QuXG4gKiBAcHJvcGVydHkge251bWJlcn0gZXZlbnRQaGFzZSBUaGUgY3VycmVudCBldmVudCBwaGFzZS5cbiAqIEBwcm9wZXJ0eSB7RXZlbnRUYXJnZXR8bnVsbH0gY3VycmVudFRhcmdldCBUaGUgY3VycmVudCBldmVudCB0YXJnZXQuXG4gKiBAcHJvcGVydHkge2Jvb2xlYW59IGNhbmNlbGVkIFRoZSBmbGFnIHRvIHByZXZlbnQgZGVmYXVsdC5cbiAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gc3RvcHBlZCBUaGUgZmxhZyB0byBzdG9wIHByb3BhZ2F0aW9uLlxuICogQHByb3BlcnR5IHtib29sZWFufSBpbW1lZGlhdGVTdG9wcGVkIFRoZSBmbGFnIHRvIHN0b3AgcHJvcGFnYXRpb24gaW1tZWRpYXRlbHkuXG4gKiBAcHJvcGVydHkge0Z1bmN0aW9ufG51bGx9IHBhc3NpdmVMaXN0ZW5lciBUaGUgbGlzdGVuZXIgaWYgdGhlIGN1cnJlbnQgbGlzdGVuZXIgaXMgcGFzc2l2ZS4gT3RoZXJ3aXNlIHRoaXMgaXMgbnVsbC5cbiAqIEBwcm9wZXJ0eSB7bnVtYmVyfSB0aW1lU3RhbXAgVGhlIHVuaXggdGltZS5cbiAqIEBwcml2YXRlXG4gKi9cblxuLyoqXG4gKiBQcml2YXRlIGRhdGEgZm9yIGV2ZW50IHdyYXBwZXJzLlxuICogQHR5cGUge1dlYWtNYXA8RXZlbnQsIFByaXZhdGVEYXRhPn1cbiAqIEBwcml2YXRlXG4gKi9cbmNvbnN0IHByaXZhdGVEYXRhID0gbmV3IFdlYWtNYXAoKTtcblxuLyoqXG4gKiBDYWNoZSBmb3Igd3JhcHBlciBjbGFzc2VzLlxuICogQHR5cGUge1dlYWtNYXA8T2JqZWN0LCBGdW5jdGlvbj59XG4gKiBAcHJpdmF0ZVxuICovXG5jb25zdCB3cmFwcGVycyA9IG5ldyBXZWFrTWFwKCk7XG5cbi8qKlxuICogR2V0IHByaXZhdGUgZGF0YS5cbiAqIEBwYXJhbSB7RXZlbnR9IGV2ZW50IFRoZSBldmVudCBvYmplY3QgdG8gZ2V0IHByaXZhdGUgZGF0YS5cbiAqIEByZXR1cm5zIHtQcml2YXRlRGF0YX0gVGhlIHByaXZhdGUgZGF0YSBvZiB0aGUgZXZlbnQuXG4gKiBAcHJpdmF0ZVxuICovXG5mdW5jdGlvbiBwZChldmVudCkge1xuICAgIGNvbnN0IHJldHYgPSBwcml2YXRlRGF0YS5nZXQoZXZlbnQpO1xuICAgIGNvbnNvbGUuYXNzZXJ0KFxuICAgICAgICByZXR2ICE9IG51bGwsXG4gICAgICAgIFwiJ3RoaXMnIGlzIGV4cGVjdGVkIGFuIEV2ZW50IG9iamVjdCwgYnV0IGdvdFwiLFxuICAgICAgICBldmVudFxuICAgICk7XG4gICAgcmV0dXJuIHJldHZcbn1cblxuLyoqXG4gKiBodHRwczovL2RvbS5zcGVjLndoYXR3Zy5vcmcvI3NldC10aGUtY2FuY2VsZWQtZmxhZ1xuICogQHBhcmFtIGRhdGEge1ByaXZhdGVEYXRhfSBwcml2YXRlIGRhdGEuXG4gKi9cbmZ1bmN0aW9uIHNldENhbmNlbEZsYWcoZGF0YSkge1xuICAgIGlmIChkYXRhLnBhc3NpdmVMaXN0ZW5lciAhPSBudWxsKSB7XG4gICAgICAgIGlmIChcbiAgICAgICAgICAgIHR5cGVvZiBjb25zb2xlICE9PSBcInVuZGVmaW5lZFwiICYmXG4gICAgICAgICAgICB0eXBlb2YgY29uc29sZS5lcnJvciA9PT0gXCJmdW5jdGlvblwiXG4gICAgICAgICkge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcihcbiAgICAgICAgICAgICAgICBcIlVuYWJsZSB0byBwcmV2ZW50RGVmYXVsdCBpbnNpZGUgcGFzc2l2ZSBldmVudCBsaXN0ZW5lciBpbnZvY2F0aW9uLlwiLFxuICAgICAgICAgICAgICAgIGRhdGEucGFzc2l2ZUxpc3RlbmVyXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIHJldHVyblxuICAgIH1cbiAgICBpZiAoIWRhdGEuZXZlbnQuY2FuY2VsYWJsZSkge1xuICAgICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBkYXRhLmNhbmNlbGVkID0gdHJ1ZTtcbiAgICBpZiAodHlwZW9mIGRhdGEuZXZlbnQucHJldmVudERlZmF1bHQgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICBkYXRhLmV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgfVxufVxuXG4vKipcbiAqIEBzZWUgaHR0cHM6Ly9kb20uc3BlYy53aGF0d2cub3JnLyNpbnRlcmZhY2UtZXZlbnRcbiAqIEBwcml2YXRlXG4gKi9cbi8qKlxuICogVGhlIGV2ZW50IHdyYXBwZXIuXG4gKiBAY29uc3RydWN0b3JcbiAqIEBwYXJhbSB7RXZlbnRUYXJnZXR9IGV2ZW50VGFyZ2V0IFRoZSBldmVudCB0YXJnZXQgb2YgdGhpcyBkaXNwYXRjaGluZy5cbiAqIEBwYXJhbSB7RXZlbnR8e3R5cGU6c3RyaW5nfX0gZXZlbnQgVGhlIG9yaWdpbmFsIGV2ZW50IHRvIHdyYXAuXG4gKi9cbmZ1bmN0aW9uIEV2ZW50KGV2ZW50VGFyZ2V0LCBldmVudCkge1xuICAgIHByaXZhdGVEYXRhLnNldCh0aGlzLCB7XG4gICAgICAgIGV2ZW50VGFyZ2V0LFxuICAgICAgICBldmVudCxcbiAgICAgICAgZXZlbnRQaGFzZTogMixcbiAgICAgICAgY3VycmVudFRhcmdldDogZXZlbnRUYXJnZXQsXG4gICAgICAgIGNhbmNlbGVkOiBmYWxzZSxcbiAgICAgICAgc3RvcHBlZDogZmFsc2UsXG4gICAgICAgIGltbWVkaWF0ZVN0b3BwZWQ6IGZhbHNlLFxuICAgICAgICBwYXNzaXZlTGlzdGVuZXI6IG51bGwsXG4gICAgICAgIHRpbWVTdGFtcDogZXZlbnQudGltZVN0YW1wIHx8IERhdGUubm93KCksXG4gICAgfSk7XG5cbiAgICAvLyBodHRwczovL2hleWNhbS5naXRodWIuaW8vd2ViaWRsLyNVbmZvcmdlYWJsZVxuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0aGlzLCBcImlzVHJ1c3RlZFwiLCB7IHZhbHVlOiBmYWxzZSwgZW51bWVyYWJsZTogdHJ1ZSB9KTtcblxuICAgIC8vIERlZmluZSBhY2Nlc3NvcnNcbiAgICBjb25zdCBrZXlzID0gT2JqZWN0LmtleXMoZXZlbnQpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwga2V5cy5sZW5ndGg7ICsraSkge1xuICAgICAgICBjb25zdCBrZXkgPSBrZXlzW2ldO1xuICAgICAgICBpZiAoIShrZXkgaW4gdGhpcykpIHtcbiAgICAgICAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0aGlzLCBrZXksIGRlZmluZVJlZGlyZWN0RGVzY3JpcHRvcihrZXkpKTtcbiAgICAgICAgfVxuICAgIH1cbn1cblxuLy8gU2hvdWxkIGJlIGVudW1lcmFibGUsIGJ1dCBjbGFzcyBtZXRob2RzIGFyZSBub3QgZW51bWVyYWJsZS5cbkV2ZW50LnByb3RvdHlwZSA9IHtcbiAgICAvKipcbiAgICAgKiBUaGUgdHlwZSBvZiB0aGlzIGV2ZW50LlxuICAgICAqIEB0eXBlIHtzdHJpbmd9XG4gICAgICovXG4gICAgZ2V0IHR5cGUoKSB7XG4gICAgICAgIHJldHVybiBwZCh0aGlzKS5ldmVudC50eXBlXG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFRoZSB0YXJnZXQgb2YgdGhpcyBldmVudC5cbiAgICAgKiBAdHlwZSB7RXZlbnRUYXJnZXR9XG4gICAgICovXG4gICAgZ2V0IHRhcmdldCgpIHtcbiAgICAgICAgcmV0dXJuIHBkKHRoaXMpLmV2ZW50VGFyZ2V0XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFRoZSB0YXJnZXQgb2YgdGhpcyBldmVudC5cbiAgICAgKiBAdHlwZSB7RXZlbnRUYXJnZXR9XG4gICAgICovXG4gICAgZ2V0IGN1cnJlbnRUYXJnZXQoKSB7XG4gICAgICAgIHJldHVybiBwZCh0aGlzKS5jdXJyZW50VGFyZ2V0XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIEByZXR1cm5zIHtFdmVudFRhcmdldFtdfSBUaGUgY29tcG9zZWQgcGF0aCBvZiB0aGlzIGV2ZW50LlxuICAgICAqL1xuICAgIGNvbXBvc2VkUGF0aCgpIHtcbiAgICAgICAgY29uc3QgY3VycmVudFRhcmdldCA9IHBkKHRoaXMpLmN1cnJlbnRUYXJnZXQ7XG4gICAgICAgIGlmIChjdXJyZW50VGFyZ2V0ID09IG51bGwpIHtcbiAgICAgICAgICAgIHJldHVybiBbXVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBbY3VycmVudFRhcmdldF1cbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogQ29uc3RhbnQgb2YgTk9ORS5cbiAgICAgKiBAdHlwZSB7bnVtYmVyfVxuICAgICAqL1xuICAgIGdldCBOT05FKCkge1xuICAgICAgICByZXR1cm4gMFxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBDb25zdGFudCBvZiBDQVBUVVJJTkdfUEhBU0UuXG4gICAgICogQHR5cGUge251bWJlcn1cbiAgICAgKi9cbiAgICBnZXQgQ0FQVFVSSU5HX1BIQVNFKCkge1xuICAgICAgICByZXR1cm4gMVxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBDb25zdGFudCBvZiBBVF9UQVJHRVQuXG4gICAgICogQHR5cGUge251bWJlcn1cbiAgICAgKi9cbiAgICBnZXQgQVRfVEFSR0VUKCkge1xuICAgICAgICByZXR1cm4gMlxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBDb25zdGFudCBvZiBCVUJCTElOR19QSEFTRS5cbiAgICAgKiBAdHlwZSB7bnVtYmVyfVxuICAgICAqL1xuICAgIGdldCBCVUJCTElOR19QSEFTRSgpIHtcbiAgICAgICAgcmV0dXJuIDNcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogVGhlIHRhcmdldCBvZiB0aGlzIGV2ZW50LlxuICAgICAqIEB0eXBlIHtudW1iZXJ9XG4gICAgICovXG4gICAgZ2V0IGV2ZW50UGhhc2UoKSB7XG4gICAgICAgIHJldHVybiBwZCh0aGlzKS5ldmVudFBoYXNlXG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFN0b3AgZXZlbnQgYnViYmxpbmcuXG4gICAgICogQHJldHVybnMge3ZvaWR9XG4gICAgICovXG4gICAgc3RvcFByb3BhZ2F0aW9uKCkge1xuICAgICAgICBjb25zdCBkYXRhID0gcGQodGhpcyk7XG5cbiAgICAgICAgZGF0YS5zdG9wcGVkID0gdHJ1ZTtcbiAgICAgICAgaWYgKHR5cGVvZiBkYXRhLmV2ZW50LnN0b3BQcm9wYWdhdGlvbiA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgICAgICBkYXRhLmV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFN0b3AgZXZlbnQgYnViYmxpbmcuXG4gICAgICogQHJldHVybnMge3ZvaWR9XG4gICAgICovXG4gICAgc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKCkge1xuICAgICAgICBjb25zdCBkYXRhID0gcGQodGhpcyk7XG5cbiAgICAgICAgZGF0YS5zdG9wcGVkID0gdHJ1ZTtcbiAgICAgICAgZGF0YS5pbW1lZGlhdGVTdG9wcGVkID0gdHJ1ZTtcbiAgICAgICAgaWYgKHR5cGVvZiBkYXRhLmV2ZW50LnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbiA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgICAgICBkYXRhLmV2ZW50LnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFRoZSBmbGFnIHRvIGJlIGJ1YmJsaW5nLlxuICAgICAqIEB0eXBlIHtib29sZWFufVxuICAgICAqL1xuICAgIGdldCBidWJibGVzKCkge1xuICAgICAgICByZXR1cm4gQm9vbGVhbihwZCh0aGlzKS5ldmVudC5idWJibGVzKVxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBUaGUgZmxhZyB0byBiZSBjYW5jZWxhYmxlLlxuICAgICAqIEB0eXBlIHtib29sZWFufVxuICAgICAqL1xuICAgIGdldCBjYW5jZWxhYmxlKCkge1xuICAgICAgICByZXR1cm4gQm9vbGVhbihwZCh0aGlzKS5ldmVudC5jYW5jZWxhYmxlKVxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBDYW5jZWwgdGhpcyBldmVudC5cbiAgICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICAgKi9cbiAgICBwcmV2ZW50RGVmYXVsdCgpIHtcbiAgICAgICAgc2V0Q2FuY2VsRmxhZyhwZCh0aGlzKSk7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFRoZSBmbGFnIHRvIGluZGljYXRlIGNhbmNlbGxhdGlvbiBzdGF0ZS5cbiAgICAgKiBAdHlwZSB7Ym9vbGVhbn1cbiAgICAgKi9cbiAgICBnZXQgZGVmYXVsdFByZXZlbnRlZCgpIHtcbiAgICAgICAgcmV0dXJuIHBkKHRoaXMpLmNhbmNlbGVkXG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFRoZSBmbGFnIHRvIGJlIGNvbXBvc2VkLlxuICAgICAqIEB0eXBlIHtib29sZWFufVxuICAgICAqL1xuICAgIGdldCBjb21wb3NlZCgpIHtcbiAgICAgICAgcmV0dXJuIEJvb2xlYW4ocGQodGhpcykuZXZlbnQuY29tcG9zZWQpXG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFRoZSB1bml4IHRpbWUgb2YgdGhpcyBldmVudC5cbiAgICAgKiBAdHlwZSB7bnVtYmVyfVxuICAgICAqL1xuICAgIGdldCB0aW1lU3RhbXAoKSB7XG4gICAgICAgIHJldHVybiBwZCh0aGlzKS50aW1lU3RhbXBcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogVGhlIHRhcmdldCBvZiB0aGlzIGV2ZW50LlxuICAgICAqIEB0eXBlIHtFdmVudFRhcmdldH1cbiAgICAgKiBAZGVwcmVjYXRlZFxuICAgICAqL1xuICAgIGdldCBzcmNFbGVtZW50KCkge1xuICAgICAgICByZXR1cm4gcGQodGhpcykuZXZlbnRUYXJnZXRcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogVGhlIGZsYWcgdG8gc3RvcCBldmVudCBidWJibGluZy5cbiAgICAgKiBAdHlwZSB7Ym9vbGVhbn1cbiAgICAgKiBAZGVwcmVjYXRlZFxuICAgICAqL1xuICAgIGdldCBjYW5jZWxCdWJibGUoKSB7XG4gICAgICAgIHJldHVybiBwZCh0aGlzKS5zdG9wcGVkXG4gICAgfSxcbiAgICBzZXQgY2FuY2VsQnViYmxlKHZhbHVlKSB7XG4gICAgICAgIGlmICghdmFsdWUpIHtcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGRhdGEgPSBwZCh0aGlzKTtcblxuICAgICAgICBkYXRhLnN0b3BwZWQgPSB0cnVlO1xuICAgICAgICBpZiAodHlwZW9mIGRhdGEuZXZlbnQuY2FuY2VsQnViYmxlID09PSBcImJvb2xlYW5cIikge1xuICAgICAgICAgICAgZGF0YS5ldmVudC5jYW5jZWxCdWJibGUgPSB0cnVlO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFRoZSBmbGFnIHRvIGluZGljYXRlIGNhbmNlbGxhdGlvbiBzdGF0ZS5cbiAgICAgKiBAdHlwZSB7Ym9vbGVhbn1cbiAgICAgKiBAZGVwcmVjYXRlZFxuICAgICAqL1xuICAgIGdldCByZXR1cm5WYWx1ZSgpIHtcbiAgICAgICAgcmV0dXJuICFwZCh0aGlzKS5jYW5jZWxlZFxuICAgIH0sXG4gICAgc2V0IHJldHVyblZhbHVlKHZhbHVlKSB7XG4gICAgICAgIGlmICghdmFsdWUpIHtcbiAgICAgICAgICAgIHNldENhbmNlbEZsYWcocGQodGhpcykpO1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIEluaXRpYWxpemUgdGhpcyBldmVudCBvYmplY3QuIEJ1dCBkbyBub3RoaW5nIHVuZGVyIGV2ZW50IGRpc3BhdGNoaW5nLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIFRoZSBldmVudCB0eXBlLlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW2J1YmJsZXM9ZmFsc2VdIFRoZSBmbGFnIHRvIGJlIHBvc3NpYmxlIHRvIGJ1YmJsZSB1cC5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtjYW5jZWxhYmxlPWZhbHNlXSBUaGUgZmxhZyB0byBiZSBwb3NzaWJsZSB0byBjYW5jZWwuXG4gICAgICogQGRlcHJlY2F0ZWRcbiAgICAgKi9cbiAgICBpbml0RXZlbnQoKSB7XG4gICAgICAgIC8vIERvIG5vdGhpbmcuXG4gICAgfSxcbn07XG5cbi8vIGBjb25zdHJ1Y3RvcmAgaXMgbm90IGVudW1lcmFibGUuXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoRXZlbnQucHJvdG90eXBlLCBcImNvbnN0cnVjdG9yXCIsIHtcbiAgICB2YWx1ZTogRXZlbnQsXG4gICAgY29uZmlndXJhYmxlOiB0cnVlLFxuICAgIHdyaXRhYmxlOiB0cnVlLFxufSk7XG5cbi8vIEVuc3VyZSBgZXZlbnQgaW5zdGFuY2VvZiB3aW5kb3cuRXZlbnRgIGlzIGB0cnVlYC5cbmlmICh0eXBlb2Ygd2luZG93ICE9PSBcInVuZGVmaW5lZFwiICYmIHR5cGVvZiB3aW5kb3cuRXZlbnQgIT09IFwidW5kZWZpbmVkXCIpIHtcbiAgICBPYmplY3Quc2V0UHJvdG90eXBlT2YoRXZlbnQucHJvdG90eXBlLCB3aW5kb3cuRXZlbnQucHJvdG90eXBlKTtcblxuICAgIC8vIE1ha2UgYXNzb2NpYXRpb24gZm9yIHdyYXBwZXJzLlxuICAgIHdyYXBwZXJzLnNldCh3aW5kb3cuRXZlbnQucHJvdG90eXBlLCBFdmVudCk7XG59XG5cbi8qKlxuICogR2V0IHRoZSBwcm9wZXJ0eSBkZXNjcmlwdG9yIHRvIHJlZGlyZWN0IGEgZ2l2ZW4gcHJvcGVydHkuXG4gKiBAcGFyYW0ge3N0cmluZ30ga2V5IFByb3BlcnR5IG5hbWUgdG8gZGVmaW5lIHByb3BlcnR5IGRlc2NyaXB0b3IuXG4gKiBAcmV0dXJucyB7UHJvcGVydHlEZXNjcmlwdG9yfSBUaGUgcHJvcGVydHkgZGVzY3JpcHRvciB0byByZWRpcmVjdCB0aGUgcHJvcGVydHkuXG4gKiBAcHJpdmF0ZVxuICovXG5mdW5jdGlvbiBkZWZpbmVSZWRpcmVjdERlc2NyaXB0b3Ioa2V5KSB7XG4gICAgcmV0dXJuIHtcbiAgICAgICAgZ2V0KCkge1xuICAgICAgICAgICAgcmV0dXJuIHBkKHRoaXMpLmV2ZW50W2tleV1cbiAgICAgICAgfSxcbiAgICAgICAgc2V0KHZhbHVlKSB7XG4gICAgICAgICAgICBwZCh0aGlzKS5ldmVudFtrZXldID0gdmFsdWU7XG4gICAgICAgIH0sXG4gICAgICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZSxcbiAgICAgICAgZW51bWVyYWJsZTogdHJ1ZSxcbiAgICB9XG59XG5cbi8qKlxuICogR2V0IHRoZSBwcm9wZXJ0eSBkZXNjcmlwdG9yIHRvIGNhbGwgYSBnaXZlbiBtZXRob2QgcHJvcGVydHkuXG4gKiBAcGFyYW0ge3N0cmluZ30ga2V5IFByb3BlcnR5IG5hbWUgdG8gZGVmaW5lIHByb3BlcnR5IGRlc2NyaXB0b3IuXG4gKiBAcmV0dXJucyB7UHJvcGVydHlEZXNjcmlwdG9yfSBUaGUgcHJvcGVydHkgZGVzY3JpcHRvciB0byBjYWxsIHRoZSBtZXRob2QgcHJvcGVydHkuXG4gKiBAcHJpdmF0ZVxuICovXG5mdW5jdGlvbiBkZWZpbmVDYWxsRGVzY3JpcHRvcihrZXkpIHtcbiAgICByZXR1cm4ge1xuICAgICAgICB2YWx1ZSgpIHtcbiAgICAgICAgICAgIGNvbnN0IGV2ZW50ID0gcGQodGhpcykuZXZlbnQ7XG4gICAgICAgICAgICByZXR1cm4gZXZlbnRba2V5XS5hcHBseShldmVudCwgYXJndW1lbnRzKVxuICAgICAgICB9LFxuICAgICAgICBjb25maWd1cmFibGU6IHRydWUsXG4gICAgICAgIGVudW1lcmFibGU6IHRydWUsXG4gICAgfVxufVxuXG4vKipcbiAqIERlZmluZSBuZXcgd3JhcHBlciBjbGFzcy5cbiAqIEBwYXJhbSB7RnVuY3Rpb259IEJhc2VFdmVudCBUaGUgYmFzZSB3cmFwcGVyIGNsYXNzLlxuICogQHBhcmFtIHtPYmplY3R9IHByb3RvIFRoZSBwcm90b3R5cGUgb2YgdGhlIG9yaWdpbmFsIGV2ZW50LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBUaGUgZGVmaW5lZCB3cmFwcGVyIGNsYXNzLlxuICogQHByaXZhdGVcbiAqL1xuZnVuY3Rpb24gZGVmaW5lV3JhcHBlcihCYXNlRXZlbnQsIHByb3RvKSB7XG4gICAgY29uc3Qga2V5cyA9IE9iamVjdC5rZXlzKHByb3RvKTtcbiAgICBpZiAoa2V5cy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgcmV0dXJuIEJhc2VFdmVudFxuICAgIH1cblxuICAgIC8qKiBDdXN0b21FdmVudCAqL1xuICAgIGZ1bmN0aW9uIEN1c3RvbUV2ZW50KGV2ZW50VGFyZ2V0LCBldmVudCkge1xuICAgICAgICBCYXNlRXZlbnQuY2FsbCh0aGlzLCBldmVudFRhcmdldCwgZXZlbnQpO1xuICAgIH1cblxuICAgIEN1c3RvbUV2ZW50LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoQmFzZUV2ZW50LnByb3RvdHlwZSwge1xuICAgICAgICBjb25zdHJ1Y3RvcjogeyB2YWx1ZTogQ3VzdG9tRXZlbnQsIGNvbmZpZ3VyYWJsZTogdHJ1ZSwgd3JpdGFibGU6IHRydWUgfSxcbiAgICB9KTtcblxuICAgIC8vIERlZmluZSBhY2Nlc3NvcnMuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBrZXlzLmxlbmd0aDsgKytpKSB7XG4gICAgICAgIGNvbnN0IGtleSA9IGtleXNbaV07XG4gICAgICAgIGlmICghKGtleSBpbiBCYXNlRXZlbnQucHJvdG90eXBlKSkge1xuICAgICAgICAgICAgY29uc3QgZGVzY3JpcHRvciA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IocHJvdG8sIGtleSk7XG4gICAgICAgICAgICBjb25zdCBpc0Z1bmMgPSB0eXBlb2YgZGVzY3JpcHRvci52YWx1ZSA9PT0gXCJmdW5jdGlvblwiO1xuICAgICAgICAgICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KFxuICAgICAgICAgICAgICAgIEN1c3RvbUV2ZW50LnByb3RvdHlwZSxcbiAgICAgICAgICAgICAgICBrZXksXG4gICAgICAgICAgICAgICAgaXNGdW5jXG4gICAgICAgICAgICAgICAgICAgID8gZGVmaW5lQ2FsbERlc2NyaXB0b3Ioa2V5KVxuICAgICAgICAgICAgICAgICAgICA6IGRlZmluZVJlZGlyZWN0RGVzY3JpcHRvcihrZXkpXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIEN1c3RvbUV2ZW50XG59XG5cbi8qKlxuICogR2V0IHRoZSB3cmFwcGVyIGNsYXNzIG9mIGEgZ2l2ZW4gcHJvdG90eXBlLlxuICogQHBhcmFtIHtPYmplY3R9IHByb3RvIFRoZSBwcm90b3R5cGUgb2YgdGhlIG9yaWdpbmFsIGV2ZW50IHRvIGdldCBpdHMgd3JhcHBlci5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gVGhlIHdyYXBwZXIgY2xhc3MuXG4gKiBAcHJpdmF0ZVxuICovXG5mdW5jdGlvbiBnZXRXcmFwcGVyKHByb3RvKSB7XG4gICAgaWYgKHByb3RvID09IG51bGwgfHwgcHJvdG8gPT09IE9iamVjdC5wcm90b3R5cGUpIHtcbiAgICAgICAgcmV0dXJuIEV2ZW50XG4gICAgfVxuXG4gICAgbGV0IHdyYXBwZXIgPSB3cmFwcGVycy5nZXQocHJvdG8pO1xuICAgIGlmICh3cmFwcGVyID09IG51bGwpIHtcbiAgICAgICAgd3JhcHBlciA9IGRlZmluZVdyYXBwZXIoZ2V0V3JhcHBlcihPYmplY3QuZ2V0UHJvdG90eXBlT2YocHJvdG8pKSwgcHJvdG8pO1xuICAgICAgICB3cmFwcGVycy5zZXQocHJvdG8sIHdyYXBwZXIpO1xuICAgIH1cbiAgICByZXR1cm4gd3JhcHBlclxufVxuXG4vKipcbiAqIFdyYXAgYSBnaXZlbiBldmVudCB0byBtYW5hZ2VtZW50IGEgZGlzcGF0Y2hpbmcuXG4gKiBAcGFyYW0ge0V2ZW50VGFyZ2V0fSBldmVudFRhcmdldCBUaGUgZXZlbnQgdGFyZ2V0IG9mIHRoaXMgZGlzcGF0Y2hpbmcuXG4gKiBAcGFyYW0ge09iamVjdH0gZXZlbnQgVGhlIGV2ZW50IHRvIHdyYXAuXG4gKiBAcmV0dXJucyB7RXZlbnR9IFRoZSB3cmFwcGVyIGluc3RhbmNlLlxuICogQHByaXZhdGVcbiAqL1xuZnVuY3Rpb24gd3JhcEV2ZW50KGV2ZW50VGFyZ2V0LCBldmVudCkge1xuICAgIGNvbnN0IFdyYXBwZXIgPSBnZXRXcmFwcGVyKE9iamVjdC5nZXRQcm90b3R5cGVPZihldmVudCkpO1xuICAgIHJldHVybiBuZXcgV3JhcHBlcihldmVudFRhcmdldCwgZXZlbnQpXG59XG5cbi8qKlxuICogR2V0IHRoZSBpbW1lZGlhdGVTdG9wcGVkIGZsYWcgb2YgYSBnaXZlbiBldmVudC5cbiAqIEBwYXJhbSB7RXZlbnR9IGV2ZW50IFRoZSBldmVudCB0byBnZXQuXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVGhlIGZsYWcgdG8gc3RvcCBwcm9wYWdhdGlvbiBpbW1lZGlhdGVseS5cbiAqIEBwcml2YXRlXG4gKi9cbmZ1bmN0aW9uIGlzU3RvcHBlZChldmVudCkge1xuICAgIHJldHVybiBwZChldmVudCkuaW1tZWRpYXRlU3RvcHBlZFxufVxuXG4vKipcbiAqIFNldCB0aGUgY3VycmVudCBldmVudCBwaGFzZSBvZiBhIGdpdmVuIGV2ZW50LlxuICogQHBhcmFtIHtFdmVudH0gZXZlbnQgVGhlIGV2ZW50IHRvIHNldCBjdXJyZW50IHRhcmdldC5cbiAqIEBwYXJhbSB7bnVtYmVyfSBldmVudFBoYXNlIE5ldyBldmVudCBwaGFzZS5cbiAqIEByZXR1cm5zIHt2b2lkfVxuICogQHByaXZhdGVcbiAqL1xuZnVuY3Rpb24gc2V0RXZlbnRQaGFzZShldmVudCwgZXZlbnRQaGFzZSkge1xuICAgIHBkKGV2ZW50KS5ldmVudFBoYXNlID0gZXZlbnRQaGFzZTtcbn1cblxuLyoqXG4gKiBTZXQgdGhlIGN1cnJlbnQgdGFyZ2V0IG9mIGEgZ2l2ZW4gZXZlbnQuXG4gKiBAcGFyYW0ge0V2ZW50fSBldmVudCBUaGUgZXZlbnQgdG8gc2V0IGN1cnJlbnQgdGFyZ2V0LlxuICogQHBhcmFtIHtFdmVudFRhcmdldHxudWxsfSBjdXJyZW50VGFyZ2V0IE5ldyBjdXJyZW50IHRhcmdldC5cbiAqIEByZXR1cm5zIHt2b2lkfVxuICogQHByaXZhdGVcbiAqL1xuZnVuY3Rpb24gc2V0Q3VycmVudFRhcmdldChldmVudCwgY3VycmVudFRhcmdldCkge1xuICAgIHBkKGV2ZW50KS5jdXJyZW50VGFyZ2V0ID0gY3VycmVudFRhcmdldDtcbn1cblxuLyoqXG4gKiBTZXQgYSBwYXNzaXZlIGxpc3RlbmVyIG9mIGEgZ2l2ZW4gZXZlbnQuXG4gKiBAcGFyYW0ge0V2ZW50fSBldmVudCBUaGUgZXZlbnQgdG8gc2V0IGN1cnJlbnQgdGFyZ2V0LlxuICogQHBhcmFtIHtGdW5jdGlvbnxudWxsfSBwYXNzaXZlTGlzdGVuZXIgTmV3IHBhc3NpdmUgbGlzdGVuZXIuXG4gKiBAcmV0dXJucyB7dm9pZH1cbiAqIEBwcml2YXRlXG4gKi9cbmZ1bmN0aW9uIHNldFBhc3NpdmVMaXN0ZW5lcihldmVudCwgcGFzc2l2ZUxpc3RlbmVyKSB7XG4gICAgcGQoZXZlbnQpLnBhc3NpdmVMaXN0ZW5lciA9IHBhc3NpdmVMaXN0ZW5lcjtcbn1cblxuLyoqXG4gKiBAdHlwZWRlZiB7b2JqZWN0fSBMaXN0ZW5lck5vZGVcbiAqIEBwcm9wZXJ0eSB7RnVuY3Rpb259IGxpc3RlbmVyXG4gKiBAcHJvcGVydHkgezF8MnwzfSBsaXN0ZW5lclR5cGVcbiAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gcGFzc2l2ZVxuICogQHByb3BlcnR5IHtib29sZWFufSBvbmNlXG4gKiBAcHJvcGVydHkge0xpc3RlbmVyTm9kZXxudWxsfSBuZXh0XG4gKiBAcHJpdmF0ZVxuICovXG5cbi8qKlxuICogQHR5cGUge1dlYWtNYXA8b2JqZWN0LCBNYXA8c3RyaW5nLCBMaXN0ZW5lck5vZGU+Pn1cbiAqIEBwcml2YXRlXG4gKi9cbmNvbnN0IGxpc3RlbmVyc01hcCA9IG5ldyBXZWFrTWFwKCk7XG5cbi8vIExpc3RlbmVyIHR5cGVzXG5jb25zdCBDQVBUVVJFID0gMTtcbmNvbnN0IEJVQkJMRSA9IDI7XG5jb25zdCBBVFRSSUJVVEUgPSAzO1xuXG4vKipcbiAqIENoZWNrIHdoZXRoZXIgYSBnaXZlbiB2YWx1ZSBpcyBhbiBvYmplY3Qgb3Igbm90LlxuICogQHBhcmFtIHthbnl9IHggVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IGB0cnVlYCBpZiB0aGUgdmFsdWUgaXMgYW4gb2JqZWN0LlxuICovXG5mdW5jdGlvbiBpc09iamVjdCh4KSB7XG4gICAgcmV0dXJuIHggIT09IG51bGwgJiYgdHlwZW9mIHggPT09IFwib2JqZWN0XCIgLy9lc2xpbnQtZGlzYWJsZS1saW5lIG5vLXJlc3RyaWN0ZWQtc3ludGF4XG59XG5cbi8qKlxuICogR2V0IGxpc3RlbmVycy5cbiAqIEBwYXJhbSB7RXZlbnRUYXJnZXR9IGV2ZW50VGFyZ2V0IFRoZSBldmVudCB0YXJnZXQgdG8gZ2V0LlxuICogQHJldHVybnMge01hcDxzdHJpbmcsIExpc3RlbmVyTm9kZT59IFRoZSBsaXN0ZW5lcnMuXG4gKiBAcHJpdmF0ZVxuICovXG5mdW5jdGlvbiBnZXRMaXN0ZW5lcnMoZXZlbnRUYXJnZXQpIHtcbiAgICBjb25zdCBsaXN0ZW5lcnMgPSBsaXN0ZW5lcnNNYXAuZ2V0KGV2ZW50VGFyZ2V0KTtcbiAgICBpZiAobGlzdGVuZXJzID09IG51bGwpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihcbiAgICAgICAgICAgIFwiJ3RoaXMnIGlzIGV4cGVjdGVkIGFuIEV2ZW50VGFyZ2V0IG9iamVjdCwgYnV0IGdvdCBhbm90aGVyIHZhbHVlLlwiXG4gICAgICAgIClcbiAgICB9XG4gICAgcmV0dXJuIGxpc3RlbmVyc1xufVxuXG4vKipcbiAqIEdldCB0aGUgcHJvcGVydHkgZGVzY3JpcHRvciBmb3IgdGhlIGV2ZW50IGF0dHJpYnV0ZSBvZiBhIGdpdmVuIGV2ZW50LlxuICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50TmFtZSBUaGUgZXZlbnQgbmFtZSB0byBnZXQgcHJvcGVydHkgZGVzY3JpcHRvci5cbiAqIEByZXR1cm5zIHtQcm9wZXJ0eURlc2NyaXB0b3J9IFRoZSBwcm9wZXJ0eSBkZXNjcmlwdG9yLlxuICogQHByaXZhdGVcbiAqL1xuZnVuY3Rpb24gZGVmaW5lRXZlbnRBdHRyaWJ1dGVEZXNjcmlwdG9yKGV2ZW50TmFtZSkge1xuICAgIHJldHVybiB7XG4gICAgICAgIGdldCgpIHtcbiAgICAgICAgICAgIGNvbnN0IGxpc3RlbmVycyA9IGdldExpc3RlbmVycyh0aGlzKTtcbiAgICAgICAgICAgIGxldCBub2RlID0gbGlzdGVuZXJzLmdldChldmVudE5hbWUpO1xuICAgICAgICAgICAgd2hpbGUgKG5vZGUgIT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIGlmIChub2RlLmxpc3RlbmVyVHlwZSA9PT0gQVRUUklCVVRFKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBub2RlLmxpc3RlbmVyXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIG5vZGUgPSBub2RlLm5leHQ7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gbnVsbFxuICAgICAgICB9LFxuXG4gICAgICAgIHNldChsaXN0ZW5lcikge1xuICAgICAgICAgICAgaWYgKHR5cGVvZiBsaXN0ZW5lciAhPT0gXCJmdW5jdGlvblwiICYmICFpc09iamVjdChsaXN0ZW5lcikpIHtcbiAgICAgICAgICAgICAgICBsaXN0ZW5lciA9IG51bGw7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tcGFyYW0tcmVhc3NpZ25cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IGxpc3RlbmVycyA9IGdldExpc3RlbmVycyh0aGlzKTtcblxuICAgICAgICAgICAgLy8gVHJhdmVyc2UgdG8gdGhlIHRhaWwgd2hpbGUgcmVtb3Zpbmcgb2xkIHZhbHVlLlxuICAgICAgICAgICAgbGV0IHByZXYgPSBudWxsO1xuICAgICAgICAgICAgbGV0IG5vZGUgPSBsaXN0ZW5lcnMuZ2V0KGV2ZW50TmFtZSk7XG4gICAgICAgICAgICB3aGlsZSAobm9kZSAhPSBudWxsKSB7XG4gICAgICAgICAgICAgICAgaWYgKG5vZGUubGlzdGVuZXJUeXBlID09PSBBVFRSSUJVVEUpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gUmVtb3ZlIG9sZCB2YWx1ZS5cbiAgICAgICAgICAgICAgICAgICAgaWYgKHByZXYgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHByZXYubmV4dCA9IG5vZGUubmV4dDtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmIChub2RlLm5leHQgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGxpc3RlbmVycy5zZXQoZXZlbnROYW1lLCBub2RlLm5leHQpO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgbGlzdGVuZXJzLmRlbGV0ZShldmVudE5hbWUpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgcHJldiA9IG5vZGU7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgbm9kZSA9IG5vZGUubmV4dDtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gQWRkIG5ldyB2YWx1ZS5cbiAgICAgICAgICAgIGlmIChsaXN0ZW5lciAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIGNvbnN0IG5ld05vZGUgPSB7XG4gICAgICAgICAgICAgICAgICAgIGxpc3RlbmVyLFxuICAgICAgICAgICAgICAgICAgICBsaXN0ZW5lclR5cGU6IEFUVFJJQlVURSxcbiAgICAgICAgICAgICAgICAgICAgcGFzc2l2ZTogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgIG9uY2U6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBuZXh0OiBudWxsLFxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgaWYgKHByZXYgPT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgbGlzdGVuZXJzLnNldChldmVudE5hbWUsIG5ld05vZGUpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHByZXYubmV4dCA9IG5ld05vZGU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICBjb25maWd1cmFibGU6IHRydWUsXG4gICAgICAgIGVudW1lcmFibGU6IHRydWUsXG4gICAgfVxufVxuXG4vKipcbiAqIERlZmluZSBhbiBldmVudCBhdHRyaWJ1dGUgKGUuZy4gYGV2ZW50VGFyZ2V0Lm9uY2xpY2tgKS5cbiAqIEBwYXJhbSB7T2JqZWN0fSBldmVudFRhcmdldFByb3RvdHlwZSBUaGUgZXZlbnQgdGFyZ2V0IHByb3RvdHlwZSB0byBkZWZpbmUgYW4gZXZlbnQgYXR0cmJpdGUuXG4gKiBAcGFyYW0ge3N0cmluZ30gZXZlbnROYW1lIFRoZSBldmVudCBuYW1lIHRvIGRlZmluZS5cbiAqIEByZXR1cm5zIHt2b2lkfVxuICovXG5mdW5jdGlvbiBkZWZpbmVFdmVudEF0dHJpYnV0ZShldmVudFRhcmdldFByb3RvdHlwZSwgZXZlbnROYW1lKSB7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KFxuICAgICAgICBldmVudFRhcmdldFByb3RvdHlwZSxcbiAgICAgICAgYG9uJHtldmVudE5hbWV9YCxcbiAgICAgICAgZGVmaW5lRXZlbnRBdHRyaWJ1dGVEZXNjcmlwdG9yKGV2ZW50TmFtZSlcbiAgICApO1xufVxuXG4vKipcbiAqIERlZmluZSBhIGN1c3RvbSBFdmVudFRhcmdldCB3aXRoIGV2ZW50IGF0dHJpYnV0ZXMuXG4gKiBAcGFyYW0ge3N0cmluZ1tdfSBldmVudE5hbWVzIEV2ZW50IG5hbWVzIGZvciBldmVudCBhdHRyaWJ1dGVzLlxuICogQHJldHVybnMge0V2ZW50VGFyZ2V0fSBUaGUgY3VzdG9tIEV2ZW50VGFyZ2V0LlxuICogQHByaXZhdGVcbiAqL1xuZnVuY3Rpb24gZGVmaW5lQ3VzdG9tRXZlbnRUYXJnZXQoZXZlbnROYW1lcykge1xuICAgIC8qKiBDdXN0b21FdmVudFRhcmdldCAqL1xuICAgIGZ1bmN0aW9uIEN1c3RvbUV2ZW50VGFyZ2V0KCkge1xuICAgICAgICBFdmVudFRhcmdldC5jYWxsKHRoaXMpO1xuICAgIH1cblxuICAgIEN1c3RvbUV2ZW50VGFyZ2V0LnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoRXZlbnRUYXJnZXQucHJvdG90eXBlLCB7XG4gICAgICAgIGNvbnN0cnVjdG9yOiB7XG4gICAgICAgICAgICB2YWx1ZTogQ3VzdG9tRXZlbnRUYXJnZXQsXG4gICAgICAgICAgICBjb25maWd1cmFibGU6IHRydWUsXG4gICAgICAgICAgICB3cml0YWJsZTogdHJ1ZSxcbiAgICAgICAgfSxcbiAgICB9KTtcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgZXZlbnROYW1lcy5sZW5ndGg7ICsraSkge1xuICAgICAgICBkZWZpbmVFdmVudEF0dHJpYnV0ZShDdXN0b21FdmVudFRhcmdldC5wcm90b3R5cGUsIGV2ZW50TmFtZXNbaV0pO1xuICAgIH1cblxuICAgIHJldHVybiBDdXN0b21FdmVudFRhcmdldFxufVxuXG4vKipcbiAqIEV2ZW50VGFyZ2V0LlxuICpcbiAqIC0gVGhpcyBpcyBjb25zdHJ1Y3RvciBpZiBubyBhcmd1bWVudHMuXG4gKiAtIFRoaXMgaXMgYSBmdW5jdGlvbiB3aGljaCByZXR1cm5zIGEgQ3VzdG9tRXZlbnRUYXJnZXQgY29uc3RydWN0b3IgaWYgdGhlcmUgYXJlIGFyZ3VtZW50cy5cbiAqXG4gKiBGb3IgZXhhbXBsZTpcbiAqXG4gKiAgICAgY2xhc3MgQSBleHRlbmRzIEV2ZW50VGFyZ2V0IHt9XG4gKiAgICAgY2xhc3MgQiBleHRlbmRzIEV2ZW50VGFyZ2V0KFwibWVzc2FnZVwiKSB7fVxuICogICAgIGNsYXNzIEMgZXh0ZW5kcyBFdmVudFRhcmdldChcIm1lc3NhZ2VcIiwgXCJlcnJvclwiKSB7fVxuICogICAgIGNsYXNzIEQgZXh0ZW5kcyBFdmVudFRhcmdldChbXCJtZXNzYWdlXCIsIFwiZXJyb3JcIl0pIHt9XG4gKi9cbmZ1bmN0aW9uIEV2ZW50VGFyZ2V0KCkge1xuICAgIC8qZXNsaW50LWRpc2FibGUgY29uc2lzdGVudC1yZXR1cm4gKi9cbiAgICBpZiAodGhpcyBpbnN0YW5jZW9mIEV2ZW50VGFyZ2V0KSB7XG4gICAgICAgIGxpc3RlbmVyc01hcC5zZXQodGhpcywgbmV3IE1hcCgpKTtcbiAgICAgICAgcmV0dXJuXG4gICAgfVxuICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID09PSAxICYmIEFycmF5LmlzQXJyYXkoYXJndW1lbnRzWzBdKSkge1xuICAgICAgICByZXR1cm4gZGVmaW5lQ3VzdG9tRXZlbnRUYXJnZXQoYXJndW1lbnRzWzBdKVxuICAgIH1cbiAgICBpZiAoYXJndW1lbnRzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgY29uc3QgdHlwZXMgPSBuZXcgQXJyYXkoYXJndW1lbnRzLmxlbmd0aCk7XG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYXJndW1lbnRzLmxlbmd0aDsgKytpKSB7XG4gICAgICAgICAgICB0eXBlc1tpXSA9IGFyZ3VtZW50c1tpXTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZGVmaW5lQ3VzdG9tRXZlbnRUYXJnZXQodHlwZXMpXG4gICAgfVxuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoXCJDYW5ub3QgY2FsbCBhIGNsYXNzIGFzIGEgZnVuY3Rpb25cIilcbiAgICAvKmVzbGludC1lbmFibGUgY29uc2lzdGVudC1yZXR1cm4gKi9cbn1cblxuLy8gU2hvdWxkIGJlIGVudW1lcmFibGUsIGJ1dCBjbGFzcyBtZXRob2RzIGFyZSBub3QgZW51bWVyYWJsZS5cbkV2ZW50VGFyZ2V0LnByb3RvdHlwZSA9IHtcbiAgICAvKipcbiAgICAgKiBBZGQgYSBnaXZlbiBsaXN0ZW5lciB0byB0aGlzIGV2ZW50IHRhcmdldC5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gZXZlbnROYW1lIFRoZSBldmVudCBuYW1lIHRvIGFkZC5cbiAgICAgKiBAcGFyYW0ge0Z1bmN0aW9ufSBsaXN0ZW5lciBUaGUgbGlzdGVuZXIgdG8gYWRkLlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbnx7Y2FwdHVyZT86Ym9vbGVhbixwYXNzaXZlPzpib29sZWFuLG9uY2U/OmJvb2xlYW59fSBbb3B0aW9uc10gVGhlIG9wdGlvbnMgZm9yIHRoaXMgbGlzdGVuZXIuXG4gICAgICogQHJldHVybnMge3ZvaWR9XG4gICAgICovXG4gICAgYWRkRXZlbnRMaXN0ZW5lcihldmVudE5hbWUsIGxpc3RlbmVyLCBvcHRpb25zKSB7XG4gICAgICAgIGlmIChsaXN0ZW5lciA9PSBudWxsKSB7XG4gICAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBpZiAodHlwZW9mIGxpc3RlbmVyICE9PSBcImZ1bmN0aW9uXCIgJiYgIWlzT2JqZWN0KGxpc3RlbmVyKSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihcIidsaXN0ZW5lcicgc2hvdWxkIGJlIGEgZnVuY3Rpb24gb3IgYW4gb2JqZWN0LlwiKVxuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgbGlzdGVuZXJzID0gZ2V0TGlzdGVuZXJzKHRoaXMpO1xuICAgICAgICBjb25zdCBvcHRpb25zSXNPYmogPSBpc09iamVjdChvcHRpb25zKTtcbiAgICAgICAgY29uc3QgY2FwdHVyZSA9IG9wdGlvbnNJc09ialxuICAgICAgICAgICAgPyBCb29sZWFuKG9wdGlvbnMuY2FwdHVyZSlcbiAgICAgICAgICAgIDogQm9vbGVhbihvcHRpb25zKTtcbiAgICAgICAgY29uc3QgbGlzdGVuZXJUeXBlID0gY2FwdHVyZSA/IENBUFRVUkUgOiBCVUJCTEU7XG4gICAgICAgIGNvbnN0IG5ld05vZGUgPSB7XG4gICAgICAgICAgICBsaXN0ZW5lcixcbiAgICAgICAgICAgIGxpc3RlbmVyVHlwZSxcbiAgICAgICAgICAgIHBhc3NpdmU6IG9wdGlvbnNJc09iaiAmJiBCb29sZWFuKG9wdGlvbnMucGFzc2l2ZSksXG4gICAgICAgICAgICBvbmNlOiBvcHRpb25zSXNPYmogJiYgQm9vbGVhbihvcHRpb25zLm9uY2UpLFxuICAgICAgICAgICAgbmV4dDogbnVsbCxcbiAgICAgICAgfTtcblxuICAgICAgICAvLyBTZXQgaXQgYXMgdGhlIGZpcnN0IG5vZGUgaWYgdGhlIGZpcnN0IG5vZGUgaXMgbnVsbC5cbiAgICAgICAgbGV0IG5vZGUgPSBsaXN0ZW5lcnMuZ2V0KGV2ZW50TmFtZSk7XG4gICAgICAgIGlmIChub2RlID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGxpc3RlbmVycy5zZXQoZXZlbnROYW1lLCBuZXdOb2RlKTtcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG5cbiAgICAgICAgLy8gVHJhdmVyc2UgdG8gdGhlIHRhaWwgd2hpbGUgY2hlY2tpbmcgZHVwbGljYXRpb24uLlxuICAgICAgICBsZXQgcHJldiA9IG51bGw7XG4gICAgICAgIHdoaWxlIChub2RlICE9IG51bGwpIHtcbiAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICBub2RlLmxpc3RlbmVyID09PSBsaXN0ZW5lciAmJlxuICAgICAgICAgICAgICAgIG5vZGUubGlzdGVuZXJUeXBlID09PSBsaXN0ZW5lclR5cGVcbiAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgIC8vIFNob3VsZCBpZ25vcmUgZHVwbGljYXRpb24uXG4gICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBwcmV2ID0gbm9kZTtcbiAgICAgICAgICAgIG5vZGUgPSBub2RlLm5leHQ7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBBZGQgaXQuXG4gICAgICAgIHByZXYubmV4dCA9IG5ld05vZGU7XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFJlbW92ZSBhIGdpdmVuIGxpc3RlbmVyIGZyb20gdGhpcyBldmVudCB0YXJnZXQuXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50TmFtZSBUaGUgZXZlbnQgbmFtZSB0byByZW1vdmUuXG4gICAgICogQHBhcmFtIHtGdW5jdGlvbn0gbGlzdGVuZXIgVGhlIGxpc3RlbmVyIHRvIHJlbW92ZS5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW58e2NhcHR1cmU/OmJvb2xlYW4scGFzc2l2ZT86Ym9vbGVhbixvbmNlPzpib29sZWFufX0gW29wdGlvbnNdIFRoZSBvcHRpb25zIGZvciB0aGlzIGxpc3RlbmVyLlxuICAgICAqIEByZXR1cm5zIHt2b2lkfVxuICAgICAqL1xuICAgIHJlbW92ZUV2ZW50TGlzdGVuZXIoZXZlbnROYW1lLCBsaXN0ZW5lciwgb3B0aW9ucykge1xuICAgICAgICBpZiAobGlzdGVuZXIgPT0gbnVsbCkge1xuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBsaXN0ZW5lcnMgPSBnZXRMaXN0ZW5lcnModGhpcyk7XG4gICAgICAgIGNvbnN0IGNhcHR1cmUgPSBpc09iamVjdChvcHRpb25zKVxuICAgICAgICAgICAgPyBCb29sZWFuKG9wdGlvbnMuY2FwdHVyZSlcbiAgICAgICAgICAgIDogQm9vbGVhbihvcHRpb25zKTtcbiAgICAgICAgY29uc3QgbGlzdGVuZXJUeXBlID0gY2FwdHVyZSA/IENBUFRVUkUgOiBCVUJCTEU7XG5cbiAgICAgICAgbGV0IHByZXYgPSBudWxsO1xuICAgICAgICBsZXQgbm9kZSA9IGxpc3RlbmVycy5nZXQoZXZlbnROYW1lKTtcbiAgICAgICAgd2hpbGUgKG5vZGUgIT0gbnVsbCkge1xuICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgIG5vZGUubGlzdGVuZXIgPT09IGxpc3RlbmVyICYmXG4gICAgICAgICAgICAgICAgbm9kZS5saXN0ZW5lclR5cGUgPT09IGxpc3RlbmVyVHlwZVxuICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgICAgaWYgKHByZXYgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgcHJldi5uZXh0ID0gbm9kZS5uZXh0O1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAobm9kZS5uZXh0ICE9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIGxpc3RlbmVycy5zZXQoZXZlbnROYW1lLCBub2RlLm5leHQpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGxpc3RlbmVycy5kZWxldGUoZXZlbnROYW1lKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHByZXYgPSBub2RlO1xuICAgICAgICAgICAgbm9kZSA9IG5vZGUubmV4dDtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBEaXNwYXRjaCBhIGdpdmVuIGV2ZW50LlxuICAgICAqIEBwYXJhbSB7RXZlbnR8e3R5cGU6c3RyaW5nfX0gZXZlbnQgVGhlIGV2ZW50IHRvIGRpc3BhdGNoLlxuICAgICAqIEByZXR1cm5zIHtib29sZWFufSBgZmFsc2VgIGlmIGNhbmNlbGVkLlxuICAgICAqL1xuICAgIGRpc3BhdGNoRXZlbnQoZXZlbnQpIHtcbiAgICAgICAgaWYgKGV2ZW50ID09IG51bGwgfHwgdHlwZW9mIGV2ZW50LnR5cGUgIT09IFwic3RyaW5nXCIpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ1wiZXZlbnQudHlwZVwiIHNob3VsZCBiZSBhIHN0cmluZy4nKVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gSWYgbGlzdGVuZXJzIGFyZW4ndCByZWdpc3RlcmVkLCB0ZXJtaW5hdGUuXG4gICAgICAgIGNvbnN0IGxpc3RlbmVycyA9IGdldExpc3RlbmVycyh0aGlzKTtcbiAgICAgICAgY29uc3QgZXZlbnROYW1lID0gZXZlbnQudHlwZTtcbiAgICAgICAgbGV0IG5vZGUgPSBsaXN0ZW5lcnMuZ2V0KGV2ZW50TmFtZSk7XG4gICAgICAgIGlmIChub2RlID09IG51bGwpIHtcbiAgICAgICAgICAgIHJldHVybiB0cnVlXG4gICAgICAgIH1cblxuICAgICAgICAvLyBTaW5jZSB3ZSBjYW5ub3QgcmV3cml0ZSBzZXZlcmFsIHByb3BlcnRpZXMsIHNvIHdyYXAgb2JqZWN0LlxuICAgICAgICBjb25zdCB3cmFwcGVkRXZlbnQgPSB3cmFwRXZlbnQodGhpcywgZXZlbnQpO1xuXG4gICAgICAgIC8vIFRoaXMgZG9lc24ndCBwcm9jZXNzIGNhcHR1cmluZyBwaGFzZSBhbmQgYnViYmxpbmcgcGhhc2UuXG4gICAgICAgIC8vIFRoaXMgaXNuJ3QgcGFydGljaXBhdGluZyBpbiBhIHRyZWUuXG4gICAgICAgIGxldCBwcmV2ID0gbnVsbDtcbiAgICAgICAgd2hpbGUgKG5vZGUgIT0gbnVsbCkge1xuICAgICAgICAgICAgLy8gUmVtb3ZlIHRoaXMgbGlzdGVuZXIgaWYgaXQncyBvbmNlXG4gICAgICAgICAgICBpZiAobm9kZS5vbmNlKSB7XG4gICAgICAgICAgICAgICAgaWYgKHByZXYgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgcHJldi5uZXh0ID0gbm9kZS5uZXh0O1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAobm9kZS5uZXh0ICE9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIGxpc3RlbmVycy5zZXQoZXZlbnROYW1lLCBub2RlLm5leHQpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGxpc3RlbmVycy5kZWxldGUoZXZlbnROYW1lKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHByZXYgPSBub2RlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBDYWxsIHRoaXMgbGlzdGVuZXJcbiAgICAgICAgICAgIHNldFBhc3NpdmVMaXN0ZW5lcihcbiAgICAgICAgICAgICAgICB3cmFwcGVkRXZlbnQsXG4gICAgICAgICAgICAgICAgbm9kZS5wYXNzaXZlID8gbm9kZS5saXN0ZW5lciA6IG51bGxcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBpZiAodHlwZW9mIG5vZGUubGlzdGVuZXIgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgIG5vZGUubGlzdGVuZXIuY2FsbCh0aGlzLCB3cmFwcGVkRXZlbnQpO1xuICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlb2YgY29uc29sZSAhPT0gXCJ1bmRlZmluZWRcIiAmJlxuICAgICAgICAgICAgICAgICAgICAgICAgdHlwZW9mIGNvbnNvbGUuZXJyb3IgPT09IFwiZnVuY3Rpb25cIlxuICAgICAgICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZXJyKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAoXG4gICAgICAgICAgICAgICAgbm9kZS5saXN0ZW5lclR5cGUgIT09IEFUVFJJQlVURSAmJlxuICAgICAgICAgICAgICAgIHR5cGVvZiBub2RlLmxpc3RlbmVyLmhhbmRsZUV2ZW50ID09PSBcImZ1bmN0aW9uXCJcbiAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgIG5vZGUubGlzdGVuZXIuaGFuZGxlRXZlbnQod3JhcHBlZEV2ZW50KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gQnJlYWsgaWYgYGV2ZW50LnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbmAgd2FzIGNhbGxlZC5cbiAgICAgICAgICAgIGlmIChpc1N0b3BwZWQod3JhcHBlZEV2ZW50KSkge1xuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIG5vZGUgPSBub2RlLm5leHQ7XG4gICAgICAgIH1cbiAgICAgICAgc2V0UGFzc2l2ZUxpc3RlbmVyKHdyYXBwZWRFdmVudCwgbnVsbCk7XG4gICAgICAgIHNldEV2ZW50UGhhc2Uod3JhcHBlZEV2ZW50LCAwKTtcbiAgICAgICAgc2V0Q3VycmVudFRhcmdldCh3cmFwcGVkRXZlbnQsIG51bGwpO1xuXG4gICAgICAgIHJldHVybiAhd3JhcHBlZEV2ZW50LmRlZmF1bHRQcmV2ZW50ZWRcbiAgICB9LFxufTtcblxuLy8gYGNvbnN0cnVjdG9yYCBpcyBub3QgZW51bWVyYWJsZS5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShFdmVudFRhcmdldC5wcm90b3R5cGUsIFwiY29uc3RydWN0b3JcIiwge1xuICAgIHZhbHVlOiBFdmVudFRhcmdldCxcbiAgICBjb25maWd1cmFibGU6IHRydWUsXG4gICAgd3JpdGFibGU6IHRydWUsXG59KTtcblxuLy8gRW5zdXJlIGBldmVudFRhcmdldCBpbnN0YW5jZW9mIHdpbmRvdy5FdmVudFRhcmdldGAgaXMgYHRydWVgLlxuaWYgKFxuICAgIHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIgJiZcbiAgICB0eXBlb2Ygd2luZG93LkV2ZW50VGFyZ2V0ICE9PSBcInVuZGVmaW5lZFwiXG4pIHtcbiAgICBPYmplY3Quc2V0UHJvdG90eXBlT2YoRXZlbnRUYXJnZXQucHJvdG90eXBlLCB3aW5kb3cuRXZlbnRUYXJnZXQucHJvdG90eXBlKTtcbn1cblxuZXhwb3J0cy5kZWZpbmVFdmVudEF0dHJpYnV0ZSA9IGRlZmluZUV2ZW50QXR0cmlidXRlO1xuZXhwb3J0cy5FdmVudFRhcmdldCA9IEV2ZW50VGFyZ2V0O1xuZXhwb3J0cy5kZWZhdWx0ID0gRXZlbnRUYXJnZXQ7XG5cbm1vZHVsZS5leHBvcnRzID0gRXZlbnRUYXJnZXRcbm1vZHVsZS5leHBvcnRzLkV2ZW50VGFyZ2V0ID0gbW9kdWxlLmV4cG9ydHNbXCJkZWZhdWx0XCJdID0gRXZlbnRUYXJnZXRcbm1vZHVsZS5leHBvcnRzLmRlZmluZUV2ZW50QXR0cmlidXRlID0gZGVmaW5lRXZlbnRBdHRyaWJ1dGVcbi8vIyBzb3VyY2VNYXBwaW5nVVJMPWV2ZW50LXRhcmdldC1zaGltLmpzLm1hcFxuIiwiY29uc3Qge1JlYWRhYmxlfSA9IHJlcXVpcmUoJ3N0cmVhbScpO1xuXG4vKipcbiAqIEB0eXBlIHtXZWFrTWFwPEJsb2IsIHt0eXBlOiBzdHJpbmcsIHNpemU6IG51bWJlciwgcGFydHM6IChCbG9iIHwgQnVmZmVyKVtdIH0+fVxuICovXG5jb25zdCB3bSA9IG5ldyBXZWFrTWFwKCk7XG5cbmFzeW5jIGZ1bmN0aW9uICogcmVhZChwYXJ0cykge1xuXHRmb3IgKGNvbnN0IHBhcnQgb2YgcGFydHMpIHtcblx0XHRpZiAoJ3N0cmVhbScgaW4gcGFydCkge1xuXHRcdFx0eWllbGQgKiBwYXJ0LnN0cmVhbSgpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR5aWVsZCBwYXJ0O1xuXHRcdH1cblx0fVxufVxuXG5jbGFzcyBCbG9iIHtcblx0LyoqXG5cdCAqIFRoZSBCbG9iKCkgY29uc3RydWN0b3IgcmV0dXJucyBhIG5ldyBCbG9iIG9iamVjdC4gVGhlIGNvbnRlbnRcblx0ICogb2YgdGhlIGJsb2IgY29uc2lzdHMgb2YgdGhlIGNvbmNhdGVuYXRpb24gb2YgdGhlIHZhbHVlcyBnaXZlblxuXHQgKiBpbiB0aGUgcGFyYW1ldGVyIGFycmF5LlxuXHQgKlxuXHQgKiBAcGFyYW0geyhBcnJheUJ1ZmZlckxpa2UgfCBBcnJheUJ1ZmZlclZpZXcgfCBCbG9iIHwgQnVmZmVyIHwgc3RyaW5nKVtdfSBibG9iUGFydHNcblx0ICogQHBhcmFtIHt7IHR5cGU/OiBzdHJpbmcgfX0gW29wdGlvbnNdXG5cdCAqL1xuXHRjb25zdHJ1Y3RvcihibG9iUGFydHMgPSBbXSwgb3B0aW9ucyA9IHt0eXBlOiAnJ30pIHtcblx0XHRsZXQgc2l6ZSA9IDA7XG5cblx0XHRjb25zdCBwYXJ0cyA9IGJsb2JQYXJ0cy5tYXAoZWxlbWVudCA9PiB7XG5cdFx0XHRsZXQgYnVmZmVyO1xuXHRcdFx0aWYgKGVsZW1lbnQgaW5zdGFuY2VvZiBCdWZmZXIpIHtcblx0XHRcdFx0YnVmZmVyID0gZWxlbWVudDtcblx0XHRcdH0gZWxzZSBpZiAoQXJyYXlCdWZmZXIuaXNWaWV3KGVsZW1lbnQpKSB7XG5cdFx0XHRcdGJ1ZmZlciA9IEJ1ZmZlci5mcm9tKGVsZW1lbnQuYnVmZmVyLCBlbGVtZW50LmJ5dGVPZmZzZXQsIGVsZW1lbnQuYnl0ZUxlbmd0aCk7XG5cdFx0XHR9IGVsc2UgaWYgKGVsZW1lbnQgaW5zdGFuY2VvZiBBcnJheUJ1ZmZlcikge1xuXHRcdFx0XHRidWZmZXIgPSBCdWZmZXIuZnJvbShlbGVtZW50KTtcblx0XHRcdH0gZWxzZSBpZiAoZWxlbWVudCBpbnN0YW5jZW9mIEJsb2IpIHtcblx0XHRcdFx0YnVmZmVyID0gZWxlbWVudDtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGJ1ZmZlciA9IEJ1ZmZlci5mcm9tKHR5cGVvZiBlbGVtZW50ID09PSAnc3RyaW5nJyA/IGVsZW1lbnQgOiBTdHJpbmcoZWxlbWVudCkpO1xuXHRcdFx0fVxuXG5cdFx0XHRzaXplICs9IGJ1ZmZlci5sZW5ndGggfHwgYnVmZmVyLnNpemUgfHwgMDtcblx0XHRcdHJldHVybiBidWZmZXI7XG5cdFx0fSk7XG5cblx0XHRjb25zdCB0eXBlID0gb3B0aW9ucy50eXBlID09PSB1bmRlZmluZWQgPyAnJyA6IFN0cmluZyhvcHRpb25zLnR5cGUpLnRvTG93ZXJDYXNlKCk7XG5cblx0XHR3bS5zZXQodGhpcywge1xuXHRcdFx0dHlwZTogL1teXFx1MDAyMC1cXHUwMDdFXS8udGVzdCh0eXBlKSA/ICcnIDogdHlwZSxcblx0XHRcdHNpemUsXG5cdFx0XHRwYXJ0c1xuXHRcdH0pO1xuXHR9XG5cblx0LyoqXG5cdCAqIFRoZSBCbG9iIGludGVyZmFjZSdzIHNpemUgcHJvcGVydHkgcmV0dXJucyB0aGVcblx0ICogc2l6ZSBvZiB0aGUgQmxvYiBpbiBieXRlcy5cblx0ICovXG5cdGdldCBzaXplKCkge1xuXHRcdHJldHVybiB3bS5nZXQodGhpcykuc2l6ZTtcblx0fVxuXG5cdC8qKlxuXHQgKiBUaGUgdHlwZSBwcm9wZXJ0eSBvZiBhIEJsb2Igb2JqZWN0IHJldHVybnMgdGhlIE1JTUUgdHlwZSBvZiB0aGUgZmlsZS5cblx0ICovXG5cdGdldCB0eXBlKCkge1xuXHRcdHJldHVybiB3bS5nZXQodGhpcykudHlwZTtcblx0fVxuXG5cdC8qKlxuXHQgKiBUaGUgdGV4dCgpIG1ldGhvZCBpbiB0aGUgQmxvYiBpbnRlcmZhY2UgcmV0dXJucyBhIFByb21pc2Vcblx0ICogdGhhdCByZXNvbHZlcyB3aXRoIGEgc3RyaW5nIGNvbnRhaW5pbmcgdGhlIGNvbnRlbnRzIG9mXG5cdCAqIHRoZSBibG9iLCBpbnRlcnByZXRlZCBhcyBVVEYtOC5cblx0ICpcblx0ICogQHJldHVybiB7UHJvbWlzZTxzdHJpbmc+fVxuXHQgKi9cblx0YXN5bmMgdGV4dCgpIHtcblx0XHRyZXR1cm4gQnVmZmVyLmZyb20oYXdhaXQgdGhpcy5hcnJheUJ1ZmZlcigpKS50b1N0cmluZygpO1xuXHR9XG5cblx0LyoqXG5cdCAqIFRoZSBhcnJheUJ1ZmZlcigpIG1ldGhvZCBpbiB0aGUgQmxvYiBpbnRlcmZhY2UgcmV0dXJucyBhXG5cdCAqIFByb21pc2UgdGhhdCByZXNvbHZlcyB3aXRoIHRoZSBjb250ZW50cyBvZiB0aGUgYmxvYiBhc1xuXHQgKiBiaW5hcnkgZGF0YSBjb250YWluZWQgaW4gYW4gQXJyYXlCdWZmZXIuXG5cdCAqXG5cdCAqIEByZXR1cm4ge1Byb21pc2U8QXJyYXlCdWZmZXI+fVxuXHQgKi9cblx0YXN5bmMgYXJyYXlCdWZmZXIoKSB7XG5cdFx0Y29uc3QgZGF0YSA9IG5ldyBVaW50OEFycmF5KHRoaXMuc2l6ZSk7XG5cdFx0bGV0IG9mZnNldCA9IDA7XG5cdFx0Zm9yIGF3YWl0IChjb25zdCBjaHVuayBvZiB0aGlzLnN0cmVhbSgpKSB7XG5cdFx0XHRkYXRhLnNldChjaHVuaywgb2Zmc2V0KTtcblx0XHRcdG9mZnNldCArPSBjaHVuay5sZW5ndGg7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIGRhdGEuYnVmZmVyO1xuXHR9XG5cblx0LyoqXG5cdCAqIFRoZSBCbG9iIGludGVyZmFjZSdzIHN0cmVhbSgpIG1ldGhvZCBpcyBkaWZmZXJlbmNlIGZyb20gbmF0aXZlXG5cdCAqIGFuZCB1c2VzIG5vZGUgc3RyZWFtcyBpbnN0ZWFkIG9mIHdoYXR3ZyBzdHJlYW1zLlxuXHQgKlxuXHQgKiBAcmV0dXJucyB7UmVhZGFibGV9IE5vZGUgcmVhZGFibGUgc3RyZWFtXG5cdCAqL1xuXHRzdHJlYW0oKSB7XG5cdFx0cmV0dXJuIFJlYWRhYmxlLmZyb20ocmVhZCh3bS5nZXQodGhpcykucGFydHMpKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBUaGUgQmxvYiBpbnRlcmZhY2UncyBzbGljZSgpIG1ldGhvZCBjcmVhdGVzIGFuZCByZXR1cm5zIGFcblx0ICogbmV3IEJsb2Igb2JqZWN0IHdoaWNoIGNvbnRhaW5zIGRhdGEgZnJvbSBhIHN1YnNldCBvZiB0aGVcblx0ICogYmxvYiBvbiB3aGljaCBpdCdzIGNhbGxlZC5cblx0ICpcblx0ICogQHBhcmFtIHtudW1iZXJ9IFtzdGFydF1cblx0ICogQHBhcmFtIHtudW1iZXJ9IFtlbmRdXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSBbdHlwZV1cblx0ICovXG5cdHNsaWNlKHN0YXJ0ID0gMCwgZW5kID0gdGhpcy5zaXplLCB0eXBlID0gJycpIHtcblx0XHRjb25zdCB7c2l6ZX0gPSB0aGlzO1xuXG5cdFx0bGV0IHJlbGF0aXZlU3RhcnQgPSBzdGFydCA8IDAgPyBNYXRoLm1heChzaXplICsgc3RhcnQsIDApIDogTWF0aC5taW4oc3RhcnQsIHNpemUpO1xuXHRcdGxldCByZWxhdGl2ZUVuZCA9IGVuZCA8IDAgPyBNYXRoLm1heChzaXplICsgZW5kLCAwKSA6IE1hdGgubWluKGVuZCwgc2l6ZSk7XG5cblx0XHRjb25zdCBzcGFuID0gTWF0aC5tYXgocmVsYXRpdmVFbmQgLSByZWxhdGl2ZVN0YXJ0LCAwKTtcblx0XHRjb25zdCBwYXJ0cyA9IHdtLmdldCh0aGlzKS5wYXJ0cy52YWx1ZXMoKTtcblx0XHRjb25zdCBibG9iUGFydHMgPSBbXTtcblx0XHRsZXQgYWRkZWQgPSAwO1xuXG5cdFx0Zm9yIChjb25zdCBwYXJ0IG9mIHBhcnRzKSB7XG5cdFx0XHRjb25zdCBzaXplID0gQXJyYXlCdWZmZXIuaXNWaWV3KHBhcnQpID8gcGFydC5ieXRlTGVuZ3RoIDogcGFydC5zaXplO1xuXHRcdFx0aWYgKHJlbGF0aXZlU3RhcnQgJiYgc2l6ZSA8PSByZWxhdGl2ZVN0YXJ0KSB7XG5cdFx0XHRcdC8vIFNraXAgdGhlIGJlZ2lubmluZyBhbmQgY2hhbmdlIHRoZSByZWxhdGl2ZVxuXHRcdFx0XHQvLyBzdGFydCAmIGVuZCBwb3NpdGlvbiBhcyB3ZSBza2lwIHRoZSB1bndhbnRlZCBwYXJ0c1xuXHRcdFx0XHRyZWxhdGl2ZVN0YXJ0IC09IHNpemU7XG5cdFx0XHRcdHJlbGF0aXZlRW5kIC09IHNpemU7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRjb25zdCBjaHVuayA9IHBhcnQuc2xpY2UocmVsYXRpdmVTdGFydCwgTWF0aC5taW4oc2l6ZSwgcmVsYXRpdmVFbmQpKTtcblx0XHRcdFx0YmxvYlBhcnRzLnB1c2goY2h1bmspO1xuXHRcdFx0XHRhZGRlZCArPSBBcnJheUJ1ZmZlci5pc1ZpZXcoY2h1bmspID8gY2h1bmsuYnl0ZUxlbmd0aCA6IGNodW5rLnNpemU7XG5cdFx0XHRcdHJlbGF0aXZlU3RhcnQgPSAwOyAvLyBBbGwgbmV4dCBzZXF1ZW50YWwgcGFydHMgc2hvdWxkIHN0YXJ0IGF0IDBcblxuXHRcdFx0XHQvLyBkb24ndCBhZGQgdGhlIG92ZXJmbG93IHRvIG5ldyBibG9iUGFydHNcblx0XHRcdFx0aWYgKGFkZGVkID49IHNwYW4pIHtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGNvbnN0IGJsb2IgPSBuZXcgQmxvYihbXSwge3R5cGV9KTtcblx0XHRPYmplY3QuYXNzaWduKHdtLmdldChibG9iKSwge3NpemU6IHNwYW4sIHBhcnRzOiBibG9iUGFydHN9KTtcblxuXHRcdHJldHVybiBibG9iO1xuXHR9XG5cblx0Z2V0IFtTeW1ib2wudG9TdHJpbmdUYWddKCkge1xuXHRcdHJldHVybiAnQmxvYic7XG5cdH1cblxuXHRzdGF0aWMgW1N5bWJvbC5oYXNJbnN0YW5jZV0ob2JqZWN0KSB7XG5cdFx0cmV0dXJuIChcblx0XHRcdHR5cGVvZiBvYmplY3QgPT09ICdvYmplY3QnICYmXG5cdFx0XHR0eXBlb2Ygb2JqZWN0LnN0cmVhbSA9PT0gJ2Z1bmN0aW9uJyAmJlxuXHRcdFx0b2JqZWN0LnN0cmVhbS5sZW5ndGggPT09IDAgJiZcblx0XHRcdHR5cGVvZiBvYmplY3QuY29uc3RydWN0b3IgPT09ICdmdW5jdGlvbicgJiZcblx0XHRcdC9eKEJsb2J8RmlsZSkkLy50ZXN0KG9iamVjdFtTeW1ib2wudG9TdHJpbmdUYWddKVxuXHRcdCk7XG5cdH1cbn1cblxuT2JqZWN0LmRlZmluZVByb3BlcnRpZXMoQmxvYi5wcm90b3R5cGUsIHtcblx0c2l6ZToge2VudW1lcmFibGU6IHRydWV9LFxuXHR0eXBlOiB7ZW51bWVyYWJsZTogdHJ1ZX0sXG5cdHNsaWNlOiB7ZW51bWVyYWJsZTogdHJ1ZX1cbn0pO1xuXG5tb2R1bGUuZXhwb3J0cyA9IEJsb2I7XG4iLCIndXNlIHN0cmljdCc7XG5jb25zdCBmZXRjaCA9IHJlcXVpcmUoJ25vZGUtZmV0Y2gnKTtcbmNvbnN0IEFib3J0Q29udHJvbGxlciA9IHJlcXVpcmUoJ2Fib3J0LWNvbnRyb2xsZXInKTtcblxuY29uc3QgVEVOX01FR0FCWVRFUyA9IDEwMDAgKiAxMDAwICogMTA7XG5cbmlmICghZ2xvYmFsLmZldGNoKSB7XG5cdGdsb2JhbC5mZXRjaCA9ICh1cmwsIG9wdGlvbnMpID0+IGZldGNoKHVybCwge2hpZ2hXYXRlck1hcms6IFRFTl9NRUdBQllURVMsIC4uLm9wdGlvbnN9KTtcbn1cblxuaWYgKCFnbG9iYWwuSGVhZGVycykge1xuXHRnbG9iYWwuSGVhZGVycyA9IGZldGNoLkhlYWRlcnM7XG59XG5cbmlmICghZ2xvYmFsLlJlcXVlc3QpIHtcblx0Z2xvYmFsLlJlcXVlc3QgPSBmZXRjaC5SZXF1ZXN0O1xufVxuXG5pZiAoIWdsb2JhbC5SZXNwb25zZSkge1xuXHRnbG9iYWwuUmVzcG9uc2UgPSBmZXRjaC5SZXNwb25zZTtcbn1cblxuaWYgKCFnbG9iYWwuQWJvcnRDb250cm9sbGVyKSB7XG5cdGdsb2JhbC5BYm9ydENvbnRyb2xsZXIgPSBBYm9ydENvbnRyb2xsZXI7XG59XG5cbmlmICghZ2xvYmFsLlJlYWRhYmxlU3RyZWFtKSB7XG5cdHRyeSB7XG5cdFx0Z2xvYmFsLlJlYWRhYmxlU3RyZWFtID0gcmVxdWlyZSgnd2ViLXN0cmVhbXMtcG9seWZpbGwvcG9ueWZpbGwvZXMyMDE4Jyk7XG5cdH0gY2F0Y2ggKF8pIHt9XG59XG5cbm1vZHVsZS5leHBvcnRzID0gcmVxdWlyZSgna3kvdW1kJyk7XG4iLCIoZnVuY3Rpb24gKGdsb2JhbCwgZmFjdG9yeSkge1xuXHR0eXBlb2YgZXhwb3J0cyA9PT0gJ29iamVjdCcgJiYgdHlwZW9mIG1vZHVsZSAhPT0gJ3VuZGVmaW5lZCcgPyBtb2R1bGUuZXhwb3J0cyA9IGZhY3RvcnkoKSA6XG5cdHR5cGVvZiBkZWZpbmUgPT09ICdmdW5jdGlvbicgJiYgZGVmaW5lLmFtZCA/IGRlZmluZShmYWN0b3J5KSA6XG5cdChnbG9iYWwgPSB0eXBlb2YgZ2xvYmFsVGhpcyAhPT0gJ3VuZGVmaW5lZCcgPyBnbG9iYWxUaGlzIDogZ2xvYmFsIHx8IHNlbGYsIGdsb2JhbC5reSA9IGZhY3RvcnkoKSk7XG59KHRoaXMsIChmdW5jdGlvbiAoKSB7ICd1c2Ugc3RyaWN0JztcblxuXHQvKiEgTUlUIExpY2Vuc2UgwqkgU2luZHJlIFNvcmh1cyAqL1xuXG5cdGNvbnN0IGdsb2JhbHMgPSB7fTtcblxuXHRjb25zdCBnZXRHbG9iYWwgPSBwcm9wZXJ0eSA9PiB7XG5cdFx0LyogaXN0YW5idWwgaWdub3JlIG5leHQgKi9cblx0XHRpZiAodHlwZW9mIHNlbGYgIT09ICd1bmRlZmluZWQnICYmIHNlbGYgJiYgcHJvcGVydHkgaW4gc2VsZikge1xuXHRcdFx0cmV0dXJuIHNlbGY7XG5cdFx0fVxuXG5cdFx0LyogaXN0YW5idWwgaWdub3JlIG5leHQgKi9cblx0XHRpZiAodHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcgJiYgd2luZG93ICYmIHByb3BlcnR5IGluIHdpbmRvdykge1xuXHRcdFx0cmV0dXJuIHdpbmRvdztcblx0XHR9XG5cblx0XHRpZiAodHlwZW9mIGdsb2JhbCAhPT0gJ3VuZGVmaW5lZCcgJiYgZ2xvYmFsICYmIHByb3BlcnR5IGluIGdsb2JhbCkge1xuXHRcdFx0cmV0dXJuIGdsb2JhbDtcblx0XHR9XG5cblx0XHQvKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dCAqL1xuXHRcdGlmICh0eXBlb2YgZ2xvYmFsVGhpcyAhPT0gJ3VuZGVmaW5lZCcgJiYgZ2xvYmFsVGhpcykge1xuXHRcdFx0cmV0dXJuIGdsb2JhbFRoaXM7XG5cdFx0fVxuXHR9O1xuXG5cdGNvbnN0IGdsb2JhbFByb3BlcnRpZXMgPSBbXG5cdFx0J0hlYWRlcnMnLFxuXHRcdCdSZXF1ZXN0Jyxcblx0XHQnUmVzcG9uc2UnLFxuXHRcdCdSZWFkYWJsZVN0cmVhbScsXG5cdFx0J2ZldGNoJyxcblx0XHQnQWJvcnRDb250cm9sbGVyJyxcblx0XHQnRm9ybURhdGEnXG5cdF07XG5cblx0Zm9yIChjb25zdCBwcm9wZXJ0eSBvZiBnbG9iYWxQcm9wZXJ0aWVzKSB7XG5cdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGdsb2JhbHMsIHByb3BlcnR5LCB7XG5cdFx0XHRnZXQoKSB7XG5cdFx0XHRcdGNvbnN0IGdsb2JhbE9iamVjdCA9IGdldEdsb2JhbChwcm9wZXJ0eSk7XG5cdFx0XHRcdGNvbnN0IHZhbHVlID0gZ2xvYmFsT2JqZWN0ICYmIGdsb2JhbE9iamVjdFtwcm9wZXJ0eV07XG5cdFx0XHRcdHJldHVybiB0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbicgPyB2YWx1ZS5iaW5kKGdsb2JhbE9iamVjdCkgOiB2YWx1ZTtcblx0XHRcdH1cblx0XHR9KTtcblx0fVxuXG5cdGNvbnN0IGlzT2JqZWN0ID0gdmFsdWUgPT4gdmFsdWUgIT09IG51bGwgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0Jztcblx0Y29uc3Qgc3VwcG9ydHNBYm9ydENvbnRyb2xsZXIgPSB0eXBlb2YgZ2xvYmFscy5BYm9ydENvbnRyb2xsZXIgPT09ICdmdW5jdGlvbic7XG5cdGNvbnN0IHN1cHBvcnRzU3RyZWFtcyA9IHR5cGVvZiBnbG9iYWxzLlJlYWRhYmxlU3RyZWFtID09PSAnZnVuY3Rpb24nO1xuXHRjb25zdCBzdXBwb3J0c0Zvcm1EYXRhID0gdHlwZW9mIGdsb2JhbHMuRm9ybURhdGEgPT09ICdmdW5jdGlvbic7XG5cblx0Y29uc3QgbWVyZ2VIZWFkZXJzID0gKHNvdXJjZTEsIHNvdXJjZTIpID0+IHtcblx0XHRjb25zdCByZXN1bHQgPSBuZXcgZ2xvYmFscy5IZWFkZXJzKHNvdXJjZTEgfHwge30pO1xuXHRcdGNvbnN0IGlzSGVhZGVyc0luc3RhbmNlID0gc291cmNlMiBpbnN0YW5jZW9mIGdsb2JhbHMuSGVhZGVycztcblx0XHRjb25zdCBzb3VyY2UgPSBuZXcgZ2xvYmFscy5IZWFkZXJzKHNvdXJjZTIgfHwge30pO1xuXG5cdFx0Zm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2Ygc291cmNlKSB7XG5cdFx0XHRpZiAoKGlzSGVhZGVyc0luc3RhbmNlICYmIHZhbHVlID09PSAndW5kZWZpbmVkJykgfHwgdmFsdWUgPT09IHVuZGVmaW5lZCkge1xuXHRcdFx0XHRyZXN1bHQuZGVsZXRlKGtleSk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRyZXN1bHQuc2V0KGtleSwgdmFsdWUpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHJldHVybiByZXN1bHQ7XG5cdH07XG5cblx0Y29uc3QgZGVlcE1lcmdlID0gKC4uLnNvdXJjZXMpID0+IHtcblx0XHRsZXQgcmV0dXJuVmFsdWUgPSB7fTtcblx0XHRsZXQgaGVhZGVycyA9IHt9O1xuXG5cdFx0Zm9yIChjb25zdCBzb3VyY2Ugb2Ygc291cmNlcykge1xuXHRcdFx0aWYgKEFycmF5LmlzQXJyYXkoc291cmNlKSkge1xuXHRcdFx0XHRpZiAoIShBcnJheS5pc0FycmF5KHJldHVyblZhbHVlKSkpIHtcblx0XHRcdFx0XHRyZXR1cm5WYWx1ZSA9IFtdO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmV0dXJuVmFsdWUgPSBbLi4ucmV0dXJuVmFsdWUsIC4uLnNvdXJjZV07XG5cdFx0XHR9IGVsc2UgaWYgKGlzT2JqZWN0KHNvdXJjZSkpIHtcblx0XHRcdFx0Zm9yIChsZXQgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKHNvdXJjZSkpIHtcblx0XHRcdFx0XHRpZiAoaXNPYmplY3QodmFsdWUpICYmIChrZXkgaW4gcmV0dXJuVmFsdWUpKSB7XG5cdFx0XHRcdFx0XHR2YWx1ZSA9IGRlZXBNZXJnZShyZXR1cm5WYWx1ZVtrZXldLCB2YWx1ZSk7XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0cmV0dXJuVmFsdWUgPSB7Li4ucmV0dXJuVmFsdWUsIFtrZXldOiB2YWx1ZX07XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoaXNPYmplY3Qoc291cmNlLmhlYWRlcnMpKSB7XG5cdFx0XHRcdFx0aGVhZGVycyA9IG1lcmdlSGVhZGVycyhoZWFkZXJzLCBzb3VyY2UuaGVhZGVycyk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuVmFsdWUuaGVhZGVycyA9IGhlYWRlcnM7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHJldHVyblZhbHVlO1xuXHR9O1xuXG5cdGNvbnN0IHJlcXVlc3RNZXRob2RzID0gW1xuXHRcdCdnZXQnLFxuXHRcdCdwb3N0Jyxcblx0XHQncHV0Jyxcblx0XHQncGF0Y2gnLFxuXHRcdCdoZWFkJyxcblx0XHQnZGVsZXRlJ1xuXHRdO1xuXG5cdGNvbnN0IHJlc3BvbnNlVHlwZXMgPSB7XG5cdFx0anNvbjogJ2FwcGxpY2F0aW9uL2pzb24nLFxuXHRcdHRleHQ6ICd0ZXh0LyonLFxuXHRcdGZvcm1EYXRhOiAnbXVsdGlwYXJ0L2Zvcm0tZGF0YScsXG5cdFx0YXJyYXlCdWZmZXI6ICcqLyonLFxuXHRcdGJsb2I6ICcqLyonXG5cdH07XG5cblx0Y29uc3QgcmV0cnlNZXRob2RzID0gW1xuXHRcdCdnZXQnLFxuXHRcdCdwdXQnLFxuXHRcdCdoZWFkJyxcblx0XHQnZGVsZXRlJyxcblx0XHQnb3B0aW9ucycsXG5cdFx0J3RyYWNlJ1xuXHRdO1xuXG5cdGNvbnN0IHJldHJ5U3RhdHVzQ29kZXMgPSBbXG5cdFx0NDA4LFxuXHRcdDQxMyxcblx0XHQ0MjksXG5cdFx0NTAwLFxuXHRcdDUwMixcblx0XHQ1MDMsXG5cdFx0NTA0XG5cdF07XG5cblx0Y29uc3QgcmV0cnlBZnRlclN0YXR1c0NvZGVzID0gW1xuXHRcdDQxMyxcblx0XHQ0MjksXG5cdFx0NTAzXG5cdF07XG5cblx0Y29uc3Qgc3RvcCA9IFN5bWJvbCgnc3RvcCcpO1xuXG5cdGNsYXNzIEhUVFBFcnJvciBleHRlbmRzIEVycm9yIHtcblx0XHRjb25zdHJ1Y3RvcihyZXNwb25zZSkge1xuXHRcdFx0Ly8gU2V0IHRoZSBtZXNzYWdlIHRvIHRoZSBzdGF0dXMgdGV4dCwgc3VjaCBhcyBVbmF1dGhvcml6ZWQsXG5cdFx0XHQvLyB3aXRoIHNvbWUgZmFsbGJhY2tzLiBUaGlzIG1lc3NhZ2Ugc2hvdWxkIG5ldmVyIGJlIHVuZGVmaW5lZC5cblx0XHRcdHN1cGVyKFxuXHRcdFx0XHRyZXNwb25zZS5zdGF0dXNUZXh0IHx8XG5cdFx0XHRcdFN0cmluZyhcblx0XHRcdFx0XHQocmVzcG9uc2Uuc3RhdHVzID09PSAwIHx8IHJlc3BvbnNlLnN0YXR1cykgP1xuXHRcdFx0XHRcdFx0cmVzcG9uc2Uuc3RhdHVzIDogJ1Vua25vd24gcmVzcG9uc2UgZXJyb3InXG5cdFx0XHRcdClcblx0XHRcdCk7XG5cdFx0XHR0aGlzLm5hbWUgPSAnSFRUUEVycm9yJztcblx0XHRcdHRoaXMucmVzcG9uc2UgPSByZXNwb25zZTtcblx0XHR9XG5cdH1cblxuXHRjbGFzcyBUaW1lb3V0RXJyb3IgZXh0ZW5kcyBFcnJvciB7XG5cdFx0Y29uc3RydWN0b3IocmVxdWVzdCkge1xuXHRcdFx0c3VwZXIoJ1JlcXVlc3QgdGltZWQgb3V0Jyk7XG5cdFx0XHR0aGlzLm5hbWUgPSAnVGltZW91dEVycm9yJztcblx0XHRcdHRoaXMucmVxdWVzdCA9IHJlcXVlc3Q7XG5cdFx0fVxuXHR9XG5cblx0Y29uc3QgZGVsYXkgPSBtcyA9PiBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgbXMpKTtcblxuXHQvLyBgUHJvbWlzZS5yYWNlKClgIHdvcmthcm91bmQgKCM5MSlcblx0Y29uc3QgdGltZW91dCA9IChyZXF1ZXN0LCBhYm9ydENvbnRyb2xsZXIsIG9wdGlvbnMpID0+XG5cdFx0bmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuXHRcdFx0Y29uc3QgdGltZW91dElEID0gc2V0VGltZW91dCgoKSA9PiB7XG5cdFx0XHRcdGlmIChhYm9ydENvbnRyb2xsZXIpIHtcblx0XHRcdFx0XHRhYm9ydENvbnRyb2xsZXIuYWJvcnQoKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdHJlamVjdChuZXcgVGltZW91dEVycm9yKHJlcXVlc3QpKTtcblx0XHRcdH0sIG9wdGlvbnMudGltZW91dCk7XG5cblx0XHRcdC8qIGVzbGludC1kaXNhYmxlIHByb21pc2UvcHJlZmVyLWF3YWl0LXRvLXRoZW4gKi9cblx0XHRcdG9wdGlvbnMuZmV0Y2gocmVxdWVzdClcblx0XHRcdFx0LnRoZW4ocmVzb2x2ZSlcblx0XHRcdFx0LmNhdGNoKHJlamVjdClcblx0XHRcdFx0LnRoZW4oKCkgPT4ge1xuXHRcdFx0XHRcdGNsZWFyVGltZW91dCh0aW1lb3V0SUQpO1xuXHRcdFx0XHR9KTtcblx0XHRcdC8qIGVzbGludC1lbmFibGUgcHJvbWlzZS9wcmVmZXItYXdhaXQtdG8tdGhlbiAqL1xuXHRcdH0pO1xuXG5cdGNvbnN0IG5vcm1hbGl6ZVJlcXVlc3RNZXRob2QgPSBpbnB1dCA9PiByZXF1ZXN0TWV0aG9kcy5pbmNsdWRlcyhpbnB1dCkgPyBpbnB1dC50b1VwcGVyQ2FzZSgpIDogaW5wdXQ7XG5cblx0Y29uc3QgZGVmYXVsdFJldHJ5T3B0aW9ucyA9IHtcblx0XHRsaW1pdDogMixcblx0XHRtZXRob2RzOiByZXRyeU1ldGhvZHMsXG5cdFx0c3RhdHVzQ29kZXM6IHJldHJ5U3RhdHVzQ29kZXMsXG5cdFx0YWZ0ZXJTdGF0dXNDb2RlczogcmV0cnlBZnRlclN0YXR1c0NvZGVzXG5cdH07XG5cblx0Y29uc3Qgbm9ybWFsaXplUmV0cnlPcHRpb25zID0gKHJldHJ5ID0ge30pID0+IHtcblx0XHRpZiAodHlwZW9mIHJldHJ5ID09PSAnbnVtYmVyJykge1xuXHRcdFx0cmV0dXJuIHtcblx0XHRcdFx0Li4uZGVmYXVsdFJldHJ5T3B0aW9ucyxcblx0XHRcdFx0bGltaXQ6IHJldHJ5XG5cdFx0XHR9O1xuXHRcdH1cblxuXHRcdGlmIChyZXRyeS5tZXRob2RzICYmICFBcnJheS5pc0FycmF5KHJldHJ5Lm1ldGhvZHMpKSB7XG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ3JldHJ5Lm1ldGhvZHMgbXVzdCBiZSBhbiBhcnJheScpO1xuXHRcdH1cblxuXHRcdGlmIChyZXRyeS5zdGF0dXNDb2RlcyAmJiAhQXJyYXkuaXNBcnJheShyZXRyeS5zdGF0dXNDb2RlcykpIHtcblx0XHRcdHRocm93IG5ldyBFcnJvcigncmV0cnkuc3RhdHVzQ29kZXMgbXVzdCBiZSBhbiBhcnJheScpO1xuXHRcdH1cblxuXHRcdHJldHVybiB7XG5cdFx0XHQuLi5kZWZhdWx0UmV0cnlPcHRpb25zLFxuXHRcdFx0Li4ucmV0cnksXG5cdFx0XHRhZnRlclN0YXR1c0NvZGVzOiByZXRyeUFmdGVyU3RhdHVzQ29kZXNcblx0XHR9O1xuXHR9O1xuXG5cdC8vIFRoZSBtYXhpbXVtIHZhbHVlIG9mIGEgMzJiaXQgaW50IChzZWUgaXNzdWUgIzExNylcblx0Y29uc3QgbWF4U2FmZVRpbWVvdXQgPSAyMTQ3NDgzNjQ3O1xuXG5cdGNsYXNzIEt5IHtcblx0XHRjb25zdHJ1Y3RvcihpbnB1dCwgb3B0aW9ucyA9IHt9KSB7XG5cdFx0XHR0aGlzLl9yZXRyeUNvdW50ID0gMDtcblx0XHRcdHRoaXMuX2lucHV0ID0gaW5wdXQ7XG5cdFx0XHR0aGlzLl9vcHRpb25zID0ge1xuXHRcdFx0XHQvLyBUT0RPOiBjcmVkZW50aWFscyBjYW4gYmUgcmVtb3ZlZCB3aGVuIHRoZSBzcGVjIGNoYW5nZSBpcyBpbXBsZW1lbnRlZCBpbiBhbGwgYnJvd3NlcnMuIENvbnRleHQ6IGh0dHBzOi8vd3d3LmNocm9tZXN0YXR1cy5jb20vZmVhdHVyZS80NTM5NDczMzEyMzUwMjA4XG5cdFx0XHRcdGNyZWRlbnRpYWxzOiB0aGlzLl9pbnB1dC5jcmVkZW50aWFscyB8fCAnc2FtZS1vcmlnaW4nLFxuXHRcdFx0XHQuLi5vcHRpb25zLFxuXHRcdFx0XHRoZWFkZXJzOiBtZXJnZUhlYWRlcnModGhpcy5faW5wdXQuaGVhZGVycywgb3B0aW9ucy5oZWFkZXJzKSxcblx0XHRcdFx0aG9va3M6IGRlZXBNZXJnZSh7XG5cdFx0XHRcdFx0YmVmb3JlUmVxdWVzdDogW10sXG5cdFx0XHRcdFx0YmVmb3JlUmV0cnk6IFtdLFxuXHRcdFx0XHRcdGFmdGVyUmVzcG9uc2U6IFtdXG5cdFx0XHRcdH0sIG9wdGlvbnMuaG9va3MpLFxuXHRcdFx0XHRtZXRob2Q6IG5vcm1hbGl6ZVJlcXVlc3RNZXRob2Qob3B0aW9ucy5tZXRob2QgfHwgdGhpcy5faW5wdXQubWV0aG9kKSxcblx0XHRcdFx0cHJlZml4VXJsOiBTdHJpbmcob3B0aW9ucy5wcmVmaXhVcmwgfHwgJycpLFxuXHRcdFx0XHRyZXRyeTogbm9ybWFsaXplUmV0cnlPcHRpb25zKG9wdGlvbnMucmV0cnkpLFxuXHRcdFx0XHR0aHJvd0h0dHBFcnJvcnM6IG9wdGlvbnMudGhyb3dIdHRwRXJyb3JzICE9PSBmYWxzZSxcblx0XHRcdFx0dGltZW91dDogdHlwZW9mIG9wdGlvbnMudGltZW91dCA9PT0gJ3VuZGVmaW5lZCcgPyAxMDAwMCA6IG9wdGlvbnMudGltZW91dCxcblx0XHRcdFx0ZmV0Y2g6IG9wdGlvbnMuZmV0Y2ggfHwgZ2xvYmFscy5mZXRjaFxuXHRcdFx0fTtcblxuXHRcdFx0aWYgKHR5cGVvZiB0aGlzLl9pbnB1dCAhPT0gJ3N0cmluZycgJiYgISh0aGlzLl9pbnB1dCBpbnN0YW5jZW9mIFVSTCB8fCB0aGlzLl9pbnB1dCBpbnN0YW5jZW9mIGdsb2JhbHMuUmVxdWVzdCkpIHtcblx0XHRcdFx0dGhyb3cgbmV3IFR5cGVFcnJvcignYGlucHV0YCBtdXN0IGJlIGEgc3RyaW5nLCBVUkwsIG9yIFJlcXVlc3QnKTtcblx0XHRcdH1cblxuXHRcdFx0aWYgKHRoaXMuX29wdGlvbnMucHJlZml4VXJsICYmIHR5cGVvZiB0aGlzLl9pbnB1dCA9PT0gJ3N0cmluZycpIHtcblx0XHRcdFx0aWYgKHRoaXMuX2lucHV0LnN0YXJ0c1dpdGgoJy8nKSkge1xuXHRcdFx0XHRcdHRocm93IG5ldyBFcnJvcignYGlucHV0YCBtdXN0IG5vdCBiZWdpbiB3aXRoIGEgc2xhc2ggd2hlbiB1c2luZyBgcHJlZml4VXJsYCcpO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCF0aGlzLl9vcHRpb25zLnByZWZpeFVybC5lbmRzV2l0aCgnLycpKSB7XG5cdFx0XHRcdFx0dGhpcy5fb3B0aW9ucy5wcmVmaXhVcmwgKz0gJy8nO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0dGhpcy5faW5wdXQgPSB0aGlzLl9vcHRpb25zLnByZWZpeFVybCArIHRoaXMuX2lucHV0O1xuXHRcdFx0fVxuXG5cdFx0XHRpZiAoc3VwcG9ydHNBYm9ydENvbnRyb2xsZXIpIHtcblx0XHRcdFx0dGhpcy5hYm9ydENvbnRyb2xsZXIgPSBuZXcgZ2xvYmFscy5BYm9ydENvbnRyb2xsZXIoKTtcblx0XHRcdFx0aWYgKHRoaXMuX29wdGlvbnMuc2lnbmFsKSB7XG5cdFx0XHRcdFx0dGhpcy5fb3B0aW9ucy5zaWduYWwuYWRkRXZlbnRMaXN0ZW5lcignYWJvcnQnLCAoKSA9PiB7XG5cdFx0XHRcdFx0XHR0aGlzLmFib3J0Q29udHJvbGxlci5hYm9ydCgpO1xuXHRcdFx0XHRcdH0pO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0dGhpcy5fb3B0aW9ucy5zaWduYWwgPSB0aGlzLmFib3J0Q29udHJvbGxlci5zaWduYWw7XG5cdFx0XHR9XG5cblx0XHRcdHRoaXMucmVxdWVzdCA9IG5ldyBnbG9iYWxzLlJlcXVlc3QodGhpcy5faW5wdXQsIHRoaXMuX29wdGlvbnMpO1xuXG5cdFx0XHRpZiAodGhpcy5fb3B0aW9ucy5zZWFyY2hQYXJhbXMpIHtcblx0XHRcdFx0Y29uc3Qgc2VhcmNoUGFyYW1zID0gJz8nICsgbmV3IFVSTFNlYXJjaFBhcmFtcyh0aGlzLl9vcHRpb25zLnNlYXJjaFBhcmFtcykudG9TdHJpbmcoKTtcblx0XHRcdFx0Y29uc3QgdXJsID0gdGhpcy5yZXF1ZXN0LnVybC5yZXBsYWNlKC8oPzpcXD8uKj8pPyg/PSN8JCkvLCBzZWFyY2hQYXJhbXMpO1xuXG5cdFx0XHRcdC8vIFRvIHByb3ZpZGUgY29ycmVjdCBmb3JtIGJvdW5kYXJ5LCBDb250ZW50LVR5cGUgaGVhZGVyIHNob3VsZCBiZSBkZWxldGVkIGVhY2ggdGltZSB3aGVuIG5ldyBSZXF1ZXN0IGluc3RhbnRpYXRlZCBmcm9tIGFub3RoZXIgb25lXG5cdFx0XHRcdGlmICgoKHN1cHBvcnRzRm9ybURhdGEgJiYgdGhpcy5fb3B0aW9ucy5ib2R5IGluc3RhbmNlb2YgZ2xvYmFscy5Gb3JtRGF0YSkgfHwgdGhpcy5fb3B0aW9ucy5ib2R5IGluc3RhbmNlb2YgVVJMU2VhcmNoUGFyYW1zKSAmJiAhKHRoaXMuX29wdGlvbnMuaGVhZGVycyAmJiB0aGlzLl9vcHRpb25zLmhlYWRlcnNbJ2NvbnRlbnQtdHlwZSddKSkge1xuXHRcdFx0XHRcdHRoaXMucmVxdWVzdC5oZWFkZXJzLmRlbGV0ZSgnY29udGVudC10eXBlJyk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHR0aGlzLnJlcXVlc3QgPSBuZXcgZ2xvYmFscy5SZXF1ZXN0KG5ldyBnbG9iYWxzLlJlcXVlc3QodXJsLCB0aGlzLnJlcXVlc3QpLCB0aGlzLl9vcHRpb25zKTtcblx0XHRcdH1cblxuXHRcdFx0aWYgKHRoaXMuX29wdGlvbnMuanNvbiAhPT0gdW5kZWZpbmVkKSB7XG5cdFx0XHRcdHRoaXMuX29wdGlvbnMuYm9keSA9IEpTT04uc3RyaW5naWZ5KHRoaXMuX29wdGlvbnMuanNvbik7XG5cdFx0XHRcdHRoaXMucmVxdWVzdC5oZWFkZXJzLnNldCgnY29udGVudC10eXBlJywgJ2FwcGxpY2F0aW9uL2pzb24nKTtcblx0XHRcdFx0dGhpcy5yZXF1ZXN0ID0gbmV3IGdsb2JhbHMuUmVxdWVzdCh0aGlzLnJlcXVlc3QsIHtib2R5OiB0aGlzLl9vcHRpb25zLmJvZHl9KTtcblx0XHRcdH1cblxuXHRcdFx0Y29uc3QgZm4gPSBhc3luYyAoKSA9PiB7XG5cdFx0XHRcdGlmICh0aGlzLl9vcHRpb25zLnRpbWVvdXQgPiBtYXhTYWZlVGltZW91dCkge1xuXHRcdFx0XHRcdHRocm93IG5ldyBSYW5nZUVycm9yKGBUaGUgXFxgdGltZW91dFxcYCBvcHRpb24gY2Fubm90IGJlIGdyZWF0ZXIgdGhhbiAke21heFNhZmVUaW1lb3V0fWApO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0YXdhaXQgZGVsYXkoMSk7XG5cdFx0XHRcdGxldCByZXNwb25zZSA9IGF3YWl0IHRoaXMuX2ZldGNoKCk7XG5cblx0XHRcdFx0Zm9yIChjb25zdCBob29rIG9mIHRoaXMuX29wdGlvbnMuaG9va3MuYWZ0ZXJSZXNwb25zZSkge1xuXHRcdFx0XHRcdC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1hd2FpdC1pbi1sb29wXG5cdFx0XHRcdFx0Y29uc3QgbW9kaWZpZWRSZXNwb25zZSA9IGF3YWl0IGhvb2soXG5cdFx0XHRcdFx0XHR0aGlzLnJlcXVlc3QsXG5cdFx0XHRcdFx0XHR0aGlzLl9vcHRpb25zLFxuXHRcdFx0XHRcdFx0dGhpcy5fZGVjb3JhdGVSZXNwb25zZShyZXNwb25zZS5jbG9uZSgpKVxuXHRcdFx0XHRcdCk7XG5cblx0XHRcdFx0XHRpZiAobW9kaWZpZWRSZXNwb25zZSBpbnN0YW5jZW9mIGdsb2JhbHMuUmVzcG9uc2UpIHtcblx0XHRcdFx0XHRcdHJlc3BvbnNlID0gbW9kaWZpZWRSZXNwb25zZTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblxuXHRcdFx0XHR0aGlzLl9kZWNvcmF0ZVJlc3BvbnNlKHJlc3BvbnNlKTtcblxuXHRcdFx0XHRpZiAoIXJlc3BvbnNlLm9rICYmIHRoaXMuX29wdGlvbnMudGhyb3dIdHRwRXJyb3JzKSB7XG5cdFx0XHRcdFx0dGhyb3cgbmV3IEhUVFBFcnJvcihyZXNwb25zZSk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBJZiBgb25Eb3dubG9hZFByb2dyZXNzYCBpcyBwYXNzZWQsIGl0IHVzZXMgdGhlIHN0cmVhbSBBUEkgaW50ZXJuYWxseVxuXHRcdFx0XHQvKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dCAqL1xuXHRcdFx0XHRpZiAodGhpcy5fb3B0aW9ucy5vbkRvd25sb2FkUHJvZ3Jlc3MpIHtcblx0XHRcdFx0XHRpZiAodHlwZW9mIHRoaXMuX29wdGlvbnMub25Eb3dubG9hZFByb2dyZXNzICE9PSAnZnVuY3Rpb24nKSB7XG5cdFx0XHRcdFx0XHR0aHJvdyBuZXcgVHlwZUVycm9yKCdUaGUgYG9uRG93bmxvYWRQcm9ncmVzc2Agb3B0aW9uIG11c3QgYmUgYSBmdW5jdGlvbicpO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdGlmICghc3VwcG9ydHNTdHJlYW1zKSB7XG5cdFx0XHRcdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ1N0cmVhbXMgYXJlIG5vdCBzdXBwb3J0ZWQgaW4geW91ciBlbnZpcm9ubWVudC4gYFJlYWRhYmxlU3RyZWFtYCBpcyBtaXNzaW5nLicpO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdHJldHVybiB0aGlzLl9zdHJlYW0ocmVzcG9uc2UuY2xvbmUoKSwgdGhpcy5fb3B0aW9ucy5vbkRvd25sb2FkUHJvZ3Jlc3MpO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmV0dXJuIHJlc3BvbnNlO1xuXHRcdFx0fTtcblxuXHRcdFx0Y29uc3QgaXNSZXRyaWFibGVNZXRob2QgPSB0aGlzLl9vcHRpb25zLnJldHJ5Lm1ldGhvZHMuaW5jbHVkZXModGhpcy5yZXF1ZXN0Lm1ldGhvZC50b0xvd2VyQ2FzZSgpKTtcblx0XHRcdGNvbnN0IHJlc3VsdCA9IGlzUmV0cmlhYmxlTWV0aG9kID8gdGhpcy5fcmV0cnkoZm4pIDogZm4oKTtcblxuXHRcdFx0Zm9yIChjb25zdCBbdHlwZSwgbWltZVR5cGVdIG9mIE9iamVjdC5lbnRyaWVzKHJlc3BvbnNlVHlwZXMpKSB7XG5cdFx0XHRcdHJlc3VsdFt0eXBlXSA9IGFzeW5jICgpID0+IHtcblx0XHRcdFx0XHR0aGlzLnJlcXVlc3QuaGVhZGVycy5zZXQoJ2FjY2VwdCcsIHRoaXMucmVxdWVzdC5oZWFkZXJzLmdldCgnYWNjZXB0JykgfHwgbWltZVR5cGUpO1xuXG5cdFx0XHRcdFx0Y29uc3QgcmVzcG9uc2UgPSAoYXdhaXQgcmVzdWx0KS5jbG9uZSgpO1xuXG5cdFx0XHRcdFx0aWYgKHR5cGUgPT09ICdqc29uJykge1xuXHRcdFx0XHRcdFx0aWYgKHJlc3BvbnNlLnN0YXR1cyA9PT0gMjA0KSB7XG5cdFx0XHRcdFx0XHRcdHJldHVybiAnJztcblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0aWYgKG9wdGlvbnMucGFyc2VKc29uKSB7XG5cdFx0XHRcdFx0XHRcdHJldHVybiBvcHRpb25zLnBhcnNlSnNvbihhd2FpdCByZXNwb25zZS50ZXh0KCkpO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdHJldHVybiByZXNwb25zZVt0eXBlXSgpO1xuXHRcdFx0XHR9O1xuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gcmVzdWx0O1xuXHRcdH1cblxuXHRcdF9jYWxjdWxhdGVSZXRyeURlbGF5KGVycm9yKSB7XG5cdFx0XHR0aGlzLl9yZXRyeUNvdW50Kys7XG5cblx0XHRcdGlmICh0aGlzLl9yZXRyeUNvdW50IDwgdGhpcy5fb3B0aW9ucy5yZXRyeS5saW1pdCAmJiAhKGVycm9yIGluc3RhbmNlb2YgVGltZW91dEVycm9yKSkge1xuXHRcdFx0XHRpZiAoZXJyb3IgaW5zdGFuY2VvZiBIVFRQRXJyb3IpIHtcblx0XHRcdFx0XHRpZiAoIXRoaXMuX29wdGlvbnMucmV0cnkuc3RhdHVzQ29kZXMuaW5jbHVkZXMoZXJyb3IucmVzcG9uc2Uuc3RhdHVzKSkge1xuXHRcdFx0XHRcdFx0cmV0dXJuIDA7XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0Y29uc3QgcmV0cnlBZnRlciA9IGVycm9yLnJlc3BvbnNlLmhlYWRlcnMuZ2V0KCdSZXRyeS1BZnRlcicpO1xuXHRcdFx0XHRcdGlmIChyZXRyeUFmdGVyICYmIHRoaXMuX29wdGlvbnMucmV0cnkuYWZ0ZXJTdGF0dXNDb2Rlcy5pbmNsdWRlcyhlcnJvci5yZXNwb25zZS5zdGF0dXMpKSB7XG5cdFx0XHRcdFx0XHRsZXQgYWZ0ZXIgPSBOdW1iZXIocmV0cnlBZnRlcik7XG5cdFx0XHRcdFx0XHRpZiAoTnVtYmVyLmlzTmFOKGFmdGVyKSkge1xuXHRcdFx0XHRcdFx0XHRhZnRlciA9IERhdGUucGFyc2UocmV0cnlBZnRlcikgLSBEYXRlLm5vdygpO1xuXHRcdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdFx0YWZ0ZXIgKj0gMTAwMDtcblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0aWYgKHR5cGVvZiB0aGlzLl9vcHRpb25zLnJldHJ5Lm1heFJldHJ5QWZ0ZXIgIT09ICd1bmRlZmluZWQnICYmIGFmdGVyID4gdGhpcy5fb3B0aW9ucy5yZXRyeS5tYXhSZXRyeUFmdGVyKSB7XG5cdFx0XHRcdFx0XHRcdHJldHVybiAwO1xuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRyZXR1cm4gYWZ0ZXI7XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0aWYgKGVycm9yLnJlc3BvbnNlLnN0YXR1cyA9PT0gNDEzKSB7XG5cdFx0XHRcdFx0XHRyZXR1cm4gMDtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRjb25zdCBCQUNLT0ZGX0ZBQ1RPUiA9IDAuMztcblx0XHRcdFx0cmV0dXJuIEJBQ0tPRkZfRkFDVE9SICogKDIgKiogKHRoaXMuX3JldHJ5Q291bnQgLSAxKSkgKiAxMDAwO1xuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gMDtcblx0XHR9XG5cblx0XHRfZGVjb3JhdGVSZXNwb25zZShyZXNwb25zZSkge1xuXHRcdFx0aWYgKHRoaXMuX29wdGlvbnMucGFyc2VKc29uKSB7XG5cdFx0XHRcdHJlc3BvbnNlLmpzb24gPSBhc3luYyAoKSA9PiB7XG5cdFx0XHRcdFx0cmV0dXJuIHRoaXMuX29wdGlvbnMucGFyc2VKc29uKGF3YWl0IHJlc3BvbnNlLnRleHQoKSk7XG5cdFx0XHRcdH07XG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiByZXNwb25zZTtcblx0XHR9XG5cblx0XHRhc3luYyBfcmV0cnkoZm4pIHtcblx0XHRcdHRyeSB7XG5cdFx0XHRcdHJldHVybiBhd2FpdCBmbigpO1xuXHRcdFx0fSBjYXRjaCAoZXJyb3IpIHtcblx0XHRcdFx0Y29uc3QgbXMgPSBNYXRoLm1pbih0aGlzLl9jYWxjdWxhdGVSZXRyeURlbGF5KGVycm9yKSwgbWF4U2FmZVRpbWVvdXQpO1xuXHRcdFx0XHRpZiAobXMgIT09IDAgJiYgdGhpcy5fcmV0cnlDb3VudCA+IDApIHtcblx0XHRcdFx0XHRhd2FpdCBkZWxheShtcyk7XG5cblx0XHRcdFx0XHRmb3IgKGNvbnN0IGhvb2sgb2YgdGhpcy5fb3B0aW9ucy5ob29rcy5iZWZvcmVSZXRyeSkge1xuXHRcdFx0XHRcdFx0Ly8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWF3YWl0LWluLWxvb3Bcblx0XHRcdFx0XHRcdGNvbnN0IGhvb2tSZXN1bHQgPSBhd2FpdCBob29rKHtcblx0XHRcdFx0XHRcdFx0cmVxdWVzdDogdGhpcy5yZXF1ZXN0LFxuXHRcdFx0XHRcdFx0XHRvcHRpb25zOiB0aGlzLl9vcHRpb25zLFxuXHRcdFx0XHRcdFx0XHRlcnJvcixcblx0XHRcdFx0XHRcdFx0cmV0cnlDb3VudDogdGhpcy5fcmV0cnlDb3VudFxuXHRcdFx0XHRcdFx0fSk7XG5cblx0XHRcdFx0XHRcdC8vIElmIGBzdG9wYCBpcyByZXR1cm5lZCBmcm9tIHRoZSBob29rLCB0aGUgcmV0cnkgcHJvY2VzcyBpcyBzdG9wcGVkXG5cdFx0XHRcdFx0XHRpZiAoaG9va1Jlc3VsdCA9PT0gc3RvcCkge1xuXHRcdFx0XHRcdFx0XHRyZXR1cm47XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0cmV0dXJuIHRoaXMuX3JldHJ5KGZuKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICh0aGlzLl9vcHRpb25zLnRocm93SHR0cEVycm9ycykge1xuXHRcdFx0XHRcdHRocm93IGVycm9yO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0YXN5bmMgX2ZldGNoKCkge1xuXHRcdFx0Zm9yIChjb25zdCBob29rIG9mIHRoaXMuX29wdGlvbnMuaG9va3MuYmVmb3JlUmVxdWVzdCkge1xuXHRcdFx0XHQvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tYXdhaXQtaW4tbG9vcFxuXHRcdFx0XHRjb25zdCByZXN1bHQgPSBhd2FpdCBob29rKHRoaXMucmVxdWVzdCwgdGhpcy5fb3B0aW9ucyk7XG5cblx0XHRcdFx0aWYgKHJlc3VsdCBpbnN0YW5jZW9mIFJlcXVlc3QpIHtcblx0XHRcdFx0XHR0aGlzLnJlcXVlc3QgPSByZXN1bHQ7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAocmVzdWx0IGluc3RhbmNlb2YgUmVzcG9uc2UpIHtcblx0XHRcdFx0XHRyZXR1cm4gcmVzdWx0O1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cblx0XHRcdGlmICh0aGlzLl9vcHRpb25zLnRpbWVvdXQgPT09IGZhbHNlKSB7XG5cdFx0XHRcdHJldHVybiB0aGlzLl9vcHRpb25zLmZldGNoKHRoaXMucmVxdWVzdC5jbG9uZSgpKTtcblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRpbWVvdXQodGhpcy5yZXF1ZXN0LmNsb25lKCksIHRoaXMuYWJvcnRDb250cm9sbGVyLCB0aGlzLl9vcHRpb25zKTtcblx0XHR9XG5cblx0XHQvKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dCAqL1xuXHRcdF9zdHJlYW0ocmVzcG9uc2UsIG9uRG93bmxvYWRQcm9ncmVzcykge1xuXHRcdFx0Y29uc3QgdG90YWxCeXRlcyA9IE51bWJlcihyZXNwb25zZS5oZWFkZXJzLmdldCgnY29udGVudC1sZW5ndGgnKSkgfHwgMDtcblx0XHRcdGxldCB0cmFuc2ZlcnJlZEJ5dGVzID0gMDtcblxuXHRcdFx0cmV0dXJuIG5ldyBnbG9iYWxzLlJlc3BvbnNlKFxuXHRcdFx0XHRuZXcgZ2xvYmFscy5SZWFkYWJsZVN0cmVhbSh7XG5cdFx0XHRcdFx0c3RhcnQoY29udHJvbGxlcikge1xuXHRcdFx0XHRcdFx0Y29uc3QgcmVhZGVyID0gcmVzcG9uc2UuYm9keS5nZXRSZWFkZXIoKTtcblxuXHRcdFx0XHRcdFx0aWYgKG9uRG93bmxvYWRQcm9ncmVzcykge1xuXHRcdFx0XHRcdFx0XHRvbkRvd25sb2FkUHJvZ3Jlc3Moe3BlcmNlbnQ6IDAsIHRyYW5zZmVycmVkQnl0ZXM6IDAsIHRvdGFsQnl0ZXN9LCBuZXcgVWludDhBcnJheSgpKTtcblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0YXN5bmMgZnVuY3Rpb24gcmVhZCgpIHtcblx0XHRcdFx0XHRcdFx0Y29uc3Qge2RvbmUsIHZhbHVlfSA9IGF3YWl0IHJlYWRlci5yZWFkKCk7XG5cdFx0XHRcdFx0XHRcdGlmIChkb25lKSB7XG5cdFx0XHRcdFx0XHRcdFx0Y29udHJvbGxlci5jbG9zZSgpO1xuXHRcdFx0XHRcdFx0XHRcdHJldHVybjtcblx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRcdGlmIChvbkRvd25sb2FkUHJvZ3Jlc3MpIHtcblx0XHRcdFx0XHRcdFx0XHR0cmFuc2ZlcnJlZEJ5dGVzICs9IHZhbHVlLmJ5dGVMZW5ndGg7XG5cdFx0XHRcdFx0XHRcdFx0Y29uc3QgcGVyY2VudCA9IHRvdGFsQnl0ZXMgPT09IDAgPyAwIDogdHJhbnNmZXJyZWRCeXRlcyAvIHRvdGFsQnl0ZXM7XG5cdFx0XHRcdFx0XHRcdFx0b25Eb3dubG9hZFByb2dyZXNzKHtwZXJjZW50LCB0cmFuc2ZlcnJlZEJ5dGVzLCB0b3RhbEJ5dGVzfSwgdmFsdWUpO1xuXHRcdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdFx0Y29udHJvbGxlci5lbnF1ZXVlKHZhbHVlKTtcblx0XHRcdFx0XHRcdFx0cmVhZCgpO1xuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRyZWFkKCk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9KVxuXHRcdFx0KTtcblx0XHR9XG5cdH1cblxuXHRjb25zdCB2YWxpZGF0ZUFuZE1lcmdlID0gKC4uLnNvdXJjZXMpID0+IHtcblx0XHRmb3IgKGNvbnN0IHNvdXJjZSBvZiBzb3VyY2VzKSB7XG5cdFx0XHRpZiAoKCFpc09iamVjdChzb3VyY2UpIHx8IEFycmF5LmlzQXJyYXkoc291cmNlKSkgJiYgdHlwZW9mIHNvdXJjZSAhPT0gJ3VuZGVmaW5lZCcpIHtcblx0XHRcdFx0dGhyb3cgbmV3IFR5cGVFcnJvcignVGhlIGBvcHRpb25zYCBhcmd1bWVudCBtdXN0IGJlIGFuIG9iamVjdCcpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHJldHVybiBkZWVwTWVyZ2Uoe30sIC4uLnNvdXJjZXMpO1xuXHR9O1xuXG5cdGNvbnN0IGNyZWF0ZUluc3RhbmNlID0gZGVmYXVsdHMgPT4ge1xuXHRcdGNvbnN0IGt5ID0gKGlucHV0LCBvcHRpb25zKSA9PiBuZXcgS3koaW5wdXQsIHZhbGlkYXRlQW5kTWVyZ2UoZGVmYXVsdHMsIG9wdGlvbnMpKTtcblxuXHRcdGZvciAoY29uc3QgbWV0aG9kIG9mIHJlcXVlc3RNZXRob2RzKSB7XG5cdFx0XHRreVttZXRob2RdID0gKGlucHV0LCBvcHRpb25zKSA9PiBuZXcgS3koaW5wdXQsIHZhbGlkYXRlQW5kTWVyZ2UoZGVmYXVsdHMsIG9wdGlvbnMsIHttZXRob2R9KSk7XG5cdFx0fVxuXG5cdFx0a3kuSFRUUEVycm9yID0gSFRUUEVycm9yO1xuXHRcdGt5LlRpbWVvdXRFcnJvciA9IFRpbWVvdXRFcnJvcjtcblx0XHRreS5jcmVhdGUgPSBuZXdEZWZhdWx0cyA9PiBjcmVhdGVJbnN0YW5jZSh2YWxpZGF0ZUFuZE1lcmdlKG5ld0RlZmF1bHRzKSk7XG5cdFx0a3kuZXh0ZW5kID0gbmV3RGVmYXVsdHMgPT4gY3JlYXRlSW5zdGFuY2UodmFsaWRhdGVBbmRNZXJnZShkZWZhdWx0cywgbmV3RGVmYXVsdHMpKTtcblx0XHRreS5zdG9wID0gc3RvcDtcblxuXHRcdHJldHVybiBreTtcblx0fTtcblxuXHR2YXIgaW5kZXggPSBjcmVhdGVJbnN0YW5jZSgpO1xuXG5cdHJldHVybiBpbmRleDtcblxufSkpKTtcbiIsIid1c2Ugc3RyaWN0JztcblxuZXhwb3J0cyA9IG1vZHVsZS5leHBvcnRzID0gZmV0Y2g7XG5cbmNvbnN0IGh0dHAgPSByZXF1aXJlKCdodHRwJyk7XG5jb25zdCBodHRwcyA9IHJlcXVpcmUoJ2h0dHBzJyk7XG5jb25zdCB6bGliID0gcmVxdWlyZSgnemxpYicpO1xuY29uc3QgU3RyZWFtID0gcmVxdWlyZSgnc3RyZWFtJyk7XG5jb25zdCBkYXRhVXJpVG9CdWZmZXIgPSByZXF1aXJlKCdkYXRhLXVyaS10by1idWZmZXInKTtcbmNvbnN0IHV0aWwgPSByZXF1aXJlKCd1dGlsJyk7XG5jb25zdCBCbG9iID0gcmVxdWlyZSgnZmV0Y2gtYmxvYicpO1xuY29uc3QgY3J5cHRvID0gcmVxdWlyZSgnY3J5cHRvJyk7XG5jb25zdCB1cmwgPSByZXF1aXJlKCd1cmwnKTtcblxuY2xhc3MgRmV0Y2hCYXNlRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG5cdGNvbnN0cnVjdG9yKG1lc3NhZ2UsIHR5cGUpIHtcblx0XHRzdXBlcihtZXNzYWdlKTtcblx0XHQvLyBIaWRlIGN1c3RvbSBlcnJvciBpbXBsZW1lbnRhdGlvbiBkZXRhaWxzIGZyb20gZW5kLXVzZXJzXG5cdFx0RXJyb3IuY2FwdHVyZVN0YWNrVHJhY2UodGhpcywgdGhpcy5jb25zdHJ1Y3Rvcik7XG5cblx0XHR0aGlzLnR5cGUgPSB0eXBlO1xuXHR9XG5cblx0Z2V0IG5hbWUoKSB7XG5cdFx0cmV0dXJuIHRoaXMuY29uc3RydWN0b3IubmFtZTtcblx0fVxuXG5cdGdldCBbU3ltYm9sLnRvU3RyaW5nVGFnXSgpIHtcblx0XHRyZXR1cm4gdGhpcy5jb25zdHJ1Y3Rvci5uYW1lO1xuXHR9XG59XG5cbi8qKlxuICogQHR5cGVkZWYge3sgYWRkcmVzcz86IHN0cmluZywgY29kZTogc3RyaW5nLCBkZXN0Pzogc3RyaW5nLCBlcnJubzogbnVtYmVyLCBpbmZvPzogb2JqZWN0LCBtZXNzYWdlOiBzdHJpbmcsIHBhdGg/OiBzdHJpbmcsIHBvcnQ/OiBudW1iZXIsIHN5c2NhbGw6IHN0cmluZ319IFN5c3RlbUVycm9yXG4qL1xuXG4vKipcbiAqIEZldGNoRXJyb3IgaW50ZXJmYWNlIGZvciBvcGVyYXRpb25hbCBlcnJvcnNcbiAqL1xuY2xhc3MgRmV0Y2hFcnJvciBleHRlbmRzIEZldGNoQmFzZUVycm9yIHtcblx0LyoqXG5cdCAqIEBwYXJhbSAge3N0cmluZ30gbWVzc2FnZSAtICAgICAgRXJyb3IgbWVzc2FnZSBmb3IgaHVtYW5cblx0ICogQHBhcmFtICB7c3RyaW5nfSBbdHlwZV0gLSAgICAgICAgRXJyb3IgdHlwZSBmb3IgbWFjaGluZVxuXHQgKiBAcGFyYW0gIHtTeXN0ZW1FcnJvcn0gW3N5c3RlbUVycm9yXSAtIEZvciBOb2RlLmpzIHN5c3RlbSBlcnJvclxuXHQgKi9cblx0Y29uc3RydWN0b3IobWVzc2FnZSwgdHlwZSwgc3lzdGVtRXJyb3IpIHtcblx0XHRzdXBlcihtZXNzYWdlLCB0eXBlKTtcblx0XHQvLyBXaGVuIGVyci50eXBlIGlzIGBzeXN0ZW1gLCBlcnIuZXJyb3JlZFN5c0NhbGwgY29udGFpbnMgc3lzdGVtIGVycm9yIGFuZCBlcnIuY29kZSBjb250YWlucyBzeXN0ZW0gZXJyb3IgY29kZVxuXHRcdGlmIChzeXN0ZW1FcnJvcikge1xuXHRcdFx0Ly8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLW11bHRpLWFzc2lnblxuXHRcdFx0dGhpcy5jb2RlID0gdGhpcy5lcnJubyA9IHN5c3RlbUVycm9yLmNvZGU7XG5cdFx0XHR0aGlzLmVycm9yZWRTeXNDYWxsID0gc3lzdGVtRXJyb3Iuc3lzY2FsbDtcblx0XHR9XG5cdH1cbn1cblxuLyoqXG4gKiBJcy5qc1xuICpcbiAqIE9iamVjdCB0eXBlIGNoZWNrcy5cbiAqL1xuXG5jb25zdCBOQU1FID0gU3ltYm9sLnRvU3RyaW5nVGFnO1xuXG4vKipcbiAqIENoZWNrIGlmIGBvYmpgIGlzIGEgVVJMU2VhcmNoUGFyYW1zIG9iamVjdFxuICogcmVmOiBodHRwczovL2dpdGh1Yi5jb20vbm9kZS1mZXRjaC9ub2RlLWZldGNoL2lzc3Vlcy8yOTYjaXNzdWVjb21tZW50LTMwNzU5ODE0M1xuICpcbiAqIEBwYXJhbSAgeyp9IG9ialxuICogQHJldHVybiB7Ym9vbGVhbn1cbiAqL1xuY29uc3QgaXNVUkxTZWFyY2hQYXJhbWV0ZXJzID0gb2JqZWN0ID0+IHtcblx0cmV0dXJuIChcblx0XHR0eXBlb2Ygb2JqZWN0ID09PSAnb2JqZWN0JyAmJlxuXHRcdHR5cGVvZiBvYmplY3QuYXBwZW5kID09PSAnZnVuY3Rpb24nICYmXG5cdFx0dHlwZW9mIG9iamVjdC5kZWxldGUgPT09ICdmdW5jdGlvbicgJiZcblx0XHR0eXBlb2Ygb2JqZWN0LmdldCA9PT0gJ2Z1bmN0aW9uJyAmJlxuXHRcdHR5cGVvZiBvYmplY3QuZ2V0QWxsID09PSAnZnVuY3Rpb24nICYmXG5cdFx0dHlwZW9mIG9iamVjdC5oYXMgPT09ICdmdW5jdGlvbicgJiZcblx0XHR0eXBlb2Ygb2JqZWN0LnNldCA9PT0gJ2Z1bmN0aW9uJyAmJlxuXHRcdHR5cGVvZiBvYmplY3Quc29ydCA9PT0gJ2Z1bmN0aW9uJyAmJlxuXHRcdG9iamVjdFtOQU1FXSA9PT0gJ1VSTFNlYXJjaFBhcmFtcydcblx0KTtcbn07XG5cbi8qKlxuICogQ2hlY2sgaWYgYG9iamVjdGAgaXMgYSBXM0MgYEJsb2JgIG9iamVjdCAod2hpY2ggYEZpbGVgIGluaGVyaXRzIGZyb20pXG4gKlxuICogQHBhcmFtICB7Kn0gb2JqXG4gKiBAcmV0dXJuIHtib29sZWFufVxuICovXG5jb25zdCBpc0Jsb2IgPSBvYmplY3QgPT4ge1xuXHRyZXR1cm4gKFxuXHRcdHR5cGVvZiBvYmplY3QgPT09ICdvYmplY3QnICYmXG5cdFx0dHlwZW9mIG9iamVjdC5hcnJheUJ1ZmZlciA9PT0gJ2Z1bmN0aW9uJyAmJlxuXHRcdHR5cGVvZiBvYmplY3QudHlwZSA9PT0gJ3N0cmluZycgJiZcblx0XHR0eXBlb2Ygb2JqZWN0LnN0cmVhbSA9PT0gJ2Z1bmN0aW9uJyAmJlxuXHRcdHR5cGVvZiBvYmplY3QuY29uc3RydWN0b3IgPT09ICdmdW5jdGlvbicgJiZcblx0XHQvXihCbG9ifEZpbGUpJC8udGVzdChvYmplY3RbTkFNRV0pXG5cdCk7XG59O1xuXG4vKipcbiAqIENoZWNrIGlmIGBvYmpgIGlzIGEgc3BlYy1jb21wbGlhbnQgYEZvcm1EYXRhYCBvYmplY3RcbiAqXG4gKiBAcGFyYW0geyp9IG9iamVjdFxuICogQHJldHVybiB7Ym9vbGVhbn1cbiAqL1xuZnVuY3Rpb24gaXNGb3JtRGF0YShvYmplY3QpIHtcblx0cmV0dXJuIChcblx0XHR0eXBlb2Ygb2JqZWN0ID09PSAnb2JqZWN0JyAmJlxuXHRcdHR5cGVvZiBvYmplY3QuYXBwZW5kID09PSAnZnVuY3Rpb24nICYmXG5cdFx0dHlwZW9mIG9iamVjdC5zZXQgPT09ICdmdW5jdGlvbicgJiZcblx0XHR0eXBlb2Ygb2JqZWN0LmdldCA9PT0gJ2Z1bmN0aW9uJyAmJlxuXHRcdHR5cGVvZiBvYmplY3QuZ2V0QWxsID09PSAnZnVuY3Rpb24nICYmXG5cdFx0dHlwZW9mIG9iamVjdC5kZWxldGUgPT09ICdmdW5jdGlvbicgJiZcblx0XHR0eXBlb2Ygb2JqZWN0LmtleXMgPT09ICdmdW5jdGlvbicgJiZcblx0XHR0eXBlb2Ygb2JqZWN0LnZhbHVlcyA9PT0gJ2Z1bmN0aW9uJyAmJlxuXHRcdHR5cGVvZiBvYmplY3QuZW50cmllcyA9PT0gJ2Z1bmN0aW9uJyAmJlxuXHRcdHR5cGVvZiBvYmplY3QuY29uc3RydWN0b3IgPT09ICdmdW5jdGlvbicgJiZcblx0XHRvYmplY3RbTkFNRV0gPT09ICdGb3JtRGF0YSdcblx0KTtcbn1cblxuLyoqXG4gKiBDaGVjayBpZiBgb2JqYCBpcyBhbiBpbnN0YW5jZSBvZiBBYm9ydFNpZ25hbC5cbiAqXG4gKiBAcGFyYW0gIHsqfSBvYmpcbiAqIEByZXR1cm4ge2Jvb2xlYW59XG4gKi9cbmNvbnN0IGlzQWJvcnRTaWduYWwgPSBvYmplY3QgPT4ge1xuXHRyZXR1cm4gKFxuXHRcdHR5cGVvZiBvYmplY3QgPT09ICdvYmplY3QnICYmXG5cdFx0b2JqZWN0W05BTUVdID09PSAnQWJvcnRTaWduYWwnXG5cdCk7XG59O1xuXG5jb25zdCBjYXJyaWFnZSA9ICdcXHJcXG4nO1xuY29uc3QgZGFzaGVzID0gJy0nLnJlcGVhdCgyKTtcbmNvbnN0IGNhcnJpYWdlTGVuZ3RoID0gQnVmZmVyLmJ5dGVMZW5ndGgoY2FycmlhZ2UpO1xuXG4vKipcbiAqIEBwYXJhbSB7c3RyaW5nfSBib3VuZGFyeVxuICovXG5jb25zdCBnZXRGb290ZXIgPSBib3VuZGFyeSA9PiBgJHtkYXNoZXN9JHtib3VuZGFyeX0ke2Rhc2hlc30ke2NhcnJpYWdlLnJlcGVhdCgyKX1gO1xuXG4vKipcbiAqIEBwYXJhbSB7c3RyaW5nfSBib3VuZGFyeVxuICogQHBhcmFtIHtzdHJpbmd9IG5hbWVcbiAqIEBwYXJhbSB7Kn0gZmllbGRcbiAqXG4gKiBAcmV0dXJuIHtzdHJpbmd9XG4gKi9cbmZ1bmN0aW9uIGdldEhlYWRlcihib3VuZGFyeSwgbmFtZSwgZmllbGQpIHtcblx0bGV0IGhlYWRlciA9ICcnO1xuXG5cdGhlYWRlciArPSBgJHtkYXNoZXN9JHtib3VuZGFyeX0ke2NhcnJpYWdlfWA7XG5cdGhlYWRlciArPSBgQ29udGVudC1EaXNwb3NpdGlvbjogZm9ybS1kYXRhOyBuYW1lPVwiJHtuYW1lfVwiYDtcblxuXHRpZiAoaXNCbG9iKGZpZWxkKSkge1xuXHRcdGhlYWRlciArPSBgOyBmaWxlbmFtZT1cIiR7ZmllbGQubmFtZX1cIiR7Y2FycmlhZ2V9YDtcblx0XHRoZWFkZXIgKz0gYENvbnRlbnQtVHlwZTogJHtmaWVsZC50eXBlIHx8ICdhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0nfWA7XG5cdH1cblxuXHRyZXR1cm4gYCR7aGVhZGVyfSR7Y2FycmlhZ2UucmVwZWF0KDIpfWA7XG59XG5cbi8qKlxuICogQHJldHVybiB7c3RyaW5nfVxuICovXG5jb25zdCBnZXRCb3VuZGFyeSA9ICgpID0+IGNyeXB0by5yYW5kb21CeXRlcyg4KS50b1N0cmluZygnaGV4Jyk7XG5cbi8qKlxuICogQHBhcmFtIHtGb3JtRGF0YX0gZm9ybVxuICogQHBhcmFtIHtzdHJpbmd9IGJvdW5kYXJ5XG4gKi9cbmFzeW5jIGZ1bmN0aW9uICogZm9ybURhdGFJdGVyYXRvcihmb3JtLCBib3VuZGFyeSkge1xuXHRmb3IgKGNvbnN0IFtuYW1lLCB2YWx1ZV0gb2YgZm9ybSkge1xuXHRcdHlpZWxkIGdldEhlYWRlcihib3VuZGFyeSwgbmFtZSwgdmFsdWUpO1xuXG5cdFx0aWYgKGlzQmxvYih2YWx1ZSkpIHtcblx0XHRcdHlpZWxkICogdmFsdWUuc3RyZWFtKCk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHlpZWxkIHZhbHVlO1xuXHRcdH1cblxuXHRcdHlpZWxkIGNhcnJpYWdlO1xuXHR9XG5cblx0eWllbGQgZ2V0Rm9vdGVyKGJvdW5kYXJ5KTtcbn1cblxuLyoqXG4gKiBAcGFyYW0ge0Zvcm1EYXRhfSBmb3JtXG4gKiBAcGFyYW0ge3N0cmluZ30gYm91bmRhcnlcbiAqL1xuZnVuY3Rpb24gZ2V0Rm9ybURhdGFMZW5ndGgoZm9ybSwgYm91bmRhcnkpIHtcblx0bGV0IGxlbmd0aCA9IDA7XG5cblx0Zm9yIChjb25zdCBbbmFtZSwgdmFsdWVdIG9mIGZvcm0pIHtcblx0XHRsZW5ndGggKz0gQnVmZmVyLmJ5dGVMZW5ndGgoZ2V0SGVhZGVyKGJvdW5kYXJ5LCBuYW1lLCB2YWx1ZSkpO1xuXG5cdFx0aWYgKGlzQmxvYih2YWx1ZSkpIHtcblx0XHRcdGxlbmd0aCArPSB2YWx1ZS5zaXplO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRsZW5ndGggKz0gQnVmZmVyLmJ5dGVMZW5ndGgoU3RyaW5nKHZhbHVlKSk7XG5cdFx0fVxuXG5cdFx0bGVuZ3RoICs9IGNhcnJpYWdlTGVuZ3RoO1xuXHR9XG5cblx0bGVuZ3RoICs9IEJ1ZmZlci5ieXRlTGVuZ3RoKGdldEZvb3Rlcihib3VuZGFyeSkpO1xuXG5cdHJldHVybiBsZW5ndGg7XG59XG5cbmNvbnN0IElOVEVSTkFMUyA9IFN5bWJvbCgnQm9keSBpbnRlcm5hbHMnKTtcblxuLyoqXG4gKiBCb2R5IG1peGluXG4gKlxuICogUmVmOiBodHRwczovL2ZldGNoLnNwZWMud2hhdHdnLm9yZy8jYm9keVxuICpcbiAqIEBwYXJhbSAgIFN0cmVhbSAgYm9keSAgUmVhZGFibGUgc3RyZWFtXG4gKiBAcGFyYW0gICBPYmplY3QgIG9wdHMgIFJlc3BvbnNlIG9wdGlvbnNcbiAqIEByZXR1cm4gIFZvaWRcbiAqL1xuY2xhc3MgQm9keSB7XG5cdGNvbnN0cnVjdG9yKGJvZHksIHtcblx0XHRzaXplID0gMFxuXHR9ID0ge30pIHtcblx0XHRsZXQgYm91bmRhcnkgPSBudWxsO1xuXG5cdFx0aWYgKGJvZHkgPT09IG51bGwpIHtcblx0XHRcdC8vIEJvZHkgaXMgdW5kZWZpbmVkIG9yIG51bGxcblx0XHRcdGJvZHkgPSBudWxsO1xuXHRcdH0gZWxzZSBpZiAoaXNVUkxTZWFyY2hQYXJhbWV0ZXJzKGJvZHkpKSB7XG5cdFx0Ly8gQm9keSBpcyBhIFVSTFNlYXJjaFBhcmFtc1xuXHRcdFx0Ym9keSA9IEJ1ZmZlci5mcm9tKGJvZHkudG9TdHJpbmcoKSk7XG5cdFx0fSBlbHNlIGlmIChpc0Jsb2IoYm9keSkpIDsgZWxzZSBpZiAoQnVmZmVyLmlzQnVmZmVyKGJvZHkpKSA7IGVsc2UgaWYgKHV0aWwudHlwZXMuaXNBbnlBcnJheUJ1ZmZlcihib2R5KSkge1xuXHRcdFx0Ly8gQm9keSBpcyBBcnJheUJ1ZmZlclxuXHRcdFx0Ym9keSA9IEJ1ZmZlci5mcm9tKGJvZHkpO1xuXHRcdH0gZWxzZSBpZiAoQXJyYXlCdWZmZXIuaXNWaWV3KGJvZHkpKSB7XG5cdFx0XHQvLyBCb2R5IGlzIEFycmF5QnVmZmVyVmlld1xuXHRcdFx0Ym9keSA9IEJ1ZmZlci5mcm9tKGJvZHkuYnVmZmVyLCBib2R5LmJ5dGVPZmZzZXQsIGJvZHkuYnl0ZUxlbmd0aCk7XG5cdFx0fSBlbHNlIGlmIChib2R5IGluc3RhbmNlb2YgU3RyZWFtKSA7IGVsc2UgaWYgKGlzRm9ybURhdGEoYm9keSkpIHtcblx0XHRcdC8vIEJvZHkgaXMgYW4gaW5zdGFuY2Ugb2YgZm9ybWRhdGEtbm9kZVxuXHRcdFx0Ym91bmRhcnkgPSBgTm9kZUZldGNoRm9ybURhdGFCb3VuZGFyeSR7Z2V0Qm91bmRhcnkoKX1gO1xuXHRcdFx0Ym9keSA9IFN0cmVhbS5SZWFkYWJsZS5mcm9tKGZvcm1EYXRhSXRlcmF0b3IoYm9keSwgYm91bmRhcnkpKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gTm9uZSBvZiB0aGUgYWJvdmVcblx0XHRcdC8vIGNvZXJjZSB0byBzdHJpbmcgdGhlbiBidWZmZXJcblx0XHRcdGJvZHkgPSBCdWZmZXIuZnJvbShTdHJpbmcoYm9keSkpO1xuXHRcdH1cblxuXHRcdHRoaXNbSU5URVJOQUxTXSA9IHtcblx0XHRcdGJvZHksXG5cdFx0XHRib3VuZGFyeSxcblx0XHRcdGRpc3R1cmJlZDogZmFsc2UsXG5cdFx0XHRlcnJvcjogbnVsbFxuXHRcdH07XG5cdFx0dGhpcy5zaXplID0gc2l6ZTtcblxuXHRcdGlmIChib2R5IGluc3RhbmNlb2YgU3RyZWFtKSB7XG5cdFx0XHRib2R5Lm9uKCdlcnJvcicsIGVyciA9PiB7XG5cdFx0XHRcdGNvbnN0IGVycm9yID0gZXJyIGluc3RhbmNlb2YgRmV0Y2hCYXNlRXJyb3IgP1xuXHRcdFx0XHRcdGVyciA6XG5cdFx0XHRcdFx0bmV3IEZldGNoRXJyb3IoYEludmFsaWQgcmVzcG9uc2UgYm9keSB3aGlsZSB0cnlpbmcgdG8gZmV0Y2ggJHt0aGlzLnVybH06ICR7ZXJyLm1lc3NhZ2V9YCwgJ3N5c3RlbScsIGVycik7XG5cdFx0XHRcdHRoaXNbSU5URVJOQUxTXS5lcnJvciA9IGVycm9yO1xuXHRcdFx0fSk7XG5cdFx0fVxuXHR9XG5cblx0Z2V0IGJvZHkoKSB7XG5cdFx0cmV0dXJuIHRoaXNbSU5URVJOQUxTXS5ib2R5O1xuXHR9XG5cblx0Z2V0IGJvZHlVc2VkKCkge1xuXHRcdHJldHVybiB0aGlzW0lOVEVSTkFMU10uZGlzdHVyYmVkO1xuXHR9XG5cblx0LyoqXG5cdCAqIERlY29kZSByZXNwb25zZSBhcyBBcnJheUJ1ZmZlclxuXHQgKlxuXHQgKiBAcmV0dXJuICBQcm9taXNlXG5cdCAqL1xuXHRhc3luYyBhcnJheUJ1ZmZlcigpIHtcblx0XHRjb25zdCB7YnVmZmVyLCBieXRlT2Zmc2V0LCBieXRlTGVuZ3RofSA9IGF3YWl0IGNvbnN1bWVCb2R5KHRoaXMpO1xuXHRcdHJldHVybiBidWZmZXIuc2xpY2UoYnl0ZU9mZnNldCwgYnl0ZU9mZnNldCArIGJ5dGVMZW5ndGgpO1xuXHR9XG5cblx0LyoqXG5cdCAqIFJldHVybiByYXcgcmVzcG9uc2UgYXMgQmxvYlxuXHQgKlxuXHQgKiBAcmV0dXJuIFByb21pc2Vcblx0ICovXG5cdGFzeW5jIGJsb2IoKSB7XG5cdFx0Y29uc3QgY3QgPSAodGhpcy5oZWFkZXJzICYmIHRoaXMuaGVhZGVycy5nZXQoJ2NvbnRlbnQtdHlwZScpKSB8fCAodGhpc1tJTlRFUk5BTFNdLmJvZHkgJiYgdGhpc1tJTlRFUk5BTFNdLmJvZHkudHlwZSkgfHwgJyc7XG5cdFx0Y29uc3QgYnVmID0gYXdhaXQgdGhpcy5idWZmZXIoKTtcblxuXHRcdHJldHVybiBuZXcgQmxvYihbYnVmXSwge1xuXHRcdFx0dHlwZTogY3Rcblx0XHR9KTtcblx0fVxuXG5cdC8qKlxuXHQgKiBEZWNvZGUgcmVzcG9uc2UgYXMganNvblxuXHQgKlxuXHQgKiBAcmV0dXJuICBQcm9taXNlXG5cdCAqL1xuXHRhc3luYyBqc29uKCkge1xuXHRcdGNvbnN0IGJ1ZmZlciA9IGF3YWl0IGNvbnN1bWVCb2R5KHRoaXMpO1xuXHRcdHJldHVybiBKU09OLnBhcnNlKGJ1ZmZlci50b1N0cmluZygpKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBEZWNvZGUgcmVzcG9uc2UgYXMgdGV4dFxuXHQgKlxuXHQgKiBAcmV0dXJuICBQcm9taXNlXG5cdCAqL1xuXHRhc3luYyB0ZXh0KCkge1xuXHRcdGNvbnN0IGJ1ZmZlciA9IGF3YWl0IGNvbnN1bWVCb2R5KHRoaXMpO1xuXHRcdHJldHVybiBidWZmZXIudG9TdHJpbmcoKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBEZWNvZGUgcmVzcG9uc2UgYXMgYnVmZmVyIChub24tc3BlYyBhcGkpXG5cdCAqXG5cdCAqIEByZXR1cm4gIFByb21pc2Vcblx0ICovXG5cdGJ1ZmZlcigpIHtcblx0XHRyZXR1cm4gY29uc3VtZUJvZHkodGhpcyk7XG5cdH1cbn1cblxuLy8gSW4gYnJvd3NlcnMsIGFsbCBwcm9wZXJ0aWVzIGFyZSBlbnVtZXJhYmxlLlxuT2JqZWN0LmRlZmluZVByb3BlcnRpZXMoQm9keS5wcm90b3R5cGUsIHtcblx0Ym9keToge2VudW1lcmFibGU6IHRydWV9LFxuXHRib2R5VXNlZDoge2VudW1lcmFibGU6IHRydWV9LFxuXHRhcnJheUJ1ZmZlcjoge2VudW1lcmFibGU6IHRydWV9LFxuXHRibG9iOiB7ZW51bWVyYWJsZTogdHJ1ZX0sXG5cdGpzb246IHtlbnVtZXJhYmxlOiB0cnVlfSxcblx0dGV4dDoge2VudW1lcmFibGU6IHRydWV9XG59KTtcblxuLyoqXG4gKiBDb25zdW1lIGFuZCBjb252ZXJ0IGFuIGVudGlyZSBCb2R5IHRvIGEgQnVmZmVyLlxuICpcbiAqIFJlZjogaHR0cHM6Ly9mZXRjaC5zcGVjLndoYXR3Zy5vcmcvI2NvbmNlcHQtYm9keS1jb25zdW1lLWJvZHlcbiAqXG4gKiBAcmV0dXJuIFByb21pc2VcbiAqL1xuYXN5bmMgZnVuY3Rpb24gY29uc3VtZUJvZHkoZGF0YSkge1xuXHRpZiAoZGF0YVtJTlRFUk5BTFNdLmRpc3R1cmJlZCkge1xuXHRcdHRocm93IG5ldyBUeXBlRXJyb3IoYGJvZHkgdXNlZCBhbHJlYWR5IGZvcjogJHtkYXRhLnVybH1gKTtcblx0fVxuXG5cdGRhdGFbSU5URVJOQUxTXS5kaXN0dXJiZWQgPSB0cnVlO1xuXG5cdGlmIChkYXRhW0lOVEVSTkFMU10uZXJyb3IpIHtcblx0XHR0aHJvdyBkYXRhW0lOVEVSTkFMU10uZXJyb3I7XG5cdH1cblxuXHRsZXQge2JvZHl9ID0gZGF0YTtcblxuXHQvLyBCb2R5IGlzIG51bGxcblx0aWYgKGJvZHkgPT09IG51bGwpIHtcblx0XHRyZXR1cm4gQnVmZmVyLmFsbG9jKDApO1xuXHR9XG5cblx0Ly8gQm9keSBpcyBibG9iXG5cdGlmIChpc0Jsb2IoYm9keSkpIHtcblx0XHRib2R5ID0gYm9keS5zdHJlYW0oKTtcblx0fVxuXG5cdC8vIEJvZHkgaXMgYnVmZmVyXG5cdGlmIChCdWZmZXIuaXNCdWZmZXIoYm9keSkpIHtcblx0XHRyZXR1cm4gYm9keTtcblx0fVxuXG5cdC8qIGM4IGlnbm9yZSBuZXh0IDMgKi9cblx0aWYgKCEoYm9keSBpbnN0YW5jZW9mIFN0cmVhbSkpIHtcblx0XHRyZXR1cm4gQnVmZmVyLmFsbG9jKDApO1xuXHR9XG5cblx0Ly8gQm9keSBpcyBzdHJlYW1cblx0Ly8gZ2V0IHJlYWR5IHRvIGFjdHVhbGx5IGNvbnN1bWUgdGhlIGJvZHlcblx0Y29uc3QgYWNjdW0gPSBbXTtcblx0bGV0IGFjY3VtQnl0ZXMgPSAwO1xuXG5cdHRyeSB7XG5cdFx0Zm9yIGF3YWl0IChjb25zdCBjaHVuayBvZiBib2R5KSB7XG5cdFx0XHRpZiAoZGF0YS5zaXplID4gMCAmJiBhY2N1bUJ5dGVzICsgY2h1bmsubGVuZ3RoID4gZGF0YS5zaXplKSB7XG5cdFx0XHRcdGNvbnN0IGVyciA9IG5ldyBGZXRjaEVycm9yKGBjb250ZW50IHNpemUgYXQgJHtkYXRhLnVybH0gb3ZlciBsaW1pdDogJHtkYXRhLnNpemV9YCwgJ21heC1zaXplJyk7XG5cdFx0XHRcdGJvZHkuZGVzdHJveShlcnIpO1xuXHRcdFx0XHR0aHJvdyBlcnI7XG5cdFx0XHR9XG5cblx0XHRcdGFjY3VtQnl0ZXMgKz0gY2h1bmsubGVuZ3RoO1xuXHRcdFx0YWNjdW0ucHVzaChjaHVuayk7XG5cdFx0fVxuXHR9IGNhdGNoIChlcnJvcikge1xuXHRcdGlmIChlcnJvciBpbnN0YW5jZW9mIEZldGNoQmFzZUVycm9yKSB7XG5cdFx0XHR0aHJvdyBlcnJvcjtcblx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gT3RoZXIgZXJyb3JzLCBzdWNoIGFzIGluY29ycmVjdCBjb250ZW50LWVuY29kaW5nXG5cdFx0XHR0aHJvdyBuZXcgRmV0Y2hFcnJvcihgSW52YWxpZCByZXNwb25zZSBib2R5IHdoaWxlIHRyeWluZyB0byBmZXRjaCAke2RhdGEudXJsfTogJHtlcnJvci5tZXNzYWdlfWAsICdzeXN0ZW0nLCBlcnJvcik7XG5cdFx0fVxuXHR9XG5cblx0aWYgKGJvZHkucmVhZGFibGVFbmRlZCA9PT0gdHJ1ZSB8fCBib2R5Ll9yZWFkYWJsZVN0YXRlLmVuZGVkID09PSB0cnVlKSB7XG5cdFx0dHJ5IHtcblx0XHRcdGlmIChhY2N1bS5ldmVyeShjID0+IHR5cGVvZiBjID09PSAnc3RyaW5nJykpIHtcblx0XHRcdFx0cmV0dXJuIEJ1ZmZlci5mcm9tKGFjY3VtLmpvaW4oJycpKTtcblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIEJ1ZmZlci5jb25jYXQoYWNjdW0sIGFjY3VtQnl0ZXMpO1xuXHRcdH0gY2F0Y2ggKGVycm9yKSB7XG5cdFx0XHR0aHJvdyBuZXcgRmV0Y2hFcnJvcihgQ291bGQgbm90IGNyZWF0ZSBCdWZmZXIgZnJvbSByZXNwb25zZSBib2R5IGZvciAke2RhdGEudXJsfTogJHtlcnJvci5tZXNzYWdlfWAsICdzeXN0ZW0nLCBlcnJvcik7XG5cdFx0fVxuXHR9IGVsc2Uge1xuXHRcdHRocm93IG5ldyBGZXRjaEVycm9yKGBQcmVtYXR1cmUgY2xvc2Ugb2Ygc2VydmVyIHJlc3BvbnNlIHdoaWxlIHRyeWluZyB0byBmZXRjaCAke2RhdGEudXJsfWApO1xuXHR9XG59XG5cbi8qKlxuICogQ2xvbmUgYm9keSBnaXZlbiBSZXMvUmVxIGluc3RhbmNlXG4gKlxuICogQHBhcmFtICAgTWl4ZWQgICBpbnN0YW5jZSAgICAgICBSZXNwb25zZSBvciBSZXF1ZXN0IGluc3RhbmNlXG4gKiBAcGFyYW0gICBTdHJpbmcgIGhpZ2hXYXRlck1hcmsgIGhpZ2hXYXRlck1hcmsgZm9yIGJvdGggUGFzc1Rocm91Z2ggYm9keSBzdHJlYW1zXG4gKiBAcmV0dXJuICBNaXhlZFxuICovXG5jb25zdCBjbG9uZSA9IChpbnN0YW5jZSwgaGlnaFdhdGVyTWFyaykgPT4ge1xuXHRsZXQgcDE7XG5cdGxldCBwMjtcblx0bGV0IHtib2R5fSA9IGluc3RhbmNlO1xuXG5cdC8vIERvbid0IGFsbG93IGNsb25pbmcgYSB1c2VkIGJvZHlcblx0aWYgKGluc3RhbmNlLmJvZHlVc2VkKSB7XG5cdFx0dGhyb3cgbmV3IEVycm9yKCdjYW5ub3QgY2xvbmUgYm9keSBhZnRlciBpdCBpcyB1c2VkJyk7XG5cdH1cblxuXHQvLyBDaGVjayB0aGF0IGJvZHkgaXMgYSBzdHJlYW0gYW5kIG5vdCBmb3JtLWRhdGEgb2JqZWN0XG5cdC8vIG5vdGU6IHdlIGNhbid0IGNsb25lIHRoZSBmb3JtLWRhdGEgb2JqZWN0IHdpdGhvdXQgaGF2aW5nIGl0IGFzIGEgZGVwZW5kZW5jeVxuXHRpZiAoKGJvZHkgaW5zdGFuY2VvZiBTdHJlYW0pICYmICh0eXBlb2YgYm9keS5nZXRCb3VuZGFyeSAhPT0gJ2Z1bmN0aW9uJykpIHtcblx0XHQvLyBUZWUgaW5zdGFuY2UgYm9keVxuXHRcdHAxID0gbmV3IFN0cmVhbS5QYXNzVGhyb3VnaCh7aGlnaFdhdGVyTWFya30pO1xuXHRcdHAyID0gbmV3IFN0cmVhbS5QYXNzVGhyb3VnaCh7aGlnaFdhdGVyTWFya30pO1xuXHRcdGJvZHkucGlwZShwMSk7XG5cdFx0Ym9keS5waXBlKHAyKTtcblx0XHQvLyBTZXQgaW5zdGFuY2UgYm9keSB0byB0ZWVkIGJvZHkgYW5kIHJldHVybiB0aGUgb3RoZXIgdGVlZCBib2R5XG5cdFx0aW5zdGFuY2VbSU5URVJOQUxTXS5ib2R5ID0gcDE7XG5cdFx0Ym9keSA9IHAyO1xuXHR9XG5cblx0cmV0dXJuIGJvZHk7XG59O1xuXG4vKipcbiAqIFBlcmZvcm1zIHRoZSBvcGVyYXRpb24gXCJleHRyYWN0IGEgYENvbnRlbnQtVHlwZWAgdmFsdWUgZnJvbSB8b2JqZWN0fFwiIGFzXG4gKiBzcGVjaWZpZWQgaW4gdGhlIHNwZWNpZmljYXRpb246XG4gKiBodHRwczovL2ZldGNoLnNwZWMud2hhdHdnLm9yZy8jY29uY2VwdC1ib2R5aW5pdC1leHRyYWN0XG4gKlxuICogVGhpcyBmdW5jdGlvbiBhc3N1bWVzIHRoYXQgaW5zdGFuY2UuYm9keSBpcyBwcmVzZW50LlxuICpcbiAqIEBwYXJhbSB7YW55fSBib2R5IEFueSBvcHRpb25zLmJvZHkgaW5wdXRcbiAqIEByZXR1cm5zIHtzdHJpbmcgfCBudWxsfVxuICovXG5jb25zdCBleHRyYWN0Q29udGVudFR5cGUgPSAoYm9keSwgcmVxdWVzdCkgPT4ge1xuXHQvLyBCb2R5IGlzIG51bGwgb3IgdW5kZWZpbmVkXG5cdGlmIChib2R5ID09PSBudWxsKSB7XG5cdFx0cmV0dXJuIG51bGw7XG5cdH1cblxuXHQvLyBCb2R5IGlzIHN0cmluZ1xuXHRpZiAodHlwZW9mIGJvZHkgPT09ICdzdHJpbmcnKSB7XG5cdFx0cmV0dXJuICd0ZXh0L3BsYWluO2NoYXJzZXQ9VVRGLTgnO1xuXHR9XG5cblx0Ly8gQm9keSBpcyBhIFVSTFNlYXJjaFBhcmFtc1xuXHRpZiAoaXNVUkxTZWFyY2hQYXJhbWV0ZXJzKGJvZHkpKSB7XG5cdFx0cmV0dXJuICdhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQ7Y2hhcnNldD1VVEYtOCc7XG5cdH1cblxuXHQvLyBCb2R5IGlzIGJsb2Jcblx0aWYgKGlzQmxvYihib2R5KSkge1xuXHRcdHJldHVybiBib2R5LnR5cGUgfHwgbnVsbDtcblx0fVxuXG5cdC8vIEJvZHkgaXMgYSBCdWZmZXIgKEJ1ZmZlciwgQXJyYXlCdWZmZXIgb3IgQXJyYXlCdWZmZXJWaWV3KVxuXHRpZiAoQnVmZmVyLmlzQnVmZmVyKGJvZHkpIHx8IHV0aWwudHlwZXMuaXNBbnlBcnJheUJ1ZmZlcihib2R5KSB8fCBBcnJheUJ1ZmZlci5pc1ZpZXcoYm9keSkpIHtcblx0XHRyZXR1cm4gbnVsbDtcblx0fVxuXG5cdC8vIERldGVjdCBmb3JtIGRhdGEgaW5wdXQgZnJvbSBmb3JtLWRhdGEgbW9kdWxlXG5cdGlmIChib2R5ICYmIHR5cGVvZiBib2R5LmdldEJvdW5kYXJ5ID09PSAnZnVuY3Rpb24nKSB7XG5cdFx0cmV0dXJuIGBtdWx0aXBhcnQvZm9ybS1kYXRhO2JvdW5kYXJ5PSR7Ym9keS5nZXRCb3VuZGFyeSgpfWA7XG5cdH1cblxuXHRpZiAoaXNGb3JtRGF0YShib2R5KSkge1xuXHRcdHJldHVybiBgbXVsdGlwYXJ0L2Zvcm0tZGF0YTsgYm91bmRhcnk9JHtyZXF1ZXN0W0lOVEVSTkFMU10uYm91bmRhcnl9YDtcblx0fVxuXG5cdC8vIEJvZHkgaXMgc3RyZWFtIC0gY2FuJ3QgcmVhbGx5IGRvIG11Y2ggYWJvdXQgdGhpc1xuXHRpZiAoYm9keSBpbnN0YW5jZW9mIFN0cmVhbSkge1xuXHRcdHJldHVybiBudWxsO1xuXHR9XG5cblx0Ly8gQm9keSBjb25zdHJ1Y3RvciBkZWZhdWx0cyBvdGhlciB0aGluZ3MgdG8gc3RyaW5nXG5cdHJldHVybiAndGV4dC9wbGFpbjtjaGFyc2V0PVVURi04Jztcbn07XG5cbi8qKlxuICogVGhlIEZldGNoIFN0YW5kYXJkIHRyZWF0cyB0aGlzIGFzIGlmIFwidG90YWwgYnl0ZXNcIiBpcyBhIHByb3BlcnR5IG9uIHRoZSBib2R5LlxuICogRm9yIHVzLCB3ZSBoYXZlIHRvIGV4cGxpY2l0bHkgZ2V0IGl0IHdpdGggYSBmdW5jdGlvbi5cbiAqXG4gKiByZWY6IGh0dHBzOi8vZmV0Y2guc3BlYy53aGF0d2cub3JnLyNjb25jZXB0LWJvZHktdG90YWwtYnl0ZXNcbiAqXG4gKiBAcGFyYW0ge2FueX0gb2JqLmJvZHkgQm9keSBvYmplY3QgZnJvbSB0aGUgQm9keSBpbnN0YW5jZS5cbiAqIEByZXR1cm5zIHtudW1iZXIgfCBudWxsfVxuICovXG5jb25zdCBnZXRUb3RhbEJ5dGVzID0gcmVxdWVzdCA9PiB7XG5cdGNvbnN0IHtib2R5fSA9IHJlcXVlc3Q7XG5cblx0Ly8gQm9keSBpcyBudWxsIG9yIHVuZGVmaW5lZFxuXHRpZiAoYm9keSA9PT0gbnVsbCkge1xuXHRcdHJldHVybiAwO1xuXHR9XG5cblx0Ly8gQm9keSBpcyBCbG9iXG5cdGlmIChpc0Jsb2IoYm9keSkpIHtcblx0XHRyZXR1cm4gYm9keS5zaXplO1xuXHR9XG5cblx0Ly8gQm9keSBpcyBCdWZmZXJcblx0aWYgKEJ1ZmZlci5pc0J1ZmZlcihib2R5KSkge1xuXHRcdHJldHVybiBib2R5Lmxlbmd0aDtcblx0fVxuXG5cdC8vIERldGVjdCBmb3JtIGRhdGEgaW5wdXQgZnJvbSBmb3JtLWRhdGEgbW9kdWxlXG5cdGlmIChib2R5ICYmIHR5cGVvZiBib2R5LmdldExlbmd0aFN5bmMgPT09ICdmdW5jdGlvbicpIHtcblx0XHRyZXR1cm4gYm9keS5oYXNLbm93bkxlbmd0aCAmJiBib2R5Lmhhc0tub3duTGVuZ3RoKCkgPyBib2R5LmdldExlbmd0aFN5bmMoKSA6IG51bGw7XG5cdH1cblxuXHQvLyBCb2R5IGlzIGEgc3BlYy1jb21wbGlhbnQgZm9ybS1kYXRhXG5cdGlmIChpc0Zvcm1EYXRhKGJvZHkpKSB7XG5cdFx0cmV0dXJuIGdldEZvcm1EYXRhTGVuZ3RoKHJlcXVlc3RbSU5URVJOQUxTXS5ib3VuZGFyeSk7XG5cdH1cblxuXHQvLyBCb2R5IGlzIHN0cmVhbVxuXHRyZXR1cm4gbnVsbDtcbn07XG5cbi8qKlxuICogV3JpdGUgYSBCb2R5IHRvIGEgTm9kZS5qcyBXcml0YWJsZVN0cmVhbSAoZS5nLiBodHRwLlJlcXVlc3QpIG9iamVjdC5cbiAqXG4gKiBAcGFyYW0ge1N0cmVhbS5Xcml0YWJsZX0gZGVzdCBUaGUgc3RyZWFtIHRvIHdyaXRlIHRvLlxuICogQHBhcmFtIG9iai5ib2R5IEJvZHkgb2JqZWN0IGZyb20gdGhlIEJvZHkgaW5zdGFuY2UuXG4gKiBAcmV0dXJucyB7dm9pZH1cbiAqL1xuY29uc3Qgd3JpdGVUb1N0cmVhbSA9IChkZXN0LCB7Ym9keX0pID0+IHtcblx0aWYgKGJvZHkgPT09IG51bGwpIHtcblx0XHQvLyBCb2R5IGlzIG51bGxcblx0XHRkZXN0LmVuZCgpO1xuXHR9IGVsc2UgaWYgKGlzQmxvYihib2R5KSkge1xuXHRcdC8vIEJvZHkgaXMgQmxvYlxuXHRcdGJvZHkuc3RyZWFtKCkucGlwZShkZXN0KTtcblx0fSBlbHNlIGlmIChCdWZmZXIuaXNCdWZmZXIoYm9keSkpIHtcblx0XHQvLyBCb2R5IGlzIGJ1ZmZlclxuXHRcdGRlc3Qud3JpdGUoYm9keSk7XG5cdFx0ZGVzdC5lbmQoKTtcblx0fSBlbHNlIHtcblx0XHQvLyBCb2R5IGlzIHN0cmVhbVxuXHRcdGJvZHkucGlwZShkZXN0KTtcblx0fVxufTtcblxuLyoqXG4gKiBIZWFkZXJzLmpzXG4gKlxuICogSGVhZGVycyBjbGFzcyBvZmZlcnMgY29udmVuaWVudCBoZWxwZXJzXG4gKi9cblxuY29uc3QgdmFsaWRhdGVIZWFkZXJOYW1lID0gdHlwZW9mIGh0dHAudmFsaWRhdGVIZWFkZXJOYW1lID09PSAnZnVuY3Rpb24nID9cblx0aHR0cC52YWxpZGF0ZUhlYWRlck5hbWUgOlxuXHRuYW1lID0+IHtcblx0XHRpZiAoIS9eW1xcXmBcXC1cXHchIyQlJicqKy58fl0rJC8udGVzdChuYW1lKSkge1xuXHRcdFx0Y29uc3QgZXJyID0gbmV3IFR5cGVFcnJvcihgSGVhZGVyIG5hbWUgbXVzdCBiZSBhIHZhbGlkIEhUVFAgdG9rZW4gWyR7bmFtZX1dYCk7XG5cdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXJyLCAnY29kZScsIHt2YWx1ZTogJ0VSUl9JTlZBTElEX0hUVFBfVE9LRU4nfSk7XG5cdFx0XHR0aHJvdyBlcnI7XG5cdFx0fVxuXHR9O1xuXG5jb25zdCB2YWxpZGF0ZUhlYWRlclZhbHVlID0gdHlwZW9mIGh0dHAudmFsaWRhdGVIZWFkZXJWYWx1ZSA9PT0gJ2Z1bmN0aW9uJyA/XG5cdGh0dHAudmFsaWRhdGVIZWFkZXJWYWx1ZSA6XG5cdChuYW1lLCB2YWx1ZSkgPT4ge1xuXHRcdGlmICgvW15cXHRcXHUwMDIwLVxcdTAwN0VcXHUwMDgwLVxcdTAwRkZdLy50ZXN0KHZhbHVlKSkge1xuXHRcdFx0Y29uc3QgZXJyID0gbmV3IFR5cGVFcnJvcihgSW52YWxpZCBjaGFyYWN0ZXIgaW4gaGVhZGVyIGNvbnRlbnQgW1wiJHtuYW1lfVwiXWApO1xuXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGVyciwgJ2NvZGUnLCB7dmFsdWU6ICdFUlJfSU5WQUxJRF9DSEFSJ30pO1xuXHRcdFx0dGhyb3cgZXJyO1xuXHRcdH1cblx0fTtcblxuLyoqXG4gKiBAdHlwZWRlZiB7SGVhZGVycyB8IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gfCBJdGVyYWJsZTxyZWFkb25seSBbc3RyaW5nLCBzdHJpbmddPiB8IEl0ZXJhYmxlPEl0ZXJhYmxlPHN0cmluZz4+fSBIZWFkZXJzSW5pdFxuICovXG5cbi8qKlxuICogVGhpcyBGZXRjaCBBUEkgaW50ZXJmYWNlIGFsbG93cyB5b3UgdG8gcGVyZm9ybSB2YXJpb3VzIGFjdGlvbnMgb24gSFRUUCByZXF1ZXN0IGFuZCByZXNwb25zZSBoZWFkZXJzLlxuICogVGhlc2UgYWN0aW9ucyBpbmNsdWRlIHJldHJpZXZpbmcsIHNldHRpbmcsIGFkZGluZyB0bywgYW5kIHJlbW92aW5nLlxuICogQSBIZWFkZXJzIG9iamVjdCBoYXMgYW4gYXNzb2NpYXRlZCBoZWFkZXIgbGlzdCwgd2hpY2ggaXMgaW5pdGlhbGx5IGVtcHR5IGFuZCBjb25zaXN0cyBvZiB6ZXJvIG9yIG1vcmUgbmFtZSBhbmQgdmFsdWUgcGFpcnMuXG4gKiBZb3UgY2FuIGFkZCB0byB0aGlzIHVzaW5nIG1ldGhvZHMgbGlrZSBhcHBlbmQoKSAoc2VlIEV4YW1wbGVzLilcbiAqIEluIGFsbCBtZXRob2RzIG9mIHRoaXMgaW50ZXJmYWNlLCBoZWFkZXIgbmFtZXMgYXJlIG1hdGNoZWQgYnkgY2FzZS1pbnNlbnNpdGl2ZSBieXRlIHNlcXVlbmNlLlxuICpcbiAqL1xuY2xhc3MgSGVhZGVycyBleHRlbmRzIFVSTFNlYXJjaFBhcmFtcyB7XG5cdC8qKlxuXHQgKiBIZWFkZXJzIGNsYXNzXG5cdCAqXG5cdCAqIEBjb25zdHJ1Y3RvclxuXHQgKiBAcGFyYW0ge0hlYWRlcnNJbml0fSBbaW5pdF0gLSBSZXNwb25zZSBoZWFkZXJzXG5cdCAqL1xuXHRjb25zdHJ1Y3Rvcihpbml0KSB7XG5cdFx0Ly8gVmFsaWRhdGUgYW5kIG5vcm1hbGl6ZSBpbml0IG9iamVjdCBpbiBbbmFtZSwgdmFsdWUocyldW11cblx0XHQvKiogQHR5cGUge3N0cmluZ1tdW119ICovXG5cdFx0bGV0IHJlc3VsdCA9IFtdO1xuXHRcdGlmIChpbml0IGluc3RhbmNlb2YgSGVhZGVycykge1xuXHRcdFx0Y29uc3QgcmF3ID0gaW5pdC5yYXcoKTtcblx0XHRcdGZvciAoY29uc3QgW25hbWUsIHZhbHVlc10gb2YgT2JqZWN0LmVudHJpZXMocmF3KSkge1xuXHRcdFx0XHRyZXN1bHQucHVzaCguLi52YWx1ZXMubWFwKHZhbHVlID0+IFtuYW1lLCB2YWx1ZV0pKTtcblx0XHRcdH1cblx0XHR9IGVsc2UgaWYgKGluaXQgPT0gbnVsbCkgOyBlbHNlIGlmICh0eXBlb2YgaW5pdCA9PT0gJ29iamVjdCcgJiYgIXV0aWwudHlwZXMuaXNCb3hlZFByaW1pdGl2ZShpbml0KSkge1xuXHRcdFx0Y29uc3QgbWV0aG9kID0gaW5pdFtTeW1ib2wuaXRlcmF0b3JdO1xuXHRcdFx0Ly8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWVxLW51bGwsIGVxZXFlcVxuXHRcdFx0aWYgKG1ldGhvZCA9PSBudWxsKSB7XG5cdFx0XHRcdC8vIFJlY29yZDxCeXRlU3RyaW5nLCBCeXRlU3RyaW5nPlxuXHRcdFx0XHRyZXN1bHQucHVzaCguLi5PYmplY3QuZW50cmllcyhpbml0KSk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRpZiAodHlwZW9mIG1ldGhvZCAhPT0gJ2Z1bmN0aW9uJykge1xuXHRcdFx0XHRcdHRocm93IG5ldyBUeXBlRXJyb3IoJ0hlYWRlciBwYWlycyBtdXN0IGJlIGl0ZXJhYmxlJyk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBTZXF1ZW5jZTxzZXF1ZW5jZTxCeXRlU3RyaW5nPj5cblx0XHRcdFx0Ly8gTm90ZTogcGVyIHNwZWMgd2UgaGF2ZSB0byBmaXJzdCBleGhhdXN0IHRoZSBsaXN0cyB0aGVuIHByb2Nlc3MgdGhlbVxuXHRcdFx0XHRyZXN1bHQgPSBbLi4uaW5pdF1cblx0XHRcdFx0XHQubWFwKHBhaXIgPT4ge1xuXHRcdFx0XHRcdFx0aWYgKFxuXHRcdFx0XHRcdFx0XHR0eXBlb2YgcGFpciAhPT0gJ29iamVjdCcgfHwgdXRpbC50eXBlcy5pc0JveGVkUHJpbWl0aXZlKHBhaXIpXG5cdFx0XHRcdFx0XHQpIHtcblx0XHRcdFx0XHRcdFx0dGhyb3cgbmV3IFR5cGVFcnJvcignRWFjaCBoZWFkZXIgcGFpciBtdXN0IGJlIGFuIGl0ZXJhYmxlIG9iamVjdCcpO1xuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRyZXR1cm4gWy4uLnBhaXJdO1xuXHRcdFx0XHRcdH0pLm1hcChwYWlyID0+IHtcblx0XHRcdFx0XHRcdGlmIChwYWlyLmxlbmd0aCAhPT0gMikge1xuXHRcdFx0XHRcdFx0XHR0aHJvdyBuZXcgVHlwZUVycm9yKCdFYWNoIGhlYWRlciBwYWlyIG11c3QgYmUgYSBuYW1lL3ZhbHVlIHR1cGxlJyk7XG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdHJldHVybiBbLi4ucGFpcl07XG5cdFx0XHRcdFx0fSk7XG5cdFx0XHR9XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRocm93IG5ldyBUeXBlRXJyb3IoJ0ZhaWxlZCB0byBjb25zdHJ1Y3QgXFwnSGVhZGVyc1xcJzogVGhlIHByb3ZpZGVkIHZhbHVlIGlzIG5vdCBvZiB0eXBlIFxcJyhzZXF1ZW5jZTxzZXF1ZW5jZTxCeXRlU3RyaW5nPj4gb3IgcmVjb3JkPEJ5dGVTdHJpbmcsIEJ5dGVTdHJpbmc+KScpO1xuXHRcdH1cblxuXHRcdC8vIFZhbGlkYXRlIGFuZCBsb3dlcmNhc2Vcblx0XHRyZXN1bHQgPVxuXHRcdFx0cmVzdWx0Lmxlbmd0aCA+IDAgP1xuXHRcdFx0XHRyZXN1bHQubWFwKChbbmFtZSwgdmFsdWVdKSA9PiB7XG5cdFx0XHRcdFx0dmFsaWRhdGVIZWFkZXJOYW1lKG5hbWUpO1xuXHRcdFx0XHRcdHZhbGlkYXRlSGVhZGVyVmFsdWUobmFtZSwgU3RyaW5nKHZhbHVlKSk7XG5cdFx0XHRcdFx0cmV0dXJuIFtTdHJpbmcobmFtZSkudG9Mb3dlckNhc2UoKSwgU3RyaW5nKHZhbHVlKV07XG5cdFx0XHRcdH0pIDpcblx0XHRcdFx0dW5kZWZpbmVkO1xuXG5cdFx0c3VwZXIocmVzdWx0KTtcblxuXHRcdC8vIFJldHVybmluZyBhIFByb3h5IHRoYXQgd2lsbCBsb3dlcmNhc2Uga2V5IG5hbWVzLCB2YWxpZGF0ZSBwYXJhbWV0ZXJzIGFuZCBzb3J0IGtleXNcblx0XHQvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc3RydWN0b3ItcmV0dXJuXG5cdFx0cmV0dXJuIG5ldyBQcm94eSh0aGlzLCB7XG5cdFx0XHRnZXQodGFyZ2V0LCBwLCByZWNlaXZlcikge1xuXHRcdFx0XHRzd2l0Y2ggKHApIHtcblx0XHRcdFx0XHRjYXNlICdhcHBlbmQnOlxuXHRcdFx0XHRcdGNhc2UgJ3NldCc6XG5cdFx0XHRcdFx0XHRyZXR1cm4gKG5hbWUsIHZhbHVlKSA9PiB7XG5cdFx0XHRcdFx0XHRcdHZhbGlkYXRlSGVhZGVyTmFtZShuYW1lKTtcblx0XHRcdFx0XHRcdFx0dmFsaWRhdGVIZWFkZXJWYWx1ZShuYW1lLCBTdHJpbmcodmFsdWUpKTtcblx0XHRcdFx0XHRcdFx0cmV0dXJuIFVSTFNlYXJjaFBhcmFtcy5wcm90b3R5cGVbcF0uY2FsbChcblx0XHRcdFx0XHRcdFx0XHRyZWNlaXZlcixcblx0XHRcdFx0XHRcdFx0XHRTdHJpbmcobmFtZSkudG9Mb3dlckNhc2UoKSxcblx0XHRcdFx0XHRcdFx0XHRTdHJpbmcodmFsdWUpXG5cdFx0XHRcdFx0XHRcdCk7XG5cdFx0XHRcdFx0XHR9O1xuXG5cdFx0XHRcdFx0Y2FzZSAnZGVsZXRlJzpcblx0XHRcdFx0XHRjYXNlICdoYXMnOlxuXHRcdFx0XHRcdGNhc2UgJ2dldEFsbCc6XG5cdFx0XHRcdFx0XHRyZXR1cm4gbmFtZSA9PiB7XG5cdFx0XHRcdFx0XHRcdHZhbGlkYXRlSGVhZGVyTmFtZShuYW1lKTtcblx0XHRcdFx0XHRcdFx0cmV0dXJuIFVSTFNlYXJjaFBhcmFtcy5wcm90b3R5cGVbcF0uY2FsbChcblx0XHRcdFx0XHRcdFx0XHRyZWNlaXZlcixcblx0XHRcdFx0XHRcdFx0XHRTdHJpbmcobmFtZSkudG9Mb3dlckNhc2UoKVxuXHRcdFx0XHRcdFx0XHQpO1xuXHRcdFx0XHRcdFx0fTtcblxuXHRcdFx0XHRcdGNhc2UgJ2tleXMnOlxuXHRcdFx0XHRcdFx0cmV0dXJuICgpID0+IHtcblx0XHRcdFx0XHRcdFx0dGFyZ2V0LnNvcnQoKTtcblx0XHRcdFx0XHRcdFx0cmV0dXJuIG5ldyBTZXQoVVJMU2VhcmNoUGFyYW1zLnByb3RvdHlwZS5rZXlzLmNhbGwodGFyZ2V0KSkua2V5cygpO1xuXHRcdFx0XHRcdFx0fTtcblxuXHRcdFx0XHRcdGRlZmF1bHQ6XG5cdFx0XHRcdFx0XHRyZXR1cm4gUmVmbGVjdC5nZXQodGFyZ2V0LCBwLCByZWNlaXZlcik7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdC8qIGM4IGlnbm9yZSBuZXh0ICovXG5cdFx0fSk7XG5cdH1cblxuXHRnZXQgW1N5bWJvbC50b1N0cmluZ1RhZ10oKSB7XG5cdFx0cmV0dXJuIHRoaXMuY29uc3RydWN0b3IubmFtZTtcblx0fVxuXG5cdHRvU3RyaW5nKCkge1xuXHRcdHJldHVybiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwodGhpcyk7XG5cdH1cblxuXHRnZXQobmFtZSkge1xuXHRcdGNvbnN0IHZhbHVlcyA9IHRoaXMuZ2V0QWxsKG5hbWUpO1xuXHRcdGlmICh2YWx1ZXMubGVuZ3RoID09PSAwKSB7XG5cdFx0XHRyZXR1cm4gbnVsbDtcblx0XHR9XG5cblx0XHRsZXQgdmFsdWUgPSB2YWx1ZXMuam9pbignLCAnKTtcblx0XHRpZiAoL15jb250ZW50LWVuY29kaW5nJC9pLnRlc3QobmFtZSkpIHtcblx0XHRcdHZhbHVlID0gdmFsdWUudG9Mb3dlckNhc2UoKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gdmFsdWU7XG5cdH1cblxuXHRmb3JFYWNoKGNhbGxiYWNrKSB7XG5cdFx0Zm9yIChjb25zdCBuYW1lIG9mIHRoaXMua2V5cygpKSB7XG5cdFx0XHRjYWxsYmFjayh0aGlzLmdldChuYW1lKSwgbmFtZSk7XG5cdFx0fVxuXHR9XG5cblx0KiB2YWx1ZXMoKSB7XG5cdFx0Zm9yIChjb25zdCBuYW1lIG9mIHRoaXMua2V5cygpKSB7XG5cdFx0XHR5aWVsZCB0aGlzLmdldChuYW1lKTtcblx0XHR9XG5cdH1cblxuXHQvKipcblx0ICogQHR5cGUgeygpID0+IEl0ZXJhYmxlSXRlcmF0b3I8W3N0cmluZywgc3RyaW5nXT59XG5cdCAqL1xuXHQqIGVudHJpZXMoKSB7XG5cdFx0Zm9yIChjb25zdCBuYW1lIG9mIHRoaXMua2V5cygpKSB7XG5cdFx0XHR5aWVsZCBbbmFtZSwgdGhpcy5nZXQobmFtZSldO1xuXHRcdH1cblx0fVxuXG5cdFtTeW1ib2wuaXRlcmF0b3JdKCkge1xuXHRcdHJldHVybiB0aGlzLmVudHJpZXMoKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBOb2RlLWZldGNoIG5vbi1zcGVjIG1ldGhvZFxuXHQgKiByZXR1cm5pbmcgYWxsIGhlYWRlcnMgYW5kIHRoZWlyIHZhbHVlcyBhcyBhcnJheVxuXHQgKiBAcmV0dXJucyB7UmVjb3JkPHN0cmluZywgc3RyaW5nW10+fVxuXHQgKi9cblx0cmF3KCkge1xuXHRcdHJldHVybiBbLi4udGhpcy5rZXlzKCldLnJlZHVjZSgocmVzdWx0LCBrZXkpID0+IHtcblx0XHRcdHJlc3VsdFtrZXldID0gdGhpcy5nZXRBbGwoa2V5KTtcblx0XHRcdHJldHVybiByZXN1bHQ7XG5cdFx0fSwge30pO1xuXHR9XG5cblx0LyoqXG5cdCAqIEZvciBiZXR0ZXIgY29uc29sZS5sb2coaGVhZGVycykgYW5kIGFsc28gdG8gY29udmVydCBIZWFkZXJzIGludG8gTm9kZS5qcyBSZXF1ZXN0IGNvbXBhdGlibGUgZm9ybWF0XG5cdCAqL1xuXHRbU3ltYm9sLmZvcignbm9kZWpzLnV0aWwuaW5zcGVjdC5jdXN0b20nKV0oKSB7XG5cdFx0cmV0dXJuIFsuLi50aGlzLmtleXMoKV0ucmVkdWNlKChyZXN1bHQsIGtleSkgPT4ge1xuXHRcdFx0Y29uc3QgdmFsdWVzID0gdGhpcy5nZXRBbGwoa2V5KTtcblx0XHRcdC8vIEh0dHAucmVxdWVzdCgpIG9ubHkgc3VwcG9ydHMgc3RyaW5nIGFzIEhvc3QgaGVhZGVyLlxuXHRcdFx0Ly8gVGhpcyBoYWNrIG1ha2VzIHNwZWNpZnlpbmcgY3VzdG9tIEhvc3QgaGVhZGVyIHBvc3NpYmxlLlxuXHRcdFx0aWYgKGtleSA9PT0gJ2hvc3QnKSB7XG5cdFx0XHRcdHJlc3VsdFtrZXldID0gdmFsdWVzWzBdO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0cmVzdWx0W2tleV0gPSB2YWx1ZXMubGVuZ3RoID4gMSA/IHZhbHVlcyA6IHZhbHVlc1swXTtcblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHJlc3VsdDtcblx0XHR9LCB7fSk7XG5cdH1cbn1cblxuLyoqXG4gKiBSZS1zaGFwaW5nIG9iamVjdCBmb3IgV2ViIElETCB0ZXN0c1xuICogT25seSBuZWVkIHRvIGRvIGl0IGZvciBvdmVycmlkZGVuIG1ldGhvZHNcbiAqL1xuT2JqZWN0LmRlZmluZVByb3BlcnRpZXMoXG5cdEhlYWRlcnMucHJvdG90eXBlLFxuXHRbJ2dldCcsICdlbnRyaWVzJywgJ2ZvckVhY2gnLCAndmFsdWVzJ10ucmVkdWNlKChyZXN1bHQsIHByb3BlcnR5KSA9PiB7XG5cdFx0cmVzdWx0W3Byb3BlcnR5XSA9IHtlbnVtZXJhYmxlOiB0cnVlfTtcblx0XHRyZXR1cm4gcmVzdWx0O1xuXHR9LCB7fSlcbik7XG5cbi8qKlxuICogQ3JlYXRlIGEgSGVhZGVycyBvYmplY3QgZnJvbSBhbiBodHRwLkluY29taW5nTWVzc2FnZS5yYXdIZWFkZXJzLCBpZ25vcmluZyB0aG9zZSB0aGF0IGRvXG4gKiBub3QgY29uZm9ybSB0byBIVFRQIGdyYW1tYXIgcHJvZHVjdGlvbnMuXG4gKiBAcGFyYW0ge2ltcG9ydCgnaHR0cCcpLkluY29taW5nTWVzc2FnZVsncmF3SGVhZGVycyddfSBoZWFkZXJzXG4gKi9cbmZ1bmN0aW9uIGZyb21SYXdIZWFkZXJzKGhlYWRlcnMgPSBbXSkge1xuXHRyZXR1cm4gbmV3IEhlYWRlcnMoXG5cdFx0aGVhZGVyc1xuXHRcdFx0Ly8gU3BsaXQgaW50byBwYWlyc1xuXHRcdFx0LnJlZHVjZSgocmVzdWx0LCB2YWx1ZSwgaW5kZXgsIGFycmF5KSA9PiB7XG5cdFx0XHRcdGlmIChpbmRleCAlIDIgPT09IDApIHtcblx0XHRcdFx0XHRyZXN1bHQucHVzaChhcnJheS5zbGljZShpbmRleCwgaW5kZXggKyAyKSk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRyZXR1cm4gcmVzdWx0O1xuXHRcdFx0fSwgW10pXG5cdFx0XHQuZmlsdGVyKChbbmFtZSwgdmFsdWVdKSA9PiB7XG5cdFx0XHRcdHRyeSB7XG5cdFx0XHRcdFx0dmFsaWRhdGVIZWFkZXJOYW1lKG5hbWUpO1xuXHRcdFx0XHRcdHZhbGlkYXRlSGVhZGVyVmFsdWUobmFtZSwgU3RyaW5nKHZhbHVlKSk7XG5cdFx0XHRcdFx0cmV0dXJuIHRydWU7XG5cdFx0XHRcdH0gY2F0Y2gge1xuXHRcdFx0XHRcdHJldHVybiBmYWxzZTtcblx0XHRcdFx0fVxuXHRcdFx0fSlcblxuXHQpO1xufVxuXG5jb25zdCByZWRpcmVjdFN0YXR1cyA9IG5ldyBTZXQoWzMwMSwgMzAyLCAzMDMsIDMwNywgMzA4XSk7XG5cbi8qKlxuICogUmVkaXJlY3QgY29kZSBtYXRjaGluZ1xuICpcbiAqIEBwYXJhbSB7bnVtYmVyfSBjb2RlIC0gU3RhdHVzIGNvZGVcbiAqIEByZXR1cm4ge2Jvb2xlYW59XG4gKi9cbmNvbnN0IGlzUmVkaXJlY3QgPSBjb2RlID0+IHtcblx0cmV0dXJuIHJlZGlyZWN0U3RhdHVzLmhhcyhjb2RlKTtcbn07XG5cbi8qKlxuICogUmVzcG9uc2UuanNcbiAqXG4gKiBSZXNwb25zZSBjbGFzcyBwcm92aWRlcyBjb250ZW50IGRlY29kaW5nXG4gKi9cblxuY29uc3QgSU5URVJOQUxTJDEgPSBTeW1ib2woJ1Jlc3BvbnNlIGludGVybmFscycpO1xuXG4vKipcbiAqIFJlc3BvbnNlIGNsYXNzXG4gKlxuICogQHBhcmFtICAgU3RyZWFtICBib2R5ICBSZWFkYWJsZSBzdHJlYW1cbiAqIEBwYXJhbSAgIE9iamVjdCAgb3B0cyAgUmVzcG9uc2Ugb3B0aW9uc1xuICogQHJldHVybiAgVm9pZFxuICovXG5jbGFzcyBSZXNwb25zZSBleHRlbmRzIEJvZHkge1xuXHRjb25zdHJ1Y3Rvcihib2R5ID0gbnVsbCwgb3B0aW9ucyA9IHt9KSB7XG5cdFx0c3VwZXIoYm9keSwgb3B0aW9ucyk7XG5cblx0XHRjb25zdCBzdGF0dXMgPSBvcHRpb25zLnN0YXR1cyB8fCAyMDA7XG5cdFx0Y29uc3QgaGVhZGVycyA9IG5ldyBIZWFkZXJzKG9wdGlvbnMuaGVhZGVycyk7XG5cblx0XHRpZiAoYm9keSAhPT0gbnVsbCAmJiAhaGVhZGVycy5oYXMoJ0NvbnRlbnQtVHlwZScpKSB7XG5cdFx0XHRjb25zdCBjb250ZW50VHlwZSA9IGV4dHJhY3RDb250ZW50VHlwZShib2R5KTtcblx0XHRcdGlmIChjb250ZW50VHlwZSkge1xuXHRcdFx0XHRoZWFkZXJzLmFwcGVuZCgnQ29udGVudC1UeXBlJywgY29udGVudFR5cGUpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHRoaXNbSU5URVJOQUxTJDFdID0ge1xuXHRcdFx0dXJsOiBvcHRpb25zLnVybCxcblx0XHRcdHN0YXR1cyxcblx0XHRcdHN0YXR1c1RleHQ6IG9wdGlvbnMuc3RhdHVzVGV4dCB8fCAnJyxcblx0XHRcdGhlYWRlcnMsXG5cdFx0XHRjb3VudGVyOiBvcHRpb25zLmNvdW50ZXIsXG5cdFx0XHRoaWdoV2F0ZXJNYXJrOiBvcHRpb25zLmhpZ2hXYXRlck1hcmtcblx0XHR9O1xuXHR9XG5cblx0Z2V0IHVybCgpIHtcblx0XHRyZXR1cm4gdGhpc1tJTlRFUk5BTFMkMV0udXJsIHx8ICcnO1xuXHR9XG5cblx0Z2V0IHN0YXR1cygpIHtcblx0XHRyZXR1cm4gdGhpc1tJTlRFUk5BTFMkMV0uc3RhdHVzO1xuXHR9XG5cblx0LyoqXG5cdCAqIENvbnZlbmllbmNlIHByb3BlcnR5IHJlcHJlc2VudGluZyBpZiB0aGUgcmVxdWVzdCBlbmRlZCBub3JtYWxseVxuXHQgKi9cblx0Z2V0IG9rKCkge1xuXHRcdHJldHVybiB0aGlzW0lOVEVSTkFMUyQxXS5zdGF0dXMgPj0gMjAwICYmIHRoaXNbSU5URVJOQUxTJDFdLnN0YXR1cyA8IDMwMDtcblx0fVxuXG5cdGdldCByZWRpcmVjdGVkKCkge1xuXHRcdHJldHVybiB0aGlzW0lOVEVSTkFMUyQxXS5jb3VudGVyID4gMDtcblx0fVxuXG5cdGdldCBzdGF0dXNUZXh0KCkge1xuXHRcdHJldHVybiB0aGlzW0lOVEVSTkFMUyQxXS5zdGF0dXNUZXh0O1xuXHR9XG5cblx0Z2V0IGhlYWRlcnMoKSB7XG5cdFx0cmV0dXJuIHRoaXNbSU5URVJOQUxTJDFdLmhlYWRlcnM7XG5cdH1cblxuXHRnZXQgaGlnaFdhdGVyTWFyaygpIHtcblx0XHRyZXR1cm4gdGhpc1tJTlRFUk5BTFMkMV0uaGlnaFdhdGVyTWFyaztcblx0fVxuXG5cdC8qKlxuXHQgKiBDbG9uZSB0aGlzIHJlc3BvbnNlXG5cdCAqXG5cdCAqIEByZXR1cm4gIFJlc3BvbnNlXG5cdCAqL1xuXHRjbG9uZSgpIHtcblx0XHRyZXR1cm4gbmV3IFJlc3BvbnNlKGNsb25lKHRoaXMsIHRoaXMuaGlnaFdhdGVyTWFyayksIHtcblx0XHRcdHVybDogdGhpcy51cmwsXG5cdFx0XHRzdGF0dXM6IHRoaXMuc3RhdHVzLFxuXHRcdFx0c3RhdHVzVGV4dDogdGhpcy5zdGF0dXNUZXh0LFxuXHRcdFx0aGVhZGVyczogdGhpcy5oZWFkZXJzLFxuXHRcdFx0b2s6IHRoaXMub2ssXG5cdFx0XHRyZWRpcmVjdGVkOiB0aGlzLnJlZGlyZWN0ZWQsXG5cdFx0XHRzaXplOiB0aGlzLnNpemVcblx0XHR9KTtcblx0fVxuXG5cdC8qKlxuXHQgKiBAcGFyYW0ge3N0cmluZ30gdXJsICAgIFRoZSBVUkwgdGhhdCB0aGUgbmV3IHJlc3BvbnNlIGlzIHRvIG9yaWdpbmF0ZSBmcm9tLlxuXHQgKiBAcGFyYW0ge251bWJlcn0gc3RhdHVzIEFuIG9wdGlvbmFsIHN0YXR1cyBjb2RlIGZvciB0aGUgcmVzcG9uc2UgKGUuZy4sIDMwMi4pXG5cdCAqIEByZXR1cm5zIHtSZXNwb25zZX0gICAgQSBSZXNwb25zZSBvYmplY3QuXG5cdCAqL1xuXHRzdGF0aWMgcmVkaXJlY3QodXJsLCBzdGF0dXMgPSAzMDIpIHtcblx0XHRpZiAoIWlzUmVkaXJlY3Qoc3RhdHVzKSkge1xuXHRcdFx0dGhyb3cgbmV3IFJhbmdlRXJyb3IoJ0ZhaWxlZCB0byBleGVjdXRlIFwicmVkaXJlY3RcIiBvbiBcInJlc3BvbnNlXCI6IEludmFsaWQgc3RhdHVzIGNvZGUnKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gbmV3IFJlc3BvbnNlKG51bGwsIHtcblx0XHRcdGhlYWRlcnM6IHtcblx0XHRcdFx0bG9jYXRpb246IG5ldyBVUkwodXJsKS50b1N0cmluZygpXG5cdFx0XHR9LFxuXHRcdFx0c3RhdHVzXG5cdFx0fSk7XG5cdH1cblxuXHRnZXQgW1N5bWJvbC50b1N0cmluZ1RhZ10oKSB7XG5cdFx0cmV0dXJuICdSZXNwb25zZSc7XG5cdH1cbn1cblxuT2JqZWN0LmRlZmluZVByb3BlcnRpZXMoUmVzcG9uc2UucHJvdG90eXBlLCB7XG5cdHVybDoge2VudW1lcmFibGU6IHRydWV9LFxuXHRzdGF0dXM6IHtlbnVtZXJhYmxlOiB0cnVlfSxcblx0b2s6IHtlbnVtZXJhYmxlOiB0cnVlfSxcblx0cmVkaXJlY3RlZDoge2VudW1lcmFibGU6IHRydWV9LFxuXHRzdGF0dXNUZXh0OiB7ZW51bWVyYWJsZTogdHJ1ZX0sXG5cdGhlYWRlcnM6IHtlbnVtZXJhYmxlOiB0cnVlfSxcblx0Y2xvbmU6IHtlbnVtZXJhYmxlOiB0cnVlfVxufSk7XG5cbmNvbnN0IGdldFNlYXJjaCA9IHBhcnNlZFVSTCA9PiB7XG5cdGlmIChwYXJzZWRVUkwuc2VhcmNoKSB7XG5cdFx0cmV0dXJuIHBhcnNlZFVSTC5zZWFyY2g7XG5cdH1cblxuXHRjb25zdCBsYXN0T2Zmc2V0ID0gcGFyc2VkVVJMLmhyZWYubGVuZ3RoIC0gMTtcblx0Y29uc3QgaGFzaCA9IHBhcnNlZFVSTC5oYXNoIHx8IChwYXJzZWRVUkwuaHJlZltsYXN0T2Zmc2V0XSA9PT0gJyMnID8gJyMnIDogJycpO1xuXHRyZXR1cm4gcGFyc2VkVVJMLmhyZWZbbGFzdE9mZnNldCAtIGhhc2gubGVuZ3RoXSA9PT0gJz8nID8gJz8nIDogJyc7XG59O1xuXG5jb25zdCBJTlRFUk5BTFMkMiA9IFN5bWJvbCgnUmVxdWVzdCBpbnRlcm5hbHMnKTtcblxuLyoqXG4gKiBDaGVjayBpZiBgb2JqYCBpcyBhbiBpbnN0YW5jZSBvZiBSZXF1ZXN0LlxuICpcbiAqIEBwYXJhbSAgeyp9IG9ialxuICogQHJldHVybiB7Ym9vbGVhbn1cbiAqL1xuY29uc3QgaXNSZXF1ZXN0ID0gb2JqZWN0ID0+IHtcblx0cmV0dXJuIChcblx0XHR0eXBlb2Ygb2JqZWN0ID09PSAnb2JqZWN0JyAmJlxuXHRcdHR5cGVvZiBvYmplY3RbSU5URVJOQUxTJDJdID09PSAnb2JqZWN0J1xuXHQpO1xufTtcblxuLyoqXG4gKiBSZXF1ZXN0IGNsYXNzXG4gKlxuICogQHBhcmFtICAgTWl4ZWQgICBpbnB1dCAgVXJsIG9yIFJlcXVlc3QgaW5zdGFuY2VcbiAqIEBwYXJhbSAgIE9iamVjdCAgaW5pdCAgIEN1c3RvbSBvcHRpb25zXG4gKiBAcmV0dXJuICBWb2lkXG4gKi9cbmNsYXNzIFJlcXVlc3QgZXh0ZW5kcyBCb2R5IHtcblx0Y29uc3RydWN0b3IoaW5wdXQsIGluaXQgPSB7fSkge1xuXHRcdGxldCBwYXJzZWRVUkw7XG5cblx0XHQvLyBOb3JtYWxpemUgaW5wdXQgYW5kIGZvcmNlIFVSTCB0byBiZSBlbmNvZGVkIGFzIFVURi04IChodHRwczovL2dpdGh1Yi5jb20vbm9kZS1mZXRjaC9ub2RlLWZldGNoL2lzc3Vlcy8yNDUpXG5cdFx0aWYgKGlzUmVxdWVzdChpbnB1dCkpIHtcblx0XHRcdHBhcnNlZFVSTCA9IG5ldyBVUkwoaW5wdXQudXJsKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0cGFyc2VkVVJMID0gbmV3IFVSTChpbnB1dCk7XG5cdFx0XHRpbnB1dCA9IHt9O1xuXHRcdH1cblxuXHRcdGxldCBtZXRob2QgPSBpbml0Lm1ldGhvZCB8fCBpbnB1dC5tZXRob2QgfHwgJ0dFVCc7XG5cdFx0bWV0aG9kID0gbWV0aG9kLnRvVXBwZXJDYXNlKCk7XG5cblx0XHQvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tZXEtbnVsbCwgZXFlcWVxXG5cdFx0aWYgKCgoaW5pdC5ib2R5ICE9IG51bGwgfHwgaXNSZXF1ZXN0KGlucHV0KSkgJiYgaW5wdXQuYm9keSAhPT0gbnVsbCkgJiZcblx0XHRcdChtZXRob2QgPT09ICdHRVQnIHx8IG1ldGhvZCA9PT0gJ0hFQUQnKSkge1xuXHRcdFx0dGhyb3cgbmV3IFR5cGVFcnJvcignUmVxdWVzdCB3aXRoIEdFVC9IRUFEIG1ldGhvZCBjYW5ub3QgaGF2ZSBib2R5Jyk7XG5cdFx0fVxuXG5cdFx0Y29uc3QgaW5wdXRCb2R5ID0gaW5pdC5ib2R5ID9cblx0XHRcdGluaXQuYm9keSA6XG5cdFx0XHQoaXNSZXF1ZXN0KGlucHV0KSAmJiBpbnB1dC5ib2R5ICE9PSBudWxsID9cblx0XHRcdFx0Y2xvbmUoaW5wdXQpIDpcblx0XHRcdFx0bnVsbCk7XG5cblx0XHRzdXBlcihpbnB1dEJvZHksIHtcblx0XHRcdHNpemU6IGluaXQuc2l6ZSB8fCBpbnB1dC5zaXplIHx8IDBcblx0XHR9KTtcblxuXHRcdGNvbnN0IGhlYWRlcnMgPSBuZXcgSGVhZGVycyhpbml0LmhlYWRlcnMgfHwgaW5wdXQuaGVhZGVycyB8fCB7fSk7XG5cblx0XHRpZiAoaW5wdXRCb2R5ICE9PSBudWxsICYmICFoZWFkZXJzLmhhcygnQ29udGVudC1UeXBlJykpIHtcblx0XHRcdGNvbnN0IGNvbnRlbnRUeXBlID0gZXh0cmFjdENvbnRlbnRUeXBlKGlucHV0Qm9keSwgdGhpcyk7XG5cdFx0XHRpZiAoY29udGVudFR5cGUpIHtcblx0XHRcdFx0aGVhZGVycy5hcHBlbmQoJ0NvbnRlbnQtVHlwZScsIGNvbnRlbnRUeXBlKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRsZXQgc2lnbmFsID0gaXNSZXF1ZXN0KGlucHV0KSA/XG5cdFx0XHRpbnB1dC5zaWduYWwgOlxuXHRcdFx0bnVsbDtcblx0XHRpZiAoJ3NpZ25hbCcgaW4gaW5pdCkge1xuXHRcdFx0c2lnbmFsID0gaW5pdC5zaWduYWw7XG5cdFx0fVxuXG5cdFx0aWYgKHNpZ25hbCAhPT0gbnVsbCAmJiAhaXNBYm9ydFNpZ25hbChzaWduYWwpKSB7XG5cdFx0XHR0aHJvdyBuZXcgVHlwZUVycm9yKCdFeHBlY3RlZCBzaWduYWwgdG8gYmUgYW4gaW5zdGFuY2VvZiBBYm9ydFNpZ25hbCcpO1xuXHRcdH1cblxuXHRcdHRoaXNbSU5URVJOQUxTJDJdID0ge1xuXHRcdFx0bWV0aG9kLFxuXHRcdFx0cmVkaXJlY3Q6IGluaXQucmVkaXJlY3QgfHwgaW5wdXQucmVkaXJlY3QgfHwgJ2ZvbGxvdycsXG5cdFx0XHRoZWFkZXJzLFxuXHRcdFx0cGFyc2VkVVJMLFxuXHRcdFx0c2lnbmFsXG5cdFx0fTtcblxuXHRcdC8vIE5vZGUtZmV0Y2gtb25seSBvcHRpb25zXG5cdFx0dGhpcy5mb2xsb3cgPSBpbml0LmZvbGxvdyA9PT0gdW5kZWZpbmVkID8gKGlucHV0LmZvbGxvdyA9PT0gdW5kZWZpbmVkID8gMjAgOiBpbnB1dC5mb2xsb3cpIDogaW5pdC5mb2xsb3c7XG5cdFx0dGhpcy5jb21wcmVzcyA9IGluaXQuY29tcHJlc3MgPT09IHVuZGVmaW5lZCA/IChpbnB1dC5jb21wcmVzcyA9PT0gdW5kZWZpbmVkID8gdHJ1ZSA6IGlucHV0LmNvbXByZXNzKSA6IGluaXQuY29tcHJlc3M7XG5cdFx0dGhpcy5jb3VudGVyID0gaW5pdC5jb3VudGVyIHx8IGlucHV0LmNvdW50ZXIgfHwgMDtcblx0XHR0aGlzLmFnZW50ID0gaW5pdC5hZ2VudCB8fCBpbnB1dC5hZ2VudDtcblx0XHR0aGlzLmhpZ2hXYXRlck1hcmsgPSBpbml0LmhpZ2hXYXRlck1hcmsgfHwgaW5wdXQuaGlnaFdhdGVyTWFyayB8fCAxNjM4NDtcblx0XHR0aGlzLmluc2VjdXJlSFRUUFBhcnNlciA9IGluaXQuaW5zZWN1cmVIVFRQUGFyc2VyIHx8IGlucHV0Lmluc2VjdXJlSFRUUFBhcnNlciB8fCBmYWxzZTtcblx0fVxuXG5cdGdldCBtZXRob2QoKSB7XG5cdFx0cmV0dXJuIHRoaXNbSU5URVJOQUxTJDJdLm1ldGhvZDtcblx0fVxuXG5cdGdldCB1cmwoKSB7XG5cdFx0cmV0dXJuIHVybC5mb3JtYXQodGhpc1tJTlRFUk5BTFMkMl0ucGFyc2VkVVJMKTtcblx0fVxuXG5cdGdldCBoZWFkZXJzKCkge1xuXHRcdHJldHVybiB0aGlzW0lOVEVSTkFMUyQyXS5oZWFkZXJzO1xuXHR9XG5cblx0Z2V0IHJlZGlyZWN0KCkge1xuXHRcdHJldHVybiB0aGlzW0lOVEVSTkFMUyQyXS5yZWRpcmVjdDtcblx0fVxuXG5cdGdldCBzaWduYWwoKSB7XG5cdFx0cmV0dXJuIHRoaXNbSU5URVJOQUxTJDJdLnNpZ25hbDtcblx0fVxuXG5cdC8qKlxuXHQgKiBDbG9uZSB0aGlzIHJlcXVlc3Rcblx0ICpcblx0ICogQHJldHVybiAgUmVxdWVzdFxuXHQgKi9cblx0Y2xvbmUoKSB7XG5cdFx0cmV0dXJuIG5ldyBSZXF1ZXN0KHRoaXMpO1xuXHR9XG5cblx0Z2V0IFtTeW1ib2wudG9TdHJpbmdUYWddKCkge1xuXHRcdHJldHVybiAnUmVxdWVzdCc7XG5cdH1cbn1cblxuT2JqZWN0LmRlZmluZVByb3BlcnRpZXMoUmVxdWVzdC5wcm90b3R5cGUsIHtcblx0bWV0aG9kOiB7ZW51bWVyYWJsZTogdHJ1ZX0sXG5cdHVybDoge2VudW1lcmFibGU6IHRydWV9LFxuXHRoZWFkZXJzOiB7ZW51bWVyYWJsZTogdHJ1ZX0sXG5cdHJlZGlyZWN0OiB7ZW51bWVyYWJsZTogdHJ1ZX0sXG5cdGNsb25lOiB7ZW51bWVyYWJsZTogdHJ1ZX0sXG5cdHNpZ25hbDoge2VudW1lcmFibGU6IHRydWV9XG59KTtcblxuLyoqXG4gKiBDb252ZXJ0IGEgUmVxdWVzdCB0byBOb2RlLmpzIGh0dHAgcmVxdWVzdCBvcHRpb25zLlxuICpcbiAqIEBwYXJhbSAgIFJlcXVlc3QgIEEgUmVxdWVzdCBpbnN0YW5jZVxuICogQHJldHVybiAgT2JqZWN0ICAgVGhlIG9wdGlvbnMgb2JqZWN0IHRvIGJlIHBhc3NlZCB0byBodHRwLnJlcXVlc3RcbiAqL1xuY29uc3QgZ2V0Tm9kZVJlcXVlc3RPcHRpb25zID0gcmVxdWVzdCA9PiB7XG5cdGNvbnN0IHtwYXJzZWRVUkx9ID0gcmVxdWVzdFtJTlRFUk5BTFMkMl07XG5cdGNvbnN0IGhlYWRlcnMgPSBuZXcgSGVhZGVycyhyZXF1ZXN0W0lOVEVSTkFMUyQyXS5oZWFkZXJzKTtcblxuXHQvLyBGZXRjaCBzdGVwIDEuM1xuXHRpZiAoIWhlYWRlcnMuaGFzKCdBY2NlcHQnKSkge1xuXHRcdGhlYWRlcnMuc2V0KCdBY2NlcHQnLCAnKi8qJyk7XG5cdH1cblxuXHQvLyBIVFRQLW5ldHdvcmstb3ItY2FjaGUgZmV0Y2ggc3RlcHMgMi40LTIuN1xuXHRsZXQgY29udGVudExlbmd0aFZhbHVlID0gbnVsbDtcblx0aWYgKHJlcXVlc3QuYm9keSA9PT0gbnVsbCAmJiAvXihwb3N0fHB1dCkkL2kudGVzdChyZXF1ZXN0Lm1ldGhvZCkpIHtcblx0XHRjb250ZW50TGVuZ3RoVmFsdWUgPSAnMCc7XG5cdH1cblxuXHRpZiAocmVxdWVzdC5ib2R5ICE9PSBudWxsKSB7XG5cdFx0Y29uc3QgdG90YWxCeXRlcyA9IGdldFRvdGFsQnl0ZXMocmVxdWVzdCk7XG5cdFx0Ly8gU2V0IENvbnRlbnQtTGVuZ3RoIGlmIHRvdGFsQnl0ZXMgaXMgYSBudW1iZXIgKHRoYXQgaXMgbm90IE5hTilcblx0XHRpZiAodHlwZW9mIHRvdGFsQnl0ZXMgPT09ICdudW1iZXInICYmICFOdW1iZXIuaXNOYU4odG90YWxCeXRlcykpIHtcblx0XHRcdGNvbnRlbnRMZW5ndGhWYWx1ZSA9IFN0cmluZyh0b3RhbEJ5dGVzKTtcblx0XHR9XG5cdH1cblxuXHRpZiAoY29udGVudExlbmd0aFZhbHVlKSB7XG5cdFx0aGVhZGVycy5zZXQoJ0NvbnRlbnQtTGVuZ3RoJywgY29udGVudExlbmd0aFZhbHVlKTtcblx0fVxuXG5cdC8vIEhUVFAtbmV0d29yay1vci1jYWNoZSBmZXRjaCBzdGVwIDIuMTFcblx0aWYgKCFoZWFkZXJzLmhhcygnVXNlci1BZ2VudCcpKSB7XG5cdFx0aGVhZGVycy5zZXQoJ1VzZXItQWdlbnQnLCAnbm9kZS1mZXRjaCcpO1xuXHR9XG5cblx0Ly8gSFRUUC1uZXR3b3JrLW9yLWNhY2hlIGZldGNoIHN0ZXAgMi4xNVxuXHRpZiAocmVxdWVzdC5jb21wcmVzcyAmJiAhaGVhZGVycy5oYXMoJ0FjY2VwdC1FbmNvZGluZycpKSB7XG5cdFx0aGVhZGVycy5zZXQoJ0FjY2VwdC1FbmNvZGluZycsICdnemlwLGRlZmxhdGUsYnInKTtcblx0fVxuXG5cdGxldCB7YWdlbnR9ID0gcmVxdWVzdDtcblx0aWYgKHR5cGVvZiBhZ2VudCA9PT0gJ2Z1bmN0aW9uJykge1xuXHRcdGFnZW50ID0gYWdlbnQocGFyc2VkVVJMKTtcblx0fVxuXG5cdGlmICghaGVhZGVycy5oYXMoJ0Nvbm5lY3Rpb24nKSAmJiAhYWdlbnQpIHtcblx0XHRoZWFkZXJzLnNldCgnQ29ubmVjdGlvbicsICdjbG9zZScpO1xuXHR9XG5cblx0Ly8gSFRUUC1uZXR3b3JrIGZldGNoIHN0ZXAgNC4yXG5cdC8vIGNodW5rZWQgZW5jb2RpbmcgaXMgaGFuZGxlZCBieSBOb2RlLmpzXG5cblx0Y29uc3Qgc2VhcmNoID0gZ2V0U2VhcmNoKHBhcnNlZFVSTCk7XG5cblx0Ly8gTWFudWFsbHkgc3ByZWFkIHRoZSBVUkwgb2JqZWN0IGluc3RlYWQgb2Ygc3ByZWFkIHN5bnRheFxuXHRjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHtcblx0XHRwYXRoOiBwYXJzZWRVUkwucGF0aG5hbWUgKyBzZWFyY2gsXG5cdFx0cGF0aG5hbWU6IHBhcnNlZFVSTC5wYXRobmFtZSxcblx0XHRob3N0bmFtZTogcGFyc2VkVVJMLmhvc3RuYW1lLFxuXHRcdHByb3RvY29sOiBwYXJzZWRVUkwucHJvdG9jb2wsXG5cdFx0cG9ydDogcGFyc2VkVVJMLnBvcnQsXG5cdFx0aGFzaDogcGFyc2VkVVJMLmhhc2gsXG5cdFx0c2VhcmNoOiBwYXJzZWRVUkwuc2VhcmNoLFxuXHRcdHF1ZXJ5OiBwYXJzZWRVUkwucXVlcnksXG5cdFx0aHJlZjogcGFyc2VkVVJMLmhyZWYsXG5cdFx0bWV0aG9kOiByZXF1ZXN0Lm1ldGhvZCxcblx0XHRoZWFkZXJzOiBoZWFkZXJzW1N5bWJvbC5mb3IoJ25vZGVqcy51dGlsLmluc3BlY3QuY3VzdG9tJyldKCksXG5cdFx0aW5zZWN1cmVIVFRQUGFyc2VyOiByZXF1ZXN0Lmluc2VjdXJlSFRUUFBhcnNlcixcblx0XHRhZ2VudFxuXHR9O1xuXG5cdHJldHVybiByZXF1ZXN0T3B0aW9ucztcbn07XG5cbi8qKlxuICogQWJvcnRFcnJvciBpbnRlcmZhY2UgZm9yIGNhbmNlbGxlZCByZXF1ZXN0c1xuICovXG5jbGFzcyBBYm9ydEVycm9yIGV4dGVuZHMgRmV0Y2hCYXNlRXJyb3Ige1xuXHRjb25zdHJ1Y3RvcihtZXNzYWdlLCB0eXBlID0gJ2Fib3J0ZWQnKSB7XG5cdFx0c3VwZXIobWVzc2FnZSwgdHlwZSk7XG5cdH1cbn1cblxuLyoqXG4gKiBJbmRleC5qc1xuICpcbiAqIGEgcmVxdWVzdCBBUEkgY29tcGF0aWJsZSB3aXRoIHdpbmRvdy5mZXRjaFxuICpcbiAqIEFsbCBzcGVjIGFsZ29yaXRobSBzdGVwIG51bWJlcnMgYXJlIGJhc2VkIG9uIGh0dHBzOi8vZmV0Y2guc3BlYy53aGF0d2cub3JnL2NvbW1pdC1zbmFwc2hvdHMvYWU3MTY4MjJjYjNhNjE4NDMyMjZjZDA5MGVlZmM2NTg5NDQ2YzFkMi8uXG4gKi9cblxuY29uc3Qgc3VwcG9ydGVkU2NoZW1hcyA9IG5ldyBTZXQoWydkYXRhOicsICdodHRwOicsICdodHRwczonXSk7XG5cbi8qKlxuICogRmV0Y2ggZnVuY3Rpb25cbiAqXG4gKiBAcGFyYW0gICB7c3RyaW5nIHwgVVJMIHwgaW1wb3J0KCcuL3JlcXVlc3QnKS5kZWZhdWx0fSB1cmwgLSBBYnNvbHV0ZSB1cmwgb3IgUmVxdWVzdCBpbnN0YW5jZVxuICogQHBhcmFtICAgeyp9IFtvcHRpb25zX10gLSBGZXRjaCBvcHRpb25zXG4gKiBAcmV0dXJuICB7UHJvbWlzZTxpbXBvcnQoJy4vcmVzcG9uc2UnKS5kZWZhdWx0Pn1cbiAqL1xuYXN5bmMgZnVuY3Rpb24gZmV0Y2godXJsLCBvcHRpb25zXykge1xuXHRyZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuXHRcdC8vIEJ1aWxkIHJlcXVlc3Qgb2JqZWN0XG5cdFx0Y29uc3QgcmVxdWVzdCA9IG5ldyBSZXF1ZXN0KHVybCwgb3B0aW9uc18pO1xuXHRcdGNvbnN0IG9wdGlvbnMgPSBnZXROb2RlUmVxdWVzdE9wdGlvbnMocmVxdWVzdCk7XG5cdFx0aWYgKCFzdXBwb3J0ZWRTY2hlbWFzLmhhcyhvcHRpb25zLnByb3RvY29sKSkge1xuXHRcdFx0dGhyb3cgbmV3IFR5cGVFcnJvcihgbm9kZS1mZXRjaCBjYW5ub3QgbG9hZCAke3VybH0uIFVSTCBzY2hlbWUgXCIke29wdGlvbnMucHJvdG9jb2wucmVwbGFjZSgvOiQvLCAnJyl9XCIgaXMgbm90IHN1cHBvcnRlZC5gKTtcblx0XHR9XG5cblx0XHRpZiAob3B0aW9ucy5wcm90b2NvbCA9PT0gJ2RhdGE6Jykge1xuXHRcdFx0Y29uc3QgZGF0YSA9IGRhdGFVcmlUb0J1ZmZlcihyZXF1ZXN0LnVybCk7XG5cdFx0XHRjb25zdCByZXNwb25zZSA9IG5ldyBSZXNwb25zZShkYXRhLCB7aGVhZGVyczogeydDb250ZW50LVR5cGUnOiBkYXRhLnR5cGVGdWxsfX0pO1xuXHRcdFx0cmVzb2x2ZShyZXNwb25zZSk7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Ly8gV3JhcCBodHRwLnJlcXVlc3QgaW50byBmZXRjaFxuXHRcdGNvbnN0IHNlbmQgPSAob3B0aW9ucy5wcm90b2NvbCA9PT0gJ2h0dHBzOicgPyBodHRwcyA6IGh0dHApLnJlcXVlc3Q7XG5cdFx0Y29uc3Qge3NpZ25hbH0gPSByZXF1ZXN0O1xuXHRcdGxldCByZXNwb25zZSA9IG51bGw7XG5cblx0XHRjb25zdCBhYm9ydCA9ICgpID0+IHtcblx0XHRcdGNvbnN0IGVycm9yID0gbmV3IEFib3J0RXJyb3IoJ1RoZSBvcGVyYXRpb24gd2FzIGFib3J0ZWQuJyk7XG5cdFx0XHRyZWplY3QoZXJyb3IpO1xuXHRcdFx0aWYgKHJlcXVlc3QuYm9keSAmJiByZXF1ZXN0LmJvZHkgaW5zdGFuY2VvZiBTdHJlYW0uUmVhZGFibGUpIHtcblx0XHRcdFx0cmVxdWVzdC5ib2R5LmRlc3Ryb3koZXJyb3IpO1xuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIXJlc3BvbnNlIHx8ICFyZXNwb25zZS5ib2R5KSB7XG5cdFx0XHRcdHJldHVybjtcblx0XHRcdH1cblxuXHRcdFx0cmVzcG9uc2UuYm9keS5lbWl0KCdlcnJvcicsIGVycm9yKTtcblx0XHR9O1xuXG5cdFx0aWYgKHNpZ25hbCAmJiBzaWduYWwuYWJvcnRlZCkge1xuXHRcdFx0YWJvcnQoKTtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRjb25zdCBhYm9ydEFuZEZpbmFsaXplID0gKCkgPT4ge1xuXHRcdFx0YWJvcnQoKTtcblx0XHRcdGZpbmFsaXplKCk7XG5cdFx0fTtcblxuXHRcdC8vIFNlbmQgcmVxdWVzdFxuXHRcdGNvbnN0IHJlcXVlc3RfID0gc2VuZChvcHRpb25zKTtcblxuXHRcdGlmIChzaWduYWwpIHtcblx0XHRcdHNpZ25hbC5hZGRFdmVudExpc3RlbmVyKCdhYm9ydCcsIGFib3J0QW5kRmluYWxpemUpO1xuXHRcdH1cblxuXHRcdGNvbnN0IGZpbmFsaXplID0gKCkgPT4ge1xuXHRcdFx0cmVxdWVzdF8uYWJvcnQoKTtcblx0XHRcdGlmIChzaWduYWwpIHtcblx0XHRcdFx0c2lnbmFsLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2Fib3J0JywgYWJvcnRBbmRGaW5hbGl6ZSk7XG5cdFx0XHR9XG5cdFx0fTtcblxuXHRcdHJlcXVlc3RfLm9uKCdlcnJvcicsIGVyciA9PiB7XG5cdFx0XHRyZWplY3QobmV3IEZldGNoRXJyb3IoYHJlcXVlc3QgdG8gJHtyZXF1ZXN0LnVybH0gZmFpbGVkLCByZWFzb246ICR7ZXJyLm1lc3NhZ2V9YCwgJ3N5c3RlbScsIGVycikpO1xuXHRcdFx0ZmluYWxpemUoKTtcblx0XHR9KTtcblxuXHRcdHJlcXVlc3RfLm9uKCdyZXNwb25zZScsIHJlc3BvbnNlXyA9PiB7XG5cdFx0XHRyZXF1ZXN0Xy5zZXRUaW1lb3V0KDApO1xuXHRcdFx0Y29uc3QgaGVhZGVycyA9IGZyb21SYXdIZWFkZXJzKHJlc3BvbnNlXy5yYXdIZWFkZXJzKTtcblxuXHRcdFx0Ly8gSFRUUCBmZXRjaCBzdGVwIDVcblx0XHRcdGlmIChpc1JlZGlyZWN0KHJlc3BvbnNlXy5zdGF0dXNDb2RlKSkge1xuXHRcdFx0XHQvLyBIVFRQIGZldGNoIHN0ZXAgNS4yXG5cdFx0XHRcdGNvbnN0IGxvY2F0aW9uID0gaGVhZGVycy5nZXQoJ0xvY2F0aW9uJyk7XG5cblx0XHRcdFx0Ly8gSFRUUCBmZXRjaCBzdGVwIDUuM1xuXHRcdFx0XHRjb25zdCBsb2NhdGlvblVSTCA9IGxvY2F0aW9uID09PSBudWxsID8gbnVsbCA6IG5ldyBVUkwobG9jYXRpb24sIHJlcXVlc3QudXJsKTtcblxuXHRcdFx0XHQvLyBIVFRQIGZldGNoIHN0ZXAgNS41XG5cdFx0XHRcdHN3aXRjaCAocmVxdWVzdC5yZWRpcmVjdCkge1xuXHRcdFx0XHRcdGNhc2UgJ2Vycm9yJzpcblx0XHRcdFx0XHRcdHJlamVjdChuZXcgRmV0Y2hFcnJvcihgdXJpIHJlcXVlc3RlZCByZXNwb25kcyB3aXRoIGEgcmVkaXJlY3QsIHJlZGlyZWN0IG1vZGUgaXMgc2V0IHRvIGVycm9yOiAke3JlcXVlc3QudXJsfWAsICduby1yZWRpcmVjdCcpKTtcblx0XHRcdFx0XHRcdGZpbmFsaXplKCk7XG5cdFx0XHRcdFx0XHRyZXR1cm47XG5cdFx0XHRcdFx0Y2FzZSAnbWFudWFsJzpcblx0XHRcdFx0XHRcdC8vIE5vZGUtZmV0Y2gtc3BlY2lmaWMgc3RlcDogbWFrZSBtYW51YWwgcmVkaXJlY3QgYSBiaXQgZWFzaWVyIHRvIHVzZSBieSBzZXR0aW5nIHRoZSBMb2NhdGlvbiBoZWFkZXIgdmFsdWUgdG8gdGhlIHJlc29sdmVkIFVSTC5cblx0XHRcdFx0XHRcdGlmIChsb2NhdGlvblVSTCAhPT0gbnVsbCkge1xuXHRcdFx0XHRcdFx0XHQvLyBIYW5kbGUgY29ycnVwdGVkIGhlYWRlclxuXHRcdFx0XHRcdFx0XHR0cnkge1xuXHRcdFx0XHRcdFx0XHRcdGhlYWRlcnMuc2V0KCdMb2NhdGlvbicsIGxvY2F0aW9uVVJMKTtcblx0XHRcdFx0XHRcdFx0XHQvKiBjOCBpZ25vcmUgbmV4dCAzICovXG5cdFx0XHRcdFx0XHRcdH0gY2F0Y2ggKGVycm9yKSB7XG5cdFx0XHRcdFx0XHRcdFx0cmVqZWN0KGVycm9yKTtcblx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRjYXNlICdmb2xsb3cnOiB7XG5cdFx0XHRcdFx0XHQvLyBIVFRQLXJlZGlyZWN0IGZldGNoIHN0ZXAgMlxuXHRcdFx0XHRcdFx0aWYgKGxvY2F0aW9uVVJMID09PSBudWxsKSB7XG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHQvLyBIVFRQLXJlZGlyZWN0IGZldGNoIHN0ZXAgNVxuXHRcdFx0XHRcdFx0aWYgKHJlcXVlc3QuY291bnRlciA+PSByZXF1ZXN0LmZvbGxvdykge1xuXHRcdFx0XHRcdFx0XHRyZWplY3QobmV3IEZldGNoRXJyb3IoYG1heGltdW0gcmVkaXJlY3QgcmVhY2hlZCBhdDogJHtyZXF1ZXN0LnVybH1gLCAnbWF4LXJlZGlyZWN0JykpO1xuXHRcdFx0XHRcdFx0XHRmaW5hbGl6ZSgpO1xuXHRcdFx0XHRcdFx0XHRyZXR1cm47XG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdC8vIEhUVFAtcmVkaXJlY3QgZmV0Y2ggc3RlcCA2IChjb3VudGVyIGluY3JlbWVudClcblx0XHRcdFx0XHRcdC8vIENyZWF0ZSBhIG5ldyBSZXF1ZXN0IG9iamVjdC5cblx0XHRcdFx0XHRcdGNvbnN0IHJlcXVlc3RPcHRpb25zID0ge1xuXHRcdFx0XHRcdFx0XHRoZWFkZXJzOiBuZXcgSGVhZGVycyhyZXF1ZXN0LmhlYWRlcnMpLFxuXHRcdFx0XHRcdFx0XHRmb2xsb3c6IHJlcXVlc3QuZm9sbG93LFxuXHRcdFx0XHRcdFx0XHRjb3VudGVyOiByZXF1ZXN0LmNvdW50ZXIgKyAxLFxuXHRcdFx0XHRcdFx0XHRhZ2VudDogcmVxdWVzdC5hZ2VudCxcblx0XHRcdFx0XHRcdFx0Y29tcHJlc3M6IHJlcXVlc3QuY29tcHJlc3MsXG5cdFx0XHRcdFx0XHRcdG1ldGhvZDogcmVxdWVzdC5tZXRob2QsXG5cdFx0XHRcdFx0XHRcdGJvZHk6IHJlcXVlc3QuYm9keSxcblx0XHRcdFx0XHRcdFx0c2lnbmFsOiByZXF1ZXN0LnNpZ25hbCxcblx0XHRcdFx0XHRcdFx0c2l6ZTogcmVxdWVzdC5zaXplXG5cdFx0XHRcdFx0XHR9O1xuXG5cdFx0XHRcdFx0XHQvLyBIVFRQLXJlZGlyZWN0IGZldGNoIHN0ZXAgOVxuXHRcdFx0XHRcdFx0aWYgKHJlc3BvbnNlXy5zdGF0dXNDb2RlICE9PSAzMDMgJiYgcmVxdWVzdC5ib2R5ICYmIG9wdGlvbnNfLmJvZHkgaW5zdGFuY2VvZiBTdHJlYW0uUmVhZGFibGUpIHtcblx0XHRcdFx0XHRcdFx0cmVqZWN0KG5ldyBGZXRjaEVycm9yKCdDYW5ub3QgZm9sbG93IHJlZGlyZWN0IHdpdGggYm9keSBiZWluZyBhIHJlYWRhYmxlIHN0cmVhbScsICd1bnN1cHBvcnRlZC1yZWRpcmVjdCcpKTtcblx0XHRcdFx0XHRcdFx0ZmluYWxpemUoKTtcblx0XHRcdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHQvLyBIVFRQLXJlZGlyZWN0IGZldGNoIHN0ZXAgMTFcblx0XHRcdFx0XHRcdGlmIChyZXNwb25zZV8uc3RhdHVzQ29kZSA9PT0gMzAzIHx8ICgocmVzcG9uc2VfLnN0YXR1c0NvZGUgPT09IDMwMSB8fCByZXNwb25zZV8uc3RhdHVzQ29kZSA9PT0gMzAyKSAmJiByZXF1ZXN0Lm1ldGhvZCA9PT0gJ1BPU1QnKSkge1xuXHRcdFx0XHRcdFx0XHRyZXF1ZXN0T3B0aW9ucy5tZXRob2QgPSAnR0VUJztcblx0XHRcdFx0XHRcdFx0cmVxdWVzdE9wdGlvbnMuYm9keSA9IHVuZGVmaW5lZDtcblx0XHRcdFx0XHRcdFx0cmVxdWVzdE9wdGlvbnMuaGVhZGVycy5kZWxldGUoJ2NvbnRlbnQtbGVuZ3RoJyk7XG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdC8vIEhUVFAtcmVkaXJlY3QgZmV0Y2ggc3RlcCAxNVxuXHRcdFx0XHRcdFx0cmVzb2x2ZShmZXRjaChuZXcgUmVxdWVzdChsb2NhdGlvblVSTCwgcmVxdWVzdE9wdGlvbnMpKSk7XG5cdFx0XHRcdFx0XHRmaW5hbGl6ZSgpO1xuXHRcdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHQvLyBEbyBub3RoaW5nXG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0Ly8gUHJlcGFyZSByZXNwb25zZVxuXHRcdFx0cmVzcG9uc2VfLm9uY2UoJ2VuZCcsICgpID0+IHtcblx0XHRcdFx0aWYgKHNpZ25hbCkge1xuXHRcdFx0XHRcdHNpZ25hbC5yZW1vdmVFdmVudExpc3RlbmVyKCdhYm9ydCcsIGFib3J0QW5kRmluYWxpemUpO1xuXHRcdFx0XHR9XG5cdFx0XHR9KTtcblxuXHRcdFx0bGV0IGJvZHkgPSBTdHJlYW0ucGlwZWxpbmUocmVzcG9uc2VfLCBuZXcgU3RyZWFtLlBhc3NUaHJvdWdoKCksIGVycm9yID0+IHtcblx0XHRcdFx0cmVqZWN0KGVycm9yKTtcblx0XHRcdH0pO1xuXHRcdFx0Ly8gc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9ub2RlanMvbm9kZS9wdWxsLzI5Mzc2XG5cdFx0XHRpZiAocHJvY2Vzcy52ZXJzaW9uIDwgJ3YxMi4xMCcpIHtcblx0XHRcdFx0cmVzcG9uc2VfLm9uKCdhYm9ydGVkJywgYWJvcnRBbmRGaW5hbGl6ZSk7XG5cdFx0XHR9XG5cblx0XHRcdGNvbnN0IHJlc3BvbnNlT3B0aW9ucyA9IHtcblx0XHRcdFx0dXJsOiByZXF1ZXN0LnVybCxcblx0XHRcdFx0c3RhdHVzOiByZXNwb25zZV8uc3RhdHVzQ29kZSxcblx0XHRcdFx0c3RhdHVzVGV4dDogcmVzcG9uc2VfLnN0YXR1c01lc3NhZ2UsXG5cdFx0XHRcdGhlYWRlcnMsXG5cdFx0XHRcdHNpemU6IHJlcXVlc3Quc2l6ZSxcblx0XHRcdFx0Y291bnRlcjogcmVxdWVzdC5jb3VudGVyLFxuXHRcdFx0XHRoaWdoV2F0ZXJNYXJrOiByZXF1ZXN0LmhpZ2hXYXRlck1hcmtcblx0XHRcdH07XG5cblx0XHRcdC8vIEhUVFAtbmV0d29yayBmZXRjaCBzdGVwIDEyLjEuMS4zXG5cdFx0XHRjb25zdCBjb2RpbmdzID0gaGVhZGVycy5nZXQoJ0NvbnRlbnQtRW5jb2RpbmcnKTtcblxuXHRcdFx0Ly8gSFRUUC1uZXR3b3JrIGZldGNoIHN0ZXAgMTIuMS4xLjQ6IGhhbmRsZSBjb250ZW50IGNvZGluZ3NcblxuXHRcdFx0Ly8gaW4gZm9sbG93aW5nIHNjZW5hcmlvcyB3ZSBpZ25vcmUgY29tcHJlc3Npb24gc3VwcG9ydFxuXHRcdFx0Ly8gMS4gY29tcHJlc3Npb24gc3VwcG9ydCBpcyBkaXNhYmxlZFxuXHRcdFx0Ly8gMi4gSEVBRCByZXF1ZXN0XG5cdFx0XHQvLyAzLiBubyBDb250ZW50LUVuY29kaW5nIGhlYWRlclxuXHRcdFx0Ly8gNC4gbm8gY29udGVudCByZXNwb25zZSAoMjA0KVxuXHRcdFx0Ly8gNS4gY29udGVudCBub3QgbW9kaWZpZWQgcmVzcG9uc2UgKDMwNClcblx0XHRcdGlmICghcmVxdWVzdC5jb21wcmVzcyB8fCByZXF1ZXN0Lm1ldGhvZCA9PT0gJ0hFQUQnIHx8IGNvZGluZ3MgPT09IG51bGwgfHwgcmVzcG9uc2VfLnN0YXR1c0NvZGUgPT09IDIwNCB8fCByZXNwb25zZV8uc3RhdHVzQ29kZSA9PT0gMzA0KSB7XG5cdFx0XHRcdHJlc3BvbnNlID0gbmV3IFJlc3BvbnNlKGJvZHksIHJlc3BvbnNlT3B0aW9ucyk7XG5cdFx0XHRcdHJlc29sdmUocmVzcG9uc2UpO1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cblx0XHRcdC8vIEZvciBOb2RlIHY2K1xuXHRcdFx0Ly8gQmUgbGVzcyBzdHJpY3Qgd2hlbiBkZWNvZGluZyBjb21wcmVzc2VkIHJlc3BvbnNlcywgc2luY2Ugc29tZXRpbWVzXG5cdFx0XHQvLyBzZXJ2ZXJzIHNlbmQgc2xpZ2h0bHkgaW52YWxpZCByZXNwb25zZXMgdGhhdCBhcmUgc3RpbGwgYWNjZXB0ZWRcblx0XHRcdC8vIGJ5IGNvbW1vbiBicm93c2Vycy5cblx0XHRcdC8vIEFsd2F5cyB1c2luZyBaX1NZTkNfRkxVU0ggaXMgd2hhdCBjVVJMIGRvZXMuXG5cdFx0XHRjb25zdCB6bGliT3B0aW9ucyA9IHtcblx0XHRcdFx0Zmx1c2g6IHpsaWIuWl9TWU5DX0ZMVVNILFxuXHRcdFx0XHRmaW5pc2hGbHVzaDogemxpYi5aX1NZTkNfRkxVU0hcblx0XHRcdH07XG5cblx0XHRcdC8vIEZvciBnemlwXG5cdFx0XHRpZiAoY29kaW5ncyA9PT0gJ2d6aXAnIHx8IGNvZGluZ3MgPT09ICd4LWd6aXAnKSB7XG5cdFx0XHRcdGJvZHkgPSBTdHJlYW0ucGlwZWxpbmUoYm9keSwgemxpYi5jcmVhdGVHdW56aXAoemxpYk9wdGlvbnMpLCBlcnJvciA9PiB7XG5cdFx0XHRcdFx0cmVqZWN0KGVycm9yKTtcblx0XHRcdFx0fSk7XG5cdFx0XHRcdHJlc3BvbnNlID0gbmV3IFJlc3BvbnNlKGJvZHksIHJlc3BvbnNlT3B0aW9ucyk7XG5cdFx0XHRcdHJlc29sdmUocmVzcG9uc2UpO1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cblx0XHRcdC8vIEZvciBkZWZsYXRlXG5cdFx0XHRpZiAoY29kaW5ncyA9PT0gJ2RlZmxhdGUnIHx8IGNvZGluZ3MgPT09ICd4LWRlZmxhdGUnKSB7XG5cdFx0XHRcdC8vIEhhbmRsZSB0aGUgaW5mYW1vdXMgcmF3IGRlZmxhdGUgcmVzcG9uc2UgZnJvbSBvbGQgc2VydmVyc1xuXHRcdFx0XHQvLyBhIGhhY2sgZm9yIG9sZCBJSVMgYW5kIEFwYWNoZSBzZXJ2ZXJzXG5cdFx0XHRcdGNvbnN0IHJhdyA9IFN0cmVhbS5waXBlbGluZShyZXNwb25zZV8sIG5ldyBTdHJlYW0uUGFzc1Rocm91Z2goKSwgZXJyb3IgPT4ge1xuXHRcdFx0XHRcdHJlamVjdChlcnJvcik7XG5cdFx0XHRcdH0pO1xuXHRcdFx0XHRyYXcub25jZSgnZGF0YScsIGNodW5rID0+IHtcblx0XHRcdFx0XHQvLyBTZWUgaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8zNzUxOTgyOFxuXHRcdFx0XHRcdGlmICgoY2h1bmtbMF0gJiAweDBGKSA9PT0gMHgwOCkge1xuXHRcdFx0XHRcdFx0Ym9keSA9IFN0cmVhbS5waXBlbGluZShib2R5LCB6bGliLmNyZWF0ZUluZmxhdGUoKSwgZXJyb3IgPT4ge1xuXHRcdFx0XHRcdFx0XHRyZWplY3QoZXJyb3IpO1xuXHRcdFx0XHRcdFx0fSk7XG5cdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdGJvZHkgPSBTdHJlYW0ucGlwZWxpbmUoYm9keSwgemxpYi5jcmVhdGVJbmZsYXRlUmF3KCksIGVycm9yID0+IHtcblx0XHRcdFx0XHRcdFx0cmVqZWN0KGVycm9yKTtcblx0XHRcdFx0XHRcdH0pO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdHJlc3BvbnNlID0gbmV3IFJlc3BvbnNlKGJvZHksIHJlc3BvbnNlT3B0aW9ucyk7XG5cdFx0XHRcdFx0cmVzb2x2ZShyZXNwb25zZSk7XG5cdFx0XHRcdH0pO1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cblx0XHRcdC8vIEZvciBiclxuXHRcdFx0aWYgKGNvZGluZ3MgPT09ICdicicpIHtcblx0XHRcdFx0Ym9keSA9IFN0cmVhbS5waXBlbGluZShib2R5LCB6bGliLmNyZWF0ZUJyb3RsaURlY29tcHJlc3MoKSwgZXJyb3IgPT4ge1xuXHRcdFx0XHRcdHJlamVjdChlcnJvcik7XG5cdFx0XHRcdH0pO1xuXHRcdFx0XHRyZXNwb25zZSA9IG5ldyBSZXNwb25zZShib2R5LCByZXNwb25zZU9wdGlvbnMpO1xuXHRcdFx0XHRyZXNvbHZlKHJlc3BvbnNlKTtcblx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBPdGhlcndpc2UsIHVzZSByZXNwb25zZSBhcy1pc1xuXHRcdFx0cmVzcG9uc2UgPSBuZXcgUmVzcG9uc2UoYm9keSwgcmVzcG9uc2VPcHRpb25zKTtcblx0XHRcdHJlc29sdmUocmVzcG9uc2UpO1xuXHRcdH0pO1xuXG5cdFx0d3JpdGVUb1N0cmVhbShyZXF1ZXN0XywgcmVxdWVzdCk7XG5cdH0pO1xufVxuXG5leHBvcnRzLkFib3J0RXJyb3IgPSBBYm9ydEVycm9yO1xuZXhwb3J0cy5GZXRjaEVycm9yID0gRmV0Y2hFcnJvcjtcbmV4cG9ydHMuSGVhZGVycyA9IEhlYWRlcnM7XG5leHBvcnRzLlJlcXVlc3QgPSBSZXF1ZXN0O1xuZXhwb3J0cy5SZXNwb25zZSA9IFJlc3BvbnNlO1xuZXhwb3J0cy5kZWZhdWx0ID0gZmV0Y2g7XG5leHBvcnRzLmlzUmVkaXJlY3QgPSBpc1JlZGlyZWN0O1xuLy8jIHNvdXJjZU1hcHBpbmdVUkw9aW5kZXguY2pzLm1hcFxuIiwiZnVuY3Rpb24gbm9ybWFsaXplIChzdHIpIHtcbiAgcmV0dXJuIHN0clxuICAgICAgICAgIC5yZXBsYWNlKC9bXFwvXSsvZywgJy8nKVxuICAgICAgICAgIC5yZXBsYWNlKC9cXC9cXD8vZywgJz8nKVxuICAgICAgICAgIC5yZXBsYWNlKC9cXC9cXCMvZywgJyMnKVxuICAgICAgICAgIC5yZXBsYWNlKC9cXDpcXC8vZywgJzovLycpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uICgpIHtcbiAgdmFyIGpvaW5lZCA9IFtdLnNsaWNlLmNhbGwoYXJndW1lbnRzLCAwKS5qb2luKCcvJyk7XG4gIHJldHVybiBub3JtYWxpemUoam9pbmVkKTtcbn07IiwiLyoqXG4gKiB3ZWItc3RyZWFtcy1wb2x5ZmlsbCB2My4wLjFcbiAqL1xuLy8vIDxyZWZlcmVuY2UgbGliPVwiZXMyMDE1LnN5bWJvbFwiIC8+XG5jb25zdCBTeW1ib2xQb2x5ZmlsbCA9IHR5cGVvZiBTeW1ib2wgPT09ICdmdW5jdGlvbicgJiYgdHlwZW9mIFN5bWJvbC5pdGVyYXRvciA9PT0gJ3N5bWJvbCcgP1xuICAgIFN5bWJvbCA6XG4gICAgZGVzY3JpcHRpb24gPT4gYFN5bWJvbCgke2Rlc2NyaXB0aW9ufSlgO1xuXG4vLy8gPHJlZmVyZW5jZSBsaWI9XCJkb21cIiAvPlxuZnVuY3Rpb24gbm9vcCgpIHtcbiAgICAvLyBkbyBub3RoaW5nXG59XG5mdW5jdGlvbiBnZXRHbG9iYWxzKCkge1xuICAgIGlmICh0eXBlb2Ygc2VsZiAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgcmV0dXJuIHNlbGY7XG4gICAgfVxuICAgIGVsc2UgaWYgKHR5cGVvZiB3aW5kb3cgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgIHJldHVybiB3aW5kb3c7XG4gICAgfVxuICAgIGVsc2UgaWYgKHR5cGVvZiBnbG9iYWwgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgIHJldHVybiBnbG9iYWw7XG4gICAgfVxuICAgIHJldHVybiB1bmRlZmluZWQ7XG59XG5jb25zdCBnbG9iYWxzID0gZ2V0R2xvYmFscygpO1xuXG5mdW5jdGlvbiB0eXBlSXNPYmplY3QoeCkge1xuICAgIHJldHVybiAodHlwZW9mIHggPT09ICdvYmplY3QnICYmIHggIT09IG51bGwpIHx8IHR5cGVvZiB4ID09PSAnZnVuY3Rpb24nO1xufVxuY29uc3QgcmV0aHJvd0Fzc2VydGlvbkVycm9yUmVqZWN0aW9uID0gIG5vb3A7XG5cbmNvbnN0IG9yaWdpbmFsUHJvbWlzZSA9IFByb21pc2U7XG5jb25zdCBvcmlnaW5hbFByb21pc2VUaGVuID0gUHJvbWlzZS5wcm90b3R5cGUudGhlbjtcbmNvbnN0IG9yaWdpbmFsUHJvbWlzZVJlc29sdmUgPSBQcm9taXNlLnJlc29sdmUuYmluZChvcmlnaW5hbFByb21pc2UpO1xuY29uc3Qgb3JpZ2luYWxQcm9taXNlUmVqZWN0ID0gUHJvbWlzZS5yZWplY3QuYmluZChvcmlnaW5hbFByb21pc2UpO1xuZnVuY3Rpb24gbmV3UHJvbWlzZShleGVjdXRvcikge1xuICAgIHJldHVybiBuZXcgb3JpZ2luYWxQcm9taXNlKGV4ZWN1dG9yKTtcbn1cbmZ1bmN0aW9uIHByb21pc2VSZXNvbHZlZFdpdGgodmFsdWUpIHtcbiAgICByZXR1cm4gb3JpZ2luYWxQcm9taXNlUmVzb2x2ZSh2YWx1ZSk7XG59XG5mdW5jdGlvbiBwcm9taXNlUmVqZWN0ZWRXaXRoKHJlYXNvbikge1xuICAgIHJldHVybiBvcmlnaW5hbFByb21pc2VSZWplY3QocmVhc29uKTtcbn1cbmZ1bmN0aW9uIFBlcmZvcm1Qcm9taXNlVGhlbihwcm9taXNlLCBvbkZ1bGZpbGxlZCwgb25SZWplY3RlZCkge1xuICAgIC8vIFRoZXJlIGRvZXNuJ3QgYXBwZWFyIHRvIGJlIGFueSB3YXkgdG8gY29ycmVjdGx5IGVtdWxhdGUgdGhlIGJlaGF2aW91ciBmcm9tIEphdmFTY3JpcHQsIHNvIHRoaXMgaXMganVzdCBhblxuICAgIC8vIGFwcHJveGltYXRpb24uXG4gICAgcmV0dXJuIG9yaWdpbmFsUHJvbWlzZVRoZW4uY2FsbChwcm9taXNlLCBvbkZ1bGZpbGxlZCwgb25SZWplY3RlZCk7XG59XG5mdW5jdGlvbiB1cG9uUHJvbWlzZShwcm9taXNlLCBvbkZ1bGZpbGxlZCwgb25SZWplY3RlZCkge1xuICAgIFBlcmZvcm1Qcm9taXNlVGhlbihQZXJmb3JtUHJvbWlzZVRoZW4ocHJvbWlzZSwgb25GdWxmaWxsZWQsIG9uUmVqZWN0ZWQpLCB1bmRlZmluZWQsIHJldGhyb3dBc3NlcnRpb25FcnJvclJlamVjdGlvbik7XG59XG5mdW5jdGlvbiB1cG9uRnVsZmlsbG1lbnQocHJvbWlzZSwgb25GdWxmaWxsZWQpIHtcbiAgICB1cG9uUHJvbWlzZShwcm9taXNlLCBvbkZ1bGZpbGxlZCk7XG59XG5mdW5jdGlvbiB1cG9uUmVqZWN0aW9uKHByb21pc2UsIG9uUmVqZWN0ZWQpIHtcbiAgICB1cG9uUHJvbWlzZShwcm9taXNlLCB1bmRlZmluZWQsIG9uUmVqZWN0ZWQpO1xufVxuZnVuY3Rpb24gdHJhbnNmb3JtUHJvbWlzZVdpdGgocHJvbWlzZSwgZnVsZmlsbG1lbnRIYW5kbGVyLCByZWplY3Rpb25IYW5kbGVyKSB7XG4gICAgcmV0dXJuIFBlcmZvcm1Qcm9taXNlVGhlbihwcm9taXNlLCBmdWxmaWxsbWVudEhhbmRsZXIsIHJlamVjdGlvbkhhbmRsZXIpO1xufVxuZnVuY3Rpb24gc2V0UHJvbWlzZUlzSGFuZGxlZFRvVHJ1ZShwcm9taXNlKSB7XG4gICAgUGVyZm9ybVByb21pc2VUaGVuKHByb21pc2UsIHVuZGVmaW5lZCwgcmV0aHJvd0Fzc2VydGlvbkVycm9yUmVqZWN0aW9uKTtcbn1cbmNvbnN0IHF1ZXVlTWljcm90YXNrID0gKCgpID0+IHtcbiAgICBjb25zdCBnbG9iYWxRdWV1ZU1pY3JvdGFzayA9IGdsb2JhbHMgJiYgZ2xvYmFscy5xdWV1ZU1pY3JvdGFzaztcbiAgICBpZiAodHlwZW9mIGdsb2JhbFF1ZXVlTWljcm90YXNrID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIHJldHVybiBnbG9iYWxRdWV1ZU1pY3JvdGFzaztcbiAgICB9XG4gICAgY29uc3QgcmVzb2x2ZWRQcm9taXNlID0gcHJvbWlzZVJlc29sdmVkV2l0aCh1bmRlZmluZWQpO1xuICAgIHJldHVybiAoZm4pID0+IFBlcmZvcm1Qcm9taXNlVGhlbihyZXNvbHZlZFByb21pc2UsIGZuKTtcbn0pKCk7XG5mdW5jdGlvbiByZWZsZWN0Q2FsbChGLCBWLCBhcmdzKSB7XG4gICAgaWYgKHR5cGVvZiBGICE9PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0FyZ3VtZW50IGlzIG5vdCBhIGZ1bmN0aW9uJyk7XG4gICAgfVxuICAgIHJldHVybiBGdW5jdGlvbi5wcm90b3R5cGUuYXBwbHkuY2FsbChGLCBWLCBhcmdzKTtcbn1cbmZ1bmN0aW9uIHByb21pc2VDYWxsKEYsIFYsIGFyZ3MpIHtcbiAgICB0cnkge1xuICAgICAgICByZXR1cm4gcHJvbWlzZVJlc29sdmVkV2l0aChyZWZsZWN0Q2FsbChGLCBWLCBhcmdzKSk7XG4gICAgfVxuICAgIGNhdGNoICh2YWx1ZSkge1xuICAgICAgICByZXR1cm4gcHJvbWlzZVJlamVjdGVkV2l0aCh2YWx1ZSk7XG4gICAgfVxufVxuXG4vLyBPcmlnaW5hbCBmcm9tIENocm9taXVtXG4vLyBodHRwczovL2Nocm9taXVtLmdvb2dsZXNvdXJjZS5jb20vY2hyb21pdW0vc3JjLysvMGFlZTQ0MzRhNGRiYTQyYTQyYWJhZWE5YmZiYzBjZDE5NmE2M2JjMS90aGlyZF9wYXJ0eS9ibGluay9yZW5kZXJlci9jb3JlL3N0cmVhbXMvU2ltcGxlUXVldWUuanNcbmNvbnN0IFFVRVVFX01BWF9BUlJBWV9TSVpFID0gMTYzODQ7XG4vKipcbiAqIFNpbXBsZSBxdWV1ZSBzdHJ1Y3R1cmUuXG4gKlxuICogQXZvaWRzIHNjYWxhYmlsaXR5IGlzc3VlcyB3aXRoIHVzaW5nIGEgcGFja2VkIGFycmF5IGRpcmVjdGx5IGJ5IHVzaW5nXG4gKiBtdWx0aXBsZSBhcnJheXMgaW4gYSBsaW5rZWQgbGlzdCBhbmQga2VlcGluZyB0aGUgYXJyYXkgc2l6ZSBib3VuZGVkLlxuICovXG5jbGFzcyBTaW1wbGVRdWV1ZSB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuX2N1cnNvciA9IDA7XG4gICAgICAgIHRoaXMuX3NpemUgPSAwO1xuICAgICAgICAvLyBfZnJvbnQgYW5kIF9iYWNrIGFyZSBhbHdheXMgZGVmaW5lZC5cbiAgICAgICAgdGhpcy5fZnJvbnQgPSB7XG4gICAgICAgICAgICBfZWxlbWVudHM6IFtdLFxuICAgICAgICAgICAgX25leHQ6IHVuZGVmaW5lZFxuICAgICAgICB9O1xuICAgICAgICB0aGlzLl9iYWNrID0gdGhpcy5fZnJvbnQ7XG4gICAgICAgIC8vIFRoZSBjdXJzb3IgaXMgdXNlZCB0byBhdm9pZCBjYWxsaW5nIEFycmF5LnNoaWZ0KCkuXG4gICAgICAgIC8vIEl0IGNvbnRhaW5zIHRoZSBpbmRleCBvZiB0aGUgZnJvbnQgZWxlbWVudCBvZiB0aGUgYXJyYXkgaW5zaWRlIHRoZVxuICAgICAgICAvLyBmcm9udC1tb3N0IG5vZGUuIEl0IGlzIGFsd2F5cyBpbiB0aGUgcmFuZ2UgWzAsIFFVRVVFX01BWF9BUlJBWV9TSVpFKS5cbiAgICAgICAgdGhpcy5fY3Vyc29yID0gMDtcbiAgICAgICAgLy8gV2hlbiB0aGVyZSBpcyBvbmx5IG9uZSBub2RlLCBzaXplID09PSBlbGVtZW50cy5sZW5ndGggLSBjdXJzb3IuXG4gICAgICAgIHRoaXMuX3NpemUgPSAwO1xuICAgIH1cbiAgICBnZXQgbGVuZ3RoKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fc2l6ZTtcbiAgICB9XG4gICAgLy8gRm9yIGV4Y2VwdGlvbiBzYWZldHksIHRoaXMgbWV0aG9kIGlzIHN0cnVjdHVyZWQgaW4gb3JkZXI6XG4gICAgLy8gMS4gUmVhZCBzdGF0ZVxuICAgIC8vIDIuIENhbGN1bGF0ZSByZXF1aXJlZCBzdGF0ZSBtdXRhdGlvbnNcbiAgICAvLyAzLiBQZXJmb3JtIHN0YXRlIG11dGF0aW9uc1xuICAgIHB1c2goZWxlbWVudCkge1xuICAgICAgICBjb25zdCBvbGRCYWNrID0gdGhpcy5fYmFjaztcbiAgICAgICAgbGV0IG5ld0JhY2sgPSBvbGRCYWNrO1xuICAgICAgICBpZiAob2xkQmFjay5fZWxlbWVudHMubGVuZ3RoID09PSBRVUVVRV9NQVhfQVJSQVlfU0laRSAtIDEpIHtcbiAgICAgICAgICAgIG5ld0JhY2sgPSB7XG4gICAgICAgICAgICAgICAgX2VsZW1lbnRzOiBbXSxcbiAgICAgICAgICAgICAgICBfbmV4dDogdW5kZWZpbmVkXG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICAgIC8vIHB1c2goKSBpcyB0aGUgbXV0YXRpb24gbW9zdCBsaWtlbHkgdG8gdGhyb3cgYW4gZXhjZXB0aW9uLCBzbyBpdFxuICAgICAgICAvLyBnb2VzIGZpcnN0LlxuICAgICAgICBvbGRCYWNrLl9lbGVtZW50cy5wdXNoKGVsZW1lbnQpO1xuICAgICAgICBpZiAobmV3QmFjayAhPT0gb2xkQmFjaykge1xuICAgICAgICAgICAgdGhpcy5fYmFjayA9IG5ld0JhY2s7XG4gICAgICAgICAgICBvbGRCYWNrLl9uZXh0ID0gbmV3QmFjaztcbiAgICAgICAgfVxuICAgICAgICArK3RoaXMuX3NpemU7XG4gICAgfVxuICAgIC8vIExpa2UgcHVzaCgpLCBzaGlmdCgpIGZvbGxvd3MgdGhlIHJlYWQgLT4gY2FsY3VsYXRlIC0+IG11dGF0ZSBwYXR0ZXJuIGZvclxuICAgIC8vIGV4Y2VwdGlvbiBzYWZldHkuXG4gICAgc2hpZnQoKSB7IC8vIG11c3Qgbm90IGJlIGNhbGxlZCBvbiBhbiBlbXB0eSBxdWV1ZVxuICAgICAgICBjb25zdCBvbGRGcm9udCA9IHRoaXMuX2Zyb250O1xuICAgICAgICBsZXQgbmV3RnJvbnQgPSBvbGRGcm9udDtcbiAgICAgICAgY29uc3Qgb2xkQ3Vyc29yID0gdGhpcy5fY3Vyc29yO1xuICAgICAgICBsZXQgbmV3Q3Vyc29yID0gb2xkQ3Vyc29yICsgMTtcbiAgICAgICAgY29uc3QgZWxlbWVudHMgPSBvbGRGcm9udC5fZWxlbWVudHM7XG4gICAgICAgIGNvbnN0IGVsZW1lbnQgPSBlbGVtZW50c1tvbGRDdXJzb3JdO1xuICAgICAgICBpZiAobmV3Q3Vyc29yID09PSBRVUVVRV9NQVhfQVJSQVlfU0laRSkge1xuICAgICAgICAgICAgbmV3RnJvbnQgPSBvbGRGcm9udC5fbmV4dDtcbiAgICAgICAgICAgIG5ld0N1cnNvciA9IDA7XG4gICAgICAgIH1cbiAgICAgICAgLy8gTm8gbXV0YXRpb25zIGJlZm9yZSB0aGlzIHBvaW50LlxuICAgICAgICAtLXRoaXMuX3NpemU7XG4gICAgICAgIHRoaXMuX2N1cnNvciA9IG5ld0N1cnNvcjtcbiAgICAgICAgaWYgKG9sZEZyb250ICE9PSBuZXdGcm9udCkge1xuICAgICAgICAgICAgdGhpcy5fZnJvbnQgPSBuZXdGcm9udDtcbiAgICAgICAgfVxuICAgICAgICAvLyBQZXJtaXQgc2hpZnRlZCBlbGVtZW50IHRvIGJlIGdhcmJhZ2UgY29sbGVjdGVkLlxuICAgICAgICBlbGVtZW50c1tvbGRDdXJzb3JdID0gdW5kZWZpbmVkO1xuICAgICAgICByZXR1cm4gZWxlbWVudDtcbiAgICB9XG4gICAgLy8gVGhlIHRyaWNreSB0aGluZyBhYm91dCBmb3JFYWNoKCkgaXMgdGhhdCBpdCBjYW4gYmUgY2FsbGVkXG4gICAgLy8gcmUtZW50cmFudGx5LiBUaGUgcXVldWUgbWF5IGJlIG11dGF0ZWQgaW5zaWRlIHRoZSBjYWxsYmFjay4gSXQgaXMgZWFzeSB0b1xuICAgIC8vIHNlZSB0aGF0IHB1c2goKSB3aXRoaW4gdGhlIGNhbGxiYWNrIGhhcyBubyBuZWdhdGl2ZSBlZmZlY3RzIHNpbmNlIHRoZSBlbmRcbiAgICAvLyBvZiB0aGUgcXVldWUgaXMgY2hlY2tlZCBmb3Igb24gZXZlcnkgaXRlcmF0aW9uLiBJZiBzaGlmdCgpIGlzIGNhbGxlZFxuICAgIC8vIHJlcGVhdGVkbHkgd2l0aGluIHRoZSBjYWxsYmFjayB0aGVuIHRoZSBuZXh0IGl0ZXJhdGlvbiBtYXkgcmV0dXJuIGFuXG4gICAgLy8gZWxlbWVudCB0aGF0IGhhcyBiZWVuIHJlbW92ZWQuIEluIHRoaXMgY2FzZSB0aGUgY2FsbGJhY2sgd2lsbCBiZSBjYWxsZWRcbiAgICAvLyB3aXRoIHVuZGVmaW5lZCB2YWx1ZXMgdW50aWwgd2UgZWl0aGVyIFwiY2F0Y2ggdXBcIiB3aXRoIGVsZW1lbnRzIHRoYXQgc3RpbGxcbiAgICAvLyBleGlzdCBvciByZWFjaCB0aGUgYmFjayBvZiB0aGUgcXVldWUuXG4gICAgZm9yRWFjaChjYWxsYmFjaykge1xuICAgICAgICBsZXQgaSA9IHRoaXMuX2N1cnNvcjtcbiAgICAgICAgbGV0IG5vZGUgPSB0aGlzLl9mcm9udDtcbiAgICAgICAgbGV0IGVsZW1lbnRzID0gbm9kZS5fZWxlbWVudHM7XG4gICAgICAgIHdoaWxlIChpICE9PSBlbGVtZW50cy5sZW5ndGggfHwgbm9kZS5fbmV4dCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBpZiAoaSA9PT0gZWxlbWVudHMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgbm9kZSA9IG5vZGUuX25leHQ7XG4gICAgICAgICAgICAgICAgZWxlbWVudHMgPSBub2RlLl9lbGVtZW50cztcbiAgICAgICAgICAgICAgICBpID0gMDtcbiAgICAgICAgICAgICAgICBpZiAoZWxlbWVudHMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNhbGxiYWNrKGVsZW1lbnRzW2ldKTtcbiAgICAgICAgICAgICsraTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvLyBSZXR1cm4gdGhlIGVsZW1lbnQgdGhhdCB3b3VsZCBiZSByZXR1cm5lZCBpZiBzaGlmdCgpIHdhcyBjYWxsZWQgbm93LFxuICAgIC8vIHdpdGhvdXQgbW9kaWZ5aW5nIHRoZSBxdWV1ZS5cbiAgICBwZWVrKCkgeyAvLyBtdXN0IG5vdCBiZSBjYWxsZWQgb24gYW4gZW1wdHkgcXVldWVcbiAgICAgICAgY29uc3QgZnJvbnQgPSB0aGlzLl9mcm9udDtcbiAgICAgICAgY29uc3QgY3Vyc29yID0gdGhpcy5fY3Vyc29yO1xuICAgICAgICByZXR1cm4gZnJvbnQuX2VsZW1lbnRzW2N1cnNvcl07XG4gICAgfVxufVxuXG5mdW5jdGlvbiBSZWFkYWJsZVN0cmVhbVJlYWRlckdlbmVyaWNJbml0aWFsaXplKHJlYWRlciwgc3RyZWFtKSB7XG4gICAgcmVhZGVyLl9vd25lclJlYWRhYmxlU3RyZWFtID0gc3RyZWFtO1xuICAgIHN0cmVhbS5fcmVhZGVyID0gcmVhZGVyO1xuICAgIGlmIChzdHJlYW0uX3N0YXRlID09PSAncmVhZGFibGUnKSB7XG4gICAgICAgIGRlZmF1bHRSZWFkZXJDbG9zZWRQcm9taXNlSW5pdGlhbGl6ZShyZWFkZXIpO1xuICAgIH1cbiAgICBlbHNlIGlmIChzdHJlYW0uX3N0YXRlID09PSAnY2xvc2VkJykge1xuICAgICAgICBkZWZhdWx0UmVhZGVyQ2xvc2VkUHJvbWlzZUluaXRpYWxpemVBc1Jlc29sdmVkKHJlYWRlcik7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBkZWZhdWx0UmVhZGVyQ2xvc2VkUHJvbWlzZUluaXRpYWxpemVBc1JlamVjdGVkKHJlYWRlciwgc3RyZWFtLl9zdG9yZWRFcnJvcik7XG4gICAgfVxufVxuLy8gQSBjbGllbnQgb2YgUmVhZGFibGVTdHJlYW1EZWZhdWx0UmVhZGVyIGFuZCBSZWFkYWJsZVN0cmVhbUJZT0JSZWFkZXIgbWF5IHVzZSB0aGVzZSBmdW5jdGlvbnMgZGlyZWN0bHkgdG8gYnlwYXNzIHN0YXRlXG4vLyBjaGVjay5cbmZ1bmN0aW9uIFJlYWRhYmxlU3RyZWFtUmVhZGVyR2VuZXJpY0NhbmNlbChyZWFkZXIsIHJlYXNvbikge1xuICAgIGNvbnN0IHN0cmVhbSA9IHJlYWRlci5fb3duZXJSZWFkYWJsZVN0cmVhbTtcbiAgICByZXR1cm4gUmVhZGFibGVTdHJlYW1DYW5jZWwoc3RyZWFtLCByZWFzb24pO1xufVxuZnVuY3Rpb24gUmVhZGFibGVTdHJlYW1SZWFkZXJHZW5lcmljUmVsZWFzZShyZWFkZXIpIHtcbiAgICBpZiAocmVhZGVyLl9vd25lclJlYWRhYmxlU3RyZWFtLl9zdGF0ZSA9PT0gJ3JlYWRhYmxlJykge1xuICAgICAgICBkZWZhdWx0UmVhZGVyQ2xvc2VkUHJvbWlzZVJlamVjdChyZWFkZXIsIG5ldyBUeXBlRXJyb3IoYFJlYWRlciB3YXMgcmVsZWFzZWQgYW5kIGNhbiBubyBsb25nZXIgYmUgdXNlZCB0byBtb25pdG9yIHRoZSBzdHJlYW0ncyBjbG9zZWRuZXNzYCkpO1xuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgICAgZGVmYXVsdFJlYWRlckNsb3NlZFByb21pc2VSZXNldFRvUmVqZWN0ZWQocmVhZGVyLCBuZXcgVHlwZUVycm9yKGBSZWFkZXIgd2FzIHJlbGVhc2VkIGFuZCBjYW4gbm8gbG9uZ2VyIGJlIHVzZWQgdG8gbW9uaXRvciB0aGUgc3RyZWFtJ3MgY2xvc2VkbmVzc2ApKTtcbiAgICB9XG4gICAgcmVhZGVyLl9vd25lclJlYWRhYmxlU3RyZWFtLl9yZWFkZXIgPSB1bmRlZmluZWQ7XG4gICAgcmVhZGVyLl9vd25lclJlYWRhYmxlU3RyZWFtID0gdW5kZWZpbmVkO1xufVxuLy8gSGVscGVyIGZ1bmN0aW9ucyBmb3IgdGhlIHJlYWRlcnMuXG5mdW5jdGlvbiByZWFkZXJMb2NrRXhjZXB0aW9uKG5hbWUpIHtcbiAgICByZXR1cm4gbmV3IFR5cGVFcnJvcignQ2Fubm90ICcgKyBuYW1lICsgJyBhIHN0cmVhbSB1c2luZyBhIHJlbGVhc2VkIHJlYWRlcicpO1xufVxuLy8gSGVscGVyIGZ1bmN0aW9ucyBmb3IgdGhlIFJlYWRhYmxlU3RyZWFtRGVmYXVsdFJlYWRlci5cbmZ1bmN0aW9uIGRlZmF1bHRSZWFkZXJDbG9zZWRQcm9taXNlSW5pdGlhbGl6ZShyZWFkZXIpIHtcbiAgICByZWFkZXIuX2Nsb3NlZFByb21pc2UgPSBuZXdQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgcmVhZGVyLl9jbG9zZWRQcm9taXNlX3Jlc29sdmUgPSByZXNvbHZlO1xuICAgICAgICByZWFkZXIuX2Nsb3NlZFByb21pc2VfcmVqZWN0ID0gcmVqZWN0O1xuICAgIH0pO1xufVxuZnVuY3Rpb24gZGVmYXVsdFJlYWRlckNsb3NlZFByb21pc2VJbml0aWFsaXplQXNSZWplY3RlZChyZWFkZXIsIHJlYXNvbikge1xuICAgIGRlZmF1bHRSZWFkZXJDbG9zZWRQcm9taXNlSW5pdGlhbGl6ZShyZWFkZXIpO1xuICAgIGRlZmF1bHRSZWFkZXJDbG9zZWRQcm9taXNlUmVqZWN0KHJlYWRlciwgcmVhc29uKTtcbn1cbmZ1bmN0aW9uIGRlZmF1bHRSZWFkZXJDbG9zZWRQcm9taXNlSW5pdGlhbGl6ZUFzUmVzb2x2ZWQocmVhZGVyKSB7XG4gICAgZGVmYXVsdFJlYWRlckNsb3NlZFByb21pc2VJbml0aWFsaXplKHJlYWRlcik7XG4gICAgZGVmYXVsdFJlYWRlckNsb3NlZFByb21pc2VSZXNvbHZlKHJlYWRlcik7XG59XG5mdW5jdGlvbiBkZWZhdWx0UmVhZGVyQ2xvc2VkUHJvbWlzZVJlamVjdChyZWFkZXIsIHJlYXNvbikge1xuICAgIGlmIChyZWFkZXIuX2Nsb3NlZFByb21pc2VfcmVqZWN0ID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBzZXRQcm9taXNlSXNIYW5kbGVkVG9UcnVlKHJlYWRlci5fY2xvc2VkUHJvbWlzZSk7XG4gICAgcmVhZGVyLl9jbG9zZWRQcm9taXNlX3JlamVjdChyZWFzb24pO1xuICAgIHJlYWRlci5fY2xvc2VkUHJvbWlzZV9yZXNvbHZlID0gdW5kZWZpbmVkO1xuICAgIHJlYWRlci5fY2xvc2VkUHJvbWlzZV9yZWplY3QgPSB1bmRlZmluZWQ7XG59XG5mdW5jdGlvbiBkZWZhdWx0UmVhZGVyQ2xvc2VkUHJvbWlzZVJlc2V0VG9SZWplY3RlZChyZWFkZXIsIHJlYXNvbikge1xuICAgIGRlZmF1bHRSZWFkZXJDbG9zZWRQcm9taXNlSW5pdGlhbGl6ZUFzUmVqZWN0ZWQocmVhZGVyLCByZWFzb24pO1xufVxuZnVuY3Rpb24gZGVmYXVsdFJlYWRlckNsb3NlZFByb21pc2VSZXNvbHZlKHJlYWRlcikge1xuICAgIGlmIChyZWFkZXIuX2Nsb3NlZFByb21pc2VfcmVzb2x2ZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgcmVhZGVyLl9jbG9zZWRQcm9taXNlX3Jlc29sdmUodW5kZWZpbmVkKTtcbiAgICByZWFkZXIuX2Nsb3NlZFByb21pc2VfcmVzb2x2ZSA9IHVuZGVmaW5lZDtcbiAgICByZWFkZXIuX2Nsb3NlZFByb21pc2VfcmVqZWN0ID0gdW5kZWZpbmVkO1xufVxuXG5jb25zdCBBYm9ydFN0ZXBzID0gU3ltYm9sUG9seWZpbGwoJ1tbQWJvcnRTdGVwc11dJyk7XG5jb25zdCBFcnJvclN0ZXBzID0gU3ltYm9sUG9seWZpbGwoJ1tbRXJyb3JTdGVwc11dJyk7XG5jb25zdCBDYW5jZWxTdGVwcyA9IFN5bWJvbFBvbHlmaWxsKCdbW0NhbmNlbFN0ZXBzXV0nKTtcbmNvbnN0IFB1bGxTdGVwcyA9IFN5bWJvbFBvbHlmaWxsKCdbW1B1bGxTdGVwc11dJyk7XG5cbi8vLyA8cmVmZXJlbmNlIGxpYj1cImVzMjAxNS5jb3JlXCIgLz5cbi8vIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL051bWJlci9pc0Zpbml0ZSNQb2x5ZmlsbFxuY29uc3QgTnVtYmVySXNGaW5pdGUgPSBOdW1iZXIuaXNGaW5pdGUgfHwgZnVuY3Rpb24gKHgpIHtcbiAgICByZXR1cm4gdHlwZW9mIHggPT09ICdudW1iZXInICYmIGlzRmluaXRlKHgpO1xufTtcblxuLy8vIDxyZWZlcmVuY2UgbGliPVwiZXMyMDE1LmNvcmVcIiAvPlxuLy8gaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvTWF0aC90cnVuYyNQb2x5ZmlsbFxuY29uc3QgTWF0aFRydW5jID0gTWF0aC50cnVuYyB8fCBmdW5jdGlvbiAodikge1xuICAgIHJldHVybiB2IDwgMCA/IE1hdGguY2VpbCh2KSA6IE1hdGguZmxvb3Iodik7XG59O1xuXG4vLyBodHRwczovL2hleWNhbS5naXRodWIuaW8vd2ViaWRsLyNpZGwtZGljdGlvbmFyaWVzXG5mdW5jdGlvbiBpc0RpY3Rpb25hcnkoeCkge1xuICAgIHJldHVybiB0eXBlb2YgeCA9PT0gJ29iamVjdCcgfHwgdHlwZW9mIHggPT09ICdmdW5jdGlvbic7XG59XG5mdW5jdGlvbiBhc3NlcnREaWN0aW9uYXJ5KG9iaiwgY29udGV4dCkge1xuICAgIGlmIChvYmogIT09IHVuZGVmaW5lZCAmJiAhaXNEaWN0aW9uYXJ5KG9iaikpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihgJHtjb250ZXh0fSBpcyBub3QgYW4gb2JqZWN0LmApO1xuICAgIH1cbn1cbi8vIGh0dHBzOi8vaGV5Y2FtLmdpdGh1Yi5pby93ZWJpZGwvI2lkbC1jYWxsYmFjay1mdW5jdGlvbnNcbmZ1bmN0aW9uIGFzc2VydEZ1bmN0aW9uKHgsIGNvbnRleHQpIHtcbiAgICBpZiAodHlwZW9mIHggIT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihgJHtjb250ZXh0fSBpcyBub3QgYSBmdW5jdGlvbi5gKTtcbiAgICB9XG59XG4vLyBodHRwczovL2hleWNhbS5naXRodWIuaW8vd2ViaWRsLyNpZGwtb2JqZWN0XG5mdW5jdGlvbiBpc09iamVjdCh4KSB7XG4gICAgcmV0dXJuICh0eXBlb2YgeCA9PT0gJ29iamVjdCcgJiYgeCAhPT0gbnVsbCkgfHwgdHlwZW9mIHggPT09ICdmdW5jdGlvbic7XG59XG5mdW5jdGlvbiBhc3NlcnRPYmplY3QoeCwgY29udGV4dCkge1xuICAgIGlmICghaXNPYmplY3QoeCkpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihgJHtjb250ZXh0fSBpcyBub3QgYW4gb2JqZWN0LmApO1xuICAgIH1cbn1cbmZ1bmN0aW9uIGFzc2VydFJlcXVpcmVkQXJndW1lbnQoeCwgcG9zaXRpb24sIGNvbnRleHQpIHtcbiAgICBpZiAoeCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoYFBhcmFtZXRlciAke3Bvc2l0aW9ufSBpcyByZXF1aXJlZCBpbiAnJHtjb250ZXh0fScuYCk7XG4gICAgfVxufVxuZnVuY3Rpb24gYXNzZXJ0UmVxdWlyZWRGaWVsZCh4LCBmaWVsZCwgY29udGV4dCkge1xuICAgIGlmICh4ID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihgJHtmaWVsZH0gaXMgcmVxdWlyZWQgaW4gJyR7Y29udGV4dH0nLmApO1xuICAgIH1cbn1cbi8vIGh0dHBzOi8vaGV5Y2FtLmdpdGh1Yi5pby93ZWJpZGwvI2lkbC11bnJlc3RyaWN0ZWQtZG91YmxlXG5mdW5jdGlvbiBjb252ZXJ0VW5yZXN0cmljdGVkRG91YmxlKHZhbHVlKSB7XG4gICAgcmV0dXJuIE51bWJlcih2YWx1ZSk7XG59XG5mdW5jdGlvbiBjZW5zb3JOZWdhdGl2ZVplcm8oeCkge1xuICAgIHJldHVybiB4ID09PSAwID8gMCA6IHg7XG59XG5mdW5jdGlvbiBpbnRlZ2VyUGFydCh4KSB7XG4gICAgcmV0dXJuIGNlbnNvck5lZ2F0aXZlWmVybyhNYXRoVHJ1bmMoeCkpO1xufVxuLy8gaHR0cHM6Ly9oZXljYW0uZ2l0aHViLmlvL3dlYmlkbC8jaWRsLXVuc2lnbmVkLWxvbmctbG9uZ1xuZnVuY3Rpb24gY29udmVydFVuc2lnbmVkTG9uZ0xvbmdXaXRoRW5mb3JjZVJhbmdlKHZhbHVlLCBjb250ZXh0KSB7XG4gICAgY29uc3QgbG93ZXJCb3VuZCA9IDA7XG4gICAgY29uc3QgdXBwZXJCb3VuZCA9IE51bWJlci5NQVhfU0FGRV9JTlRFR0VSO1xuICAgIGxldCB4ID0gTnVtYmVyKHZhbHVlKTtcbiAgICB4ID0gY2Vuc29yTmVnYXRpdmVaZXJvKHgpO1xuICAgIGlmICghTnVtYmVySXNGaW5pdGUoeCkpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihgJHtjb250ZXh0fSBpcyBub3QgYSBmaW5pdGUgbnVtYmVyYCk7XG4gICAgfVxuICAgIHggPSBpbnRlZ2VyUGFydCh4KTtcbiAgICBpZiAoeCA8IGxvd2VyQm91bmQgfHwgeCA+IHVwcGVyQm91bmQpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihgJHtjb250ZXh0fSBpcyBvdXRzaWRlIHRoZSBhY2NlcHRlZCByYW5nZSBvZiAke2xvd2VyQm91bmR9IHRvICR7dXBwZXJCb3VuZH0sIGluY2x1c2l2ZWApO1xuICAgIH1cbiAgICBpZiAoIU51bWJlcklzRmluaXRlKHgpIHx8IHggPT09IDApIHtcbiAgICAgICAgcmV0dXJuIDA7XG4gICAgfVxuICAgIC8vIFRPRE8gVXNlIEJpZ0ludCBpZiBzdXBwb3J0ZWQ/XG4gICAgLy8gbGV0IHhCaWdJbnQgPSBCaWdJbnQoaW50ZWdlclBhcnQoeCkpO1xuICAgIC8vIHhCaWdJbnQgPSBCaWdJbnQuYXNVaW50Tig2NCwgeEJpZ0ludCk7XG4gICAgLy8gcmV0dXJuIE51bWJlcih4QmlnSW50KTtcbiAgICByZXR1cm4geDtcbn1cblxuZnVuY3Rpb24gYXNzZXJ0UmVhZGFibGVTdHJlYW0oeCwgY29udGV4dCkge1xuICAgIGlmICghSXNSZWFkYWJsZVN0cmVhbSh4KSkge1xuICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKGAke2NvbnRleHR9IGlzIG5vdCBhIFJlYWRhYmxlU3RyZWFtLmApO1xuICAgIH1cbn1cblxuLy8gQWJzdHJhY3Qgb3BlcmF0aW9ucyBmb3IgdGhlIFJlYWRhYmxlU3RyZWFtLlxuZnVuY3Rpb24gQWNxdWlyZVJlYWRhYmxlU3RyZWFtRGVmYXVsdFJlYWRlcihzdHJlYW0pIHtcbiAgICByZXR1cm4gbmV3IFJlYWRhYmxlU3RyZWFtRGVmYXVsdFJlYWRlcihzdHJlYW0pO1xufVxuLy8gUmVhZGFibGVTdHJlYW0gQVBJIGV4cG9zZWQgZm9yIGNvbnRyb2xsZXJzLlxuZnVuY3Rpb24gUmVhZGFibGVTdHJlYW1BZGRSZWFkUmVxdWVzdChzdHJlYW0sIHJlYWRSZXF1ZXN0KSB7XG4gICAgc3RyZWFtLl9yZWFkZXIuX3JlYWRSZXF1ZXN0cy5wdXNoKHJlYWRSZXF1ZXN0KTtcbn1cbmZ1bmN0aW9uIFJlYWRhYmxlU3RyZWFtRnVsZmlsbFJlYWRSZXF1ZXN0KHN0cmVhbSwgY2h1bmssIGRvbmUpIHtcbiAgICBjb25zdCByZWFkZXIgPSBzdHJlYW0uX3JlYWRlcjtcbiAgICBjb25zdCByZWFkUmVxdWVzdCA9IHJlYWRlci5fcmVhZFJlcXVlc3RzLnNoaWZ0KCk7XG4gICAgaWYgKGRvbmUpIHtcbiAgICAgICAgcmVhZFJlcXVlc3QuX2Nsb3NlU3RlcHMoKTtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIHJlYWRSZXF1ZXN0Ll9jaHVua1N0ZXBzKGNodW5rKTtcbiAgICB9XG59XG5mdW5jdGlvbiBSZWFkYWJsZVN0cmVhbUdldE51bVJlYWRSZXF1ZXN0cyhzdHJlYW0pIHtcbiAgICByZXR1cm4gc3RyZWFtLl9yZWFkZXIuX3JlYWRSZXF1ZXN0cy5sZW5ndGg7XG59XG5mdW5jdGlvbiBSZWFkYWJsZVN0cmVhbUhhc0RlZmF1bHRSZWFkZXIoc3RyZWFtKSB7XG4gICAgY29uc3QgcmVhZGVyID0gc3RyZWFtLl9yZWFkZXI7XG4gICAgaWYgKHJlYWRlciA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgaWYgKCFJc1JlYWRhYmxlU3RyZWFtRGVmYXVsdFJlYWRlcihyZWFkZXIpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgcmV0dXJuIHRydWU7XG59XG4vKipcbiAqIEEgZGVmYXVsdCByZWFkZXIgdmVuZGVkIGJ5IGEge0BsaW5rIFJlYWRhYmxlU3RyZWFtfS5cbiAqXG4gKiBAcHVibGljXG4gKi9cbmNsYXNzIFJlYWRhYmxlU3RyZWFtRGVmYXVsdFJlYWRlciB7XG4gICAgY29uc3RydWN0b3Ioc3RyZWFtKSB7XG4gICAgICAgIGFzc2VydFJlcXVpcmVkQXJndW1lbnQoc3RyZWFtLCAxLCAnUmVhZGFibGVTdHJlYW1EZWZhdWx0UmVhZGVyJyk7XG4gICAgICAgIGFzc2VydFJlYWRhYmxlU3RyZWFtKHN0cmVhbSwgJ0ZpcnN0IHBhcmFtZXRlcicpO1xuICAgICAgICBpZiAoSXNSZWFkYWJsZVN0cmVhbUxvY2tlZChzdHJlYW0pKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdUaGlzIHN0cmVhbSBoYXMgYWxyZWFkeSBiZWVuIGxvY2tlZCBmb3IgZXhjbHVzaXZlIHJlYWRpbmcgYnkgYW5vdGhlciByZWFkZXInKTtcbiAgICAgICAgfVxuICAgICAgICBSZWFkYWJsZVN0cmVhbVJlYWRlckdlbmVyaWNJbml0aWFsaXplKHRoaXMsIHN0cmVhbSk7XG4gICAgICAgIHRoaXMuX3JlYWRSZXF1ZXN0cyA9IG5ldyBTaW1wbGVRdWV1ZSgpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIGEgcHJvbWlzZSB0aGF0IHdpbGwgYmUgZnVsZmlsbGVkIHdoZW4gdGhlIHN0cmVhbSBiZWNvbWVzIGNsb3NlZCxcbiAgICAgKiBvciByZWplY3RlZCBpZiB0aGUgc3RyZWFtIGV2ZXIgZXJyb3JzIG9yIHRoZSByZWFkZXIncyBsb2NrIGlzIHJlbGVhc2VkIGJlZm9yZSB0aGUgc3RyZWFtIGZpbmlzaGVzIGNsb3NpbmcuXG4gICAgICovXG4gICAgZ2V0IGNsb3NlZCgpIHtcbiAgICAgICAgaWYgKCFJc1JlYWRhYmxlU3RyZWFtRGVmYXVsdFJlYWRlcih0aGlzKSkge1xuICAgICAgICAgICAgcmV0dXJuIHByb21pc2VSZWplY3RlZFdpdGgoZGVmYXVsdFJlYWRlckJyYW5kQ2hlY2tFeGNlcHRpb24oJ2Nsb3NlZCcpKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcy5fY2xvc2VkUHJvbWlzZTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogSWYgdGhlIHJlYWRlciBpcyBhY3RpdmUsIGJlaGF2ZXMgdGhlIHNhbWUgYXMge0BsaW5rIFJlYWRhYmxlU3RyZWFtLmNhbmNlbCB8IHN0cmVhbS5jYW5jZWwocmVhc29uKX0uXG4gICAgICovXG4gICAgY2FuY2VsKHJlYXNvbiA9IHVuZGVmaW5lZCkge1xuICAgICAgICBpZiAoIUlzUmVhZGFibGVTdHJlYW1EZWZhdWx0UmVhZGVyKHRoaXMpKSB7XG4gICAgICAgICAgICByZXR1cm4gcHJvbWlzZVJlamVjdGVkV2l0aChkZWZhdWx0UmVhZGVyQnJhbmRDaGVja0V4Y2VwdGlvbignY2FuY2VsJykpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLl9vd25lclJlYWRhYmxlU3RyZWFtID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHJldHVybiBwcm9taXNlUmVqZWN0ZWRXaXRoKHJlYWRlckxvY2tFeGNlcHRpb24oJ2NhbmNlbCcpKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gUmVhZGFibGVTdHJlYW1SZWFkZXJHZW5lcmljQ2FuY2VsKHRoaXMsIHJlYXNvbik7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJldHVybnMgYSBwcm9taXNlIHRoYXQgYWxsb3dzIGFjY2VzcyB0byB0aGUgbmV4dCBjaHVuayBmcm9tIHRoZSBzdHJlYW0ncyBpbnRlcm5hbCBxdWV1ZSwgaWYgYXZhaWxhYmxlLlxuICAgICAqXG4gICAgICogSWYgcmVhZGluZyBhIGNodW5rIGNhdXNlcyB0aGUgcXVldWUgdG8gYmVjb21lIGVtcHR5LCBtb3JlIGRhdGEgd2lsbCBiZSBwdWxsZWQgZnJvbSB0aGUgdW5kZXJseWluZyBzb3VyY2UuXG4gICAgICovXG4gICAgcmVhZCgpIHtcbiAgICAgICAgaWYgKCFJc1JlYWRhYmxlU3RyZWFtRGVmYXVsdFJlYWRlcih0aGlzKSkge1xuICAgICAgICAgICAgcmV0dXJuIHByb21pc2VSZWplY3RlZFdpdGgoZGVmYXVsdFJlYWRlckJyYW5kQ2hlY2tFeGNlcHRpb24oJ3JlYWQnKSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMuX293bmVyUmVhZGFibGVTdHJlYW0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgcmV0dXJuIHByb21pc2VSZWplY3RlZFdpdGgocmVhZGVyTG9ja0V4Y2VwdGlvbigncmVhZCBmcm9tJykpO1xuICAgICAgICB9XG4gICAgICAgIGxldCByZXNvbHZlUHJvbWlzZTtcbiAgICAgICAgbGV0IHJlamVjdFByb21pc2U7XG4gICAgICAgIGNvbnN0IHByb21pc2UgPSBuZXdQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIHJlc29sdmVQcm9taXNlID0gcmVzb2x2ZTtcbiAgICAgICAgICAgIHJlamVjdFByb21pc2UgPSByZWplY3Q7XG4gICAgICAgIH0pO1xuICAgICAgICBjb25zdCByZWFkUmVxdWVzdCA9IHtcbiAgICAgICAgICAgIF9jaHVua1N0ZXBzOiBjaHVuayA9PiByZXNvbHZlUHJvbWlzZSh7IHZhbHVlOiBjaHVuaywgZG9uZTogZmFsc2UgfSksXG4gICAgICAgICAgICBfY2xvc2VTdGVwczogKCkgPT4gcmVzb2x2ZVByb21pc2UoeyB2YWx1ZTogdW5kZWZpbmVkLCBkb25lOiB0cnVlIH0pLFxuICAgICAgICAgICAgX2Vycm9yU3RlcHM6IGUgPT4gcmVqZWN0UHJvbWlzZShlKVxuICAgICAgICB9O1xuICAgICAgICBSZWFkYWJsZVN0cmVhbURlZmF1bHRSZWFkZXJSZWFkKHRoaXMsIHJlYWRSZXF1ZXN0KTtcbiAgICAgICAgcmV0dXJuIHByb21pc2U7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlbGVhc2VzIHRoZSByZWFkZXIncyBsb2NrIG9uIHRoZSBjb3JyZXNwb25kaW5nIHN0cmVhbS4gQWZ0ZXIgdGhlIGxvY2sgaXMgcmVsZWFzZWQsIHRoZSByZWFkZXIgaXMgbm8gbG9uZ2VyIGFjdGl2ZS5cbiAgICAgKiBJZiB0aGUgYXNzb2NpYXRlZCBzdHJlYW0gaXMgZXJyb3JlZCB3aGVuIHRoZSBsb2NrIGlzIHJlbGVhc2VkLCB0aGUgcmVhZGVyIHdpbGwgYXBwZWFyIGVycm9yZWQgaW4gdGhlIHNhbWUgd2F5XG4gICAgICogZnJvbSBub3cgb247IG90aGVyd2lzZSwgdGhlIHJlYWRlciB3aWxsIGFwcGVhciBjbG9zZWQuXG4gICAgICpcbiAgICAgKiBBIHJlYWRlcidzIGxvY2sgY2Fubm90IGJlIHJlbGVhc2VkIHdoaWxlIGl0IHN0aWxsIGhhcyBhIHBlbmRpbmcgcmVhZCByZXF1ZXN0LCBpLmUuLCBpZiBhIHByb21pc2UgcmV0dXJuZWQgYnlcbiAgICAgKiB0aGUgcmVhZGVyJ3Mge0BsaW5rIFJlYWRhYmxlU3RyZWFtRGVmYXVsdFJlYWRlci5yZWFkIHwgcmVhZCgpfSBtZXRob2QgaGFzIG5vdCB5ZXQgYmVlbiBzZXR0bGVkLiBBdHRlbXB0aW5nIHRvXG4gICAgICogZG8gc28gd2lsbCB0aHJvdyBhIGBUeXBlRXJyb3JgIGFuZCBsZWF2ZSB0aGUgcmVhZGVyIGxvY2tlZCB0byB0aGUgc3RyZWFtLlxuICAgICAqL1xuICAgIHJlbGVhc2VMb2NrKCkge1xuICAgICAgICBpZiAoIUlzUmVhZGFibGVTdHJlYW1EZWZhdWx0UmVhZGVyKHRoaXMpKSB7XG4gICAgICAgICAgICB0aHJvdyBkZWZhdWx0UmVhZGVyQnJhbmRDaGVja0V4Y2VwdGlvbigncmVsZWFzZUxvY2snKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5fb3duZXJSZWFkYWJsZVN0cmVhbSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMuX3JlYWRSZXF1ZXN0cy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdUcmllZCB0byByZWxlYXNlIGEgcmVhZGVyIGxvY2sgd2hlbiB0aGF0IHJlYWRlciBoYXMgcGVuZGluZyByZWFkKCkgY2FsbHMgdW4tc2V0dGxlZCcpO1xuICAgICAgICB9XG4gICAgICAgIFJlYWRhYmxlU3RyZWFtUmVhZGVyR2VuZXJpY1JlbGVhc2UodGhpcyk7XG4gICAgfVxufVxuT2JqZWN0LmRlZmluZVByb3BlcnRpZXMoUmVhZGFibGVTdHJlYW1EZWZhdWx0UmVhZGVyLnByb3RvdHlwZSwge1xuICAgIGNhbmNlbDogeyBlbnVtZXJhYmxlOiB0cnVlIH0sXG4gICAgcmVhZDogeyBlbnVtZXJhYmxlOiB0cnVlIH0sXG4gICAgcmVsZWFzZUxvY2s6IHsgZW51bWVyYWJsZTogdHJ1ZSB9LFxuICAgIGNsb3NlZDogeyBlbnVtZXJhYmxlOiB0cnVlIH1cbn0pO1xuaWYgKHR5cGVvZiBTeW1ib2xQb2x5ZmlsbC50b1N0cmluZ1RhZyA9PT0gJ3N5bWJvbCcpIHtcbiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkoUmVhZGFibGVTdHJlYW1EZWZhdWx0UmVhZGVyLnByb3RvdHlwZSwgU3ltYm9sUG9seWZpbGwudG9TdHJpbmdUYWcsIHtcbiAgICAgICAgdmFsdWU6ICdSZWFkYWJsZVN0cmVhbURlZmF1bHRSZWFkZXInLFxuICAgICAgICBjb25maWd1cmFibGU6IHRydWVcbiAgICB9KTtcbn1cbi8vIEFic3RyYWN0IG9wZXJhdGlvbnMgZm9yIHRoZSByZWFkZXJzLlxuZnVuY3Rpb24gSXNSZWFkYWJsZVN0cmVhbURlZmF1bHRSZWFkZXIoeCkge1xuICAgIGlmICghdHlwZUlzT2JqZWN0KHgpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgaWYgKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoeCwgJ19yZWFkUmVxdWVzdHMnKSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xufVxuZnVuY3Rpb24gUmVhZGFibGVTdHJlYW1EZWZhdWx0UmVhZGVyUmVhZChyZWFkZXIsIHJlYWRSZXF1ZXN0KSB7XG4gICAgY29uc3Qgc3RyZWFtID0gcmVhZGVyLl9vd25lclJlYWRhYmxlU3RyZWFtO1xuICAgIHN0cmVhbS5fZGlzdHVyYmVkID0gdHJ1ZTtcbiAgICBpZiAoc3RyZWFtLl9zdGF0ZSA9PT0gJ2Nsb3NlZCcpIHtcbiAgICAgICAgcmVhZFJlcXVlc3QuX2Nsb3NlU3RlcHMoKTtcbiAgICB9XG4gICAgZWxzZSBpZiAoc3RyZWFtLl9zdGF0ZSA9PT0gJ2Vycm9yZWQnKSB7XG4gICAgICAgIHJlYWRSZXF1ZXN0Ll9lcnJvclN0ZXBzKHN0cmVhbS5fc3RvcmVkRXJyb3IpO1xuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgICAgc3RyZWFtLl9yZWFkYWJsZVN0cmVhbUNvbnRyb2xsZXJbUHVsbFN0ZXBzXShyZWFkUmVxdWVzdCk7XG4gICAgfVxufVxuLy8gSGVscGVyIGZ1bmN0aW9ucyBmb3IgdGhlIFJlYWRhYmxlU3RyZWFtRGVmYXVsdFJlYWRlci5cbmZ1bmN0aW9uIGRlZmF1bHRSZWFkZXJCcmFuZENoZWNrRXhjZXB0aW9uKG5hbWUpIHtcbiAgICByZXR1cm4gbmV3IFR5cGVFcnJvcihgUmVhZGFibGVTdHJlYW1EZWZhdWx0UmVhZGVyLnByb3RvdHlwZS4ke25hbWV9IGNhbiBvbmx5IGJlIHVzZWQgb24gYSBSZWFkYWJsZVN0cmVhbURlZmF1bHRSZWFkZXJgKTtcbn1cblxuLy8vIDxyZWZlcmVuY2UgbGliPVwiZXMyMDE4LmFzeW5jaXRlcmFibGVcIiAvPlxuLyogZXNsaW50LWRpc2FibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWVtcHR5LWZ1bmN0aW9uICovXG5jb25zdCBBc3luY0l0ZXJhdG9yUHJvdG90eXBlID0gT2JqZWN0LmdldFByb3RvdHlwZU9mKE9iamVjdC5nZXRQcm90b3R5cGVPZihhc3luYyBmdW5jdGlvbiogKCkgeyB9KS5wcm90b3R5cGUpO1xuXG4vLy8gPHJlZmVyZW5jZSBsaWI9XCJlczIwMTguYXN5bmNpdGVyYWJsZVwiIC8+XG5jbGFzcyBSZWFkYWJsZVN0cmVhbUFzeW5jSXRlcmF0b3JJbXBsIHtcbiAgICBjb25zdHJ1Y3RvcihyZWFkZXIsIHByZXZlbnRDYW5jZWwpIHtcbiAgICAgICAgdGhpcy5fb25nb2luZ1Byb21pc2UgPSB1bmRlZmluZWQ7XG4gICAgICAgIHRoaXMuX2lzRmluaXNoZWQgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5fcmVhZGVyID0gcmVhZGVyO1xuICAgICAgICB0aGlzLl9wcmV2ZW50Q2FuY2VsID0gcHJldmVudENhbmNlbDtcbiAgICB9XG4gICAgbmV4dCgpIHtcbiAgICAgICAgY29uc3QgbmV4dFN0ZXBzID0gKCkgPT4gdGhpcy5fbmV4dFN0ZXBzKCk7XG4gICAgICAgIHRoaXMuX29uZ29pbmdQcm9taXNlID0gdGhpcy5fb25nb2luZ1Byb21pc2UgP1xuICAgICAgICAgICAgdHJhbnNmb3JtUHJvbWlzZVdpdGgodGhpcy5fb25nb2luZ1Byb21pc2UsIG5leHRTdGVwcywgbmV4dFN0ZXBzKSA6XG4gICAgICAgICAgICBuZXh0U3RlcHMoKTtcbiAgICAgICAgcmV0dXJuIHRoaXMuX29uZ29pbmdQcm9taXNlO1xuICAgIH1cbiAgICByZXR1cm4odmFsdWUpIHtcbiAgICAgICAgY29uc3QgcmV0dXJuU3RlcHMgPSAoKSA9PiB0aGlzLl9yZXR1cm5TdGVwcyh2YWx1ZSk7XG4gICAgICAgIHJldHVybiB0aGlzLl9vbmdvaW5nUHJvbWlzZSA/XG4gICAgICAgICAgICB0cmFuc2Zvcm1Qcm9taXNlV2l0aCh0aGlzLl9vbmdvaW5nUHJvbWlzZSwgcmV0dXJuU3RlcHMsIHJldHVyblN0ZXBzKSA6XG4gICAgICAgICAgICByZXR1cm5TdGVwcygpO1xuICAgIH1cbiAgICBfbmV4dFN0ZXBzKCkge1xuICAgICAgICBpZiAodGhpcy5faXNGaW5pc2hlZCkge1xuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSh7IHZhbHVlOiB1bmRlZmluZWQsIGRvbmU6IHRydWUgfSk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgcmVhZGVyID0gdGhpcy5fcmVhZGVyO1xuICAgICAgICBpZiAocmVhZGVyLl9vd25lclJlYWRhYmxlU3RyZWFtID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHJldHVybiBwcm9taXNlUmVqZWN0ZWRXaXRoKHJlYWRlckxvY2tFeGNlcHRpb24oJ2l0ZXJhdGUnKSk7XG4gICAgICAgIH1cbiAgICAgICAgbGV0IHJlc29sdmVQcm9taXNlO1xuICAgICAgICBsZXQgcmVqZWN0UHJvbWlzZTtcbiAgICAgICAgY29uc3QgcHJvbWlzZSA9IG5ld1Byb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgcmVzb2x2ZVByb21pc2UgPSByZXNvbHZlO1xuICAgICAgICAgICAgcmVqZWN0UHJvbWlzZSA9IHJlamVjdDtcbiAgICAgICAgfSk7XG4gICAgICAgIGNvbnN0IHJlYWRSZXF1ZXN0ID0ge1xuICAgICAgICAgICAgX2NodW5rU3RlcHM6IGNodW5rID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLl9vbmdvaW5nUHJvbWlzZSA9IHVuZGVmaW5lZDtcbiAgICAgICAgICAgICAgICAvLyBUaGlzIG5lZWRzIHRvIGJlIGRlbGF5ZWQgYnkgb25lIG1pY3JvdGFzaywgb3RoZXJ3aXNlIHdlIHN0b3AgcHVsbGluZyB0b28gZWFybHkgd2hpY2ggYnJlYWtzIGEgdGVzdC5cbiAgICAgICAgICAgICAgICAvLyBGSVhNRSBJcyB0aGlzIGEgYnVnIGluIHRoZSBzcGVjaWZpY2F0aW9uLCBvciBpbiB0aGUgdGVzdD9cbiAgICAgICAgICAgICAgICBxdWV1ZU1pY3JvdGFzaygoKSA9PiByZXNvbHZlUHJvbWlzZSh7IHZhbHVlOiBjaHVuaywgZG9uZTogZmFsc2UgfSkpO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIF9jbG9zZVN0ZXBzOiAoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5fb25nb2luZ1Byb21pc2UgPSB1bmRlZmluZWQ7XG4gICAgICAgICAgICAgICAgdGhpcy5faXNGaW5pc2hlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgUmVhZGFibGVTdHJlYW1SZWFkZXJHZW5lcmljUmVsZWFzZShyZWFkZXIpO1xuICAgICAgICAgICAgICAgIHJlc29sdmVQcm9taXNlKHsgdmFsdWU6IHVuZGVmaW5lZCwgZG9uZTogdHJ1ZSB9KTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBfZXJyb3JTdGVwczogcmVhc29uID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLl9vbmdvaW5nUHJvbWlzZSA9IHVuZGVmaW5lZDtcbiAgICAgICAgICAgICAgICB0aGlzLl9pc0ZpbmlzaGVkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICBSZWFkYWJsZVN0cmVhbVJlYWRlckdlbmVyaWNSZWxlYXNlKHJlYWRlcik7XG4gICAgICAgICAgICAgICAgcmVqZWN0UHJvbWlzZShyZWFzb24pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgICBSZWFkYWJsZVN0cmVhbURlZmF1bHRSZWFkZXJSZWFkKHJlYWRlciwgcmVhZFJlcXVlc3QpO1xuICAgICAgICByZXR1cm4gcHJvbWlzZTtcbiAgICB9XG4gICAgX3JldHVyblN0ZXBzKHZhbHVlKSB7XG4gICAgICAgIGlmICh0aGlzLl9pc0ZpbmlzaGVkKSB7XG4gICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHsgdmFsdWUsIGRvbmU6IHRydWUgfSk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5faXNGaW5pc2hlZCA9IHRydWU7XG4gICAgICAgIGNvbnN0IHJlYWRlciA9IHRoaXMuX3JlYWRlcjtcbiAgICAgICAgaWYgKHJlYWRlci5fb3duZXJSZWFkYWJsZVN0cmVhbSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICByZXR1cm4gcHJvbWlzZVJlamVjdGVkV2l0aChyZWFkZXJMb2NrRXhjZXB0aW9uKCdmaW5pc2ggaXRlcmF0aW5nJykpO1xuICAgICAgICB9XG4gICAgICAgIGlmICghdGhpcy5fcHJldmVudENhbmNlbCkge1xuICAgICAgICAgICAgY29uc3QgcmVzdWx0ID0gUmVhZGFibGVTdHJlYW1SZWFkZXJHZW5lcmljQ2FuY2VsKHJlYWRlciwgdmFsdWUpO1xuICAgICAgICAgICAgUmVhZGFibGVTdHJlYW1SZWFkZXJHZW5lcmljUmVsZWFzZShyZWFkZXIpO1xuICAgICAgICAgICAgcmV0dXJuIHRyYW5zZm9ybVByb21pc2VXaXRoKHJlc3VsdCwgKCkgPT4gKHsgdmFsdWUsIGRvbmU6IHRydWUgfSkpO1xuICAgICAgICB9XG4gICAgICAgIFJlYWRhYmxlU3RyZWFtUmVhZGVyR2VuZXJpY1JlbGVhc2UocmVhZGVyKTtcbiAgICAgICAgcmV0dXJuIHByb21pc2VSZXNvbHZlZFdpdGgoeyB2YWx1ZSwgZG9uZTogdHJ1ZSB9KTtcbiAgICB9XG59XG5jb25zdCBSZWFkYWJsZVN0cmVhbUFzeW5jSXRlcmF0b3JQcm90b3R5cGUgPSB7XG4gICAgbmV4dCgpIHtcbiAgICAgICAgaWYgKCFJc1JlYWRhYmxlU3RyZWFtQXN5bmNJdGVyYXRvcih0aGlzKSkge1xuICAgICAgICAgICAgcmV0dXJuIHByb21pc2VSZWplY3RlZFdpdGgoc3RyZWFtQXN5bmNJdGVyYXRvckJyYW5kQ2hlY2tFeGNlcHRpb24oJ25leHQnKSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXMuX2FzeW5jSXRlcmF0b3JJbXBsLm5leHQoKTtcbiAgICB9LFxuICAgIHJldHVybih2YWx1ZSkge1xuICAgICAgICBpZiAoIUlzUmVhZGFibGVTdHJlYW1Bc3luY0l0ZXJhdG9yKHRoaXMpKSB7XG4gICAgICAgICAgICByZXR1cm4gcHJvbWlzZVJlamVjdGVkV2l0aChzdHJlYW1Bc3luY0l0ZXJhdG9yQnJhbmRDaGVja0V4Y2VwdGlvbigncmV0dXJuJykpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLl9hc3luY0l0ZXJhdG9ySW1wbC5yZXR1cm4odmFsdWUpO1xuICAgIH1cbn07XG5pZiAoQXN5bmNJdGVyYXRvclByb3RvdHlwZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgT2JqZWN0LnNldFByb3RvdHlwZU9mKFJlYWRhYmxlU3RyZWFtQXN5bmNJdGVyYXRvclByb3RvdHlwZSwgQXN5bmNJdGVyYXRvclByb3RvdHlwZSk7XG59XG4vLyBBYnN0cmFjdCBvcGVyYXRpb25zIGZvciB0aGUgUmVhZGFibGVTdHJlYW0uXG5mdW5jdGlvbiBBY3F1aXJlUmVhZGFibGVTdHJlYW1Bc3luY0l0ZXJhdG9yKHN0cmVhbSwgcHJldmVudENhbmNlbCkge1xuICAgIGNvbnN0IHJlYWRlciA9IEFjcXVpcmVSZWFkYWJsZVN0cmVhbURlZmF1bHRSZWFkZXIoc3RyZWFtKTtcbiAgICBjb25zdCBpbXBsID0gbmV3IFJlYWRhYmxlU3RyZWFtQXN5bmNJdGVyYXRvckltcGwocmVhZGVyLCBwcmV2ZW50Q2FuY2VsKTtcbiAgICBjb25zdCBpdGVyYXRvciA9IE9iamVjdC5jcmVhdGUoUmVhZGFibGVTdHJlYW1Bc3luY0l0ZXJhdG9yUHJvdG90eXBlKTtcbiAgICBpdGVyYXRvci5fYXN5bmNJdGVyYXRvckltcGwgPSBpbXBsO1xuICAgIHJldHVybiBpdGVyYXRvcjtcbn1cbmZ1bmN0aW9uIElzUmVhZGFibGVTdHJlYW1Bc3luY0l0ZXJhdG9yKHgpIHtcbiAgICBpZiAoIXR5cGVJc09iamVjdCh4KSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGlmICghT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHgsICdfYXN5bmNJdGVyYXRvckltcGwnKSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xufVxuLy8gSGVscGVyIGZ1bmN0aW9ucyBmb3IgdGhlIFJlYWRhYmxlU3RyZWFtLlxuZnVuY3Rpb24gc3RyZWFtQXN5bmNJdGVyYXRvckJyYW5kQ2hlY2tFeGNlcHRpb24obmFtZSkge1xuICAgIHJldHVybiBuZXcgVHlwZUVycm9yKGBSZWFkYWJsZVN0cmVhbUFzeW5jSXRlcmF0b3IuJHtuYW1lfSBjYW4gb25seSBiZSB1c2VkIG9uIGEgUmVhZGFibGVTdGVhbUFzeW5jSXRlcmF0b3JgKTtcbn1cblxuLy8vIDxyZWZlcmVuY2UgbGliPVwiZXMyMDE1LmNvcmVcIiAvPlxuLy8gaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvTnVtYmVyL2lzTmFOI1BvbHlmaWxsXG5jb25zdCBOdW1iZXJJc05hTiA9IE51bWJlci5pc05hTiB8fCBmdW5jdGlvbiAoeCkge1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1zZWxmLWNvbXBhcmVcbiAgICByZXR1cm4geCAhPT0geDtcbn07XG5cbmZ1bmN0aW9uIElzRmluaXRlTm9uTmVnYXRpdmVOdW1iZXIodikge1xuICAgIGlmICghSXNOb25OZWdhdGl2ZU51bWJlcih2KSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGlmICh2ID09PSBJbmZpbml0eSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xufVxuZnVuY3Rpb24gSXNOb25OZWdhdGl2ZU51bWJlcih2KSB7XG4gICAgaWYgKHR5cGVvZiB2ICE9PSAnbnVtYmVyJykge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGlmIChOdW1iZXJJc05hTih2KSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGlmICh2IDwgMCkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xufVxuXG5mdW5jdGlvbiBEZXF1ZXVlVmFsdWUoY29udGFpbmVyKSB7XG4gICAgY29uc3QgcGFpciA9IGNvbnRhaW5lci5fcXVldWUuc2hpZnQoKTtcbiAgICBjb250YWluZXIuX3F1ZXVlVG90YWxTaXplIC09IHBhaXIuc2l6ZTtcbiAgICBpZiAoY29udGFpbmVyLl9xdWV1ZVRvdGFsU2l6ZSA8IDApIHtcbiAgICAgICAgY29udGFpbmVyLl9xdWV1ZVRvdGFsU2l6ZSA9IDA7XG4gICAgfVxuICAgIHJldHVybiBwYWlyLnZhbHVlO1xufVxuZnVuY3Rpb24gRW5xdWV1ZVZhbHVlV2l0aFNpemUoY29udGFpbmVyLCB2YWx1ZSwgc2l6ZSkge1xuICAgIHNpemUgPSBOdW1iZXIoc2l6ZSk7XG4gICAgaWYgKCFJc0Zpbml0ZU5vbk5lZ2F0aXZlTnVtYmVyKHNpemUpKSB7XG4gICAgICAgIHRocm93IG5ldyBSYW5nZUVycm9yKCdTaXplIG11c3QgYmUgYSBmaW5pdGUsIG5vbi1OYU4sIG5vbi1uZWdhdGl2ZSBudW1iZXIuJyk7XG4gICAgfVxuICAgIGNvbnRhaW5lci5fcXVldWUucHVzaCh7IHZhbHVlLCBzaXplIH0pO1xuICAgIGNvbnRhaW5lci5fcXVldWVUb3RhbFNpemUgKz0gc2l6ZTtcbn1cbmZ1bmN0aW9uIFBlZWtRdWV1ZVZhbHVlKGNvbnRhaW5lcikge1xuICAgIGNvbnN0IHBhaXIgPSBjb250YWluZXIuX3F1ZXVlLnBlZWsoKTtcbiAgICByZXR1cm4gcGFpci52YWx1ZTtcbn1cbmZ1bmN0aW9uIFJlc2V0UXVldWUoY29udGFpbmVyKSB7XG4gICAgY29udGFpbmVyLl9xdWV1ZSA9IG5ldyBTaW1wbGVRdWV1ZSgpO1xuICAgIGNvbnRhaW5lci5fcXVldWVUb3RhbFNpemUgPSAwO1xufVxuXG5mdW5jdGlvbiBDcmVhdGVBcnJheUZyb21MaXN0KGVsZW1lbnRzKSB7XG4gICAgLy8gV2UgdXNlIGFycmF5cyB0byByZXByZXNlbnQgbGlzdHMsIHNvIHRoaXMgaXMgYmFzaWNhbGx5IGEgbm8tb3AuXG4gICAgLy8gRG8gYSBzbGljZSB0aG91Z2gganVzdCBpbiBjYXNlIHdlIGhhcHBlbiB0byBkZXBlbmQgb24gdGhlIHVuaXF1ZS1uZXNzLlxuICAgIHJldHVybiBlbGVtZW50cy5zbGljZSgpO1xufVxuZnVuY3Rpb24gQ29weURhdGFCbG9ja0J5dGVzKGRlc3QsIGRlc3RPZmZzZXQsIHNyYywgc3JjT2Zmc2V0LCBuKSB7XG4gICAgbmV3IFVpbnQ4QXJyYXkoZGVzdCkuc2V0KG5ldyBVaW50OEFycmF5KHNyYywgc3JjT2Zmc2V0LCBuKSwgZGVzdE9mZnNldCk7XG59XG4vLyBOb3QgaW1wbGVtZW50ZWQgY29ycmVjdGx5XG5mdW5jdGlvbiBUcmFuc2ZlckFycmF5QnVmZmVyKE8pIHtcbiAgICByZXR1cm4gTztcbn1cbi8vIE5vdCBpbXBsZW1lbnRlZCBjb3JyZWN0bHlcbmZ1bmN0aW9uIElzRGV0YWNoZWRCdWZmZXIoTykge1xuICAgIHJldHVybiBmYWxzZTtcbn1cblxuLyoqXG4gKiBBIHB1bGwtaW50byByZXF1ZXN0IGluIGEge0BsaW5rIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJ9LlxuICpcbiAqIEBwdWJsaWNcbiAqL1xuY2xhc3MgUmVhZGFibGVTdHJlYW1CWU9CUmVxdWVzdCB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0lsbGVnYWwgY29uc3RydWN0b3InKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgdmlldyBmb3Igd3JpdGluZyBpbiB0bywgb3IgYG51bGxgIGlmIHRoZSBCWU9CIHJlcXVlc3QgaGFzIGFscmVhZHkgYmVlbiByZXNwb25kZWQgdG8uXG4gICAgICovXG4gICAgZ2V0IHZpZXcoKSB7XG4gICAgICAgIGlmICghSXNSZWFkYWJsZVN0cmVhbUJZT0JSZXF1ZXN0KHRoaXMpKSB7XG4gICAgICAgICAgICB0aHJvdyBieW9iUmVxdWVzdEJyYW5kQ2hlY2tFeGNlcHRpb24oJ3ZpZXcnKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcy5fdmlldztcbiAgICB9XG4gICAgcmVzcG9uZChieXRlc1dyaXR0ZW4pIHtcbiAgICAgICAgaWYgKCFJc1JlYWRhYmxlU3RyZWFtQllPQlJlcXVlc3QodGhpcykpIHtcbiAgICAgICAgICAgIHRocm93IGJ5b2JSZXF1ZXN0QnJhbmRDaGVja0V4Y2VwdGlvbigncmVzcG9uZCcpO1xuICAgICAgICB9XG4gICAgICAgIGFzc2VydFJlcXVpcmVkQXJndW1lbnQoYnl0ZXNXcml0dGVuLCAxLCAncmVzcG9uZCcpO1xuICAgICAgICBieXRlc1dyaXR0ZW4gPSBjb252ZXJ0VW5zaWduZWRMb25nTG9uZ1dpdGhFbmZvcmNlUmFuZ2UoYnl0ZXNXcml0dGVuLCAnRmlyc3QgcGFyYW1ldGVyJyk7XG4gICAgICAgIGlmICh0aGlzLl9hc3NvY2lhdGVkUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlciA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdUaGlzIEJZT0IgcmVxdWVzdCBoYXMgYmVlbiBpbnZhbGlkYXRlZCcpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChJc0RldGFjaGVkQnVmZmVyKHRoaXMuX3ZpZXcuYnVmZmVyKSkgO1xuICAgICAgICBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyUmVzcG9uZCh0aGlzLl9hc3NvY2lhdGVkUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlciwgYnl0ZXNXcml0dGVuKTtcbiAgICB9XG4gICAgcmVzcG9uZFdpdGhOZXdWaWV3KHZpZXcpIHtcbiAgICAgICAgaWYgKCFJc1JlYWRhYmxlU3RyZWFtQllPQlJlcXVlc3QodGhpcykpIHtcbiAgICAgICAgICAgIHRocm93IGJ5b2JSZXF1ZXN0QnJhbmRDaGVja0V4Y2VwdGlvbigncmVzcG9uZFdpdGhOZXdWaWV3Jyk7XG4gICAgICAgIH1cbiAgICAgICAgYXNzZXJ0UmVxdWlyZWRBcmd1bWVudCh2aWV3LCAxLCAncmVzcG9uZFdpdGhOZXdWaWV3Jyk7XG4gICAgICAgIGlmICghQXJyYXlCdWZmZXIuaXNWaWV3KHZpZXcpKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdZb3UgY2FuIG9ubHkgcmVzcG9uZCB3aXRoIGFycmF5IGJ1ZmZlciB2aWV3cycpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh2aWV3LmJ5dGVMZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ2NodW5rIG11c3QgaGF2ZSBub24temVybyBieXRlTGVuZ3RoJyk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHZpZXcuYnVmZmVyLmJ5dGVMZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoYGNodW5rJ3MgYnVmZmVyIG11c3QgaGF2ZSBub24temVybyBieXRlTGVuZ3RoYCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMuX2Fzc29jaWF0ZWRSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ1RoaXMgQllPQiByZXF1ZXN0IGhhcyBiZWVuIGludmFsaWRhdGVkJyk7XG4gICAgICAgIH1cbiAgICAgICAgUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlclJlc3BvbmRXaXRoTmV3Vmlldyh0aGlzLl9hc3NvY2lhdGVkUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlciwgdmlldyk7XG4gICAgfVxufVxuT2JqZWN0LmRlZmluZVByb3BlcnRpZXMoUmVhZGFibGVTdHJlYW1CWU9CUmVxdWVzdC5wcm90b3R5cGUsIHtcbiAgICByZXNwb25kOiB7IGVudW1lcmFibGU6IHRydWUgfSxcbiAgICByZXNwb25kV2l0aE5ld1ZpZXc6IHsgZW51bWVyYWJsZTogdHJ1ZSB9LFxuICAgIHZpZXc6IHsgZW51bWVyYWJsZTogdHJ1ZSB9XG59KTtcbmlmICh0eXBlb2YgU3ltYm9sUG9seWZpbGwudG9TdHJpbmdUYWcgPT09ICdzeW1ib2wnKSB7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KFJlYWRhYmxlU3RyZWFtQllPQlJlcXVlc3QucHJvdG90eXBlLCBTeW1ib2xQb2x5ZmlsbC50b1N0cmluZ1RhZywge1xuICAgICAgICB2YWx1ZTogJ1JlYWRhYmxlU3RyZWFtQllPQlJlcXVlc3QnLFxuICAgICAgICBjb25maWd1cmFibGU6IHRydWVcbiAgICB9KTtcbn1cbi8qKlxuICogQWxsb3dzIGNvbnRyb2wgb2YgYSB7QGxpbmsgUmVhZGFibGVTdHJlYW0gfCByZWFkYWJsZSBieXRlIHN0cmVhbX0ncyBzdGF0ZSBhbmQgaW50ZXJuYWwgcXVldWUuXG4gKlxuICogQHB1YmxpY1xuICovXG5jbGFzcyBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyIHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignSWxsZWdhbCBjb25zdHJ1Y3RvcicpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBjdXJyZW50IEJZT0IgcHVsbCByZXF1ZXN0LCBvciBgbnVsbGAgaWYgdGhlcmUgaXNuJ3Qgb25lLlxuICAgICAqL1xuICAgIGdldCBieW9iUmVxdWVzdCgpIHtcbiAgICAgICAgaWYgKCFJc1JlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXIodGhpcykpIHtcbiAgICAgICAgICAgIHRocm93IGJ5dGVTdHJlYW1Db250cm9sbGVyQnJhbmRDaGVja0V4Y2VwdGlvbignYnlvYlJlcXVlc3QnKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5fYnlvYlJlcXVlc3QgPT09IG51bGwgJiYgdGhpcy5fcGVuZGluZ1B1bGxJbnRvcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBjb25zdCBmaXJzdERlc2NyaXB0b3IgPSB0aGlzLl9wZW5kaW5nUHVsbEludG9zLnBlZWsoKTtcbiAgICAgICAgICAgIGNvbnN0IHZpZXcgPSBuZXcgVWludDhBcnJheShmaXJzdERlc2NyaXB0b3IuYnVmZmVyLCBmaXJzdERlc2NyaXB0b3IuYnl0ZU9mZnNldCArIGZpcnN0RGVzY3JpcHRvci5ieXRlc0ZpbGxlZCwgZmlyc3REZXNjcmlwdG9yLmJ5dGVMZW5ndGggLSBmaXJzdERlc2NyaXB0b3IuYnl0ZXNGaWxsZWQpO1xuICAgICAgICAgICAgY29uc3QgYnlvYlJlcXVlc3QgPSBPYmplY3QuY3JlYXRlKFJlYWRhYmxlU3RyZWFtQllPQlJlcXVlc3QucHJvdG90eXBlKTtcbiAgICAgICAgICAgIFNldFVwUmVhZGFibGVTdHJlYW1CWU9CUmVxdWVzdChieW9iUmVxdWVzdCwgdGhpcywgdmlldyk7XG4gICAgICAgICAgICB0aGlzLl9ieW9iUmVxdWVzdCA9IGJ5b2JSZXF1ZXN0O1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLl9ieW9iUmVxdWVzdDtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgZGVzaXJlZCBzaXplIHRvIGZpbGwgdGhlIGNvbnRyb2xsZWQgc3RyZWFtJ3MgaW50ZXJuYWwgcXVldWUuIEl0IGNhbiBiZSBuZWdhdGl2ZSwgaWYgdGhlIHF1ZXVlIGlzXG4gICAgICogb3Zlci1mdWxsLiBBbiB1bmRlcmx5aW5nIGJ5dGUgc291cmNlIG91Z2h0IHRvIHVzZSB0aGlzIGluZm9ybWF0aW9uIHRvIGRldGVybWluZSB3aGVuIGFuZCBob3cgdG8gYXBwbHkgYmFja3ByZXNzdXJlLlxuICAgICAqL1xuICAgIGdldCBkZXNpcmVkU2l6ZSgpIHtcbiAgICAgICAgaWYgKCFJc1JlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXIodGhpcykpIHtcbiAgICAgICAgICAgIHRocm93IGJ5dGVTdHJlYW1Db250cm9sbGVyQnJhbmRDaGVja0V4Y2VwdGlvbignZGVzaXJlZFNpemUnKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckdldERlc2lyZWRTaXplKHRoaXMpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBDbG9zZXMgdGhlIGNvbnRyb2xsZWQgcmVhZGFibGUgc3RyZWFtLiBDb25zdW1lcnMgd2lsbCBzdGlsbCBiZSBhYmxlIHRvIHJlYWQgYW55IHByZXZpb3VzbHktZW5xdWV1ZWQgY2h1bmtzIGZyb21cbiAgICAgKiB0aGUgc3RyZWFtLCBidXQgb25jZSB0aG9zZSBhcmUgcmVhZCwgdGhlIHN0cmVhbSB3aWxsIGJlY29tZSBjbG9zZWQuXG4gICAgICovXG4gICAgY2xvc2UoKSB7XG4gICAgICAgIGlmICghSXNSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyKHRoaXMpKSB7XG4gICAgICAgICAgICB0aHJvdyBieXRlU3RyZWFtQ29udHJvbGxlckJyYW5kQ2hlY2tFeGNlcHRpb24oJ2Nsb3NlJyk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMuX2Nsb3NlUmVxdWVzdGVkKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdUaGUgc3RyZWFtIGhhcyBhbHJlYWR5IGJlZW4gY2xvc2VkOyBkbyBub3QgY2xvc2UgaXQgYWdhaW4hJyk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3Qgc3RhdGUgPSB0aGlzLl9jb250cm9sbGVkUmVhZGFibGVCeXRlU3RyZWFtLl9zdGF0ZTtcbiAgICAgICAgaWYgKHN0YXRlICE9PSAncmVhZGFibGUnKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKGBUaGUgc3RyZWFtIChpbiAke3N0YXRlfSBzdGF0ZSkgaXMgbm90IGluIHRoZSByZWFkYWJsZSBzdGF0ZSBhbmQgY2Fubm90IGJlIGNsb3NlZGApO1xuICAgICAgICB9XG4gICAgICAgIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJDbG9zZSh0aGlzKTtcbiAgICB9XG4gICAgZW5xdWV1ZShjaHVuaykge1xuICAgICAgICBpZiAoIUlzUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlcih0aGlzKSkge1xuICAgICAgICAgICAgdGhyb3cgYnl0ZVN0cmVhbUNvbnRyb2xsZXJCcmFuZENoZWNrRXhjZXB0aW9uKCdlbnF1ZXVlJyk7XG4gICAgICAgIH1cbiAgICAgICAgYXNzZXJ0UmVxdWlyZWRBcmd1bWVudChjaHVuaywgMSwgJ2VucXVldWUnKTtcbiAgICAgICAgaWYgKCFBcnJheUJ1ZmZlci5pc1ZpZXcoY2h1bmspKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdjaHVuayBtdXN0IGJlIGFuIGFycmF5IGJ1ZmZlciB2aWV3Jyk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNodW5rLmJ5dGVMZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ2NodW5rIG11c3QgaGF2ZSBub24temVybyBieXRlTGVuZ3RoJyk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNodW5rLmJ1ZmZlci5ieXRlTGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKGBjaHVuaydzIGJ1ZmZlciBtdXN0IGhhdmUgbm9uLXplcm8gYnl0ZUxlbmd0aGApO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLl9jbG9zZVJlcXVlc3RlZCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignc3RyZWFtIGlzIGNsb3NlZCBvciBkcmFpbmluZycpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHN0YXRlID0gdGhpcy5fY29udHJvbGxlZFJlYWRhYmxlQnl0ZVN0cmVhbS5fc3RhdGU7XG4gICAgICAgIGlmIChzdGF0ZSAhPT0gJ3JlYWRhYmxlJykge1xuICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihgVGhlIHN0cmVhbSAoaW4gJHtzdGF0ZX0gc3RhdGUpIGlzIG5vdCBpbiB0aGUgcmVhZGFibGUgc3RhdGUgYW5kIGNhbm5vdCBiZSBlbnF1ZXVlZCB0b2ApO1xuICAgICAgICB9XG4gICAgICAgIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJFbnF1ZXVlKHRoaXMsIGNodW5rKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogRXJyb3JzIHRoZSBjb250cm9sbGVkIHJlYWRhYmxlIHN0cmVhbSwgbWFraW5nIGFsbCBmdXR1cmUgaW50ZXJhY3Rpb25zIHdpdGggaXQgZmFpbCB3aXRoIHRoZSBnaXZlbiBlcnJvciBgZWAuXG4gICAgICovXG4gICAgZXJyb3IoZSA9IHVuZGVmaW5lZCkge1xuICAgICAgICBpZiAoIUlzUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlcih0aGlzKSkge1xuICAgICAgICAgICAgdGhyb3cgYnl0ZVN0cmVhbUNvbnRyb2xsZXJCcmFuZENoZWNrRXhjZXB0aW9uKCdlcnJvcicpO1xuICAgICAgICB9XG4gICAgICAgIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJFcnJvcih0aGlzLCBlKTtcbiAgICB9XG4gICAgLyoqIEBpbnRlcm5hbCAqL1xuICAgIFtDYW5jZWxTdGVwc10ocmVhc29uKSB7XG4gICAgICAgIGlmICh0aGlzLl9wZW5kaW5nUHVsbEludG9zLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIGNvbnN0IGZpcnN0RGVzY3JpcHRvciA9IHRoaXMuX3BlbmRpbmdQdWxsSW50b3MucGVlaygpO1xuICAgICAgICAgICAgZmlyc3REZXNjcmlwdG9yLmJ5dGVzRmlsbGVkID0gMDtcbiAgICAgICAgfVxuICAgICAgICBSZXNldFF1ZXVlKHRoaXMpO1xuICAgICAgICBjb25zdCByZXN1bHQgPSB0aGlzLl9jYW5jZWxBbGdvcml0aG0ocmVhc29uKTtcbiAgICAgICAgUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckNsZWFyQWxnb3JpdGhtcyh0aGlzKTtcbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG4gICAgLyoqIEBpbnRlcm5hbCAqL1xuICAgIFtQdWxsU3RlcHNdKHJlYWRSZXF1ZXN0KSB7XG4gICAgICAgIGNvbnN0IHN0cmVhbSA9IHRoaXMuX2NvbnRyb2xsZWRSZWFkYWJsZUJ5dGVTdHJlYW07XG4gICAgICAgIGlmICh0aGlzLl9xdWV1ZVRvdGFsU2l6ZSA+IDApIHtcbiAgICAgICAgICAgIGNvbnN0IGVudHJ5ID0gdGhpcy5fcXVldWUuc2hpZnQoKTtcbiAgICAgICAgICAgIHRoaXMuX3F1ZXVlVG90YWxTaXplIC09IGVudHJ5LmJ5dGVMZW5ndGg7XG4gICAgICAgICAgICBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVySGFuZGxlUXVldWVEcmFpbih0aGlzKTtcbiAgICAgICAgICAgIGNvbnN0IHZpZXcgPSBuZXcgVWludDhBcnJheShlbnRyeS5idWZmZXIsIGVudHJ5LmJ5dGVPZmZzZXQsIGVudHJ5LmJ5dGVMZW5ndGgpO1xuICAgICAgICAgICAgcmVhZFJlcXVlc3QuX2NodW5rU3RlcHModmlldyk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgYXV0b0FsbG9jYXRlQ2h1bmtTaXplID0gdGhpcy5fYXV0b0FsbG9jYXRlQ2h1bmtTaXplO1xuICAgICAgICBpZiAoYXV0b0FsbG9jYXRlQ2h1bmtTaXplICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGxldCBidWZmZXI7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIGJ1ZmZlciA9IG5ldyBBcnJheUJ1ZmZlcihhdXRvQWxsb2NhdGVDaHVua1NpemUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY2F0Y2ggKGJ1ZmZlckUpIHtcbiAgICAgICAgICAgICAgICByZWFkUmVxdWVzdC5fZXJyb3JTdGVwcyhidWZmZXJFKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zdCBwdWxsSW50b0Rlc2NyaXB0b3IgPSB7XG4gICAgICAgICAgICAgICAgYnVmZmVyLFxuICAgICAgICAgICAgICAgIGJ5dGVPZmZzZXQ6IDAsXG4gICAgICAgICAgICAgICAgYnl0ZUxlbmd0aDogYXV0b0FsbG9jYXRlQ2h1bmtTaXplLFxuICAgICAgICAgICAgICAgIGJ5dGVzRmlsbGVkOiAwLFxuICAgICAgICAgICAgICAgIGVsZW1lbnRTaXplOiAxLFxuICAgICAgICAgICAgICAgIHZpZXdDb25zdHJ1Y3RvcjogVWludDhBcnJheSxcbiAgICAgICAgICAgICAgICByZWFkZXJUeXBlOiAnZGVmYXVsdCdcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICB0aGlzLl9wZW5kaW5nUHVsbEludG9zLnB1c2gocHVsbEludG9EZXNjcmlwdG9yKTtcbiAgICAgICAgfVxuICAgICAgICBSZWFkYWJsZVN0cmVhbUFkZFJlYWRSZXF1ZXN0KHN0cmVhbSwgcmVhZFJlcXVlc3QpO1xuICAgICAgICBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyQ2FsbFB1bGxJZk5lZWRlZCh0aGlzKTtcbiAgICB9XG59XG5PYmplY3QuZGVmaW5lUHJvcGVydGllcyhSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyLnByb3RvdHlwZSwge1xuICAgIGNsb3NlOiB7IGVudW1lcmFibGU6IHRydWUgfSxcbiAgICBlbnF1ZXVlOiB7IGVudW1lcmFibGU6IHRydWUgfSxcbiAgICBlcnJvcjogeyBlbnVtZXJhYmxlOiB0cnVlIH0sXG4gICAgYnlvYlJlcXVlc3Q6IHsgZW51bWVyYWJsZTogdHJ1ZSB9LFxuICAgIGRlc2lyZWRTaXplOiB7IGVudW1lcmFibGU6IHRydWUgfVxufSk7XG5pZiAodHlwZW9mIFN5bWJvbFBvbHlmaWxsLnRvU3RyaW5nVGFnID09PSAnc3ltYm9sJykge1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyLnByb3RvdHlwZSwgU3ltYm9sUG9seWZpbGwudG9TdHJpbmdUYWcsIHtcbiAgICAgICAgdmFsdWU6ICdSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyJyxcbiAgICAgICAgY29uZmlndXJhYmxlOiB0cnVlXG4gICAgfSk7XG59XG4vLyBBYnN0cmFjdCBvcGVyYXRpb25zIGZvciB0aGUgUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlci5cbmZ1bmN0aW9uIElzUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlcih4KSB7XG4gICAgaWYgKCF0eXBlSXNPYmplY3QoeCkpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh4LCAnX2NvbnRyb2xsZWRSZWFkYWJsZUJ5dGVTdHJlYW0nKSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xufVxuZnVuY3Rpb24gSXNSZWFkYWJsZVN0cmVhbUJZT0JSZXF1ZXN0KHgpIHtcbiAgICBpZiAoIXR5cGVJc09iamVjdCh4KSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGlmICghT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHgsICdfYXNzb2NpYXRlZFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXInKSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xufVxuZnVuY3Rpb24gUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckNhbGxQdWxsSWZOZWVkZWQoY29udHJvbGxlcikge1xuICAgIGNvbnN0IHNob3VsZFB1bGwgPSBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyU2hvdWxkQ2FsbFB1bGwoY29udHJvbGxlcik7XG4gICAgaWYgKCFzaG91bGRQdWxsKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKGNvbnRyb2xsZXIuX3B1bGxpbmcpIHtcbiAgICAgICAgY29udHJvbGxlci5fcHVsbEFnYWluID0gdHJ1ZTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb250cm9sbGVyLl9wdWxsaW5nID0gdHJ1ZTtcbiAgICAvLyBUT0RPOiBUZXN0IGNvbnRyb2xsZXIgYXJndW1lbnRcbiAgICBjb25zdCBwdWxsUHJvbWlzZSA9IGNvbnRyb2xsZXIuX3B1bGxBbGdvcml0aG0oKTtcbiAgICB1cG9uUHJvbWlzZShwdWxsUHJvbWlzZSwgKCkgPT4ge1xuICAgICAgICBjb250cm9sbGVyLl9wdWxsaW5nID0gZmFsc2U7XG4gICAgICAgIGlmIChjb250cm9sbGVyLl9wdWxsQWdhaW4pIHtcbiAgICAgICAgICAgIGNvbnRyb2xsZXIuX3B1bGxBZ2FpbiA9IGZhbHNlO1xuICAgICAgICAgICAgUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckNhbGxQdWxsSWZOZWVkZWQoY29udHJvbGxlcik7XG4gICAgICAgIH1cbiAgICB9LCBlID0+IHtcbiAgICAgICAgUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckVycm9yKGNvbnRyb2xsZXIsIGUpO1xuICAgIH0pO1xufVxuZnVuY3Rpb24gUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckNsZWFyUGVuZGluZ1B1bGxJbnRvcyhjb250cm9sbGVyKSB7XG4gICAgUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckludmFsaWRhdGVCWU9CUmVxdWVzdChjb250cm9sbGVyKTtcbiAgICBjb250cm9sbGVyLl9wZW5kaW5nUHVsbEludG9zID0gbmV3IFNpbXBsZVF1ZXVlKCk7XG59XG5mdW5jdGlvbiBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyQ29tbWl0UHVsbEludG9EZXNjcmlwdG9yKHN0cmVhbSwgcHVsbEludG9EZXNjcmlwdG9yKSB7XG4gICAgbGV0IGRvbmUgPSBmYWxzZTtcbiAgICBpZiAoc3RyZWFtLl9zdGF0ZSA9PT0gJ2Nsb3NlZCcpIHtcbiAgICAgICAgZG9uZSA9IHRydWU7XG4gICAgfVxuICAgIGNvbnN0IGZpbGxlZFZpZXcgPSBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyQ29udmVydFB1bGxJbnRvRGVzY3JpcHRvcihwdWxsSW50b0Rlc2NyaXB0b3IpO1xuICAgIGlmIChwdWxsSW50b0Rlc2NyaXB0b3IucmVhZGVyVHlwZSA9PT0gJ2RlZmF1bHQnKSB7XG4gICAgICAgIFJlYWRhYmxlU3RyZWFtRnVsZmlsbFJlYWRSZXF1ZXN0KHN0cmVhbSwgZmlsbGVkVmlldywgZG9uZSk7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBSZWFkYWJsZVN0cmVhbUZ1bGZpbGxSZWFkSW50b1JlcXVlc3Qoc3RyZWFtLCBmaWxsZWRWaWV3LCBkb25lKTtcbiAgICB9XG59XG5mdW5jdGlvbiBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyQ29udmVydFB1bGxJbnRvRGVzY3JpcHRvcihwdWxsSW50b0Rlc2NyaXB0b3IpIHtcbiAgICBjb25zdCBieXRlc0ZpbGxlZCA9IHB1bGxJbnRvRGVzY3JpcHRvci5ieXRlc0ZpbGxlZDtcbiAgICBjb25zdCBlbGVtZW50U2l6ZSA9IHB1bGxJbnRvRGVzY3JpcHRvci5lbGVtZW50U2l6ZTtcbiAgICByZXR1cm4gbmV3IHB1bGxJbnRvRGVzY3JpcHRvci52aWV3Q29uc3RydWN0b3IocHVsbEludG9EZXNjcmlwdG9yLmJ1ZmZlciwgcHVsbEludG9EZXNjcmlwdG9yLmJ5dGVPZmZzZXQsIGJ5dGVzRmlsbGVkIC8gZWxlbWVudFNpemUpO1xufVxuZnVuY3Rpb24gUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckVucXVldWVDaHVua1RvUXVldWUoY29udHJvbGxlciwgYnVmZmVyLCBieXRlT2Zmc2V0LCBieXRlTGVuZ3RoKSB7XG4gICAgY29udHJvbGxlci5fcXVldWUucHVzaCh7IGJ1ZmZlciwgYnl0ZU9mZnNldCwgYnl0ZUxlbmd0aCB9KTtcbiAgICBjb250cm9sbGVyLl9xdWV1ZVRvdGFsU2l6ZSArPSBieXRlTGVuZ3RoO1xufVxuZnVuY3Rpb24gUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckZpbGxQdWxsSW50b0Rlc2NyaXB0b3JGcm9tUXVldWUoY29udHJvbGxlciwgcHVsbEludG9EZXNjcmlwdG9yKSB7XG4gICAgY29uc3QgZWxlbWVudFNpemUgPSBwdWxsSW50b0Rlc2NyaXB0b3IuZWxlbWVudFNpemU7XG4gICAgY29uc3QgY3VycmVudEFsaWduZWRCeXRlcyA9IHB1bGxJbnRvRGVzY3JpcHRvci5ieXRlc0ZpbGxlZCAtIHB1bGxJbnRvRGVzY3JpcHRvci5ieXRlc0ZpbGxlZCAlIGVsZW1lbnRTaXplO1xuICAgIGNvbnN0IG1heEJ5dGVzVG9Db3B5ID0gTWF0aC5taW4oY29udHJvbGxlci5fcXVldWVUb3RhbFNpemUsIHB1bGxJbnRvRGVzY3JpcHRvci5ieXRlTGVuZ3RoIC0gcHVsbEludG9EZXNjcmlwdG9yLmJ5dGVzRmlsbGVkKTtcbiAgICBjb25zdCBtYXhCeXRlc0ZpbGxlZCA9IHB1bGxJbnRvRGVzY3JpcHRvci5ieXRlc0ZpbGxlZCArIG1heEJ5dGVzVG9Db3B5O1xuICAgIGNvbnN0IG1heEFsaWduZWRCeXRlcyA9IG1heEJ5dGVzRmlsbGVkIC0gbWF4Qnl0ZXNGaWxsZWQgJSBlbGVtZW50U2l6ZTtcbiAgICBsZXQgdG90YWxCeXRlc1RvQ29weVJlbWFpbmluZyA9IG1heEJ5dGVzVG9Db3B5O1xuICAgIGxldCByZWFkeSA9IGZhbHNlO1xuICAgIGlmIChtYXhBbGlnbmVkQnl0ZXMgPiBjdXJyZW50QWxpZ25lZEJ5dGVzKSB7XG4gICAgICAgIHRvdGFsQnl0ZXNUb0NvcHlSZW1haW5pbmcgPSBtYXhBbGlnbmVkQnl0ZXMgLSBwdWxsSW50b0Rlc2NyaXB0b3IuYnl0ZXNGaWxsZWQ7XG4gICAgICAgIHJlYWR5ID0gdHJ1ZTtcbiAgICB9XG4gICAgY29uc3QgcXVldWUgPSBjb250cm9sbGVyLl9xdWV1ZTtcbiAgICB3aGlsZSAodG90YWxCeXRlc1RvQ29weVJlbWFpbmluZyA+IDApIHtcbiAgICAgICAgY29uc3QgaGVhZE9mUXVldWUgPSBxdWV1ZS5wZWVrKCk7XG4gICAgICAgIGNvbnN0IGJ5dGVzVG9Db3B5ID0gTWF0aC5taW4odG90YWxCeXRlc1RvQ29weVJlbWFpbmluZywgaGVhZE9mUXVldWUuYnl0ZUxlbmd0aCk7XG4gICAgICAgIGNvbnN0IGRlc3RTdGFydCA9IHB1bGxJbnRvRGVzY3JpcHRvci5ieXRlT2Zmc2V0ICsgcHVsbEludG9EZXNjcmlwdG9yLmJ5dGVzRmlsbGVkO1xuICAgICAgICBDb3B5RGF0YUJsb2NrQnl0ZXMocHVsbEludG9EZXNjcmlwdG9yLmJ1ZmZlciwgZGVzdFN0YXJ0LCBoZWFkT2ZRdWV1ZS5idWZmZXIsIGhlYWRPZlF1ZXVlLmJ5dGVPZmZzZXQsIGJ5dGVzVG9Db3B5KTtcbiAgICAgICAgaWYgKGhlYWRPZlF1ZXVlLmJ5dGVMZW5ndGggPT09IGJ5dGVzVG9Db3B5KSB7XG4gICAgICAgICAgICBxdWV1ZS5zaGlmdCgpO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgaGVhZE9mUXVldWUuYnl0ZU9mZnNldCArPSBieXRlc1RvQ29weTtcbiAgICAgICAgICAgIGhlYWRPZlF1ZXVlLmJ5dGVMZW5ndGggLT0gYnl0ZXNUb0NvcHk7XG4gICAgICAgIH1cbiAgICAgICAgY29udHJvbGxlci5fcXVldWVUb3RhbFNpemUgLT0gYnl0ZXNUb0NvcHk7XG4gICAgICAgIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJGaWxsSGVhZFB1bGxJbnRvRGVzY3JpcHRvcihjb250cm9sbGVyLCBieXRlc1RvQ29weSwgcHVsbEludG9EZXNjcmlwdG9yKTtcbiAgICAgICAgdG90YWxCeXRlc1RvQ29weVJlbWFpbmluZyAtPSBieXRlc1RvQ29weTtcbiAgICB9XG4gICAgcmV0dXJuIHJlYWR5O1xufVxuZnVuY3Rpb24gUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckZpbGxIZWFkUHVsbEludG9EZXNjcmlwdG9yKGNvbnRyb2xsZXIsIHNpemUsIHB1bGxJbnRvRGVzY3JpcHRvcikge1xuICAgIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJJbnZhbGlkYXRlQllPQlJlcXVlc3QoY29udHJvbGxlcik7XG4gICAgcHVsbEludG9EZXNjcmlwdG9yLmJ5dGVzRmlsbGVkICs9IHNpemU7XG59XG5mdW5jdGlvbiBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVySGFuZGxlUXVldWVEcmFpbihjb250cm9sbGVyKSB7XG4gICAgaWYgKGNvbnRyb2xsZXIuX3F1ZXVlVG90YWxTaXplID09PSAwICYmIGNvbnRyb2xsZXIuX2Nsb3NlUmVxdWVzdGVkKSB7XG4gICAgICAgIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJDbGVhckFsZ29yaXRobXMoY29udHJvbGxlcik7XG4gICAgICAgIFJlYWRhYmxlU3RyZWFtQ2xvc2UoY29udHJvbGxlci5fY29udHJvbGxlZFJlYWRhYmxlQnl0ZVN0cmVhbSk7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyQ2FsbFB1bGxJZk5lZWRlZChjb250cm9sbGVyKTtcbiAgICB9XG59XG5mdW5jdGlvbiBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVySW52YWxpZGF0ZUJZT0JSZXF1ZXN0KGNvbnRyb2xsZXIpIHtcbiAgICBpZiAoY29udHJvbGxlci5fYnlvYlJlcXVlc3QgPT09IG51bGwpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb250cm9sbGVyLl9ieW9iUmVxdWVzdC5fYXNzb2NpYXRlZFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXIgPSB1bmRlZmluZWQ7XG4gICAgY29udHJvbGxlci5fYnlvYlJlcXVlc3QuX3ZpZXcgPSBudWxsO1xuICAgIGNvbnRyb2xsZXIuX2J5b2JSZXF1ZXN0ID0gbnVsbDtcbn1cbmZ1bmN0aW9uIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJQcm9jZXNzUHVsbEludG9EZXNjcmlwdG9yc1VzaW5nUXVldWUoY29udHJvbGxlcikge1xuICAgIHdoaWxlIChjb250cm9sbGVyLl9wZW5kaW5nUHVsbEludG9zLmxlbmd0aCA+IDApIHtcbiAgICAgICAgaWYgKGNvbnRyb2xsZXIuX3F1ZXVlVG90YWxTaXplID09PSAwKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgcHVsbEludG9EZXNjcmlwdG9yID0gY29udHJvbGxlci5fcGVuZGluZ1B1bGxJbnRvcy5wZWVrKCk7XG4gICAgICAgIGlmIChSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyRmlsbFB1bGxJbnRvRGVzY3JpcHRvckZyb21RdWV1ZShjb250cm9sbGVyLCBwdWxsSW50b0Rlc2NyaXB0b3IpKSB7XG4gICAgICAgICAgICBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyU2hpZnRQZW5kaW5nUHVsbEludG8oY29udHJvbGxlcik7XG4gICAgICAgICAgICBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyQ29tbWl0UHVsbEludG9EZXNjcmlwdG9yKGNvbnRyb2xsZXIuX2NvbnRyb2xsZWRSZWFkYWJsZUJ5dGVTdHJlYW0sIHB1bGxJbnRvRGVzY3JpcHRvcik7XG4gICAgICAgIH1cbiAgICB9XG59XG5mdW5jdGlvbiBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyUHVsbEludG8oY29udHJvbGxlciwgdmlldywgcmVhZEludG9SZXF1ZXN0KSB7XG4gICAgY29uc3Qgc3RyZWFtID0gY29udHJvbGxlci5fY29udHJvbGxlZFJlYWRhYmxlQnl0ZVN0cmVhbTtcbiAgICBsZXQgZWxlbWVudFNpemUgPSAxO1xuICAgIGlmICh2aWV3LmNvbnN0cnVjdG9yICE9PSBEYXRhVmlldykge1xuICAgICAgICBlbGVtZW50U2l6ZSA9IHZpZXcuY29uc3RydWN0b3IuQllURVNfUEVSX0VMRU1FTlQ7XG4gICAgfVxuICAgIGNvbnN0IGN0b3IgPSB2aWV3LmNvbnN0cnVjdG9yO1xuICAgIGNvbnN0IGJ1ZmZlciA9IFRyYW5zZmVyQXJyYXlCdWZmZXIodmlldy5idWZmZXIpO1xuICAgIGNvbnN0IHB1bGxJbnRvRGVzY3JpcHRvciA9IHtcbiAgICAgICAgYnVmZmVyLFxuICAgICAgICBieXRlT2Zmc2V0OiB2aWV3LmJ5dGVPZmZzZXQsXG4gICAgICAgIGJ5dGVMZW5ndGg6IHZpZXcuYnl0ZUxlbmd0aCxcbiAgICAgICAgYnl0ZXNGaWxsZWQ6IDAsXG4gICAgICAgIGVsZW1lbnRTaXplLFxuICAgICAgICB2aWV3Q29uc3RydWN0b3I6IGN0b3IsXG4gICAgICAgIHJlYWRlclR5cGU6ICdieW9iJ1xuICAgIH07XG4gICAgaWYgKGNvbnRyb2xsZXIuX3BlbmRpbmdQdWxsSW50b3MubGVuZ3RoID4gMCkge1xuICAgICAgICBjb250cm9sbGVyLl9wZW5kaW5nUHVsbEludG9zLnB1c2gocHVsbEludG9EZXNjcmlwdG9yKTtcbiAgICAgICAgLy8gTm8gUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckNhbGxQdWxsSWZOZWVkZWQoKSBjYWxsIHNpbmNlOlxuICAgICAgICAvLyAtIE5vIGNoYW5nZSBoYXBwZW5zIG9uIGRlc2lyZWRTaXplXG4gICAgICAgIC8vIC0gVGhlIHNvdXJjZSBoYXMgYWxyZWFkeSBiZWVuIG5vdGlmaWVkIG9mIHRoYXQgdGhlcmUncyBhdCBsZWFzdCAxIHBlbmRpbmcgcmVhZCh2aWV3KVxuICAgICAgICBSZWFkYWJsZVN0cmVhbUFkZFJlYWRJbnRvUmVxdWVzdChzdHJlYW0sIHJlYWRJbnRvUmVxdWVzdCk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKHN0cmVhbS5fc3RhdGUgPT09ICdjbG9zZWQnKSB7XG4gICAgICAgIGNvbnN0IGVtcHR5VmlldyA9IG5ldyBjdG9yKHB1bGxJbnRvRGVzY3JpcHRvci5idWZmZXIsIHB1bGxJbnRvRGVzY3JpcHRvci5ieXRlT2Zmc2V0LCAwKTtcbiAgICAgICAgcmVhZEludG9SZXF1ZXN0Ll9jbG9zZVN0ZXBzKGVtcHR5Vmlldyk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKGNvbnRyb2xsZXIuX3F1ZXVlVG90YWxTaXplID4gMCkge1xuICAgICAgICBpZiAoUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckZpbGxQdWxsSW50b0Rlc2NyaXB0b3JGcm9tUXVldWUoY29udHJvbGxlciwgcHVsbEludG9EZXNjcmlwdG9yKSkge1xuICAgICAgICAgICAgY29uc3QgZmlsbGVkVmlldyA9IFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJDb252ZXJ0UHVsbEludG9EZXNjcmlwdG9yKHB1bGxJbnRvRGVzY3JpcHRvcik7XG4gICAgICAgICAgICBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVySGFuZGxlUXVldWVEcmFpbihjb250cm9sbGVyKTtcbiAgICAgICAgICAgIHJlYWRJbnRvUmVxdWVzdC5fY2h1bmtTdGVwcyhmaWxsZWRWaWV3KTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoY29udHJvbGxlci5fY2xvc2VSZXF1ZXN0ZWQpIHtcbiAgICAgICAgICAgIGNvbnN0IGUgPSBuZXcgVHlwZUVycm9yKCdJbnN1ZmZpY2llbnQgYnl0ZXMgdG8gZmlsbCBlbGVtZW50cyBpbiB0aGUgZ2l2ZW4gYnVmZmVyJyk7XG4gICAgICAgICAgICBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyRXJyb3IoY29udHJvbGxlciwgZSk7XG4gICAgICAgICAgICByZWFkSW50b1JlcXVlc3QuX2Vycm9yU3RlcHMoZSk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICB9XG4gICAgY29udHJvbGxlci5fcGVuZGluZ1B1bGxJbnRvcy5wdXNoKHB1bGxJbnRvRGVzY3JpcHRvcik7XG4gICAgUmVhZGFibGVTdHJlYW1BZGRSZWFkSW50b1JlcXVlc3Qoc3RyZWFtLCByZWFkSW50b1JlcXVlc3QpO1xuICAgIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJDYWxsUHVsbElmTmVlZGVkKGNvbnRyb2xsZXIpO1xufVxuZnVuY3Rpb24gUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlclJlc3BvbmRJbkNsb3NlZFN0YXRlKGNvbnRyb2xsZXIsIGZpcnN0RGVzY3JpcHRvcikge1xuICAgIGZpcnN0RGVzY3JpcHRvci5idWZmZXIgPSBUcmFuc2ZlckFycmF5QnVmZmVyKGZpcnN0RGVzY3JpcHRvci5idWZmZXIpO1xuICAgIGNvbnN0IHN0cmVhbSA9IGNvbnRyb2xsZXIuX2NvbnRyb2xsZWRSZWFkYWJsZUJ5dGVTdHJlYW07XG4gICAgaWYgKFJlYWRhYmxlU3RyZWFtSGFzQllPQlJlYWRlcihzdHJlYW0pKSB7XG4gICAgICAgIHdoaWxlIChSZWFkYWJsZVN0cmVhbUdldE51bVJlYWRJbnRvUmVxdWVzdHMoc3RyZWFtKSA+IDApIHtcbiAgICAgICAgICAgIGNvbnN0IHB1bGxJbnRvRGVzY3JpcHRvciA9IFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJTaGlmdFBlbmRpbmdQdWxsSW50byhjb250cm9sbGVyKTtcbiAgICAgICAgICAgIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJDb21taXRQdWxsSW50b0Rlc2NyaXB0b3Ioc3RyZWFtLCBwdWxsSW50b0Rlc2NyaXB0b3IpO1xuICAgICAgICB9XG4gICAgfVxufVxuZnVuY3Rpb24gUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlclJlc3BvbmRJblJlYWRhYmxlU3RhdGUoY29udHJvbGxlciwgYnl0ZXNXcml0dGVuLCBwdWxsSW50b0Rlc2NyaXB0b3IpIHtcbiAgICBpZiAocHVsbEludG9EZXNjcmlwdG9yLmJ5dGVzRmlsbGVkICsgYnl0ZXNXcml0dGVuID4gcHVsbEludG9EZXNjcmlwdG9yLmJ5dGVMZW5ndGgpIHtcbiAgICAgICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ2J5dGVzV3JpdHRlbiBvdXQgb2YgcmFuZ2UnKTtcbiAgICB9XG4gICAgUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckZpbGxIZWFkUHVsbEludG9EZXNjcmlwdG9yKGNvbnRyb2xsZXIsIGJ5dGVzV3JpdHRlbiwgcHVsbEludG9EZXNjcmlwdG9yKTtcbiAgICBpZiAocHVsbEludG9EZXNjcmlwdG9yLmJ5dGVzRmlsbGVkIDwgcHVsbEludG9EZXNjcmlwdG9yLmVsZW1lbnRTaXplKSB7XG4gICAgICAgIC8vIFRPRE86IEZpZ3VyZSBvdXQgd2hldGhlciB3ZSBzaG91bGQgZGV0YWNoIHRoZSBidWZmZXIgb3Igbm90IGhlcmUuXG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlclNoaWZ0UGVuZGluZ1B1bGxJbnRvKGNvbnRyb2xsZXIpO1xuICAgIGNvbnN0IHJlbWFpbmRlclNpemUgPSBwdWxsSW50b0Rlc2NyaXB0b3IuYnl0ZXNGaWxsZWQgJSBwdWxsSW50b0Rlc2NyaXB0b3IuZWxlbWVudFNpemU7XG4gICAgaWYgKHJlbWFpbmRlclNpemUgPiAwKSB7XG4gICAgICAgIGNvbnN0IGVuZCA9IHB1bGxJbnRvRGVzY3JpcHRvci5ieXRlT2Zmc2V0ICsgcHVsbEludG9EZXNjcmlwdG9yLmJ5dGVzRmlsbGVkO1xuICAgICAgICBjb25zdCByZW1haW5kZXIgPSBwdWxsSW50b0Rlc2NyaXB0b3IuYnVmZmVyLnNsaWNlKGVuZCAtIHJlbWFpbmRlclNpemUsIGVuZCk7XG4gICAgICAgIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJFbnF1ZXVlQ2h1bmtUb1F1ZXVlKGNvbnRyb2xsZXIsIHJlbWFpbmRlciwgMCwgcmVtYWluZGVyLmJ5dGVMZW5ndGgpO1xuICAgIH1cbiAgICBwdWxsSW50b0Rlc2NyaXB0b3IuYnVmZmVyID0gVHJhbnNmZXJBcnJheUJ1ZmZlcihwdWxsSW50b0Rlc2NyaXB0b3IuYnVmZmVyKTtcbiAgICBwdWxsSW50b0Rlc2NyaXB0b3IuYnl0ZXNGaWxsZWQgLT0gcmVtYWluZGVyU2l6ZTtcbiAgICBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyQ29tbWl0UHVsbEludG9EZXNjcmlwdG9yKGNvbnRyb2xsZXIuX2NvbnRyb2xsZWRSZWFkYWJsZUJ5dGVTdHJlYW0sIHB1bGxJbnRvRGVzY3JpcHRvcik7XG4gICAgUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlclByb2Nlc3NQdWxsSW50b0Rlc2NyaXB0b3JzVXNpbmdRdWV1ZShjb250cm9sbGVyKTtcbn1cbmZ1bmN0aW9uIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJSZXNwb25kSW50ZXJuYWwoY29udHJvbGxlciwgYnl0ZXNXcml0dGVuKSB7XG4gICAgY29uc3QgZmlyc3REZXNjcmlwdG9yID0gY29udHJvbGxlci5fcGVuZGluZ1B1bGxJbnRvcy5wZWVrKCk7XG4gICAgY29uc3Qgc3RhdGUgPSBjb250cm9sbGVyLl9jb250cm9sbGVkUmVhZGFibGVCeXRlU3RyZWFtLl9zdGF0ZTtcbiAgICBpZiAoc3RhdGUgPT09ICdjbG9zZWQnKSB7XG4gICAgICAgIGlmIChieXRlc1dyaXR0ZW4gIT09IDApIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ2J5dGVzV3JpdHRlbiBtdXN0IGJlIDAgd2hlbiBjYWxsaW5nIHJlc3BvbmQoKSBvbiBhIGNsb3NlZCBzdHJlYW0nKTtcbiAgICAgICAgfVxuICAgICAgICBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyUmVzcG9uZEluQ2xvc2VkU3RhdGUoY29udHJvbGxlciwgZmlyc3REZXNjcmlwdG9yKTtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJSZXNwb25kSW5SZWFkYWJsZVN0YXRlKGNvbnRyb2xsZXIsIGJ5dGVzV3JpdHRlbiwgZmlyc3REZXNjcmlwdG9yKTtcbiAgICB9XG4gICAgUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckNhbGxQdWxsSWZOZWVkZWQoY29udHJvbGxlcik7XG59XG5mdW5jdGlvbiBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyU2hpZnRQZW5kaW5nUHVsbEludG8oY29udHJvbGxlcikge1xuICAgIGNvbnN0IGRlc2NyaXB0b3IgPSBjb250cm9sbGVyLl9wZW5kaW5nUHVsbEludG9zLnNoaWZ0KCk7XG4gICAgUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckludmFsaWRhdGVCWU9CUmVxdWVzdChjb250cm9sbGVyKTtcbiAgICByZXR1cm4gZGVzY3JpcHRvcjtcbn1cbmZ1bmN0aW9uIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJTaG91bGRDYWxsUHVsbChjb250cm9sbGVyKSB7XG4gICAgY29uc3Qgc3RyZWFtID0gY29udHJvbGxlci5fY29udHJvbGxlZFJlYWRhYmxlQnl0ZVN0cmVhbTtcbiAgICBpZiAoc3RyZWFtLl9zdGF0ZSAhPT0gJ3JlYWRhYmxlJykge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGlmIChjb250cm9sbGVyLl9jbG9zZVJlcXVlc3RlZCkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGlmICghY29udHJvbGxlci5fc3RhcnRlZCkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGlmIChSZWFkYWJsZVN0cmVhbUhhc0RlZmF1bHRSZWFkZXIoc3RyZWFtKSAmJiBSZWFkYWJsZVN0cmVhbUdldE51bVJlYWRSZXF1ZXN0cyhzdHJlYW0pID4gMCkge1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgaWYgKFJlYWRhYmxlU3RyZWFtSGFzQllPQlJlYWRlcihzdHJlYW0pICYmIFJlYWRhYmxlU3RyZWFtR2V0TnVtUmVhZEludG9SZXF1ZXN0cyhzdHJlYW0pID4gMCkge1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgY29uc3QgZGVzaXJlZFNpemUgPSBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyR2V0RGVzaXJlZFNpemUoY29udHJvbGxlcik7XG4gICAgaWYgKGRlc2lyZWRTaXplID4gMCkge1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xufVxuZnVuY3Rpb24gUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckNsZWFyQWxnb3JpdGhtcyhjb250cm9sbGVyKSB7XG4gICAgY29udHJvbGxlci5fcHVsbEFsZ29yaXRobSA9IHVuZGVmaW5lZDtcbiAgICBjb250cm9sbGVyLl9jYW5jZWxBbGdvcml0aG0gPSB1bmRlZmluZWQ7XG59XG4vLyBBIGNsaWVudCBvZiBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyIG1heSB1c2UgdGhlc2UgZnVuY3Rpb25zIGRpcmVjdGx5IHRvIGJ5cGFzcyBzdGF0ZSBjaGVjay5cbmZ1bmN0aW9uIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJDbG9zZShjb250cm9sbGVyKSB7XG4gICAgY29uc3Qgc3RyZWFtID0gY29udHJvbGxlci5fY29udHJvbGxlZFJlYWRhYmxlQnl0ZVN0cmVhbTtcbiAgICBpZiAoY29udHJvbGxlci5fY2xvc2VSZXF1ZXN0ZWQgfHwgc3RyZWFtLl9zdGF0ZSAhPT0gJ3JlYWRhYmxlJykge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChjb250cm9sbGVyLl9xdWV1ZVRvdGFsU2l6ZSA+IDApIHtcbiAgICAgICAgY29udHJvbGxlci5fY2xvc2VSZXF1ZXN0ZWQgPSB0cnVlO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChjb250cm9sbGVyLl9wZW5kaW5nUHVsbEludG9zLmxlbmd0aCA+IDApIHtcbiAgICAgICAgY29uc3QgZmlyc3RQZW5kaW5nUHVsbEludG8gPSBjb250cm9sbGVyLl9wZW5kaW5nUHVsbEludG9zLnBlZWsoKTtcbiAgICAgICAgaWYgKGZpcnN0UGVuZGluZ1B1bGxJbnRvLmJ5dGVzRmlsbGVkID4gMCkge1xuICAgICAgICAgICAgY29uc3QgZSA9IG5ldyBUeXBlRXJyb3IoJ0luc3VmZmljaWVudCBieXRlcyB0byBmaWxsIGVsZW1lbnRzIGluIHRoZSBnaXZlbiBidWZmZXInKTtcbiAgICAgICAgICAgIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJFcnJvcihjb250cm9sbGVyLCBlKTtcbiAgICAgICAgICAgIHRocm93IGU7XG4gICAgICAgIH1cbiAgICB9XG4gICAgUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckNsZWFyQWxnb3JpdGhtcyhjb250cm9sbGVyKTtcbiAgICBSZWFkYWJsZVN0cmVhbUNsb3NlKHN0cmVhbSk7XG59XG5mdW5jdGlvbiBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyRW5xdWV1ZShjb250cm9sbGVyLCBjaHVuaykge1xuICAgIGNvbnN0IHN0cmVhbSA9IGNvbnRyb2xsZXIuX2NvbnRyb2xsZWRSZWFkYWJsZUJ5dGVTdHJlYW07XG4gICAgaWYgKGNvbnRyb2xsZXIuX2Nsb3NlUmVxdWVzdGVkIHx8IHN0cmVhbS5fc3RhdGUgIT09ICdyZWFkYWJsZScpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBidWZmZXIgPSBjaHVuay5idWZmZXI7XG4gICAgY29uc3QgYnl0ZU9mZnNldCA9IGNodW5rLmJ5dGVPZmZzZXQ7XG4gICAgY29uc3QgYnl0ZUxlbmd0aCA9IGNodW5rLmJ5dGVMZW5ndGg7XG4gICAgY29uc3QgdHJhbnNmZXJyZWRCdWZmZXIgPSBUcmFuc2ZlckFycmF5QnVmZmVyKGJ1ZmZlcik7XG4gICAgaWYgKFJlYWRhYmxlU3RyZWFtSGFzRGVmYXVsdFJlYWRlcihzdHJlYW0pKSB7XG4gICAgICAgIGlmIChSZWFkYWJsZVN0cmVhbUdldE51bVJlYWRSZXF1ZXN0cyhzdHJlYW0pID09PSAwKSB7XG4gICAgICAgICAgICBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyRW5xdWV1ZUNodW5rVG9RdWV1ZShjb250cm9sbGVyLCB0cmFuc2ZlcnJlZEJ1ZmZlciwgYnl0ZU9mZnNldCwgYnl0ZUxlbmd0aCk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBjb25zdCB0cmFuc2ZlcnJlZFZpZXcgPSBuZXcgVWludDhBcnJheSh0cmFuc2ZlcnJlZEJ1ZmZlciwgYnl0ZU9mZnNldCwgYnl0ZUxlbmd0aCk7XG4gICAgICAgICAgICBSZWFkYWJsZVN0cmVhbUZ1bGZpbGxSZWFkUmVxdWVzdChzdHJlYW0sIHRyYW5zZmVycmVkVmlldywgZmFsc2UpO1xuICAgICAgICB9XG4gICAgfVxuICAgIGVsc2UgaWYgKFJlYWRhYmxlU3RyZWFtSGFzQllPQlJlYWRlcihzdHJlYW0pKSB7XG4gICAgICAgIC8vIFRPRE86IElkZWFsbHkgaW4gdGhpcyBicmFuY2ggZGV0YWNoaW5nIHNob3VsZCBoYXBwZW4gb25seSBpZiB0aGUgYnVmZmVyIGlzIG5vdCBjb25zdW1lZCBmdWxseS5cbiAgICAgICAgUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckVucXVldWVDaHVua1RvUXVldWUoY29udHJvbGxlciwgdHJhbnNmZXJyZWRCdWZmZXIsIGJ5dGVPZmZzZXQsIGJ5dGVMZW5ndGgpO1xuICAgICAgICBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyUHJvY2Vzc1B1bGxJbnRvRGVzY3JpcHRvcnNVc2luZ1F1ZXVlKGNvbnRyb2xsZXIpO1xuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgICAgUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlckVucXVldWVDaHVua1RvUXVldWUoY29udHJvbGxlciwgdHJhbnNmZXJyZWRCdWZmZXIsIGJ5dGVPZmZzZXQsIGJ5dGVMZW5ndGgpO1xuICAgIH1cbiAgICBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyQ2FsbFB1bGxJZk5lZWRlZChjb250cm9sbGVyKTtcbn1cbmZ1bmN0aW9uIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJFcnJvcihjb250cm9sbGVyLCBlKSB7XG4gICAgY29uc3Qgc3RyZWFtID0gY29udHJvbGxlci5fY29udHJvbGxlZFJlYWRhYmxlQnl0ZVN0cmVhbTtcbiAgICBpZiAoc3RyZWFtLl9zdGF0ZSAhPT0gJ3JlYWRhYmxlJykge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJDbGVhclBlbmRpbmdQdWxsSW50b3MoY29udHJvbGxlcik7XG4gICAgUmVzZXRRdWV1ZShjb250cm9sbGVyKTtcbiAgICBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyQ2xlYXJBbGdvcml0aG1zKGNvbnRyb2xsZXIpO1xuICAgIFJlYWRhYmxlU3RyZWFtRXJyb3Ioc3RyZWFtLCBlKTtcbn1cbmZ1bmN0aW9uIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJHZXREZXNpcmVkU2l6ZShjb250cm9sbGVyKSB7XG4gICAgY29uc3Qgc3RhdGUgPSBjb250cm9sbGVyLl9jb250cm9sbGVkUmVhZGFibGVCeXRlU3RyZWFtLl9zdGF0ZTtcbiAgICBpZiAoc3RhdGUgPT09ICdlcnJvcmVkJykge1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgaWYgKHN0YXRlID09PSAnY2xvc2VkJykge1xuICAgICAgICByZXR1cm4gMDtcbiAgICB9XG4gICAgcmV0dXJuIGNvbnRyb2xsZXIuX3N0cmF0ZWd5SFdNIC0gY29udHJvbGxlci5fcXVldWVUb3RhbFNpemU7XG59XG5mdW5jdGlvbiBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyUmVzcG9uZChjb250cm9sbGVyLCBieXRlc1dyaXR0ZW4pIHtcbiAgICBieXRlc1dyaXR0ZW4gPSBOdW1iZXIoYnl0ZXNXcml0dGVuKTtcbiAgICBpZiAoIUlzRmluaXRlTm9uTmVnYXRpdmVOdW1iZXIoYnl0ZXNXcml0dGVuKSkge1xuICAgICAgICB0aHJvdyBuZXcgUmFuZ2VFcnJvcignYnl0ZXNXcml0dGVuIG11c3QgYmUgYSBmaW5pdGUnKTtcbiAgICB9XG4gICAgUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlclJlc3BvbmRJbnRlcm5hbChjb250cm9sbGVyLCBieXRlc1dyaXR0ZW4pO1xufVxuZnVuY3Rpb24gUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlclJlc3BvbmRXaXRoTmV3Vmlldyhjb250cm9sbGVyLCB2aWV3KSB7XG4gICAgY29uc3QgZmlyc3REZXNjcmlwdG9yID0gY29udHJvbGxlci5fcGVuZGluZ1B1bGxJbnRvcy5wZWVrKCk7XG4gICAgaWYgKGZpcnN0RGVzY3JpcHRvci5ieXRlT2Zmc2V0ICsgZmlyc3REZXNjcmlwdG9yLmJ5dGVzRmlsbGVkICE9PSB2aWV3LmJ5dGVPZmZzZXQpIHtcbiAgICAgICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ1RoZSByZWdpb24gc3BlY2lmaWVkIGJ5IHZpZXcgZG9lcyBub3QgbWF0Y2ggYnlvYlJlcXVlc3QnKTtcbiAgICB9XG4gICAgaWYgKGZpcnN0RGVzY3JpcHRvci5ieXRlTGVuZ3RoICE9PSB2aWV3LmJ5dGVMZW5ndGgpIHtcbiAgICAgICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ1RoZSBidWZmZXIgb2YgdmlldyBoYXMgZGlmZmVyZW50IGNhcGFjaXR5IHRoYW4gYnlvYlJlcXVlc3QnKTtcbiAgICB9XG4gICAgZmlyc3REZXNjcmlwdG9yLmJ1ZmZlciA9IHZpZXcuYnVmZmVyO1xuICAgIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJSZXNwb25kSW50ZXJuYWwoY29udHJvbGxlciwgdmlldy5ieXRlTGVuZ3RoKTtcbn1cbmZ1bmN0aW9uIFNldFVwUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlcihzdHJlYW0sIGNvbnRyb2xsZXIsIHN0YXJ0QWxnb3JpdGhtLCBwdWxsQWxnb3JpdGhtLCBjYW5jZWxBbGdvcml0aG0sIGhpZ2hXYXRlck1hcmssIGF1dG9BbGxvY2F0ZUNodW5rU2l6ZSkge1xuICAgIGNvbnRyb2xsZXIuX2NvbnRyb2xsZWRSZWFkYWJsZUJ5dGVTdHJlYW0gPSBzdHJlYW07XG4gICAgY29udHJvbGxlci5fcHVsbEFnYWluID0gZmFsc2U7XG4gICAgY29udHJvbGxlci5fcHVsbGluZyA9IGZhbHNlO1xuICAgIGNvbnRyb2xsZXIuX2J5b2JSZXF1ZXN0ID0gbnVsbDtcbiAgICAvLyBOZWVkIHRvIHNldCB0aGUgc2xvdHMgc28gdGhhdCB0aGUgYXNzZXJ0IGRvZXNuJ3QgZmlyZS4gSW4gdGhlIHNwZWMgdGhlIHNsb3RzIGFscmVhZHkgZXhpc3QgaW1wbGljaXRseS5cbiAgICBjb250cm9sbGVyLl9xdWV1ZSA9IGNvbnRyb2xsZXIuX3F1ZXVlVG90YWxTaXplID0gdW5kZWZpbmVkO1xuICAgIFJlc2V0UXVldWUoY29udHJvbGxlcik7XG4gICAgY29udHJvbGxlci5fY2xvc2VSZXF1ZXN0ZWQgPSBmYWxzZTtcbiAgICBjb250cm9sbGVyLl9zdGFydGVkID0gZmFsc2U7XG4gICAgY29udHJvbGxlci5fc3RyYXRlZ3lIV00gPSBoaWdoV2F0ZXJNYXJrO1xuICAgIGNvbnRyb2xsZXIuX3B1bGxBbGdvcml0aG0gPSBwdWxsQWxnb3JpdGhtO1xuICAgIGNvbnRyb2xsZXIuX2NhbmNlbEFsZ29yaXRobSA9IGNhbmNlbEFsZ29yaXRobTtcbiAgICBjb250cm9sbGVyLl9hdXRvQWxsb2NhdGVDaHVua1NpemUgPSBhdXRvQWxsb2NhdGVDaHVua1NpemU7XG4gICAgY29udHJvbGxlci5fcGVuZGluZ1B1bGxJbnRvcyA9IG5ldyBTaW1wbGVRdWV1ZSgpO1xuICAgIHN0cmVhbS5fcmVhZGFibGVTdHJlYW1Db250cm9sbGVyID0gY29udHJvbGxlcjtcbiAgICBjb25zdCBzdGFydFJlc3VsdCA9IHN0YXJ0QWxnb3JpdGhtKCk7XG4gICAgdXBvblByb21pc2UocHJvbWlzZVJlc29sdmVkV2l0aChzdGFydFJlc3VsdCksICgpID0+IHtcbiAgICAgICAgY29udHJvbGxlci5fc3RhcnRlZCA9IHRydWU7XG4gICAgICAgIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJDYWxsUHVsbElmTmVlZGVkKGNvbnRyb2xsZXIpO1xuICAgIH0sIHIgPT4ge1xuICAgICAgICBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyRXJyb3IoY29udHJvbGxlciwgcik7XG4gICAgfSk7XG59XG5mdW5jdGlvbiBTZXRVcFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJGcm9tVW5kZXJseWluZ1NvdXJjZShzdHJlYW0sIHVuZGVybHlpbmdCeXRlU291cmNlLCBoaWdoV2F0ZXJNYXJrKSB7XG4gICAgY29uc3QgY29udHJvbGxlciA9IE9iamVjdC5jcmVhdGUoUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlci5wcm90b3R5cGUpO1xuICAgIGxldCBzdGFydEFsZ29yaXRobSA9ICgpID0+IHVuZGVmaW5lZDtcbiAgICBsZXQgcHVsbEFsZ29yaXRobSA9ICgpID0+IHByb21pc2VSZXNvbHZlZFdpdGgodW5kZWZpbmVkKTtcbiAgICBsZXQgY2FuY2VsQWxnb3JpdGhtID0gKCkgPT4gcHJvbWlzZVJlc29sdmVkV2l0aCh1bmRlZmluZWQpO1xuICAgIGlmICh1bmRlcmx5aW5nQnl0ZVNvdXJjZS5zdGFydCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHN0YXJ0QWxnb3JpdGhtID0gKCkgPT4gdW5kZXJseWluZ0J5dGVTb3VyY2Uuc3RhcnQoY29udHJvbGxlcik7XG4gICAgfVxuICAgIGlmICh1bmRlcmx5aW5nQnl0ZVNvdXJjZS5wdWxsICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcHVsbEFsZ29yaXRobSA9ICgpID0+IHVuZGVybHlpbmdCeXRlU291cmNlLnB1bGwoY29udHJvbGxlcik7XG4gICAgfVxuICAgIGlmICh1bmRlcmx5aW5nQnl0ZVNvdXJjZS5jYW5jZWwgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBjYW5jZWxBbGdvcml0aG0gPSByZWFzb24gPT4gdW5kZXJseWluZ0J5dGVTb3VyY2UuY2FuY2VsKHJlYXNvbik7XG4gICAgfVxuICAgIGNvbnN0IGF1dG9BbGxvY2F0ZUNodW5rU2l6ZSA9IHVuZGVybHlpbmdCeXRlU291cmNlLmF1dG9BbGxvY2F0ZUNodW5rU2l6ZTtcbiAgICBTZXRVcFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXIoc3RyZWFtLCBjb250cm9sbGVyLCBzdGFydEFsZ29yaXRobSwgcHVsbEFsZ29yaXRobSwgY2FuY2VsQWxnb3JpdGhtLCBoaWdoV2F0ZXJNYXJrLCBhdXRvQWxsb2NhdGVDaHVua1NpemUpO1xufVxuZnVuY3Rpb24gU2V0VXBSZWFkYWJsZVN0cmVhbUJZT0JSZXF1ZXN0KHJlcXVlc3QsIGNvbnRyb2xsZXIsIHZpZXcpIHtcbiAgICByZXF1ZXN0Ll9hc3NvY2lhdGVkUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlciA9IGNvbnRyb2xsZXI7XG4gICAgcmVxdWVzdC5fdmlldyA9IHZpZXc7XG59XG4vLyBIZWxwZXIgZnVuY3Rpb25zIGZvciB0aGUgUmVhZGFibGVTdHJlYW1CWU9CUmVxdWVzdC5cbmZ1bmN0aW9uIGJ5b2JSZXF1ZXN0QnJhbmRDaGVja0V4Y2VwdGlvbihuYW1lKSB7XG4gICAgcmV0dXJuIG5ldyBUeXBlRXJyb3IoYFJlYWRhYmxlU3RyZWFtQllPQlJlcXVlc3QucHJvdG90eXBlLiR7bmFtZX0gY2FuIG9ubHkgYmUgdXNlZCBvbiBhIFJlYWRhYmxlU3RyZWFtQllPQlJlcXVlc3RgKTtcbn1cbi8vIEhlbHBlciBmdW5jdGlvbnMgZm9yIHRoZSBSZWFkYWJsZUJ5dGVTdHJlYW1Db250cm9sbGVyLlxuZnVuY3Rpb24gYnl0ZVN0cmVhbUNvbnRyb2xsZXJCcmFuZENoZWNrRXhjZXB0aW9uKG5hbWUpIHtcbiAgICByZXR1cm4gbmV3IFR5cGVFcnJvcihgUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlci5wcm90b3R5cGUuJHtuYW1lfSBjYW4gb25seSBiZSB1c2VkIG9uIGEgUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlcmApO1xufVxuXG4vLyBBYnN0cmFjdCBvcGVyYXRpb25zIGZvciB0aGUgUmVhZGFibGVTdHJlYW0uXG5mdW5jdGlvbiBBY3F1aXJlUmVhZGFibGVTdHJlYW1CWU9CUmVhZGVyKHN0cmVhbSkge1xuICAgIHJldHVybiBuZXcgUmVhZGFibGVTdHJlYW1CWU9CUmVhZGVyKHN0cmVhbSk7XG59XG4vLyBSZWFkYWJsZVN0cmVhbSBBUEkgZXhwb3NlZCBmb3IgY29udHJvbGxlcnMuXG5mdW5jdGlvbiBSZWFkYWJsZVN0cmVhbUFkZFJlYWRJbnRvUmVxdWVzdChzdHJlYW0sIHJlYWRJbnRvUmVxdWVzdCkge1xuICAgIHN0cmVhbS5fcmVhZGVyLl9yZWFkSW50b1JlcXVlc3RzLnB1c2gocmVhZEludG9SZXF1ZXN0KTtcbn1cbmZ1bmN0aW9uIFJlYWRhYmxlU3RyZWFtRnVsZmlsbFJlYWRJbnRvUmVxdWVzdChzdHJlYW0sIGNodW5rLCBkb25lKSB7XG4gICAgY29uc3QgcmVhZGVyID0gc3RyZWFtLl9yZWFkZXI7XG4gICAgY29uc3QgcmVhZEludG9SZXF1ZXN0ID0gcmVhZGVyLl9yZWFkSW50b1JlcXVlc3RzLnNoaWZ0KCk7XG4gICAgaWYgKGRvbmUpIHtcbiAgICAgICAgcmVhZEludG9SZXF1ZXN0Ll9jbG9zZVN0ZXBzKGNodW5rKTtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIHJlYWRJbnRvUmVxdWVzdC5fY2h1bmtTdGVwcyhjaHVuayk7XG4gICAgfVxufVxuZnVuY3Rpb24gUmVhZGFibGVTdHJlYW1HZXROdW1SZWFkSW50b1JlcXVlc3RzKHN0cmVhbSkge1xuICAgIHJldHVybiBzdHJlYW0uX3JlYWRlci5fcmVhZEludG9SZXF1ZXN0cy5sZW5ndGg7XG59XG5mdW5jdGlvbiBSZWFkYWJsZVN0cmVhbUhhc0JZT0JSZWFkZXIoc3RyZWFtKSB7XG4gICAgY29uc3QgcmVhZGVyID0gc3RyZWFtLl9yZWFkZXI7XG4gICAgaWYgKHJlYWRlciA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgaWYgKCFJc1JlYWRhYmxlU3RyZWFtQllPQlJlYWRlcihyZWFkZXIpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgcmV0dXJuIHRydWU7XG59XG4vKipcbiAqIEEgQllPQiByZWFkZXIgdmVuZGVkIGJ5IGEge0BsaW5rIFJlYWRhYmxlU3RyZWFtfS5cbiAqXG4gKiBAcHVibGljXG4gKi9cbmNsYXNzIFJlYWRhYmxlU3RyZWFtQllPQlJlYWRlciB7XG4gICAgY29uc3RydWN0b3Ioc3RyZWFtKSB7XG4gICAgICAgIGFzc2VydFJlcXVpcmVkQXJndW1lbnQoc3RyZWFtLCAxLCAnUmVhZGFibGVTdHJlYW1CWU9CUmVhZGVyJyk7XG4gICAgICAgIGFzc2VydFJlYWRhYmxlU3RyZWFtKHN0cmVhbSwgJ0ZpcnN0IHBhcmFtZXRlcicpO1xuICAgICAgICBpZiAoSXNSZWFkYWJsZVN0cmVhbUxvY2tlZChzdHJlYW0pKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdUaGlzIHN0cmVhbSBoYXMgYWxyZWFkeSBiZWVuIGxvY2tlZCBmb3IgZXhjbHVzaXZlIHJlYWRpbmcgYnkgYW5vdGhlciByZWFkZXInKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIUlzUmVhZGFibGVCeXRlU3RyZWFtQ29udHJvbGxlcihzdHJlYW0uX3JlYWRhYmxlU3RyZWFtQ29udHJvbGxlcikpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0Nhbm5vdCBjb25zdHJ1Y3QgYSBSZWFkYWJsZVN0cmVhbUJZT0JSZWFkZXIgZm9yIGEgc3RyZWFtIG5vdCBjb25zdHJ1Y3RlZCB3aXRoIGEgYnl0ZSAnICtcbiAgICAgICAgICAgICAgICAnc291cmNlJyk7XG4gICAgICAgIH1cbiAgICAgICAgUmVhZGFibGVTdHJlYW1SZWFkZXJHZW5lcmljSW5pdGlhbGl6ZSh0aGlzLCBzdHJlYW0pO1xuICAgICAgICB0aGlzLl9yZWFkSW50b1JlcXVlc3RzID0gbmV3IFNpbXBsZVF1ZXVlKCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJldHVybnMgYSBwcm9taXNlIHRoYXQgd2lsbCBiZSBmdWxmaWxsZWQgd2hlbiB0aGUgc3RyZWFtIGJlY29tZXMgY2xvc2VkLCBvciByZWplY3RlZCBpZiB0aGUgc3RyZWFtIGV2ZXIgZXJyb3JzIG9yXG4gICAgICogdGhlIHJlYWRlcidzIGxvY2sgaXMgcmVsZWFzZWQgYmVmb3JlIHRoZSBzdHJlYW0gZmluaXNoZXMgY2xvc2luZy5cbiAgICAgKi9cbiAgICBnZXQgY2xvc2VkKCkge1xuICAgICAgICBpZiAoIUlzUmVhZGFibGVTdHJlYW1CWU9CUmVhZGVyKHRoaXMpKSB7XG4gICAgICAgICAgICByZXR1cm4gcHJvbWlzZVJlamVjdGVkV2l0aChieW9iUmVhZGVyQnJhbmRDaGVja0V4Y2VwdGlvbignY2xvc2VkJykpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLl9jbG9zZWRQcm9taXNlO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBJZiB0aGUgcmVhZGVyIGlzIGFjdGl2ZSwgYmVoYXZlcyB0aGUgc2FtZSBhcyB7QGxpbmsgUmVhZGFibGVTdHJlYW0uY2FuY2VsIHwgc3RyZWFtLmNhbmNlbChyZWFzb24pfS5cbiAgICAgKi9cbiAgICBjYW5jZWwocmVhc29uID0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGlmICghSXNSZWFkYWJsZVN0cmVhbUJZT0JSZWFkZXIodGhpcykpIHtcbiAgICAgICAgICAgIHJldHVybiBwcm9taXNlUmVqZWN0ZWRXaXRoKGJ5b2JSZWFkZXJCcmFuZENoZWNrRXhjZXB0aW9uKCdjYW5jZWwnKSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMuX293bmVyUmVhZGFibGVTdHJlYW0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgcmV0dXJuIHByb21pc2VSZWplY3RlZFdpdGgocmVhZGVyTG9ja0V4Y2VwdGlvbignY2FuY2VsJykpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBSZWFkYWJsZVN0cmVhbVJlYWRlckdlbmVyaWNDYW5jZWwodGhpcywgcmVhc29uKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQXR0ZW1wdHMgdG8gcmVhZHMgYnl0ZXMgaW50byB2aWV3LCBhbmQgcmV0dXJucyBhIHByb21pc2UgcmVzb2x2ZWQgd2l0aCB0aGUgcmVzdWx0LlxuICAgICAqXG4gICAgICogSWYgcmVhZGluZyBhIGNodW5rIGNhdXNlcyB0aGUgcXVldWUgdG8gYmVjb21lIGVtcHR5LCBtb3JlIGRhdGEgd2lsbCBiZSBwdWxsZWQgZnJvbSB0aGUgdW5kZXJseWluZyBzb3VyY2UuXG4gICAgICovXG4gICAgcmVhZCh2aWV3KSB7XG4gICAgICAgIGlmICghSXNSZWFkYWJsZVN0cmVhbUJZT0JSZWFkZXIodGhpcykpIHtcbiAgICAgICAgICAgIHJldHVybiBwcm9taXNlUmVqZWN0ZWRXaXRoKGJ5b2JSZWFkZXJCcmFuZENoZWNrRXhjZXB0aW9uKCdyZWFkJykpO1xuICAgICAgICB9XG4gICAgICAgIGlmICghQXJyYXlCdWZmZXIuaXNWaWV3KHZpZXcpKSB7XG4gICAgICAgICAgICByZXR1cm4gcHJvbWlzZVJlamVjdGVkV2l0aChuZXcgVHlwZUVycm9yKCd2aWV3IG11c3QgYmUgYW4gYXJyYXkgYnVmZmVyIHZpZXcnKSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHZpZXcuYnl0ZUxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgcmV0dXJuIHByb21pc2VSZWplY3RlZFdpdGgobmV3IFR5cGVFcnJvcigndmlldyBtdXN0IGhhdmUgbm9uLXplcm8gYnl0ZUxlbmd0aCcpKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodmlldy5idWZmZXIuYnl0ZUxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgcmV0dXJuIHByb21pc2VSZWplY3RlZFdpdGgobmV3IFR5cGVFcnJvcihgdmlldydzIGJ1ZmZlciBtdXN0IGhhdmUgbm9uLXplcm8gYnl0ZUxlbmd0aGApKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5fb3duZXJSZWFkYWJsZVN0cmVhbSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICByZXR1cm4gcHJvbWlzZVJlamVjdGVkV2l0aChyZWFkZXJMb2NrRXhjZXB0aW9uKCdyZWFkIGZyb20nKSk7XG4gICAgICAgIH1cbiAgICAgICAgbGV0IHJlc29sdmVQcm9taXNlO1xuICAgICAgICBsZXQgcmVqZWN0UHJvbWlzZTtcbiAgICAgICAgY29uc3QgcHJvbWlzZSA9IG5ld1Byb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgcmVzb2x2ZVByb21pc2UgPSByZXNvbHZlO1xuICAgICAgICAgICAgcmVqZWN0UHJvbWlzZSA9IHJlamVjdDtcbiAgICAgICAgfSk7XG4gICAgICAgIGNvbnN0IHJlYWRJbnRvUmVxdWVzdCA9IHtcbiAgICAgICAgICAgIF9jaHVua1N0ZXBzOiBjaHVuayA9PiByZXNvbHZlUHJvbWlzZSh7IHZhbHVlOiBjaHVuaywgZG9uZTogZmFsc2UgfSksXG4gICAgICAgICAgICBfY2xvc2VTdGVwczogY2h1bmsgPT4gcmVzb2x2ZVByb21pc2UoeyB2YWx1ZTogY2h1bmssIGRvbmU6IHRydWUgfSksXG4gICAgICAgICAgICBfZXJyb3JTdGVwczogZSA9PiByZWplY3RQcm9taXNlKGUpXG4gICAgICAgIH07XG4gICAgICAgIFJlYWRhYmxlU3RyZWFtQllPQlJlYWRlclJlYWQodGhpcywgdmlldywgcmVhZEludG9SZXF1ZXN0KTtcbiAgICAgICAgcmV0dXJuIHByb21pc2U7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlbGVhc2VzIHRoZSByZWFkZXIncyBsb2NrIG9uIHRoZSBjb3JyZXNwb25kaW5nIHN0cmVhbS4gQWZ0ZXIgdGhlIGxvY2sgaXMgcmVsZWFzZWQsIHRoZSByZWFkZXIgaXMgbm8gbG9uZ2VyIGFjdGl2ZS5cbiAgICAgKiBJZiB0aGUgYXNzb2NpYXRlZCBzdHJlYW0gaXMgZXJyb3JlZCB3aGVuIHRoZSBsb2NrIGlzIHJlbGVhc2VkLCB0aGUgcmVhZGVyIHdpbGwgYXBwZWFyIGVycm9yZWQgaW4gdGhlIHNhbWUgd2F5XG4gICAgICogZnJvbSBub3cgb247IG90aGVyd2lzZSwgdGhlIHJlYWRlciB3aWxsIGFwcGVhciBjbG9zZWQuXG4gICAgICpcbiAgICAgKiBBIHJlYWRlcidzIGxvY2sgY2Fubm90IGJlIHJlbGVhc2VkIHdoaWxlIGl0IHN0aWxsIGhhcyBhIHBlbmRpbmcgcmVhZCByZXF1ZXN0LCBpLmUuLCBpZiBhIHByb21pc2UgcmV0dXJuZWQgYnlcbiAgICAgKiB0aGUgcmVhZGVyJ3Mge0BsaW5rIFJlYWRhYmxlU3RyZWFtQllPQlJlYWRlci5yZWFkIHwgcmVhZCgpfSBtZXRob2QgaGFzIG5vdCB5ZXQgYmVlbiBzZXR0bGVkLiBBdHRlbXB0aW5nIHRvXG4gICAgICogZG8gc28gd2lsbCB0aHJvdyBhIGBUeXBlRXJyb3JgIGFuZCBsZWF2ZSB0aGUgcmVhZGVyIGxvY2tlZCB0byB0aGUgc3RyZWFtLlxuICAgICAqL1xuICAgIHJlbGVhc2VMb2NrKCkge1xuICAgICAgICBpZiAoIUlzUmVhZGFibGVTdHJlYW1CWU9CUmVhZGVyKHRoaXMpKSB7XG4gICAgICAgICAgICB0aHJvdyBieW9iUmVhZGVyQnJhbmRDaGVja0V4Y2VwdGlvbigncmVsZWFzZUxvY2snKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5fb3duZXJSZWFkYWJsZVN0cmVhbSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMuX3JlYWRJbnRvUmVxdWVzdHMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignVHJpZWQgdG8gcmVsZWFzZSBhIHJlYWRlciBsb2NrIHdoZW4gdGhhdCByZWFkZXIgaGFzIHBlbmRpbmcgcmVhZCgpIGNhbGxzIHVuLXNldHRsZWQnKTtcbiAgICAgICAgfVxuICAgICAgICBSZWFkYWJsZVN0cmVhbVJlYWRlckdlbmVyaWNSZWxlYXNlKHRoaXMpO1xuICAgIH1cbn1cbk9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKFJlYWRhYmxlU3RyZWFtQllPQlJlYWRlci5wcm90b3R5cGUsIHtcbiAgICBjYW5jZWw6IHsgZW51bWVyYWJsZTogdHJ1ZSB9LFxuICAgIHJlYWQ6IHsgZW51bWVyYWJsZTogdHJ1ZSB9LFxuICAgIHJlbGVhc2VMb2NrOiB7IGVudW1lcmFibGU6IHRydWUgfSxcbiAgICBjbG9zZWQ6IHsgZW51bWVyYWJsZTogdHJ1ZSB9XG59KTtcbmlmICh0eXBlb2YgU3ltYm9sUG9seWZpbGwudG9TdHJpbmdUYWcgPT09ICdzeW1ib2wnKSB7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KFJlYWRhYmxlU3RyZWFtQllPQlJlYWRlci5wcm90b3R5cGUsIFN5bWJvbFBvbHlmaWxsLnRvU3RyaW5nVGFnLCB7XG4gICAgICAgIHZhbHVlOiAnUmVhZGFibGVTdHJlYW1CWU9CUmVhZGVyJyxcbiAgICAgICAgY29uZmlndXJhYmxlOiB0cnVlXG4gICAgfSk7XG59XG4vLyBBYnN0cmFjdCBvcGVyYXRpb25zIGZvciB0aGUgcmVhZGVycy5cbmZ1bmN0aW9uIElzUmVhZGFibGVTdHJlYW1CWU9CUmVhZGVyKHgpIHtcbiAgICBpZiAoIXR5cGVJc09iamVjdCh4KSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGlmICghT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHgsICdfcmVhZEludG9SZXF1ZXN0cycpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgcmV0dXJuIHRydWU7XG59XG5mdW5jdGlvbiBSZWFkYWJsZVN0cmVhbUJZT0JSZWFkZXJSZWFkKHJlYWRlciwgdmlldywgcmVhZEludG9SZXF1ZXN0KSB7XG4gICAgY29uc3Qgc3RyZWFtID0gcmVhZGVyLl9vd25lclJlYWRhYmxlU3RyZWFtO1xuICAgIHN0cmVhbS5fZGlzdHVyYmVkID0gdHJ1ZTtcbiAgICBpZiAoc3RyZWFtLl9zdGF0ZSA9PT0gJ2Vycm9yZWQnKSB7XG4gICAgICAgIHJlYWRJbnRvUmVxdWVzdC5fZXJyb3JTdGVwcyhzdHJlYW0uX3N0b3JlZEVycm9yKTtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJQdWxsSW50byhzdHJlYW0uX3JlYWRhYmxlU3RyZWFtQ29udHJvbGxlciwgdmlldywgcmVhZEludG9SZXF1ZXN0KTtcbiAgICB9XG59XG4vLyBIZWxwZXIgZnVuY3Rpb25zIGZvciB0aGUgUmVhZGFibGVTdHJlYW1CWU9CUmVhZGVyLlxuZnVuY3Rpb24gYnlvYlJlYWRlckJyYW5kQ2hlY2tFeGNlcHRpb24obmFtZSkge1xuICAgIHJldHVybiBuZXcgVHlwZUVycm9yKGBSZWFkYWJsZVN0cmVhbUJZT0JSZWFkZXIucHJvdG90eXBlLiR7bmFtZX0gY2FuIG9ubHkgYmUgdXNlZCBvbiBhIFJlYWRhYmxlU3RyZWFtQllPQlJlYWRlcmApO1xufVxuXG5mdW5jdGlvbiBFeHRyYWN0SGlnaFdhdGVyTWFyayhzdHJhdGVneSwgZGVmYXVsdEhXTSkge1xuICAgIGNvbnN0IHsgaGlnaFdhdGVyTWFyayB9ID0gc3RyYXRlZ3k7XG4gICAgaWYgKGhpZ2hXYXRlck1hcmsgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm4gZGVmYXVsdEhXTTtcbiAgICB9XG4gICAgaWYgKE51bWJlcklzTmFOKGhpZ2hXYXRlck1hcmspIHx8IGhpZ2hXYXRlck1hcmsgPCAwKSB7XG4gICAgICAgIHRocm93IG5ldyBSYW5nZUVycm9yKCdJbnZhbGlkIGhpZ2hXYXRlck1hcmsnKTtcbiAgICB9XG4gICAgcmV0dXJuIGhpZ2hXYXRlck1hcms7XG59XG5mdW5jdGlvbiBFeHRyYWN0U2l6ZUFsZ29yaXRobShzdHJhdGVneSkge1xuICAgIGNvbnN0IHsgc2l6ZSB9ID0gc3RyYXRlZ3k7XG4gICAgaWYgKCFzaXplKSB7XG4gICAgICAgIHJldHVybiAoKSA9PiAxO1xuICAgIH1cbiAgICByZXR1cm4gc2l6ZTtcbn1cblxuZnVuY3Rpb24gY29udmVydFF1ZXVpbmdTdHJhdGVneShpbml0LCBjb250ZXh0KSB7XG4gICAgYXNzZXJ0RGljdGlvbmFyeShpbml0LCBjb250ZXh0KTtcbiAgICBjb25zdCBoaWdoV2F0ZXJNYXJrID0gaW5pdCA9PT0gbnVsbCB8fCBpbml0ID09PSB2b2lkIDAgPyB2b2lkIDAgOiBpbml0LmhpZ2hXYXRlck1hcms7XG4gICAgY29uc3Qgc2l6ZSA9IGluaXQgPT09IG51bGwgfHwgaW5pdCA9PT0gdm9pZCAwID8gdm9pZCAwIDogaW5pdC5zaXplO1xuICAgIHJldHVybiB7XG4gICAgICAgIGhpZ2hXYXRlck1hcms6IGhpZ2hXYXRlck1hcmsgPT09IHVuZGVmaW5lZCA/IHVuZGVmaW5lZCA6IGNvbnZlcnRVbnJlc3RyaWN0ZWREb3VibGUoaGlnaFdhdGVyTWFyayksXG4gICAgICAgIHNpemU6IHNpemUgPT09IHVuZGVmaW5lZCA/IHVuZGVmaW5lZCA6IGNvbnZlcnRRdWV1aW5nU3RyYXRlZ3lTaXplKHNpemUsIGAke2NvbnRleHR9IGhhcyBtZW1iZXIgJ3NpemUnIHRoYXRgKVxuICAgIH07XG59XG5mdW5jdGlvbiBjb252ZXJ0UXVldWluZ1N0cmF0ZWd5U2l6ZShmbiwgY29udGV4dCkge1xuICAgIGFzc2VydEZ1bmN0aW9uKGZuLCBjb250ZXh0KTtcbiAgICByZXR1cm4gY2h1bmsgPT4gY29udmVydFVucmVzdHJpY3RlZERvdWJsZShmbihjaHVuaykpO1xufVxuXG5mdW5jdGlvbiBjb252ZXJ0VW5kZXJseWluZ1Npbmsob3JpZ2luYWwsIGNvbnRleHQpIHtcbiAgICBhc3NlcnREaWN0aW9uYXJ5KG9yaWdpbmFsLCBjb250ZXh0KTtcbiAgICBjb25zdCBhYm9ydCA9IG9yaWdpbmFsID09PSBudWxsIHx8IG9yaWdpbmFsID09PSB2b2lkIDAgPyB2b2lkIDAgOiBvcmlnaW5hbC5hYm9ydDtcbiAgICBjb25zdCBjbG9zZSA9IG9yaWdpbmFsID09PSBudWxsIHx8IG9yaWdpbmFsID09PSB2b2lkIDAgPyB2b2lkIDAgOiBvcmlnaW5hbC5jbG9zZTtcbiAgICBjb25zdCBzdGFydCA9IG9yaWdpbmFsID09PSBudWxsIHx8IG9yaWdpbmFsID09PSB2b2lkIDAgPyB2b2lkIDAgOiBvcmlnaW5hbC5zdGFydDtcbiAgICBjb25zdCB0eXBlID0gb3JpZ2luYWwgPT09IG51bGwgfHwgb3JpZ2luYWwgPT09IHZvaWQgMCA/IHZvaWQgMCA6IG9yaWdpbmFsLnR5cGU7XG4gICAgY29uc3Qgd3JpdGUgPSBvcmlnaW5hbCA9PT0gbnVsbCB8fCBvcmlnaW5hbCA9PT0gdm9pZCAwID8gdm9pZCAwIDogb3JpZ2luYWwud3JpdGU7XG4gICAgcmV0dXJuIHtcbiAgICAgICAgYWJvcnQ6IGFib3J0ID09PSB1bmRlZmluZWQgP1xuICAgICAgICAgICAgdW5kZWZpbmVkIDpcbiAgICAgICAgICAgIGNvbnZlcnRVbmRlcmx5aW5nU2lua0Fib3J0Q2FsbGJhY2soYWJvcnQsIG9yaWdpbmFsLCBgJHtjb250ZXh0fSBoYXMgbWVtYmVyICdhYm9ydCcgdGhhdGApLFxuICAgICAgICBjbG9zZTogY2xvc2UgPT09IHVuZGVmaW5lZCA/XG4gICAgICAgICAgICB1bmRlZmluZWQgOlxuICAgICAgICAgICAgY29udmVydFVuZGVybHlpbmdTaW5rQ2xvc2VDYWxsYmFjayhjbG9zZSwgb3JpZ2luYWwsIGAke2NvbnRleHR9IGhhcyBtZW1iZXIgJ2Nsb3NlJyB0aGF0YCksXG4gICAgICAgIHN0YXJ0OiBzdGFydCA9PT0gdW5kZWZpbmVkID9cbiAgICAgICAgICAgIHVuZGVmaW5lZCA6XG4gICAgICAgICAgICBjb252ZXJ0VW5kZXJseWluZ1NpbmtTdGFydENhbGxiYWNrKHN0YXJ0LCBvcmlnaW5hbCwgYCR7Y29udGV4dH0gaGFzIG1lbWJlciAnc3RhcnQnIHRoYXRgKSxcbiAgICAgICAgd3JpdGU6IHdyaXRlID09PSB1bmRlZmluZWQgP1xuICAgICAgICAgICAgdW5kZWZpbmVkIDpcbiAgICAgICAgICAgIGNvbnZlcnRVbmRlcmx5aW5nU2lua1dyaXRlQ2FsbGJhY2sod3JpdGUsIG9yaWdpbmFsLCBgJHtjb250ZXh0fSBoYXMgbWVtYmVyICd3cml0ZScgdGhhdGApLFxuICAgICAgICB0eXBlXG4gICAgfTtcbn1cbmZ1bmN0aW9uIGNvbnZlcnRVbmRlcmx5aW5nU2lua0Fib3J0Q2FsbGJhY2soZm4sIG9yaWdpbmFsLCBjb250ZXh0KSB7XG4gICAgYXNzZXJ0RnVuY3Rpb24oZm4sIGNvbnRleHQpO1xuICAgIHJldHVybiAocmVhc29uKSA9PiBwcm9taXNlQ2FsbChmbiwgb3JpZ2luYWwsIFtyZWFzb25dKTtcbn1cbmZ1bmN0aW9uIGNvbnZlcnRVbmRlcmx5aW5nU2lua0Nsb3NlQ2FsbGJhY2soZm4sIG9yaWdpbmFsLCBjb250ZXh0KSB7XG4gICAgYXNzZXJ0RnVuY3Rpb24oZm4sIGNvbnRleHQpO1xuICAgIHJldHVybiAoKSA9PiBwcm9taXNlQ2FsbChmbiwgb3JpZ2luYWwsIFtdKTtcbn1cbmZ1bmN0aW9uIGNvbnZlcnRVbmRlcmx5aW5nU2lua1N0YXJ0Q2FsbGJhY2soZm4sIG9yaWdpbmFsLCBjb250ZXh0KSB7XG4gICAgYXNzZXJ0RnVuY3Rpb24oZm4sIGNvbnRleHQpO1xuICAgIHJldHVybiAoY29udHJvbGxlcikgPT4gcmVmbGVjdENhbGwoZm4sIG9yaWdpbmFsLCBbY29udHJvbGxlcl0pO1xufVxuZnVuY3Rpb24gY29udmVydFVuZGVybHlpbmdTaW5rV3JpdGVDYWxsYmFjayhmbiwgb3JpZ2luYWwsIGNvbnRleHQpIHtcbiAgICBhc3NlcnRGdW5jdGlvbihmbiwgY29udGV4dCk7XG4gICAgcmV0dXJuIChjaHVuaywgY29udHJvbGxlcikgPT4gcHJvbWlzZUNhbGwoZm4sIG9yaWdpbmFsLCBbY2h1bmssIGNvbnRyb2xsZXJdKTtcbn1cblxuZnVuY3Rpb24gYXNzZXJ0V3JpdGFibGVTdHJlYW0oeCwgY29udGV4dCkge1xuICAgIGlmICghSXNXcml0YWJsZVN0cmVhbSh4KSkge1xuICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKGAke2NvbnRleHR9IGlzIG5vdCBhIFdyaXRhYmxlU3RyZWFtLmApO1xuICAgIH1cbn1cblxuLyoqXG4gKiBBIHdyaXRhYmxlIHN0cmVhbSByZXByZXNlbnRzIGEgZGVzdGluYXRpb24gZm9yIGRhdGEsIGludG8gd2hpY2ggeW91IGNhbiB3cml0ZS5cbiAqXG4gKiBAcHVibGljXG4gKi9cbmNsYXNzIFdyaXRhYmxlU3RyZWFtIHtcbiAgICBjb25zdHJ1Y3RvcihyYXdVbmRlcmx5aW5nU2luayA9IHt9LCByYXdTdHJhdGVneSA9IHt9KSB7XG4gICAgICAgIGlmIChyYXdVbmRlcmx5aW5nU2luayA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICByYXdVbmRlcmx5aW5nU2luayA9IG51bGw7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBhc3NlcnRPYmplY3QocmF3VW5kZXJseWluZ1NpbmssICdGaXJzdCBwYXJhbWV0ZXInKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBzdHJhdGVneSA9IGNvbnZlcnRRdWV1aW5nU3RyYXRlZ3kocmF3U3RyYXRlZ3ksICdTZWNvbmQgcGFyYW1ldGVyJyk7XG4gICAgICAgIGNvbnN0IHVuZGVybHlpbmdTaW5rID0gY29udmVydFVuZGVybHlpbmdTaW5rKHJhd1VuZGVybHlpbmdTaW5rLCAnRmlyc3QgcGFyYW1ldGVyJyk7XG4gICAgICAgIEluaXRpYWxpemVXcml0YWJsZVN0cmVhbSh0aGlzKTtcbiAgICAgICAgY29uc3QgdHlwZSA9IHVuZGVybHlpbmdTaW5rLnR5cGU7XG4gICAgICAgIGlmICh0eXBlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBSYW5nZUVycm9yKCdJbnZhbGlkIHR5cGUgaXMgc3BlY2lmaWVkJyk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3Qgc2l6ZUFsZ29yaXRobSA9IEV4dHJhY3RTaXplQWxnb3JpdGhtKHN0cmF0ZWd5KTtcbiAgICAgICAgY29uc3QgaGlnaFdhdGVyTWFyayA9IEV4dHJhY3RIaWdoV2F0ZXJNYXJrKHN0cmF0ZWd5LCAxKTtcbiAgICAgICAgU2V0VXBXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyRnJvbVVuZGVybHlpbmdTaW5rKHRoaXMsIHVuZGVybHlpbmdTaW5rLCBoaWdoV2F0ZXJNYXJrLCBzaXplQWxnb3JpdGhtKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmV0dXJucyB3aGV0aGVyIG9yIG5vdCB0aGUgd3JpdGFibGUgc3RyZWFtIGlzIGxvY2tlZCB0byBhIHdyaXRlci5cbiAgICAgKi9cbiAgICBnZXQgbG9ja2VkKCkge1xuICAgICAgICBpZiAoIUlzV3JpdGFibGVTdHJlYW0odGhpcykpIHtcbiAgICAgICAgICAgIHRocm93IHN0cmVhbUJyYW5kQ2hlY2tFeGNlcHRpb24oJ2xvY2tlZCcpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBJc1dyaXRhYmxlU3RyZWFtTG9ja2VkKHRoaXMpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBBYm9ydHMgdGhlIHN0cmVhbSwgc2lnbmFsaW5nIHRoYXQgdGhlIHByb2R1Y2VyIGNhbiBubyBsb25nZXIgc3VjY2Vzc2Z1bGx5IHdyaXRlIHRvIHRoZSBzdHJlYW0gYW5kIGl0IGlzIHRvIGJlXG4gICAgICogaW1tZWRpYXRlbHkgbW92ZWQgdG8gYW4gZXJyb3JlZCBzdGF0ZSwgd2l0aCBhbnkgcXVldWVkLXVwIHdyaXRlcyBkaXNjYXJkZWQuIFRoaXMgd2lsbCBhbHNvIGV4ZWN1dGUgYW55IGFib3J0XG4gICAgICogbWVjaGFuaXNtIG9mIHRoZSB1bmRlcmx5aW5nIHNpbmsuXG4gICAgICpcbiAgICAgKiBUaGUgcmV0dXJuZWQgcHJvbWlzZSB3aWxsIGZ1bGZpbGwgaWYgdGhlIHN0cmVhbSBzaHV0cyBkb3duIHN1Y2Nlc3NmdWxseSwgb3IgcmVqZWN0IGlmIHRoZSB1bmRlcmx5aW5nIHNpbmsgc2lnbmFsZWRcbiAgICAgKiB0aGF0IHRoZXJlIHdhcyBhbiBlcnJvciBkb2luZyBzby4gQWRkaXRpb25hbGx5LCBpdCB3aWxsIHJlamVjdCB3aXRoIGEgYFR5cGVFcnJvcmAgKHdpdGhvdXQgYXR0ZW1wdGluZyB0byBjYW5jZWxcbiAgICAgKiB0aGUgc3RyZWFtKSBpZiB0aGUgc3RyZWFtIGlzIGN1cnJlbnRseSBsb2NrZWQuXG4gICAgICovXG4gICAgYWJvcnQocmVhc29uID0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGlmICghSXNXcml0YWJsZVN0cmVhbSh0aGlzKSkge1xuICAgICAgICAgICAgcmV0dXJuIHByb21pc2VSZWplY3RlZFdpdGgoc3RyZWFtQnJhbmRDaGVja0V4Y2VwdGlvbignYWJvcnQnKSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKElzV3JpdGFibGVTdHJlYW1Mb2NrZWQodGhpcykpIHtcbiAgICAgICAgICAgIHJldHVybiBwcm9taXNlUmVqZWN0ZWRXaXRoKG5ldyBUeXBlRXJyb3IoJ0Nhbm5vdCBhYm9ydCBhIHN0cmVhbSB0aGF0IGFscmVhZHkgaGFzIGEgd3JpdGVyJykpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBXcml0YWJsZVN0cmVhbUFib3J0KHRoaXMsIHJlYXNvbik7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENsb3NlcyB0aGUgc3RyZWFtLiBUaGUgdW5kZXJseWluZyBzaW5rIHdpbGwgZmluaXNoIHByb2Nlc3NpbmcgYW55IHByZXZpb3VzbHktd3JpdHRlbiBjaHVua3MsIGJlZm9yZSBpbnZva2luZyBpdHNcbiAgICAgKiBjbG9zZSBiZWhhdmlvci4gRHVyaW5nIHRoaXMgdGltZSBhbnkgZnVydGhlciBhdHRlbXB0cyB0byB3cml0ZSB3aWxsIGZhaWwgKHdpdGhvdXQgZXJyb3JpbmcgdGhlIHN0cmVhbSkuXG4gICAgICpcbiAgICAgKiBUaGUgbWV0aG9kIHJldHVybnMgYSBwcm9taXNlIHRoYXQgd2lsbCBmdWxmaWxsIGlmIGFsbCByZW1haW5pbmcgY2h1bmtzIGFyZSBzdWNjZXNzZnVsbHkgd3JpdHRlbiBhbmQgdGhlIHN0cmVhbVxuICAgICAqIHN1Y2Nlc3NmdWxseSBjbG9zZXMsIG9yIHJlamVjdHMgaWYgYW4gZXJyb3IgaXMgZW5jb3VudGVyZWQgZHVyaW5nIHRoaXMgcHJvY2Vzcy4gQWRkaXRpb25hbGx5LCBpdCB3aWxsIHJlamVjdCB3aXRoXG4gICAgICogYSBgVHlwZUVycm9yYCAod2l0aG91dCBhdHRlbXB0aW5nIHRvIGNhbmNlbCB0aGUgc3RyZWFtKSBpZiB0aGUgc3RyZWFtIGlzIGN1cnJlbnRseSBsb2NrZWQuXG4gICAgICovXG4gICAgY2xvc2UoKSB7XG4gICAgICAgIGlmICghSXNXcml0YWJsZVN0cmVhbSh0aGlzKSkge1xuICAgICAgICAgICAgcmV0dXJuIHByb21pc2VSZWplY3RlZFdpdGgoc3RyZWFtQnJhbmRDaGVja0V4Y2VwdGlvbignY2xvc2UnKSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKElzV3JpdGFibGVTdHJlYW1Mb2NrZWQodGhpcykpIHtcbiAgICAgICAgICAgIHJldHVybiBwcm9taXNlUmVqZWN0ZWRXaXRoKG5ldyBUeXBlRXJyb3IoJ0Nhbm5vdCBjbG9zZSBhIHN0cmVhbSB0aGF0IGFscmVhZHkgaGFzIGEgd3JpdGVyJykpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChXcml0YWJsZVN0cmVhbUNsb3NlUXVldWVkT3JJbkZsaWdodCh0aGlzKSkge1xuICAgICAgICAgICAgcmV0dXJuIHByb21pc2VSZWplY3RlZFdpdGgobmV3IFR5cGVFcnJvcignQ2Fubm90IGNsb3NlIGFuIGFscmVhZHktY2xvc2luZyBzdHJlYW0nKSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIFdyaXRhYmxlU3RyZWFtQ2xvc2UodGhpcyk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYSB7QGxpbmsgV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyIHwgd3JpdGVyfSBhbmQgbG9ja3MgdGhlIHN0cmVhbSB0byB0aGUgbmV3IHdyaXRlci4gV2hpbGUgdGhlIHN0cmVhbVxuICAgICAqIGlzIGxvY2tlZCwgbm8gb3RoZXIgd3JpdGVyIGNhbiBiZSBhY3F1aXJlZCB1bnRpbCB0aGlzIG9uZSBpcyByZWxlYXNlZC5cbiAgICAgKlxuICAgICAqIFRoaXMgZnVuY3Rpb25hbGl0eSBpcyBlc3BlY2lhbGx5IHVzZWZ1bCBmb3IgY3JlYXRpbmcgYWJzdHJhY3Rpb25zIHRoYXQgZGVzaXJlIHRoZSBhYmlsaXR5IHRvIHdyaXRlIHRvIGEgc3RyZWFtXG4gICAgICogd2l0aG91dCBpbnRlcnJ1cHRpb24gb3IgaW50ZXJsZWF2aW5nLiBCeSBnZXR0aW5nIGEgd3JpdGVyIGZvciB0aGUgc3RyZWFtLCB5b3UgY2FuIGVuc3VyZSBub2JvZHkgZWxzZSBjYW4gd3JpdGUgYXRcbiAgICAgKiB0aGUgc2FtZSB0aW1lLCB3aGljaCB3b3VsZCBjYXVzZSB0aGUgcmVzdWx0aW5nIHdyaXR0ZW4gZGF0YSB0byBiZSB1bnByZWRpY3RhYmxlIGFuZCBwcm9iYWJseSB1c2VsZXNzLlxuICAgICAqL1xuICAgIGdldFdyaXRlcigpIHtcbiAgICAgICAgaWYgKCFJc1dyaXRhYmxlU3RyZWFtKHRoaXMpKSB7XG4gICAgICAgICAgICB0aHJvdyBzdHJlYW1CcmFuZENoZWNrRXhjZXB0aW9uKCdnZXRXcml0ZXInKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gQWNxdWlyZVdyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlcih0aGlzKTtcbiAgICB9XG59XG5PYmplY3QuZGVmaW5lUHJvcGVydGllcyhXcml0YWJsZVN0cmVhbS5wcm90b3R5cGUsIHtcbiAgICBhYm9ydDogeyBlbnVtZXJhYmxlOiB0cnVlIH0sXG4gICAgY2xvc2U6IHsgZW51bWVyYWJsZTogdHJ1ZSB9LFxuICAgIGdldFdyaXRlcjogeyBlbnVtZXJhYmxlOiB0cnVlIH0sXG4gICAgbG9ja2VkOiB7IGVudW1lcmFibGU6IHRydWUgfVxufSk7XG5pZiAodHlwZW9mIFN5bWJvbFBvbHlmaWxsLnRvU3RyaW5nVGFnID09PSAnc3ltYm9sJykge1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShXcml0YWJsZVN0cmVhbS5wcm90b3R5cGUsIFN5bWJvbFBvbHlmaWxsLnRvU3RyaW5nVGFnLCB7XG4gICAgICAgIHZhbHVlOiAnV3JpdGFibGVTdHJlYW0nLFxuICAgICAgICBjb25maWd1cmFibGU6IHRydWVcbiAgICB9KTtcbn1cbi8vIEFic3RyYWN0IG9wZXJhdGlvbnMgZm9yIHRoZSBXcml0YWJsZVN0cmVhbS5cbmZ1bmN0aW9uIEFjcXVpcmVXcml0YWJsZVN0cmVhbURlZmF1bHRXcml0ZXIoc3RyZWFtKSB7XG4gICAgcmV0dXJuIG5ldyBXcml0YWJsZVN0cmVhbURlZmF1bHRXcml0ZXIoc3RyZWFtKTtcbn1cbi8vIFRocm93cyBpZiBhbmQgb25seSBpZiBzdGFydEFsZ29yaXRobSB0aHJvd3MuXG5mdW5jdGlvbiBDcmVhdGVXcml0YWJsZVN0cmVhbShzdGFydEFsZ29yaXRobSwgd3JpdGVBbGdvcml0aG0sIGNsb3NlQWxnb3JpdGhtLCBhYm9ydEFsZ29yaXRobSwgaGlnaFdhdGVyTWFyayA9IDEsIHNpemVBbGdvcml0aG0gPSAoKSA9PiAxKSB7XG4gICAgY29uc3Qgc3RyZWFtID0gT2JqZWN0LmNyZWF0ZShXcml0YWJsZVN0cmVhbS5wcm90b3R5cGUpO1xuICAgIEluaXRpYWxpemVXcml0YWJsZVN0cmVhbShzdHJlYW0pO1xuICAgIGNvbnN0IGNvbnRyb2xsZXIgPSBPYmplY3QuY3JlYXRlKFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXIucHJvdG90eXBlKTtcbiAgICBTZXRVcFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXIoc3RyZWFtLCBjb250cm9sbGVyLCBzdGFydEFsZ29yaXRobSwgd3JpdGVBbGdvcml0aG0sIGNsb3NlQWxnb3JpdGhtLCBhYm9ydEFsZ29yaXRobSwgaGlnaFdhdGVyTWFyaywgc2l6ZUFsZ29yaXRobSk7XG4gICAgcmV0dXJuIHN0cmVhbTtcbn1cbmZ1bmN0aW9uIEluaXRpYWxpemVXcml0YWJsZVN0cmVhbShzdHJlYW0pIHtcbiAgICBzdHJlYW0uX3N0YXRlID0gJ3dyaXRhYmxlJztcbiAgICAvLyBUaGUgZXJyb3IgdGhhdCB3aWxsIGJlIHJlcG9ydGVkIGJ5IG5ldyBtZXRob2QgY2FsbHMgb25jZSB0aGUgc3RhdGUgYmVjb21lcyBlcnJvcmVkLiBPbmx5IHNldCB3aGVuIFtbc3RhdGVdXSBpc1xuICAgIC8vICdlcnJvcmluZycgb3IgJ2Vycm9yZWQnLiBNYXkgYmUgc2V0IHRvIGFuIHVuZGVmaW5lZCB2YWx1ZS5cbiAgICBzdHJlYW0uX3N0b3JlZEVycm9yID0gdW5kZWZpbmVkO1xuICAgIHN0cmVhbS5fd3JpdGVyID0gdW5kZWZpbmVkO1xuICAgIC8vIEluaXRpYWxpemUgdG8gdW5kZWZpbmVkIGZpcnN0IGJlY2F1c2UgdGhlIGNvbnN0cnVjdG9yIG9mIHRoZSBjb250cm9sbGVyIGNoZWNrcyB0aGlzXG4gICAgLy8gdmFyaWFibGUgdG8gdmFsaWRhdGUgdGhlIGNhbGxlci5cbiAgICBzdHJlYW0uX3dyaXRhYmxlU3RyZWFtQ29udHJvbGxlciA9IHVuZGVmaW5lZDtcbiAgICAvLyBUaGlzIHF1ZXVlIGlzIHBsYWNlZCBoZXJlIGluc3RlYWQgb2YgdGhlIHdyaXRlciBjbGFzcyBpbiBvcmRlciB0byBhbGxvdyBmb3IgcGFzc2luZyBhIHdyaXRlciB0byB0aGUgbmV4dCBkYXRhXG4gICAgLy8gcHJvZHVjZXIgd2l0aG91dCB3YWl0aW5nIGZvciB0aGUgcXVldWVkIHdyaXRlcyB0byBmaW5pc2guXG4gICAgc3RyZWFtLl93cml0ZVJlcXVlc3RzID0gbmV3IFNpbXBsZVF1ZXVlKCk7XG4gICAgLy8gV3JpdGUgcmVxdWVzdHMgYXJlIHJlbW92ZWQgZnJvbSBfd3JpdGVSZXF1ZXN0cyB3aGVuIHdyaXRlKCkgaXMgY2FsbGVkIG9uIHRoZSB1bmRlcmx5aW5nIHNpbmsuIFRoaXMgcHJldmVudHNcbiAgICAvLyB0aGVtIGZyb20gYmVpbmcgZXJyb25lb3VzbHkgcmVqZWN0ZWQgb24gZXJyb3IuIElmIGEgd3JpdGUoKSBjYWxsIGlzIGluLWZsaWdodCwgdGhlIHJlcXVlc3QgaXMgc3RvcmVkIGhlcmUuXG4gICAgc3RyZWFtLl9pbkZsaWdodFdyaXRlUmVxdWVzdCA9IHVuZGVmaW5lZDtcbiAgICAvLyBUaGUgcHJvbWlzZSB0aGF0IHdhcyByZXR1cm5lZCBmcm9tIHdyaXRlci5jbG9zZSgpLiBTdG9yZWQgaGVyZSBiZWNhdXNlIGl0IG1heSBiZSBmdWxmaWxsZWQgYWZ0ZXIgdGhlIHdyaXRlclxuICAgIC8vIGhhcyBiZWVuIGRldGFjaGVkLlxuICAgIHN0cmVhbS5fY2xvc2VSZXF1ZXN0ID0gdW5kZWZpbmVkO1xuICAgIC8vIENsb3NlIHJlcXVlc3QgaXMgcmVtb3ZlZCBmcm9tIF9jbG9zZVJlcXVlc3Qgd2hlbiBjbG9zZSgpIGlzIGNhbGxlZCBvbiB0aGUgdW5kZXJseWluZyBzaW5rLiBUaGlzIHByZXZlbnRzIGl0XG4gICAgLy8gZnJvbSBiZWluZyBlcnJvbmVvdXNseSByZWplY3RlZCBvbiBlcnJvci4gSWYgYSBjbG9zZSgpIGNhbGwgaXMgaW4tZmxpZ2h0LCB0aGUgcmVxdWVzdCBpcyBzdG9yZWQgaGVyZS5cbiAgICBzdHJlYW0uX2luRmxpZ2h0Q2xvc2VSZXF1ZXN0ID0gdW5kZWZpbmVkO1xuICAgIC8vIFRoZSBwcm9taXNlIHRoYXQgd2FzIHJldHVybmVkIGZyb20gd3JpdGVyLmFib3J0KCkuIFRoaXMgbWF5IGFsc28gYmUgZnVsZmlsbGVkIGFmdGVyIHRoZSB3cml0ZXIgaGFzIGRldGFjaGVkLlxuICAgIHN0cmVhbS5fcGVuZGluZ0Fib3J0UmVxdWVzdCA9IHVuZGVmaW5lZDtcbiAgICAvLyBUaGUgYmFja3ByZXNzdXJlIHNpZ25hbCBzZXQgYnkgdGhlIGNvbnRyb2xsZXIuXG4gICAgc3RyZWFtLl9iYWNrcHJlc3N1cmUgPSBmYWxzZTtcbn1cbmZ1bmN0aW9uIElzV3JpdGFibGVTdHJlYW0oeCkge1xuICAgIGlmICghdHlwZUlzT2JqZWN0KHgpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgaWYgKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoeCwgJ193cml0YWJsZVN0cmVhbUNvbnRyb2xsZXInKSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xufVxuZnVuY3Rpb24gSXNXcml0YWJsZVN0cmVhbUxvY2tlZChzdHJlYW0pIHtcbiAgICBpZiAoc3RyZWFtLl93cml0ZXIgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xufVxuZnVuY3Rpb24gV3JpdGFibGVTdHJlYW1BYm9ydChzdHJlYW0sIHJlYXNvbikge1xuICAgIGNvbnN0IHN0YXRlID0gc3RyZWFtLl9zdGF0ZTtcbiAgICBpZiAoc3RhdGUgPT09ICdjbG9zZWQnIHx8IHN0YXRlID09PSAnZXJyb3JlZCcpIHtcbiAgICAgICAgcmV0dXJuIHByb21pc2VSZXNvbHZlZFdpdGgodW5kZWZpbmVkKTtcbiAgICB9XG4gICAgaWYgKHN0cmVhbS5fcGVuZGluZ0Fib3J0UmVxdWVzdCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiBzdHJlYW0uX3BlbmRpbmdBYm9ydFJlcXVlc3QuX3Byb21pc2U7XG4gICAgfVxuICAgIGxldCB3YXNBbHJlYWR5RXJyb3JpbmcgPSBmYWxzZTtcbiAgICBpZiAoc3RhdGUgPT09ICdlcnJvcmluZycpIHtcbiAgICAgICAgd2FzQWxyZWFkeUVycm9yaW5nID0gdHJ1ZTtcbiAgICAgICAgLy8gcmVhc29uIHdpbGwgbm90IGJlIHVzZWQsIHNvIGRvbid0IGtlZXAgYSByZWZlcmVuY2UgdG8gaXQuXG4gICAgICAgIHJlYXNvbiA9IHVuZGVmaW5lZDtcbiAgICB9XG4gICAgY29uc3QgcHJvbWlzZSA9IG5ld1Byb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICBzdHJlYW0uX3BlbmRpbmdBYm9ydFJlcXVlc3QgPSB7XG4gICAgICAgICAgICBfcHJvbWlzZTogdW5kZWZpbmVkLFxuICAgICAgICAgICAgX3Jlc29sdmU6IHJlc29sdmUsXG4gICAgICAgICAgICBfcmVqZWN0OiByZWplY3QsXG4gICAgICAgICAgICBfcmVhc29uOiByZWFzb24sXG4gICAgICAgICAgICBfd2FzQWxyZWFkeUVycm9yaW5nOiB3YXNBbHJlYWR5RXJyb3JpbmdcbiAgICAgICAgfTtcbiAgICB9KTtcbiAgICBzdHJlYW0uX3BlbmRpbmdBYm9ydFJlcXVlc3QuX3Byb21pc2UgPSBwcm9taXNlO1xuICAgIGlmICghd2FzQWxyZWFkeUVycm9yaW5nKSB7XG4gICAgICAgIFdyaXRhYmxlU3RyZWFtU3RhcnRFcnJvcmluZyhzdHJlYW0sIHJlYXNvbik7XG4gICAgfVxuICAgIHJldHVybiBwcm9taXNlO1xufVxuZnVuY3Rpb24gV3JpdGFibGVTdHJlYW1DbG9zZShzdHJlYW0pIHtcbiAgICBjb25zdCBzdGF0ZSA9IHN0cmVhbS5fc3RhdGU7XG4gICAgaWYgKHN0YXRlID09PSAnY2xvc2VkJyB8fCBzdGF0ZSA9PT0gJ2Vycm9yZWQnKSB7XG4gICAgICAgIHJldHVybiBwcm9taXNlUmVqZWN0ZWRXaXRoKG5ldyBUeXBlRXJyb3IoYFRoZSBzdHJlYW0gKGluICR7c3RhdGV9IHN0YXRlKSBpcyBub3QgaW4gdGhlIHdyaXRhYmxlIHN0YXRlIGFuZCBjYW5ub3QgYmUgY2xvc2VkYCkpO1xuICAgIH1cbiAgICBjb25zdCBwcm9taXNlID0gbmV3UHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgIGNvbnN0IGNsb3NlUmVxdWVzdCA9IHtcbiAgICAgICAgICAgIF9yZXNvbHZlOiByZXNvbHZlLFxuICAgICAgICAgICAgX3JlamVjdDogcmVqZWN0XG4gICAgICAgIH07XG4gICAgICAgIHN0cmVhbS5fY2xvc2VSZXF1ZXN0ID0gY2xvc2VSZXF1ZXN0O1xuICAgIH0pO1xuICAgIGNvbnN0IHdyaXRlciA9IHN0cmVhbS5fd3JpdGVyO1xuICAgIGlmICh3cml0ZXIgIT09IHVuZGVmaW5lZCAmJiBzdHJlYW0uX2JhY2twcmVzc3VyZSAmJiBzdGF0ZSA9PT0gJ3dyaXRhYmxlJykge1xuICAgICAgICBkZWZhdWx0V3JpdGVyUmVhZHlQcm9taXNlUmVzb2x2ZSh3cml0ZXIpO1xuICAgIH1cbiAgICBXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyQ2xvc2Uoc3RyZWFtLl93cml0YWJsZVN0cmVhbUNvbnRyb2xsZXIpO1xuICAgIHJldHVybiBwcm9taXNlO1xufVxuLy8gV3JpdGFibGVTdHJlYW0gQVBJIGV4cG9zZWQgZm9yIGNvbnRyb2xsZXJzLlxuZnVuY3Rpb24gV3JpdGFibGVTdHJlYW1BZGRXcml0ZVJlcXVlc3Qoc3RyZWFtKSB7XG4gICAgY29uc3QgcHJvbWlzZSA9IG5ld1Byb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICBjb25zdCB3cml0ZVJlcXVlc3QgPSB7XG4gICAgICAgICAgICBfcmVzb2x2ZTogcmVzb2x2ZSxcbiAgICAgICAgICAgIF9yZWplY3Q6IHJlamVjdFxuICAgICAgICB9O1xuICAgICAgICBzdHJlYW0uX3dyaXRlUmVxdWVzdHMucHVzaCh3cml0ZVJlcXVlc3QpO1xuICAgIH0pO1xuICAgIHJldHVybiBwcm9taXNlO1xufVxuZnVuY3Rpb24gV3JpdGFibGVTdHJlYW1EZWFsV2l0aFJlamVjdGlvbihzdHJlYW0sIGVycm9yKSB7XG4gICAgY29uc3Qgc3RhdGUgPSBzdHJlYW0uX3N0YXRlO1xuICAgIGlmIChzdGF0ZSA9PT0gJ3dyaXRhYmxlJykge1xuICAgICAgICBXcml0YWJsZVN0cmVhbVN0YXJ0RXJyb3Jpbmcoc3RyZWFtLCBlcnJvcik7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgV3JpdGFibGVTdHJlYW1GaW5pc2hFcnJvcmluZyhzdHJlYW0pO1xufVxuZnVuY3Rpb24gV3JpdGFibGVTdHJlYW1TdGFydEVycm9yaW5nKHN0cmVhbSwgcmVhc29uKSB7XG4gICAgY29uc3QgY29udHJvbGxlciA9IHN0cmVhbS5fd3JpdGFibGVTdHJlYW1Db250cm9sbGVyO1xuICAgIHN0cmVhbS5fc3RhdGUgPSAnZXJyb3JpbmcnO1xuICAgIHN0cmVhbS5fc3RvcmVkRXJyb3IgPSByZWFzb247XG4gICAgY29uc3Qgd3JpdGVyID0gc3RyZWFtLl93cml0ZXI7XG4gICAgaWYgKHdyaXRlciAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIFdyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlckVuc3VyZVJlYWR5UHJvbWlzZVJlamVjdGVkKHdyaXRlciwgcmVhc29uKTtcbiAgICB9XG4gICAgaWYgKCFXcml0YWJsZVN0cmVhbUhhc09wZXJhdGlvbk1hcmtlZEluRmxpZ2h0KHN0cmVhbSkgJiYgY29udHJvbGxlci5fc3RhcnRlZCkge1xuICAgICAgICBXcml0YWJsZVN0cmVhbUZpbmlzaEVycm9yaW5nKHN0cmVhbSk7XG4gICAgfVxufVxuZnVuY3Rpb24gV3JpdGFibGVTdHJlYW1GaW5pc2hFcnJvcmluZyhzdHJlYW0pIHtcbiAgICBzdHJlYW0uX3N0YXRlID0gJ2Vycm9yZWQnO1xuICAgIHN0cmVhbS5fd3JpdGFibGVTdHJlYW1Db250cm9sbGVyW0Vycm9yU3RlcHNdKCk7XG4gICAgY29uc3Qgc3RvcmVkRXJyb3IgPSBzdHJlYW0uX3N0b3JlZEVycm9yO1xuICAgIHN0cmVhbS5fd3JpdGVSZXF1ZXN0cy5mb3JFYWNoKHdyaXRlUmVxdWVzdCA9PiB7XG4gICAgICAgIHdyaXRlUmVxdWVzdC5fcmVqZWN0KHN0b3JlZEVycm9yKTtcbiAgICB9KTtcbiAgICBzdHJlYW0uX3dyaXRlUmVxdWVzdHMgPSBuZXcgU2ltcGxlUXVldWUoKTtcbiAgICBpZiAoc3RyZWFtLl9wZW5kaW5nQWJvcnRSZXF1ZXN0ID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgV3JpdGFibGVTdHJlYW1SZWplY3RDbG9zZUFuZENsb3NlZFByb21pc2VJZk5lZWRlZChzdHJlYW0pO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGFib3J0UmVxdWVzdCA9IHN0cmVhbS5fcGVuZGluZ0Fib3J0UmVxdWVzdDtcbiAgICBzdHJlYW0uX3BlbmRpbmdBYm9ydFJlcXVlc3QgPSB1bmRlZmluZWQ7XG4gICAgaWYgKGFib3J0UmVxdWVzdC5fd2FzQWxyZWFkeUVycm9yaW5nKSB7XG4gICAgICAgIGFib3J0UmVxdWVzdC5fcmVqZWN0KHN0b3JlZEVycm9yKTtcbiAgICAgICAgV3JpdGFibGVTdHJlYW1SZWplY3RDbG9zZUFuZENsb3NlZFByb21pc2VJZk5lZWRlZChzdHJlYW0pO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHByb21pc2UgPSBzdHJlYW0uX3dyaXRhYmxlU3RyZWFtQ29udHJvbGxlcltBYm9ydFN0ZXBzXShhYm9ydFJlcXVlc3QuX3JlYXNvbik7XG4gICAgdXBvblByb21pc2UocHJvbWlzZSwgKCkgPT4ge1xuICAgICAgICBhYm9ydFJlcXVlc3QuX3Jlc29sdmUoKTtcbiAgICAgICAgV3JpdGFibGVTdHJlYW1SZWplY3RDbG9zZUFuZENsb3NlZFByb21pc2VJZk5lZWRlZChzdHJlYW0pO1xuICAgIH0sIChyZWFzb24pID0+IHtcbiAgICAgICAgYWJvcnRSZXF1ZXN0Ll9yZWplY3QocmVhc29uKTtcbiAgICAgICAgV3JpdGFibGVTdHJlYW1SZWplY3RDbG9zZUFuZENsb3NlZFByb21pc2VJZk5lZWRlZChzdHJlYW0pO1xuICAgIH0pO1xufVxuZnVuY3Rpb24gV3JpdGFibGVTdHJlYW1GaW5pc2hJbkZsaWdodFdyaXRlKHN0cmVhbSkge1xuICAgIHN0cmVhbS5faW5GbGlnaHRXcml0ZVJlcXVlc3QuX3Jlc29sdmUodW5kZWZpbmVkKTtcbiAgICBzdHJlYW0uX2luRmxpZ2h0V3JpdGVSZXF1ZXN0ID0gdW5kZWZpbmVkO1xufVxuZnVuY3Rpb24gV3JpdGFibGVTdHJlYW1GaW5pc2hJbkZsaWdodFdyaXRlV2l0aEVycm9yKHN0cmVhbSwgZXJyb3IpIHtcbiAgICBzdHJlYW0uX2luRmxpZ2h0V3JpdGVSZXF1ZXN0Ll9yZWplY3QoZXJyb3IpO1xuICAgIHN0cmVhbS5faW5GbGlnaHRXcml0ZVJlcXVlc3QgPSB1bmRlZmluZWQ7XG4gICAgV3JpdGFibGVTdHJlYW1EZWFsV2l0aFJlamVjdGlvbihzdHJlYW0sIGVycm9yKTtcbn1cbmZ1bmN0aW9uIFdyaXRhYmxlU3RyZWFtRmluaXNoSW5GbGlnaHRDbG9zZShzdHJlYW0pIHtcbiAgICBzdHJlYW0uX2luRmxpZ2h0Q2xvc2VSZXF1ZXN0Ll9yZXNvbHZlKHVuZGVmaW5lZCk7XG4gICAgc3RyZWFtLl9pbkZsaWdodENsb3NlUmVxdWVzdCA9IHVuZGVmaW5lZDtcbiAgICBjb25zdCBzdGF0ZSA9IHN0cmVhbS5fc3RhdGU7XG4gICAgaWYgKHN0YXRlID09PSAnZXJyb3JpbmcnKSB7XG4gICAgICAgIC8vIFRoZSBlcnJvciB3YXMgdG9vIGxhdGUgdG8gZG8gYW55dGhpbmcsIHNvIGl0IGlzIGlnbm9yZWQuXG4gICAgICAgIHN0cmVhbS5fc3RvcmVkRXJyb3IgPSB1bmRlZmluZWQ7XG4gICAgICAgIGlmIChzdHJlYW0uX3BlbmRpbmdBYm9ydFJlcXVlc3QgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgc3RyZWFtLl9wZW5kaW5nQWJvcnRSZXF1ZXN0Ll9yZXNvbHZlKCk7XG4gICAgICAgICAgICBzdHJlYW0uX3BlbmRpbmdBYm9ydFJlcXVlc3QgPSB1bmRlZmluZWQ7XG4gICAgICAgIH1cbiAgICB9XG4gICAgc3RyZWFtLl9zdGF0ZSA9ICdjbG9zZWQnO1xuICAgIGNvbnN0IHdyaXRlciA9IHN0cmVhbS5fd3JpdGVyO1xuICAgIGlmICh3cml0ZXIgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBkZWZhdWx0V3JpdGVyQ2xvc2VkUHJvbWlzZVJlc29sdmUod3JpdGVyKTtcbiAgICB9XG59XG5mdW5jdGlvbiBXcml0YWJsZVN0cmVhbUZpbmlzaEluRmxpZ2h0Q2xvc2VXaXRoRXJyb3Ioc3RyZWFtLCBlcnJvcikge1xuICAgIHN0cmVhbS5faW5GbGlnaHRDbG9zZVJlcXVlc3QuX3JlamVjdChlcnJvcik7XG4gICAgc3RyZWFtLl9pbkZsaWdodENsb3NlUmVxdWVzdCA9IHVuZGVmaW5lZDtcbiAgICAvLyBOZXZlciBleGVjdXRlIHNpbmsgYWJvcnQoKSBhZnRlciBzaW5rIGNsb3NlKCkuXG4gICAgaWYgKHN0cmVhbS5fcGVuZGluZ0Fib3J0UmVxdWVzdCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHN0cmVhbS5fcGVuZGluZ0Fib3J0UmVxdWVzdC5fcmVqZWN0KGVycm9yKTtcbiAgICAgICAgc3RyZWFtLl9wZW5kaW5nQWJvcnRSZXF1ZXN0ID0gdW5kZWZpbmVkO1xuICAgIH1cbiAgICBXcml0YWJsZVN0cmVhbURlYWxXaXRoUmVqZWN0aW9uKHN0cmVhbSwgZXJyb3IpO1xufVxuLy8gVE9ETyhyaWNlYSk6IEZpeCBhbHBoYWJldGljYWwgb3JkZXIuXG5mdW5jdGlvbiBXcml0YWJsZVN0cmVhbUNsb3NlUXVldWVkT3JJbkZsaWdodChzdHJlYW0pIHtcbiAgICBpZiAoc3RyZWFtLl9jbG9zZVJlcXVlc3QgPT09IHVuZGVmaW5lZCAmJiBzdHJlYW0uX2luRmxpZ2h0Q2xvc2VSZXF1ZXN0ID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICByZXR1cm4gdHJ1ZTtcbn1cbmZ1bmN0aW9uIFdyaXRhYmxlU3RyZWFtSGFzT3BlcmF0aW9uTWFya2VkSW5GbGlnaHQoc3RyZWFtKSB7XG4gICAgaWYgKHN0cmVhbS5faW5GbGlnaHRXcml0ZVJlcXVlc3QgPT09IHVuZGVmaW5lZCAmJiBzdHJlYW0uX2luRmxpZ2h0Q2xvc2VSZXF1ZXN0ID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICByZXR1cm4gdHJ1ZTtcbn1cbmZ1bmN0aW9uIFdyaXRhYmxlU3RyZWFtTWFya0Nsb3NlUmVxdWVzdEluRmxpZ2h0KHN0cmVhbSkge1xuICAgIHN0cmVhbS5faW5GbGlnaHRDbG9zZVJlcXVlc3QgPSBzdHJlYW0uX2Nsb3NlUmVxdWVzdDtcbiAgICBzdHJlYW0uX2Nsb3NlUmVxdWVzdCA9IHVuZGVmaW5lZDtcbn1cbmZ1bmN0aW9uIFdyaXRhYmxlU3RyZWFtTWFya0ZpcnN0V3JpdGVSZXF1ZXN0SW5GbGlnaHQoc3RyZWFtKSB7XG4gICAgc3RyZWFtLl9pbkZsaWdodFdyaXRlUmVxdWVzdCA9IHN0cmVhbS5fd3JpdGVSZXF1ZXN0cy5zaGlmdCgpO1xufVxuZnVuY3Rpb24gV3JpdGFibGVTdHJlYW1SZWplY3RDbG9zZUFuZENsb3NlZFByb21pc2VJZk5lZWRlZChzdHJlYW0pIHtcbiAgICBpZiAoc3RyZWFtLl9jbG9zZVJlcXVlc3QgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBzdHJlYW0uX2Nsb3NlUmVxdWVzdC5fcmVqZWN0KHN0cmVhbS5fc3RvcmVkRXJyb3IpO1xuICAgICAgICBzdHJlYW0uX2Nsb3NlUmVxdWVzdCA9IHVuZGVmaW5lZDtcbiAgICB9XG4gICAgY29uc3Qgd3JpdGVyID0gc3RyZWFtLl93cml0ZXI7XG4gICAgaWYgKHdyaXRlciAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGRlZmF1bHRXcml0ZXJDbG9zZWRQcm9taXNlUmVqZWN0KHdyaXRlciwgc3RyZWFtLl9zdG9yZWRFcnJvcik7XG4gICAgfVxufVxuZnVuY3Rpb24gV3JpdGFibGVTdHJlYW1VcGRhdGVCYWNrcHJlc3N1cmUoc3RyZWFtLCBiYWNrcHJlc3N1cmUpIHtcbiAgICBjb25zdCB3cml0ZXIgPSBzdHJlYW0uX3dyaXRlcjtcbiAgICBpZiAod3JpdGVyICE9PSB1bmRlZmluZWQgJiYgYmFja3ByZXNzdXJlICE9PSBzdHJlYW0uX2JhY2twcmVzc3VyZSkge1xuICAgICAgICBpZiAoYmFja3ByZXNzdXJlKSB7XG4gICAgICAgICAgICBkZWZhdWx0V3JpdGVyUmVhZHlQcm9taXNlUmVzZXQod3JpdGVyKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGRlZmF1bHRXcml0ZXJSZWFkeVByb21pc2VSZXNvbHZlKHdyaXRlcik7XG4gICAgICAgIH1cbiAgICB9XG4gICAgc3RyZWFtLl9iYWNrcHJlc3N1cmUgPSBiYWNrcHJlc3N1cmU7XG59XG4vKipcbiAqIEEgZGVmYXVsdCB3cml0ZXIgdmVuZGVkIGJ5IGEge0BsaW5rIFdyaXRhYmxlU3RyZWFtfS5cbiAqXG4gKiBAcHVibGljXG4gKi9cbmNsYXNzIFdyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlciB7XG4gICAgY29uc3RydWN0b3Ioc3RyZWFtKSB7XG4gICAgICAgIGFzc2VydFJlcXVpcmVkQXJndW1lbnQoc3RyZWFtLCAxLCAnV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyJyk7XG4gICAgICAgIGFzc2VydFdyaXRhYmxlU3RyZWFtKHN0cmVhbSwgJ0ZpcnN0IHBhcmFtZXRlcicpO1xuICAgICAgICBpZiAoSXNXcml0YWJsZVN0cmVhbUxvY2tlZChzdHJlYW0pKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdUaGlzIHN0cmVhbSBoYXMgYWxyZWFkeSBiZWVuIGxvY2tlZCBmb3IgZXhjbHVzaXZlIHdyaXRpbmcgYnkgYW5vdGhlciB3cml0ZXInKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLl9vd25lcldyaXRhYmxlU3RyZWFtID0gc3RyZWFtO1xuICAgICAgICBzdHJlYW0uX3dyaXRlciA9IHRoaXM7XG4gICAgICAgIGNvbnN0IHN0YXRlID0gc3RyZWFtLl9zdGF0ZTtcbiAgICAgICAgaWYgKHN0YXRlID09PSAnd3JpdGFibGUnKSB7XG4gICAgICAgICAgICBpZiAoIVdyaXRhYmxlU3RyZWFtQ2xvc2VRdWV1ZWRPckluRmxpZ2h0KHN0cmVhbSkgJiYgc3RyZWFtLl9iYWNrcHJlc3N1cmUpIHtcbiAgICAgICAgICAgICAgICBkZWZhdWx0V3JpdGVyUmVhZHlQcm9taXNlSW5pdGlhbGl6ZSh0aGlzKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgIGRlZmF1bHRXcml0ZXJSZWFkeVByb21pc2VJbml0aWFsaXplQXNSZXNvbHZlZCh0aGlzKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGRlZmF1bHRXcml0ZXJDbG9zZWRQcm9taXNlSW5pdGlhbGl6ZSh0aGlzKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmIChzdGF0ZSA9PT0gJ2Vycm9yaW5nJykge1xuICAgICAgICAgICAgZGVmYXVsdFdyaXRlclJlYWR5UHJvbWlzZUluaXRpYWxpemVBc1JlamVjdGVkKHRoaXMsIHN0cmVhbS5fc3RvcmVkRXJyb3IpO1xuICAgICAgICAgICAgZGVmYXVsdFdyaXRlckNsb3NlZFByb21pc2VJbml0aWFsaXplKHRoaXMpO1xuICAgICAgICB9XG4gICAgICAgIGVsc2UgaWYgKHN0YXRlID09PSAnY2xvc2VkJykge1xuICAgICAgICAgICAgZGVmYXVsdFdyaXRlclJlYWR5UHJvbWlzZUluaXRpYWxpemVBc1Jlc29sdmVkKHRoaXMpO1xuICAgICAgICAgICAgZGVmYXVsdFdyaXRlckNsb3NlZFByb21pc2VJbml0aWFsaXplQXNSZXNvbHZlZCh0aGlzKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGNvbnN0IHN0b3JlZEVycm9yID0gc3RyZWFtLl9zdG9yZWRFcnJvcjtcbiAgICAgICAgICAgIGRlZmF1bHRXcml0ZXJSZWFkeVByb21pc2VJbml0aWFsaXplQXNSZWplY3RlZCh0aGlzLCBzdG9yZWRFcnJvcik7XG4gICAgICAgICAgICBkZWZhdWx0V3JpdGVyQ2xvc2VkUHJvbWlzZUluaXRpYWxpemVBc1JlamVjdGVkKHRoaXMsIHN0b3JlZEVycm9yKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIGEgcHJvbWlzZSB0aGF0IHdpbGwgYmUgZnVsZmlsbGVkIHdoZW4gdGhlIHN0cmVhbSBiZWNvbWVzIGNsb3NlZCwgb3IgcmVqZWN0ZWQgaWYgdGhlIHN0cmVhbSBldmVyIGVycm9ycyBvclxuICAgICAqIHRoZSB3cml0ZXLigJlzIGxvY2sgaXMgcmVsZWFzZWQgYmVmb3JlIHRoZSBzdHJlYW0gZmluaXNoZXMgY2xvc2luZy5cbiAgICAgKi9cbiAgICBnZXQgY2xvc2VkKCkge1xuICAgICAgICBpZiAoIUlzV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyKHRoaXMpKSB7XG4gICAgICAgICAgICByZXR1cm4gcHJvbWlzZVJlamVjdGVkV2l0aChkZWZhdWx0V3JpdGVyQnJhbmRDaGVja0V4Y2VwdGlvbignY2xvc2VkJykpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLl9jbG9zZWRQcm9taXNlO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBkZXNpcmVkIHNpemUgdG8gZmlsbCB0aGUgc3RyZWFt4oCZcyBpbnRlcm5hbCBxdWV1ZS4gSXQgY2FuIGJlIG5lZ2F0aXZlLCBpZiB0aGUgcXVldWUgaXMgb3Zlci1mdWxsLlxuICAgICAqIEEgcHJvZHVjZXIgY2FuIHVzZSB0aGlzIGluZm9ybWF0aW9uIHRvIGRldGVybWluZSB0aGUgcmlnaHQgYW1vdW50IG9mIGRhdGEgdG8gd3JpdGUuXG4gICAgICpcbiAgICAgKiBJdCB3aWxsIGJlIGBudWxsYCBpZiB0aGUgc3RyZWFtIGNhbm5vdCBiZSBzdWNjZXNzZnVsbHkgd3JpdHRlbiB0byAoZHVlIHRvIGVpdGhlciBiZWluZyBlcnJvcmVkLCBvciBoYXZpbmcgYW4gYWJvcnRcbiAgICAgKiBxdWV1ZWQgdXApLiBJdCB3aWxsIHJldHVybiB6ZXJvIGlmIHRoZSBzdHJlYW0gaXMgY2xvc2VkLiBBbmQgdGhlIGdldHRlciB3aWxsIHRocm93IGFuIGV4Y2VwdGlvbiBpZiBpbnZva2VkIHdoZW5cbiAgICAgKiB0aGUgd3JpdGVy4oCZcyBsb2NrIGlzIHJlbGVhc2VkLlxuICAgICAqL1xuICAgIGdldCBkZXNpcmVkU2l6ZSgpIHtcbiAgICAgICAgaWYgKCFJc1dyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlcih0aGlzKSkge1xuICAgICAgICAgICAgdGhyb3cgZGVmYXVsdFdyaXRlckJyYW5kQ2hlY2tFeGNlcHRpb24oJ2Rlc2lyZWRTaXplJyk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMuX293bmVyV3JpdGFibGVTdHJlYW0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgdGhyb3cgZGVmYXVsdFdyaXRlckxvY2tFeGNlcHRpb24oJ2Rlc2lyZWRTaXplJyk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIFdyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlckdldERlc2lyZWRTaXplKHRoaXMpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIGEgcHJvbWlzZSB0aGF0IHdpbGwgYmUgZnVsZmlsbGVkIHdoZW4gdGhlIGRlc2lyZWQgc2l6ZSB0byBmaWxsIHRoZSBzdHJlYW3igJlzIGludGVybmFsIHF1ZXVlIHRyYW5zaXRpb25zXG4gICAgICogZnJvbSBub24tcG9zaXRpdmUgdG8gcG9zaXRpdmUsIHNpZ25hbGluZyB0aGF0IGl0IGlzIG5vIGxvbmdlciBhcHBseWluZyBiYWNrcHJlc3N1cmUuIE9uY2UgdGhlIGRlc2lyZWQgc2l6ZSBkaXBzXG4gICAgICogYmFjayB0byB6ZXJvIG9yIGJlbG93LCB0aGUgZ2V0dGVyIHdpbGwgcmV0dXJuIGEgbmV3IHByb21pc2UgdGhhdCBzdGF5cyBwZW5kaW5nIHVudGlsIHRoZSBuZXh0IHRyYW5zaXRpb24uXG4gICAgICpcbiAgICAgKiBJZiB0aGUgc3RyZWFtIGJlY29tZXMgZXJyb3JlZCBvciBhYm9ydGVkLCBvciB0aGUgd3JpdGVy4oCZcyBsb2NrIGlzIHJlbGVhc2VkLCB0aGUgcmV0dXJuZWQgcHJvbWlzZSB3aWxsIGJlY29tZVxuICAgICAqIHJlamVjdGVkLlxuICAgICAqL1xuICAgIGdldCByZWFkeSgpIHtcbiAgICAgICAgaWYgKCFJc1dyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlcih0aGlzKSkge1xuICAgICAgICAgICAgcmV0dXJuIHByb21pc2VSZWplY3RlZFdpdGgoZGVmYXVsdFdyaXRlckJyYW5kQ2hlY2tFeGNlcHRpb24oJ3JlYWR5JykpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLl9yZWFkeVByb21pc2U7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIElmIHRoZSByZWFkZXIgaXMgYWN0aXZlLCBiZWhhdmVzIHRoZSBzYW1lIGFzIHtAbGluayBXcml0YWJsZVN0cmVhbS5hYm9ydCB8IHN0cmVhbS5hYm9ydChyZWFzb24pfS5cbiAgICAgKi9cbiAgICBhYm9ydChyZWFzb24gPSB1bmRlZmluZWQpIHtcbiAgICAgICAgaWYgKCFJc1dyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlcih0aGlzKSkge1xuICAgICAgICAgICAgcmV0dXJuIHByb21pc2VSZWplY3RlZFdpdGgoZGVmYXVsdFdyaXRlckJyYW5kQ2hlY2tFeGNlcHRpb24oJ2Fib3J0JykpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLl9vd25lcldyaXRhYmxlU3RyZWFtID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHJldHVybiBwcm9taXNlUmVqZWN0ZWRXaXRoKGRlZmF1bHRXcml0ZXJMb2NrRXhjZXB0aW9uKCdhYm9ydCcpKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyQWJvcnQodGhpcywgcmVhc29uKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogSWYgdGhlIHJlYWRlciBpcyBhY3RpdmUsIGJlaGF2ZXMgdGhlIHNhbWUgYXMge0BsaW5rIFdyaXRhYmxlU3RyZWFtLmNsb3NlIHwgc3RyZWFtLmNsb3NlKCl9LlxuICAgICAqL1xuICAgIGNsb3NlKCkge1xuICAgICAgICBpZiAoIUlzV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyKHRoaXMpKSB7XG4gICAgICAgICAgICByZXR1cm4gcHJvbWlzZVJlamVjdGVkV2l0aChkZWZhdWx0V3JpdGVyQnJhbmRDaGVja0V4Y2VwdGlvbignY2xvc2UnKSk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3Qgc3RyZWFtID0gdGhpcy5fb3duZXJXcml0YWJsZVN0cmVhbTtcbiAgICAgICAgaWYgKHN0cmVhbSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICByZXR1cm4gcHJvbWlzZVJlamVjdGVkV2l0aChkZWZhdWx0V3JpdGVyTG9ja0V4Y2VwdGlvbignY2xvc2UnKSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKFdyaXRhYmxlU3RyZWFtQ2xvc2VRdWV1ZWRPckluRmxpZ2h0KHN0cmVhbSkpIHtcbiAgICAgICAgICAgIHJldHVybiBwcm9taXNlUmVqZWN0ZWRXaXRoKG5ldyBUeXBlRXJyb3IoJ0Nhbm5vdCBjbG9zZSBhbiBhbHJlYWR5LWNsb3Npbmcgc3RyZWFtJykpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBXcml0YWJsZVN0cmVhbURlZmF1bHRXcml0ZXJDbG9zZSh0aGlzKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVsZWFzZXMgdGhlIHdyaXRlcuKAmXMgbG9jayBvbiB0aGUgY29ycmVzcG9uZGluZyBzdHJlYW0uIEFmdGVyIHRoZSBsb2NrIGlzIHJlbGVhc2VkLCB0aGUgd3JpdGVyIGlzIG5vIGxvbmdlciBhY3RpdmUuXG4gICAgICogSWYgdGhlIGFzc29jaWF0ZWQgc3RyZWFtIGlzIGVycm9yZWQgd2hlbiB0aGUgbG9jayBpcyByZWxlYXNlZCwgdGhlIHdyaXRlciB3aWxsIGFwcGVhciBlcnJvcmVkIGluIHRoZSBzYW1lIHdheSBmcm9tXG4gICAgICogbm93IG9uOyBvdGhlcndpc2UsIHRoZSB3cml0ZXIgd2lsbCBhcHBlYXIgY2xvc2VkLlxuICAgICAqXG4gICAgICogTm90ZSB0aGF0IHRoZSBsb2NrIGNhbiBzdGlsbCBiZSByZWxlYXNlZCBldmVuIGlmIHNvbWUgb25nb2luZyB3cml0ZXMgaGF2ZSBub3QgeWV0IGZpbmlzaGVkIChpLmUuIGV2ZW4gaWYgdGhlXG4gICAgICogcHJvbWlzZXMgcmV0dXJuZWQgZnJvbSBwcmV2aW91cyBjYWxscyB0byB7QGxpbmsgV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyLndyaXRlIHwgd3JpdGUoKX0gaGF2ZSBub3QgeWV0IHNldHRsZWQpLlxuICAgICAqIEl04oCZcyBub3QgbmVjZXNzYXJ5IHRvIGhvbGQgdGhlIGxvY2sgb24gdGhlIHdyaXRlciBmb3IgdGhlIGR1cmF0aW9uIG9mIHRoZSB3cml0ZTsgdGhlIGxvY2sgaW5zdGVhZCBzaW1wbHkgcHJldmVudHNcbiAgICAgKiBvdGhlciBwcm9kdWNlcnMgZnJvbSB3cml0aW5nIGluIGFuIGludGVybGVhdmVkIG1hbm5lci5cbiAgICAgKi9cbiAgICByZWxlYXNlTG9jaygpIHtcbiAgICAgICAgaWYgKCFJc1dyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlcih0aGlzKSkge1xuICAgICAgICAgICAgdGhyb3cgZGVmYXVsdFdyaXRlckJyYW5kQ2hlY2tFeGNlcHRpb24oJ3JlbGVhc2VMb2NrJyk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3Qgc3RyZWFtID0gdGhpcy5fb3duZXJXcml0YWJsZVN0cmVhbTtcbiAgICAgICAgaWYgKHN0cmVhbSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyUmVsZWFzZSh0aGlzKTtcbiAgICB9XG4gICAgd3JpdGUoY2h1bmsgPSB1bmRlZmluZWQpIHtcbiAgICAgICAgaWYgKCFJc1dyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlcih0aGlzKSkge1xuICAgICAgICAgICAgcmV0dXJuIHByb21pc2VSZWplY3RlZFdpdGgoZGVmYXVsdFdyaXRlckJyYW5kQ2hlY2tFeGNlcHRpb24oJ3dyaXRlJykpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLl9vd25lcldyaXRhYmxlU3RyZWFtID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHJldHVybiBwcm9taXNlUmVqZWN0ZWRXaXRoKGRlZmF1bHRXcml0ZXJMb2NrRXhjZXB0aW9uKCd3cml0ZSB0bycpKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyV3JpdGUodGhpcywgY2h1bmspO1xuICAgIH1cbn1cbk9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKFdyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlci5wcm90b3R5cGUsIHtcbiAgICBhYm9ydDogeyBlbnVtZXJhYmxlOiB0cnVlIH0sXG4gICAgY2xvc2U6IHsgZW51bWVyYWJsZTogdHJ1ZSB9LFxuICAgIHJlbGVhc2VMb2NrOiB7IGVudW1lcmFibGU6IHRydWUgfSxcbiAgICB3cml0ZTogeyBlbnVtZXJhYmxlOiB0cnVlIH0sXG4gICAgY2xvc2VkOiB7IGVudW1lcmFibGU6IHRydWUgfSxcbiAgICBkZXNpcmVkU2l6ZTogeyBlbnVtZXJhYmxlOiB0cnVlIH0sXG4gICAgcmVhZHk6IHsgZW51bWVyYWJsZTogdHJ1ZSB9XG59KTtcbmlmICh0eXBlb2YgU3ltYm9sUG9seWZpbGwudG9TdHJpbmdUYWcgPT09ICdzeW1ib2wnKSB7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KFdyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlci5wcm90b3R5cGUsIFN5bWJvbFBvbHlmaWxsLnRvU3RyaW5nVGFnLCB7XG4gICAgICAgIHZhbHVlOiAnV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyJyxcbiAgICAgICAgY29uZmlndXJhYmxlOiB0cnVlXG4gICAgfSk7XG59XG4vLyBBYnN0cmFjdCBvcGVyYXRpb25zIGZvciB0aGUgV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyLlxuZnVuY3Rpb24gSXNXcml0YWJsZVN0cmVhbURlZmF1bHRXcml0ZXIoeCkge1xuICAgIGlmICghdHlwZUlzT2JqZWN0KHgpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgaWYgKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoeCwgJ19vd25lcldyaXRhYmxlU3RyZWFtJykpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICByZXR1cm4gdHJ1ZTtcbn1cbi8vIEEgY2xpZW50IG9mIFdyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlciBtYXkgdXNlIHRoZXNlIGZ1bmN0aW9ucyBkaXJlY3RseSB0byBieXBhc3Mgc3RhdGUgY2hlY2suXG5mdW5jdGlvbiBXcml0YWJsZVN0cmVhbURlZmF1bHRXcml0ZXJBYm9ydCh3cml0ZXIsIHJlYXNvbikge1xuICAgIGNvbnN0IHN0cmVhbSA9IHdyaXRlci5fb3duZXJXcml0YWJsZVN0cmVhbTtcbiAgICByZXR1cm4gV3JpdGFibGVTdHJlYW1BYm9ydChzdHJlYW0sIHJlYXNvbik7XG59XG5mdW5jdGlvbiBXcml0YWJsZVN0cmVhbURlZmF1bHRXcml0ZXJDbG9zZSh3cml0ZXIpIHtcbiAgICBjb25zdCBzdHJlYW0gPSB3cml0ZXIuX293bmVyV3JpdGFibGVTdHJlYW07XG4gICAgcmV0dXJuIFdyaXRhYmxlU3RyZWFtQ2xvc2Uoc3RyZWFtKTtcbn1cbmZ1bmN0aW9uIFdyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlckNsb3NlV2l0aEVycm9yUHJvcGFnYXRpb24od3JpdGVyKSB7XG4gICAgY29uc3Qgc3RyZWFtID0gd3JpdGVyLl9vd25lcldyaXRhYmxlU3RyZWFtO1xuICAgIGNvbnN0IHN0YXRlID0gc3RyZWFtLl9zdGF0ZTtcbiAgICBpZiAoV3JpdGFibGVTdHJlYW1DbG9zZVF1ZXVlZE9ySW5GbGlnaHQoc3RyZWFtKSB8fCBzdGF0ZSA9PT0gJ2Nsb3NlZCcpIHtcbiAgICAgICAgcmV0dXJuIHByb21pc2VSZXNvbHZlZFdpdGgodW5kZWZpbmVkKTtcbiAgICB9XG4gICAgaWYgKHN0YXRlID09PSAnZXJyb3JlZCcpIHtcbiAgICAgICAgcmV0dXJuIHByb21pc2VSZWplY3RlZFdpdGgoc3RyZWFtLl9zdG9yZWRFcnJvcik7XG4gICAgfVxuICAgIHJldHVybiBXcml0YWJsZVN0cmVhbURlZmF1bHRXcml0ZXJDbG9zZSh3cml0ZXIpO1xufVxuZnVuY3Rpb24gV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyRW5zdXJlQ2xvc2VkUHJvbWlzZVJlamVjdGVkKHdyaXRlciwgZXJyb3IpIHtcbiAgICBpZiAod3JpdGVyLl9jbG9zZWRQcm9taXNlU3RhdGUgPT09ICdwZW5kaW5nJykge1xuICAgICAgICBkZWZhdWx0V3JpdGVyQ2xvc2VkUHJvbWlzZVJlamVjdCh3cml0ZXIsIGVycm9yKTtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIGRlZmF1bHRXcml0ZXJDbG9zZWRQcm9taXNlUmVzZXRUb1JlamVjdGVkKHdyaXRlciwgZXJyb3IpO1xuICAgIH1cbn1cbmZ1bmN0aW9uIFdyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlckVuc3VyZVJlYWR5UHJvbWlzZVJlamVjdGVkKHdyaXRlciwgZXJyb3IpIHtcbiAgICBpZiAod3JpdGVyLl9yZWFkeVByb21pc2VTdGF0ZSA9PT0gJ3BlbmRpbmcnKSB7XG4gICAgICAgIGRlZmF1bHRXcml0ZXJSZWFkeVByb21pc2VSZWplY3Qod3JpdGVyLCBlcnJvcik7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBkZWZhdWx0V3JpdGVyUmVhZHlQcm9taXNlUmVzZXRUb1JlamVjdGVkKHdyaXRlciwgZXJyb3IpO1xuICAgIH1cbn1cbmZ1bmN0aW9uIFdyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlckdldERlc2lyZWRTaXplKHdyaXRlcikge1xuICAgIGNvbnN0IHN0cmVhbSA9IHdyaXRlci5fb3duZXJXcml0YWJsZVN0cmVhbTtcbiAgICBjb25zdCBzdGF0ZSA9IHN0cmVhbS5fc3RhdGU7XG4gICAgaWYgKHN0YXRlID09PSAnZXJyb3JlZCcgfHwgc3RhdGUgPT09ICdlcnJvcmluZycpIHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIGlmIChzdGF0ZSA9PT0gJ2Nsb3NlZCcpIHtcbiAgICAgICAgcmV0dXJuIDA7XG4gICAgfVxuICAgIHJldHVybiBXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyR2V0RGVzaXJlZFNpemUoc3RyZWFtLl93cml0YWJsZVN0cmVhbUNvbnRyb2xsZXIpO1xufVxuZnVuY3Rpb24gV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyUmVsZWFzZSh3cml0ZXIpIHtcbiAgICBjb25zdCBzdHJlYW0gPSB3cml0ZXIuX293bmVyV3JpdGFibGVTdHJlYW07XG4gICAgY29uc3QgcmVsZWFzZWRFcnJvciA9IG5ldyBUeXBlRXJyb3IoYFdyaXRlciB3YXMgcmVsZWFzZWQgYW5kIGNhbiBubyBsb25nZXIgYmUgdXNlZCB0byBtb25pdG9yIHRoZSBzdHJlYW0ncyBjbG9zZWRuZXNzYCk7XG4gICAgV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyRW5zdXJlUmVhZHlQcm9taXNlUmVqZWN0ZWQod3JpdGVyLCByZWxlYXNlZEVycm9yKTtcbiAgICAvLyBUaGUgc3RhdGUgdHJhbnNpdGlvbnMgdG8gXCJlcnJvcmVkXCIgYmVmb3JlIHRoZSBzaW5rIGFib3J0KCkgbWV0aG9kIHJ1bnMsIGJ1dCB0aGUgd3JpdGVyLmNsb3NlZCBwcm9taXNlIGlzIG5vdFxuICAgIC8vIHJlamVjdGVkIHVudGlsIGFmdGVyd2FyZHMuIFRoaXMgbWVhbnMgdGhhdCBzaW1wbHkgdGVzdGluZyBzdGF0ZSB3aWxsIG5vdCB3b3JrLlxuICAgIFdyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlckVuc3VyZUNsb3NlZFByb21pc2VSZWplY3RlZCh3cml0ZXIsIHJlbGVhc2VkRXJyb3IpO1xuICAgIHN0cmVhbS5fd3JpdGVyID0gdW5kZWZpbmVkO1xuICAgIHdyaXRlci5fb3duZXJXcml0YWJsZVN0cmVhbSA9IHVuZGVmaW5lZDtcbn1cbmZ1bmN0aW9uIFdyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlcldyaXRlKHdyaXRlciwgY2h1bmspIHtcbiAgICBjb25zdCBzdHJlYW0gPSB3cml0ZXIuX293bmVyV3JpdGFibGVTdHJlYW07XG4gICAgY29uc3QgY29udHJvbGxlciA9IHN0cmVhbS5fd3JpdGFibGVTdHJlYW1Db250cm9sbGVyO1xuICAgIGNvbnN0IGNodW5rU2l6ZSA9IFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJHZXRDaHVua1NpemUoY29udHJvbGxlciwgY2h1bmspO1xuICAgIGlmIChzdHJlYW0gIT09IHdyaXRlci5fb3duZXJXcml0YWJsZVN0cmVhbSkge1xuICAgICAgICByZXR1cm4gcHJvbWlzZVJlamVjdGVkV2l0aChkZWZhdWx0V3JpdGVyTG9ja0V4Y2VwdGlvbignd3JpdGUgdG8nKSk7XG4gICAgfVxuICAgIGNvbnN0IHN0YXRlID0gc3RyZWFtLl9zdGF0ZTtcbiAgICBpZiAoc3RhdGUgPT09ICdlcnJvcmVkJykge1xuICAgICAgICByZXR1cm4gcHJvbWlzZVJlamVjdGVkV2l0aChzdHJlYW0uX3N0b3JlZEVycm9yKTtcbiAgICB9XG4gICAgaWYgKFdyaXRhYmxlU3RyZWFtQ2xvc2VRdWV1ZWRPckluRmxpZ2h0KHN0cmVhbSkgfHwgc3RhdGUgPT09ICdjbG9zZWQnKSB7XG4gICAgICAgIHJldHVybiBwcm9taXNlUmVqZWN0ZWRXaXRoKG5ldyBUeXBlRXJyb3IoJ1RoZSBzdHJlYW0gaXMgY2xvc2luZyBvciBjbG9zZWQgYW5kIGNhbm5vdCBiZSB3cml0dGVuIHRvJykpO1xuICAgIH1cbiAgICBpZiAoc3RhdGUgPT09ICdlcnJvcmluZycpIHtcbiAgICAgICAgcmV0dXJuIHByb21pc2VSZWplY3RlZFdpdGgoc3RyZWFtLl9zdG9yZWRFcnJvcik7XG4gICAgfVxuICAgIGNvbnN0IHByb21pc2UgPSBXcml0YWJsZVN0cmVhbUFkZFdyaXRlUmVxdWVzdChzdHJlYW0pO1xuICAgIFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJXcml0ZShjb250cm9sbGVyLCBjaHVuaywgY2h1bmtTaXplKTtcbiAgICByZXR1cm4gcHJvbWlzZTtcbn1cbmNvbnN0IGNsb3NlU2VudGluZWwgPSB7fTtcbi8qKlxuICogQWxsb3dzIGNvbnRyb2wgb2YgYSB7QGxpbmsgV3JpdGFibGVTdHJlYW0gfCB3cml0YWJsZSBzdHJlYW19J3Mgc3RhdGUgYW5kIGludGVybmFsIHF1ZXVlLlxuICpcbiAqIEBwdWJsaWNcbiAqL1xuY2xhc3MgV3JpdGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlciB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0lsbGVnYWwgY29uc3RydWN0b3InKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQ2xvc2VzIHRoZSBjb250cm9sbGVkIHdyaXRhYmxlIHN0cmVhbSwgbWFraW5nIGFsbCBmdXR1cmUgaW50ZXJhY3Rpb25zIHdpdGggaXQgZmFpbCB3aXRoIHRoZSBnaXZlbiBlcnJvciBgZWAuXG4gICAgICpcbiAgICAgKiBUaGlzIG1ldGhvZCBpcyByYXJlbHkgdXNlZCwgc2luY2UgdXN1YWxseSBpdCBzdWZmaWNlcyB0byByZXR1cm4gYSByZWplY3RlZCBwcm9taXNlIGZyb20gb25lIG9mIHRoZSB1bmRlcmx5aW5nXG4gICAgICogc2luaydzIG1ldGhvZHMuIEhvd2V2ZXIsIGl0IGNhbiBiZSB1c2VmdWwgZm9yIHN1ZGRlbmx5IHNodXR0aW5nIGRvd24gYSBzdHJlYW0gaW4gcmVzcG9uc2UgdG8gYW4gZXZlbnQgb3V0c2lkZSB0aGVcbiAgICAgKiBub3JtYWwgbGlmZWN5Y2xlIG9mIGludGVyYWN0aW9ucyB3aXRoIHRoZSB1bmRlcmx5aW5nIHNpbmsuXG4gICAgICovXG4gICAgZXJyb3IoZSA9IHVuZGVmaW5lZCkge1xuICAgICAgICBpZiAoIUlzV3JpdGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlcih0aGlzKSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignV3JpdGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlci5wcm90b3R5cGUuZXJyb3IgY2FuIG9ubHkgYmUgdXNlZCBvbiBhIFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXInKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBzdGF0ZSA9IHRoaXMuX2NvbnRyb2xsZWRXcml0YWJsZVN0cmVhbS5fc3RhdGU7XG4gICAgICAgIGlmIChzdGF0ZSAhPT0gJ3dyaXRhYmxlJykge1xuICAgICAgICAgICAgLy8gVGhlIHN0cmVhbSBpcyBjbG9zZWQsIGVycm9yZWQgb3Igd2lsbCBiZSBzb29uLiBUaGUgc2luayBjYW4ndCBkbyBhbnl0aGluZyB1c2VmdWwgaWYgaXQgZ2V0cyBhbiBlcnJvciBoZXJlLCBzb1xuICAgICAgICAgICAgLy8ganVzdCB0cmVhdCBpdCBhcyBhIG5vLW9wLlxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJFcnJvcih0aGlzLCBlKTtcbiAgICB9XG4gICAgLyoqIEBpbnRlcm5hbCAqL1xuICAgIFtBYm9ydFN0ZXBzXShyZWFzb24pIHtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gdGhpcy5fYWJvcnRBbGdvcml0aG0ocmVhc29uKTtcbiAgICAgICAgV3JpdGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckNsZWFyQWxnb3JpdGhtcyh0aGlzKTtcbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG4gICAgLyoqIEBpbnRlcm5hbCAqL1xuICAgIFtFcnJvclN0ZXBzXSgpIHtcbiAgICAgICAgUmVzZXRRdWV1ZSh0aGlzKTtcbiAgICB9XG59XG5PYmplY3QuZGVmaW5lUHJvcGVydGllcyhXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyLnByb3RvdHlwZSwge1xuICAgIGVycm9yOiB7IGVudW1lcmFibGU6IHRydWUgfVxufSk7XG5pZiAodHlwZW9mIFN5bWJvbFBvbHlmaWxsLnRvU3RyaW5nVGFnID09PSAnc3ltYm9sJykge1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyLnByb3RvdHlwZSwgU3ltYm9sUG9seWZpbGwudG9TdHJpbmdUYWcsIHtcbiAgICAgICAgdmFsdWU6ICdXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyJyxcbiAgICAgICAgY29uZmlndXJhYmxlOiB0cnVlXG4gICAgfSk7XG59XG4vLyBBYnN0cmFjdCBvcGVyYXRpb25zIGltcGxlbWVudGluZyBpbnRlcmZhY2UgcmVxdWlyZWQgYnkgdGhlIFdyaXRhYmxlU3RyZWFtLlxuZnVuY3Rpb24gSXNXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyKHgpIHtcbiAgICBpZiAoIXR5cGVJc09iamVjdCh4KSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGlmICghT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHgsICdfY29udHJvbGxlZFdyaXRhYmxlU3RyZWFtJykpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICByZXR1cm4gdHJ1ZTtcbn1cbmZ1bmN0aW9uIFNldFVwV3JpdGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlcihzdHJlYW0sIGNvbnRyb2xsZXIsIHN0YXJ0QWxnb3JpdGhtLCB3cml0ZUFsZ29yaXRobSwgY2xvc2VBbGdvcml0aG0sIGFib3J0QWxnb3JpdGhtLCBoaWdoV2F0ZXJNYXJrLCBzaXplQWxnb3JpdGhtKSB7XG4gICAgY29udHJvbGxlci5fY29udHJvbGxlZFdyaXRhYmxlU3RyZWFtID0gc3RyZWFtO1xuICAgIHN0cmVhbS5fd3JpdGFibGVTdHJlYW1Db250cm9sbGVyID0gY29udHJvbGxlcjtcbiAgICAvLyBOZWVkIHRvIHNldCB0aGUgc2xvdHMgc28gdGhhdCB0aGUgYXNzZXJ0IGRvZXNuJ3QgZmlyZS4gSW4gdGhlIHNwZWMgdGhlIHNsb3RzIGFscmVhZHkgZXhpc3QgaW1wbGljaXRseS5cbiAgICBjb250cm9sbGVyLl9xdWV1ZSA9IHVuZGVmaW5lZDtcbiAgICBjb250cm9sbGVyLl9xdWV1ZVRvdGFsU2l6ZSA9IHVuZGVmaW5lZDtcbiAgICBSZXNldFF1ZXVlKGNvbnRyb2xsZXIpO1xuICAgIGNvbnRyb2xsZXIuX3N0YXJ0ZWQgPSBmYWxzZTtcbiAgICBjb250cm9sbGVyLl9zdHJhdGVneVNpemVBbGdvcml0aG0gPSBzaXplQWxnb3JpdGhtO1xuICAgIGNvbnRyb2xsZXIuX3N0cmF0ZWd5SFdNID0gaGlnaFdhdGVyTWFyaztcbiAgICBjb250cm9sbGVyLl93cml0ZUFsZ29yaXRobSA9IHdyaXRlQWxnb3JpdGhtO1xuICAgIGNvbnRyb2xsZXIuX2Nsb3NlQWxnb3JpdGhtID0gY2xvc2VBbGdvcml0aG07XG4gICAgY29udHJvbGxlci5fYWJvcnRBbGdvcml0aG0gPSBhYm9ydEFsZ29yaXRobTtcbiAgICBjb25zdCBiYWNrcHJlc3N1cmUgPSBXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyR2V0QmFja3ByZXNzdXJlKGNvbnRyb2xsZXIpO1xuICAgIFdyaXRhYmxlU3RyZWFtVXBkYXRlQmFja3ByZXNzdXJlKHN0cmVhbSwgYmFja3ByZXNzdXJlKTtcbiAgICBjb25zdCBzdGFydFJlc3VsdCA9IHN0YXJ0QWxnb3JpdGhtKCk7XG4gICAgY29uc3Qgc3RhcnRQcm9taXNlID0gcHJvbWlzZVJlc29sdmVkV2l0aChzdGFydFJlc3VsdCk7XG4gICAgdXBvblByb21pc2Uoc3RhcnRQcm9taXNlLCAoKSA9PiB7XG4gICAgICAgIGNvbnRyb2xsZXIuX3N0YXJ0ZWQgPSB0cnVlO1xuICAgICAgICBXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyQWR2YW5jZVF1ZXVlSWZOZWVkZWQoY29udHJvbGxlcik7XG4gICAgfSwgciA9PiB7XG4gICAgICAgIGNvbnRyb2xsZXIuX3N0YXJ0ZWQgPSB0cnVlO1xuICAgICAgICBXcml0YWJsZVN0cmVhbURlYWxXaXRoUmVqZWN0aW9uKHN0cmVhbSwgcik7XG4gICAgfSk7XG59XG5mdW5jdGlvbiBTZXRVcFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJGcm9tVW5kZXJseWluZ1Npbmsoc3RyZWFtLCB1bmRlcmx5aW5nU2luaywgaGlnaFdhdGVyTWFyaywgc2l6ZUFsZ29yaXRobSkge1xuICAgIGNvbnN0IGNvbnRyb2xsZXIgPSBPYmplY3QuY3JlYXRlKFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXIucHJvdG90eXBlKTtcbiAgICBsZXQgc3RhcnRBbGdvcml0aG0gPSAoKSA9PiB1bmRlZmluZWQ7XG4gICAgbGV0IHdyaXRlQWxnb3JpdGhtID0gKCkgPT4gcHJvbWlzZVJlc29sdmVkV2l0aCh1bmRlZmluZWQpO1xuICAgIGxldCBjbG9zZUFsZ29yaXRobSA9ICgpID0+IHByb21pc2VSZXNvbHZlZFdpdGgodW5kZWZpbmVkKTtcbiAgICBsZXQgYWJvcnRBbGdvcml0aG0gPSAoKSA9PiBwcm9taXNlUmVzb2x2ZWRXaXRoKHVuZGVmaW5lZCk7XG4gICAgaWYgKHVuZGVybHlpbmdTaW5rLnN0YXJ0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgc3RhcnRBbGdvcml0aG0gPSAoKSA9PiB1bmRlcmx5aW5nU2luay5zdGFydChjb250cm9sbGVyKTtcbiAgICB9XG4gICAgaWYgKHVuZGVybHlpbmdTaW5rLndyaXRlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgd3JpdGVBbGdvcml0aG0gPSBjaHVuayA9PiB1bmRlcmx5aW5nU2luay53cml0ZShjaHVuaywgY29udHJvbGxlcik7XG4gICAgfVxuICAgIGlmICh1bmRlcmx5aW5nU2luay5jbG9zZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGNsb3NlQWxnb3JpdGhtID0gKCkgPT4gdW5kZXJseWluZ1NpbmsuY2xvc2UoKTtcbiAgICB9XG4gICAgaWYgKHVuZGVybHlpbmdTaW5rLmFib3J0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgYWJvcnRBbGdvcml0aG0gPSByZWFzb24gPT4gdW5kZXJseWluZ1NpbmsuYWJvcnQocmVhc29uKTtcbiAgICB9XG4gICAgU2V0VXBXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyKHN0cmVhbSwgY29udHJvbGxlciwgc3RhcnRBbGdvcml0aG0sIHdyaXRlQWxnb3JpdGhtLCBjbG9zZUFsZ29yaXRobSwgYWJvcnRBbGdvcml0aG0sIGhpZ2hXYXRlck1hcmssIHNpemVBbGdvcml0aG0pO1xufVxuLy8gQ2xlYXJBbGdvcml0aG1zIG1heSBiZSBjYWxsZWQgdHdpY2UuIEVycm9yaW5nIHRoZSBzYW1lIHN0cmVhbSBpbiBtdWx0aXBsZSB3YXlzIHdpbGwgb2Z0ZW4gcmVzdWx0IGluIHJlZHVuZGFudCBjYWxscy5cbmZ1bmN0aW9uIFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJDbGVhckFsZ29yaXRobXMoY29udHJvbGxlcikge1xuICAgIGNvbnRyb2xsZXIuX3dyaXRlQWxnb3JpdGhtID0gdW5kZWZpbmVkO1xuICAgIGNvbnRyb2xsZXIuX2Nsb3NlQWxnb3JpdGhtID0gdW5kZWZpbmVkO1xuICAgIGNvbnRyb2xsZXIuX2Fib3J0QWxnb3JpdGhtID0gdW5kZWZpbmVkO1xuICAgIGNvbnRyb2xsZXIuX3N0cmF0ZWd5U2l6ZUFsZ29yaXRobSA9IHVuZGVmaW5lZDtcbn1cbmZ1bmN0aW9uIFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJDbG9zZShjb250cm9sbGVyKSB7XG4gICAgRW5xdWV1ZVZhbHVlV2l0aFNpemUoY29udHJvbGxlciwgY2xvc2VTZW50aW5lbCwgMCk7XG4gICAgV3JpdGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckFkdmFuY2VRdWV1ZUlmTmVlZGVkKGNvbnRyb2xsZXIpO1xufVxuZnVuY3Rpb24gV3JpdGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckdldENodW5rU2l6ZShjb250cm9sbGVyLCBjaHVuaykge1xuICAgIHRyeSB7XG4gICAgICAgIHJldHVybiBjb250cm9sbGVyLl9zdHJhdGVneVNpemVBbGdvcml0aG0oY2h1bmspO1xuICAgIH1cbiAgICBjYXRjaCAoY2h1bmtTaXplRSkge1xuICAgICAgICBXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyRXJyb3JJZk5lZWRlZChjb250cm9sbGVyLCBjaHVua1NpemVFKTtcbiAgICAgICAgcmV0dXJuIDE7XG4gICAgfVxufVxuZnVuY3Rpb24gV3JpdGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckdldERlc2lyZWRTaXplKGNvbnRyb2xsZXIpIHtcbiAgICByZXR1cm4gY29udHJvbGxlci5fc3RyYXRlZ3lIV00gLSBjb250cm9sbGVyLl9xdWV1ZVRvdGFsU2l6ZTtcbn1cbmZ1bmN0aW9uIFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJXcml0ZShjb250cm9sbGVyLCBjaHVuaywgY2h1bmtTaXplKSB7XG4gICAgdHJ5IHtcbiAgICAgICAgRW5xdWV1ZVZhbHVlV2l0aFNpemUoY29udHJvbGxlciwgY2h1bmssIGNodW5rU2l6ZSk7XG4gICAgfVxuICAgIGNhdGNoIChlbnF1ZXVlRSkge1xuICAgICAgICBXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyRXJyb3JJZk5lZWRlZChjb250cm9sbGVyLCBlbnF1ZXVlRSk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3Qgc3RyZWFtID0gY29udHJvbGxlci5fY29udHJvbGxlZFdyaXRhYmxlU3RyZWFtO1xuICAgIGlmICghV3JpdGFibGVTdHJlYW1DbG9zZVF1ZXVlZE9ySW5GbGlnaHQoc3RyZWFtKSAmJiBzdHJlYW0uX3N0YXRlID09PSAnd3JpdGFibGUnKSB7XG4gICAgICAgIGNvbnN0IGJhY2twcmVzc3VyZSA9IFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJHZXRCYWNrcHJlc3N1cmUoY29udHJvbGxlcik7XG4gICAgICAgIFdyaXRhYmxlU3RyZWFtVXBkYXRlQmFja3ByZXNzdXJlKHN0cmVhbSwgYmFja3ByZXNzdXJlKTtcbiAgICB9XG4gICAgV3JpdGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckFkdmFuY2VRdWV1ZUlmTmVlZGVkKGNvbnRyb2xsZXIpO1xufVxuLy8gQWJzdHJhY3Qgb3BlcmF0aW9ucyBmb3IgdGhlIFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXIuXG5mdW5jdGlvbiBXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyQWR2YW5jZVF1ZXVlSWZOZWVkZWQoY29udHJvbGxlcikge1xuICAgIGNvbnN0IHN0cmVhbSA9IGNvbnRyb2xsZXIuX2NvbnRyb2xsZWRXcml0YWJsZVN0cmVhbTtcbiAgICBpZiAoIWNvbnRyb2xsZXIuX3N0YXJ0ZWQpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAoc3RyZWFtLl9pbkZsaWdodFdyaXRlUmVxdWVzdCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3Qgc3RhdGUgPSBzdHJlYW0uX3N0YXRlO1xuICAgIGlmIChzdGF0ZSA9PT0gJ2Vycm9yaW5nJykge1xuICAgICAgICBXcml0YWJsZVN0cmVhbUZpbmlzaEVycm9yaW5nKHN0cmVhbSk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKGNvbnRyb2xsZXIuX3F1ZXVlLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHZhbHVlID0gUGVla1F1ZXVlVmFsdWUoY29udHJvbGxlcik7XG4gICAgaWYgKHZhbHVlID09PSBjbG9zZVNlbnRpbmVsKSB7XG4gICAgICAgIFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJQcm9jZXNzQ2xvc2UoY29udHJvbGxlcik7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyUHJvY2Vzc1dyaXRlKGNvbnRyb2xsZXIsIHZhbHVlKTtcbiAgICB9XG59XG5mdW5jdGlvbiBXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyRXJyb3JJZk5lZWRlZChjb250cm9sbGVyLCBlcnJvcikge1xuICAgIGlmIChjb250cm9sbGVyLl9jb250cm9sbGVkV3JpdGFibGVTdHJlYW0uX3N0YXRlID09PSAnd3JpdGFibGUnKSB7XG4gICAgICAgIFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJFcnJvcihjb250cm9sbGVyLCBlcnJvcik7XG4gICAgfVxufVxuZnVuY3Rpb24gV3JpdGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlclByb2Nlc3NDbG9zZShjb250cm9sbGVyKSB7XG4gICAgY29uc3Qgc3RyZWFtID0gY29udHJvbGxlci5fY29udHJvbGxlZFdyaXRhYmxlU3RyZWFtO1xuICAgIFdyaXRhYmxlU3RyZWFtTWFya0Nsb3NlUmVxdWVzdEluRmxpZ2h0KHN0cmVhbSk7XG4gICAgRGVxdWV1ZVZhbHVlKGNvbnRyb2xsZXIpO1xuICAgIGNvbnN0IHNpbmtDbG9zZVByb21pc2UgPSBjb250cm9sbGVyLl9jbG9zZUFsZ29yaXRobSgpO1xuICAgIFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJDbGVhckFsZ29yaXRobXMoY29udHJvbGxlcik7XG4gICAgdXBvblByb21pc2Uoc2lua0Nsb3NlUHJvbWlzZSwgKCkgPT4ge1xuICAgICAgICBXcml0YWJsZVN0cmVhbUZpbmlzaEluRmxpZ2h0Q2xvc2Uoc3RyZWFtKTtcbiAgICB9LCByZWFzb24gPT4ge1xuICAgICAgICBXcml0YWJsZVN0cmVhbUZpbmlzaEluRmxpZ2h0Q2xvc2VXaXRoRXJyb3Ioc3RyZWFtLCByZWFzb24pO1xuICAgIH0pO1xufVxuZnVuY3Rpb24gV3JpdGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlclByb2Nlc3NXcml0ZShjb250cm9sbGVyLCBjaHVuaykge1xuICAgIGNvbnN0IHN0cmVhbSA9IGNvbnRyb2xsZXIuX2NvbnRyb2xsZWRXcml0YWJsZVN0cmVhbTtcbiAgICBXcml0YWJsZVN0cmVhbU1hcmtGaXJzdFdyaXRlUmVxdWVzdEluRmxpZ2h0KHN0cmVhbSk7XG4gICAgY29uc3Qgc2lua1dyaXRlUHJvbWlzZSA9IGNvbnRyb2xsZXIuX3dyaXRlQWxnb3JpdGhtKGNodW5rKTtcbiAgICB1cG9uUHJvbWlzZShzaW5rV3JpdGVQcm9taXNlLCAoKSA9PiB7XG4gICAgICAgIFdyaXRhYmxlU3RyZWFtRmluaXNoSW5GbGlnaHRXcml0ZShzdHJlYW0pO1xuICAgICAgICBjb25zdCBzdGF0ZSA9IHN0cmVhbS5fc3RhdGU7XG4gICAgICAgIERlcXVldWVWYWx1ZShjb250cm9sbGVyKTtcbiAgICAgICAgaWYgKCFXcml0YWJsZVN0cmVhbUNsb3NlUXVldWVkT3JJbkZsaWdodChzdHJlYW0pICYmIHN0YXRlID09PSAnd3JpdGFibGUnKSB7XG4gICAgICAgICAgICBjb25zdCBiYWNrcHJlc3N1cmUgPSBXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyR2V0QmFja3ByZXNzdXJlKGNvbnRyb2xsZXIpO1xuICAgICAgICAgICAgV3JpdGFibGVTdHJlYW1VcGRhdGVCYWNrcHJlc3N1cmUoc3RyZWFtLCBiYWNrcHJlc3N1cmUpO1xuICAgICAgICB9XG4gICAgICAgIFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJBZHZhbmNlUXVldWVJZk5lZWRlZChjb250cm9sbGVyKTtcbiAgICB9LCByZWFzb24gPT4ge1xuICAgICAgICBpZiAoc3RyZWFtLl9zdGF0ZSA9PT0gJ3dyaXRhYmxlJykge1xuICAgICAgICAgICAgV3JpdGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckNsZWFyQWxnb3JpdGhtcyhjb250cm9sbGVyKTtcbiAgICAgICAgfVxuICAgICAgICBXcml0YWJsZVN0cmVhbUZpbmlzaEluRmxpZ2h0V3JpdGVXaXRoRXJyb3Ioc3RyZWFtLCByZWFzb24pO1xuICAgIH0pO1xufVxuZnVuY3Rpb24gV3JpdGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckdldEJhY2twcmVzc3VyZShjb250cm9sbGVyKSB7XG4gICAgY29uc3QgZGVzaXJlZFNpemUgPSBXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyR2V0RGVzaXJlZFNpemUoY29udHJvbGxlcik7XG4gICAgcmV0dXJuIGRlc2lyZWRTaXplIDw9IDA7XG59XG4vLyBBIGNsaWVudCBvZiBXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyIG1heSB1c2UgdGhlc2UgZnVuY3Rpb25zIGRpcmVjdGx5IHRvIGJ5cGFzcyBzdGF0ZSBjaGVjay5cbmZ1bmN0aW9uIFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJFcnJvcihjb250cm9sbGVyLCBlcnJvcikge1xuICAgIGNvbnN0IHN0cmVhbSA9IGNvbnRyb2xsZXIuX2NvbnRyb2xsZWRXcml0YWJsZVN0cmVhbTtcbiAgICBXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyQ2xlYXJBbGdvcml0aG1zKGNvbnRyb2xsZXIpO1xuICAgIFdyaXRhYmxlU3RyZWFtU3RhcnRFcnJvcmluZyhzdHJlYW0sIGVycm9yKTtcbn1cbi8vIEhlbHBlciBmdW5jdGlvbnMgZm9yIHRoZSBXcml0YWJsZVN0cmVhbS5cbmZ1bmN0aW9uIHN0cmVhbUJyYW5kQ2hlY2tFeGNlcHRpb24obmFtZSkge1xuICAgIHJldHVybiBuZXcgVHlwZUVycm9yKGBXcml0YWJsZVN0cmVhbS5wcm90b3R5cGUuJHtuYW1lfSBjYW4gb25seSBiZSB1c2VkIG9uIGEgV3JpdGFibGVTdHJlYW1gKTtcbn1cbi8vIEhlbHBlciBmdW5jdGlvbnMgZm9yIHRoZSBXcml0YWJsZVN0cmVhbURlZmF1bHRXcml0ZXIuXG5mdW5jdGlvbiBkZWZhdWx0V3JpdGVyQnJhbmRDaGVja0V4Y2VwdGlvbihuYW1lKSB7XG4gICAgcmV0dXJuIG5ldyBUeXBlRXJyb3IoYFdyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlci5wcm90b3R5cGUuJHtuYW1lfSBjYW4gb25seSBiZSB1c2VkIG9uIGEgV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyYCk7XG59XG5mdW5jdGlvbiBkZWZhdWx0V3JpdGVyTG9ja0V4Y2VwdGlvbihuYW1lKSB7XG4gICAgcmV0dXJuIG5ldyBUeXBlRXJyb3IoJ0Nhbm5vdCAnICsgbmFtZSArICcgYSBzdHJlYW0gdXNpbmcgYSByZWxlYXNlZCB3cml0ZXInKTtcbn1cbmZ1bmN0aW9uIGRlZmF1bHRXcml0ZXJDbG9zZWRQcm9taXNlSW5pdGlhbGl6ZSh3cml0ZXIpIHtcbiAgICB3cml0ZXIuX2Nsb3NlZFByb21pc2UgPSBuZXdQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgd3JpdGVyLl9jbG9zZWRQcm9taXNlX3Jlc29sdmUgPSByZXNvbHZlO1xuICAgICAgICB3cml0ZXIuX2Nsb3NlZFByb21pc2VfcmVqZWN0ID0gcmVqZWN0O1xuICAgICAgICB3cml0ZXIuX2Nsb3NlZFByb21pc2VTdGF0ZSA9ICdwZW5kaW5nJztcbiAgICB9KTtcbn1cbmZ1bmN0aW9uIGRlZmF1bHRXcml0ZXJDbG9zZWRQcm9taXNlSW5pdGlhbGl6ZUFzUmVqZWN0ZWQod3JpdGVyLCByZWFzb24pIHtcbiAgICBkZWZhdWx0V3JpdGVyQ2xvc2VkUHJvbWlzZUluaXRpYWxpemUod3JpdGVyKTtcbiAgICBkZWZhdWx0V3JpdGVyQ2xvc2VkUHJvbWlzZVJlamVjdCh3cml0ZXIsIHJlYXNvbik7XG59XG5mdW5jdGlvbiBkZWZhdWx0V3JpdGVyQ2xvc2VkUHJvbWlzZUluaXRpYWxpemVBc1Jlc29sdmVkKHdyaXRlcikge1xuICAgIGRlZmF1bHRXcml0ZXJDbG9zZWRQcm9taXNlSW5pdGlhbGl6ZSh3cml0ZXIpO1xuICAgIGRlZmF1bHRXcml0ZXJDbG9zZWRQcm9taXNlUmVzb2x2ZSh3cml0ZXIpO1xufVxuZnVuY3Rpb24gZGVmYXVsdFdyaXRlckNsb3NlZFByb21pc2VSZWplY3Qod3JpdGVyLCByZWFzb24pIHtcbiAgICBpZiAod3JpdGVyLl9jbG9zZWRQcm9taXNlX3JlamVjdCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgc2V0UHJvbWlzZUlzSGFuZGxlZFRvVHJ1ZSh3cml0ZXIuX2Nsb3NlZFByb21pc2UpO1xuICAgIHdyaXRlci5fY2xvc2VkUHJvbWlzZV9yZWplY3QocmVhc29uKTtcbiAgICB3cml0ZXIuX2Nsb3NlZFByb21pc2VfcmVzb2x2ZSA9IHVuZGVmaW5lZDtcbiAgICB3cml0ZXIuX2Nsb3NlZFByb21pc2VfcmVqZWN0ID0gdW5kZWZpbmVkO1xuICAgIHdyaXRlci5fY2xvc2VkUHJvbWlzZVN0YXRlID0gJ3JlamVjdGVkJztcbn1cbmZ1bmN0aW9uIGRlZmF1bHRXcml0ZXJDbG9zZWRQcm9taXNlUmVzZXRUb1JlamVjdGVkKHdyaXRlciwgcmVhc29uKSB7XG4gICAgZGVmYXVsdFdyaXRlckNsb3NlZFByb21pc2VJbml0aWFsaXplQXNSZWplY3RlZCh3cml0ZXIsIHJlYXNvbik7XG59XG5mdW5jdGlvbiBkZWZhdWx0V3JpdGVyQ2xvc2VkUHJvbWlzZVJlc29sdmUod3JpdGVyKSB7XG4gICAgaWYgKHdyaXRlci5fY2xvc2VkUHJvbWlzZV9yZXNvbHZlID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB3cml0ZXIuX2Nsb3NlZFByb21pc2VfcmVzb2x2ZSh1bmRlZmluZWQpO1xuICAgIHdyaXRlci5fY2xvc2VkUHJvbWlzZV9yZXNvbHZlID0gdW5kZWZpbmVkO1xuICAgIHdyaXRlci5fY2xvc2VkUHJvbWlzZV9yZWplY3QgPSB1bmRlZmluZWQ7XG4gICAgd3JpdGVyLl9jbG9zZWRQcm9taXNlU3RhdGUgPSAncmVzb2x2ZWQnO1xufVxuZnVuY3Rpb24gZGVmYXVsdFdyaXRlclJlYWR5UHJvbWlzZUluaXRpYWxpemUod3JpdGVyKSB7XG4gICAgd3JpdGVyLl9yZWFkeVByb21pc2UgPSBuZXdQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgd3JpdGVyLl9yZWFkeVByb21pc2VfcmVzb2x2ZSA9IHJlc29sdmU7XG4gICAgICAgIHdyaXRlci5fcmVhZHlQcm9taXNlX3JlamVjdCA9IHJlamVjdDtcbiAgICB9KTtcbiAgICB3cml0ZXIuX3JlYWR5UHJvbWlzZVN0YXRlID0gJ3BlbmRpbmcnO1xufVxuZnVuY3Rpb24gZGVmYXVsdFdyaXRlclJlYWR5UHJvbWlzZUluaXRpYWxpemVBc1JlamVjdGVkKHdyaXRlciwgcmVhc29uKSB7XG4gICAgZGVmYXVsdFdyaXRlclJlYWR5UHJvbWlzZUluaXRpYWxpemUod3JpdGVyKTtcbiAgICBkZWZhdWx0V3JpdGVyUmVhZHlQcm9taXNlUmVqZWN0KHdyaXRlciwgcmVhc29uKTtcbn1cbmZ1bmN0aW9uIGRlZmF1bHRXcml0ZXJSZWFkeVByb21pc2VJbml0aWFsaXplQXNSZXNvbHZlZCh3cml0ZXIpIHtcbiAgICBkZWZhdWx0V3JpdGVyUmVhZHlQcm9taXNlSW5pdGlhbGl6ZSh3cml0ZXIpO1xuICAgIGRlZmF1bHRXcml0ZXJSZWFkeVByb21pc2VSZXNvbHZlKHdyaXRlcik7XG59XG5mdW5jdGlvbiBkZWZhdWx0V3JpdGVyUmVhZHlQcm9taXNlUmVqZWN0KHdyaXRlciwgcmVhc29uKSB7XG4gICAgaWYgKHdyaXRlci5fcmVhZHlQcm9taXNlX3JlamVjdCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgc2V0UHJvbWlzZUlzSGFuZGxlZFRvVHJ1ZSh3cml0ZXIuX3JlYWR5UHJvbWlzZSk7XG4gICAgd3JpdGVyLl9yZWFkeVByb21pc2VfcmVqZWN0KHJlYXNvbik7XG4gICAgd3JpdGVyLl9yZWFkeVByb21pc2VfcmVzb2x2ZSA9IHVuZGVmaW5lZDtcbiAgICB3cml0ZXIuX3JlYWR5UHJvbWlzZV9yZWplY3QgPSB1bmRlZmluZWQ7XG4gICAgd3JpdGVyLl9yZWFkeVByb21pc2VTdGF0ZSA9ICdyZWplY3RlZCc7XG59XG5mdW5jdGlvbiBkZWZhdWx0V3JpdGVyUmVhZHlQcm9taXNlUmVzZXQod3JpdGVyKSB7XG4gICAgZGVmYXVsdFdyaXRlclJlYWR5UHJvbWlzZUluaXRpYWxpemUod3JpdGVyKTtcbn1cbmZ1bmN0aW9uIGRlZmF1bHRXcml0ZXJSZWFkeVByb21pc2VSZXNldFRvUmVqZWN0ZWQod3JpdGVyLCByZWFzb24pIHtcbiAgICBkZWZhdWx0V3JpdGVyUmVhZHlQcm9taXNlSW5pdGlhbGl6ZUFzUmVqZWN0ZWQod3JpdGVyLCByZWFzb24pO1xufVxuZnVuY3Rpb24gZGVmYXVsdFdyaXRlclJlYWR5UHJvbWlzZVJlc29sdmUod3JpdGVyKSB7XG4gICAgaWYgKHdyaXRlci5fcmVhZHlQcm9taXNlX3Jlc29sdmUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIHdyaXRlci5fcmVhZHlQcm9taXNlX3Jlc29sdmUodW5kZWZpbmVkKTtcbiAgICB3cml0ZXIuX3JlYWR5UHJvbWlzZV9yZXNvbHZlID0gdW5kZWZpbmVkO1xuICAgIHdyaXRlci5fcmVhZHlQcm9taXNlX3JlamVjdCA9IHVuZGVmaW5lZDtcbiAgICB3cml0ZXIuX3JlYWR5UHJvbWlzZVN0YXRlID0gJ2Z1bGZpbGxlZCc7XG59XG5cbmZ1bmN0aW9uIGlzQWJvcnRTaWduYWwodmFsdWUpIHtcbiAgICBpZiAodHlwZW9mIHZhbHVlICE9PSAnb2JqZWN0JyB8fCB2YWx1ZSA9PT0gbnVsbCkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICAgIHJldHVybiB0eXBlb2YgdmFsdWUuYWJvcnRlZCA9PT0gJ2Jvb2xlYW4nO1xuICAgIH1cbiAgICBjYXRjaCAoX2EpIHtcbiAgICAgICAgLy8gQWJvcnRTaWduYWwucHJvdG90eXBlLmFib3J0ZWQgdGhyb3dzIGlmIGl0cyBicmFuZCBjaGVjayBmYWlsc1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxufVxuXG4vLy8gPHJlZmVyZW5jZSBsaWI9XCJkb21cIiAvPlxuY29uc3QgTmF0aXZlRE9NRXhjZXB0aW9uID0gdHlwZW9mIERPTUV4Y2VwdGlvbiAhPT0gJ3VuZGVmaW5lZCcgPyBET01FeGNlcHRpb24gOiB1bmRlZmluZWQ7XG5cbi8vLyA8cmVmZXJlbmNlIHR5cGVzPVwibm9kZVwiIC8+XG5mdW5jdGlvbiBpc0RPTUV4Y2VwdGlvbkNvbnN0cnVjdG9yKGN0b3IpIHtcbiAgICBpZiAoISh0eXBlb2YgY3RvciA9PT0gJ2Z1bmN0aW9uJyB8fCB0eXBlb2YgY3RvciA9PT0gJ29iamVjdCcpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgICAgbmV3IGN0b3IoKTtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIGNhdGNoIChfYSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxufVxuZnVuY3Rpb24gY3JlYXRlRE9NRXhjZXB0aW9uUG9seWZpbGwoKSB7XG4gICAgY29uc3QgY3RvciA9IGZ1bmN0aW9uIERPTUV4Y2VwdGlvbihtZXNzYWdlLCBuYW1lKSB7XG4gICAgICAgIHRoaXMubWVzc2FnZSA9IG1lc3NhZ2UgfHwgJyc7XG4gICAgICAgIHRoaXMubmFtZSA9IG5hbWUgfHwgJ0Vycm9yJztcbiAgICAgICAgaWYgKEVycm9yLmNhcHR1cmVTdGFja1RyYWNlKSB7XG4gICAgICAgICAgICBFcnJvci5jYXB0dXJlU3RhY2tUcmFjZSh0aGlzLCB0aGlzLmNvbnN0cnVjdG9yKTtcbiAgICAgICAgfVxuICAgIH07XG4gICAgY3Rvci5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKEVycm9yLnByb3RvdHlwZSk7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KGN0b3IucHJvdG90eXBlLCAnY29uc3RydWN0b3InLCB7IHZhbHVlOiBjdG9yLCB3cml0YWJsZTogdHJ1ZSwgY29uZmlndXJhYmxlOiB0cnVlIH0pO1xuICAgIHJldHVybiBjdG9yO1xufVxuY29uc3QgRE9NRXhjZXB0aW9uJDEgPSBpc0RPTUV4Y2VwdGlvbkNvbnN0cnVjdG9yKE5hdGl2ZURPTUV4Y2VwdGlvbikgPyBOYXRpdmVET01FeGNlcHRpb24gOiBjcmVhdGVET01FeGNlcHRpb25Qb2x5ZmlsbCgpO1xuXG5mdW5jdGlvbiBSZWFkYWJsZVN0cmVhbVBpcGVUbyhzb3VyY2UsIGRlc3QsIHByZXZlbnRDbG9zZSwgcHJldmVudEFib3J0LCBwcmV2ZW50Q2FuY2VsLCBzaWduYWwpIHtcbiAgICBjb25zdCByZWFkZXIgPSBBY3F1aXJlUmVhZGFibGVTdHJlYW1EZWZhdWx0UmVhZGVyKHNvdXJjZSk7XG4gICAgY29uc3Qgd3JpdGVyID0gQWNxdWlyZVdyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlcihkZXN0KTtcbiAgICBzb3VyY2UuX2Rpc3R1cmJlZCA9IHRydWU7XG4gICAgbGV0IHNodXR0aW5nRG93biA9IGZhbHNlO1xuICAgIC8vIFRoaXMgaXMgdXNlZCB0byBrZWVwIHRyYWNrIG9mIHRoZSBzcGVjJ3MgcmVxdWlyZW1lbnQgdGhhdCB3ZSB3YWl0IGZvciBvbmdvaW5nIHdyaXRlcyBkdXJpbmcgc2h1dGRvd24uXG4gICAgbGV0IGN1cnJlbnRXcml0ZSA9IHByb21pc2VSZXNvbHZlZFdpdGgodW5kZWZpbmVkKTtcbiAgICByZXR1cm4gbmV3UHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgIGxldCBhYm9ydEFsZ29yaXRobTtcbiAgICAgICAgaWYgKHNpZ25hbCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBhYm9ydEFsZ29yaXRobSA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBlcnJvciA9IG5ldyBET01FeGNlcHRpb24kMSgnQWJvcnRlZCcsICdBYm9ydEVycm9yJyk7XG4gICAgICAgICAgICAgICAgY29uc3QgYWN0aW9ucyA9IFtdO1xuICAgICAgICAgICAgICAgIGlmICghcHJldmVudEFib3J0KSB7XG4gICAgICAgICAgICAgICAgICAgIGFjdGlvbnMucHVzaCgoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoZGVzdC5fc3RhdGUgPT09ICd3cml0YWJsZScpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gV3JpdGFibGVTdHJlYW1BYm9ydChkZXN0LCBlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gcHJvbWlzZVJlc29sdmVkV2l0aCh1bmRlZmluZWQpO1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKCFwcmV2ZW50Q2FuY2VsKSB7XG4gICAgICAgICAgICAgICAgICAgIGFjdGlvbnMucHVzaCgoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoc291cmNlLl9zdGF0ZSA9PT0gJ3JlYWRhYmxlJykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBSZWFkYWJsZVN0cmVhbUNhbmNlbChzb3VyY2UsIGVycm9yKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBwcm9taXNlUmVzb2x2ZWRXaXRoKHVuZGVmaW5lZCk7XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBzaHV0ZG93bldpdGhBY3Rpb24oKCkgPT4gUHJvbWlzZS5hbGwoYWN0aW9ucy5tYXAoYWN0aW9uID0+IGFjdGlvbigpKSksIHRydWUsIGVycm9yKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBpZiAoc2lnbmFsLmFib3J0ZWQpIHtcbiAgICAgICAgICAgICAgICBhYm9ydEFsZ29yaXRobSgpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHNpZ25hbC5hZGRFdmVudExpc3RlbmVyKCdhYm9ydCcsIGFib3J0QWxnb3JpdGhtKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBVc2luZyByZWFkZXIgYW5kIHdyaXRlciwgcmVhZCBhbGwgY2h1bmtzIGZyb20gdGhpcyBhbmQgd3JpdGUgdGhlbSB0byBkZXN0XG4gICAgICAgIC8vIC0gQmFja3ByZXNzdXJlIG11c3QgYmUgZW5mb3JjZWRcbiAgICAgICAgLy8gLSBTaHV0ZG93biBtdXN0IHN0b3AgYWxsIGFjdGl2aXR5XG4gICAgICAgIGZ1bmN0aW9uIHBpcGVMb29wKCkge1xuICAgICAgICAgICAgcmV0dXJuIG5ld1Byb21pc2UoKHJlc29sdmVMb29wLCByZWplY3RMb29wKSA9PiB7XG4gICAgICAgICAgICAgICAgZnVuY3Rpb24gbmV4dChkb25lKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChkb25lKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXNvbHZlTG9vcCgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gVXNlIGBQZXJmb3JtUHJvbWlzZVRoZW5gIGluc3RlYWQgb2YgYHVwb25Qcm9taXNlYCB0byBhdm9pZFxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gYWRkaW5nIHVubmVjZXNzYXJ5IGAuY2F0Y2gocmV0aHJvd0Fzc2VydGlvbkVycm9yUmVqZWN0aW9uKWAgaGFuZGxlcnNcbiAgICAgICAgICAgICAgICAgICAgICAgIFBlcmZvcm1Qcm9taXNlVGhlbihwaXBlU3RlcCgpLCBuZXh0LCByZWplY3RMb29wKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBuZXh0KGZhbHNlKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICAgIGZ1bmN0aW9uIHBpcGVTdGVwKCkge1xuICAgICAgICAgICAgaWYgKHNodXR0aW5nRG93bikge1xuICAgICAgICAgICAgICAgIHJldHVybiBwcm9taXNlUmVzb2x2ZWRXaXRoKHRydWUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIFBlcmZvcm1Qcm9taXNlVGhlbih3cml0ZXIuX3JlYWR5UHJvbWlzZSwgKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJldHVybiBuZXdQcm9taXNlKChyZXNvbHZlUmVhZCwgcmVqZWN0UmVhZCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICBSZWFkYWJsZVN0cmVhbURlZmF1bHRSZWFkZXJSZWFkKHJlYWRlciwge1xuICAgICAgICAgICAgICAgICAgICAgICAgX2NodW5rU3RlcHM6IGNodW5rID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJyZW50V3JpdGUgPSBQZXJmb3JtUHJvbWlzZVRoZW4oV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyV3JpdGUod3JpdGVyLCBjaHVuayksIHVuZGVmaW5lZCwgbm9vcCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZVJlYWQoZmFsc2UpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIF9jbG9zZVN0ZXBzOiAoKSA9PiByZXNvbHZlUmVhZCh0cnVlKSxcbiAgICAgICAgICAgICAgICAgICAgICAgIF9lcnJvclN0ZXBzOiByZWplY3RSZWFkXG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gRXJyb3JzIG11c3QgYmUgcHJvcGFnYXRlZCBmb3J3YXJkXG4gICAgICAgIGlzT3JCZWNvbWVzRXJyb3JlZChzb3VyY2UsIHJlYWRlci5fY2xvc2VkUHJvbWlzZSwgc3RvcmVkRXJyb3IgPT4ge1xuICAgICAgICAgICAgaWYgKCFwcmV2ZW50QWJvcnQpIHtcbiAgICAgICAgICAgICAgICBzaHV0ZG93bldpdGhBY3Rpb24oKCkgPT4gV3JpdGFibGVTdHJlYW1BYm9ydChkZXN0LCBzdG9yZWRFcnJvciksIHRydWUsIHN0b3JlZEVycm9yKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgIHNodXRkb3duKHRydWUsIHN0b3JlZEVycm9yKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIC8vIEVycm9ycyBtdXN0IGJlIHByb3BhZ2F0ZWQgYmFja3dhcmRcbiAgICAgICAgaXNPckJlY29tZXNFcnJvcmVkKGRlc3QsIHdyaXRlci5fY2xvc2VkUHJvbWlzZSwgc3RvcmVkRXJyb3IgPT4ge1xuICAgICAgICAgICAgaWYgKCFwcmV2ZW50Q2FuY2VsKSB7XG4gICAgICAgICAgICAgICAgc2h1dGRvd25XaXRoQWN0aW9uKCgpID0+IFJlYWRhYmxlU3RyZWFtQ2FuY2VsKHNvdXJjZSwgc3RvcmVkRXJyb3IpLCB0cnVlLCBzdG9yZWRFcnJvcik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICBzaHV0ZG93bih0cnVlLCBzdG9yZWRFcnJvcik7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICAvLyBDbG9zaW5nIG11c3QgYmUgcHJvcGFnYXRlZCBmb3J3YXJkXG4gICAgICAgIGlzT3JCZWNvbWVzQ2xvc2VkKHNvdXJjZSwgcmVhZGVyLl9jbG9zZWRQcm9taXNlLCAoKSA9PiB7XG4gICAgICAgICAgICBpZiAoIXByZXZlbnRDbG9zZSkge1xuICAgICAgICAgICAgICAgIHNodXRkb3duV2l0aEFjdGlvbigoKSA9PiBXcml0YWJsZVN0cmVhbURlZmF1bHRXcml0ZXJDbG9zZVdpdGhFcnJvclByb3BhZ2F0aW9uKHdyaXRlcikpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgc2h1dGRvd24oKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIC8vIENsb3NpbmcgbXVzdCBiZSBwcm9wYWdhdGVkIGJhY2t3YXJkXG4gICAgICAgIGlmIChXcml0YWJsZVN0cmVhbUNsb3NlUXVldWVkT3JJbkZsaWdodChkZXN0KSB8fCBkZXN0Ll9zdGF0ZSA9PT0gJ2Nsb3NlZCcpIHtcbiAgICAgICAgICAgIGNvbnN0IGRlc3RDbG9zZWQgPSBuZXcgVHlwZUVycm9yKCd0aGUgZGVzdGluYXRpb24gd3JpdGFibGUgc3RyZWFtIGNsb3NlZCBiZWZvcmUgYWxsIGRhdGEgY291bGQgYmUgcGlwZWQgdG8gaXQnKTtcbiAgICAgICAgICAgIGlmICghcHJldmVudENhbmNlbCkge1xuICAgICAgICAgICAgICAgIHNodXRkb3duV2l0aEFjdGlvbigoKSA9PiBSZWFkYWJsZVN0cmVhbUNhbmNlbChzb3VyY2UsIGRlc3RDbG9zZWQpLCB0cnVlLCBkZXN0Q2xvc2VkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgIHNodXRkb3duKHRydWUsIGRlc3RDbG9zZWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHNldFByb21pc2VJc0hhbmRsZWRUb1RydWUocGlwZUxvb3AoKSk7XG4gICAgICAgIGZ1bmN0aW9uIHdhaXRGb3JXcml0ZXNUb0ZpbmlzaCgpIHtcbiAgICAgICAgICAgIC8vIEFub3RoZXIgd3JpdGUgbWF5IGhhdmUgc3RhcnRlZCB3aGlsZSB3ZSB3ZXJlIHdhaXRpbmcgb24gdGhpcyBjdXJyZW50V3JpdGUsIHNvIHdlIGhhdmUgdG8gYmUgc3VyZSB0byB3YWl0XG4gICAgICAgICAgICAvLyBmb3IgdGhhdCB0b28uXG4gICAgICAgICAgICBjb25zdCBvbGRDdXJyZW50V3JpdGUgPSBjdXJyZW50V3JpdGU7XG4gICAgICAgICAgICByZXR1cm4gUGVyZm9ybVByb21pc2VUaGVuKGN1cnJlbnRXcml0ZSwgKCkgPT4gb2xkQ3VycmVudFdyaXRlICE9PSBjdXJyZW50V3JpdGUgPyB3YWl0Rm9yV3JpdGVzVG9GaW5pc2goKSA6IHVuZGVmaW5lZCk7XG4gICAgICAgIH1cbiAgICAgICAgZnVuY3Rpb24gaXNPckJlY29tZXNFcnJvcmVkKHN0cmVhbSwgcHJvbWlzZSwgYWN0aW9uKSB7XG4gICAgICAgICAgICBpZiAoc3RyZWFtLl9zdGF0ZSA9PT0gJ2Vycm9yZWQnKSB7XG4gICAgICAgICAgICAgICAgYWN0aW9uKHN0cmVhbS5fc3RvcmVkRXJyb3IpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgdXBvblJlamVjdGlvbihwcm9taXNlLCBhY3Rpb24pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGZ1bmN0aW9uIGlzT3JCZWNvbWVzQ2xvc2VkKHN0cmVhbSwgcHJvbWlzZSwgYWN0aW9uKSB7XG4gICAgICAgICAgICBpZiAoc3RyZWFtLl9zdGF0ZSA9PT0gJ2Nsb3NlZCcpIHtcbiAgICAgICAgICAgICAgICBhY3Rpb24oKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgIHVwb25GdWxmaWxsbWVudChwcm9taXNlLCBhY3Rpb24pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGZ1bmN0aW9uIHNodXRkb3duV2l0aEFjdGlvbihhY3Rpb24sIG9yaWdpbmFsSXNFcnJvciwgb3JpZ2luYWxFcnJvcikge1xuICAgICAgICAgICAgaWYgKHNodXR0aW5nRG93bikge1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHNodXR0aW5nRG93biA9IHRydWU7XG4gICAgICAgICAgICBpZiAoZGVzdC5fc3RhdGUgPT09ICd3cml0YWJsZScgJiYgIVdyaXRhYmxlU3RyZWFtQ2xvc2VRdWV1ZWRPckluRmxpZ2h0KGRlc3QpKSB7XG4gICAgICAgICAgICAgICAgdXBvbkZ1bGZpbGxtZW50KHdhaXRGb3JXcml0ZXNUb0ZpbmlzaCgpLCBkb1RoZVJlc3QpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgZG9UaGVSZXN0KCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBmdW5jdGlvbiBkb1RoZVJlc3QoKSB7XG4gICAgICAgICAgICAgICAgdXBvblByb21pc2UoYWN0aW9uKCksICgpID0+IGZpbmFsaXplKG9yaWdpbmFsSXNFcnJvciwgb3JpZ2luYWxFcnJvciksIG5ld0Vycm9yID0+IGZpbmFsaXplKHRydWUsIG5ld0Vycm9yKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZnVuY3Rpb24gc2h1dGRvd24oaXNFcnJvciwgZXJyb3IpIHtcbiAgICAgICAgICAgIGlmIChzaHV0dGluZ0Rvd24pIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBzaHV0dGluZ0Rvd24gPSB0cnVlO1xuICAgICAgICAgICAgaWYgKGRlc3QuX3N0YXRlID09PSAnd3JpdGFibGUnICYmICFXcml0YWJsZVN0cmVhbUNsb3NlUXVldWVkT3JJbkZsaWdodChkZXN0KSkge1xuICAgICAgICAgICAgICAgIHVwb25GdWxmaWxsbWVudCh3YWl0Rm9yV3JpdGVzVG9GaW5pc2goKSwgKCkgPT4gZmluYWxpemUoaXNFcnJvciwgZXJyb3IpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgIGZpbmFsaXplKGlzRXJyb3IsIGVycm9yKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBmdW5jdGlvbiBmaW5hbGl6ZShpc0Vycm9yLCBlcnJvcikge1xuICAgICAgICAgICAgV3JpdGFibGVTdHJlYW1EZWZhdWx0V3JpdGVyUmVsZWFzZSh3cml0ZXIpO1xuICAgICAgICAgICAgUmVhZGFibGVTdHJlYW1SZWFkZXJHZW5lcmljUmVsZWFzZShyZWFkZXIpO1xuICAgICAgICAgICAgaWYgKHNpZ25hbCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgc2lnbmFsLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2Fib3J0JywgYWJvcnRBbGdvcml0aG0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKGlzRXJyb3IpIHtcbiAgICAgICAgICAgICAgICByZWplY3QoZXJyb3IpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh1bmRlZmluZWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfSk7XG59XG5cbi8qKlxuICogQWxsb3dzIGNvbnRyb2wgb2YgYSB7QGxpbmsgUmVhZGFibGVTdHJlYW0gfCByZWFkYWJsZSBzdHJlYW19J3Mgc3RhdGUgYW5kIGludGVybmFsIHF1ZXVlLlxuICpcbiAqIEBwdWJsaWNcbiAqL1xuY2xhc3MgUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlciB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0lsbGVnYWwgY29uc3RydWN0b3InKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgZGVzaXJlZCBzaXplIHRvIGZpbGwgdGhlIGNvbnRyb2xsZWQgc3RyZWFtJ3MgaW50ZXJuYWwgcXVldWUuIEl0IGNhbiBiZSBuZWdhdGl2ZSwgaWYgdGhlIHF1ZXVlIGlzXG4gICAgICogb3Zlci1mdWxsLiBBbiB1bmRlcmx5aW5nIHNvdXJjZSBvdWdodCB0byB1c2UgdGhpcyBpbmZvcm1hdGlvbiB0byBkZXRlcm1pbmUgd2hlbiBhbmQgaG93IHRvIGFwcGx5IGJhY2twcmVzc3VyZS5cbiAgICAgKi9cbiAgICBnZXQgZGVzaXJlZFNpemUoKSB7XG4gICAgICAgIGlmICghSXNSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyKHRoaXMpKSB7XG4gICAgICAgICAgICB0aHJvdyBkZWZhdWx0Q29udHJvbGxlckJyYW5kQ2hlY2tFeGNlcHRpb24oJ2Rlc2lyZWRTaXplJyk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJHZXREZXNpcmVkU2l6ZSh0aGlzKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQ2xvc2VzIHRoZSBjb250cm9sbGVkIHJlYWRhYmxlIHN0cmVhbS4gQ29uc3VtZXJzIHdpbGwgc3RpbGwgYmUgYWJsZSB0byByZWFkIGFueSBwcmV2aW91c2x5LWVucXVldWVkIGNodW5rcyBmcm9tXG4gICAgICogdGhlIHN0cmVhbSwgYnV0IG9uY2UgdGhvc2UgYXJlIHJlYWQsIHRoZSBzdHJlYW0gd2lsbCBiZWNvbWUgY2xvc2VkLlxuICAgICAqL1xuICAgIGNsb3NlKCkge1xuICAgICAgICBpZiAoIUlzUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlcih0aGlzKSkge1xuICAgICAgICAgICAgdGhyb3cgZGVmYXVsdENvbnRyb2xsZXJCcmFuZENoZWNrRXhjZXB0aW9uKCdjbG9zZScpO1xuICAgICAgICB9XG4gICAgICAgIGlmICghUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckNhbkNsb3NlT3JFbnF1ZXVlKHRoaXMpKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdUaGUgc3RyZWFtIGlzIG5vdCBpbiBhIHN0YXRlIHRoYXQgcGVybWl0cyBjbG9zZScpO1xuICAgICAgICB9XG4gICAgICAgIFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJDbG9zZSh0aGlzKTtcbiAgICB9XG4gICAgZW5xdWV1ZShjaHVuayA9IHVuZGVmaW5lZCkge1xuICAgICAgICBpZiAoIUlzUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlcih0aGlzKSkge1xuICAgICAgICAgICAgdGhyb3cgZGVmYXVsdENvbnRyb2xsZXJCcmFuZENoZWNrRXhjZXB0aW9uKCdlbnF1ZXVlJyk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyQ2FuQ2xvc2VPckVucXVldWUodGhpcykpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ1RoZSBzdHJlYW0gaXMgbm90IGluIGEgc3RhdGUgdGhhdCBwZXJtaXRzIGVucXVldWUnKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckVucXVldWUodGhpcywgY2h1bmspO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBFcnJvcnMgdGhlIGNvbnRyb2xsZWQgcmVhZGFibGUgc3RyZWFtLCBtYWtpbmcgYWxsIGZ1dHVyZSBpbnRlcmFjdGlvbnMgd2l0aCBpdCBmYWlsIHdpdGggdGhlIGdpdmVuIGVycm9yIGBlYC5cbiAgICAgKi9cbiAgICBlcnJvcihlID0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGlmICghSXNSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyKHRoaXMpKSB7XG4gICAgICAgICAgICB0aHJvdyBkZWZhdWx0Q29udHJvbGxlckJyYW5kQ2hlY2tFeGNlcHRpb24oJ2Vycm9yJyk7XG4gICAgICAgIH1cbiAgICAgICAgUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckVycm9yKHRoaXMsIGUpO1xuICAgIH1cbiAgICAvKiogQGludGVybmFsICovXG4gICAgW0NhbmNlbFN0ZXBzXShyZWFzb24pIHtcbiAgICAgICAgUmVzZXRRdWV1ZSh0aGlzKTtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gdGhpcy5fY2FuY2VsQWxnb3JpdGhtKHJlYXNvbik7XG4gICAgICAgIFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJDbGVhckFsZ29yaXRobXModGhpcyk7XG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuICAgIC8qKiBAaW50ZXJuYWwgKi9cbiAgICBbUHVsbFN0ZXBzXShyZWFkUmVxdWVzdCkge1xuICAgICAgICBjb25zdCBzdHJlYW0gPSB0aGlzLl9jb250cm9sbGVkUmVhZGFibGVTdHJlYW07XG4gICAgICAgIGlmICh0aGlzLl9xdWV1ZS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBjb25zdCBjaHVuayA9IERlcXVldWVWYWx1ZSh0aGlzKTtcbiAgICAgICAgICAgIGlmICh0aGlzLl9jbG9zZVJlcXVlc3RlZCAmJiB0aGlzLl9xdWV1ZS5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgICAgICBSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyQ2xlYXJBbGdvcml0aG1zKHRoaXMpO1xuICAgICAgICAgICAgICAgIFJlYWRhYmxlU3RyZWFtQ2xvc2Uoc3RyZWFtKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgIFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJDYWxsUHVsbElmTmVlZGVkKHRoaXMpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmVhZFJlcXVlc3QuX2NodW5rU3RlcHMoY2h1bmspO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgUmVhZGFibGVTdHJlYW1BZGRSZWFkUmVxdWVzdChzdHJlYW0sIHJlYWRSZXF1ZXN0KTtcbiAgICAgICAgICAgIFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJDYWxsUHVsbElmTmVlZGVkKHRoaXMpO1xuICAgICAgICB9XG4gICAgfVxufVxuT2JqZWN0LmRlZmluZVByb3BlcnRpZXMoUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlci5wcm90b3R5cGUsIHtcbiAgICBjbG9zZTogeyBlbnVtZXJhYmxlOiB0cnVlIH0sXG4gICAgZW5xdWV1ZTogeyBlbnVtZXJhYmxlOiB0cnVlIH0sXG4gICAgZXJyb3I6IHsgZW51bWVyYWJsZTogdHJ1ZSB9LFxuICAgIGRlc2lyZWRTaXplOiB7IGVudW1lcmFibGU6IHRydWUgfVxufSk7XG5pZiAodHlwZW9mIFN5bWJvbFBvbHlmaWxsLnRvU3RyaW5nVGFnID09PSAnc3ltYm9sJykge1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyLnByb3RvdHlwZSwgU3ltYm9sUG9seWZpbGwudG9TdHJpbmdUYWcsIHtcbiAgICAgICAgdmFsdWU6ICdSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyJyxcbiAgICAgICAgY29uZmlndXJhYmxlOiB0cnVlXG4gICAgfSk7XG59XG4vLyBBYnN0cmFjdCBvcGVyYXRpb25zIGZvciB0aGUgUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlci5cbmZ1bmN0aW9uIElzUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlcih4KSB7XG4gICAgaWYgKCF0eXBlSXNPYmplY3QoeCkpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh4LCAnX2NvbnRyb2xsZWRSZWFkYWJsZVN0cmVhbScpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgcmV0dXJuIHRydWU7XG59XG5mdW5jdGlvbiBSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyQ2FsbFB1bGxJZk5lZWRlZChjb250cm9sbGVyKSB7XG4gICAgY29uc3Qgc2hvdWxkUHVsbCA9IFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJTaG91bGRDYWxsUHVsbChjb250cm9sbGVyKTtcbiAgICBpZiAoIXNob3VsZFB1bGwpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAoY29udHJvbGxlci5fcHVsbGluZykge1xuICAgICAgICBjb250cm9sbGVyLl9wdWxsQWdhaW4gPSB0cnVlO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnRyb2xsZXIuX3B1bGxpbmcgPSB0cnVlO1xuICAgIGNvbnN0IHB1bGxQcm9taXNlID0gY29udHJvbGxlci5fcHVsbEFsZ29yaXRobSgpO1xuICAgIHVwb25Qcm9taXNlKHB1bGxQcm9taXNlLCAoKSA9PiB7XG4gICAgICAgIGNvbnRyb2xsZXIuX3B1bGxpbmcgPSBmYWxzZTtcbiAgICAgICAgaWYgKGNvbnRyb2xsZXIuX3B1bGxBZ2Fpbikge1xuICAgICAgICAgICAgY29udHJvbGxlci5fcHVsbEFnYWluID0gZmFsc2U7XG4gICAgICAgICAgICBSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyQ2FsbFB1bGxJZk5lZWRlZChjb250cm9sbGVyKTtcbiAgICAgICAgfVxuICAgIH0sIGUgPT4ge1xuICAgICAgICBSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyRXJyb3IoY29udHJvbGxlciwgZSk7XG4gICAgfSk7XG59XG5mdW5jdGlvbiBSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyU2hvdWxkQ2FsbFB1bGwoY29udHJvbGxlcikge1xuICAgIGNvbnN0IHN0cmVhbSA9IGNvbnRyb2xsZXIuX2NvbnRyb2xsZWRSZWFkYWJsZVN0cmVhbTtcbiAgICBpZiAoIVJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJDYW5DbG9zZU9yRW5xdWV1ZShjb250cm9sbGVyKSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGlmICghY29udHJvbGxlci5fc3RhcnRlZCkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGlmIChJc1JlYWRhYmxlU3RyZWFtTG9ja2VkKHN0cmVhbSkgJiYgUmVhZGFibGVTdHJlYW1HZXROdW1SZWFkUmVxdWVzdHMoc3RyZWFtKSA+IDApIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIGNvbnN0IGRlc2lyZWRTaXplID0gUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckdldERlc2lyZWRTaXplKGNvbnRyb2xsZXIpO1xuICAgIGlmIChkZXNpcmVkU2l6ZSA+IDApIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbn1cbmZ1bmN0aW9uIFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJDbGVhckFsZ29yaXRobXMoY29udHJvbGxlcikge1xuICAgIGNvbnRyb2xsZXIuX3B1bGxBbGdvcml0aG0gPSB1bmRlZmluZWQ7XG4gICAgY29udHJvbGxlci5fY2FuY2VsQWxnb3JpdGhtID0gdW5kZWZpbmVkO1xuICAgIGNvbnRyb2xsZXIuX3N0cmF0ZWd5U2l6ZUFsZ29yaXRobSA9IHVuZGVmaW5lZDtcbn1cbi8vIEEgY2xpZW50IG9mIFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXIgbWF5IHVzZSB0aGVzZSBmdW5jdGlvbnMgZGlyZWN0bHkgdG8gYnlwYXNzIHN0YXRlIGNoZWNrLlxuZnVuY3Rpb24gUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckNsb3NlKGNvbnRyb2xsZXIpIHtcbiAgICBpZiAoIVJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJDYW5DbG9zZU9yRW5xdWV1ZShjb250cm9sbGVyKSkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHN0cmVhbSA9IGNvbnRyb2xsZXIuX2NvbnRyb2xsZWRSZWFkYWJsZVN0cmVhbTtcbiAgICBjb250cm9sbGVyLl9jbG9zZVJlcXVlc3RlZCA9IHRydWU7XG4gICAgaWYgKGNvbnRyb2xsZXIuX3F1ZXVlLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICBSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyQ2xlYXJBbGdvcml0aG1zKGNvbnRyb2xsZXIpO1xuICAgICAgICBSZWFkYWJsZVN0cmVhbUNsb3NlKHN0cmVhbSk7XG4gICAgfVxufVxuZnVuY3Rpb24gUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckVucXVldWUoY29udHJvbGxlciwgY2h1bmspIHtcbiAgICBpZiAoIVJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJDYW5DbG9zZU9yRW5xdWV1ZShjb250cm9sbGVyKSkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHN0cmVhbSA9IGNvbnRyb2xsZXIuX2NvbnRyb2xsZWRSZWFkYWJsZVN0cmVhbTtcbiAgICBpZiAoSXNSZWFkYWJsZVN0cmVhbUxvY2tlZChzdHJlYW0pICYmIFJlYWRhYmxlU3RyZWFtR2V0TnVtUmVhZFJlcXVlc3RzKHN0cmVhbSkgPiAwKSB7XG4gICAgICAgIFJlYWRhYmxlU3RyZWFtRnVsZmlsbFJlYWRSZXF1ZXN0KHN0cmVhbSwgY2h1bmssIGZhbHNlKTtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIGxldCBjaHVua1NpemU7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjaHVua1NpemUgPSBjb250cm9sbGVyLl9zdHJhdGVneVNpemVBbGdvcml0aG0oY2h1bmspO1xuICAgICAgICB9XG4gICAgICAgIGNhdGNoIChjaHVua1NpemVFKSB7XG4gICAgICAgICAgICBSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyRXJyb3IoY29udHJvbGxlciwgY2h1bmtTaXplRSk7XG4gICAgICAgICAgICB0aHJvdyBjaHVua1NpemVFO1xuICAgICAgICB9XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBFbnF1ZXVlVmFsdWVXaXRoU2l6ZShjb250cm9sbGVyLCBjaHVuaywgY2h1bmtTaXplKTtcbiAgICAgICAgfVxuICAgICAgICBjYXRjaCAoZW5xdWV1ZUUpIHtcbiAgICAgICAgICAgIFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJFcnJvcihjb250cm9sbGVyLCBlbnF1ZXVlRSk7XG4gICAgICAgICAgICB0aHJvdyBlbnF1ZXVlRTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyQ2FsbFB1bGxJZk5lZWRlZChjb250cm9sbGVyKTtcbn1cbmZ1bmN0aW9uIFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJFcnJvcihjb250cm9sbGVyLCBlKSB7XG4gICAgY29uc3Qgc3RyZWFtID0gY29udHJvbGxlci5fY29udHJvbGxlZFJlYWRhYmxlU3RyZWFtO1xuICAgIGlmIChzdHJlYW0uX3N0YXRlICE9PSAncmVhZGFibGUnKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgUmVzZXRRdWV1ZShjb250cm9sbGVyKTtcbiAgICBSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyQ2xlYXJBbGdvcml0aG1zKGNvbnRyb2xsZXIpO1xuICAgIFJlYWRhYmxlU3RyZWFtRXJyb3Ioc3RyZWFtLCBlKTtcbn1cbmZ1bmN0aW9uIFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJHZXREZXNpcmVkU2l6ZShjb250cm9sbGVyKSB7XG4gICAgY29uc3Qgc3RhdGUgPSBjb250cm9sbGVyLl9jb250cm9sbGVkUmVhZGFibGVTdHJlYW0uX3N0YXRlO1xuICAgIGlmIChzdGF0ZSA9PT0gJ2Vycm9yZWQnKSB7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICBpZiAoc3RhdGUgPT09ICdjbG9zZWQnKSB7XG4gICAgICAgIHJldHVybiAwO1xuICAgIH1cbiAgICByZXR1cm4gY29udHJvbGxlci5fc3RyYXRlZ3lIV00gLSBjb250cm9sbGVyLl9xdWV1ZVRvdGFsU2l6ZTtcbn1cbi8vIFRoaXMgaXMgdXNlZCBpbiB0aGUgaW1wbGVtZW50YXRpb24gb2YgVHJhbnNmb3JtU3RyZWFtLlxuZnVuY3Rpb24gUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckhhc0JhY2twcmVzc3VyZShjb250cm9sbGVyKSB7XG4gICAgaWYgKFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJTaG91bGRDYWxsUHVsbChjb250cm9sbGVyKSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xufVxuZnVuY3Rpb24gUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckNhbkNsb3NlT3JFbnF1ZXVlKGNvbnRyb2xsZXIpIHtcbiAgICBjb25zdCBzdGF0ZSA9IGNvbnRyb2xsZXIuX2NvbnRyb2xsZWRSZWFkYWJsZVN0cmVhbS5fc3RhdGU7XG4gICAgaWYgKCFjb250cm9sbGVyLl9jbG9zZVJlcXVlc3RlZCAmJiBzdGF0ZSA9PT0gJ3JlYWRhYmxlJykge1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xufVxuZnVuY3Rpb24gU2V0VXBSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyKHN0cmVhbSwgY29udHJvbGxlciwgc3RhcnRBbGdvcml0aG0sIHB1bGxBbGdvcml0aG0sIGNhbmNlbEFsZ29yaXRobSwgaGlnaFdhdGVyTWFyaywgc2l6ZUFsZ29yaXRobSkge1xuICAgIGNvbnRyb2xsZXIuX2NvbnRyb2xsZWRSZWFkYWJsZVN0cmVhbSA9IHN0cmVhbTtcbiAgICBjb250cm9sbGVyLl9xdWV1ZSA9IHVuZGVmaW5lZDtcbiAgICBjb250cm9sbGVyLl9xdWV1ZVRvdGFsU2l6ZSA9IHVuZGVmaW5lZDtcbiAgICBSZXNldFF1ZXVlKGNvbnRyb2xsZXIpO1xuICAgIGNvbnRyb2xsZXIuX3N0YXJ0ZWQgPSBmYWxzZTtcbiAgICBjb250cm9sbGVyLl9jbG9zZVJlcXVlc3RlZCA9IGZhbHNlO1xuICAgIGNvbnRyb2xsZXIuX3B1bGxBZ2FpbiA9IGZhbHNlO1xuICAgIGNvbnRyb2xsZXIuX3B1bGxpbmcgPSBmYWxzZTtcbiAgICBjb250cm9sbGVyLl9zdHJhdGVneVNpemVBbGdvcml0aG0gPSBzaXplQWxnb3JpdGhtO1xuICAgIGNvbnRyb2xsZXIuX3N0cmF0ZWd5SFdNID0gaGlnaFdhdGVyTWFyaztcbiAgICBjb250cm9sbGVyLl9wdWxsQWxnb3JpdGhtID0gcHVsbEFsZ29yaXRobTtcbiAgICBjb250cm9sbGVyLl9jYW5jZWxBbGdvcml0aG0gPSBjYW5jZWxBbGdvcml0aG07XG4gICAgc3RyZWFtLl9yZWFkYWJsZVN0cmVhbUNvbnRyb2xsZXIgPSBjb250cm9sbGVyO1xuICAgIGNvbnN0IHN0YXJ0UmVzdWx0ID0gc3RhcnRBbGdvcml0aG0oKTtcbiAgICB1cG9uUHJvbWlzZShwcm9taXNlUmVzb2x2ZWRXaXRoKHN0YXJ0UmVzdWx0KSwgKCkgPT4ge1xuICAgICAgICBjb250cm9sbGVyLl9zdGFydGVkID0gdHJ1ZTtcbiAgICAgICAgUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckNhbGxQdWxsSWZOZWVkZWQoY29udHJvbGxlcik7XG4gICAgfSwgciA9PiB7XG4gICAgICAgIFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJFcnJvcihjb250cm9sbGVyLCByKTtcbiAgICB9KTtcbn1cbmZ1bmN0aW9uIFNldFVwUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckZyb21VbmRlcmx5aW5nU291cmNlKHN0cmVhbSwgdW5kZXJseWluZ1NvdXJjZSwgaGlnaFdhdGVyTWFyaywgc2l6ZUFsZ29yaXRobSkge1xuICAgIGNvbnN0IGNvbnRyb2xsZXIgPSBPYmplY3QuY3JlYXRlKFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXIucHJvdG90eXBlKTtcbiAgICBsZXQgc3RhcnRBbGdvcml0aG0gPSAoKSA9PiB1bmRlZmluZWQ7XG4gICAgbGV0IHB1bGxBbGdvcml0aG0gPSAoKSA9PiBwcm9taXNlUmVzb2x2ZWRXaXRoKHVuZGVmaW5lZCk7XG4gICAgbGV0IGNhbmNlbEFsZ29yaXRobSA9ICgpID0+IHByb21pc2VSZXNvbHZlZFdpdGgodW5kZWZpbmVkKTtcbiAgICBpZiAodW5kZXJseWluZ1NvdXJjZS5zdGFydCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHN0YXJ0QWxnb3JpdGhtID0gKCkgPT4gdW5kZXJseWluZ1NvdXJjZS5zdGFydChjb250cm9sbGVyKTtcbiAgICB9XG4gICAgaWYgKHVuZGVybHlpbmdTb3VyY2UucHVsbCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHB1bGxBbGdvcml0aG0gPSAoKSA9PiB1bmRlcmx5aW5nU291cmNlLnB1bGwoY29udHJvbGxlcik7XG4gICAgfVxuICAgIGlmICh1bmRlcmx5aW5nU291cmNlLmNhbmNlbCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGNhbmNlbEFsZ29yaXRobSA9IHJlYXNvbiA9PiB1bmRlcmx5aW5nU291cmNlLmNhbmNlbChyZWFzb24pO1xuICAgIH1cbiAgICBTZXRVcFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXIoc3RyZWFtLCBjb250cm9sbGVyLCBzdGFydEFsZ29yaXRobSwgcHVsbEFsZ29yaXRobSwgY2FuY2VsQWxnb3JpdGhtLCBoaWdoV2F0ZXJNYXJrLCBzaXplQWxnb3JpdGhtKTtcbn1cbi8vIEhlbHBlciBmdW5jdGlvbnMgZm9yIHRoZSBSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyLlxuZnVuY3Rpb24gZGVmYXVsdENvbnRyb2xsZXJCcmFuZENoZWNrRXhjZXB0aW9uKG5hbWUpIHtcbiAgICByZXR1cm4gbmV3IFR5cGVFcnJvcihgUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlci5wcm90b3R5cGUuJHtuYW1lfSBjYW4gb25seSBiZSB1c2VkIG9uIGEgUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlcmApO1xufVxuXG5mdW5jdGlvbiBSZWFkYWJsZVN0cmVhbVRlZShzdHJlYW0sIGNsb25lRm9yQnJhbmNoMikge1xuICAgIGNvbnN0IHJlYWRlciA9IEFjcXVpcmVSZWFkYWJsZVN0cmVhbURlZmF1bHRSZWFkZXIoc3RyZWFtKTtcbiAgICBsZXQgcmVhZGluZyA9IGZhbHNlO1xuICAgIGxldCBjYW5jZWxlZDEgPSBmYWxzZTtcbiAgICBsZXQgY2FuY2VsZWQyID0gZmFsc2U7XG4gICAgbGV0IHJlYXNvbjE7XG4gICAgbGV0IHJlYXNvbjI7XG4gICAgbGV0IGJyYW5jaDE7XG4gICAgbGV0IGJyYW5jaDI7XG4gICAgbGV0IHJlc29sdmVDYW5jZWxQcm9taXNlO1xuICAgIGNvbnN0IGNhbmNlbFByb21pc2UgPSBuZXdQcm9taXNlKHJlc29sdmUgPT4ge1xuICAgICAgICByZXNvbHZlQ2FuY2VsUHJvbWlzZSA9IHJlc29sdmU7XG4gICAgfSk7XG4gICAgZnVuY3Rpb24gcHVsbEFsZ29yaXRobSgpIHtcbiAgICAgICAgaWYgKHJlYWRpbmcpIHtcbiAgICAgICAgICAgIHJldHVybiBwcm9taXNlUmVzb2x2ZWRXaXRoKHVuZGVmaW5lZCk7XG4gICAgICAgIH1cbiAgICAgICAgcmVhZGluZyA9IHRydWU7XG4gICAgICAgIGNvbnN0IHJlYWRSZXF1ZXN0ID0ge1xuICAgICAgICAgICAgX2NodW5rU3RlcHM6IHZhbHVlID0+IHtcbiAgICAgICAgICAgICAgICAvLyBUaGlzIG5lZWRzIHRvIGJlIGRlbGF5ZWQgYSBtaWNyb3Rhc2sgYmVjYXVzZSBpdCB0YWtlcyBhdCBsZWFzdCBhIG1pY3JvdGFzayB0byBkZXRlY3QgZXJyb3JzICh1c2luZ1xuICAgICAgICAgICAgICAgIC8vIHJlYWRlci5fY2xvc2VkUHJvbWlzZSBiZWxvdyksIGFuZCB3ZSB3YW50IGVycm9ycyBpbiBzdHJlYW0gdG8gZXJyb3IgYm90aCBicmFuY2hlcyBpbW1lZGlhdGVseS4gV2UgY2Fubm90IGxldFxuICAgICAgICAgICAgICAgIC8vIHN1Y2Nlc3NmdWwgc3luY2hyb25vdXNseS1hdmFpbGFibGUgcmVhZHMgZ2V0IGFoZWFkIG9mIGFzeW5jaHJvbm91c2x5LWF2YWlsYWJsZSBlcnJvcnMuXG4gICAgICAgICAgICAgICAgcXVldWVNaWNyb3Rhc2soKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICByZWFkaW5nID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHZhbHVlMSA9IHZhbHVlO1xuICAgICAgICAgICAgICAgICAgICBjb25zdCB2YWx1ZTIgPSB2YWx1ZTtcbiAgICAgICAgICAgICAgICAgICAgLy8gVGhlcmUgaXMgbm8gd2F5IHRvIGFjY2VzcyB0aGUgY2xvbmluZyBjb2RlIHJpZ2h0IG5vdyBpbiB0aGUgcmVmZXJlbmNlIGltcGxlbWVudGF0aW9uLlxuICAgICAgICAgICAgICAgICAgICAvLyBJZiB3ZSBhZGQgb25lIHRoZW4gd2UnbGwgbmVlZCBhbiBpbXBsZW1lbnRhdGlvbiBmb3Igc2VyaWFsaXphYmxlIG9iamVjdHMuXG4gICAgICAgICAgICAgICAgICAgIC8vIGlmICghY2FuY2VsZWQyICYmIGNsb25lRm9yQnJhbmNoMikge1xuICAgICAgICAgICAgICAgICAgICAvLyAgIHZhbHVlMiA9IFN0cnVjdHVyZWREZXNlcmlhbGl6ZShTdHJ1Y3R1cmVkU2VyaWFsaXplKHZhbHVlMikpO1xuICAgICAgICAgICAgICAgICAgICAvLyB9XG4gICAgICAgICAgICAgICAgICAgIGlmICghY2FuY2VsZWQxKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyRW5xdWV1ZShicmFuY2gxLl9yZWFkYWJsZVN0cmVhbUNvbnRyb2xsZXIsIHZhbHVlMSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgaWYgKCFjYW5jZWxlZDIpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJFbnF1ZXVlKGJyYW5jaDIuX3JlYWRhYmxlU3RyZWFtQ29udHJvbGxlciwgdmFsdWUyKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICByZXNvbHZlQ2FuY2VsUHJvbWlzZSh1bmRlZmluZWQpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIF9jbG9zZVN0ZXBzOiAoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVhZGluZyA9IGZhbHNlO1xuICAgICAgICAgICAgICAgIGlmICghY2FuY2VsZWQxKSB7XG4gICAgICAgICAgICAgICAgICAgIFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJDbG9zZShicmFuY2gxLl9yZWFkYWJsZVN0cmVhbUNvbnRyb2xsZXIpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAoIWNhbmNlbGVkMikge1xuICAgICAgICAgICAgICAgICAgICBSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyQ2xvc2UoYnJhbmNoMi5fcmVhZGFibGVTdHJlYW1Db250cm9sbGVyKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgX2Vycm9yU3RlcHM6ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZWFkaW5nID0gZmFsc2U7XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgICAgIFJlYWRhYmxlU3RyZWFtRGVmYXVsdFJlYWRlclJlYWQocmVhZGVyLCByZWFkUmVxdWVzdCk7XG4gICAgICAgIHJldHVybiBwcm9taXNlUmVzb2x2ZWRXaXRoKHVuZGVmaW5lZCk7XG4gICAgfVxuICAgIGZ1bmN0aW9uIGNhbmNlbDFBbGdvcml0aG0ocmVhc29uKSB7XG4gICAgICAgIGNhbmNlbGVkMSA9IHRydWU7XG4gICAgICAgIHJlYXNvbjEgPSByZWFzb247XG4gICAgICAgIGlmIChjYW5jZWxlZDIpIHtcbiAgICAgICAgICAgIGNvbnN0IGNvbXBvc2l0ZVJlYXNvbiA9IENyZWF0ZUFycmF5RnJvbUxpc3QoW3JlYXNvbjEsIHJlYXNvbjJdKTtcbiAgICAgICAgICAgIGNvbnN0IGNhbmNlbFJlc3VsdCA9IFJlYWRhYmxlU3RyZWFtQ2FuY2VsKHN0cmVhbSwgY29tcG9zaXRlUmVhc29uKTtcbiAgICAgICAgICAgIHJlc29sdmVDYW5jZWxQcm9taXNlKGNhbmNlbFJlc3VsdCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGNhbmNlbFByb21pc2U7XG4gICAgfVxuICAgIGZ1bmN0aW9uIGNhbmNlbDJBbGdvcml0aG0ocmVhc29uKSB7XG4gICAgICAgIGNhbmNlbGVkMiA9IHRydWU7XG4gICAgICAgIHJlYXNvbjIgPSByZWFzb247XG4gICAgICAgIGlmIChjYW5jZWxlZDEpIHtcbiAgICAgICAgICAgIGNvbnN0IGNvbXBvc2l0ZVJlYXNvbiA9IENyZWF0ZUFycmF5RnJvbUxpc3QoW3JlYXNvbjEsIHJlYXNvbjJdKTtcbiAgICAgICAgICAgIGNvbnN0IGNhbmNlbFJlc3VsdCA9IFJlYWRhYmxlU3RyZWFtQ2FuY2VsKHN0cmVhbSwgY29tcG9zaXRlUmVhc29uKTtcbiAgICAgICAgICAgIHJlc29sdmVDYW5jZWxQcm9taXNlKGNhbmNlbFJlc3VsdCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGNhbmNlbFByb21pc2U7XG4gICAgfVxuICAgIGZ1bmN0aW9uIHN0YXJ0QWxnb3JpdGhtKCkge1xuICAgICAgICAvLyBkbyBub3RoaW5nXG4gICAgfVxuICAgIGJyYW5jaDEgPSBDcmVhdGVSZWFkYWJsZVN0cmVhbShzdGFydEFsZ29yaXRobSwgcHVsbEFsZ29yaXRobSwgY2FuY2VsMUFsZ29yaXRobSk7XG4gICAgYnJhbmNoMiA9IENyZWF0ZVJlYWRhYmxlU3RyZWFtKHN0YXJ0QWxnb3JpdGhtLCBwdWxsQWxnb3JpdGhtLCBjYW5jZWwyQWxnb3JpdGhtKTtcbiAgICB1cG9uUmVqZWN0aW9uKHJlYWRlci5fY2xvc2VkUHJvbWlzZSwgKHIpID0+IHtcbiAgICAgICAgUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckVycm9yKGJyYW5jaDEuX3JlYWRhYmxlU3RyZWFtQ29udHJvbGxlciwgcik7XG4gICAgICAgIFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJFcnJvcihicmFuY2gyLl9yZWFkYWJsZVN0cmVhbUNvbnRyb2xsZXIsIHIpO1xuICAgICAgICByZXNvbHZlQ2FuY2VsUHJvbWlzZSh1bmRlZmluZWQpO1xuICAgIH0pO1xuICAgIHJldHVybiBbYnJhbmNoMSwgYnJhbmNoMl07XG59XG5cbmZ1bmN0aW9uIGNvbnZlcnRVbmRlcmx5aW5nRGVmYXVsdE9yQnl0ZVNvdXJjZShzb3VyY2UsIGNvbnRleHQpIHtcbiAgICBhc3NlcnREaWN0aW9uYXJ5KHNvdXJjZSwgY29udGV4dCk7XG4gICAgY29uc3Qgb3JpZ2luYWwgPSBzb3VyY2U7XG4gICAgY29uc3QgYXV0b0FsbG9jYXRlQ2h1bmtTaXplID0gb3JpZ2luYWwgPT09IG51bGwgfHwgb3JpZ2luYWwgPT09IHZvaWQgMCA/IHZvaWQgMCA6IG9yaWdpbmFsLmF1dG9BbGxvY2F0ZUNodW5rU2l6ZTtcbiAgICBjb25zdCBjYW5jZWwgPSBvcmlnaW5hbCA9PT0gbnVsbCB8fCBvcmlnaW5hbCA9PT0gdm9pZCAwID8gdm9pZCAwIDogb3JpZ2luYWwuY2FuY2VsO1xuICAgIGNvbnN0IHB1bGwgPSBvcmlnaW5hbCA9PT0gbnVsbCB8fCBvcmlnaW5hbCA9PT0gdm9pZCAwID8gdm9pZCAwIDogb3JpZ2luYWwucHVsbDtcbiAgICBjb25zdCBzdGFydCA9IG9yaWdpbmFsID09PSBudWxsIHx8IG9yaWdpbmFsID09PSB2b2lkIDAgPyB2b2lkIDAgOiBvcmlnaW5hbC5zdGFydDtcbiAgICBjb25zdCB0eXBlID0gb3JpZ2luYWwgPT09IG51bGwgfHwgb3JpZ2luYWwgPT09IHZvaWQgMCA/IHZvaWQgMCA6IG9yaWdpbmFsLnR5cGU7XG4gICAgcmV0dXJuIHtcbiAgICAgICAgYXV0b0FsbG9jYXRlQ2h1bmtTaXplOiBhdXRvQWxsb2NhdGVDaHVua1NpemUgPT09IHVuZGVmaW5lZCA/XG4gICAgICAgICAgICB1bmRlZmluZWQgOlxuICAgICAgICAgICAgY29udmVydFVuc2lnbmVkTG9uZ0xvbmdXaXRoRW5mb3JjZVJhbmdlKGF1dG9BbGxvY2F0ZUNodW5rU2l6ZSwgYCR7Y29udGV4dH0gaGFzIG1lbWJlciAnYXV0b0FsbG9jYXRlQ2h1bmtTaXplJyB0aGF0YCksXG4gICAgICAgIGNhbmNlbDogY2FuY2VsID09PSB1bmRlZmluZWQgP1xuICAgICAgICAgICAgdW5kZWZpbmVkIDpcbiAgICAgICAgICAgIGNvbnZlcnRVbmRlcmx5aW5nU291cmNlQ2FuY2VsQ2FsbGJhY2soY2FuY2VsLCBvcmlnaW5hbCwgYCR7Y29udGV4dH0gaGFzIG1lbWJlciAnY2FuY2VsJyB0aGF0YCksXG4gICAgICAgIHB1bGw6IHB1bGwgPT09IHVuZGVmaW5lZCA/XG4gICAgICAgICAgICB1bmRlZmluZWQgOlxuICAgICAgICAgICAgY29udmVydFVuZGVybHlpbmdTb3VyY2VQdWxsQ2FsbGJhY2socHVsbCwgb3JpZ2luYWwsIGAke2NvbnRleHR9IGhhcyBtZW1iZXIgJ3B1bGwnIHRoYXRgKSxcbiAgICAgICAgc3RhcnQ6IHN0YXJ0ID09PSB1bmRlZmluZWQgP1xuICAgICAgICAgICAgdW5kZWZpbmVkIDpcbiAgICAgICAgICAgIGNvbnZlcnRVbmRlcmx5aW5nU291cmNlU3RhcnRDYWxsYmFjayhzdGFydCwgb3JpZ2luYWwsIGAke2NvbnRleHR9IGhhcyBtZW1iZXIgJ3N0YXJ0JyB0aGF0YCksXG4gICAgICAgIHR5cGU6IHR5cGUgPT09IHVuZGVmaW5lZCA/IHVuZGVmaW5lZCA6IGNvbnZlcnRSZWFkYWJsZVN0cmVhbVR5cGUodHlwZSwgYCR7Y29udGV4dH0gaGFzIG1lbWJlciAndHlwZScgdGhhdGApXG4gICAgfTtcbn1cbmZ1bmN0aW9uIGNvbnZlcnRVbmRlcmx5aW5nU291cmNlQ2FuY2VsQ2FsbGJhY2soZm4sIG9yaWdpbmFsLCBjb250ZXh0KSB7XG4gICAgYXNzZXJ0RnVuY3Rpb24oZm4sIGNvbnRleHQpO1xuICAgIHJldHVybiAocmVhc29uKSA9PiBwcm9taXNlQ2FsbChmbiwgb3JpZ2luYWwsIFtyZWFzb25dKTtcbn1cbmZ1bmN0aW9uIGNvbnZlcnRVbmRlcmx5aW5nU291cmNlUHVsbENhbGxiYWNrKGZuLCBvcmlnaW5hbCwgY29udGV4dCkge1xuICAgIGFzc2VydEZ1bmN0aW9uKGZuLCBjb250ZXh0KTtcbiAgICByZXR1cm4gKGNvbnRyb2xsZXIpID0+IHByb21pc2VDYWxsKGZuLCBvcmlnaW5hbCwgW2NvbnRyb2xsZXJdKTtcbn1cbmZ1bmN0aW9uIGNvbnZlcnRVbmRlcmx5aW5nU291cmNlU3RhcnRDYWxsYmFjayhmbiwgb3JpZ2luYWwsIGNvbnRleHQpIHtcbiAgICBhc3NlcnRGdW5jdGlvbihmbiwgY29udGV4dCk7XG4gICAgcmV0dXJuIChjb250cm9sbGVyKSA9PiByZWZsZWN0Q2FsbChmbiwgb3JpZ2luYWwsIFtjb250cm9sbGVyXSk7XG59XG5mdW5jdGlvbiBjb252ZXJ0UmVhZGFibGVTdHJlYW1UeXBlKHR5cGUsIGNvbnRleHQpIHtcbiAgICB0eXBlID0gYCR7dHlwZX1gO1xuICAgIGlmICh0eXBlICE9PSAnYnl0ZXMnKSB7XG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoYCR7Y29udGV4dH0gJyR7dHlwZX0nIGlzIG5vdCBhIHZhbGlkIGVudW1lcmF0aW9uIHZhbHVlIGZvciBSZWFkYWJsZVN0cmVhbVR5cGVgKTtcbiAgICB9XG4gICAgcmV0dXJuIHR5cGU7XG59XG5cbmZ1bmN0aW9uIGNvbnZlcnRSZWFkZXJPcHRpb25zKG9wdGlvbnMsIGNvbnRleHQpIHtcbiAgICBhc3NlcnREaWN0aW9uYXJ5KG9wdGlvbnMsIGNvbnRleHQpO1xuICAgIGNvbnN0IG1vZGUgPSBvcHRpb25zID09PSBudWxsIHx8IG9wdGlvbnMgPT09IHZvaWQgMCA/IHZvaWQgMCA6IG9wdGlvbnMubW9kZTtcbiAgICByZXR1cm4ge1xuICAgICAgICBtb2RlOiBtb2RlID09PSB1bmRlZmluZWQgPyB1bmRlZmluZWQgOiBjb252ZXJ0UmVhZGFibGVTdHJlYW1SZWFkZXJNb2RlKG1vZGUsIGAke2NvbnRleHR9IGhhcyBtZW1iZXIgJ21vZGUnIHRoYXRgKVxuICAgIH07XG59XG5mdW5jdGlvbiBjb252ZXJ0UmVhZGFibGVTdHJlYW1SZWFkZXJNb2RlKG1vZGUsIGNvbnRleHQpIHtcbiAgICBtb2RlID0gYCR7bW9kZX1gO1xuICAgIGlmIChtb2RlICE9PSAnYnlvYicpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihgJHtjb250ZXh0fSAnJHttb2RlfScgaXMgbm90IGEgdmFsaWQgZW51bWVyYXRpb24gdmFsdWUgZm9yIFJlYWRhYmxlU3RyZWFtUmVhZGVyTW9kZWApO1xuICAgIH1cbiAgICByZXR1cm4gbW9kZTtcbn1cblxuZnVuY3Rpb24gY29udmVydEl0ZXJhdG9yT3B0aW9ucyhvcHRpb25zLCBjb250ZXh0KSB7XG4gICAgYXNzZXJ0RGljdGlvbmFyeShvcHRpb25zLCBjb250ZXh0KTtcbiAgICBjb25zdCBwcmV2ZW50Q2FuY2VsID0gb3B0aW9ucyA9PT0gbnVsbCB8fCBvcHRpb25zID09PSB2b2lkIDAgPyB2b2lkIDAgOiBvcHRpb25zLnByZXZlbnRDYW5jZWw7XG4gICAgcmV0dXJuIHsgcHJldmVudENhbmNlbDogQm9vbGVhbihwcmV2ZW50Q2FuY2VsKSB9O1xufVxuXG5mdW5jdGlvbiBjb252ZXJ0UGlwZU9wdGlvbnMob3B0aW9ucywgY29udGV4dCkge1xuICAgIGFzc2VydERpY3Rpb25hcnkob3B0aW9ucywgY29udGV4dCk7XG4gICAgY29uc3QgcHJldmVudEFib3J0ID0gb3B0aW9ucyA9PT0gbnVsbCB8fCBvcHRpb25zID09PSB2b2lkIDAgPyB2b2lkIDAgOiBvcHRpb25zLnByZXZlbnRBYm9ydDtcbiAgICBjb25zdCBwcmV2ZW50Q2FuY2VsID0gb3B0aW9ucyA9PT0gbnVsbCB8fCBvcHRpb25zID09PSB2b2lkIDAgPyB2b2lkIDAgOiBvcHRpb25zLnByZXZlbnRDYW5jZWw7XG4gICAgY29uc3QgcHJldmVudENsb3NlID0gb3B0aW9ucyA9PT0gbnVsbCB8fCBvcHRpb25zID09PSB2b2lkIDAgPyB2b2lkIDAgOiBvcHRpb25zLnByZXZlbnRDbG9zZTtcbiAgICBjb25zdCBzaWduYWwgPSBvcHRpb25zID09PSBudWxsIHx8IG9wdGlvbnMgPT09IHZvaWQgMCA/IHZvaWQgMCA6IG9wdGlvbnMuc2lnbmFsO1xuICAgIGlmIChzaWduYWwgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBhc3NlcnRBYm9ydFNpZ25hbChzaWduYWwsIGAke2NvbnRleHR9IGhhcyBtZW1iZXIgJ3NpZ25hbCcgdGhhdGApO1xuICAgIH1cbiAgICByZXR1cm4ge1xuICAgICAgICBwcmV2ZW50QWJvcnQ6IEJvb2xlYW4ocHJldmVudEFib3J0KSxcbiAgICAgICAgcHJldmVudENhbmNlbDogQm9vbGVhbihwcmV2ZW50Q2FuY2VsKSxcbiAgICAgICAgcHJldmVudENsb3NlOiBCb29sZWFuKHByZXZlbnRDbG9zZSksXG4gICAgICAgIHNpZ25hbFxuICAgIH07XG59XG5mdW5jdGlvbiBhc3NlcnRBYm9ydFNpZ25hbChzaWduYWwsIGNvbnRleHQpIHtcbiAgICBpZiAoIWlzQWJvcnRTaWduYWwoc2lnbmFsKSkge1xuICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKGAke2NvbnRleHR9IGlzIG5vdCBhbiBBYm9ydFNpZ25hbC5gKTtcbiAgICB9XG59XG5cbmZ1bmN0aW9uIGNvbnZlcnRSZWFkYWJsZVdyaXRhYmxlUGFpcihwYWlyLCBjb250ZXh0KSB7XG4gICAgYXNzZXJ0RGljdGlvbmFyeShwYWlyLCBjb250ZXh0KTtcbiAgICBjb25zdCByZWFkYWJsZSA9IHBhaXIgPT09IG51bGwgfHwgcGFpciA9PT0gdm9pZCAwID8gdm9pZCAwIDogcGFpci5yZWFkYWJsZTtcbiAgICBhc3NlcnRSZXF1aXJlZEZpZWxkKHJlYWRhYmxlLCAncmVhZGFibGUnLCAnUmVhZGFibGVXcml0YWJsZVBhaXInKTtcbiAgICBhc3NlcnRSZWFkYWJsZVN0cmVhbShyZWFkYWJsZSwgYCR7Y29udGV4dH0gaGFzIG1lbWJlciAncmVhZGFibGUnIHRoYXRgKTtcbiAgICBjb25zdCB3cml0YWJsZSA9IHBhaXIgPT09IG51bGwgfHwgcGFpciA9PT0gdm9pZCAwID8gdm9pZCAwIDogcGFpci53cml0YWJsZTtcbiAgICBhc3NlcnRSZXF1aXJlZEZpZWxkKHdyaXRhYmxlLCAnd3JpdGFibGUnLCAnUmVhZGFibGVXcml0YWJsZVBhaXInKTtcbiAgICBhc3NlcnRXcml0YWJsZVN0cmVhbSh3cml0YWJsZSwgYCR7Y29udGV4dH0gaGFzIG1lbWJlciAnd3JpdGFibGUnIHRoYXRgKTtcbiAgICByZXR1cm4geyByZWFkYWJsZSwgd3JpdGFibGUgfTtcbn1cblxuLyoqXG4gKiBBIHJlYWRhYmxlIHN0cmVhbSByZXByZXNlbnRzIGEgc291cmNlIG9mIGRhdGEsIGZyb20gd2hpY2ggeW91IGNhbiByZWFkLlxuICpcbiAqIEBwdWJsaWNcbiAqL1xuY2xhc3MgUmVhZGFibGVTdHJlYW0ge1xuICAgIGNvbnN0cnVjdG9yKHJhd1VuZGVybHlpbmdTb3VyY2UgPSB7fSwgcmF3U3RyYXRlZ3kgPSB7fSkge1xuICAgICAgICBpZiAocmF3VW5kZXJseWluZ1NvdXJjZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICByYXdVbmRlcmx5aW5nU291cmNlID0gbnVsbDtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGFzc2VydE9iamVjdChyYXdVbmRlcmx5aW5nU291cmNlLCAnRmlyc3QgcGFyYW1ldGVyJyk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3Qgc3RyYXRlZ3kgPSBjb252ZXJ0UXVldWluZ1N0cmF0ZWd5KHJhd1N0cmF0ZWd5LCAnU2Vjb25kIHBhcmFtZXRlcicpO1xuICAgICAgICBjb25zdCB1bmRlcmx5aW5nU291cmNlID0gY29udmVydFVuZGVybHlpbmdEZWZhdWx0T3JCeXRlU291cmNlKHJhd1VuZGVybHlpbmdTb3VyY2UsICdGaXJzdCBwYXJhbWV0ZXInKTtcbiAgICAgICAgSW5pdGlhbGl6ZVJlYWRhYmxlU3RyZWFtKHRoaXMpO1xuICAgICAgICBpZiAodW5kZXJseWluZ1NvdXJjZS50eXBlID09PSAnYnl0ZXMnKSB7XG4gICAgICAgICAgICBpZiAoc3RyYXRlZ3kuc2l6ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ1RoZSBzdHJhdGVneSBmb3IgYSBieXRlIHN0cmVhbSBjYW5ub3QgaGF2ZSBhIHNpemUgZnVuY3Rpb24nKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IGhpZ2hXYXRlck1hcmsgPSBFeHRyYWN0SGlnaFdhdGVyTWFyayhzdHJhdGVneSwgMCk7XG4gICAgICAgICAgICBTZXRVcFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXJGcm9tVW5kZXJseWluZ1NvdXJjZSh0aGlzLCB1bmRlcmx5aW5nU291cmNlLCBoaWdoV2F0ZXJNYXJrKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGNvbnN0IHNpemVBbGdvcml0aG0gPSBFeHRyYWN0U2l6ZUFsZ29yaXRobShzdHJhdGVneSk7XG4gICAgICAgICAgICBjb25zdCBoaWdoV2F0ZXJNYXJrID0gRXh0cmFjdEhpZ2hXYXRlck1hcmsoc3RyYXRlZ3ksIDEpO1xuICAgICAgICAgICAgU2V0VXBSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyRnJvbVVuZGVybHlpbmdTb3VyY2UodGhpcywgdW5kZXJseWluZ1NvdXJjZSwgaGlnaFdhdGVyTWFyaywgc2l6ZUFsZ29yaXRobSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogV2hldGhlciBvciBub3QgdGhlIHJlYWRhYmxlIHN0cmVhbSBpcyBsb2NrZWQgdG8gYSB7QGxpbmsgUmVhZGFibGVTdHJlYW1EZWZhdWx0UmVhZGVyIHwgcmVhZGVyfS5cbiAgICAgKi9cbiAgICBnZXQgbG9ja2VkKCkge1xuICAgICAgICBpZiAoIUlzUmVhZGFibGVTdHJlYW0odGhpcykpIHtcbiAgICAgICAgICAgIHRocm93IHN0cmVhbUJyYW5kQ2hlY2tFeGNlcHRpb24kMSgnbG9ja2VkJyk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIElzUmVhZGFibGVTdHJlYW1Mb2NrZWQodGhpcyk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENhbmNlbHMgdGhlIHN0cmVhbSwgc2lnbmFsaW5nIGEgbG9zcyBvZiBpbnRlcmVzdCBpbiB0aGUgc3RyZWFtIGJ5IGEgY29uc3VtZXIuXG4gICAgICpcbiAgICAgKiBUaGUgc3VwcGxpZWQgYHJlYXNvbmAgYXJndW1lbnQgd2lsbCBiZSBnaXZlbiB0byB0aGUgdW5kZXJseWluZyBzb3VyY2UncyB7QGxpbmsgVW5kZXJseWluZ1NvdXJjZS5jYW5jZWwgfCBjYW5jZWwoKX1cbiAgICAgKiBtZXRob2QsIHdoaWNoIG1pZ2h0IG9yIG1pZ2h0IG5vdCB1c2UgaXQuXG4gICAgICovXG4gICAgY2FuY2VsKHJlYXNvbiA9IHVuZGVmaW5lZCkge1xuICAgICAgICBpZiAoIUlzUmVhZGFibGVTdHJlYW0odGhpcykpIHtcbiAgICAgICAgICAgIHJldHVybiBwcm9taXNlUmVqZWN0ZWRXaXRoKHN0cmVhbUJyYW5kQ2hlY2tFeGNlcHRpb24kMSgnY2FuY2VsJykpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChJc1JlYWRhYmxlU3RyZWFtTG9ja2VkKHRoaXMpKSB7XG4gICAgICAgICAgICByZXR1cm4gcHJvbWlzZVJlamVjdGVkV2l0aChuZXcgVHlwZUVycm9yKCdDYW5ub3QgY2FuY2VsIGEgc3RyZWFtIHRoYXQgYWxyZWFkeSBoYXMgYSByZWFkZXInKSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIFJlYWRhYmxlU3RyZWFtQ2FuY2VsKHRoaXMsIHJlYXNvbik7XG4gICAgfVxuICAgIGdldFJlYWRlcihyYXdPcHRpb25zID0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGlmICghSXNSZWFkYWJsZVN0cmVhbSh0aGlzKSkge1xuICAgICAgICAgICAgdGhyb3cgc3RyZWFtQnJhbmRDaGVja0V4Y2VwdGlvbiQxKCdnZXRSZWFkZXInKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBvcHRpb25zID0gY29udmVydFJlYWRlck9wdGlvbnMocmF3T3B0aW9ucywgJ0ZpcnN0IHBhcmFtZXRlcicpO1xuICAgICAgICBpZiAob3B0aW9ucy5tb2RlID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHJldHVybiBBY3F1aXJlUmVhZGFibGVTdHJlYW1EZWZhdWx0UmVhZGVyKHRoaXMpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBBY3F1aXJlUmVhZGFibGVTdHJlYW1CWU9CUmVhZGVyKHRoaXMpO1xuICAgIH1cbiAgICBwaXBlVGhyb3VnaChyYXdUcmFuc2Zvcm0sIHJhd09wdGlvbnMgPSB7fSkge1xuICAgICAgICBpZiAoIUlzUmVhZGFibGVTdHJlYW0odGhpcykpIHtcbiAgICAgICAgICAgIHRocm93IHN0cmVhbUJyYW5kQ2hlY2tFeGNlcHRpb24kMSgncGlwZVRocm91Z2gnKTtcbiAgICAgICAgfVxuICAgICAgICBhc3NlcnRSZXF1aXJlZEFyZ3VtZW50KHJhd1RyYW5zZm9ybSwgMSwgJ3BpcGVUaHJvdWdoJyk7XG4gICAgICAgIGNvbnN0IHRyYW5zZm9ybSA9IGNvbnZlcnRSZWFkYWJsZVdyaXRhYmxlUGFpcihyYXdUcmFuc2Zvcm0sICdGaXJzdCBwYXJhbWV0ZXInKTtcbiAgICAgICAgY29uc3Qgb3B0aW9ucyA9IGNvbnZlcnRQaXBlT3B0aW9ucyhyYXdPcHRpb25zLCAnU2Vjb25kIHBhcmFtZXRlcicpO1xuICAgICAgICBpZiAoSXNSZWFkYWJsZVN0cmVhbUxvY2tlZCh0aGlzKSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignUmVhZGFibGVTdHJlYW0ucHJvdG90eXBlLnBpcGVUaHJvdWdoIGNhbm5vdCBiZSB1c2VkIG9uIGEgbG9ja2VkIFJlYWRhYmxlU3RyZWFtJyk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKElzV3JpdGFibGVTdHJlYW1Mb2NrZWQodHJhbnNmb3JtLndyaXRhYmxlKSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignUmVhZGFibGVTdHJlYW0ucHJvdG90eXBlLnBpcGVUaHJvdWdoIGNhbm5vdCBiZSB1c2VkIG9uIGEgbG9ja2VkIFdyaXRhYmxlU3RyZWFtJyk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgcHJvbWlzZSA9IFJlYWRhYmxlU3RyZWFtUGlwZVRvKHRoaXMsIHRyYW5zZm9ybS53cml0YWJsZSwgb3B0aW9ucy5wcmV2ZW50Q2xvc2UsIG9wdGlvbnMucHJldmVudEFib3J0LCBvcHRpb25zLnByZXZlbnRDYW5jZWwsIG9wdGlvbnMuc2lnbmFsKTtcbiAgICAgICAgc2V0UHJvbWlzZUlzSGFuZGxlZFRvVHJ1ZShwcm9taXNlKTtcbiAgICAgICAgcmV0dXJuIHRyYW5zZm9ybS5yZWFkYWJsZTtcbiAgICB9XG4gICAgcGlwZVRvKGRlc3RpbmF0aW9uLCByYXdPcHRpb25zID0ge30pIHtcbiAgICAgICAgaWYgKCFJc1JlYWRhYmxlU3RyZWFtKHRoaXMpKSB7XG4gICAgICAgICAgICByZXR1cm4gcHJvbWlzZVJlamVjdGVkV2l0aChzdHJlYW1CcmFuZENoZWNrRXhjZXB0aW9uJDEoJ3BpcGVUbycpKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoZGVzdGluYXRpb24gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgcmV0dXJuIHByb21pc2VSZWplY3RlZFdpdGgoYFBhcmFtZXRlciAxIGlzIHJlcXVpcmVkIGluICdwaXBlVG8nLmApO1xuICAgICAgICB9XG4gICAgICAgIGlmICghSXNXcml0YWJsZVN0cmVhbShkZXN0aW5hdGlvbikpIHtcbiAgICAgICAgICAgIHJldHVybiBwcm9taXNlUmVqZWN0ZWRXaXRoKG5ldyBUeXBlRXJyb3IoYFJlYWRhYmxlU3RyZWFtLnByb3RvdHlwZS5waXBlVG8ncyBmaXJzdCBhcmd1bWVudCBtdXN0IGJlIGEgV3JpdGFibGVTdHJlYW1gKSk7XG4gICAgICAgIH1cbiAgICAgICAgbGV0IG9wdGlvbnM7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBvcHRpb25zID0gY29udmVydFBpcGVPcHRpb25zKHJhd09wdGlvbnMsICdTZWNvbmQgcGFyYW1ldGVyJyk7XG4gICAgICAgIH1cbiAgICAgICAgY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIHJldHVybiBwcm9taXNlUmVqZWN0ZWRXaXRoKGUpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChJc1JlYWRhYmxlU3RyZWFtTG9ja2VkKHRoaXMpKSB7XG4gICAgICAgICAgICByZXR1cm4gcHJvbWlzZVJlamVjdGVkV2l0aChuZXcgVHlwZUVycm9yKCdSZWFkYWJsZVN0cmVhbS5wcm90b3R5cGUucGlwZVRvIGNhbm5vdCBiZSB1c2VkIG9uIGEgbG9ja2VkIFJlYWRhYmxlU3RyZWFtJykpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChJc1dyaXRhYmxlU3RyZWFtTG9ja2VkKGRlc3RpbmF0aW9uKSkge1xuICAgICAgICAgICAgcmV0dXJuIHByb21pc2VSZWplY3RlZFdpdGgobmV3IFR5cGVFcnJvcignUmVhZGFibGVTdHJlYW0ucHJvdG90eXBlLnBpcGVUbyBjYW5ub3QgYmUgdXNlZCBvbiBhIGxvY2tlZCBXcml0YWJsZVN0cmVhbScpKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gUmVhZGFibGVTdHJlYW1QaXBlVG8odGhpcywgZGVzdGluYXRpb24sIG9wdGlvbnMucHJldmVudENsb3NlLCBvcHRpb25zLnByZXZlbnRBYm9ydCwgb3B0aW9ucy5wcmV2ZW50Q2FuY2VsLCBvcHRpb25zLnNpZ25hbCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFRlZXMgdGhpcyByZWFkYWJsZSBzdHJlYW0sIHJldHVybmluZyBhIHR3by1lbGVtZW50IGFycmF5IGNvbnRhaW5pbmcgdGhlIHR3byByZXN1bHRpbmcgYnJhbmNoZXMgYXNcbiAgICAgKiBuZXcge0BsaW5rIFJlYWRhYmxlU3RyZWFtfSBpbnN0YW5jZXMuXG4gICAgICpcbiAgICAgKiBUZWVpbmcgYSBzdHJlYW0gd2lsbCBsb2NrIGl0LCBwcmV2ZW50aW5nIGFueSBvdGhlciBjb25zdW1lciBmcm9tIGFjcXVpcmluZyBhIHJlYWRlci5cbiAgICAgKiBUbyBjYW5jZWwgdGhlIHN0cmVhbSwgY2FuY2VsIGJvdGggb2YgdGhlIHJlc3VsdGluZyBicmFuY2hlczsgYSBjb21wb3NpdGUgY2FuY2VsbGF0aW9uIHJlYXNvbiB3aWxsIHRoZW4gYmVcbiAgICAgKiBwcm9wYWdhdGVkIHRvIHRoZSBzdHJlYW0ncyB1bmRlcmx5aW5nIHNvdXJjZS5cbiAgICAgKlxuICAgICAqIE5vdGUgdGhhdCB0aGUgY2h1bmtzIHNlZW4gaW4gZWFjaCBicmFuY2ggd2lsbCBiZSB0aGUgc2FtZSBvYmplY3QuIElmIHRoZSBjaHVua3MgYXJlIG5vdCBpbW11dGFibGUsXG4gICAgICogdGhpcyBjb3VsZCBhbGxvdyBpbnRlcmZlcmVuY2UgYmV0d2VlbiB0aGUgdHdvIGJyYW5jaGVzLlxuICAgICAqL1xuICAgIHRlZSgpIHtcbiAgICAgICAgaWYgKCFJc1JlYWRhYmxlU3RyZWFtKHRoaXMpKSB7XG4gICAgICAgICAgICB0aHJvdyBzdHJlYW1CcmFuZENoZWNrRXhjZXB0aW9uJDEoJ3RlZScpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGJyYW5jaGVzID0gUmVhZGFibGVTdHJlYW1UZWUodGhpcyk7XG4gICAgICAgIHJldHVybiBDcmVhdGVBcnJheUZyb21MaXN0KGJyYW5jaGVzKTtcbiAgICB9XG4gICAgdmFsdWVzKHJhd09wdGlvbnMgPSB1bmRlZmluZWQpIHtcbiAgICAgICAgaWYgKCFJc1JlYWRhYmxlU3RyZWFtKHRoaXMpKSB7XG4gICAgICAgICAgICB0aHJvdyBzdHJlYW1CcmFuZENoZWNrRXhjZXB0aW9uJDEoJ3ZhbHVlcycpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IG9wdGlvbnMgPSBjb252ZXJ0SXRlcmF0b3JPcHRpb25zKHJhd09wdGlvbnMsICdGaXJzdCBwYXJhbWV0ZXInKTtcbiAgICAgICAgcmV0dXJuIEFjcXVpcmVSZWFkYWJsZVN0cmVhbUFzeW5jSXRlcmF0b3IodGhpcywgb3B0aW9ucy5wcmV2ZW50Q2FuY2VsKTtcbiAgICB9XG59XG5PYmplY3QuZGVmaW5lUHJvcGVydGllcyhSZWFkYWJsZVN0cmVhbS5wcm90b3R5cGUsIHtcbiAgICBjYW5jZWw6IHsgZW51bWVyYWJsZTogdHJ1ZSB9LFxuICAgIGdldFJlYWRlcjogeyBlbnVtZXJhYmxlOiB0cnVlIH0sXG4gICAgcGlwZVRocm91Z2g6IHsgZW51bWVyYWJsZTogdHJ1ZSB9LFxuICAgIHBpcGVUbzogeyBlbnVtZXJhYmxlOiB0cnVlIH0sXG4gICAgdGVlOiB7IGVudW1lcmFibGU6IHRydWUgfSxcbiAgICB2YWx1ZXM6IHsgZW51bWVyYWJsZTogdHJ1ZSB9LFxuICAgIGxvY2tlZDogeyBlbnVtZXJhYmxlOiB0cnVlIH1cbn0pO1xuaWYgKHR5cGVvZiBTeW1ib2xQb2x5ZmlsbC50b1N0cmluZ1RhZyA9PT0gJ3N5bWJvbCcpIHtcbiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkoUmVhZGFibGVTdHJlYW0ucHJvdG90eXBlLCBTeW1ib2xQb2x5ZmlsbC50b1N0cmluZ1RhZywge1xuICAgICAgICB2YWx1ZTogJ1JlYWRhYmxlU3RyZWFtJyxcbiAgICAgICAgY29uZmlndXJhYmxlOiB0cnVlXG4gICAgfSk7XG59XG5pZiAodHlwZW9mIFN5bWJvbFBvbHlmaWxsLmFzeW5jSXRlcmF0b3IgPT09ICdzeW1ib2wnKSB7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KFJlYWRhYmxlU3RyZWFtLnByb3RvdHlwZSwgU3ltYm9sUG9seWZpbGwuYXN5bmNJdGVyYXRvciwge1xuICAgICAgICB2YWx1ZTogUmVhZGFibGVTdHJlYW0ucHJvdG90eXBlLnZhbHVlcyxcbiAgICAgICAgd3JpdGFibGU6IHRydWUsXG4gICAgICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZVxuICAgIH0pO1xufVxuLy8gQWJzdHJhY3Qgb3BlcmF0aW9ucyBmb3IgdGhlIFJlYWRhYmxlU3RyZWFtLlxuLy8gVGhyb3dzIGlmIGFuZCBvbmx5IGlmIHN0YXJ0QWxnb3JpdGhtIHRocm93cy5cbmZ1bmN0aW9uIENyZWF0ZVJlYWRhYmxlU3RyZWFtKHN0YXJ0QWxnb3JpdGhtLCBwdWxsQWxnb3JpdGhtLCBjYW5jZWxBbGdvcml0aG0sIGhpZ2hXYXRlck1hcmsgPSAxLCBzaXplQWxnb3JpdGhtID0gKCkgPT4gMSkge1xuICAgIGNvbnN0IHN0cmVhbSA9IE9iamVjdC5jcmVhdGUoUmVhZGFibGVTdHJlYW0ucHJvdG90eXBlKTtcbiAgICBJbml0aWFsaXplUmVhZGFibGVTdHJlYW0oc3RyZWFtKTtcbiAgICBjb25zdCBjb250cm9sbGVyID0gT2JqZWN0LmNyZWF0ZShSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyLnByb3RvdHlwZSk7XG4gICAgU2V0VXBSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyKHN0cmVhbSwgY29udHJvbGxlciwgc3RhcnRBbGdvcml0aG0sIHB1bGxBbGdvcml0aG0sIGNhbmNlbEFsZ29yaXRobSwgaGlnaFdhdGVyTWFyaywgc2l6ZUFsZ29yaXRobSk7XG4gICAgcmV0dXJuIHN0cmVhbTtcbn1cbmZ1bmN0aW9uIEluaXRpYWxpemVSZWFkYWJsZVN0cmVhbShzdHJlYW0pIHtcbiAgICBzdHJlYW0uX3N0YXRlID0gJ3JlYWRhYmxlJztcbiAgICBzdHJlYW0uX3JlYWRlciA9IHVuZGVmaW5lZDtcbiAgICBzdHJlYW0uX3N0b3JlZEVycm9yID0gdW5kZWZpbmVkO1xuICAgIHN0cmVhbS5fZGlzdHVyYmVkID0gZmFsc2U7XG59XG5mdW5jdGlvbiBJc1JlYWRhYmxlU3RyZWFtKHgpIHtcbiAgICBpZiAoIXR5cGVJc09iamVjdCh4KSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGlmICghT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHgsICdfcmVhZGFibGVTdHJlYW1Db250cm9sbGVyJykpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICByZXR1cm4gdHJ1ZTtcbn1cbmZ1bmN0aW9uIElzUmVhZGFibGVTdHJlYW1Mb2NrZWQoc3RyZWFtKSB7XG4gICAgaWYgKHN0cmVhbS5fcmVhZGVyID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICByZXR1cm4gdHJ1ZTtcbn1cbi8vIFJlYWRhYmxlU3RyZWFtIEFQSSBleHBvc2VkIGZvciBjb250cm9sbGVycy5cbmZ1bmN0aW9uIFJlYWRhYmxlU3RyZWFtQ2FuY2VsKHN0cmVhbSwgcmVhc29uKSB7XG4gICAgc3RyZWFtLl9kaXN0dXJiZWQgPSB0cnVlO1xuICAgIGlmIChzdHJlYW0uX3N0YXRlID09PSAnY2xvc2VkJykge1xuICAgICAgICByZXR1cm4gcHJvbWlzZVJlc29sdmVkV2l0aCh1bmRlZmluZWQpO1xuICAgIH1cbiAgICBpZiAoc3RyZWFtLl9zdGF0ZSA9PT0gJ2Vycm9yZWQnKSB7XG4gICAgICAgIHJldHVybiBwcm9taXNlUmVqZWN0ZWRXaXRoKHN0cmVhbS5fc3RvcmVkRXJyb3IpO1xuICAgIH1cbiAgICBSZWFkYWJsZVN0cmVhbUNsb3NlKHN0cmVhbSk7XG4gICAgY29uc3Qgc291cmNlQ2FuY2VsUHJvbWlzZSA9IHN0cmVhbS5fcmVhZGFibGVTdHJlYW1Db250cm9sbGVyW0NhbmNlbFN0ZXBzXShyZWFzb24pO1xuICAgIHJldHVybiB0cmFuc2Zvcm1Qcm9taXNlV2l0aChzb3VyY2VDYW5jZWxQcm9taXNlLCBub29wKTtcbn1cbmZ1bmN0aW9uIFJlYWRhYmxlU3RyZWFtQ2xvc2Uoc3RyZWFtKSB7XG4gICAgc3RyZWFtLl9zdGF0ZSA9ICdjbG9zZWQnO1xuICAgIGNvbnN0IHJlYWRlciA9IHN0cmVhbS5fcmVhZGVyO1xuICAgIGlmIChyZWFkZXIgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChJc1JlYWRhYmxlU3RyZWFtRGVmYXVsdFJlYWRlcihyZWFkZXIpKSB7XG4gICAgICAgIHJlYWRlci5fcmVhZFJlcXVlc3RzLmZvckVhY2gocmVhZFJlcXVlc3QgPT4ge1xuICAgICAgICAgICAgcmVhZFJlcXVlc3QuX2Nsb3NlU3RlcHMoKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJlYWRlci5fcmVhZFJlcXVlc3RzID0gbmV3IFNpbXBsZVF1ZXVlKCk7XG4gICAgfVxuICAgIGRlZmF1bHRSZWFkZXJDbG9zZWRQcm9taXNlUmVzb2x2ZShyZWFkZXIpO1xufVxuZnVuY3Rpb24gUmVhZGFibGVTdHJlYW1FcnJvcihzdHJlYW0sIGUpIHtcbiAgICBzdHJlYW0uX3N0YXRlID0gJ2Vycm9yZWQnO1xuICAgIHN0cmVhbS5fc3RvcmVkRXJyb3IgPSBlO1xuICAgIGNvbnN0IHJlYWRlciA9IHN0cmVhbS5fcmVhZGVyO1xuICAgIGlmIChyZWFkZXIgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChJc1JlYWRhYmxlU3RyZWFtRGVmYXVsdFJlYWRlcihyZWFkZXIpKSB7XG4gICAgICAgIHJlYWRlci5fcmVhZFJlcXVlc3RzLmZvckVhY2gocmVhZFJlcXVlc3QgPT4ge1xuICAgICAgICAgICAgcmVhZFJlcXVlc3QuX2Vycm9yU3RlcHMoZSk7XG4gICAgICAgIH0pO1xuICAgICAgICByZWFkZXIuX3JlYWRSZXF1ZXN0cyA9IG5ldyBTaW1wbGVRdWV1ZSgpO1xuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgICAgcmVhZGVyLl9yZWFkSW50b1JlcXVlc3RzLmZvckVhY2gocmVhZEludG9SZXF1ZXN0ID0+IHtcbiAgICAgICAgICAgIHJlYWRJbnRvUmVxdWVzdC5fZXJyb3JTdGVwcyhlKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJlYWRlci5fcmVhZEludG9SZXF1ZXN0cyA9IG5ldyBTaW1wbGVRdWV1ZSgpO1xuICAgIH1cbiAgICBkZWZhdWx0UmVhZGVyQ2xvc2VkUHJvbWlzZVJlamVjdChyZWFkZXIsIGUpO1xufVxuLy8gSGVscGVyIGZ1bmN0aW9ucyBmb3IgdGhlIFJlYWRhYmxlU3RyZWFtLlxuZnVuY3Rpb24gc3RyZWFtQnJhbmRDaGVja0V4Y2VwdGlvbiQxKG5hbWUpIHtcbiAgICByZXR1cm4gbmV3IFR5cGVFcnJvcihgUmVhZGFibGVTdHJlYW0ucHJvdG90eXBlLiR7bmFtZX0gY2FuIG9ubHkgYmUgdXNlZCBvbiBhIFJlYWRhYmxlU3RyZWFtYCk7XG59XG5cbmZ1bmN0aW9uIGNvbnZlcnRRdWV1aW5nU3RyYXRlZ3lJbml0KGluaXQsIGNvbnRleHQpIHtcbiAgICBhc3NlcnREaWN0aW9uYXJ5KGluaXQsIGNvbnRleHQpO1xuICAgIGNvbnN0IGhpZ2hXYXRlck1hcmsgPSBpbml0ID09PSBudWxsIHx8IGluaXQgPT09IHZvaWQgMCA/IHZvaWQgMCA6IGluaXQuaGlnaFdhdGVyTWFyaztcbiAgICBhc3NlcnRSZXF1aXJlZEZpZWxkKGhpZ2hXYXRlck1hcmssICdoaWdoV2F0ZXJNYXJrJywgJ1F1ZXVpbmdTdHJhdGVneUluaXQnKTtcbiAgICByZXR1cm4ge1xuICAgICAgICBoaWdoV2F0ZXJNYXJrOiBjb252ZXJ0VW5yZXN0cmljdGVkRG91YmxlKGhpZ2hXYXRlck1hcmspXG4gICAgfTtcbn1cblxuY29uc3QgYnl0ZUxlbmd0aFNpemVGdW5jdGlvbiA9IGZ1bmN0aW9uIHNpemUoY2h1bmspIHtcbiAgICByZXR1cm4gY2h1bmsuYnl0ZUxlbmd0aDtcbn07XG4vKipcbiAqIEEgcXVldWluZyBzdHJhdGVneSB0aGF0IGNvdW50cyB0aGUgbnVtYmVyIG9mIGJ5dGVzIGluIGVhY2ggY2h1bmsuXG4gKlxuICogQHB1YmxpY1xuICovXG5jbGFzcyBCeXRlTGVuZ3RoUXVldWluZ1N0cmF0ZWd5IHtcbiAgICBjb25zdHJ1Y3RvcihvcHRpb25zKSB7XG4gICAgICAgIGFzc2VydFJlcXVpcmVkQXJndW1lbnQob3B0aW9ucywgMSwgJ0J5dGVMZW5ndGhRdWV1aW5nU3RyYXRlZ3knKTtcbiAgICAgICAgb3B0aW9ucyA9IGNvbnZlcnRRdWV1aW5nU3RyYXRlZ3lJbml0KG9wdGlvbnMsICdGaXJzdCBwYXJhbWV0ZXInKTtcbiAgICAgICAgdGhpcy5fYnl0ZUxlbmd0aFF1ZXVpbmdTdHJhdGVneUhpZ2hXYXRlck1hcmsgPSBvcHRpb25zLmhpZ2hXYXRlck1hcms7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGhpZ2ggd2F0ZXIgbWFyayBwcm92aWRlZCB0byB0aGUgY29uc3RydWN0b3IuXG4gICAgICovXG4gICAgZ2V0IGhpZ2hXYXRlck1hcmsoKSB7XG4gICAgICAgIGlmICghSXNCeXRlTGVuZ3RoUXVldWluZ1N0cmF0ZWd5KHRoaXMpKSB7XG4gICAgICAgICAgICB0aHJvdyBieXRlTGVuZ3RoQnJhbmRDaGVja0V4Y2VwdGlvbignaGlnaFdhdGVyTWFyaycpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLl9ieXRlTGVuZ3RoUXVldWluZ1N0cmF0ZWd5SGlnaFdhdGVyTWFyaztcbiAgICB9XG4gICAgLyoqXG4gICAgICogTWVhc3VyZXMgdGhlIHNpemUgb2YgYGNodW5rYCBieSByZXR1cm5pbmcgdGhlIHZhbHVlIG9mIGl0cyBgYnl0ZUxlbmd0aGAgcHJvcGVydHkuXG4gICAgICovXG4gICAgZ2V0IHNpemUoKSB7XG4gICAgICAgIGlmICghSXNCeXRlTGVuZ3RoUXVldWluZ1N0cmF0ZWd5KHRoaXMpKSB7XG4gICAgICAgICAgICB0aHJvdyBieXRlTGVuZ3RoQnJhbmRDaGVja0V4Y2VwdGlvbignc2l6ZScpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBieXRlTGVuZ3RoU2l6ZUZ1bmN0aW9uO1xuICAgIH1cbn1cbk9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKEJ5dGVMZW5ndGhRdWV1aW5nU3RyYXRlZ3kucHJvdG90eXBlLCB7XG4gICAgaGlnaFdhdGVyTWFyazogeyBlbnVtZXJhYmxlOiB0cnVlIH0sXG4gICAgc2l6ZTogeyBlbnVtZXJhYmxlOiB0cnVlIH1cbn0pO1xuaWYgKHR5cGVvZiBTeW1ib2xQb2x5ZmlsbC50b1N0cmluZ1RhZyA9PT0gJ3N5bWJvbCcpIHtcbiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkoQnl0ZUxlbmd0aFF1ZXVpbmdTdHJhdGVneS5wcm90b3R5cGUsIFN5bWJvbFBvbHlmaWxsLnRvU3RyaW5nVGFnLCB7XG4gICAgICAgIHZhbHVlOiAnQnl0ZUxlbmd0aFF1ZXVpbmdTdHJhdGVneScsXG4gICAgICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZVxuICAgIH0pO1xufVxuLy8gSGVscGVyIGZ1bmN0aW9ucyBmb3IgdGhlIEJ5dGVMZW5ndGhRdWV1aW5nU3RyYXRlZ3kuXG5mdW5jdGlvbiBieXRlTGVuZ3RoQnJhbmRDaGVja0V4Y2VwdGlvbihuYW1lKSB7XG4gICAgcmV0dXJuIG5ldyBUeXBlRXJyb3IoYEJ5dGVMZW5ndGhRdWV1aW5nU3RyYXRlZ3kucHJvdG90eXBlLiR7bmFtZX0gY2FuIG9ubHkgYmUgdXNlZCBvbiBhIEJ5dGVMZW5ndGhRdWV1aW5nU3RyYXRlZ3lgKTtcbn1cbmZ1bmN0aW9uIElzQnl0ZUxlbmd0aFF1ZXVpbmdTdHJhdGVneSh4KSB7XG4gICAgaWYgKCF0eXBlSXNPYmplY3QoeCkpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh4LCAnX2J5dGVMZW5ndGhRdWV1aW5nU3RyYXRlZ3lIaWdoV2F0ZXJNYXJrJykpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICByZXR1cm4gdHJ1ZTtcbn1cblxuY29uc3QgY291bnRTaXplRnVuY3Rpb24gPSBmdW5jdGlvbiBzaXplKCkge1xuICAgIHJldHVybiAxO1xufTtcbi8qKlxuICogQSBxdWV1aW5nIHN0cmF0ZWd5IHRoYXQgY291bnRzIHRoZSBudW1iZXIgb2YgY2h1bmtzLlxuICpcbiAqIEBwdWJsaWNcbiAqL1xuY2xhc3MgQ291bnRRdWV1aW5nU3RyYXRlZ3kge1xuICAgIGNvbnN0cnVjdG9yKG9wdGlvbnMpIHtcbiAgICAgICAgYXNzZXJ0UmVxdWlyZWRBcmd1bWVudChvcHRpb25zLCAxLCAnQ291bnRRdWV1aW5nU3RyYXRlZ3knKTtcbiAgICAgICAgb3B0aW9ucyA9IGNvbnZlcnRRdWV1aW5nU3RyYXRlZ3lJbml0KG9wdGlvbnMsICdGaXJzdCBwYXJhbWV0ZXInKTtcbiAgICAgICAgdGhpcy5fY291bnRRdWV1aW5nU3RyYXRlZ3lIaWdoV2F0ZXJNYXJrID0gb3B0aW9ucy5oaWdoV2F0ZXJNYXJrO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBoaWdoIHdhdGVyIG1hcmsgcHJvdmlkZWQgdG8gdGhlIGNvbnN0cnVjdG9yLlxuICAgICAqL1xuICAgIGdldCBoaWdoV2F0ZXJNYXJrKCkge1xuICAgICAgICBpZiAoIUlzQ291bnRRdWV1aW5nU3RyYXRlZ3kodGhpcykpIHtcbiAgICAgICAgICAgIHRocm93IGNvdW50QnJhbmRDaGVja0V4Y2VwdGlvbignaGlnaFdhdGVyTWFyaycpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLl9jb3VudFF1ZXVpbmdTdHJhdGVneUhpZ2hXYXRlck1hcms7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIE1lYXN1cmVzIHRoZSBzaXplIG9mIGBjaHVua2AgYnkgYWx3YXlzIHJldHVybmluZyAxLlxuICAgICAqIFRoaXMgZW5zdXJlcyB0aGF0IHRoZSB0b3RhbCBxdWV1ZSBzaXplIGlzIGEgY291bnQgb2YgdGhlIG51bWJlciBvZiBjaHVua3MgaW4gdGhlIHF1ZXVlLlxuICAgICAqL1xuICAgIGdldCBzaXplKCkge1xuICAgICAgICBpZiAoIUlzQ291bnRRdWV1aW5nU3RyYXRlZ3kodGhpcykpIHtcbiAgICAgICAgICAgIHRocm93IGNvdW50QnJhbmRDaGVja0V4Y2VwdGlvbignc2l6ZScpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBjb3VudFNpemVGdW5jdGlvbjtcbiAgICB9XG59XG5PYmplY3QuZGVmaW5lUHJvcGVydGllcyhDb3VudFF1ZXVpbmdTdHJhdGVneS5wcm90b3R5cGUsIHtcbiAgICBoaWdoV2F0ZXJNYXJrOiB7IGVudW1lcmFibGU6IHRydWUgfSxcbiAgICBzaXplOiB7IGVudW1lcmFibGU6IHRydWUgfVxufSk7XG5pZiAodHlwZW9mIFN5bWJvbFBvbHlmaWxsLnRvU3RyaW5nVGFnID09PSAnc3ltYm9sJykge1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShDb3VudFF1ZXVpbmdTdHJhdGVneS5wcm90b3R5cGUsIFN5bWJvbFBvbHlmaWxsLnRvU3RyaW5nVGFnLCB7XG4gICAgICAgIHZhbHVlOiAnQ291bnRRdWV1aW5nU3RyYXRlZ3knLFxuICAgICAgICBjb25maWd1cmFibGU6IHRydWVcbiAgICB9KTtcbn1cbi8vIEhlbHBlciBmdW5jdGlvbnMgZm9yIHRoZSBDb3VudFF1ZXVpbmdTdHJhdGVneS5cbmZ1bmN0aW9uIGNvdW50QnJhbmRDaGVja0V4Y2VwdGlvbihuYW1lKSB7XG4gICAgcmV0dXJuIG5ldyBUeXBlRXJyb3IoYENvdW50UXVldWluZ1N0cmF0ZWd5LnByb3RvdHlwZS4ke25hbWV9IGNhbiBvbmx5IGJlIHVzZWQgb24gYSBDb3VudFF1ZXVpbmdTdHJhdGVneWApO1xufVxuZnVuY3Rpb24gSXNDb3VudFF1ZXVpbmdTdHJhdGVneSh4KSB7XG4gICAgaWYgKCF0eXBlSXNPYmplY3QoeCkpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh4LCAnX2NvdW50UXVldWluZ1N0cmF0ZWd5SGlnaFdhdGVyTWFyaycpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgcmV0dXJuIHRydWU7XG59XG5cbmZ1bmN0aW9uIGNvbnZlcnRUcmFuc2Zvcm1lcihvcmlnaW5hbCwgY29udGV4dCkge1xuICAgIGFzc2VydERpY3Rpb25hcnkob3JpZ2luYWwsIGNvbnRleHQpO1xuICAgIGNvbnN0IGZsdXNoID0gb3JpZ2luYWwgPT09IG51bGwgfHwgb3JpZ2luYWwgPT09IHZvaWQgMCA/IHZvaWQgMCA6IG9yaWdpbmFsLmZsdXNoO1xuICAgIGNvbnN0IHJlYWRhYmxlVHlwZSA9IG9yaWdpbmFsID09PSBudWxsIHx8IG9yaWdpbmFsID09PSB2b2lkIDAgPyB2b2lkIDAgOiBvcmlnaW5hbC5yZWFkYWJsZVR5cGU7XG4gICAgY29uc3Qgc3RhcnQgPSBvcmlnaW5hbCA9PT0gbnVsbCB8fCBvcmlnaW5hbCA9PT0gdm9pZCAwID8gdm9pZCAwIDogb3JpZ2luYWwuc3RhcnQ7XG4gICAgY29uc3QgdHJhbnNmb3JtID0gb3JpZ2luYWwgPT09IG51bGwgfHwgb3JpZ2luYWwgPT09IHZvaWQgMCA/IHZvaWQgMCA6IG9yaWdpbmFsLnRyYW5zZm9ybTtcbiAgICBjb25zdCB3cml0YWJsZVR5cGUgPSBvcmlnaW5hbCA9PT0gbnVsbCB8fCBvcmlnaW5hbCA9PT0gdm9pZCAwID8gdm9pZCAwIDogb3JpZ2luYWwud3JpdGFibGVUeXBlO1xuICAgIHJldHVybiB7XG4gICAgICAgIGZsdXNoOiBmbHVzaCA9PT0gdW5kZWZpbmVkID9cbiAgICAgICAgICAgIHVuZGVmaW5lZCA6XG4gICAgICAgICAgICBjb252ZXJ0VHJhbnNmb3JtZXJGbHVzaENhbGxiYWNrKGZsdXNoLCBvcmlnaW5hbCwgYCR7Y29udGV4dH0gaGFzIG1lbWJlciAnZmx1c2gnIHRoYXRgKSxcbiAgICAgICAgcmVhZGFibGVUeXBlLFxuICAgICAgICBzdGFydDogc3RhcnQgPT09IHVuZGVmaW5lZCA/XG4gICAgICAgICAgICB1bmRlZmluZWQgOlxuICAgICAgICAgICAgY29udmVydFRyYW5zZm9ybWVyU3RhcnRDYWxsYmFjayhzdGFydCwgb3JpZ2luYWwsIGAke2NvbnRleHR9IGhhcyBtZW1iZXIgJ3N0YXJ0JyB0aGF0YCksXG4gICAgICAgIHRyYW5zZm9ybTogdHJhbnNmb3JtID09PSB1bmRlZmluZWQgP1xuICAgICAgICAgICAgdW5kZWZpbmVkIDpcbiAgICAgICAgICAgIGNvbnZlcnRUcmFuc2Zvcm1lclRyYW5zZm9ybUNhbGxiYWNrKHRyYW5zZm9ybSwgb3JpZ2luYWwsIGAke2NvbnRleHR9IGhhcyBtZW1iZXIgJ3RyYW5zZm9ybScgdGhhdGApLFxuICAgICAgICB3cml0YWJsZVR5cGVcbiAgICB9O1xufVxuZnVuY3Rpb24gY29udmVydFRyYW5zZm9ybWVyRmx1c2hDYWxsYmFjayhmbiwgb3JpZ2luYWwsIGNvbnRleHQpIHtcbiAgICBhc3NlcnRGdW5jdGlvbihmbiwgY29udGV4dCk7XG4gICAgcmV0dXJuIChjb250cm9sbGVyKSA9PiBwcm9taXNlQ2FsbChmbiwgb3JpZ2luYWwsIFtjb250cm9sbGVyXSk7XG59XG5mdW5jdGlvbiBjb252ZXJ0VHJhbnNmb3JtZXJTdGFydENhbGxiYWNrKGZuLCBvcmlnaW5hbCwgY29udGV4dCkge1xuICAgIGFzc2VydEZ1bmN0aW9uKGZuLCBjb250ZXh0KTtcbiAgICByZXR1cm4gKGNvbnRyb2xsZXIpID0+IHJlZmxlY3RDYWxsKGZuLCBvcmlnaW5hbCwgW2NvbnRyb2xsZXJdKTtcbn1cbmZ1bmN0aW9uIGNvbnZlcnRUcmFuc2Zvcm1lclRyYW5zZm9ybUNhbGxiYWNrKGZuLCBvcmlnaW5hbCwgY29udGV4dCkge1xuICAgIGFzc2VydEZ1bmN0aW9uKGZuLCBjb250ZXh0KTtcbiAgICByZXR1cm4gKGNodW5rLCBjb250cm9sbGVyKSA9PiBwcm9taXNlQ2FsbChmbiwgb3JpZ2luYWwsIFtjaHVuaywgY29udHJvbGxlcl0pO1xufVxuXG4vLyBDbGFzcyBUcmFuc2Zvcm1TdHJlYW1cbi8qKlxuICogQSB0cmFuc2Zvcm0gc3RyZWFtIGNvbnNpc3RzIG9mIGEgcGFpciBvZiBzdHJlYW1zOiBhIHtAbGluayBXcml0YWJsZVN0cmVhbSB8IHdyaXRhYmxlIHN0cmVhbX0sXG4gKiBrbm93biBhcyBpdHMgd3JpdGFibGUgc2lkZSwgYW5kIGEge0BsaW5rIFJlYWRhYmxlU3RyZWFtIHwgcmVhZGFibGUgc3RyZWFtfSwga25vd24gYXMgaXRzIHJlYWRhYmxlIHNpZGUuXG4gKiBJbiBhIG1hbm5lciBzcGVjaWZpYyB0byB0aGUgdHJhbnNmb3JtIHN0cmVhbSBpbiBxdWVzdGlvbiwgd3JpdGVzIHRvIHRoZSB3cml0YWJsZSBzaWRlIHJlc3VsdCBpbiBuZXcgZGF0YSBiZWluZ1xuICogbWFkZSBhdmFpbGFibGUgZm9yIHJlYWRpbmcgZnJvbSB0aGUgcmVhZGFibGUgc2lkZS5cbiAqXG4gKiBAcHVibGljXG4gKi9cbmNsYXNzIFRyYW5zZm9ybVN0cmVhbSB7XG4gICAgY29uc3RydWN0b3IocmF3VHJhbnNmb3JtZXIgPSB7fSwgcmF3V3JpdGFibGVTdHJhdGVneSA9IHt9LCByYXdSZWFkYWJsZVN0cmF0ZWd5ID0ge30pIHtcbiAgICAgICAgaWYgKHJhd1RyYW5zZm9ybWVyID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHJhd1RyYW5zZm9ybWVyID0gbnVsbDtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCB3cml0YWJsZVN0cmF0ZWd5ID0gY29udmVydFF1ZXVpbmdTdHJhdGVneShyYXdXcml0YWJsZVN0cmF0ZWd5LCAnU2Vjb25kIHBhcmFtZXRlcicpO1xuICAgICAgICBjb25zdCByZWFkYWJsZVN0cmF0ZWd5ID0gY29udmVydFF1ZXVpbmdTdHJhdGVneShyYXdSZWFkYWJsZVN0cmF0ZWd5LCAnVGhpcmQgcGFyYW1ldGVyJyk7XG4gICAgICAgIGNvbnN0IHRyYW5zZm9ybWVyID0gY29udmVydFRyYW5zZm9ybWVyKHJhd1RyYW5zZm9ybWVyLCAnRmlyc3QgcGFyYW1ldGVyJyk7XG4gICAgICAgIGlmICh0cmFuc2Zvcm1lci5yZWFkYWJsZVR5cGUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ0ludmFsaWQgcmVhZGFibGVUeXBlIHNwZWNpZmllZCcpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0cmFuc2Zvcm1lci53cml0YWJsZVR5cGUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ0ludmFsaWQgd3JpdGFibGVUeXBlIHNwZWNpZmllZCcpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHJlYWRhYmxlSGlnaFdhdGVyTWFyayA9IEV4dHJhY3RIaWdoV2F0ZXJNYXJrKHJlYWRhYmxlU3RyYXRlZ3ksIDApO1xuICAgICAgICBjb25zdCByZWFkYWJsZVNpemVBbGdvcml0aG0gPSBFeHRyYWN0U2l6ZUFsZ29yaXRobShyZWFkYWJsZVN0cmF0ZWd5KTtcbiAgICAgICAgY29uc3Qgd3JpdGFibGVIaWdoV2F0ZXJNYXJrID0gRXh0cmFjdEhpZ2hXYXRlck1hcmsod3JpdGFibGVTdHJhdGVneSwgMSk7XG4gICAgICAgIGNvbnN0IHdyaXRhYmxlU2l6ZUFsZ29yaXRobSA9IEV4dHJhY3RTaXplQWxnb3JpdGhtKHdyaXRhYmxlU3RyYXRlZ3kpO1xuICAgICAgICBsZXQgc3RhcnRQcm9taXNlX3Jlc29sdmU7XG4gICAgICAgIGNvbnN0IHN0YXJ0UHJvbWlzZSA9IG5ld1Byb21pc2UocmVzb2x2ZSA9PiB7XG4gICAgICAgICAgICBzdGFydFByb21pc2VfcmVzb2x2ZSA9IHJlc29sdmU7XG4gICAgICAgIH0pO1xuICAgICAgICBJbml0aWFsaXplVHJhbnNmb3JtU3RyZWFtKHRoaXMsIHN0YXJ0UHJvbWlzZSwgd3JpdGFibGVIaWdoV2F0ZXJNYXJrLCB3cml0YWJsZVNpemVBbGdvcml0aG0sIHJlYWRhYmxlSGlnaFdhdGVyTWFyaywgcmVhZGFibGVTaXplQWxnb3JpdGhtKTtcbiAgICAgICAgU2V0VXBUcmFuc2Zvcm1TdHJlYW1EZWZhdWx0Q29udHJvbGxlckZyb21UcmFuc2Zvcm1lcih0aGlzLCB0cmFuc2Zvcm1lcik7XG4gICAgICAgIGlmICh0cmFuc2Zvcm1lci5zdGFydCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBzdGFydFByb21pc2VfcmVzb2x2ZSh0cmFuc2Zvcm1lci5zdGFydCh0aGlzLl90cmFuc2Zvcm1TdHJlYW1Db250cm9sbGVyKSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBzdGFydFByb21pc2VfcmVzb2x2ZSh1bmRlZmluZWQpO1xuICAgICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFRoZSByZWFkYWJsZSBzaWRlIG9mIHRoZSB0cmFuc2Zvcm0gc3RyZWFtLlxuICAgICAqL1xuICAgIGdldCByZWFkYWJsZSgpIHtcbiAgICAgICAgaWYgKCFJc1RyYW5zZm9ybVN0cmVhbSh0aGlzKSkge1xuICAgICAgICAgICAgdGhyb3cgc3RyZWFtQnJhbmRDaGVja0V4Y2VwdGlvbiQyKCdyZWFkYWJsZScpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLl9yZWFkYWJsZTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogVGhlIHdyaXRhYmxlIHNpZGUgb2YgdGhlIHRyYW5zZm9ybSBzdHJlYW0uXG4gICAgICovXG4gICAgZ2V0IHdyaXRhYmxlKCkge1xuICAgICAgICBpZiAoIUlzVHJhbnNmb3JtU3RyZWFtKHRoaXMpKSB7XG4gICAgICAgICAgICB0aHJvdyBzdHJlYW1CcmFuZENoZWNrRXhjZXB0aW9uJDIoJ3dyaXRhYmxlJyk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXMuX3dyaXRhYmxlO1xuICAgIH1cbn1cbk9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKFRyYW5zZm9ybVN0cmVhbS5wcm90b3R5cGUsIHtcbiAgICByZWFkYWJsZTogeyBlbnVtZXJhYmxlOiB0cnVlIH0sXG4gICAgd3JpdGFibGU6IHsgZW51bWVyYWJsZTogdHJ1ZSB9XG59KTtcbmlmICh0eXBlb2YgU3ltYm9sUG9seWZpbGwudG9TdHJpbmdUYWcgPT09ICdzeW1ib2wnKSB7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KFRyYW5zZm9ybVN0cmVhbS5wcm90b3R5cGUsIFN5bWJvbFBvbHlmaWxsLnRvU3RyaW5nVGFnLCB7XG4gICAgICAgIHZhbHVlOiAnVHJhbnNmb3JtU3RyZWFtJyxcbiAgICAgICAgY29uZmlndXJhYmxlOiB0cnVlXG4gICAgfSk7XG59XG5mdW5jdGlvbiBJbml0aWFsaXplVHJhbnNmb3JtU3RyZWFtKHN0cmVhbSwgc3RhcnRQcm9taXNlLCB3cml0YWJsZUhpZ2hXYXRlck1hcmssIHdyaXRhYmxlU2l6ZUFsZ29yaXRobSwgcmVhZGFibGVIaWdoV2F0ZXJNYXJrLCByZWFkYWJsZVNpemVBbGdvcml0aG0pIHtcbiAgICBmdW5jdGlvbiBzdGFydEFsZ29yaXRobSgpIHtcbiAgICAgICAgcmV0dXJuIHN0YXJ0UHJvbWlzZTtcbiAgICB9XG4gICAgZnVuY3Rpb24gd3JpdGVBbGdvcml0aG0oY2h1bmspIHtcbiAgICAgICAgcmV0dXJuIFRyYW5zZm9ybVN0cmVhbURlZmF1bHRTaW5rV3JpdGVBbGdvcml0aG0oc3RyZWFtLCBjaHVuayk7XG4gICAgfVxuICAgIGZ1bmN0aW9uIGFib3J0QWxnb3JpdGhtKHJlYXNvbikge1xuICAgICAgICByZXR1cm4gVHJhbnNmb3JtU3RyZWFtRGVmYXVsdFNpbmtBYm9ydEFsZ29yaXRobShzdHJlYW0sIHJlYXNvbik7XG4gICAgfVxuICAgIGZ1bmN0aW9uIGNsb3NlQWxnb3JpdGhtKCkge1xuICAgICAgICByZXR1cm4gVHJhbnNmb3JtU3RyZWFtRGVmYXVsdFNpbmtDbG9zZUFsZ29yaXRobShzdHJlYW0pO1xuICAgIH1cbiAgICBzdHJlYW0uX3dyaXRhYmxlID0gQ3JlYXRlV3JpdGFibGVTdHJlYW0oc3RhcnRBbGdvcml0aG0sIHdyaXRlQWxnb3JpdGhtLCBjbG9zZUFsZ29yaXRobSwgYWJvcnRBbGdvcml0aG0sIHdyaXRhYmxlSGlnaFdhdGVyTWFyaywgd3JpdGFibGVTaXplQWxnb3JpdGhtKTtcbiAgICBmdW5jdGlvbiBwdWxsQWxnb3JpdGhtKCkge1xuICAgICAgICByZXR1cm4gVHJhbnNmb3JtU3RyZWFtRGVmYXVsdFNvdXJjZVB1bGxBbGdvcml0aG0oc3RyZWFtKTtcbiAgICB9XG4gICAgZnVuY3Rpb24gY2FuY2VsQWxnb3JpdGhtKHJlYXNvbikge1xuICAgICAgICBUcmFuc2Zvcm1TdHJlYW1FcnJvcldyaXRhYmxlQW5kVW5ibG9ja1dyaXRlKHN0cmVhbSwgcmVhc29uKTtcbiAgICAgICAgcmV0dXJuIHByb21pc2VSZXNvbHZlZFdpdGgodW5kZWZpbmVkKTtcbiAgICB9XG4gICAgc3RyZWFtLl9yZWFkYWJsZSA9IENyZWF0ZVJlYWRhYmxlU3RyZWFtKHN0YXJ0QWxnb3JpdGhtLCBwdWxsQWxnb3JpdGhtLCBjYW5jZWxBbGdvcml0aG0sIHJlYWRhYmxlSGlnaFdhdGVyTWFyaywgcmVhZGFibGVTaXplQWxnb3JpdGhtKTtcbiAgICAvLyBUaGUgW1tiYWNrcHJlc3N1cmVdXSBzbG90IGlzIHNldCB0byB1bmRlZmluZWQgc28gdGhhdCBpdCBjYW4gYmUgaW5pdGlhbGlzZWQgYnkgVHJhbnNmb3JtU3RyZWFtU2V0QmFja3ByZXNzdXJlLlxuICAgIHN0cmVhbS5fYmFja3ByZXNzdXJlID0gdW5kZWZpbmVkO1xuICAgIHN0cmVhbS5fYmFja3ByZXNzdXJlQ2hhbmdlUHJvbWlzZSA9IHVuZGVmaW5lZDtcbiAgICBzdHJlYW0uX2JhY2twcmVzc3VyZUNoYW5nZVByb21pc2VfcmVzb2x2ZSA9IHVuZGVmaW5lZDtcbiAgICBUcmFuc2Zvcm1TdHJlYW1TZXRCYWNrcHJlc3N1cmUoc3RyZWFtLCB0cnVlKTtcbiAgICBzdHJlYW0uX3RyYW5zZm9ybVN0cmVhbUNvbnRyb2xsZXIgPSB1bmRlZmluZWQ7XG59XG5mdW5jdGlvbiBJc1RyYW5zZm9ybVN0cmVhbSh4KSB7XG4gICAgaWYgKCF0eXBlSXNPYmplY3QoeCkpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh4LCAnX3RyYW5zZm9ybVN0cmVhbUNvbnRyb2xsZXInKSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xufVxuLy8gVGhpcyBpcyBhIG5vLW9wIGlmIGJvdGggc2lkZXMgYXJlIGFscmVhZHkgZXJyb3JlZC5cbmZ1bmN0aW9uIFRyYW5zZm9ybVN0cmVhbUVycm9yKHN0cmVhbSwgZSkge1xuICAgIFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJFcnJvcihzdHJlYW0uX3JlYWRhYmxlLl9yZWFkYWJsZVN0cmVhbUNvbnRyb2xsZXIsIGUpO1xuICAgIFRyYW5zZm9ybVN0cmVhbUVycm9yV3JpdGFibGVBbmRVbmJsb2NrV3JpdGUoc3RyZWFtLCBlKTtcbn1cbmZ1bmN0aW9uIFRyYW5zZm9ybVN0cmVhbUVycm9yV3JpdGFibGVBbmRVbmJsb2NrV3JpdGUoc3RyZWFtLCBlKSB7XG4gICAgVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJDbGVhckFsZ29yaXRobXMoc3RyZWFtLl90cmFuc2Zvcm1TdHJlYW1Db250cm9sbGVyKTtcbiAgICBXcml0YWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyRXJyb3JJZk5lZWRlZChzdHJlYW0uX3dyaXRhYmxlLl93cml0YWJsZVN0cmVhbUNvbnRyb2xsZXIsIGUpO1xuICAgIGlmIChzdHJlYW0uX2JhY2twcmVzc3VyZSkge1xuICAgICAgICAvLyBQcmV0ZW5kIHRoYXQgcHVsbCgpIHdhcyBjYWxsZWQgdG8gcGVybWl0IGFueSBwZW5kaW5nIHdyaXRlKCkgY2FsbHMgdG8gY29tcGxldGUuIFRyYW5zZm9ybVN0cmVhbVNldEJhY2twcmVzc3VyZSgpXG4gICAgICAgIC8vIGNhbm5vdCBiZSBjYWxsZWQgZnJvbSBlbnF1ZXVlKCkgb3IgcHVsbCgpIG9uY2UgdGhlIFJlYWRhYmxlU3RyZWFtIGlzIGVycm9yZWQsIHNvIHRoaXMgd2lsbCB3aWxsIGJlIHRoZSBmaW5hbCB0aW1lXG4gICAgICAgIC8vIF9iYWNrcHJlc3N1cmUgaXMgc2V0LlxuICAgICAgICBUcmFuc2Zvcm1TdHJlYW1TZXRCYWNrcHJlc3N1cmUoc3RyZWFtLCBmYWxzZSk7XG4gICAgfVxufVxuZnVuY3Rpb24gVHJhbnNmb3JtU3RyZWFtU2V0QmFja3ByZXNzdXJlKHN0cmVhbSwgYmFja3ByZXNzdXJlKSB7XG4gICAgLy8gUGFzc2VzIGFsc28gd2hlbiBjYWxsZWQgZHVyaW5nIGNvbnN0cnVjdGlvbi5cbiAgICBpZiAoc3RyZWFtLl9iYWNrcHJlc3N1cmVDaGFuZ2VQcm9taXNlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgc3RyZWFtLl9iYWNrcHJlc3N1cmVDaGFuZ2VQcm9taXNlX3Jlc29sdmUoKTtcbiAgICB9XG4gICAgc3RyZWFtLl9iYWNrcHJlc3N1cmVDaGFuZ2VQcm9taXNlID0gbmV3UHJvbWlzZShyZXNvbHZlID0+IHtcbiAgICAgICAgc3RyZWFtLl9iYWNrcHJlc3N1cmVDaGFuZ2VQcm9taXNlX3Jlc29sdmUgPSByZXNvbHZlO1xuICAgIH0pO1xuICAgIHN0cmVhbS5fYmFja3ByZXNzdXJlID0gYmFja3ByZXNzdXJlO1xufVxuLy8gQ2xhc3MgVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJcbi8qKlxuICogQWxsb3dzIGNvbnRyb2wgb2YgdGhlIHtAbGluayBSZWFkYWJsZVN0cmVhbX0gYW5kIHtAbGluayBXcml0YWJsZVN0cmVhbX0gb2YgdGhlIGFzc29jaWF0ZWQge0BsaW5rIFRyYW5zZm9ybVN0cmVhbX0uXG4gKlxuICogQHB1YmxpY1xuICovXG5jbGFzcyBUcmFuc2Zvcm1TdHJlYW1EZWZhdWx0Q29udHJvbGxlciB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0lsbGVnYWwgY29uc3RydWN0b3InKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgZGVzaXJlZCBzaXplIHRvIGZpbGwgdGhlIHJlYWRhYmxlIHNpZGXigJlzIGludGVybmFsIHF1ZXVlLiBJdCBjYW4gYmUgbmVnYXRpdmUsIGlmIHRoZSBxdWV1ZSBpcyBvdmVyLWZ1bGwuXG4gICAgICovXG4gICAgZ2V0IGRlc2lyZWRTaXplKCkge1xuICAgICAgICBpZiAoIUlzVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXIodGhpcykpIHtcbiAgICAgICAgICAgIHRocm93IGRlZmF1bHRDb250cm9sbGVyQnJhbmRDaGVja0V4Y2VwdGlvbiQxKCdkZXNpcmVkU2l6ZScpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHJlYWRhYmxlQ29udHJvbGxlciA9IHRoaXMuX2NvbnRyb2xsZWRUcmFuc2Zvcm1TdHJlYW0uX3JlYWRhYmxlLl9yZWFkYWJsZVN0cmVhbUNvbnRyb2xsZXI7XG4gICAgICAgIHJldHVybiBSZWFkYWJsZVN0cmVhbURlZmF1bHRDb250cm9sbGVyR2V0RGVzaXJlZFNpemUocmVhZGFibGVDb250cm9sbGVyKTtcbiAgICB9XG4gICAgZW5xdWV1ZShjaHVuayA9IHVuZGVmaW5lZCkge1xuICAgICAgICBpZiAoIUlzVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXIodGhpcykpIHtcbiAgICAgICAgICAgIHRocm93IGRlZmF1bHRDb250cm9sbGVyQnJhbmRDaGVja0V4Y2VwdGlvbiQxKCdlbnF1ZXVlJyk7XG4gICAgICAgIH1cbiAgICAgICAgVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJFbnF1ZXVlKHRoaXMsIGNodW5rKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogRXJyb3JzIGJvdGggdGhlIHJlYWRhYmxlIHNpZGUgYW5kIHRoZSB3cml0YWJsZSBzaWRlIG9mIHRoZSBjb250cm9sbGVkIHRyYW5zZm9ybSBzdHJlYW0sIG1ha2luZyBhbGwgZnV0dXJlXG4gICAgICogaW50ZXJhY3Rpb25zIHdpdGggaXQgZmFpbCB3aXRoIHRoZSBnaXZlbiBlcnJvciBgZWAuIEFueSBjaHVua3MgcXVldWVkIGZvciB0cmFuc2Zvcm1hdGlvbiB3aWxsIGJlIGRpc2NhcmRlZC5cbiAgICAgKi9cbiAgICBlcnJvcihyZWFzb24gPSB1bmRlZmluZWQpIHtcbiAgICAgICAgaWYgKCFJc1RyYW5zZm9ybVN0cmVhbURlZmF1bHRDb250cm9sbGVyKHRoaXMpKSB7XG4gICAgICAgICAgICB0aHJvdyBkZWZhdWx0Q29udHJvbGxlckJyYW5kQ2hlY2tFeGNlcHRpb24kMSgnZXJyb3InKTtcbiAgICAgICAgfVxuICAgICAgICBUcmFuc2Zvcm1TdHJlYW1EZWZhdWx0Q29udHJvbGxlckVycm9yKHRoaXMsIHJlYXNvbik7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENsb3NlcyB0aGUgcmVhZGFibGUgc2lkZSBhbmQgZXJyb3JzIHRoZSB3cml0YWJsZSBzaWRlIG9mIHRoZSBjb250cm9sbGVkIHRyYW5zZm9ybSBzdHJlYW0uIFRoaXMgaXMgdXNlZnVsIHdoZW4gdGhlXG4gICAgICogdHJhbnNmb3JtZXIgb25seSBuZWVkcyB0byBjb25zdW1lIGEgcG9ydGlvbiBvZiB0aGUgY2h1bmtzIHdyaXR0ZW4gdG8gdGhlIHdyaXRhYmxlIHNpZGUuXG4gICAgICovXG4gICAgdGVybWluYXRlKCkge1xuICAgICAgICBpZiAoIUlzVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXIodGhpcykpIHtcbiAgICAgICAgICAgIHRocm93IGRlZmF1bHRDb250cm9sbGVyQnJhbmRDaGVja0V4Y2VwdGlvbiQxKCd0ZXJtaW5hdGUnKTtcbiAgICAgICAgfVxuICAgICAgICBUcmFuc2Zvcm1TdHJlYW1EZWZhdWx0Q29udHJvbGxlclRlcm1pbmF0ZSh0aGlzKTtcbiAgICB9XG59XG5PYmplY3QuZGVmaW5lUHJvcGVydGllcyhUcmFuc2Zvcm1TdHJlYW1EZWZhdWx0Q29udHJvbGxlci5wcm90b3R5cGUsIHtcbiAgICBlbnF1ZXVlOiB7IGVudW1lcmFibGU6IHRydWUgfSxcbiAgICBlcnJvcjogeyBlbnVtZXJhYmxlOiB0cnVlIH0sXG4gICAgdGVybWluYXRlOiB7IGVudW1lcmFibGU6IHRydWUgfSxcbiAgICBkZXNpcmVkU2l6ZTogeyBlbnVtZXJhYmxlOiB0cnVlIH1cbn0pO1xuaWYgKHR5cGVvZiBTeW1ib2xQb2x5ZmlsbC50b1N0cmluZ1RhZyA9PT0gJ3N5bWJvbCcpIHtcbiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkoVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXIucHJvdG90eXBlLCBTeW1ib2xQb2x5ZmlsbC50b1N0cmluZ1RhZywge1xuICAgICAgICB2YWx1ZTogJ1RyYW5zZm9ybVN0cmVhbURlZmF1bHRDb250cm9sbGVyJyxcbiAgICAgICAgY29uZmlndXJhYmxlOiB0cnVlXG4gICAgfSk7XG59XG4vLyBUcmFuc2Zvcm0gU3RyZWFtIERlZmF1bHQgQ29udHJvbGxlciBBYnN0cmFjdCBPcGVyYXRpb25zXG5mdW5jdGlvbiBJc1RyYW5zZm9ybVN0cmVhbURlZmF1bHRDb250cm9sbGVyKHgpIHtcbiAgICBpZiAoIXR5cGVJc09iamVjdCh4KSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGlmICghT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHgsICdfY29udHJvbGxlZFRyYW5zZm9ybVN0cmVhbScpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgcmV0dXJuIHRydWU7XG59XG5mdW5jdGlvbiBTZXRVcFRyYW5zZm9ybVN0cmVhbURlZmF1bHRDb250cm9sbGVyKHN0cmVhbSwgY29udHJvbGxlciwgdHJhbnNmb3JtQWxnb3JpdGhtLCBmbHVzaEFsZ29yaXRobSkge1xuICAgIGNvbnRyb2xsZXIuX2NvbnRyb2xsZWRUcmFuc2Zvcm1TdHJlYW0gPSBzdHJlYW07XG4gICAgc3RyZWFtLl90cmFuc2Zvcm1TdHJlYW1Db250cm9sbGVyID0gY29udHJvbGxlcjtcbiAgICBjb250cm9sbGVyLl90cmFuc2Zvcm1BbGdvcml0aG0gPSB0cmFuc2Zvcm1BbGdvcml0aG07XG4gICAgY29udHJvbGxlci5fZmx1c2hBbGdvcml0aG0gPSBmbHVzaEFsZ29yaXRobTtcbn1cbmZ1bmN0aW9uIFNldFVwVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJGcm9tVHJhbnNmb3JtZXIoc3RyZWFtLCB0cmFuc2Zvcm1lcikge1xuICAgIGNvbnN0IGNvbnRyb2xsZXIgPSBPYmplY3QuY3JlYXRlKFRyYW5zZm9ybVN0cmVhbURlZmF1bHRDb250cm9sbGVyLnByb3RvdHlwZSk7XG4gICAgbGV0IHRyYW5zZm9ybUFsZ29yaXRobSA9IChjaHVuaykgPT4ge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJFbnF1ZXVlKGNvbnRyb2xsZXIsIGNodW5rKTtcbiAgICAgICAgICAgIHJldHVybiBwcm9taXNlUmVzb2x2ZWRXaXRoKHVuZGVmaW5lZCk7XG4gICAgICAgIH1cbiAgICAgICAgY2F0Y2ggKHRyYW5zZm9ybVJlc3VsdEUpIHtcbiAgICAgICAgICAgIHJldHVybiBwcm9taXNlUmVqZWN0ZWRXaXRoKHRyYW5zZm9ybVJlc3VsdEUpO1xuICAgICAgICB9XG4gICAgfTtcbiAgICBsZXQgZmx1c2hBbGdvcml0aG0gPSAoKSA9PiBwcm9taXNlUmVzb2x2ZWRXaXRoKHVuZGVmaW5lZCk7XG4gICAgaWYgKHRyYW5zZm9ybWVyLnRyYW5zZm9ybSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRyYW5zZm9ybUFsZ29yaXRobSA9IGNodW5rID0+IHRyYW5zZm9ybWVyLnRyYW5zZm9ybShjaHVuaywgY29udHJvbGxlcik7XG4gICAgfVxuICAgIGlmICh0cmFuc2Zvcm1lci5mbHVzaCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGZsdXNoQWxnb3JpdGhtID0gKCkgPT4gdHJhbnNmb3JtZXIuZmx1c2goY29udHJvbGxlcik7XG4gICAgfVxuICAgIFNldFVwVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXIoc3RyZWFtLCBjb250cm9sbGVyLCB0cmFuc2Zvcm1BbGdvcml0aG0sIGZsdXNoQWxnb3JpdGhtKTtcbn1cbmZ1bmN0aW9uIFRyYW5zZm9ybVN0cmVhbURlZmF1bHRDb250cm9sbGVyQ2xlYXJBbGdvcml0aG1zKGNvbnRyb2xsZXIpIHtcbiAgICBjb250cm9sbGVyLl90cmFuc2Zvcm1BbGdvcml0aG0gPSB1bmRlZmluZWQ7XG4gICAgY29udHJvbGxlci5fZmx1c2hBbGdvcml0aG0gPSB1bmRlZmluZWQ7XG59XG5mdW5jdGlvbiBUcmFuc2Zvcm1TdHJlYW1EZWZhdWx0Q29udHJvbGxlckVucXVldWUoY29udHJvbGxlciwgY2h1bmspIHtcbiAgICBjb25zdCBzdHJlYW0gPSBjb250cm9sbGVyLl9jb250cm9sbGVkVHJhbnNmb3JtU3RyZWFtO1xuICAgIGNvbnN0IHJlYWRhYmxlQ29udHJvbGxlciA9IHN0cmVhbS5fcmVhZGFibGUuX3JlYWRhYmxlU3RyZWFtQ29udHJvbGxlcjtcbiAgICBpZiAoIVJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJDYW5DbG9zZU9yRW5xdWV1ZShyZWFkYWJsZUNvbnRyb2xsZXIpKSB7XG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ1JlYWRhYmxlIHNpZGUgaXMgbm90IGluIGEgc3RhdGUgdGhhdCBwZXJtaXRzIGVucXVldWUnKTtcbiAgICB9XG4gICAgLy8gV2UgdGhyb3R0bGUgdHJhbnNmb3JtIGludm9jYXRpb25zIGJhc2VkIG9uIHRoZSBiYWNrcHJlc3N1cmUgb2YgdGhlIFJlYWRhYmxlU3RyZWFtLCBidXQgd2Ugc3RpbGxcbiAgICAvLyBhY2NlcHQgVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJFbnF1ZXVlKCkgY2FsbHMuXG4gICAgdHJ5IHtcbiAgICAgICAgUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckVucXVldWUocmVhZGFibGVDb250cm9sbGVyLCBjaHVuayk7XG4gICAgfVxuICAgIGNhdGNoIChlKSB7XG4gICAgICAgIC8vIFRoaXMgaGFwcGVucyB3aGVuIHJlYWRhYmxlU3RyYXRlZ3kuc2l6ZSgpIHRocm93cy5cbiAgICAgICAgVHJhbnNmb3JtU3RyZWFtRXJyb3JXcml0YWJsZUFuZFVuYmxvY2tXcml0ZShzdHJlYW0sIGUpO1xuICAgICAgICB0aHJvdyBzdHJlYW0uX3JlYWRhYmxlLl9zdG9yZWRFcnJvcjtcbiAgICB9XG4gICAgY29uc3QgYmFja3ByZXNzdXJlID0gUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckhhc0JhY2twcmVzc3VyZShyZWFkYWJsZUNvbnRyb2xsZXIpO1xuICAgIGlmIChiYWNrcHJlc3N1cmUgIT09IHN0cmVhbS5fYmFja3ByZXNzdXJlKSB7XG4gICAgICAgIFRyYW5zZm9ybVN0cmVhbVNldEJhY2twcmVzc3VyZShzdHJlYW0sIHRydWUpO1xuICAgIH1cbn1cbmZ1bmN0aW9uIFRyYW5zZm9ybVN0cmVhbURlZmF1bHRDb250cm9sbGVyRXJyb3IoY29udHJvbGxlciwgZSkge1xuICAgIFRyYW5zZm9ybVN0cmVhbUVycm9yKGNvbnRyb2xsZXIuX2NvbnRyb2xsZWRUcmFuc2Zvcm1TdHJlYW0sIGUpO1xufVxuZnVuY3Rpb24gVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJQZXJmb3JtVHJhbnNmb3JtKGNvbnRyb2xsZXIsIGNodW5rKSB7XG4gICAgY29uc3QgdHJhbnNmb3JtUHJvbWlzZSA9IGNvbnRyb2xsZXIuX3RyYW5zZm9ybUFsZ29yaXRobShjaHVuayk7XG4gICAgcmV0dXJuIHRyYW5zZm9ybVByb21pc2VXaXRoKHRyYW5zZm9ybVByb21pc2UsIHVuZGVmaW5lZCwgciA9PiB7XG4gICAgICAgIFRyYW5zZm9ybVN0cmVhbUVycm9yKGNvbnRyb2xsZXIuX2NvbnRyb2xsZWRUcmFuc2Zvcm1TdHJlYW0sIHIpO1xuICAgICAgICB0aHJvdyByO1xuICAgIH0pO1xufVxuZnVuY3Rpb24gVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJUZXJtaW5hdGUoY29udHJvbGxlcikge1xuICAgIGNvbnN0IHN0cmVhbSA9IGNvbnRyb2xsZXIuX2NvbnRyb2xsZWRUcmFuc2Zvcm1TdHJlYW07XG4gICAgY29uc3QgcmVhZGFibGVDb250cm9sbGVyID0gc3RyZWFtLl9yZWFkYWJsZS5fcmVhZGFibGVTdHJlYW1Db250cm9sbGVyO1xuICAgIFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJDbG9zZShyZWFkYWJsZUNvbnRyb2xsZXIpO1xuICAgIGNvbnN0IGVycm9yID0gbmV3IFR5cGVFcnJvcignVHJhbnNmb3JtU3RyZWFtIHRlcm1pbmF0ZWQnKTtcbiAgICBUcmFuc2Zvcm1TdHJlYW1FcnJvcldyaXRhYmxlQW5kVW5ibG9ja1dyaXRlKHN0cmVhbSwgZXJyb3IpO1xufVxuLy8gVHJhbnNmb3JtU3RyZWFtRGVmYXVsdFNpbmsgQWxnb3JpdGhtc1xuZnVuY3Rpb24gVHJhbnNmb3JtU3RyZWFtRGVmYXVsdFNpbmtXcml0ZUFsZ29yaXRobShzdHJlYW0sIGNodW5rKSB7XG4gICAgY29uc3QgY29udHJvbGxlciA9IHN0cmVhbS5fdHJhbnNmb3JtU3RyZWFtQ29udHJvbGxlcjtcbiAgICBpZiAoc3RyZWFtLl9iYWNrcHJlc3N1cmUpIHtcbiAgICAgICAgY29uc3QgYmFja3ByZXNzdXJlQ2hhbmdlUHJvbWlzZSA9IHN0cmVhbS5fYmFja3ByZXNzdXJlQ2hhbmdlUHJvbWlzZTtcbiAgICAgICAgcmV0dXJuIHRyYW5zZm9ybVByb21pc2VXaXRoKGJhY2twcmVzc3VyZUNoYW5nZVByb21pc2UsICgpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHdyaXRhYmxlID0gc3RyZWFtLl93cml0YWJsZTtcbiAgICAgICAgICAgIGNvbnN0IHN0YXRlID0gd3JpdGFibGUuX3N0YXRlO1xuICAgICAgICAgICAgaWYgKHN0YXRlID09PSAnZXJyb3JpbmcnKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgd3JpdGFibGUuX3N0b3JlZEVycm9yO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIFRyYW5zZm9ybVN0cmVhbURlZmF1bHRDb250cm9sbGVyUGVyZm9ybVRyYW5zZm9ybShjb250cm9sbGVyLCBjaHVuayk7XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJQZXJmb3JtVHJhbnNmb3JtKGNvbnRyb2xsZXIsIGNodW5rKTtcbn1cbmZ1bmN0aW9uIFRyYW5zZm9ybVN0cmVhbURlZmF1bHRTaW5rQWJvcnRBbGdvcml0aG0oc3RyZWFtLCByZWFzb24pIHtcbiAgICAvLyBhYm9ydCgpIGlzIG5vdCBjYWxsZWQgc3luY2hyb25vdXNseSwgc28gaXQgaXMgcG9zc2libGUgZm9yIGFib3J0KCkgdG8gYmUgY2FsbGVkIHdoZW4gdGhlIHN0cmVhbSBpcyBhbHJlYWR5XG4gICAgLy8gZXJyb3JlZC5cbiAgICBUcmFuc2Zvcm1TdHJlYW1FcnJvcihzdHJlYW0sIHJlYXNvbik7XG4gICAgcmV0dXJuIHByb21pc2VSZXNvbHZlZFdpdGgodW5kZWZpbmVkKTtcbn1cbmZ1bmN0aW9uIFRyYW5zZm9ybVN0cmVhbURlZmF1bHRTaW5rQ2xvc2VBbGdvcml0aG0oc3RyZWFtKSB7XG4gICAgLy8gc3RyZWFtLl9yZWFkYWJsZSBjYW5ub3QgY2hhbmdlIGFmdGVyIGNvbnN0cnVjdGlvbiwgc28gY2FjaGluZyBpdCBhY3Jvc3MgYSBjYWxsIHRvIHVzZXIgY29kZSBpcyBzYWZlLlxuICAgIGNvbnN0IHJlYWRhYmxlID0gc3RyZWFtLl9yZWFkYWJsZTtcbiAgICBjb25zdCBjb250cm9sbGVyID0gc3RyZWFtLl90cmFuc2Zvcm1TdHJlYW1Db250cm9sbGVyO1xuICAgIGNvbnN0IGZsdXNoUHJvbWlzZSA9IGNvbnRyb2xsZXIuX2ZsdXNoQWxnb3JpdGhtKCk7XG4gICAgVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXJDbGVhckFsZ29yaXRobXMoY29udHJvbGxlcik7XG4gICAgLy8gUmV0dXJuIGEgcHJvbWlzZSB0aGF0IGlzIGZ1bGZpbGxlZCB3aXRoIHVuZGVmaW5lZCBvbiBzdWNjZXNzLlxuICAgIHJldHVybiB0cmFuc2Zvcm1Qcm9taXNlV2l0aChmbHVzaFByb21pc2UsICgpID0+IHtcbiAgICAgICAgaWYgKHJlYWRhYmxlLl9zdGF0ZSA9PT0gJ2Vycm9yZWQnKSB7XG4gICAgICAgICAgICB0aHJvdyByZWFkYWJsZS5fc3RvcmVkRXJyb3I7XG4gICAgICAgIH1cbiAgICAgICAgUmVhZGFibGVTdHJlYW1EZWZhdWx0Q29udHJvbGxlckNsb3NlKHJlYWRhYmxlLl9yZWFkYWJsZVN0cmVhbUNvbnRyb2xsZXIpO1xuICAgIH0sIHIgPT4ge1xuICAgICAgICBUcmFuc2Zvcm1TdHJlYW1FcnJvcihzdHJlYW0sIHIpO1xuICAgICAgICB0aHJvdyByZWFkYWJsZS5fc3RvcmVkRXJyb3I7XG4gICAgfSk7XG59XG4vLyBUcmFuc2Zvcm1TdHJlYW1EZWZhdWx0U291cmNlIEFsZ29yaXRobXNcbmZ1bmN0aW9uIFRyYW5zZm9ybVN0cmVhbURlZmF1bHRTb3VyY2VQdWxsQWxnb3JpdGhtKHN0cmVhbSkge1xuICAgIC8vIEludmFyaWFudC4gRW5mb3JjZWQgYnkgdGhlIHByb21pc2VzIHJldHVybmVkIGJ5IHN0YXJ0KCkgYW5kIHB1bGwoKS5cbiAgICBUcmFuc2Zvcm1TdHJlYW1TZXRCYWNrcHJlc3N1cmUoc3RyZWFtLCBmYWxzZSk7XG4gICAgLy8gUHJldmVudCB0aGUgbmV4dCBwdWxsKCkgY2FsbCB1bnRpbCB0aGVyZSBpcyBiYWNrcHJlc3N1cmUuXG4gICAgcmV0dXJuIHN0cmVhbS5fYmFja3ByZXNzdXJlQ2hhbmdlUHJvbWlzZTtcbn1cbi8vIEhlbHBlciBmdW5jdGlvbnMgZm9yIHRoZSBUcmFuc2Zvcm1TdHJlYW1EZWZhdWx0Q29udHJvbGxlci5cbmZ1bmN0aW9uIGRlZmF1bHRDb250cm9sbGVyQnJhbmRDaGVja0V4Y2VwdGlvbiQxKG5hbWUpIHtcbiAgICByZXR1cm4gbmV3IFR5cGVFcnJvcihgVHJhbnNmb3JtU3RyZWFtRGVmYXVsdENvbnRyb2xsZXIucHJvdG90eXBlLiR7bmFtZX0gY2FuIG9ubHkgYmUgdXNlZCBvbiBhIFRyYW5zZm9ybVN0cmVhbURlZmF1bHRDb250cm9sbGVyYCk7XG59XG4vLyBIZWxwZXIgZnVuY3Rpb25zIGZvciB0aGUgVHJhbnNmb3JtU3RyZWFtLlxuZnVuY3Rpb24gc3RyZWFtQnJhbmRDaGVja0V4Y2VwdGlvbiQyKG5hbWUpIHtcbiAgICByZXR1cm4gbmV3IFR5cGVFcnJvcihgVHJhbnNmb3JtU3RyZWFtLnByb3RvdHlwZS4ke25hbWV9IGNhbiBvbmx5IGJlIHVzZWQgb24gYSBUcmFuc2Zvcm1TdHJlYW1gKTtcbn1cblxuZXhwb3J0IHsgQnl0ZUxlbmd0aFF1ZXVpbmdTdHJhdGVneSwgQ291bnRRdWV1aW5nU3RyYXRlZ3ksIFJlYWRhYmxlQnl0ZVN0cmVhbUNvbnRyb2xsZXIsIFJlYWRhYmxlU3RyZWFtLCBSZWFkYWJsZVN0cmVhbUJZT0JSZWFkZXIsIFJlYWRhYmxlU3RyZWFtQllPQlJlcXVlc3QsIFJlYWRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXIsIFJlYWRhYmxlU3RyZWFtRGVmYXVsdFJlYWRlciwgVHJhbnNmb3JtU3RyZWFtLCBUcmFuc2Zvcm1TdHJlYW1EZWZhdWx0Q29udHJvbGxlciwgV3JpdGFibGVTdHJlYW0sIFdyaXRhYmxlU3RyZWFtRGVmYXVsdENvbnRyb2xsZXIsIFdyaXRhYmxlU3RyZWFtRGVmYXVsdFdyaXRlciB9O1xuLy8jIHNvdXJjZU1hcHBpbmdVUkw9cG9ueWZpbGwuZXMyMDE4Lm1qcy5tYXBcbiIsIm1vZHVsZS5leHBvcnRzID0gcmVxdWlyZShcImNyeXB0b1wiKTs7IiwibW9kdWxlLmV4cG9ydHMgPSByZXF1aXJlKFwiaHR0cFwiKTs7IiwibW9kdWxlLmV4cG9ydHMgPSByZXF1aXJlKFwiaHR0cHNcIik7OyIsIm1vZHVsZS5leHBvcnRzID0gcmVxdWlyZShcInN0cmVhbVwiKTs7IiwibW9kdWxlLmV4cG9ydHMgPSByZXF1aXJlKFwidXJsXCIpOzsiLCJtb2R1bGUuZXhwb3J0cyA9IHJlcXVpcmUoXCJ1dGlsXCIpOzsiLCJtb2R1bGUuZXhwb3J0cyA9IHJlcXVpcmUoXCJ6bGliXCIpOzsiLCIvLyBUaGUgbW9kdWxlIGNhY2hlXG52YXIgX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fID0ge307XG5cbi8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG5mdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuXHRpZihfX3dlYnBhY2tfbW9kdWxlX2NhY2hlX19bbW9kdWxlSWRdKSB7XG5cdFx0cmV0dXJuIF9fd2VicGFja19tb2R1bGVfY2FjaGVfX1ttb2R1bGVJZF0uZXhwb3J0cztcblx0fVxuXHQvLyBDcmVhdGUgYSBuZXcgbW9kdWxlIChhbmQgcHV0IGl0IGludG8gdGhlIGNhY2hlKVxuXHR2YXIgbW9kdWxlID0gX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fW21vZHVsZUlkXSA9IHtcblx0XHQvLyBubyBtb2R1bGUuaWQgbmVlZGVkXG5cdFx0Ly8gbm8gbW9kdWxlLmxvYWRlZCBuZWVkZWRcblx0XHRleHBvcnRzOiB7fVxuXHR9O1xuXG5cdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuXHRfX3dlYnBhY2tfbW9kdWxlc19fW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuXHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuXHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG59XG5cbiIsIi8vIG1vZHVsZSBleHBvcnRzIG11c3QgYmUgcmV0dXJuZWQgZnJvbSBydW50aW1lIHNvIGVudHJ5IGlubGluaW5nIGlzIGRpc2FibGVkXG4vLyBzdGFydHVwXG4vLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbnJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKDk5MCk7XG4iLCIvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9ucyBmb3IgaGFybW9ueSBleHBvcnRzXG5fX3dlYnBhY2tfcmVxdWlyZV9fLmQgPSAoZXhwb3J0cywgZGVmaW5pdGlvbikgPT4ge1xuXHRmb3IodmFyIGtleSBpbiBkZWZpbml0aW9uKSB7XG5cdFx0aWYoX193ZWJwYWNrX3JlcXVpcmVfXy5vKGRlZmluaXRpb24sIGtleSkgJiYgIV9fd2VicGFja19yZXF1aXJlX18ubyhleHBvcnRzLCBrZXkpKSB7XG5cdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywga2V5LCB7IGVudW1lcmFibGU6IHRydWUsIGdldDogZGVmaW5pdGlvbltrZXldIH0pO1xuXHRcdH1cblx0fVxufTsiLCJfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSAob2JqLCBwcm9wKSA9PiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqLCBwcm9wKSIsIi8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbl9fd2VicGFja19yZXF1aXJlX18uciA9IChleHBvcnRzKSA9PiB7XG5cdGlmKHR5cGVvZiBTeW1ib2wgIT09ICd1bmRlZmluZWQnICYmIFN5bWJvbC50b1N0cmluZ1RhZykge1xuXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBTeW1ib2wudG9TdHJpbmdUYWcsIHsgdmFsdWU6ICdNb2R1bGUnIH0pO1xuXHR9XG5cdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG59OyJdLCJzb3VyY2VSb290IjoiIn0= + +/***/ }), + +/***/ 622: +/***/ (function(module) { + +module.exports = require("path"); + +/***/ }), + +/***/ 669: +/***/ (function(module) { + +module.exports = require("util"); + +/***/ }), + +/***/ 747: +/***/ (function(module) { + +module.exports = require("fs"); + +/***/ }), + +/***/ 761: +/***/ (function(module) { + +module.exports = require("zlib"); + +/***/ }), + +/***/ 762: +/***/ (function(module) { + +// API +module.exports = abort; + +/** + * Aborts leftover active jobs + * + * @param {object} state - current state object + */ +function abort(state) +{ + Object.keys(state.jobs).forEach(clean.bind(state)); + + // reset leftover jobs + state.jobs = {}; +} + +/** + * Cleans up leftover job by invoking abort function for the provided job id + * + * @this state + * @param {string|number} key - job id to abort + */ +function clean(key) +{ + if (typeof this.jobs[key] == 'function') + { + this.jobs[key](); + } +} + + +/***/ }), + +/***/ 769: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; +/*! + * mime-types + * Copyright(c) 2014 Jonathan Ong + * Copyright(c) 2015 Douglas Christopher Wilson + * MIT Licensed + */ + + + +/** + * Module dependencies. + * @private + */ + +var db = __webpack_require__(128) +var extname = __webpack_require__(622).extname + +/** + * Module variables. + * @private + */ + +var EXTRACT_TYPE_REGEXP = /^\s*([^;\s]*)(?:;|\s|$)/ +var TEXT_TYPE_REGEXP = /^text\//i + +/** + * Module exports. + * @public + */ + +exports.charset = charset +exports.charsets = { lookup: charset } +exports.contentType = contentType +exports.extension = extension +exports.extensions = Object.create(null) +exports.lookup = lookup +exports.types = Object.create(null) + +// Populate the extensions/types maps +populateMaps(exports.extensions, exports.types) + +/** + * Get the default charset for a MIME type. + * + * @param {string} type + * @return {boolean|string} + */ + +function charset (type) { + if (!type || typeof type !== 'string') { + return false + } + + // TODO: use media-typer + var match = EXTRACT_TYPE_REGEXP.exec(type) + var mime = match && db[match[1].toLowerCase()] + + if (mime && mime.charset) { + return mime.charset + } + + // default text/* to utf-8 + if (match && TEXT_TYPE_REGEXP.test(match[1])) { + return 'UTF-8' + } + + return false +} + +/** + * Create a full Content-Type header given a MIME type or extension. + * + * @param {string} str + * @return {boolean|string} + */ + +function contentType (str) { + // TODO: should this even be in this module? + if (!str || typeof str !== 'string') { + return false + } + + var mime = str.indexOf('/') === -1 + ? exports.lookup(str) + : str + + if (!mime) { + return false + } + + // TODO: use content-type or other module + if (mime.indexOf('charset') === -1) { + var charset = exports.charset(mime) + if (charset) mime += '; charset=' + charset.toLowerCase() + } + + return mime +} + +/** + * Get the default extension for a MIME type. + * + * @param {string} type + * @return {boolean|string} + */ + +function extension (type) { + if (!type || typeof type !== 'string') { + return false + } + + // TODO: use media-typer + var match = EXTRACT_TYPE_REGEXP.exec(type) + + // get extensions + var exts = match && exports.extensions[match[1].toLowerCase()] + + if (!exts || !exts.length) { + return false + } + + return exts[0] +} + +/** + * Lookup the MIME type for a file path/extension. + * + * @param {string} path + * @return {boolean|string} + */ + +function lookup (path) { + if (!path || typeof path !== 'string') { + return false + } + + // get the extension ("ext" or ".ext" or full path) + var extension = extname('x.' + path) + .toLowerCase() + .substr(1) + + if (!extension) { + return false + } + + return exports.types[extension] || false +} + +/** + * Populate the extensions and types maps. + * @private + */ + +function populateMaps (extensions, types) { + // source preference (least -> most) + var preference = ['nginx', 'apache', undefined, 'iana'] + + Object.keys(db).forEach(function forEachMimeType (type) { + var mime = db[type] + var exts = mime.extensions + + if (!exts || !exts.length) { + return + } + + // mime -> extensions + extensions[type] = exts + + // extension -> mime + for (var i = 0; i < exts.length; i++) { + var extension = exts[i] + + if (types[extension]) { + var from = preference.indexOf(db[types[extension]].source) + var to = preference.indexOf(mime.source) + + if (types[extension] !== 'application/octet-stream' && + (from > to || (from === to && types[extension].substr(0, 12) === 'application/'))) { + // skip the remapping + continue + } + } + + // set the extension -> mime + types[extension] = type + } + }) +} + + +/***/ }), + +/***/ 792: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var defer = __webpack_require__(154); + +// API +module.exports = async; + +/** + * Runs provided callback asynchronously + * even if callback itself is not + * + * @param {function} callback - callback to invoke + * @returns {function} - augmented callback + */ +function async(callback) +{ + var isAsync = false; + + // check if async happened + defer(function() { isAsync = true; }); + + return function async_callback(err, result) + { + if (isAsync) + { + callback(err, result); + } + else + { + defer(function nextTick_callback() + { + callback(err, result); + }); + } + }; +} + + +/***/ }), + +/***/ 835: +/***/ (function(module) { + +module.exports = require("url"); + +/***/ }) + +/******/ }); \ No newline at end of file diff --git a/.github/actions/send-email/index.js b/.github/actions/send-email/index.js new file mode 100644 index 0000000000..c898a55d8d --- /dev/null +++ b/.github/actions/send-email/index.js @@ -0,0 +1,75 @@ +/*! + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const core = require('@actions/core'); +const formData = require('form-data'); +const Mailgun = require('mailgun.js'); + +const mailgun = new Mailgun(formData); +const optionalFields = ['cc', 'text', 'html']; + +function loadConfig() { + return { + apiKey: core.getInput('api-key'), + domain: core.getInput('domain'), + to: core.getInput('to'), + from: core.getInput('from'), + cc: core.getInput('cc'), + subject: core.getInput('subject'), + text: core.getInput('text'), + html: core.getInput('html'), + } +} + +function validate(config) { + for (param in config) { + if (optionalFields.includes(param)) { + continue; + } + validateRequiredParameter(config[param], `'${param}'`); + } +} + +function validateRequiredParameter(value, name) { + if (!isNonEmptyString(value)) { + throw new Error(`${name} must be a non-empty string.`); + } +} + +function sendEmail(config) { + const mg = mailgun.client({ + username: 'api', + key: config.apiKey, + }); + + return mg.messages + .create(domain, config) + .then((resp) => { + core.setOutput('response', resp.message); + return; + }) + .catch((err) => { + core.setFailed(err.message); + }); +} + +function isNonEmptyString(value) { + return typeof value === 'string' && value !== ''; +} + +const config = loadConfig(); +validate(config); +sendEmail(config); diff --git a/.github/actions/send-email/package-lock.json b/.github/actions/send-email/package-lock.json new file mode 100644 index 0000000000..f75907db07 --- /dev/null +++ b/.github/actions/send-email/package-lock.json @@ -0,0 +1,173 @@ +{ + "name": "send-email", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@actions/core": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.6.tgz", + "integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA==" + }, + "@zeit/ncc": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@zeit/ncc/-/ncc-0.21.1.tgz", + "integrity": "sha512-M9WzgquSOt2nsjRkYM9LRylBLmmlwNCwYbm3Up3PDEshfvdmIfqpFNSK8EJvR18NwZjGHE5z2avlDtYQx2JQnw==", + "dev": true + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==" + }, + "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==", + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "fetch-blob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-2.1.1.tgz", + "integrity": "sha512-Uf+gxPCe1hTOFXwkxYyckn8iUSk6CFXGy5VENZKifovUTZC9eUODWSBhOBS7zICGrAetKzdwLMr85KhIcePMAQ==" + }, + "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==", + "requires": { + "isobject": "^3.0.1" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "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==" + }, + "ky": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/ky/-/ky-0.25.1.tgz", + "integrity": "sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA==" + }, + "ky-universal": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.8.2.tgz", + "integrity": "sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ==", + "requires": { + "abort-controller": "^3.0.0", + "node-fetch": "3.0.0-beta.9" + } + }, + "mailgun.js": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/mailgun.js/-/mailgun.js-3.3.0.tgz", + "integrity": "sha512-Ikcl9Lp18oXu8/ht6Ow3b2yRYBEa4S70YvquOM2O4FA4NDboa8btZIMEQRRFAmBEfEbWOPrl/Z6E8kL8vnyonQ==", + "requires": { + "bluebird": "^3.7.2", + "btoa": "^1.1.2", + "ky": "^0.25.1", + "ky-universal": "^0.8.2", + "url": "^0.11.0", + "url-join": "0.0.1", + "web-streams-polyfill": "^3.0.1", + "webpack-merge": "^5.4.0" + } + }, + "node-fetch": { + "version": "3.0.0-beta.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0-beta.9.tgz", + "integrity": "sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg==", + "requires": { + "data-uri-to-buffer": "^3.0.1", + "fetch-blob": "^2.1.1" + }, + "dependencies": { + "data-uri-to-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==" + } + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "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==", + "requires": { + "kind-of": "^6.0.2" + } + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "url-join": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-0.0.1.tgz", + "integrity": "sha1-HbSK1CLTQCRpqH99l73r/k+x48g=" + }, + "web-streams-polyfill": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.0.2.tgz", + "integrity": "sha512-JTNkNbAKoSo8NKiqu2UUaqRFCDWWZaCOsXuJEsToWopikTA0YHKKUf91GNkS/SnD8JixOkJjVsiacNlrFnRECA==" + }, + "webpack-merge": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.7.3.tgz", + "integrity": "sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA==", + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" + } + } +} diff --git a/.github/actions/send-email/package.json b/.github/actions/send-email/package.json new file mode 100644 index 0000000000..678a63992a --- /dev/null +++ b/.github/actions/send-email/package.json @@ -0,0 +1,23 @@ +{ + "name": "send-email", + "version": "1.0.0", + "description": "Send Emails from GitHub Actions workflows using Mailgun.", + "main": "index.js", + "scripts": { + "pack": "ncc build" + }, + "keywords": [ + "Firebase", + "Release", + "Automation" + ], + "author": "Firebase (https://firebase.google.com/)", + "license": "Apache-2.0", + "dependencies": { + "@actions/core": "^1.2.6", + "mailgun.js": "^3.3.0" + }, + "devDependencies": { + "@zeit/ncc": "^0.21.1" + } +} diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 10221dac24..6dd267cc3b 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -66,3 +66,33 @@ jobs: with: name: dist path: dist + + - name: Send email on failure + if: failure() + uses: ./.github/actions/send-email + with: + api-key: ${{ secrets.OSS_BOT_MAILGUN_KEY }} + domain: ${{ secrets.OSS_BOT_MAILGUN_DOMAIN }} + from: 'GitHub ' + to: ${{ secrets.FIREBASE_ADMIN_GITHUB_EMAIL }} + subject: '[${{github.repository}}] Nightly build failed!' + html: > + Nightly workflow failed on: ${{github.repository}} +

Navigate to the + failed workflow. + continue-on-error: true + + - name: Send email on cancelled + if: cancelled() + uses: ./.github/actions/send-email + with: + api-key: ${{ secrets.OSS_BOT_MAILGUN_KEY }} + domain: ${{ secrets.OSS_BOT_MAILGUN_DOMAIN }} + from: 'GitHub ' + to: ${{ secrets.FIREBASE_ADMIN_GITHUB_EMAIL }} + subject: '[${{github.repository}}] Nightly build got cancelled!' + html: > + Nightly workflow cancelled on: ${{github.repository}} +

Navigate to the + cancelled workflow. + continue-on-error: true diff --git a/test/integration/remote-config.spec.ts b/test/integration/remote-config.spec.ts index f8773c1b3c..eebab8a770 100644 --- a/test/integration/remote-config.spec.ts +++ b/test/integration/remote-config.spec.ts @@ -88,6 +88,12 @@ describe('admin.remoteConfig', () => { }).to.throw('Cannot set property etag of # which has only a getter'); }); + // A failing integration test to trigger the send email action. + // Remove this once the testing is complete. + it('A failing integration test to trigger nightly email notifications', () => { + expect('a').to.be.equal('b'); + }); + describe('validateTemplate', () => { it('should succeed with a vaild template', () => { // set parameters, groups, and conditions From e9e8a0395dbc1842b347f8bda7ebf274262d740d Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Thu, 1 Apr 2021 13:52:50 -0400 Subject: [PATCH 021/102] chore: Fix bug in send-email action code (#1214) * chore: Fix bug in send-email action code * Add run id and trigger on repo dispatch event --- .github/actions/send-email/dist/index.js | 9 ++++++++- .github/actions/send-email/index.js | 9 ++++++++- .github/workflows/nightly.yml | 15 +++++++++------ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/.github/actions/send-email/dist/index.js b/.github/actions/send-email/dist/index.js index 22a6566c3f..d1817b6db8 100644 --- a/.github/actions/send-email/dist/index.js +++ b/.github/actions/send-email/dist/index.js @@ -276,7 +276,14 @@ function sendEmail(config) { }); return mg.messages - .create(domain, config) + .create(config.domain, { + from: config.from, + to: config.to, + cc: config.cc, + subject: config.subject, + text: config.text, + html: config.html, + }) .then((resp) => { core.setOutput('response', resp.message); return; diff --git a/.github/actions/send-email/index.js b/.github/actions/send-email/index.js index c898a55d8d..38be9f04fc 100644 --- a/.github/actions/send-email/index.js +++ b/.github/actions/send-email/index.js @@ -56,7 +56,14 @@ function sendEmail(config) { }); return mg.messages - .create(domain, config) + .create(config.domain, { + from: config.from, + to: config.to, + cc: config.cc, + subject: config.subject, + text: config.text, + html: config.html, + }) .then((resp) => { core.setOutput('response', resp.message); return; diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 6dd267cc3b..3f440bcc1f 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -16,8 +16,11 @@ name: Nightly Builds on: # Runs every day at 06:00 AM (PT) and 08:00 PM (PT) / 04:00 AM (UTC) and 02:00 PM (UTC) + # or on 'firebase_build' repository dispatch event. schedule: - cron: "0 4,14 * * *" + repository_dispatch: + types: [firebase_build] jobs: nightly: @@ -75,11 +78,11 @@ jobs: domain: ${{ secrets.OSS_BOT_MAILGUN_DOMAIN }} from: 'GitHub ' to: ${{ secrets.FIREBASE_ADMIN_GITHUB_EMAIL }} - subject: '[${{github.repository}}] Nightly build failed!' + subject: 'Nightly build ${{github.run_id}} of ${{github.repository}} failed!' html: > - Nightly workflow failed on: ${{github.repository}} + Nightly workflow ${{github.run_id}} failed on: ${{github.repository}}

Navigate to the - failed workflow. + failed workflow. continue-on-error: true - name: Send email on cancelled @@ -90,9 +93,9 @@ jobs: domain: ${{ secrets.OSS_BOT_MAILGUN_DOMAIN }} from: 'GitHub ' to: ${{ secrets.FIREBASE_ADMIN_GITHUB_EMAIL }} - subject: '[${{github.repository}}] Nightly build got cancelled!' + subject: 'Nightly build ${{github.run_id}} of ${{github.repository}} cancelled!' html: > - Nightly workflow cancelled on: ${{github.repository}} + Nightly workflow ${{github.run_id}} cancelled on: ${{github.repository}}

Navigate to the - cancelled workflow. + cancelled workflow. continue-on-error: true From 1cc82ae629257d4d2d49ac6a100f152aef1e0b32 Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Thu, 1 Apr 2021 14:05:10 -0400 Subject: [PATCH 022/102] Change dispatch event name in nightly workflow (#1216) - Change dispatch event name to `firebase_nightly_build` --- .github/workflows/nightly.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 3f440bcc1f..a36144816b 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -16,11 +16,11 @@ name: Nightly Builds on: # Runs every day at 06:00 AM (PT) and 08:00 PM (PT) / 04:00 AM (UTC) and 02:00 PM (UTC) - # or on 'firebase_build' repository dispatch event. + # or on 'firebase_nightly_build' repository dispatch event. schedule: - cron: "0 4,14 * * *" repository_dispatch: - types: [firebase_build] + types: [firebase_nightly_build] jobs: nightly: From d961c3f705a8259762a796ac4f4d6a6dd0992eb1 Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Thu, 1 Apr 2021 18:08:56 -0400 Subject: [PATCH 023/102] chore: Clean up nightly workflow trigger tests (#1212) - Remove failing integration test added to trigger the send email workflow in nightly builds. --- test/integration/remote-config.spec.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/integration/remote-config.spec.ts b/test/integration/remote-config.spec.ts index eebab8a770..f8773c1b3c 100644 --- a/test/integration/remote-config.spec.ts +++ b/test/integration/remote-config.spec.ts @@ -88,12 +88,6 @@ describe('admin.remoteConfig', () => { }).to.throw('Cannot set property etag of # which has only a getter'); }); - // A failing integration test to trigger the send email action. - // Remove this once the testing is complete. - it('A failing integration test to trigger nightly email notifications', () => { - expect('a').to.be.equal('b'); - }); - describe('validateTemplate', () => { it('should succeed with a vaild template', () => { // set parameters, groups, and conditions From da0f44f7e2d80a8a3f79bd34959663a01d768c0e Mon Sep 17 00:00:00 2001 From: Abe Haskins Date: Thu, 8 Apr 2021 14:15:08 -0500 Subject: [PATCH 024/102] Add support for FIREBASE_STORAGE_EMULATOR_HOST env var (#1175) * Add support for FIREBASE_STORAGE_EMULATOR_HOST env var * Fixes lint error * Add test for FIREBASE_STORAGE_EMULATOR_HOST support * Lint fix * Minor fixes to storage tests * Address review comments * Address review suggestion Co-authored-by: Samuel Bushi --- src/storage/storage.ts | 4 ++++ test/unit/storage/storage.spec.ts | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/storage/storage.ts b/src/storage/storage.ts index 4364658a8c..aabf9dd30b 100644 --- a/src/storage/storage.ts +++ b/src/storage/storage.ts @@ -48,6 +48,10 @@ export class Storage implements StorageInterface { }); } + if (!process.env.STORAGE_EMULATOR_HOST && process.env.FIREBASE_STORAGE_EMULATOR_HOST) { + process.env.STORAGE_EMULATOR_HOST = process.env.FIREBASE_STORAGE_EMULATOR_HOST; + } + let storage: typeof StorageClient; try { storage = require('@google-cloud/storage').Storage; diff --git a/test/unit/storage/storage.spec.ts b/test/unit/storage/storage.spec.ts index 997d44846e..69ea28b217 100644 --- a/test/unit/storage/storage.spec.ts +++ b/test/unit/storage/storage.spec.ts @@ -113,4 +113,24 @@ describe('Storage', () => { expect(storage.bucket('foo').name).to.equal('foo'); }); }); + + describe('Emulator mode', () => { + const EMULATOR_HOST = 'http://localhost:9199'; + + before(() => { + delete process.env.STORAGE_EMULATOR_HOST; + process.env.FIREBASE_STORAGE_EMULATOR_HOST = EMULATOR_HOST; + }); + + it('sets STORAGE_EMULATOR_HOST if FIREBASE_STORAGE_EMULATOR_HOST is set', () => { + new Storage(mockApp); + + expect(process.env.STORAGE_EMULATOR_HOST).to.equal(EMULATOR_HOST); + }); + + after(() => { + delete process.env.STORAGE_EMULATOR_HOST; + delete process.env.FIREBASE_STORAGE_EMULATOR_HOST; + }); + }) }); From 011c53043896587e2d41228b3c8fdc463faff56a Mon Sep 17 00:00:00 2001 From: Yuchen Shi Date: Wed, 14 Apr 2021 11:23:20 -0700 Subject: [PATCH 025/102] Revert "Disable one flaky tests in emulator. (#1205)" (#1227) This reverts commit 19660d921d20732857bf54393a09e8b5bce15d63. --- test/integration/auth.spec.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/test/integration/auth.spec.ts b/test/integration/auth.spec.ts index 53174f2ff5..29060b4cdd 100644 --- a/test/integration/auth.spec.ts +++ b/test/integration/auth.spec.ts @@ -441,12 +441,8 @@ describe('admin.auth', () => { .then((listUsersResult) => { // Confirm expected number of users. expect(listUsersResult.users.length).to.equal(2); - // TODO(yuchenshi): Investigate on why this is flaky in emulator. - if (!authEmulatorHost) { - // Confirm next page token present. - expect(typeof listUsersResult.pageToken).to.equal('string'); - } - + // Confirm next page token present. + expect(typeof listUsersResult.pageToken).to.equal('string'); // Confirm each user's uid and the hashed passwords. expect(listUsersResult.users[0].uid).to.equal(uids[1]); From 58f60d680bc47641ef0216fbe8eff07afb030e5b Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Fri, 23 Apr 2021 10:44:19 -0700 Subject: [PATCH 026/102] fix(rtdb): Fixing a token refresh livelock in Cloud Functions (#1234) --- src/database/database-internal.ts | 21 +++++++++------------ src/firebase-app.ts | 6 ++++++ test/unit/database/database.spec.ts | 4 +--- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/database/database-internal.ts b/src/database/database-internal.ts index 66d8db883c..9ad9e5bb51 100644 --- a/src/database/database-internal.ts +++ b/src/database/database-internal.ts @@ -116,18 +116,15 @@ export class DatabaseService { // eslint-disable-next-line @typescript-eslint/no-unused-vars private onTokenChange(_: string): void { - this.appInternal.INTERNAL.getToken() - .then((token) => { - const delayMillis = token.expirationTime - TOKEN_REFRESH_THRESHOLD_MILLIS - Date.now(); - // If the new token is set to expire soon (unlikely), do nothing. Somebody will eventually - // notice and refresh the token, at which point this callback will fire again. - if (delayMillis > 0) { - this.scheduleTokenRefresh(delayMillis); - } - }) - .catch((err) => { - console.error('Unexpected error while attempting to schedule a token refresh:', err); - }); + const token = this.appInternal.INTERNAL.getCachedToken(); + if (token) { + const delayMillis = token.expirationTime - TOKEN_REFRESH_THRESHOLD_MILLIS - Date.now(); + // If the new token is set to expire soon (unlikely), do nothing. Somebody will eventually + // notice and refresh the token, at which point this callback will fire again. + if (delayMillis > 0) { + this.scheduleTokenRefresh(delayMillis); + } + } } private scheduleTokenRefresh(delayMillis: number): void { diff --git a/src/firebase-app.ts b/src/firebase-app.ts index 88947bec4c..c9e9588d59 100644 --- a/src/firebase-app.ts +++ b/src/firebase-app.ts @@ -74,6 +74,10 @@ export class FirebaseAppInternals { return Promise.resolve(this.cachedToken_); } + public getCachedToken(): FirebaseAccessToken | null { + return this.cachedToken_ || null; + } + private refreshToken(): Promise { return Promise.resolve(this.credential_.getAccessToken()) .then((result) => { @@ -97,6 +101,8 @@ export class FirebaseAppInternals { if (!this.cachedToken_ || this.cachedToken_.accessToken !== token.accessToken || this.cachedToken_.expirationTime !== token.expirationTime) { + // Update the cache before firing listeners. Listeners may directly query the + // cached token state. this.cachedToken_ = token; this.tokenListeners_.forEach((listener) => { listener(token.accessToken); diff --git a/test/unit/database/database.spec.ts b/test/unit/database/database.spec.ts index 739dcbccfc..763041262e 100644 --- a/test/unit/database/database.spec.ts +++ b/test/unit/database/database.spec.ts @@ -211,9 +211,7 @@ describe('Database', () => { }); }); - // Currently doesn't work as expected since onTokenChange() can force a token refresh - // by calling getToken(). Skipping for now. - xit('should not reschedule when the token is about to expire in 5 minutes', () => { + it('should not reschedule when the token is about to expire in 5 minutes', () => { database.getDatabase(); return mockApp.INTERNAL.getToken() .then((token1) => { From be4ebc61ed013315ffad954575f6223d44b5a80a Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Tue, 27 Apr 2021 16:53:29 -0400 Subject: [PATCH 027/102] [chore] Release 9.7.0 (#1240) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 240abe3cd7..28a3112ff7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-admin", - "version": "9.6.0", + "version": "9.7.0", "description": "Firebase admin SDK for Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", From c6e9ef7a9c84eb565e53eb813b8c2204933de52d Mon Sep 17 00:00:00 2001 From: bojeil-google Date: Mon, 3 May 2021 14:14:31 -0700 Subject: [PATCH 028/102] fix: adds missing EMAIL_NOT_FOUND error code (#1246) Catch `EMAIL_NOT_FOUND` and translate to `auth/email-not-found` when `/accounts:sendOobCode` is called for password reset on a user that does not exist. Fixes https://github.com/firebase/firebase-admin-node/issues/1202 --- src/utils/error.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/utils/error.ts b/src/utils/error.ts index 5809a294b6..adc1b852b8 100644 --- a/src/utils/error.ts +++ b/src/utils/error.ts @@ -374,6 +374,10 @@ export class AuthClientErrorCode { code: 'email-already-exists', message: 'The email address is already in use by another account.', }; + public static EMAIL_NOT_FOUND = { + code: 'email-not-found', + message: 'There is no user record corresponding to the provided email.', + }; public static FORBIDDEN_CLAIM = { code: 'reserved-claim', message: 'The specified developer claim is reserved and cannot be specified.', @@ -854,6 +858,8 @@ const AUTH_SERVER_TO_CLIENT_CODE: ServerToClientCode = { DUPLICATE_MFA_ENROLLMENT_ID: 'SECOND_FACTOR_UID_ALREADY_EXISTS', // setAccountInfo email already exists. EMAIL_EXISTS: 'EMAIL_ALREADY_EXISTS', + // /accounts:sendOobCode for password reset when user is not found. + EMAIL_NOT_FOUND: 'EMAIL_NOT_FOUND', // Reserved claim name. FORBIDDEN_CLAIM: 'FORBIDDEN_CLAIM', // Invalid claims provided. From d8b769a306281ab2baf0a6fd16ec4aa58d474377 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 May 2021 11:23:40 -0700 Subject: [PATCH 029/102] build(deps-dev): bump lodash from 4.17.19 to 4.17.21 (#1255) Bumps [lodash](https://github.com/lodash/lodash) from 4.17.19 to 4.17.21. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.19...4.17.21) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index f4875def9a..d455f9a489 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firebase-admin", - "version": "9.4.2", + "version": "9.7.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -5307,9 +5307,9 @@ } }, "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "lodash._basecopy": { From 43bfbd9a7c9a16825e30588bb1cb47c8ce2214bd Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Thu, 6 May 2021 11:35:25 -0700 Subject: [PATCH 030/102] chore: Upgraded RTDB and other @firebase dependencies (#1250) --- package-lock.json | 138 ++++++++++++++++++++++++++++------------------ package.json | 10 ++-- 2 files changed, 89 insertions(+), 59 deletions(-) diff --git a/package-lock.json b/package-lock.json index d455f9a489..0f5e8a1210 100644 --- a/package-lock.json +++ b/package-lock.json @@ -156,94 +156,115 @@ } }, "@firebase/app": { - "version": "0.6.13", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.6.13.tgz", - "integrity": "sha512-xGrJETzvCb89VYbGSHFHCW7O/y067HRxT7MGehUE1xMxdPVBDNayHnxEuKwzfGvXAjVmajXBKFlKxaCWpgSjCQ==", + "version": "0.6.21", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.6.21.tgz", + "integrity": "sha512-SpWXdy/U06gTOEofSjhcsFGUtYmZim7ty6U4eMUQH0ObtymeVdTiK4tJcohMT5XoihQw4CLS2YvDySwx3+BlWg==", "dev": true, "requires": { - "@firebase/app-types": "0.6.1", - "@firebase/component": "0.1.21", + "@firebase/app-types": "0.6.2", + "@firebase/component": "0.5.0", "@firebase/logger": "0.2.6", - "@firebase/util": "0.3.4", + "@firebase/util": "1.1.0", "dom-storage": "2.1.0", - "tslib": "^1.11.1", + "tslib": "^2.1.0", "xmlhttprequest": "1.8.0" + }, + "dependencies": { + "tslib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", + "dev": true + } } }, "@firebase/app-types": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.6.1.tgz", - "integrity": "sha512-L/ZnJRAq7F++utfuoTKX4CLBG5YR7tFO3PLzG1/oXXKEezJ0kRL3CMRoueBEmTCzVb/6SIs2Qlaw++uDgi5Xyg==" + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.6.2.tgz", + "integrity": "sha512-2VXvq/K+n8XMdM4L2xy5bYp2ZXMawJXluUIDzUBvMthVR+lhxK4pfFiqr1mmDbv9ydXvEAuFsD+6DpcZuJcSSw==" }, "@firebase/auth": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.16.2.tgz", - "integrity": "sha512-68TlDL0yh3kF8PiCzI8m8RWd/bf/xCLUsdz1NZ2Dwea0sp6e2WAhu0sem1GfhwuEwL+Ns4jCdX7qbe/OQlkVEA==", + "version": "0.16.5", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.16.5.tgz", + "integrity": "sha512-Cgs/TlVot2QkbJyEphvKmu+2qxYlNN+Q2+29aqZwryrnn1eLwlC7nT89K6O91/744HJRtiThm02bMj2Wh61E3Q==", "dev": true, "requires": { - "@firebase/auth-types": "0.10.1" + "@firebase/auth-types": "0.10.3" } }, "@firebase/auth-interop-types": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.5.tgz", - "integrity": "sha512-88h74TMQ6wXChPA6h9Q3E1Jg6TkTHep2+k63OWg3s0ozyGVMeY+TTOti7PFPzq5RhszQPQOoCi59es4MaRvgCw==" + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.6.tgz", + "integrity": "sha512-etIi92fW3CctsmR9e3sYM3Uqnoq861M0Id9mdOPF6PWIg38BXL5k4upCNBggGUpLIS0H1grMOvy/wn1xymwe2g==" }, "@firebase/auth-types": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.10.1.tgz", - "integrity": "sha512-/+gBHb1O9x/YlG7inXfxff/6X3BPZt4zgBv4kql6HEmdzNQCodIRlEYnI+/da+lN+dha7PjaFH7C7ewMmfV7rw==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.10.3.tgz", + "integrity": "sha512-zExrThRqyqGUbXOFrH/sowuh2rRtfKHp9SBVY2vOqKWdCX1Ztn682n9WLtlUDsiYVIbBcwautYWk2HyCGFv0OA==", "dev": true }, "@firebase/component": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.1.21.tgz", - "integrity": "sha512-kd5sVmCLB95EK81Pj+yDTea8pzN2qo/1yr0ua9yVi6UgMzm6zAeih73iVUkaat96MAHy26yosMufkvd3zC4IKg==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.0.tgz", + "integrity": "sha512-v18csWtXb0ri+3m7wuGLY/UDgcb89vuMlZGQ//+7jEPLIQeLbylvZhol1uzW9WzoOpxMxOS2W5qyVGX36wZvEA==", "dev": true, "requires": { - "@firebase/util": "0.3.4", - "tslib": "^1.11.1" + "@firebase/util": "1.1.0", + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", + "dev": true + } } }, "@firebase/database": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.8.1.tgz", - "integrity": "sha512-/1HhR4ejpqUaM9Cn3KSeNdQvdlehWIhdfTVWFxS73ZlLYf7ayk9jITwH10H3ZOIm5yNzxF67p/U7Z/0IPhgWaQ==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.10.0.tgz", + "integrity": "sha512-GsHvuES83Edtboij2h3txKg+yV/TD4b5Owc01SgXEQtvj1lulkHt4Ufmd9OZz1WreWQJMIqKpbVowIDHjlkZJQ==", "requires": { - "@firebase/auth-interop-types": "0.1.5", - "@firebase/component": "0.1.21", - "@firebase/database-types": "0.6.1", + "@firebase/auth-interop-types": "0.1.6", + "@firebase/component": "0.5.0", + "@firebase/database-types": "0.7.2", "@firebase/logger": "0.2.6", - "@firebase/util": "0.3.4", + "@firebase/util": "1.1.0", "faye-websocket": "0.11.3", - "tslib": "^1.11.1" + "tslib": "^2.1.0" }, "dependencies": { "@firebase/component": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.1.21.tgz", - "integrity": "sha512-kd5sVmCLB95EK81Pj+yDTea8pzN2qo/1yr0ua9yVi6UgMzm6zAeih73iVUkaat96MAHy26yosMufkvd3zC4IKg==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.0.tgz", + "integrity": "sha512-v18csWtXb0ri+3m7wuGLY/UDgcb89vuMlZGQ//+7jEPLIQeLbylvZhol1uzW9WzoOpxMxOS2W5qyVGX36wZvEA==", "requires": { - "@firebase/util": "0.3.4", - "tslib": "^1.11.1" + "@firebase/util": "1.1.0", + "tslib": "^2.1.0" } }, "@firebase/util": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.3.4.tgz", - "integrity": "sha512-VwjJUE2Vgr2UMfH63ZtIX9Hd7x+6gayi6RUXaTqEYxSbf/JmehLmAEYSuxS/NckfzAXWeGnKclvnXVibDgpjQQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.1.0.tgz", + "integrity": "sha512-lfuSASuPKNdfebuFR8rjFamMQUPH9iiZHcKS755Rkm/5gRT0qC7BMhCh3ZkHf7NVbplzIc/GhmX2jM+igDRCag==", "requires": { - "tslib": "^1.11.1" + "tslib": "^2.1.0" } + }, + "tslib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" } } }, "@firebase/database-types": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.6.1.tgz", - "integrity": "sha512-JtL3FUbWG+bM59iYuphfx9WOu2Mzf0OZNaqWiQ7lJR8wBe7bS9rIm9jlBFtksB7xcya1lZSQPA/GAy2jIlMIkA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.7.2.tgz", + "integrity": "sha512-cdAd/dgwvC0r3oLEDUR+ULs1vBsEvy0b27nlzKhU6LQgm9fCDzgaH9nFGv8x+S9dly4B0egAXkONkVoWcOAisg==", "requires": { - "@firebase/app-types": "0.6.1" + "@firebase/app-types": "0.6.2" } }, "@firebase/logger": { @@ -252,12 +273,20 @@ "integrity": "sha512-KIxcUvW/cRGWlzK9Vd2KB864HlUnCfdTH0taHE0sXW5Xl7+W68suaeau1oKNEqmc3l45azkd4NzXTCWZRZdXrw==" }, "@firebase/util": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.3.4.tgz", - "integrity": "sha512-VwjJUE2Vgr2UMfH63ZtIX9Hd7x+6gayi6RUXaTqEYxSbf/JmehLmAEYSuxS/NckfzAXWeGnKclvnXVibDgpjQQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.1.0.tgz", + "integrity": "sha512-lfuSASuPKNdfebuFR8rjFamMQUPH9iiZHcKS755Rkm/5gRT0qC7BMhCh3ZkHf7NVbplzIc/GhmX2jM+igDRCag==", "dev": true, "requires": { - "tslib": "^1.11.1" + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", + "dev": true + } } }, "@google-cloud/common": { @@ -4338,9 +4367,9 @@ } }, "http-parser-js": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz", - "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==" + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", + "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" }, "http-proxy-agent": { "version": "4.0.1", @@ -8848,7 +8877,8 @@ "tslib": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true }, "tsutils": { "version": "3.17.1", diff --git a/package.json b/package.json index 28a3112ff7..1d5132c8c1 100644 --- a/package.json +++ b/package.json @@ -56,8 +56,8 @@ ], "types": "./lib/index.d.ts", "dependencies": { - "@firebase/database": "^0.8.1", - "@firebase/database-types": "^0.6.1", + "@firebase/database": "^0.10.0", + "@firebase/database-types": "^0.7.2", "@types/node": "^10.10.0", "dicer": "^0.3.0", "jsonwebtoken": "^8.5.1", @@ -68,9 +68,9 @@ "@google-cloud/storage": "^5.3.0" }, "devDependencies": { - "@firebase/app": "^0.6.13", - "@firebase/auth": "^0.16.2", - "@firebase/auth-types": "^0.10.1", + "@firebase/app": "^0.6.21", + "@firebase/auth": "^0.16.5", + "@firebase/auth-types": "^0.10.3", "@microsoft/api-extractor": "^7.11.2", "@types/bcrypt": "^2.0.0", "@types/chai": "^4.0.0", From e65dbcfbf505c1a7ac4929a3770f170ce0c367bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 May 2021 12:14:28 -0700 Subject: [PATCH 031/102] build(deps): bump y18n from 3.2.1 to 3.2.2 (#1208) Bumps [y18n](https://github.com/yargs/y18n) from 3.2.1 to 3.2.2. - [Release notes](https://github.com/yargs/y18n/releases) - [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md) - [Commits](https://github.com/yargs/y18n/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0f5e8a1210..2192ef23ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -568,12 +568,6 @@ "strip-ansi": "^6.0.0" } }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "optional": true - }, "yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", @@ -3955,6 +3949,12 @@ "yargs": "^7.1.0" } }, + "y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "dev": true + }, "yargs": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz", @@ -6129,12 +6129,6 @@ "strip-ansi": "^5.0.0" } }, - "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", - "dev": true - }, "yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", @@ -6814,12 +6808,6 @@ "strip-ansi": "^5.0.0" } }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, "yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", @@ -9476,10 +9464,9 @@ "dev": true }, "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" }, "yallist": { "version": "3.1.1", @@ -9586,9 +9573,9 @@ } }, "y18n": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.4.tgz", - "integrity": "sha512-deLOfD+RvFgrpAmSZgfGdWYE+OKyHcVHaRQ7NphG/63scpRvTHHeQMAxGGvaLVGJ+HYVcCXlzcTK0ZehFf+eHQ==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yargs-parser": { From 3ae6a69b81197efcc29438142493d50dc8e6be88 Mon Sep 17 00:00:00 2001 From: Abe Haskins Date: Mon, 10 May 2021 12:23:23 -0500 Subject: [PATCH 032/102] Fix storage emulator env formatting (#1257) * Fix storage emulator env formatting * Repair test * Rename test * Dang tests 2 good 4 me * Fix test * Fix tests again --- src/storage/storage.ts | 11 ++++++++++- test/unit/storage/storage.spec.ts | 23 ++++++++++++++++------- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/storage/storage.ts b/src/storage/storage.ts index aabf9dd30b..7bac81ff46 100644 --- a/src/storage/storage.ts +++ b/src/storage/storage.ts @@ -49,7 +49,16 @@ export class Storage implements StorageInterface { } if (!process.env.STORAGE_EMULATOR_HOST && process.env.FIREBASE_STORAGE_EMULATOR_HOST) { - process.env.STORAGE_EMULATOR_HOST = process.env.FIREBASE_STORAGE_EMULATOR_HOST; + const firebaseStorageEmulatorHost = process.env.FIREBASE_STORAGE_EMULATOR_HOST; + + if (firebaseStorageEmulatorHost.match(/https?:\/\//)) { + throw new FirebaseError({ + code: 'storage/invalid-emulator-host', + message: 'FIREBASE_STORAGE_EMULATOR_HOST should not contain a protocol (http or https).', + }); + } + + process.env.STORAGE_EMULATOR_HOST = `http://${process.env.FIREBASE_STORAGE_EMULATOR_HOST}`; } let storage: typeof StorageClient; diff --git a/test/unit/storage/storage.spec.ts b/test/unit/storage/storage.spec.ts index 69ea28b217..e7d2360ee9 100644 --- a/test/unit/storage/storage.spec.ts +++ b/test/unit/storage/storage.spec.ts @@ -114,18 +114,27 @@ describe('Storage', () => { }); }); - describe('Emulator mode', () => { - const EMULATOR_HOST = 'http://localhost:9199'; + describe.only('Emulator mode', () => { + const VALID_EMULATOR_HOST = 'localhost:9199'; + const INVALID_EMULATOR_HOST = 'https://localhost:9199'; - before(() => { + beforeEach(() => { delete process.env.STORAGE_EMULATOR_HOST; - process.env.FIREBASE_STORAGE_EMULATOR_HOST = EMULATOR_HOST; + delete process.env.FIREBASE_STORAGE_EMULATOR_HOST; }); it('sets STORAGE_EMULATOR_HOST if FIREBASE_STORAGE_EMULATOR_HOST is set', () => { - new Storage(mockApp); - - expect(process.env.STORAGE_EMULATOR_HOST).to.equal(EMULATOR_HOST); + process.env.FIREBASE_STORAGE_EMULATOR_HOST = VALID_EMULATOR_HOST; + + new Storage(mockApp) + expect(process.env.STORAGE_EMULATOR_HOST).to.equal(`http://${VALID_EMULATOR_HOST}`); + }); + + it('throws if FIREBASE_STORAGE_EMULATOR_HOST has a protocol', () => { + process.env.FIREBASE_STORAGE_EMULATOR_HOST = INVALID_EMULATOR_HOST; + + expect(() => new Storage(mockApp)).to.throw( + 'FIREBASE_STORAGE_EMULATOR_HOST should not contain a protocol'); }); after(() => { From 8267b56e77283fcb366d88b0d3f8a98eeb51ab2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 May 2021 10:45:17 -0700 Subject: [PATCH 033/102] build(deps): bump hosted-git-info from 2.8.8 to 2.8.9 (#1260) Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9. - [Release notes](https://github.com/npm/hosted-git-info/releases) - [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md) - [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Hiranya Jayathilaka --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2192ef23ef..ec28e32334 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4319,9 +4319,9 @@ } }, "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==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "html-encoding-sniffer": { From b88f8919c67c252b31d6b4b455fd7ba9b6226131 Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Mon, 10 May 2021 16:22:51 -0400 Subject: [PATCH 034/102] feat: Add abuse reduction support (#1264) - Add abuse reduction support APIs --- .github/scripts/run_integration_tests.sh | 2 + .github/workflows/nightly.yml | 1 + .github/workflows/release.yml | 1 + .gitignore | 1 + docgen/content-sources/node/toc.yaml | 12 + etc/firebase-admin.api.md | 33 ++ package-lock.json | 166 ++++++++- package.json | 1 + .../app-check-api-client-internal.ts | 228 ++++++++++++ src/app-check/app-check.ts | 86 +++++ src/app-check/index.ts | 164 +++++++++ src/app-check/token-generator.ts | 145 ++++++++ src/app-check/token-verifier.ts | 165 +++++++++ src/auth/auth.ts | 21 +- src/auth/token-generator.ts | 238 +++---------- src/firebase-app.ts | 13 + src/firebase-namespace-api.ts | 2 + src/firebase-namespace.d.ts | 1 + src/firebase-namespace.ts | 14 + src/utils/crypto-signer.ts | 250 +++++++++++++ src/utils/jwt.ts | 101 +++++- test/integration/app-check.spec.ts | 103 ++++++ test/resources/mock.jwks.json | 12 + test/resources/mocks.ts | 35 ++ .../app-check-api-client-internal.spec.ts | 238 +++++++++++++ test/unit/app-check/app-check.spec.ts | 195 +++++++++++ test/unit/app-check/token-generator.spec.ts | 261 ++++++++++++++ test/unit/app-check/token-verifier.spec.ts | 245 +++++++++++++ test/unit/auth/token-generator.spec.ts | 245 +++---------- test/unit/auth/token-verifier.spec.ts | 3 +- test/unit/firebase-app.spec.ts | 28 ++ test/unit/firebase-namespace.spec.ts | 41 +++ test/unit/firebase.spec.ts | 15 + test/unit/index.spec.ts | 7 + test/unit/utils/crypto-signer.spec.ts | 224 ++++++++++++ test/unit/utils/jwt.spec.ts | 327 ++++++++++++++---- 36 files changed, 3154 insertions(+), 470 deletions(-) create mode 100644 src/app-check/app-check-api-client-internal.ts create mode 100644 src/app-check/app-check.ts create mode 100644 src/app-check/index.ts create mode 100644 src/app-check/token-generator.ts create mode 100644 src/app-check/token-verifier.ts create mode 100644 src/utils/crypto-signer.ts create mode 100644 test/integration/app-check.spec.ts create mode 100644 test/resources/mock.jwks.json create mode 100644 test/unit/app-check/app-check-api-client-internal.spec.ts create mode 100644 test/unit/app-check/app-check.spec.ts create mode 100644 test/unit/app-check/token-generator.spec.ts create mode 100644 test/unit/app-check/token-verifier.spec.ts create mode 100644 test/unit/utils/crypto-signer.spec.ts diff --git a/.github/scripts/run_integration_tests.sh b/.github/scripts/run_integration_tests.sh index fd479df552..37dc7d1216 100755 --- a/.github/scripts/run_integration_tests.sh +++ b/.github/scripts/run_integration_tests.sh @@ -22,4 +22,6 @@ gpg --quiet --batch --yes --decrypt --passphrase="${FIREBASE_SERVICE_ACCT_KEY}" echo "${FIREBASE_API_KEY}" > test/resources/apikey.txt +echo "${FIREBASE_APP_ID}" > test/resources/appid.txt + npm run test:integration -- --updateRules --testMultiTenancy diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index a36144816b..536827b1e5 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -55,6 +55,7 @@ jobs: env: FIREBASE_SERVICE_ACCT_KEY: ${{ secrets.FIREBASE_SERVICE_ACCT_KEY }} FIREBASE_API_KEY: ${{ secrets.FIREBASE_API_KEY }} + FIREBASE_APP_ID: ${{ secrets.FIREBASE_APP_ID }} - name: Package release artifacts run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4740665e9c..d2a2797765 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -66,6 +66,7 @@ jobs: env: FIREBASE_SERVICE_ACCT_KEY: ${{ secrets.FIREBASE_SERVICE_ACCT_KEY }} FIREBASE_API_KEY: ${{ secrets.FIREBASE_API_KEY }} + FIREBASE_APP_ID: ${{ secrets.FIREBASE_APP_ID }} - name: Package release artifacts run: | diff --git a/.gitignore b/.gitignore index 672f8c23a9..4c60db05ce 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ node_modules/ # Real key file should not be checked in test/resources/key.json test/resources/apikey.txt +test/resources/appid.txt # Release tarballs should not be checked in firebase-admin-*.tgz diff --git a/docgen/content-sources/node/toc.yaml b/docgen/content-sources/node/toc.yaml index 487d3fcc39..aaf1eb562d 100644 --- a/docgen/content-sources/node/toc.yaml +++ b/docgen/content-sources/node/toc.yaml @@ -19,6 +19,18 @@ toc: - title: "App" path: /docs/reference/admin/node/admin.app.App-1 +- title: "admin.appCheck" + path: /docs/reference/admin/node/admin.appCheck + section: + - title: "AppCheck" + path: /docs/reference/admin/node/admin.appCheck.AppCheck-1 + - title: "AppCheckToken" + path: /docs/reference/admin/node/admin.appCheck.AppCheckToken + - title: "DecodedAppCheckToken" + path: /docs/reference/admin/node/admin.appCheck.DecodedAppCheckToken + - title: "VerifyAppCheckTokenResponse" + path: /docs/reference/admin/node/admin.appCheck.VerifyAppCheckTokenResponse + - title: "admin.auth" path: /docs/reference/admin/node/admin.auth section: diff --git a/etc/firebase-admin.api.md b/etc/firebase-admin.api.md index eaf280aec8..39c413b7ec 100644 --- a/etc/firebase-admin.api.md +++ b/etc/firebase-admin.api.md @@ -15,6 +15,8 @@ export function app(name?: string): app.App; // @public (undocumented) export namespace app { export interface App { + // (undocumented) + appCheck(): appCheck.AppCheck; // (undocumented) auth(): auth.Auth; // (undocumented) @@ -41,6 +43,37 @@ export namespace app { } } +// @public +export function appCheck(app?: app.App): appCheck.AppCheck; + +// @public (undocumented) +export namespace appCheck { + export interface AppCheck { + // (undocumented) + app: app.App; + createToken(appId: string): Promise; + verifyToken(appCheckToken: string): Promise; + } + export interface AppCheckToken { + token: string; + ttlMillis: number; + } + export interface DecodedAppCheckToken { + // (undocumented) + [key: string]: any; + app_id: string; + aud: string[]; + exp: number; + iat: number; + iss: string; + sub: string; + } + export interface VerifyAppCheckTokenResponse { + appId: string; + token: appCheck.DecodedAppCheckToken; + } +} + // @public export interface AppOptions { credential?: credential.Credential; diff --git a/package-lock.json b/package-lock.json index ec28e32334..99e55cda11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -664,6 +664,11 @@ "integrity": "sha512-IpgPxHrNxZiMNUSXqR1l/gePKPkfAmIKoDRP9hp7OwjU29ZR8WCJsOJ8iBKgw0Qk+pFwR+8Y1cy8ImLY6e9m4A==", "dev": true }, + "@panva/asn1.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz", + "integrity": "sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==" + }, "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -876,6 +881,15 @@ "integrity": "sha512-dIOxFfI0C+jz89g6lQ+TqhGgPQ0MxSnh/E4xuC0blhFtyW269+mPG5QeLgbdwst/LvdP8o1y0o/Gz5EHXLec/g==", "dev": true }, + "@types/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, "@types/caseless": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", @@ -903,15 +917,61 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, + "@types/connect": { + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", + "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==", + "requires": { + "@types/node": "*" + } + }, "@types/eslint-visitor-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", "dev": true }, + "@types/express": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz", + "integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-jwt": { + "version": "0.0.42", + "resolved": "https://registry.npmjs.org/@types/express-jwt/-/express-jwt-0.0.42.tgz", + "integrity": "sha512-WszgUddvM1t5dPpJ3LhWNH8kfNN8GPIBrAGxgIYXVCEGx6Bx4A036aAuf/r5WH9DIEdlmp7gHOYvSM6U87B0ag==", + "requires": { + "@types/express": "*", + "@types/express-unless": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz", + "integrity": "sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA==", + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/express-unless": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.1.tgz", + "integrity": "sha512-5fuvg7C69lemNgl0+v+CUxDYWVPSfXHhJPst4yTLcqi4zKJpORCxnDrnnilk3k0DTq/WrAUdvXFs01+vUqUZHw==", + "requires": { + "@types/express": "*" + } + }, "@types/firebase-token-generator": { "version": "2.0.28", - "resolved": "http://registry.npmjs.org/@types/firebase-token-generator/-/firebase-token-generator-2.0.28.tgz", + "resolved": "https://registry.npmjs.org/@types/firebase-token-generator/-/firebase-token-generator-2.0.28.tgz", "integrity": "sha1-Z1VIHZMk4mt6XItFXWgUg3aCw5Y=", "dev": true }, @@ -942,6 +1002,11 @@ "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==", "optional": true }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -974,6 +1039,16 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.26.tgz", "integrity": "sha512-myMwkO2Cr82kirHY8uknNRHEVtn0wV3DTQfkrjx17jmkstDRZ24gNUdl8AHXVyVclTYI/bNjgTPTAWvWLqXqkw==" }, + "@types/qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==" + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" + }, "@types/request": { "version": "2.48.5", "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz", @@ -996,6 +1071,15 @@ "@types/request": "*" } }, + "@types/serve-static": { + "version": "1.13.9", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz", + "integrity": "sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==", + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, "@types/sinon": { "version": "9.0.4", "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.4.tgz", @@ -1646,7 +1730,7 @@ }, "binaryextensions": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/binaryextensions/-/binaryextensions-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-1.0.1.tgz", "integrity": "sha1-HmN0iLNbWL2l9HdL+WpSEqjJB1U=", "dev": true }, @@ -3349,7 +3433,7 @@ }, "firebase-token-generator": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/firebase-token-generator/-/firebase-token-generator-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/firebase-token-generator/-/firebase-token-generator-2.0.0.tgz", "integrity": "sha1-l2fXWewTq9yZuhFf1eqZ2Lk9EgY=", "dev": true }, @@ -3770,7 +3854,7 @@ }, "globby": { "version": "5.0.0", - "resolved": "http://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", "dev": true, "requires": { @@ -5023,7 +5107,7 @@ }, "istextorbinary": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/istextorbinary/-/istextorbinary-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-1.0.2.tgz", "integrity": "sha1-rOGTVNGpoBc+/rEITOD4ewrX3s8=", "dev": true, "requires": { @@ -5037,6 +5121,14 @@ "integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=", "dev": true }, + "jose": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.4.tgz", + "integrity": "sha512-EArN9f6aq1LT/fIGGsfghOnNXn4noD+3dG5lL/ljY3LcRjw1u9w+4ahu/4ahsN6N0kRLyyW6zqdoYk7LNx3+YQ==", + "requires": { + "@panva/asn1.js": "^1.0.0" + } + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5225,6 +5317,18 @@ "safe-buffer": "^5.0.1" } }, + "jwks-rsa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-2.0.2.tgz", + "integrity": "sha512-oRnlZvmP21LxqEgEFiPycLn3jyw/QuynyaERe7GMxR4TlTg7BRGBgEyEN+rRN4xGHMekXur1RY/MSt8UJBiSgA==", + "requires": { + "@types/express-jwt": "0.0.42", + "debug": "^4.1.0", + "jose": "^2.0.2", + "limiter": "^1.1.5", + "lru-memoizer": "^2.1.2" + } + }, "jws": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", @@ -5304,6 +5408,11 @@ "resolve": "^1.1.7" } }, + "limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + }, "load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", @@ -5401,6 +5510,11 @@ "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", "optional": true }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, "lodash.escape": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", @@ -5598,6 +5712,31 @@ "yallist": "^3.0.2" } }, + "lru-memoizer": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.1.4.tgz", + "integrity": "sha512-IXAq50s4qwrOBrXJklY+KhgZF+5y98PDaNo0gi/v2KQBFLyWr+JyFvijZXkGKjQj/h9c0OwoE+JZbwUXce76hQ==", + "requires": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "~4.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", + "integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=", + "requires": { + "pseudomap": "^1.0.1", + "yallist": "^2.0.0" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + } + } + }, "lunr": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", @@ -7140,7 +7279,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, @@ -7302,7 +7441,7 @@ }, "pretty-hrtime": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", "dev": true }, @@ -7361,8 +7500,7 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, "psl": { "version": "1.8.0", @@ -7846,7 +7984,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -8670,7 +8808,7 @@ }, "textextensions": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/textextensions/-/textextensions-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-1.0.2.tgz", "integrity": "sha1-ZUhjk+4fK7A5pgy7oFsLaL2VAdI=", "dev": true }, @@ -9142,9 +9280,9 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "uuid": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz", - "integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "optional": true }, "v8-compile-cache": { diff --git a/package.json b/package.json index 1d5132c8c1..d384d64579 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@types/node": "^10.10.0", "dicer": "^0.3.0", "jsonwebtoken": "^8.5.1", + "jwks-rsa": "^2.0.2", "node-forge": "^0.10.0" }, "optionalDependencies": { diff --git a/src/app-check/app-check-api-client-internal.ts b/src/app-check/app-check-api-client-internal.ts new file mode 100644 index 0000000000..8d25e23cf7 --- /dev/null +++ b/src/app-check/app-check-api-client-internal.ts @@ -0,0 +1,228 @@ +/*! + * @license + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { appCheck } from './index'; +import { + HttpRequestConfig, HttpClient, HttpError, AuthorizedHttpClient, HttpResponse +} from '../utils/api-request'; +import { FirebaseApp } from '../firebase-app'; +import { PrefixedFirebaseError } from '../utils/error'; + +import * as utils from '../utils/index'; +import * as validator from '../utils/validator'; + +import AppCheckToken = appCheck.AppCheckToken; + +// App Check backend constants +const FIREBASE_APP_CHECK_V1_API_URL_FORMAT = 'https://firebaseappcheck.googleapis.com/v1beta/projects/{projectId}/apps/{appId}:exchangeCustomToken'; + +const FIREBASE_APP_CHECK_CONFIG_HEADERS = { + 'X-Firebase-Client': `fire-admin-node/${utils.getSdkVersion()}` +}; + +/** + * Class that facilitates sending requests to the Firebase App Check backend API. + * + * @internal + */ +export class AppCheckApiClient { + private readonly httpClient: HttpClient; + private projectId?: string; + + constructor(private readonly app: FirebaseApp) { + if (!validator.isNonNullObject(app) || !('options' in app)) { + throw new FirebaseAppCheckError( + 'invalid-argument', + 'First argument passed to admin.appCheck() must be a valid Firebase app instance.'); + } + this.httpClient = new AuthorizedHttpClient(app); + } + + /** + * Exchange a signed custom token to App Check token + * + * @param customToken The custom token to be exchanged. + * @param appId The mobile App ID. + * @return A promise that fulfills with a `AppCheckToken`. + */ + public exchangeToken(customToken: string, appId: string): Promise { + if (!validator.isNonEmptyString(appId)) { + throw new FirebaseAppCheckError( + 'invalid-argument', + '`appId` must be a non-empty string.'); + } + if (!validator.isNonEmptyString(customToken)) { + throw new FirebaseAppCheckError( + 'invalid-argument', + '`customToken` must be a non-empty string.'); + } + return this.getUrl(appId) + .then((url) => { + const request: HttpRequestConfig = { + method: 'POST', + url, + headers: FIREBASE_APP_CHECK_CONFIG_HEADERS, + data: { customToken } + }; + return this.httpClient.send(request); + }) + .then((resp) => { + return this.toAppCheckToken(resp); + }) + .catch((err) => { + throw this.toFirebaseError(err); + }); + } + + private getUrl(appId: string): Promise { + return this.getProjectId() + .then((projectId) => { + const urlParams = { + projectId, + appId, + }; + const baseUrl = utils.formatString(FIREBASE_APP_CHECK_V1_API_URL_FORMAT, urlParams); + return utils.formatString(baseUrl); + }); + } + + private getProjectId(): Promise { + if (this.projectId) { + return Promise.resolve(this.projectId); + } + return utils.findProjectId(this.app) + .then((projectId) => { + if (!validator.isNonEmptyString(projectId)) { + throw new FirebaseAppCheckError( + 'unknown-error', + 'Failed to determine project ID. Initialize the ' + + 'SDK with service account credentials or set project ID as an app option. ' + + 'Alternatively, set the GOOGLE_CLOUD_PROJECT environment variable.'); + } + this.projectId = projectId; + return projectId; + }); + } + + private toFirebaseError(err: HttpError): PrefixedFirebaseError { + if (err instanceof PrefixedFirebaseError) { + return err; + } + + const response = err.response; + if (!response.isJson()) { + return new FirebaseAppCheckError( + 'unknown-error', + `Unexpected response with status: ${response.status} and body: ${response.text}`); + } + + const error: Error = (response.data as ErrorResponse).error || {}; + let code: AppCheckErrorCode = 'unknown-error'; + if (error.status && error.status in APP_CHECK_ERROR_CODE_MAPPING) { + code = APP_CHECK_ERROR_CODE_MAPPING[error.status]; + } + const message = error.message || `Unknown server error: ${response.text}`; + return new FirebaseAppCheckError(code, message); + } + + /** + * Creates an AppCheckToken from the API response. + * + * @param resp API response object. + * @return An AppCheckToken instance. + */ + private toAppCheckToken(resp: HttpResponse): AppCheckToken { + const token = resp.data.attestationToken; + // `ttl` is a string with the suffix "s" preceded by the number of seconds, + // with nanoseconds expressed as fractional seconds. + const ttlMillis = this.stringToMilliseconds(resp.data.ttl); + return { + token, + ttlMillis + } + } + + /** + * Converts a duration string with the suffix `s` to milliseconds. + * + * @param duration The duration as a string with the suffix "s" preceded by the + * number of seconds, with fractional seconds. For example, 3 seconds with 0 nanoseconds + * is expressed as "3s", while 3 seconds and 1 nanosecond is expressed as "3.000000001s", + * and 3 seconds and 1 microsecond is expressed as "3.000001s". + * + * @return The duration in milliseconds. + */ + private stringToMilliseconds(duration: string): number { + if (!validator.isNonEmptyString(duration) || !duration.endsWith('s')) { + throw new FirebaseAppCheckError( + 'invalid-argument', '`ttl` must be a valid duration string with the suffix `s`.'); + } + const seconds = duration.slice(0, -1); + return Math.floor(Number(seconds) * 1000); + } +} + +interface ErrorResponse { + error?: Error; +} + +interface Error { + code?: number; + message?: string; + status?: string; +} + +export const APP_CHECK_ERROR_CODE_MAPPING: { [key: string]: AppCheckErrorCode } = { + ABORTED: 'aborted', + INVALID_ARGUMENT: 'invalid-argument', + INVALID_CREDENTIAL: 'invalid-credential', + INTERNAL: 'internal-error', + PERMISSION_DENIED: 'permission-denied', + UNAUTHENTICATED: 'unauthenticated', + NOT_FOUND: 'not-found', + UNKNOWN: 'unknown-error', +}; + +export type AppCheckErrorCode = + 'aborted' + | 'invalid-argument' + | 'invalid-credential' + | 'internal-error' + | 'permission-denied' + | 'unauthenticated' + | 'not-found' + | 'app-check-token-expired' + | 'unknown-error'; + +/** + * Firebase App Check error code structure. This extends PrefixedFirebaseError. + * + * @param {AppCheckErrorCode} code The error code. + * @param {string} message The error message. + * @constructor + */ +export class FirebaseAppCheckError extends PrefixedFirebaseError { + constructor(code: AppCheckErrorCode, message: string) { + super('app-check', code, message); + + /* tslint:disable:max-line-length */ + // Set the prototype explicitly. See the following link for more details: + // https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + /* tslint:enable:max-line-length */ + (this as any).__proto__ = FirebaseAppCheckError.prototype; + } +} diff --git a/src/app-check/app-check.ts b/src/app-check/app-check.ts new file mode 100644 index 0000000000..42d8391043 --- /dev/null +++ b/src/app-check/app-check.ts @@ -0,0 +1,86 @@ +/*! + * @license + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { FirebaseApp } from '../firebase-app'; +import { appCheck } from './index'; +import { AppCheckApiClient } from './app-check-api-client-internal'; +import { + appCheckErrorFromCryptoSignerError, AppCheckTokenGenerator +} from './token-generator'; +import { AppCheckTokenVerifier } from './token-verifier'; +import { cryptoSignerFromApp } from '../utils/crypto-signer'; + +import AppCheckInterface = appCheck.AppCheck; +import AppCheckToken = appCheck.AppCheckToken; +import VerifyAppCheckTokenResponse = appCheck.VerifyAppCheckTokenResponse; + +/** + * AppCheck service bound to the provided app. + */ +export class AppCheck implements AppCheckInterface { + + private readonly client: AppCheckApiClient; + private readonly tokenGenerator: AppCheckTokenGenerator; + private readonly appCheckTokenVerifier: AppCheckTokenVerifier; + + /** + * @param app The app for this AppCheck service. + * @constructor + */ + constructor(readonly app: FirebaseApp) { + this.client = new AppCheckApiClient(app); + try { + this.tokenGenerator = new AppCheckTokenGenerator(cryptoSignerFromApp(app)); + } catch (err) { + throw appCheckErrorFromCryptoSignerError(err); + } + this.appCheckTokenVerifier = new AppCheckTokenVerifier(app); + } + + /** + * Creates a new {@link appCheck.AppCheckToken `AppCheckToken`} that can be sent + * back to a client. + * + * @param appId The app ID to use as the JWT app_id. + * + * @return A promise that fulfills with a `AppCheckToken`. + */ + public createToken(appId: string): Promise { + return this.tokenGenerator.createCustomToken(appId) + .then((customToken) => { + return this.client.exchangeToken(customToken, appId); + }); + } + + /** + * Veifies an App Check token. + * + * @param appCheckToken The App Check token to verify. + * + * @return A promise that fulfills with a `VerifyAppCheckTokenResponse` on successful + * verification. + */ + public verifyToken(appCheckToken: string): Promise { + return this.appCheckTokenVerifier.verifyToken(appCheckToken) + .then((decodedToken) => { + return { + appId: decodedToken.app_id, + token: decodedToken, + }; + }); + } +} diff --git a/src/app-check/index.ts b/src/app-check/index.ts new file mode 100644 index 0000000000..6552d9208d --- /dev/null +++ b/src/app-check/index.ts @@ -0,0 +1,164 @@ +/*! + * @license + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { app } from '../firebase-namespace-api'; + +/** + * Gets the {@link appCheck.AppCheck `AppCheck`} service for the + * default app or a given app. + * + * You can call `admin.appCheck()` with no arguments to access the default + * app's {@link appCheck.AppCheck `AppCheck`} service or as + * `admin.appCheck(app)` to access the + * {@link appCheck.AppCheck `AppCheck`} service associated with a + * specific app. + * + * @example + * ```javascript + * // Get the `AppCheck` service for the default app + * var defaultAppCheck = admin.appCheck(); + * ``` + * + * @example + * ```javascript + * // Get the `AppCheck` service for a given app + * var otherAppCheck = admin.appCheck(otherApp); + * ``` + * + * @param app Optional app for which to return the `AppCheck` service. + * If not provided, the default `AppCheck` service is returned. + * + * @return The default `AppCheck` service if no + * app is provided, or the `AppCheck` service associated with the provided + * app. + */ +export declare function appCheck(app?: app.App): appCheck.AppCheck; + +/* eslint-disable @typescript-eslint/no-namespace */ +export namespace appCheck { + /** + * The Firebase `AppCheck` service interface. + */ + export interface AppCheck { + app: app.App; + + /** + * Creates a new {@link appCheck.AppCheckToken `AppCheckToken`} that can be sent + * back to a client. + * + * @param appId The App ID of the Firebase App the token belongs to. + * + * @return A promise that fulfills with a `AppCheckToken`. + */ + createToken(appId: string): Promise; + + /** + * Verifies a Firebase App Check token (JWT). If the token is valid, the promise is + * fulfilled with the token's decoded claims; otherwise, the promise is + * rejected. + * + * @param appCheckToken The App Check token to verify. + * + * @return A promise fulfilled with the + * token's decoded claims if the App Check token is valid; otherwise, a rejected + * promise. + */ + verifyToken(appCheckToken: string): Promise; + } + + /** + * Interface representing an App Check token. + */ + export interface AppCheckToken { + /** + * The Firebase App Check token. + */ + token: string; + + /** + * The time-to-live duration of the token in milliseconds. + */ + ttlMillis: number; + } + + /** + * Interface representing a decoded Firebase App Check token, returned from the + * {@link appCheck.AppCheck.verifyToken `verifyToken()`} method. + */ + export interface DecodedAppCheckToken { + /** + * The issuer identifier for the issuer of the response. + * + * This value is a URL with the format + * `https://firebaseappcheck.googleapis.com/`, where `` is the + * same project number specified in the [`aud`](#aud) property. + */ + iss: string; + + /** + * The Firebase App ID corresponding to the app the token belonged to. + * + * As a convenience, this value is copied over to the [`app_id`](#app_id) property. + */ + sub: string; + + /** + * The audience for which this token is intended. + * + * This value is a JSON array of two strings, the first is the project number of your + * Firebase project, and the second is the project ID of the same project. + */ + aud: string[]; + + /** + * The App Check token's expiration time, in seconds since the Unix epoch. That is, the + * time at which this App Check token expires and should no longer be considered valid. + */ + exp: number; + + /** + * The App Check token's issued-at time, in seconds since the Unix epoch. That is, the + * time at which this App Check token was issued and should start to be considered + * valid. + */ + iat: number; + + /** + * The App ID corresponding to the App the App Check token belonged to. + * + * This value is not actually one of the JWT token claims. It is added as a + * convenience, and is set as the value of the [`sub`](#sub) property. + */ + app_id: string; + [key: string]: any; + } + + /** + * Interface representing a verified App Check token response. + */ + export interface VerifyAppCheckTokenResponse { + /** + * The App ID corresponding to the App the App Check token belonged to. + */ + appId: string; + + /** + * The decoded Firebase App Check token. + */ + token: appCheck.DecodedAppCheckToken; + } +} diff --git a/src/app-check/token-generator.ts b/src/app-check/token-generator.ts new file mode 100644 index 0000000000..1b557438bb --- /dev/null +++ b/src/app-check/token-generator.ts @@ -0,0 +1,145 @@ +/*! + * @license + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as validator from '../utils/validator'; +import { toWebSafeBase64 } from '../utils'; + +import { CryptoSigner, CryptoSignerError, CryptoSignerErrorCode } from '../utils/crypto-signer'; +import { + FirebaseAppCheckError, + AppCheckErrorCode, + APP_CHECK_ERROR_CODE_MAPPING, +} from './app-check-api-client-internal'; +import { HttpError } from '../utils/api-request'; + +const ONE_HOUR_IN_SECONDS = 60 * 60; + +// Audience to use for Firebase App Check Custom tokens +const FIREBASE_APP_CHECK_AUDIENCE = 'https://firebaseappcheck.googleapis.com/google.firebase.appcheck.v1beta.TokenExchangeService'; + +/** + * Class for generating Firebase App Check tokens. + * + * @internal + */ +export class AppCheckTokenGenerator { + + private readonly signer: CryptoSigner; + + /** + * The AppCheckTokenGenerator class constructor. + * + * @param signer The CryptoSigner instance for this token generator. + * @constructor + */ + constructor(signer: CryptoSigner) { + if (!validator.isNonNullObject(signer)) { + throw new FirebaseAppCheckError( + 'invalid-argument', + 'INTERNAL ASSERT: Must provide a CryptoSigner to use AppCheckTokenGenerator.'); + } + this.signer = signer; + } + + /** + * Creates a new custom token that can be exchanged to an App Check token. + * + * @param appId The Application ID to use for the generated token. + * + * @return A Promise fulfilled with a custom token signed with a service account key + * that can be exchanged to an App Check token. + */ + public createCustomToken(appId: string): Promise { + if (!validator.isNonEmptyString(appId)) { + throw new FirebaseAppCheckError( + 'invalid-argument', + '`appId` must be a non-empty string.'); + } + return this.signer.getAccountId().then((account) => { + const header = { + alg: this.signer.algorithm, + typ: 'JWT', + }; + const iat = Math.floor(Date.now() / 1000); + const body = { + iss: account, + sub: account, + // eslint-disable-next-line @typescript-eslint/camelcase + app_id: appId, + aud: FIREBASE_APP_CHECK_AUDIENCE, + exp: iat + ONE_HOUR_IN_SECONDS, + iat, + }; + const token = `${this.encodeSegment(header)}.${this.encodeSegment(body)}`; + return this.signer.sign(Buffer.from(token)) + .then((signature) => { + return `${token}.${this.encodeSegment(signature)}`; + }); + }).catch((err) => { + throw appCheckErrorFromCryptoSignerError(err); + }); + } + + private encodeSegment(segment: object | Buffer): string { + const buffer: Buffer = (segment instanceof Buffer) ? segment : Buffer.from(JSON.stringify(segment)); + return toWebSafeBase64(buffer).replace(/=+$/, ''); + } +} + +/** + * Creates a new FirebaseAppCheckError by extracting the error code, message and other relevant + * details from a CryptoSignerError. + * + * @param err The Error to convert into a FirebaseAppCheckError error + * @return A Firebase App Check error that can be returned to the user. + */ +export function appCheckErrorFromCryptoSignerError(err: Error): Error { + if (!(err instanceof CryptoSignerError)) { + return err; + } + if (err.code === CryptoSignerErrorCode.SERVER_ERROR && validator.isNonNullObject(err.cause)) { + const httpError = err.cause as HttpError + const errorResponse = httpError.response.data; + if (errorResponse?.error) { + const status = errorResponse.error.status; + const description = errorResponse.error.message || JSON.stringify(httpError.response); + + let code: AppCheckErrorCode = 'unknown-error'; + if (status && status in APP_CHECK_ERROR_CODE_MAPPING) { + code = APP_CHECK_ERROR_CODE_MAPPING[status]; + } + return new FirebaseAppCheckError(code, + `Error returned from server while siging a custom token: ${description}` + ); + } + return new FirebaseAppCheckError('internal-error', + 'Error returned from server: ' + JSON.stringify(errorResponse) + '.' + ); + } + return new FirebaseAppCheckError(mapToAppCheckErrorCode(err.code), err.message); +} + +function mapToAppCheckErrorCode(code: string): AppCheckErrorCode { + switch (code) { + case CryptoSignerErrorCode.INVALID_CREDENTIAL: + return 'invalid-credential'; + case CryptoSignerErrorCode.INVALID_ARGUMENT: + return 'invalid-argument'; + default: + return 'internal-error'; + } +} diff --git a/src/app-check/token-verifier.ts b/src/app-check/token-verifier.ts new file mode 100644 index 0000000000..318a1fd10b --- /dev/null +++ b/src/app-check/token-verifier.ts @@ -0,0 +1,165 @@ +/*! + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { appCheck } from '.'; +import * as validator from '../utils/validator'; +import * as util from '../utils/index'; +import { FirebaseAppCheckError } from './app-check-api-client-internal'; +import { FirebaseApp } from '../firebase-app'; +import { + ALGORITHM_RS256, DecodedToken, decodeJwt, JwtError, + JwtErrorCode, PublicKeySignatureVerifier, SignatureVerifier +} from '../utils/jwt'; + +import DecodedAppCheckToken = appCheck.DecodedAppCheckToken; + +const APP_CHECK_ISSUER = 'https://firebaseappcheck.googleapis.com/'; +const JWKS_URL = 'https://firebaseappcheck.googleapis.com/v1beta/jwks'; + +/** + * Class for verifying Firebase App Check tokens. + * + * @internal + */ +export class AppCheckTokenVerifier { + private readonly signatureVerifier: SignatureVerifier; + + constructor(private readonly app: FirebaseApp) { + this.signatureVerifier = PublicKeySignatureVerifier.withJwksUrl(JWKS_URL); + } + + /** + * Verifies the format and signature of a Firebase App Check token. + * + * @param token The Firebase Auth JWT token to verify. + * @return A promise fulfilled with the decoded claims of the Firebase App Check token. + */ + public verifyToken(token: string): Promise { + if (!validator.isString(token)) { + throw new FirebaseAppCheckError( + 'invalid-argument', + 'App check token must be a non-null string.', + ); + } + + return this.ensureProjectId() + .then((projectId) => { + return this.decodeAndVerify(token, projectId); + }) + .then((decoded) => { + const decodedAppCheckToken = decoded.payload as DecodedAppCheckToken; + // eslint-disable-next-line @typescript-eslint/camelcase + decodedAppCheckToken.app_id = decodedAppCheckToken.sub; + return decodedAppCheckToken; + }); + } + + private ensureProjectId(): Promise { + return util.findProjectId(this.app) + .then((projectId) => { + if (!validator.isNonEmptyString(projectId)) { + throw new FirebaseAppCheckError( + 'invalid-credential', + 'Must initialize app with a cert credential or set your Firebase project ID as the ' + + 'GOOGLE_CLOUD_PROJECT environment variable to verify an App Check token.' + ); + } + return projectId; + }) + } + + private decodeAndVerify(token: string, projectId: string): Promise { + return this.safeDecode(token) + .then((decodedToken) => { + this.verifyContent(decodedToken, projectId); + return this.verifySignature(token) + .then(() => decodedToken); + }); + } + + private safeDecode(jwtToken: string): Promise { + return decodeJwt(jwtToken) + .catch(() => { + const errorMessage = 'Decoding App Check token failed. Make sure you passed ' + + 'the entire string JWT which represents the Firebase App Check token.'; + throw new FirebaseAppCheckError('invalid-argument', errorMessage); + }); + } + + /** + * Verifies the content of a Firebase App Check JWT. + * + * @param fullDecodedToken The decoded JWT. + * @param projectId The Firebase Project Id. + */ + private verifyContent(fullDecodedToken: DecodedToken, projectId: string | null): void { + const header = fullDecodedToken.header; + const payload = fullDecodedToken.payload; + + const projectIdMatchMessage = ' Make sure the App Check token comes from the same ' + + 'Firebase project as the service account used to authenticate this SDK.'; + const scopedProjectId = `projects/${projectId}`; + + let errorMessage: string | undefined; + if (header.alg !== ALGORITHM_RS256) { + errorMessage = 'The provided App Check token has incorrect algorithm. Expected "' + + ALGORITHM_RS256 + '" but got ' + '"' + header.alg + '".'; + } else if (!validator.isNonEmptyArray(payload.aud) || !payload.aud.includes(scopedProjectId)) { + errorMessage = 'The provided App Check token has incorrect "aud" (audience) claim. Expected "' + + scopedProjectId + '" but got "' + payload.aud + '".' + projectIdMatchMessage; + } else if (typeof payload.iss !== 'string' || !payload.iss.startsWith(APP_CHECK_ISSUER)) { + errorMessage = 'The provided App Check token has incorrect "iss" (issuer) claim.'; + } else if (typeof payload.sub !== 'string') { + errorMessage = 'The provided App Check token has no "sub" (subject) claim.'; + } else if (payload.sub === '') { + errorMessage = 'The provided App Check token has an empty string "sub" (subject) claim.'; + } + if (errorMessage) { + throw new FirebaseAppCheckError('invalid-argument', errorMessage); + } + } + + private verifySignature(jwtToken: string): + Promise { + return this.signatureVerifier.verify(jwtToken) + .catch((error: JwtError) => { + throw this.mapJwtErrorToAppCheckError(error); + }); + } + + /** + * Maps JwtError to FirebaseAppCheckError + * + * @param error JwtError to be mapped. + * @returns FirebaseAppCheckError instance. + */ + private mapJwtErrorToAppCheckError(error: JwtError): FirebaseAppCheckError { + if (error.code === JwtErrorCode.TOKEN_EXPIRED) { + const errorMessage = 'The provided App Check token has expired. Get a fresh App Check token' + + ' from your client app and try again.' + return new FirebaseAppCheckError('app-check-token-expired', errorMessage); + } else if (error.code === JwtErrorCode.INVALID_SIGNATURE) { + const errorMessage = 'The provided App Check token has invalid signature.'; + return new FirebaseAppCheckError('invalid-argument', errorMessage); + } else if (error.code === JwtErrorCode.NO_MATCHING_KID) { + const errorMessage = 'The provided App Check token has "kid" claim which does not ' + + 'correspond to a known public key. Most likely the provided App Check token ' + + 'is expired, so get a fresh token from your client app and try again.'; + return new FirebaseAppCheckError('invalid-argument', errorMessage); + } + return new FirebaseAppCheckError('invalid-argument', error.message); + } +} diff --git a/src/auth/auth.ts b/src/auth/auth.ts index aa5d7b11ef..48f46c345a 100644 --- a/src/auth/auth.ts +++ b/src/auth/auth.ts @@ -21,7 +21,7 @@ import { isUidIdentifier, isEmailIdentifier, isPhoneIdentifier, isProviderIdentifier, } from './identifier'; import { FirebaseApp } from '../firebase-app'; -import { FirebaseTokenGenerator, EmulatedSigner, cryptoSignerFromApp } from './token-generator'; +import { FirebaseTokenGenerator, EmulatedSigner, handleCryptoSignerError } from './token-generator'; import { AbstractAuthRequestHandler, AuthRequestHandler, TenantAwareAuthRequestHandler, useEmulator, } from './auth-api-request'; @@ -36,6 +36,7 @@ import { SAMLConfig, OIDCConfig, OIDCConfigServerResponse, SAMLConfigServerResponse, } from './auth-config'; import { TenantManager } from './tenant-manager'; +import { cryptoSignerFromApp } from '../utils/crypto-signer'; import UserIdentifier = auth.UserIdentifier; import CreateRequest = auth.CreateRequest; @@ -82,8 +83,7 @@ export class BaseAuth implements BaseAuthI if (tokenGenerator) { this.tokenGenerator = tokenGenerator; } else { - const cryptoSigner = useEmulator() ? new EmulatedSigner() : cryptoSignerFromApp(app); - this.tokenGenerator = new FirebaseTokenGenerator(cryptoSigner); + this.tokenGenerator = createFirebaseTokenGenerator(app); } this.sessionCookieVerifier = createSessionCookieVerifier(app); @@ -772,9 +772,8 @@ export class TenantAwareAuth * @constructor */ constructor(app: FirebaseApp, tenantId: string) { - const cryptoSigner = useEmulator() ? new EmulatedSigner() : cryptoSignerFromApp(app); - const tokenGenerator = new FirebaseTokenGenerator(cryptoSigner, tenantId); - super(app, new TenantAwareAuthRequestHandler(app, tenantId), tokenGenerator); + super(app, new TenantAwareAuthRequestHandler(app, tenantId), + createFirebaseTokenGenerator(app, tenantId)); utils.addReadonlyGetter(this, 'tenantId', tenantId); } @@ -887,3 +886,13 @@ export class Auth extends BaseAuth implements AuthInterface return this.tenantManager_; } } + +function createFirebaseTokenGenerator(app: FirebaseApp, + tenantId?: string): FirebaseTokenGenerator { + try { + const signer = useEmulator() ? new EmulatedSigner() : cryptoSignerFromApp(app); + return new FirebaseTokenGenerator(signer, tenantId); + } catch (err) { + throw handleCryptoSignerError(err); + } +} diff --git a/src/auth/token-generator.ts b/src/auth/token-generator.ts index a8a76c7b28..6c464ec5f2 100644 --- a/src/auth/token-generator.ts +++ b/src/auth/token-generator.ts @@ -15,17 +15,16 @@ * limitations under the License. */ -import { FirebaseApp } from '../firebase-app'; -import { ServiceAccountCredential } from '../credential/credential-internal'; -import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error'; -import { AuthorizedHttpClient, HttpError, HttpRequestConfig, HttpClient } from '../utils/api-request'; +import { + AuthClientErrorCode, ErrorInfo, FirebaseAuthError +} from '../utils/error'; +import { CryptoSigner, CryptoSignerError, CryptoSignerErrorCode } from '../utils/crypto-signer'; import * as validator from '../utils/validator'; import { toWebSafeBase64 } from '../utils'; import { Algorithm } from 'jsonwebtoken'; +import { HttpError } from '../utils/api-request'; - -const ALGORITHM_RS256: Algorithm = 'RS256' as const; const ALGORITHM_NONE: Algorithm = 'none' as const; const ONE_HOUR_IN_SECONDS = 60 * 60; @@ -39,32 +38,6 @@ export const BLACKLISTED_CLAIMS = [ // Audience to use for Firebase Auth Custom tokens const FIREBASE_AUDIENCE = 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit'; -/** - * CryptoSigner interface represents an object that can be used to sign JWTs. - */ -export interface CryptoSigner { - - /** - * The name of the signing algorithm. - */ - readonly algorithm: Algorithm; - - /** - * Cryptographically signs a buffer of data. - * - * @param {Buffer} buffer The data to be signed. - * @return {Promise} A promise that resolves with the raw bytes of a signature. - */ - sign(buffer: Buffer): Promise; - - /** - * Returns the ID of the service account used to sign tokens. - * - * @return {Promise} A promise that resolves with a service account ID. - */ - getAccountId(): Promise; -} - /** * Represents the header of a JWT. */ @@ -87,148 +60,6 @@ interface JWTBody { tenant_id?: string; } -/** - * A CryptoSigner implementation that uses an explicitly specified service account private key to - * sign data. Performs all operations locally, and does not make any RPC calls. - */ -export class ServiceAccountSigner implements CryptoSigner { - - algorithm = ALGORITHM_RS256; - - /** - * Creates a new CryptoSigner instance from the given service account credential. - * - * @param {ServiceAccountCredential} credential A service account credential. - */ - constructor(private readonly credential: ServiceAccountCredential) { - if (!credential) { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CREDENTIAL, - 'INTERNAL ASSERT: Must provide a service account credential to initialize ServiceAccountSigner.', - ); - } - } - - /** - * @inheritDoc - */ - public sign(buffer: Buffer): Promise { - const crypto = require('crypto'); // eslint-disable-line @typescript-eslint/no-var-requires - const sign = crypto.createSign('RSA-SHA256'); - sign.update(buffer); - return Promise.resolve(sign.sign(this.credential.privateKey)); - } - - /** - * @inheritDoc - */ - public getAccountId(): Promise { - return Promise.resolve(this.credential.clientEmail); - } -} - -/** - * A CryptoSigner implementation that uses the remote IAM service to sign data. If initialized without - * a service account ID, attempts to discover a service account ID by consulting the local Metadata - * service. This will succeed in managed environments like Google Cloud Functions and App Engine. - * - * @see https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts/signBlob - * @see https://cloud.google.com/compute/docs/storing-retrieving-metadata - */ -export class IAMSigner implements CryptoSigner { - algorithm = ALGORITHM_RS256; - - private readonly httpClient: AuthorizedHttpClient; - private serviceAccountId?: string; - - constructor(httpClient: AuthorizedHttpClient, serviceAccountId?: string) { - if (!httpClient) { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, - 'INTERNAL ASSERT: Must provide a HTTP client to initialize IAMSigner.', - ); - } - if (typeof serviceAccountId !== 'undefined' && !validator.isNonEmptyString(serviceAccountId)) { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, - 'INTERNAL ASSERT: Service account ID must be undefined or a non-empty string.', - ); - } - this.httpClient = httpClient; - this.serviceAccountId = serviceAccountId; - } - - /** - * @inheritDoc - */ - public sign(buffer: Buffer): Promise { - return this.getAccountId().then((serviceAccount) => { - const request: HttpRequestConfig = { - method: 'POST', - url: `https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${serviceAccount}:signBlob`, - data: { payload: buffer.toString('base64') }, - }; - return this.httpClient.send(request); - }).then((response: any) => { - // Response from IAM is base64 encoded. Decode it into a buffer and return. - return Buffer.from(response.data.signedBlob, 'base64'); - }).catch((err) => { - if (err instanceof HttpError) { - const error = err.response.data; - if (validator.isNonNullObject(error) && error.error) { - const errorCode = error.error.status; - const description = 'Please refer to https://firebase.google.com/docs/auth/admin/create-custom-tokens ' + - 'for more details on how to use and troubleshoot this feature.'; - const errorMsg = `${error.error.message}; ${description}`; - - throw FirebaseAuthError.fromServerError(errorCode, errorMsg, error); - } - throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, - 'Error returned from server: ' + error + '. Additionally, an ' + - 'internal error occurred while attempting to extract the ' + - 'errorcode from the error.', - ); - } - throw err; - }); - } - - /** - * @inheritDoc - */ - public getAccountId(): Promise { - if (validator.isNonEmptyString(this.serviceAccountId)) { - return Promise.resolve(this.serviceAccountId); - } - const request: HttpRequestConfig = { - method: 'GET', - url: 'http://metadata/computeMetadata/v1/instance/service-accounts/default/email', - headers: { - 'Metadata-Flavor': 'Google', - }, - }; - const client = new HttpClient(); - return client.send(request).then((response) => { - if (!response.text) { - throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, - 'HTTP Response missing payload', - ); - } - this.serviceAccountId = response.text; - return response.text; - }).catch((err) => { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CREDENTIAL, - 'Failed to determine service account. Make sure to initialize ' + - 'the SDK with a service account credential. Alternatively specify a service ' + - `account with iam.serviceAccounts.signBlob permission. Original error: ${err}`, - ); - }); - } -} - /** * A CryptoSigner implementation that is used when communicating with the Auth emulator. * It produces unsigned tokens. @@ -253,22 +84,6 @@ export class EmulatedSigner implements CryptoSigner { } } -/** - * Create a new CryptoSigner instance for the given app. If the app has been initialized with a service - * account credential, creates a ServiceAccountSigner. Otherwise creates an IAMSigner. - * - * @param {FirebaseApp} app A FirebaseApp instance. - * @return {CryptoSigner} A CryptoSigner instance. - */ -export function cryptoSignerFromApp(app: FirebaseApp): CryptoSigner { - const credential = app.options.credential; - if (credential instanceof ServiceAccountCredential) { - return new ServiceAccountSigner(credential); - } - - return new IAMSigner(new AuthorizedHttpClient(app), app.options.serviceAccountId); -} - /** * Class for generating different types of Firebase Auth tokens (JWTs). */ @@ -361,6 +176,8 @@ export class FirebaseTokenGenerator { return Promise.all([token, signPromise]); }).then(([token, signature]) => { return `${token}.${this.encodeSegment(signature)}`; + }).catch((err) => { + throw handleCryptoSignerError(err); }); } @@ -383,3 +200,44 @@ export class FirebaseTokenGenerator { } } +/** + * Creates a new FirebaseAuthError by extracting the error code, message and other relevant + * details from a CryptoSignerError. + * + * @param {Error} err The Error to convert into a FirebaseAuthError error + * @return {FirebaseAuthError} A Firebase Auth error that can be returned to the user. + */ +export function handleCryptoSignerError(err: Error): Error { + if (!(err instanceof CryptoSignerError)) { + return err; + } + if (err.code === CryptoSignerErrorCode.SERVER_ERROR && validator.isNonNullObject(err.cause)) { + const httpError = err.cause; + const errorResponse = (httpError as HttpError).response.data; + if (validator.isNonNullObject(errorResponse) && errorResponse.error) { + const errorCode = errorResponse.error.status; + const description = 'Please refer to https://firebase.google.com/docs/auth/admin/create-custom-tokens ' + + 'for more details on how to use and troubleshoot this feature.'; + const errorMsg = `${errorResponse.error.message}; ${description}`; + + return FirebaseAuthError.fromServerError(errorCode, errorMsg, errorResponse); + } + return new FirebaseAuthError(AuthClientErrorCode.INTERNAL_ERROR, + 'Error returned from server: ' + errorResponse + '. Additionally, an ' + + 'internal error occurred while attempting to extract the ' + + 'errorcode from the error.' + ); + } + return new FirebaseAuthError(mapToAuthClientErrorCode(err.code), err.message); +} + +function mapToAuthClientErrorCode(code: string): ErrorInfo { + switch (code) { + case CryptoSignerErrorCode.INVALID_CREDENTIAL: + return AuthClientErrorCode.INVALID_CREDENTIAL; + case CryptoSignerErrorCode.INVALID_ARGUMENT: + return AuthClientErrorCode.INVALID_ARGUMENT; + default: + return AuthClientErrorCode.INTERNAL_ERROR; + } +} diff --git a/src/firebase-app.ts b/src/firebase-app.ts index c9e9588d59..84b30a52c5 100644 --- a/src/firebase-app.ts +++ b/src/firebase-app.ts @@ -35,6 +35,7 @@ import { InstanceId } from './instance-id/instance-id'; import { ProjectManagement } from './project-management/project-management'; import { SecurityRules } from './security-rules/security-rules'; import { RemoteConfig } from './remote-config/remote-config'; +import { AppCheck } from './app-check/app-check'; import Credential = credential.Credential; import Database = database.Database; @@ -318,6 +319,18 @@ export class FirebaseApp implements app.App { }); } + /** + * Returns the AppCheck service instance associated with this app. + * + * @return The AppCheck service instance of this app. + */ + public appCheck(): AppCheck { + return this.ensureService_('appCheck', () => { + const appCheckService: typeof AppCheck = require('./app-check/app-check').AppCheck; + return new appCheckService(this); + }); + } + /** * Returns the name of the FirebaseApp instance. * diff --git a/src/firebase-namespace-api.ts b/src/firebase-namespace-api.ts index 36df1b778d..6507fa3a88 100644 --- a/src/firebase-namespace-api.ts +++ b/src/firebase-namespace-api.ts @@ -15,6 +15,7 @@ */ import { Agent } from 'http'; +import { appCheck } from './app-check/index'; import { auth } from './auth/index'; import { credential } from './credential/index'; import { database } from './database/index'; @@ -222,6 +223,7 @@ export namespace app { */ options: AppOptions; + appCheck(): appCheck.AppCheck; auth(): auth.Auth; database(url?: string): database.Database; firestore(): firestore.Firestore; diff --git a/src/firebase-namespace.d.ts b/src/firebase-namespace.d.ts index 3de06b1bbb..ab013c3cac 100644 --- a/src/firebase-namespace.d.ts +++ b/src/firebase-namespace.d.ts @@ -16,6 +16,7 @@ export * from './credential/index'; export * from './firebase-namespace-api'; +export * from './app-check/index'; export * from './auth/index'; export * from './database/index'; export * from './firestore/index'; diff --git a/src/firebase-namespace.ts b/src/firebase-namespace.ts index 43b12a92c9..fa1a409b48 100644 --- a/src/firebase-namespace.ts +++ b/src/firebase-namespace.ts @@ -23,6 +23,7 @@ import { FirebaseApp } from './firebase-app'; import { cert, refreshToken, applicationDefault } from './credential/credential'; import { getApplicationDefault } from './credential/credential-internal'; +import { appCheck } from './app-check/index'; import { auth } from './auth/index'; import { database } from './database/index'; import { firestore } from './firestore/index'; @@ -38,6 +39,7 @@ import * as validator from './utils/validator'; import { getSdkVersion } from './utils/index'; import App = app.App; +import AppCheck = appCheck.AppCheck; import Auth = auth.Auth; import Database = database.Database; import Firestore = firestore.Firestore; @@ -357,6 +359,18 @@ export class FirebaseNamespace { return Object.assign(fn, { RemoteConfig: remoteConfig }); } + /** + * Gets the `AppCheck` service namespace. The returned namespace can be used to get the + * `AppCheck` service for the default app or an explicitly specified app. + */ + get appCheck(): FirebaseServiceNamespace { + const fn: FirebaseServiceNamespace = (app?: App) => { + return this.ensureApp(app).appCheck(); + }; + const appCheck = require('./app-check/app-check').AppCheck; + return Object.assign(fn, { AppCheck: appCheck }); + } + // TODO: Change the return types to app.App in the following methods. /** diff --git a/src/utils/crypto-signer.ts b/src/utils/crypto-signer.ts new file mode 100644 index 0000000000..e8bb8b79ae --- /dev/null +++ b/src/utils/crypto-signer.ts @@ -0,0 +1,250 @@ +/*! + * @license + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { FirebaseApp } from '../firebase-app'; +import { ServiceAccountCredential } from '../credential/credential-internal'; +import { AuthorizedHttpClient, HttpRequestConfig, HttpClient, HttpError } from './api-request'; + +import { Algorithm } from 'jsonwebtoken'; +import { ErrorInfo } from '../utils/error'; +import * as validator from '../utils/validator'; + +const ALGORITHM_RS256: Algorithm = 'RS256' as const; + +/** + * CryptoSigner interface represents an object that can be used to sign JWTs. + */ +export interface CryptoSigner { + + /** + * The name of the signing algorithm. + */ + readonly algorithm: Algorithm; + + /** + * Cryptographically signs a buffer of data. + * + * @param {Buffer} buffer The data to be signed. + * @return {Promise} A promise that resolves with the raw bytes of a signature. + */ + sign(buffer: Buffer): Promise; + + /** + * Returns the ID of the service account used to sign tokens. + * + * @return {Promise} A promise that resolves with a service account ID. + */ + getAccountId(): Promise; +} + +/** + * A CryptoSigner implementation that uses an explicitly specified service account private key to + * sign data. Performs all operations locally, and does not make any RPC calls. + */ +export class ServiceAccountSigner implements CryptoSigner { + + algorithm = ALGORITHM_RS256; + + /** + * Creates a new CryptoSigner instance from the given service account credential. + * + * @param {ServiceAccountCredential} credential A service account credential. + */ + constructor(private readonly credential: ServiceAccountCredential) { + if (!credential) { + throw new CryptoSignerError({ + code: CryptoSignerErrorCode.INVALID_CREDENTIAL, + message: 'INTERNAL ASSERT: Must provide a service account credential to initialize ServiceAccountSigner.', + }); + } + } + + /** + * @inheritDoc + */ + public sign(buffer: Buffer): Promise { + const crypto = require('crypto'); // eslint-disable-line @typescript-eslint/no-var-requires + const sign = crypto.createSign('RSA-SHA256'); + sign.update(buffer); + return Promise.resolve(sign.sign(this.credential.privateKey)); + } + + /** + * @inheritDoc + */ + public getAccountId(): Promise { + return Promise.resolve(this.credential.clientEmail); + } +} + +/** + * A CryptoSigner implementation that uses the remote IAM service to sign data. If initialized without + * a service account ID, attempts to discover a service account ID by consulting the local Metadata + * service. This will succeed in managed environments like Google Cloud Functions and App Engine. + * + * @see https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts/signBlob + * @see https://cloud.google.com/compute/docs/storing-retrieving-metadata + */ +export class IAMSigner implements CryptoSigner { + algorithm = ALGORITHM_RS256; + + private readonly httpClient: AuthorizedHttpClient; + private serviceAccountId?: string; + + constructor(httpClient: AuthorizedHttpClient, serviceAccountId?: string) { + if (!httpClient) { + throw new CryptoSignerError({ + code: CryptoSignerErrorCode.INVALID_ARGUMENT, + message: 'INTERNAL ASSERT: Must provide a HTTP client to initialize IAMSigner.', + }); + } + if (typeof serviceAccountId !== 'undefined' && !validator.isNonEmptyString(serviceAccountId)) { + throw new CryptoSignerError({ + code: CryptoSignerErrorCode.INVALID_ARGUMENT, + message: 'INTERNAL ASSERT: Service account ID must be undefined or a non-empty string.', + }); + } + this.httpClient = httpClient; + this.serviceAccountId = serviceAccountId; + } + + /** + * @inheritDoc + */ + public sign(buffer: Buffer): Promise { + return this.getAccountId().then((serviceAccount) => { + const request: HttpRequestConfig = { + method: 'POST', + url: `https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${serviceAccount}:signBlob`, + data: { payload: buffer.toString('base64') }, + }; + return this.httpClient.send(request); + }).then((response: any) => { + // Response from IAM is base64 encoded. Decode it into a buffer and return. + return Buffer.from(response.data.signedBlob, 'base64'); + }).catch((err) => { + if (err instanceof HttpError) { + throw new CryptoSignerError({ + code: CryptoSignerErrorCode.SERVER_ERROR, + message: err.message, + cause: err + }); + } + throw err + }); + } + + /** + * @inheritDoc + */ + public getAccountId(): Promise { + if (validator.isNonEmptyString(this.serviceAccountId)) { + return Promise.resolve(this.serviceAccountId); + } + const request: HttpRequestConfig = { + method: 'GET', + url: 'http://metadata/computeMetadata/v1/instance/service-accounts/default/email', + headers: { + 'Metadata-Flavor': 'Google', + }, + }; + const client = new HttpClient(); + return client.send(request).then((response) => { + if (!response.text) { + throw new CryptoSignerError({ + code: CryptoSignerErrorCode.INTERNAL_ERROR, + message: 'HTTP Response missing payload', + }); + } + this.serviceAccountId = response.text; + return response.text; + }).catch((err) => { + throw new CryptoSignerError({ + code: CryptoSignerErrorCode.INVALID_CREDENTIAL, + message: 'Failed to determine service account. Make sure to initialize ' + + 'the SDK with a service account credential. Alternatively specify a service ' + + `account with iam.serviceAccounts.signBlob permission. Original error: ${err}`, + }); + }); + } +} + +/** + * Creates a new CryptoSigner instance for the given app. If the app has been initialized with a + * service account credential, creates a ServiceAccountSigner. + * + * @param {FirebaseApp} app A FirebaseApp instance. + * @return {CryptoSigner} A CryptoSigner instance. + */ +export function cryptoSignerFromApp(app: FirebaseApp): CryptoSigner { + const credential = app.options.credential; + if (credential instanceof ServiceAccountCredential) { + return new ServiceAccountSigner(credential); + } + + return new IAMSigner(new AuthorizedHttpClient(app), app.options.serviceAccountId); +} + +/** + * Defines extended error info type. This includes a code, message string, and error data. + */ +export interface ExtendedErrorInfo extends ErrorInfo { + cause?: Error; +} + +/** + * CryptoSigner error code structure. + * + * @param {ErrorInfo} errorInfo The error information (code and message). + * @constructor + */ +export class CryptoSignerError extends Error { + constructor(private errorInfo: ExtendedErrorInfo) { + super(errorInfo.message); + + /* tslint:disable:max-line-length */ + // Set the prototype explicitly. See the following link for more details: + // https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + /* tslint:enable:max-line-length */ + (this as any).__proto__ = CryptoSignerError.prototype; + } + + /** @return {string} The error code. */ + public get code(): string { + return this.errorInfo.code; + } + + /** @return {string} The error message. */ + public get message(): string { + return this.errorInfo.message; + } + + /** @return {object} The error data. */ + public get cause(): Error | undefined { + return this.errorInfo.cause; + } +} + +/** + * Crypto Signer error codes and their default messages. + */ +export class CryptoSignerErrorCode { + public static INVALID_ARGUMENT = 'invalid-argument'; + public static INTERNAL_ERROR = 'internal-error'; + public static INVALID_CREDENTIAL = 'invalid-credential'; + public static SERVER_ERROR = 'server-error'; +} diff --git a/src/utils/jwt.ts b/src/utils/jwt.ts index d048567061..1fab2ff9fb 100644 --- a/src/utils/jwt.ts +++ b/src/utils/jwt.ts @@ -16,6 +16,7 @@ import * as validator from './validator'; import * as jwt from 'jsonwebtoken'; +import * as jwks from 'jwks-rsa'; import { HttpClient, HttpRequestConfig, HttpError } from '../utils/api-request'; import { Agent } from 'http'; @@ -28,6 +29,9 @@ export const ALGORITHM_RS256: jwt.Algorithm = 'RS256' as const; const JWT_CALLBACK_ERROR_PREFIX = 'error in secret or public key callback: '; const NO_MATCHING_KID_ERROR_MESSAGE = 'no-matching-kid-error'; +const NO_KID_IN_HEADER_ERROR_MESSAGE = 'no-kid-in-header-error'; + +const ONE_DAY_IN_SECONDS = 24 * 3600; export type Dictionary = { [key: string]: any } @@ -44,6 +48,51 @@ interface KeyFetcher { fetchPublicKeys(): Promise<{ [key: string]: string }>; } +export class JwksFetcher implements KeyFetcher { + private publicKeys: { [key: string]: string }; + private publicKeysExpireAt = 0; + private client: jwks.JwksClient; + + constructor(jwksUrl: string) { + if (!validator.isURL(jwksUrl)) { + throw new Error('The provided JWKS URL is not a valid URL.'); + } + + this.client = jwks({ + jwksUri: jwksUrl, + cache: false, // disable jwks-rsa LRU cache as the keys are always cahced for 24 hours. + }); + } + + public fetchPublicKeys(): Promise<{ [key: string]: string }> { + if (this.shouldRefresh()) { + return this.refresh(); + } + return Promise.resolve(this.publicKeys); + } + + private shouldRefresh(): boolean { + return !this.publicKeys || this.publicKeysExpireAt <= Date.now(); + } + + private refresh(): Promise<{ [key: string]: string }> { + return this.client.getSigningKeys() + .then((signingKeys) => { + // reset expire at from previous set of keys. + this.publicKeysExpireAt = 0; + const newKeys = signingKeys.reduce((map: { [key: string]: string }, signingKey: jwks.SigningKey) => { + map[signingKey.kid] = signingKey.getPublicKey(); + return map; + }, {}); + this.publicKeysExpireAt = Date.now() + (ONE_DAY_IN_SECONDS * 1000); + this.publicKeys = newKeys; + return newKeys; + }).catch((err) => { + throw new Error(`Error fetching Json Web Keys: ${err.message}`); + }); + } +} + /** * Class to fetch public keys from a client certificates URL. */ @@ -141,13 +190,51 @@ export class PublicKeySignatureVerifier implements SignatureVerifier { return new PublicKeySignatureVerifier(new UrlKeyFetcher(clientCertUrl, httpAgent)); } + public static withJwksUrl(jwksUrl: string): PublicKeySignatureVerifier { + return new PublicKeySignatureVerifier(new JwksFetcher(jwksUrl)); + } + public verify(token: string): Promise { if (!validator.isString(token)) { return Promise.reject(new JwtError(JwtErrorCode.INVALID_ARGUMENT, 'The provided token must be a string.')); } - return verifyJwtSignature(token, getKeyCallback(this.keyFetcher), { algorithms: [ALGORITHM_RS256] }); + return verifyJwtSignature(token, getKeyCallback(this.keyFetcher), { algorithms: [ALGORITHM_RS256] }) + .catch((error: JwtError) => { + if (error.code === JwtErrorCode.NO_KID_IN_HEADER) { + // No kid in JWT header. Try with all the public keys. + return this.verifyWithoutKid(token); + } + throw error; + }); + } + + private verifyWithoutKid(token: string): Promise { + return this.keyFetcher.fetchPublicKeys() + .then(publicKeys => this.verifyWithAllKeys(token, publicKeys)); + } + + private verifyWithAllKeys(token: string, keys: { [key: string]: string }): Promise { + const promises: Promise[] = []; + Object.values(keys).forEach((key) => { + const result = verifyJwtSignature(token, key) + .then(() => true) + .catch((error) => { + if (error.code === JwtErrorCode.TOKEN_EXPIRED) { + throw error; + } + return false; + }) + promises.push(result); + }); + + return Promise.all(promises) + .then((result) => { + if (result.every((r) => r === false)) { + throw new JwtError(JwtErrorCode.INVALID_SIGNATURE, 'Invalid token signature.'); + } + }); } } @@ -169,6 +256,9 @@ export class EmulatorSignatureVerifier implements SignatureVerifier { */ function getKeyCallback(fetcher: KeyFetcher): jwt.GetPublicKeyOrSecret { return (header: jwt.JwtHeader, callback: jwt.SigningKeyCallback) => { + if (!header.kid) { + callback(new Error(NO_KID_IN_HEADER_ERROR_MESSAGE)); + } const kid = header.kid || ''; fetcher.fetchPublicKeys().then((publicKeys) => { if (!Object.prototype.hasOwnProperty.call(publicKeys, kid)) { @@ -212,8 +302,12 @@ export function verifyJwtSignature(token: string, secretOrPublicKey: jwt.Secret } else if (error.name === 'JsonWebTokenError') { if (error.message && error.message.includes(JWT_CALLBACK_ERROR_PREFIX)) { const message = error.message.split(JWT_CALLBACK_ERROR_PREFIX).pop() || 'Error fetching public keys.'; - const code = (message === NO_MATCHING_KID_ERROR_MESSAGE) ? JwtErrorCode.NO_MATCHING_KID : - JwtErrorCode.KEY_FETCH_ERROR; + let code = JwtErrorCode.KEY_FETCH_ERROR; + if (message === NO_MATCHING_KID_ERROR_MESSAGE) { + code = JwtErrorCode.NO_MATCHING_KID; + } else if (message === NO_KID_IN_HEADER_ERROR_MESSAGE) { + code = JwtErrorCode.NO_KID_IN_HEADER; + } return reject(new JwtError(code, message)); } } @@ -271,5 +365,6 @@ export enum JwtErrorCode { TOKEN_EXPIRED = 'token-expired', INVALID_SIGNATURE = 'invalid-token', NO_MATCHING_KID = 'no-matching-kid-error', + NO_KID_IN_HEADER = 'no-kid-error', KEY_FETCH_ERROR = 'key-fetch-error', } diff --git a/test/integration/app-check.spec.ts b/test/integration/app-check.spec.ts new file mode 100644 index 0000000000..32386f32bc --- /dev/null +++ b/test/integration/app-check.spec.ts @@ -0,0 +1,103 @@ +/*! + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as _ from 'lodash'; +import * as admin from '../../lib/index'; +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; +import fs = require('fs'); +import path = require('path'); + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const chalk = require('chalk'); + +chai.should(); +chai.use(chaiAsPromised); + +const expect = chai.expect; + +let appId: string; + +describe('admin.appCheck', () => { + before(async () => { + try { + appId = fs.readFileSync(path.join(__dirname, '../resources/appid.txt')).toString().trim(); + } catch (error) { + console.log(chalk.yellow( + 'Unable to find an an App ID. Skipping tests that require a valid App ID.', + error, + )); + } + }); + + describe('createToken', () => { + it('should succeed with a vaild token', function() { + if (!appId) { + this.skip(); + } + return admin.appCheck().createToken(appId as string) + .then((token) => { + expect(token).to.have.keys(['token', 'ttlMillis']); + expect(token.token).to.be.a('string').and.to.not.be.empty; + expect(token.ttlMillis).to.be.a('number'); + }); + }); + + it('should propagate API errors', () => { + // rejects with invalid-argument when appId is incorrect + return admin.appCheck().createToken('incorrect-app-id') + .should.eventually.be.rejected.and.have.property('code', 'app-check/invalid-argument'); + }); + + const invalidAppIds = ['', null, NaN, 0, 1, true, false, [], {}, { a: 1 }, _.noop]; + invalidAppIds.forEach((invalidAppId) => { + it(`should throw given an invalid appId: ${JSON.stringify(invalidAppId)}`, () => { + expect(() => admin.appCheck().createToken(invalidAppId as any)) + .to.throw('appId` must be a non-empty string.'); + }); + }); + }); + + describe('verifyToken', () => { + let validToken: admin.appCheck.AppCheckToken; + + before(async () => { + if (!appId) { + return; + } + // obtain a valid app check token + validToken = await admin.appCheck().createToken(appId as string); + }); + + it('should succeed with a decoded verifed token response', function() { + if (!appId) { + this.skip(); + } + return admin.appCheck().verifyToken(validToken.token) + .then((verifedToken) => { + expect(verifedToken).to.have.keys(['token', 'appId']); + expect(verifedToken.token).to.have.keys(['iss', 'sub', 'aud', 'exp', 'iat', 'app_id']); + expect(verifedToken.token.app_id).to.be.a('string').and.equals(appId); + }); + }); + + it('should propagate API errors', () => { + // rejects with invalid-argument when the token is invalid + return admin.appCheck().verifyToken('invalid-token') + .should.eventually.be.rejected.and.have.property('code', 'app-check/invalid-argument'); + }); + }); +}); diff --git a/test/resources/mock.jwks.json b/test/resources/mock.jwks.json new file mode 100644 index 0000000000..08695991c3 --- /dev/null +++ b/test/resources/mock.jwks.json @@ -0,0 +1,12 @@ +{ + "keys": [ + { + "kty": "RSA", + "e": "AQAB", + "use": "sig", + "kid": "FGQdnRlzAmKyKr6-Hg_kMQrBkj_H6i6ADnBQz4OI6BU", + "alg": "RS256", + "n": "rFYQyEdjj43mnpXwj-3WgAE01TSYe1-XFE9mxUDShysFwtVZOHFSMm6kl-B3Y_O8NcPt5osntLlH6KHvygExAE0tDmFYq8aKt7LQQF8rTv0rI6MP92ezyCEp4MPmAPFD_tY160XGrkqApuY2_-L8eEXdkRyH2H7lCYypFC0u3DIY25Vlq-ZDkxB2kGykGgb1zVazCDDViqV1p9hSltmm4el9AyF08FsMCpk_NvwKOY4pJ_sm99CDKxMhQBaT9lrIQt0B1VqTpEwlOoiFiyXASRXp9ZTeL4mrLPqSeozwPvspD81wbgecd62F640scKBr3ko73L8M8UWcwgd-moKCJw" + } + ] +} diff --git a/test/resources/mocks.ts b/test/resources/mocks.ts index 5512756039..152364163e 100644 --- a/test/resources/mocks.ts +++ b/test/resources/mocks.ts @@ -36,6 +36,8 @@ const ONE_HOUR_IN_SECONDS = 60 * 60; export const uid = 'someUid'; export const projectId = 'project_id'; +export const projectNumber = '12345678'; +export const appId = '12345678:app:ID'; export const developerClaims = { one: 'uno', two: 'dos', @@ -146,6 +148,10 @@ export const refreshToken = { type: 'refreshToken', }; +// Randomly generated JSON Web Key Sets that do not correspond to anything related to Firebase. +// eslint-disable-next-line @typescript-eslint/no-var-requires +export const jwksResponse = require('./mock.jwks.json'); + // eslint-disable-next-line @typescript-eslint/no-var-requires export const certificateObject = require('./mock.key.json'); @@ -178,6 +184,14 @@ export const x509CertPairs = [ /* eslint-enable max-len */ ]; +// Randomly generated key pairs that don't correspond to anything related to Firebase or GCP +export const jwksKeyPair = { + /* eslint-disable max-len */ + // The private key for this key pair is identical to the one used in ./mock.jwks.json + private: '-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEArFYQyEdjj43mnpXwj+3WgAE01TSYe1+XFE9mxUDShysFwtVZ\nOHFSMm6kl+B3Y/O8NcPt5osntLlH6KHvygExAE0tDmFYq8aKt7LQQF8rTv0rI6MP\n92ezyCEp4MPmAPFD/tY160XGrkqApuY2/+L8eEXdkRyH2H7lCYypFC0u3DIY25Vl\nq+ZDkxB2kGykGgb1zVazCDDViqV1p9hSltmm4el9AyF08FsMCpk/NvwKOY4pJ/sm\n99CDKxMhQBaT9lrIQt0B1VqTpEwlOoiFiyXASRXp9ZTeL4mrLPqSeozwPvspD81w\nbgecd62F640scKBr3ko73L8M8UWcwgd+moKCJwIDAQABAoIBAEDPJQSMhE6KKL5e\n2NbntJDy4zGC1A0hh6llqtpnZETc0w/QN/tX8ndw0IklKwD1ukPl6OOYVVhLjVVZ\nANpQ1GKuo1ETHsuKoMQwhMyQfbL41m5SdkCuSRfsENmsEiUslkuRtzlBRlRpRDR/\nwxM8A4IflBFsT1IFdpC+yx8BVuwLc35iVnaGQpo/jhSDibt07j+FdOKEWkMGj+rL\nsHC6cpB2NMTBl9CIDLW/eq1amBOAGtsSKqoGJvaQY/mZf7SPkRjYIfIl2PWSaduT\nfmMrsYYFtHUKVOMYAD7P5RWNkS8oERucnXT3ouAECvip3Ew2JqlQc0FP7FS5CxH3\nWdfvLuECgYEA8Q7rJrDOdO867s7P/lXMklbAGnuNnAZJdAEXUMIaPJi7al97F119\n4DKBuF7c/dDf8CdiOvMzP8r/F8+FFx2D61xxkQNeuxo5Xjlt23OzW5EI2S6ABesZ\n/3sQWqvKCGuqN7WENYF3EiKyByQ22MYXk8CE7KZuO57Aj88t6TsaNhkCgYEAtwSs\nhbqKSCneC1bQ3wfSAF2kPYRrQEEa2VCLlX1Mz7zHufxksUWAnAbU8O3hIGnXjz6T\nqzivyJJhFSgNGeYpwV67GfXnibpr3OZ/yx2YXIQfp0daivj++kvEU7aNfM9rHZA9\nS3Gh7hKELdB9b0DkrX5GpLiZWA6NnJdrIRYbAj8CgYBCZSyJvJsxBA+EZTxOvk0Z\nZYGGCc/oUKb8p6xHVx8o35yHYQMjXWHlVaP7J03RLy3vFLnuqLvN71ixszviMQP7\n2LuDCJ2YBVIVzNWgY07cgqcgQrmKZ8YCY2AOyVBdX2JD8+AVaLJmMV49r1DYBj/K\nN3WlRPYJv+Ej+xmXKus+SQKBgHh/Zkthxxu+HQigL0M4teYxwSoTnj2e39uGsXBK\nICGCLIniiDVDCmswAFFkfV3G8frI+5a26t2Gqs6wIPgVVxaOlWeBROGkUNIPHMKR\niLgY8XJEg3OOfuoyql9niP5M3jyHtCOQ/Elv/YDgjUWLl0Q3KLHZLHUSl+AqvYj6\nMewnAoGBANgYzPZgP+wreI55BFR470blKh1mFz+YGa+53DCd7JdMH2pdp4hoh303\nXxpOSVlAuyv9SgTsZ7WjGO5UdhaBzVPKgN0OO6JQmQ5ZrOR8ZJ7VB73FiVHCEerj\n1m2zyFv6OT7vqdg+V1/SzxMEmXXFQv1g69k6nWGazne3IJlzrSpj\n-----END RSA PRIVATE KEY-----\n', + public: '-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArFYQyEdjj43mnpXwj+3W\ngAE01TSYe1+XFE9mxUDShysFwtVZOHFSMm6kl+B3Y/O8NcPt5osntLlH6KHvygEx\nAE0tDmFYq8aKt7LQQF8rTv0rI6MP92ezyCEp4MPmAPFD/tY160XGrkqApuY2/+L8\neEXdkRyH2H7lCYypFC0u3DIY25Vlq+ZDkxB2kGykGgb1zVazCDDViqV1p9hSltmm\n4el9AyF08FsMCpk/NvwKOY4pJ/sm99CDKxMhQBaT9lrIQt0B1VqTpEwlOoiFiyXA\nSRXp9ZTeL4mrLPqSeozwPvspD81wbgecd62F640scKBr3ko73L8M8UWcwgd+moKC\nJwIDAQAB\n-----END PUBLIC KEY-----\n', +}; + /** * Generates a mocked Firebase ID token. * @@ -227,6 +241,27 @@ export function generateSessionCookie(overrides?: object, expiresIn?: number): s return jwt.sign(developerClaims, certificateObject.private_key, options); } +/** + * Generates a mocked App Check token. + * + * @param {object} overrides Overrides for the generated token's attributes. + * @return {string} A mocked App Check token with any provided overrides included. + */ +export function generateAppCheckToken(overrides?: object): string { + const options = _.assign({ + audience: ['projects/' + projectNumber, 'projects/' + projectId], + expiresIn: ONE_HOUR_IN_SECONDS, + issuer: 'https://firebaseappcheck.googleapis.com/' + projectNumber, + subject: appId, + algorithm: ALGORITHM, + header: { + kid: jwksResponse.keys[0].kid, + }, + }, overrides); + + return jwt.sign(developerClaims, jwksKeyPair.private, options); +} + /** Mock socket emitter class. */ export class MockSocketEmitter extends events.EventEmitter { public setTimeout: (_: number) => void = () => undefined; diff --git a/test/unit/app-check/app-check-api-client-internal.spec.ts b/test/unit/app-check/app-check-api-client-internal.spec.ts new file mode 100644 index 0000000000..fba1fba20d --- /dev/null +++ b/test/unit/app-check/app-check-api-client-internal.spec.ts @@ -0,0 +1,238 @@ +/*! + * @license + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +import * as _ from 'lodash'; +import * as chai from 'chai'; +import * as sinon from 'sinon'; +import { HttpClient } from '../../../src/utils/api-request'; +import * as utils from '../utils'; +import * as mocks from '../../resources/mocks'; +import { getSdkVersion } from '../../../src/utils'; + +import { FirebaseApp } from '../../../src/firebase-app'; +import { AppCheckApiClient, FirebaseAppCheckError } from '../../../src/app-check/app-check-api-client-internal'; +import { FirebaseAppError } from '../../../src/utils/error'; +import { deepCopy } from '../../../src/utils/deep-copy'; + +const expect = chai.expect; + +describe('AppCheckApiClient', () => { + + const ERROR_RESPONSE = { + error: { + code: 404, + message: 'Requested entity not found', + status: 'NOT_FOUND', + }, + }; + + const EXPECTED_HEADERS = { + 'Authorization': 'Bearer mock-token', + 'X-Firebase-Client': `fire-admin-node/${getSdkVersion()}`, + }; + + const noProjectId = 'Failed to determine project ID. Initialize the SDK with service ' + + 'account credentials or set project ID as an app option. Alternatively, set the ' + + 'GOOGLE_CLOUD_PROJECT environment variable.'; + + const APP_ID = '1:1234:android:1234'; + + const TEST_TOKEN_TO_EXCHANGE = 'signed-custom-token'; + + const TEST_RESPONSE = { + attestationToken: 'token', + ttl: '3s' + }; + + const mockOptions = { + credential: new mocks.MockCredential(), + projectId: 'test-project', + }; + + const clientWithoutProjectId = new AppCheckApiClient( + mocks.mockCredentialApp()); + + // Stubs used to simulate underlying api calls. + let stubs: sinon.SinonStub[] = []; + let app: FirebaseApp; + let apiClient: AppCheckApiClient; + + beforeEach(() => { + app = mocks.appWithOptions(mockOptions); + apiClient = new AppCheckApiClient(app); + }); + + afterEach(() => { + _.forEach(stubs, (stub) => stub.restore()); + stubs = []; + return app.delete(); + }); + + describe('Constructor', () => { + it('should reject when the app is null', () => { + expect(() => new AppCheckApiClient(null as unknown as FirebaseApp)) + .to.throw('First argument passed to admin.appCheck() must be a valid Firebase app instance.'); + }); + }); + + describe('exchangeToken', () => { + it('should reject when project id is not available', () => { + return clientWithoutProjectId.exchangeToken(TEST_TOKEN_TO_EXCHANGE, APP_ID) + .should.eventually.be.rejectedWith(noProjectId); + }); + + it('should throw given no appId', () => { + expect(() => { + (apiClient as any).exchangeToken(TEST_TOKEN_TO_EXCHANGE); + }).to.throw('appId` must be a non-empty string.'); + }); + + const invalidAppIds = [null, NaN, 0, 1, true, false, [], {}, { a: 1 }, _.noop]; + invalidAppIds.forEach((invalidAppId) => { + it('should throw given a non-string appId: ' + JSON.stringify(invalidAppId), () => { + expect(() => { + apiClient.exchangeToken(TEST_TOKEN_TO_EXCHANGE, invalidAppId as any); + }).to.throw('appId` must be a non-empty string.'); + }); + }); + + it('should throw given an empty string appId', () => { + expect(() => { + apiClient.exchangeToken(TEST_TOKEN_TO_EXCHANGE, ''); + }).to.throw('appId` must be a non-empty string.'); + }); + + it('should throw given no customToken', () => { + expect(() => { + (apiClient as any).exchangeToken(undefined, APP_ID); + }).to.throw('customToken` must be a non-empty string.'); + }); + + const invalidCustomTokens = [null, NaN, 0, 1, true, false, [], {}, { a: 1 }, _.noop]; + invalidCustomTokens.forEach((invalidCustomToken) => { + it('should throw given a non-string customToken: ' + JSON.stringify(invalidCustomToken), () => { + expect(() => { + apiClient.exchangeToken(invalidCustomToken as any, APP_ID); + }).to.throw('customToken` must be a non-empty string.'); + }); + }); + + it('should throw given an empty string customToken', () => { + expect(() => { + apiClient.exchangeToken('', APP_ID); + }).to.throw('customToken` must be a non-empty string.'); + }); + + it('should reject when a full platform error response is received', () => { + const stub = sinon + .stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom(ERROR_RESPONSE, 404)); + stubs.push(stub); + const expected = new FirebaseAppCheckError('not-found', 'Requested entity not found'); + return apiClient.exchangeToken(TEST_TOKEN_TO_EXCHANGE, APP_ID) + .should.eventually.be.rejected.and.deep.include(expected); + }); + + it('should reject with unknown-error when error code is not present', () => { + const stub = sinon + .stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom({}, 404)); + stubs.push(stub); + const expected = new FirebaseAppCheckError('unknown-error', 'Unknown server error: {}'); + return apiClient.exchangeToken(TEST_TOKEN_TO_EXCHANGE, APP_ID) + .should.eventually.be.rejected.and.deep.include(expected); + }); + + it('should reject with unknown-error for non-json response', () => { + const stub = sinon + .stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom('not json', 404)); + stubs.push(stub); + const expected = new FirebaseAppCheckError( + 'unknown-error', 'Unexpected response with status: 404 and body: not json'); + return apiClient.exchangeToken(TEST_TOKEN_TO_EXCHANGE, APP_ID) + .should.eventually.be.rejected.and.deep.include(expected); + }); + + it('should reject when rejected with a FirebaseAppError', () => { + const expected = new FirebaseAppError('network-error', 'socket hang up'); + const stub = sinon + .stub(HttpClient.prototype, 'send') + .rejects(expected); + stubs.push(stub); + return apiClient.exchangeToken(TEST_TOKEN_TO_EXCHANGE, APP_ID) + .should.eventually.be.rejected.and.deep.include(expected); + }); + + ['', 'abc', '3s2', 'sssa', '3.000000001', '3.2', null, NaN, true, [], {}, 100, 1.2, -200, -2.4] + .forEach((invalidDuration) => { + it(`should throw if the returned ttl duration is: ${invalidDuration}`, () => { + const response = deepCopy(TEST_RESPONSE); + (response as any).ttl = invalidDuration; + const stub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom(response, 200)); + stubs.push(stub); + const expected = new FirebaseAppCheckError( + 'invalid-argument', '`ttl` must be a valid duration string with the suffix `s`.'); + return apiClient.exchangeToken(TEST_TOKEN_TO_EXCHANGE, APP_ID) + .should.eventually.be.rejected.and.deep.include(expected); + }); + }); + + it('should resolve with the App Check token on success', () => { + const stub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom(TEST_RESPONSE, 200)); + stubs.push(stub); + return apiClient.exchangeToken(TEST_TOKEN_TO_EXCHANGE, APP_ID) + .then((resp) => { + expect(resp.token).to.deep.equal(TEST_RESPONSE.attestationToken); + expect(resp.ttlMillis).to.deep.equal(3000); + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: `https://firebaseappcheck.googleapis.com/v1beta/projects/test-project/apps/${APP_ID}:exchangeCustomToken`, + headers: EXPECTED_HEADERS, + data: { customToken: TEST_TOKEN_TO_EXCHANGE } + }); + }); + }); + + new Map([['3s', 3000], ['4.1s', 4100], ['3.000000001s', 3000], ['3.000001s', 3000]]) + .forEach((ttlMillis, ttlString) => { // value, key, map + // 3 seconds with 0 nanoseconds expressed as "3s" + // 3 seconds and 1 nanosecond expressed as "3.000000001s" + // 3 seconds and 1 microsecond expressed as "3.000001s" + it(`should resolve with ttlMillis as ${ttlMillis} when ttl + from server is: ${ttlString}`, () => { + const response = deepCopy(TEST_RESPONSE); + (response as any).ttl = ttlString; + const stub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom(response, 200)); + stubs.push(stub); + return apiClient.exchangeToken(TEST_TOKEN_TO_EXCHANGE, APP_ID) + .then((resp) => { + expect(resp.token).to.deep.equal(response.attestationToken); + expect(resp.ttlMillis).to.deep.equal(ttlMillis); + }); + }); + }); + }); +}); diff --git a/test/unit/app-check/app-check.spec.ts b/test/unit/app-check/app-check.spec.ts new file mode 100644 index 0000000000..c30970bc13 --- /dev/null +++ b/test/unit/app-check/app-check.spec.ts @@ -0,0 +1,195 @@ +/*! + * @license + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +import * as _ from 'lodash'; +import * as chai from 'chai'; +import * as sinon from 'sinon'; +import * as mocks from '../../resources/mocks'; + +import { FirebaseApp } from '../../../src/firebase-app'; +import { AppCheck } from '../../../src/app-check/app-check'; +import { AppCheckApiClient, FirebaseAppCheckError } from '../../../src/app-check/app-check-api-client-internal'; +import { AppCheckTokenGenerator } from '../../../src/app-check/token-generator'; +import { HttpClient } from '../../../src/utils/api-request'; +import { ServiceAccountSigner } from '../../../src/utils/crypto-signer'; +import { AppCheckTokenVerifier } from '../../../src/app-check/token-verifier'; + +const expect = chai.expect; + +describe('AppCheck', () => { + + const INTERNAL_ERROR = new FirebaseAppCheckError('internal-error', 'message'); + const APP_ID = '1:1234:android:1234'; + const TEST_TOKEN_TO_EXCHANGE = 'signed-custom-token'; + + let appCheck: AppCheck; + + let mockApp: FirebaseApp; + let mockCredentialApp: FirebaseApp; + + // Stubs used to simulate underlying api calls. + let stubs: sinon.SinonStub[] = []; + + before(() => { + mockApp = mocks.app(); + mockCredentialApp = mocks.mockCredentialApp(); + appCheck = new AppCheck(mockApp); + }); + + after(() => { + return mockApp.delete(); + }); + + afterEach(() => { + _.forEach(stubs, (stub) => stub.restore()); + stubs = []; + }); + + describe('Constructor', () => { + const invalidApps = [null, NaN, 0, 1, true, false, '', 'a', [], [1, 'a'], {}, { a: 1 }, _.noop]; + invalidApps.forEach((invalidApp) => { + it('should throw given invalid app: ' + JSON.stringify(invalidApp), () => { + expect(() => { + const appCheckAny: any = AppCheck; + return new appCheckAny(invalidApp); + }).to.throw( + 'First argument passed to admin.appCheck() must be a valid Firebase app ' + + 'instance.'); + }); + }); + + it('should throw given no app', () => { + expect(() => { + const appCheckAny: any = AppCheck; + return new appCheckAny(); + }).to.throw( + 'First argument passed to admin.appCheck() must be a valid Firebase app ' + + 'instance.'); + }); + + it('should reject when initialized without project ID', () => { + // Project ID not set in the environment. + delete process.env.GOOGLE_CLOUD_PROJECT; + delete process.env.GCLOUD_PROJECT; + const noProjectId = 'Failed to determine project ID. Initialize the SDK with service ' + + 'account credentials or set project ID as an app option. Alternatively, set the ' + + 'GOOGLE_CLOUD_PROJECT environment variable.'; + const appCheckWithoutProjectId = new AppCheck(mockCredentialApp); + const stub = sinon.stub(AppCheckTokenGenerator.prototype, 'createCustomToken') + .resolves(TEST_TOKEN_TO_EXCHANGE); + stubs.push(stub); + return appCheckWithoutProjectId.createToken(APP_ID) + .should.eventually.rejectedWith(noProjectId); + }); + + it('should reject when failed to contact the Metadata server', () => { + // Remove the Project ID to force a request to the Metadata server + delete process.env.GOOGLE_CLOUD_PROJECT; + delete process.env.GCLOUD_PROJECT; + const appCheckWithoutProjectId = new AppCheck(mockCredentialApp); + const stub = sinon.stub(HttpClient.prototype, 'send') + .rejects(new Error('network error.')); + stubs.push(stub); + const expected = 'Failed to determine service account. Make sure to initialize the SDK ' + + 'with a service account credential. Alternatively specify a service account with ' + + 'iam.serviceAccounts.signBlob permission. Original error: ' + + 'Error: network error.'; + return appCheckWithoutProjectId.createToken(APP_ID) + .should.eventually.be.rejectedWith(expected); + }); + + it('should reject when failed to sign the token', () => { + const expected = 'sign error'; + const stub = sinon.stub(ServiceAccountSigner.prototype, 'sign') + .rejects(new Error(expected)); + stubs.push(stub); + return appCheck.createToken(APP_ID) + .should.eventually.be.rejectedWith(expected); + }); + + it('should not throw given a valid app', () => { + expect(() => { + return new AppCheck(mockApp); + }).not.to.throw(); + }); + }); + + describe('app', () => { + it('returns the app from the constructor', () => { + // We expect referential equality here + expect(appCheck.app).to.equal(mockApp); + }); + }); + + describe('createToken', () => { + it('should propagate API errors', () => { + const stub = sinon + .stub(AppCheckApiClient.prototype, 'exchangeToken') + .rejects(INTERNAL_ERROR); + stubs.push(stub); + return appCheck.createToken(APP_ID) + .should.eventually.be.rejected.and.deep.equal(INTERNAL_ERROR); + }); + + it('should resolve with AppCheckToken on success', () => { + const response = { token: 'token', ttlMillis: 3000 }; + const stub = sinon + .stub(AppCheckApiClient.prototype, 'exchangeToken') + .resolves(response); + stubs.push(stub); + return appCheck.createToken(APP_ID) + .then((token) => { + expect(token.token).equals('token'); + expect(token.ttlMillis).equals(3000); + }); + }); + }); + + describe('verifyToken', () => { + it('should propagate API errors', () => { + const stub = sinon + .stub(AppCheckTokenVerifier.prototype, 'verifyToken') + .rejects(INTERNAL_ERROR); + stubs.push(stub); + return appCheck.verifyToken('token') + .should.eventually.be.rejected.and.deep.equal(INTERNAL_ERROR); + }); + + it('should resolve with VerifyAppCheckTokenResponse on success', () => { + const response = { + sub: 'app-id', + iss: 'https://firebaseappcheck.googleapis.com/123456', + // eslint-disable-next-line @typescript-eslint/camelcase + app_id: 'app-id', + aud: ['123456', 'project-id'], + exp: 1617741496, + iat: 1516239022, + }; + const stub = sinon + .stub(AppCheckTokenVerifier.prototype, 'verifyToken') + .resolves(response); + stubs.push(stub); + return appCheck.verifyToken('token') + .then((tokenResponse) => { + expect(tokenResponse.appId).equals('app-id'); + expect(tokenResponse.token).equals(response); + }); + }); + }); +}); diff --git a/test/unit/app-check/token-generator.spec.ts b/test/unit/app-check/token-generator.spec.ts new file mode 100644 index 0000000000..66bb7cffba --- /dev/null +++ b/test/unit/app-check/token-generator.spec.ts @@ -0,0 +1,261 @@ +/*! + * @license + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +import * as _ from 'lodash'; +import * as jwt from 'jsonwebtoken'; +import * as chai from 'chai'; +import * as sinon from 'sinon'; +import * as sinonChai from 'sinon-chai'; +import * as chaiAsPromised from 'chai-as-promised'; +import * as mocks from '../../resources/mocks'; + +import { + appCheckErrorFromCryptoSignerError, + AppCheckTokenGenerator +} from '../../../src/app-check/token-generator'; +import { + CryptoSignerError, CryptoSignerErrorCode, ServiceAccountSigner +} from '../../../src/utils/crypto-signer'; +import { ServiceAccountCredential } from '../../../src/credential/credential-internal'; +import { FirebaseAppCheckError } from '../../../src/app-check/app-check-api-client-internal'; +import * as utils from '../utils'; + +chai.should(); +chai.use(sinonChai); +chai.use(chaiAsPromised); + +const expect = chai.expect; + +const ALGORITHM = 'RS256'; +const ONE_HOUR_IN_SECONDS = 60 * 60; +const FIREBASE_APP_CHECK_AUDIENCE = 'https://firebaseappcheck.googleapis.com/google.firebase.appcheck.v1beta.TokenExchangeService'; + +/** + * Verifies a token is signed with the private key corresponding to the provided public key. + * + * @param {string} token The token to verify. + * @param {string} publicKey The public key to use to verify the token. + * @return {Promise} A promise fulfilled with the decoded token if it is valid; otherwise, a rejected promise. + */ +function verifyToken(token: string, publicKey: string): Promise { + return new Promise((resolve, reject) => { + jwt.verify(token, publicKey, { + algorithms: [ALGORITHM], + }, (err, res) => { + if (err) { + reject(err); + } else { + resolve(res as object); + } + }); + }); +} + +describe('AppCheckTokenGenerator', () => { + const cert = new ServiceAccountCredential(mocks.certificateObject); + const APP_ID = 'test-app-id'; + + let clock: sinon.SinonFakeTimers | undefined; + afterEach(() => { + if (clock) { + clock.restore(); + clock = undefined; + } + }); + + describe('Constructor', () => { + it('should throw given no arguments', () => { + expect(() => { + // Need to overcome the type system to allow a call with no parameter + const anyFirebaseAppCheckTokenGenerator: any = AppCheckTokenGenerator; + return new anyFirebaseAppCheckTokenGenerator(); + }).to.throw('Must provide a CryptoSigner to use AppCheckTokenGenerator'); + }); + }); + + const invalidSigners: any[] = [null, NaN, 0, 1, true, false, '', 'a', [], _.noop]; + invalidSigners.forEach((invalidSigner) => { + it('should throw given invalid signer: ' + JSON.stringify(invalidSigner), () => { + expect(() => { + return new AppCheckTokenGenerator(invalidSigner as any); + }).to.throw('Must provide a CryptoSigner to use AppCheckTokenGenerator'); + }); + }); + + describe('createCustomToken()', () => { + const tokenGenerator = new AppCheckTokenGenerator(new ServiceAccountSigner(cert)); + + it('should throw given no appId', () => { + expect(() => { + (tokenGenerator as any).createCustomToken(); + }).to.throw(FirebaseAppCheckError).with.property('code', 'app-check/invalid-argument'); + }); + + const invalidAppIds = [null, NaN, 0, 1, true, false, [], {}, { a: 1 }, _.noop]; + invalidAppIds.forEach((invalidAppId) => { + it('should throw given a non-string appId: ' + JSON.stringify(invalidAppId), () => { + expect(() => { + tokenGenerator.createCustomToken(invalidAppId as any); + }).to.throw(FirebaseAppCheckError).with.property('code', 'app-check/invalid-argument'); + }); + }); + + it('should throw given an empty string appId', () => { + expect(() => { + tokenGenerator.createCustomToken(''); + }).to.throw(FirebaseAppCheckError).with.property('code', 'app-check/invalid-argument'); + }); + + it('should be fulfilled with a Firebase Custom JWT', () => { + return tokenGenerator.createCustomToken(APP_ID) + .should.eventually.be.a('string').and.not.be.empty; + }); + + it('should be fulfilled with a JWT with the correct decoded payload', () => { + clock = sinon.useFakeTimers(1000); + + return tokenGenerator.createCustomToken(APP_ID) + .then((token) => { + const decoded = jwt.decode(token); + const expected: { [key: string]: any } = { + // eslint-disable-next-line @typescript-eslint/camelcase + app_id: APP_ID, + iat: 1, + exp: ONE_HOUR_IN_SECONDS + 1, + aud: FIREBASE_APP_CHECK_AUDIENCE, + iss: mocks.certificateObject.client_email, + sub: mocks.certificateObject.client_email, + }; + + expect(decoded).to.deep.equal(expected); + }); + }); + + it('should be fulfilled with a JWT with the correct header', () => { + clock = sinon.useFakeTimers(1000); + + return tokenGenerator.createCustomToken(APP_ID) + .then((token) => { + const decoded: any = jwt.decode(token, { + complete: true, + }); + expect(decoded.header).to.deep.equal({ + alg: ALGORITHM, + typ: 'JWT', + }); + }); + }); + + it('should be fulfilled with a JWT which can be verified by the service account public key', () => { + return tokenGenerator.createCustomToken(APP_ID) + .then((token) => { + return verifyToken(token, mocks.keyPairs[0].public); + }); + }); + + it('should be fulfilled with a JWT which cannot be verified by a random public key', () => { + return tokenGenerator.createCustomToken(APP_ID) + .then((token) => { + return verifyToken(token, mocks.keyPairs[1].public) + .should.eventually.be.rejectedWith('invalid signature'); + }); + }); + + it('should be fulfilled with a JWT which expires after one hour', () => { + clock = sinon.useFakeTimers(1000); + + let token: string; + return tokenGenerator.createCustomToken(APP_ID) + .then((result) => { + token = result; + + clock!.tick((ONE_HOUR_IN_SECONDS * 1000) - 1); + + // Token should still be valid + return verifyToken(token, mocks.keyPairs[0].public); + }) + .then(() => { + clock!.tick(1); + + // Token should now be invalid + return verifyToken(token, mocks.keyPairs[0].public) + .should.eventually.be.rejectedWith('jwt expired'); + }); + }); + + describe('appCheckErrorFromCryptoSignerError', () => { + it('should convert CryptoSignerError to FirebaseAppCheckError', () => { + const cryptoError = new CryptoSignerError({ + code: CryptoSignerErrorCode.INVALID_ARGUMENT, + message: 'test error.', + }); + const appCheckError = appCheckErrorFromCryptoSignerError(cryptoError); + expect(appCheckError).to.be.an.instanceof(FirebaseAppCheckError); + expect(appCheckError).to.have.property('code', 'app-check/invalid-argument'); + expect(appCheckError).to.have.property('message', 'test error.'); + }); + + it('should convert CryptoSignerError HttpError to FirebaseAppCheckError', () => { + const cryptoError = new CryptoSignerError({ + code: CryptoSignerErrorCode.SERVER_ERROR, + message: 'test error.', + cause: utils.errorFrom({ + error: { + message: 'server error.', + }, + }) + }); + const appCheckError = appCheckErrorFromCryptoSignerError(cryptoError); + expect(appCheckError).to.be.an.instanceof(FirebaseAppCheckError); + expect(appCheckError).to.have.property('code', 'app-check/unknown-error'); + expect(appCheckError).to.have.property('message', + 'Error returned from server while siging a custom token: server error.'); + }); + + it('should convert CryptoSignerError HttpError with no error.message to FirebaseAppCheckError', () => { + const cryptoError = new CryptoSignerError({ + code: CryptoSignerErrorCode.SERVER_ERROR, + message: 'test error.', + cause: utils.errorFrom({ + error: {}, + }) + }); + const appCheckError = appCheckErrorFromCryptoSignerError(cryptoError); + expect(appCheckError).to.be.an.instanceof(FirebaseAppCheckError); + expect(appCheckError).to.have.property('code', 'app-check/unknown-error'); + expect(appCheckError).to.have.property('message', + 'Error returned from server while siging a custom token: '+ + '{"status":500,"headers":{},"data":{"error":{}},"text":"{\\"error\\":{}}"}'); + }); + + it('should convert CryptoSignerError HttpError with no errorcode to FirebaseAppCheckError', () => { + const cryptoError = new CryptoSignerError({ + code: CryptoSignerErrorCode.SERVER_ERROR, + message: 'test error.', + cause: utils.errorFrom('server error.') + }); + const appCheckError = appCheckErrorFromCryptoSignerError(cryptoError); + expect(appCheckError).to.be.an.instanceof(FirebaseAppCheckError); + expect(appCheckError).to.have.property('code', 'app-check/internal-error'); + expect(appCheckError).to.have.property('message', + 'Error returned from server: null.'); + }); + }); + }); +}); diff --git a/test/unit/app-check/token-verifier.spec.ts b/test/unit/app-check/token-verifier.spec.ts new file mode 100644 index 0000000000..27d0be2fbf --- /dev/null +++ b/test/unit/app-check/token-verifier.spec.ts @@ -0,0 +1,245 @@ +/*! + * @license + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +import * as _ from 'lodash'; +import * as chai from 'chai'; +import * as sinon from 'sinon'; +import * as mocks from '../../resources/mocks'; +import * as nock from 'nock'; + +import { AppCheckTokenVerifier } from '../../../src/app-check/token-verifier'; +import { JwtError, JwtErrorCode, PublicKeySignatureVerifier } from '../../../src/utils/jwt'; + +const expect = chai.expect; + +const ONE_HOUR_IN_SECONDS = 60 * 60; + +describe('AppCheckTokenVerifier', () => { + + // Stubs used to simulate underlying api calls. + let stubs: sinon.SinonStub[] = []; + let tokenVerifier: AppCheckTokenVerifier; + let clock: sinon.SinonFakeTimers | undefined; + + before(() => { + tokenVerifier = new AppCheckTokenVerifier(mocks.app()); + }); + + after(() => { + nock.cleanAll(); + }); + + afterEach(() => { + _.forEach(stubs, (stub) => stub.restore()); + stubs = []; + + if (clock) { + clock.restore(); + clock = undefined; + } + }); + + describe('verifyJWT()', () => { + let mockedRequests: nock.Scope[] = []; + let stubs: sinon.SinonStub[] = []; + + afterEach(() => { + _.forEach(mockedRequests, (mockedRequest) => mockedRequest.done()); + mockedRequests = []; + + _.forEach(stubs, (stub) => stub.restore()); + stubs = []; + }); + + it('should throw given no App Check token', () => { + expect(() => { + (tokenVerifier as any).verifyToken(); + }).to.throw('App check token must be a non-null string'); + }); + + const invalidTokens = [null, NaN, 0, 1, true, false, [], {}, { a: 1 }, _.noop]; + invalidTokens.forEach((invalidToken) => { + it('should throw given a non-string App Check token: ' + JSON.stringify(invalidToken), () => { + expect(() => { + tokenVerifier.verifyToken(invalidToken as any); + }).to.throw('App check token must be a non-null string'); + }); + }); + + it('should throw given an empty string App Check token', () => { + return tokenVerifier.verifyToken('') + .should.eventually.be.rejectedWith('Decoding App Check token failed'); + }); + + it('should be rejected given an invalid App Check token', () => { + return tokenVerifier.verifyToken('invalid-token') + .should.eventually.be.rejectedWith('Decoding App Check token failed'); + }); + + it('should throw if the token verifier was initialized with no "project_id"', () => { + const tokenVerifierWithNoProjectId = new AppCheckTokenVerifier(mocks.mockCredentialApp()); + const expected = 'Must initialize app with a cert credential or set your Firebase project ID as ' + + 'the GOOGLE_CLOUD_PROJECT environment variable to verify an App Check token.'; + return tokenVerifierWithNoProjectId.verifyToken('app.check.token') + .should.eventually.be.rejectedWith(expected); + }); + + it('should be rejected given an App Check token with an incorrect algorithm', () => { + const mockAppCheckToken = mocks.generateAppCheckToken({ + algorithm: 'HS256', + }); + return tokenVerifier.verifyToken(mockAppCheckToken) + .should.eventually.be.rejectedWith('The provided App Check token has incorrect algorithm'); + }); + + const invalidAudiences = [ + 'incorrectAudience', [], [mocks.projectNumber, mocks.projectId], + ['projects/' + mocks.projectNumber, mocks.projectId] + ]; + invalidAudiences.forEach((invalidAudience) => { + it('should be rejected given an App Check token with an incorrect audience:' + + JSON.stringify(invalidAudience), () => { + const mockAppCheckToken = mocks.generateAppCheckToken({ + audience: invalidAudience, + }); + + return tokenVerifier.verifyToken(mockAppCheckToken) + .should.eventually.be.rejectedWith('The provided App Check token has incorrect "aud" (audience) claim'); + }); + }); + + it('should be rejected given an App Check token with an incorrect issuer', () => { + const mockAppCheckToken = mocks.generateAppCheckToken({ + issuer: 'incorrectIssuer', + }); + + return tokenVerifier.verifyToken(mockAppCheckToken) + .should.eventually.be.rejectedWith('The provided App Check token has incorrect "iss" (issuer) claim'); + }); + + it('should be rejected given an App Check token with an empty subject', () => { + const mockAppCheckToken = mocks.generateAppCheckToken({ + subject: '', + }); + + return tokenVerifier.verifyToken(mockAppCheckToken) + .should.eventually.be.rejectedWith('The provided App Check token has an empty string "sub" (subject) claim'); + }); + + it('should be rejected when the verifier throws no maching kid error', () => { + const verifierStub = sinon.stub(PublicKeySignatureVerifier.prototype, 'verify') + .rejects(new JwtError(JwtErrorCode.NO_MATCHING_KID, 'No matching key ID.')); + stubs.push(verifierStub); + + const mockAppCheckToken = mocks.generateAppCheckToken({ + header: { + kid: 'wrongkid', + }, + }); + + return tokenVerifier.verifyToken(mockAppCheckToken) + .should.eventually.be.rejectedWith('The provided App Check token has "kid" claim which does not ' + + 'correspond to a known public key'); + }); + + it('should be rejected when the verifier throws expired token error', () => { + const verifierStub = sinon.stub(PublicKeySignatureVerifier.prototype, 'verify') + .rejects(new JwtError(JwtErrorCode.TOKEN_EXPIRED, 'Expired token.')); + stubs.push(verifierStub); + + const mockAppCheckToken = mocks.generateAppCheckToken(); + + return tokenVerifier.verifyToken(mockAppCheckToken) + .should.eventually.be.rejectedWith('The provided App Check token has expired. ' + + 'Get a fresh App Check token from your client app and try again.') + .and.have.property('code', 'app-check/app-check-token-expired'); + }); + + it('should be rejected when the verifier throws invalid signature error.', () => { + const verifierStub = sinon.stub(PublicKeySignatureVerifier.prototype, 'verify') + .rejects(new JwtError(JwtErrorCode.INVALID_SIGNATURE, 'invalid signature.')); + stubs.push(verifierStub); + + const mockAppCheckToken = mocks.generateAppCheckToken(); + + return tokenVerifier.verifyToken(mockAppCheckToken) + .should.eventually.be.rejectedWith('The provided App Check token has invalid signature'); + }); + + it('should be rejected when the verifier throws key fetch error.', () => { + const verifierStub = sinon.stub(PublicKeySignatureVerifier.prototype, 'verify') + .rejects(new JwtError(JwtErrorCode.KEY_FETCH_ERROR, 'Error fetching Json Web Keys.')); + stubs.push(verifierStub); + + const mockAppCheckToken = mocks.generateAppCheckToken(); + + return tokenVerifier.verifyToken(mockAppCheckToken) + .should.eventually.be.rejectedWith('Error fetching Json Web Keys.'); + }); + + it('should be fulfilled when the kid is not present in the header (should try all the keys)', () => { + const verifierStub = sinon.stub(PublicKeySignatureVerifier.prototype, 'verify') + .resolves(); + stubs.push(verifierStub); + + clock = sinon.useFakeTimers(1000); + + const mockAppCheckToken = mocks.generateAppCheckToken({ + header: {}, + }); + + return tokenVerifier.verifyToken(mockAppCheckToken) + .should.eventually.be.fulfilled.and.deep.equal({ + one: 'uno', + two: 'dos', + iat: 1, + exp: ONE_HOUR_IN_SECONDS + 1, + aud: ['projects/' + mocks.projectNumber, 'projects/' + mocks.projectId], + iss: 'https://firebaseappcheck.googleapis.com/' + mocks.projectNumber, + sub: mocks.appId, + // eslint-disable-next-line @typescript-eslint/camelcase + app_id: mocks.appId, + }); + }); + + it('should be fulfilled with decoded claims given a valid App Check token', () => { + const verifierStub = sinon.stub(PublicKeySignatureVerifier.prototype, 'verify') + .resolves(); + stubs.push(verifierStub); + + clock = sinon.useFakeTimers(1000); + + const mockAppCheckToken = mocks.generateAppCheckToken(); + + return tokenVerifier.verifyToken(mockAppCheckToken) + .should.eventually.be.fulfilled.and.deep.equal({ + one: 'uno', + two: 'dos', + iat: 1, + exp: ONE_HOUR_IN_SECONDS + 1, + aud: ['projects/' + mocks.projectNumber, 'projects/' + mocks.projectId], + iss: 'https://firebaseappcheck.googleapis.com/' + mocks.projectNumber, + sub: mocks.appId, + // eslint-disable-next-line @typescript-eslint/camelcase + app_id: mocks.appId, + }); + }); + + }); +}); diff --git a/test/unit/auth/token-generator.spec.ts b/test/unit/auth/token-generator.spec.ts index c519c7a3ed..6a6d148b09 100644 --- a/test/unit/auth/token-generator.spec.ts +++ b/test/unit/auth/token-generator.spec.ts @@ -26,14 +26,13 @@ import * as chaiAsPromised from 'chai-as-promised'; import * as mocks from '../../resources/mocks'; import { - BLACKLISTED_CLAIMS, FirebaseTokenGenerator, ServiceAccountSigner, IAMSigner, EmulatedSigner + BLACKLISTED_CLAIMS, FirebaseTokenGenerator, EmulatedSigner, handleCryptoSignerError } from '../../../src/auth/token-generator'; +import { CryptoSignerError, CryptoSignerErrorCode, ServiceAccountSigner } from '../../../src/utils/crypto-signer'; import { ServiceAccountCredential } from '../../../src/credential/credential-internal'; -import { AuthorizedHttpClient, HttpClient } from '../../../src/utils/api-request'; -import { FirebaseApp } from '../../../src/firebase-app'; -import * as utils from '../utils'; import { FirebaseAuthError } from '../../../src/utils/error'; +import * as utils from '../utils'; chai.should(); chai.use(sinonChai); @@ -66,195 +65,6 @@ function verifyToken(token: string, publicKey: string): Promise { }); } -describe('CryptoSigner', () => { - describe('ServiceAccountSigner', () => { - it('should throw given no arguments', () => { - expect(() => { - const anyServiceAccountSigner: any = ServiceAccountSigner; - return new anyServiceAccountSigner(); - }).to.throw('Must provide a service account credential to initialize ServiceAccountSigner'); - }); - - it('should not throw given a valid certificate', () => { - expect(() => { - return new ServiceAccountSigner(new ServiceAccountCredential(mocks.certificateObject)); - }).not.to.throw(); - }); - - it('should sign using the private_key in the certificate', () => { - const payload = Buffer.from('test'); - const cert = new ServiceAccountCredential(mocks.certificateObject); - - // eslint-disable-next-line @typescript-eslint/no-var-requires - const crypto = require('crypto'); - const rsa = crypto.createSign('RSA-SHA256'); - rsa.update(payload); - const result = rsa.sign(cert.privateKey, 'base64'); - - const signer = new ServiceAccountSigner(cert); - return signer.sign(payload).then((signature) => { - expect(signature.toString('base64')).to.equal(result); - }); - }); - - it('should return the client_email from the certificate', () => { - const cert = new ServiceAccountCredential(mocks.certificateObject); - const signer = new ServiceAccountSigner(cert); - return signer.getAccountId().should.eventually.equal(cert.clientEmail); - }); - }); - - describe('IAMSigner', () => { - let mockApp: FirebaseApp; - let getTokenStub: sinon.SinonStub; - const mockAccessToken: string = utils.generateRandomAccessToken(); - - beforeEach(() => { - mockApp = mocks.app(); - getTokenStub = utils.stubGetAccessToken(mockAccessToken, mockApp); - return mockApp.INTERNAL.getToken(); - }); - - afterEach(() => { - getTokenStub.restore(); - return mockApp.delete(); - }); - - it('should throw given no arguments', () => { - expect(() => { - const anyIAMSigner: any = IAMSigner; - return new anyIAMSigner(); - }).to.throw('Must provide a HTTP client to initialize IAMSigner'); - }); - - describe('explicit service account ID', () => { - const response = { signedBlob: Buffer.from('testsignature').toString('base64') }; - const input = Buffer.from('input'); - const signRequest = { - method: 'POST', - url: 'https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/test-service-account:signBlob', - headers: { Authorization: `Bearer ${mockAccessToken}` }, - data: { payload: input.toString('base64') }, - }; - let stub: sinon.SinonStub; - - afterEach(() => { - stub.restore(); - }); - - it('should sign using the IAM service', () => { - const expectedResult = utils.responseFrom(response); - stub = sinon.stub(HttpClient.prototype, 'send').resolves(expectedResult); - const requestHandler = new AuthorizedHttpClient(mockApp); - const signer = new IAMSigner(requestHandler, 'test-service-account'); - return signer.sign(input).then((signature) => { - expect(signature.toString('base64')).to.equal(response.signedBlob); - expect(stub).to.have.been.calledOnce.and.calledWith(signRequest); - }); - }); - - it('should fail if the IAM service responds with an error', () => { - const expectedResult = utils.errorFrom({ - error: { - status: 'PROJECT_NOT_FOUND', - message: 'test reason', - }, - }); - stub = sinon.stub(HttpClient.prototype, 'send').rejects(expectedResult); - const requestHandler = new AuthorizedHttpClient(mockApp); - const signer = new IAMSigner(requestHandler, 'test-service-account'); - return signer.sign(input).catch((err) => { - const message = 'test reason; Please refer to ' + - 'https://firebase.google.com/docs/auth/admin/create-custom-tokens for more details on ' + - 'how to use and troubleshoot this feature.'; - expect(err.message).to.equal(message); - expect(stub).to.have.been.calledOnce.and.calledWith(signRequest); - }); - }); - - it('should return the explicitly specified service account', () => { - const signer = new IAMSigner(new AuthorizedHttpClient(mockApp), 'test-service-account'); - return signer.getAccountId().should.eventually.equal('test-service-account'); - }); - }); - - describe('auto discovered service account', () => { - const input = Buffer.from('input'); - const response = { signedBlob: Buffer.from('testsignature').toString('base64') }; - const metadataRequest = { - method: 'GET', - url: 'http://metadata/computeMetadata/v1/instance/service-accounts/default/email', - headers: { 'Metadata-Flavor': 'Google' }, - }; - const signRequest = { - method: 'POST', - url: 'https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/discovered-service-account:signBlob', - headers: { Authorization: `Bearer ${mockAccessToken}` }, - data: { payload: input.toString('base64') }, - }; - let stub: sinon.SinonStub; - - afterEach(() => { - stub.restore(); - }); - - it('should sign using the IAM service', () => { - stub = sinon.stub(HttpClient.prototype, 'send'); - stub.onCall(0).resolves(utils.responseFrom('discovered-service-account')); - stub.onCall(1).resolves(utils.responseFrom(response)); - const requestHandler = new AuthorizedHttpClient(mockApp); - const signer = new IAMSigner(requestHandler); - return signer.sign(input).then((signature) => { - expect(signature.toString('base64')).to.equal(response.signedBlob); - expect(stub).to.have.been.calledTwice; - expect(stub.getCall(0).args[0]).to.deep.equal(metadataRequest); - expect(stub.getCall(1).args[0]).to.deep.equal(signRequest); - }); - }); - - it('should fail if the IAM service responds with an error', () => { - const expectedResult = { - error: { - status: 'PROJECT_NOT_FOUND', - message: 'test reason', - }, - }; - stub = sinon.stub(HttpClient.prototype, 'send'); - stub.onCall(0).resolves(utils.responseFrom('discovered-service-account')); - stub.onCall(1).rejects(utils.errorFrom(expectedResult)); - const requestHandler = new AuthorizedHttpClient(mockApp); - const signer = new IAMSigner(requestHandler); - return signer.sign(input).catch((err) => { - const message = 'test reason; Please refer to ' + - 'https://firebase.google.com/docs/auth/admin/create-custom-tokens for more details on ' + - 'how to use and troubleshoot this feature.'; - expect(err.message).to.equal(message); - expect(stub).to.have.been.calledTwice; - expect(stub.getCall(0).args[0]).to.deep.equal(metadataRequest); - expect(stub.getCall(1).args[0]).to.deep.equal(signRequest); - }); - }); - - it('should return the discovered service account', () => { - stub = sinon.stub(HttpClient.prototype, 'send'); - stub.onCall(0).resolves(utils.responseFrom('discovered-service-account')); - const signer = new IAMSigner(new AuthorizedHttpClient(mockApp)); - return signer.getAccountId().should.eventually.equal('discovered-service-account'); - }); - - it('should return the expected error when failed to contact the Metadata server', () => { - stub = sinon.stub(HttpClient.prototype, 'send'); - stub.onCall(0).rejects(utils.errorFrom('test error')); - const signer = new IAMSigner(new AuthorizedHttpClient(mockApp)); - const expected = 'Failed to determine service account. Make sure to initialize the SDK with ' + - 'a service account credential. Alternatively specify a service account with ' + - 'iam.serviceAccounts.signBlob permission.'; - return signer.getAccountId().should.eventually.be.rejectedWith(expected); - }); - }); - }); -}); - describe('FirebaseTokenGenerator', () => { const tenantId = 'tenantId1'; const cert = new ServiceAccountCredential(mocks.certificateObject); @@ -384,7 +194,7 @@ describe('FirebaseTokenGenerator', () => { BLACKLISTED_CLAIMS.forEach((blacklistedClaim) => { it('should throw given a developer claims object with a blacklisted claim: ' + blacklistedClaim, () => { - const blacklistedDeveloperClaims: {[key: string]: any} = _.clone(mocks.developerClaims); + const blacklistedDeveloperClaims: { [key: string]: any } = _.clone(mocks.developerClaims); blacklistedDeveloperClaims[blacklistedClaim] = true; expect(() => { tokenGenerator.createCustomToken(mocks.uid, blacklistedDeveloperClaims); @@ -415,7 +225,7 @@ describe('FirebaseTokenGenerator', () => { return tokenGenerator.createCustomToken(mocks.uid) .then((token) => { const decoded = jwt.decode(token); - const expected: {[key: string]: any} = { + const expected: { [key: string]: any } = { uid: mocks.uid, iat: 1, exp: ONE_HOUR_IN_SECONDS + 1, @@ -440,7 +250,7 @@ describe('FirebaseTokenGenerator', () => { .then((token) => { const decoded = jwt.decode(token); - const expected: {[key: string]: any} = { + const expected: { [key: string]: any } = { uid: mocks.uid, iat: 1, exp: ONE_HOUR_IN_SECONDS + 1, @@ -526,4 +336,47 @@ describe('FirebaseTokenGenerator', () => { }); }); }); + + describe('handleCryptoSignerError', () => { + it('should convert CryptoSignerError to FirebaseAuthError', () => { + const cryptoError = new CryptoSignerError({ + code: CryptoSignerErrorCode.INVALID_ARGUMENT, + message: 'test error.', + }); + const authError = handleCryptoSignerError(cryptoError); + expect(authError).to.be.an.instanceof(FirebaseAuthError); + expect(authError).to.have.property('code', 'auth/argument-error'); + expect(authError).to.have.property('message', 'test error.'); + }); + + it('should convert CryptoSignerError HttpError to FirebaseAuthError', () => { + const cryptoError = new CryptoSignerError({ + code: CryptoSignerErrorCode.SERVER_ERROR, + message: 'test error.', + cause: utils.errorFrom({ + error: { + message: 'server error.', + }, + }) + }); + const authError = handleCryptoSignerError(cryptoError); + expect(authError).to.be.an.instanceof(FirebaseAuthError); + expect(authError).to.have.property('code', 'auth/internal-error'); + expect(authError).to.have.property('message', 'server error.; Please refer to https://firebase.google.com/docs/auth/admin/create-custom-tokens for more details on how to use and troubleshoot this feature. Raw server response: "{"error":{"message":"server error."}}"'); + }); + + it('should convert CryptoSignerError HttpError with no errorcode to FirebaseAuthError', () => { + const cryptoError = new CryptoSignerError({ + code: CryptoSignerErrorCode.SERVER_ERROR, + message: 'test error.', + cause: utils.errorFrom('server error.') + }); + const authError = handleCryptoSignerError(cryptoError); + expect(authError).to.be.an.instanceof(FirebaseAuthError); + expect(authError).to.have.property('code', 'auth/internal-error'); + expect(authError).to.have.property('message', + 'Error returned from server: null. Additionally, an internal error occurred ' + + 'while attempting to extract the errorcode from the error.'); + }); + }); }); diff --git a/test/unit/auth/token-verifier.spec.ts b/test/unit/auth/token-verifier.spec.ts index 1f7e3546f8..a8afe7167b 100644 --- a/test/unit/auth/token-verifier.spec.ts +++ b/test/unit/auth/token-verifier.spec.ts @@ -27,7 +27,8 @@ import { Agent } from 'http'; import LegacyFirebaseTokenGenerator = require('firebase-token-generator'); import * as mocks from '../../resources/mocks'; -import { FirebaseTokenGenerator, ServiceAccountSigner } from '../../../src/auth/token-generator'; +import { FirebaseTokenGenerator } from '../../../src/auth/token-generator'; +import { ServiceAccountSigner } from '../../../src/utils/crypto-signer'; import * as verifier from '../../../src/auth/token-verifier'; import { ServiceAccountCredential } from '../../../src/credential/credential-internal'; diff --git a/test/unit/firebase-app.spec.ts b/test/unit/firebase-app.spec.ts index 6edf73b7ef..0989e718d0 100644 --- a/test/unit/firebase-app.spec.ts +++ b/test/unit/firebase-app.spec.ts @@ -41,6 +41,7 @@ import { instanceId } from '../../src/instance-id/index'; import { projectManagement } from '../../src/project-management/index'; import { securityRules } from '../../src/security-rules/index'; import { remoteConfig } from '../../src/remote-config/index'; +import { appCheck } from '../../src/app-check/index'; import { FirebaseAppError, AppErrorCodes } from '../../src/utils/error'; import Auth = auth.Auth; @@ -53,6 +54,7 @@ import InstanceId = instanceId.InstanceId; import ProjectManagement = projectManagement.ProjectManagement; import SecurityRules = securityRules.SecurityRules; import RemoteConfig = remoteConfig.RemoteConfig; +import AppCheck = appCheck.AppCheck; chai.should(); chai.use(sinonChai); @@ -669,6 +671,32 @@ describe('FirebaseApp', () => { }); }); + describe('appCheck()', () => { + it('should throw if the app has already been deleted', () => { + const app = firebaseNamespace.initializeApp(mocks.appOptions, mocks.appName); + + return app.delete().then(() => { + expect(() => { + return app.appCheck(); + }).to.throw(`Firebase app named "${mocks.appName}" has already been deleted.`); + }); + }); + + it('should return the AppCheck client', () => { + const app = firebaseNamespace.initializeApp(mocks.appOptions, mocks.appName); + + const appCheck: AppCheck = app.appCheck(); + expect(appCheck).to.not.be.null; + }); + + it('should return a cached version of AppCheck on subsequent calls', () => { + const app = firebaseNamespace.initializeApp(mocks.appOptions, mocks.appName); + const service1: AppCheck = app.appCheck(); + const service2: AppCheck = app.appCheck(); + expect(service1).to.equal(service2); + }); + }); + describe('INTERNAL.getToken()', () => { it('throws a custom credential implementation which returns invalid access tokens', () => { diff --git a/test/unit/firebase-namespace.spec.ts b/test/unit/firebase-namespace.spec.ts index 07e5a8ba78..e2efb84f4e 100644 --- a/test/unit/firebase-namespace.spec.ts +++ b/test/unit/firebase-namespace.spec.ts @@ -56,7 +56,9 @@ import { instanceId } from '../../src/instance-id/index'; import { projectManagement } from '../../src/project-management/index'; import { securityRules } from '../../src/security-rules/index'; import { remoteConfig } from '../../src/remote-config/index'; +import { appCheck } from '../../src/app-check/index'; +import { AppCheck as AppCheckImpl } from '../../src/app-check/app-check'; import { Auth as AuthImpl } from '../../src/auth/auth'; import { InstanceId as InstanceIdImpl } from '../../src/instance-id/instance-id'; import { MachineLearning as MachineLearningImpl } from '../../src/machine-learning/machine-learning'; @@ -67,6 +69,7 @@ import { SecurityRules as SecurityRulesImpl } from '../../src/security-rules/sec import { Storage as StorageImpl } from '../../src/storage/storage'; import App = app.App; +import AppCheck = appCheck.AppCheck; import Auth = auth.Auth; import Database = database.Database; import Firestore = firestore.Firestore; @@ -759,4 +762,42 @@ describe('FirebaseNamespace', () => { expect(service1).to.equal(service2); }); }); + + describe('#appCheck()', () => { + it('should throw when called before initializing an app', () => { + expect(() => { + firebaseNamespace.appCheck(); + }).to.throw(DEFAULT_APP_NOT_FOUND); + }); + + it('should throw when default app is not initialized', () => { + firebaseNamespace.initializeApp(mocks.appOptions, 'testApp'); + expect(() => { + firebaseNamespace.appCheck(); + }).to.throw(DEFAULT_APP_NOT_FOUND); + }); + + it('should return a valid namespace when the default app is initialized', () => { + const app: App = firebaseNamespace.initializeApp(mocks.appOptions); + const fac: AppCheck = firebaseNamespace.appCheck(); + expect(fac.app).to.be.deep.equal(app); + }); + + it('should return a valid namespace when the named app is initialized', () => { + const app: App = firebaseNamespace.initializeApp(mocks.appOptions, 'testApp'); + const fac: AppCheck = firebaseNamespace.appCheck(app); + expect(fac.app).to.be.deep.equal(app); + }); + + it('should return a reference to AppCheck type', () => { + expect(firebaseNamespace.appCheck.AppCheck).to.be.deep.equal(AppCheckImpl); + }); + + it('should return a cached version of AppCheck on subsequent calls', () => { + firebaseNamespace.initializeApp(mocks.appOptions); + const service1: AppCheck = firebaseNamespace.appCheck(); + const service2: AppCheck = firebaseNamespace.appCheck(); + expect(service1).to.equal(service2); + }); + }); }); diff --git a/test/unit/firebase.spec.ts b/test/unit/firebase.spec.ts index 3048121529..da1256a2c2 100644 --- a/test/unit/firebase.spec.ts +++ b/test/unit/firebase.spec.ts @@ -235,6 +235,21 @@ describe('Firebase', () => { }); }); + describe('#appCheck', () => { + it('should throw if the app has not been initialized', () => { + expect(() => { + return firebaseAdmin.appCheck(); + }).to.throw('The default Firebase app does not exist.'); + }); + + it('should return the appCheck service', () => { + firebaseAdmin.initializeApp(mocks.appOptions); + expect(() => { + return firebaseAdmin.appCheck(); + }).not.to.throw(); + }); + }); + describe('#storage', () => { it('should throw if the app has not be initialized', () => { expect(() => { diff --git a/test/unit/index.spec.ts b/test/unit/index.spec.ts index e8c5a6d17d..48c87e24e4 100644 --- a/test/unit/index.spec.ts +++ b/test/unit/index.spec.ts @@ -26,6 +26,7 @@ import './utils/error.spec'; import './utils/validator.spec'; import './utils/api-request.spec'; import './utils/jwt.spec'; +import './utils/crypto-signer.spec'; // Auth import './auth/auth.spec'; @@ -76,3 +77,9 @@ import './security-rules/security-rules-api-client.spec'; // RemoteConfig import './remote-config/remote-config.spec'; import './remote-config/remote-config-api-client.spec'; + +// AppCheck +import './app-check/app-check.spec'; +import './app-check/app-check-api-client-internal.spec'; +import './app-check/token-generator.spec'; +import './app-check/token-verifier.spec.ts'; diff --git a/test/unit/utils/crypto-signer.spec.ts b/test/unit/utils/crypto-signer.spec.ts new file mode 100644 index 0000000000..9a59fd10eb --- /dev/null +++ b/test/unit/utils/crypto-signer.spec.ts @@ -0,0 +1,224 @@ +/*! + * @license + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +import * as chai from 'chai'; +import * as sinon from 'sinon'; +import * as sinonChai from 'sinon-chai'; +import * as chaiAsPromised from 'chai-as-promised'; + +import * as mocks from '../../resources/mocks'; +import { ServiceAccountSigner, IAMSigner, CryptoSignerError } from '../../../src/utils/crypto-signer'; + +import { ServiceAccountCredential } from '../../../src/credential/credential-internal'; +import { AuthorizedHttpClient, HttpClient } from '../../../src/utils/api-request'; +import { FirebaseApp } from '../../../src/firebase-app'; +import * as utils from '../utils'; + +chai.should(); +chai.use(sinonChai); +chai.use(chaiAsPromised); + +const expect = chai.expect; + +describe('CryptoSigner', () => { + describe('ServiceAccountSigner', () => { + it('should throw given no arguments', () => { + expect(() => { + const anyServiceAccountSigner: any = ServiceAccountSigner; + return new anyServiceAccountSigner(); + }).to.throw('Must provide a service account credential to initialize ServiceAccountSigner'); + }); + + it('should not throw given a valid certificate', () => { + expect(() => { + return new ServiceAccountSigner(new ServiceAccountCredential(mocks.certificateObject)); + }).not.to.throw(); + }); + + it('should sign using the private_key in the certificate', () => { + const payload = Buffer.from('test'); + const cert = new ServiceAccountCredential(mocks.certificateObject); + + // eslint-disable-next-line @typescript-eslint/no-var-requires + const crypto = require('crypto'); + const rsa = crypto.createSign('RSA-SHA256'); + rsa.update(payload); + const result = rsa.sign(cert.privateKey, 'base64'); + + const signer = new ServiceAccountSigner(cert); + return signer.sign(payload).then((signature) => { + expect(signature.toString('base64')).to.equal(result); + }); + }); + + it('should return the client_email from the certificate', () => { + const cert = new ServiceAccountCredential(mocks.certificateObject); + const signer = new ServiceAccountSigner(cert); + return signer.getAccountId().should.eventually.equal(cert.clientEmail); + }); + }); + + describe('IAMSigner', () => { + let mockApp: FirebaseApp; + let getTokenStub: sinon.SinonStub; + const mockAccessToken: string = utils.generateRandomAccessToken(); + + beforeEach(() => { + mockApp = mocks.app(); + getTokenStub = utils.stubGetAccessToken(mockAccessToken, mockApp); + return mockApp.INTERNAL.getToken(); + }); + + afterEach(() => { + getTokenStub.restore(); + return mockApp.delete(); + }); + + it('should throw given no arguments', () => { + expect(() => { + const anyIAMSigner: any = IAMSigner; + return new anyIAMSigner(); + }).to.throw('Must provide a HTTP client to initialize IAMSigner'); + }); + + describe('explicit service account ID', () => { + const response = { signedBlob: Buffer.from('testsignature').toString('base64') }; + const input = Buffer.from('input'); + const signRequest = { + method: 'POST', + url: 'https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/test-service-account:signBlob', + headers: { Authorization: `Bearer ${mockAccessToken}` }, + data: { payload: input.toString('base64') }, + }; + let stub: sinon.SinonStub; + + afterEach(() => { + stub.restore(); + }); + + it('should sign using the IAM service', () => { + const expectedResult = utils.responseFrom(response); + stub = sinon.stub(HttpClient.prototype, 'send').resolves(expectedResult); + const requestHandler = new AuthorizedHttpClient(mockApp); + const signer = new IAMSigner(requestHandler, 'test-service-account'); + return signer.sign(input).then((signature) => { + expect(signature.toString('base64')).to.equal(response.signedBlob); + expect(stub).to.have.been.calledOnce.and.calledWith(signRequest); + }); + }); + + it('should fail if the IAM service responds with an error', () => { + const expectedResult = utils.errorFrom({ + error: { + status: 'PROJECT_NOT_FOUND', + message: 'test reason', + }, + }); + stub = sinon.stub(HttpClient.prototype, 'send').rejects(expectedResult); + const requestHandler = new AuthorizedHttpClient(mockApp); + const signer = new IAMSigner(requestHandler, 'test-service-account'); + return signer.sign(input).catch((err) => { + expect(err).to.be.instanceOf(CryptoSignerError); + expect(err.message).to.equal('Server responded with status 500.'); + expect(err.cause).to.deep.equal(expectedResult); + expect(stub).to.have.been.calledOnce.and.calledWith(signRequest); + }); + }); + + it('should return the explicitly specified service account', () => { + const signer = new IAMSigner(new AuthorizedHttpClient(mockApp), 'test-service-account'); + return signer.getAccountId().should.eventually.equal('test-service-account'); + }); + }); + + describe('auto discovered service account', () => { + const input = Buffer.from('input'); + const response = { signedBlob: Buffer.from('testsignature').toString('base64') }; + const metadataRequest = { + method: 'GET', + url: 'http://metadata/computeMetadata/v1/instance/service-accounts/default/email', + headers: { 'Metadata-Flavor': 'Google' }, + }; + const signRequest = { + method: 'POST', + url: 'https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/discovered-service-account:signBlob', + headers: { Authorization: `Bearer ${mockAccessToken}` }, + data: { payload: input.toString('base64') }, + }; + let stub: sinon.SinonStub; + + afterEach(() => { + stub.restore(); + }); + + it('should sign using the IAM service', () => { + stub = sinon.stub(HttpClient.prototype, 'send'); + stub.onCall(0).resolves(utils.responseFrom('discovered-service-account')); + stub.onCall(1).resolves(utils.responseFrom(response)); + const requestHandler = new AuthorizedHttpClient(mockApp); + const signer = new IAMSigner(requestHandler); + return signer.sign(input).then((signature) => { + expect(signature.toString('base64')).to.equal(response.signedBlob); + expect(stub).to.have.been.calledTwice; + expect(stub.getCall(0).args[0]).to.deep.equal(metadataRequest); + expect(stub.getCall(1).args[0]).to.deep.equal(signRequest); + }); + }); + + it('should fail if the IAM service responds with an error', () => { + const expectedResult = utils.errorFrom({ + error: { + status: 'PROJECT_NOT_FOUND', + message: 'test reason', + }, + }); + stub = sinon.stub(HttpClient.prototype, 'send'); + stub.onCall(0).resolves(utils.responseFrom('discovered-service-account')); + stub.onCall(1).rejects(expectedResult); + const requestHandler = new AuthorizedHttpClient(mockApp); + const signer = new IAMSigner(requestHandler); + return signer.sign(input).catch((err) => { + expect(err).to.be.instanceOf(CryptoSignerError); + expect(err.message).to.equal('Server responded with status 500.'); + expect(err.cause).to.deep.equal(expectedResult); + expect(stub).to.have.been.calledTwice; + expect(stub.getCall(0).args[0]).to.deep.equal(metadataRequest); + expect(stub.getCall(1).args[0]).to.deep.equal(signRequest); + }); + }); + + it('should return the discovered service account', () => { + stub = sinon.stub(HttpClient.prototype, 'send'); + stub.onCall(0).resolves(utils.responseFrom('discovered-service-account')); + const signer = new IAMSigner(new AuthorizedHttpClient(mockApp)); + return signer.getAccountId().should.eventually.equal('discovered-service-account'); + }); + + it('should return the expected error when failed to contact the Metadata server', () => { + stub = sinon.stub(HttpClient.prototype, 'send'); + stub.onCall(0).rejects(utils.errorFrom('test error')); + const signer = new IAMSigner(new AuthorizedHttpClient(mockApp)); + const expected = 'Failed to determine service account. Make sure to initialize the SDK with ' + + 'a service account credential. Alternatively specify a service account with ' + + 'iam.serviceAccounts.signBlob permission.'; + return signer.getAccountId().should.eventually.be.rejectedWith(expected); + }); + }); + }); +}); diff --git a/test/unit/utils/jwt.spec.ts b/test/unit/utils/jwt.spec.ts index 525608feef..775bdd63b9 100644 --- a/test/unit/utils/jwt.spec.ts +++ b/test/unit/utils/jwt.spec.ts @@ -23,16 +23,19 @@ import * as _ from 'lodash'; import * as chai from 'chai'; import * as nock from 'nock'; import * as sinon from 'sinon'; -//import * as sinonChai from 'sinon-chai'; -//import * as chaiAsPromised from 'chai-as-promised'; import * as mocks from '../../resources/mocks'; -import * as jwtUtil from '../../../src/utils/jwt'; +import { + ALGORITHM_RS256, DecodedToken, decodeJwt, EmulatorSignatureVerifier, JwksFetcher, + JwtErrorCode, PublicKeySignatureVerifier, UrlKeyFetcher, verifyJwtSignature +} from '../../../src/utils/jwt'; const expect = chai.expect; const ONE_HOUR_IN_SECONDS = 60 * 60; +const ONE_DAY_IN_SECONDS = 86400; const publicCertPath = '/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com'; +const jwksPath = '/v1alpha/jwks'; /** * Returns a mocked out success response from the URL containing the public keys for the Google certs. @@ -80,6 +83,43 @@ function mockFailedFetchPublicKeys(): nock.Scope { .replyWithError('message'); } +/** + * Returns a mocked out success JWKS response. + * + * @return {Object} A nock response object. + */ +function mockFetchJsonWebKeys(path: string = jwksPath): nock.Scope { + return nock('https://firebaseappcheck.googleapis.com') + .get(path) + .reply(200, mocks.jwksResponse); +} + +/** + * Returns a mocked out error response for JWKS. + * The status code is 200 but the response itself will contain an 'error' key. + * + * @return {Object} A nock response object. + */ +function mockFetchJsonWebKeysWithErrorResponse(): nock.Scope { + return nock('https://firebaseappcheck.googleapis.com') + .get(jwksPath) + .reply(200, { + error: 'message', + error_description: 'description', // eslint-disable-line @typescript-eslint/camelcase + }); +} + +/** + * Returns a mocked out failed JSON Web Keys response. + * The status code is non-200 and the response itself will fail. + * + * @return {Object} A nock response object. + */ +function mockFailedFetchJsonWebKeys(): nock.Scope { + return nock('https://firebaseappcheck.googleapis.com') + .get(jwksPath) + .replyWithError('message'); +} const TOKEN_PAYLOAD = { one: 'uno', @@ -91,7 +131,7 @@ const TOKEN_PAYLOAD = { sub: mocks.uid, }; -const DECODED_SIGNED_TOKEN: jwtUtil.DecodedToken = { +const DECODED_SIGNED_TOKEN: DecodedToken = { header: { alg: 'RS256', kid: 'aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd', @@ -100,7 +140,7 @@ const DECODED_SIGNED_TOKEN: jwtUtil.DecodedToken = { payload: TOKEN_PAYLOAD }; -const DECODED_UNSIGNED_TOKEN: jwtUtil.DecodedToken = { +const DECODED_UNSIGNED_TOKEN: DecodedToken = { header: { alg: 'none', typ: 'JWT', @@ -122,25 +162,25 @@ describe('decodeJwt', () => { }); it('should reject given no token', () => { - return (jwtUtil.decodeJwt as any)() + return (decodeJwt as any)() .should.eventually.be.rejectedWith('The provided token must be a string.'); }); const invalidIdTokens = [null, NaN, 0, 1, true, false, [], {}, { a: 1 }, _.noop]; invalidIdTokens.forEach((invalidIdToken) => { it('should reject given a non-string token: ' + JSON.stringify(invalidIdToken), () => { - return jwtUtil.decodeJwt(invalidIdToken as any) + return decodeJwt(invalidIdToken as any) .should.eventually.be.rejectedWith('The provided token must be a string.'); }); }); it('should reject given an empty string token', () => { - return jwtUtil.decodeJwt('') + return decodeJwt('') .should.eventually.be.rejectedWith('Decoding token failed.'); }); it('should reject given an invalid token', () => { - return jwtUtil.decodeJwt('invalid-token') + return decodeJwt('invalid-token') .should.eventually.be.rejectedWith('Decoding token failed.'); }); @@ -149,7 +189,7 @@ describe('decodeJwt', () => { const mockIdToken = mocks.generateIdToken(); - return jwtUtil.decodeJwt(mockIdToken) + return decodeJwt(mockIdToken) .should.eventually.be.fulfilled.and.deep.equal(DECODED_SIGNED_TOKEN); }); @@ -161,7 +201,7 @@ describe('decodeJwt', () => { header: {} }); - return jwtUtil.decodeJwt(mockIdToken) + return decodeJwt(mockIdToken) .should.eventually.be.fulfilled.and.deep.equal(DECODED_UNSIGNED_TOKEN); }); }); @@ -178,28 +218,28 @@ describe('verifyJwtSignature', () => { }); it('should throw given no token', () => { - return (jwtUtil.verifyJwtSignature as any)() + return (verifyJwtSignature as any)() .should.eventually.be.rejectedWith('The provided token must be a string.'); }); const invalidIdTokens = [null, NaN, 0, 1, true, false, [], {}, { a: 1 }, _.noop]; invalidIdTokens.forEach((invalidIdToken) => { it('should reject given a non-string token: ' + JSON.stringify(invalidIdToken), () => { - return jwtUtil.verifyJwtSignature(invalidIdToken as any, mocks.keyPairs[0].public) + return verifyJwtSignature(invalidIdToken as any, mocks.keyPairs[0].public) .should.eventually.be.rejectedWith('The provided token must be a string.'); }); }); it('should reject given an empty string token', () => { - return jwtUtil.verifyJwtSignature('', mocks.keyPairs[0].public) + return verifyJwtSignature('', mocks.keyPairs[0].public) .should.eventually.be.rejectedWith('jwt must be provided'); }); it('should be fulfilled given a valid signed token and public key', () => { const mockIdToken = mocks.generateIdToken(); - return jwtUtil.verifyJwtSignature(mockIdToken, mocks.keyPairs[0].public, - { algorithms: [jwtUtil.ALGORITHM_RS256] }) + return verifyJwtSignature(mockIdToken, mocks.keyPairs[0].public, + { algorithms: [ALGORITHM_RS256] }) .should.eventually.be.fulfilled; }); @@ -209,7 +249,7 @@ describe('verifyJwtSignature', () => { header: {} }); - return jwtUtil.verifyJwtSignature(mockIdToken, '') + return verifyJwtSignature(mockIdToken, '') .should.eventually.be.fulfilled; }); @@ -217,18 +257,18 @@ describe('verifyJwtSignature', () => { const mockIdToken = mocks.generateIdToken(); const getKeyCallback = (_: any, callback: any): void => callback(null, mocks.keyPairs[0].public); - return jwtUtil.verifyJwtSignature(mockIdToken, getKeyCallback, - { algorithms: [jwtUtil.ALGORITHM_RS256] }) + return verifyJwtSignature(mockIdToken, getKeyCallback, + { algorithms: [ALGORITHM_RS256] }) .should.eventually.be.fulfilled; }); it('should be rejected when the given algorithm does not match the token', () => { const mockIdToken = mocks.generateIdToken(); - return jwtUtil.verifyJwtSignature(mockIdToken, mocks.keyPairs[0].public, + return verifyJwtSignature(mockIdToken, mocks.keyPairs[0].public, { algorithms: ['RS384'] }) .should.eventually.be.rejectedWith('invalid algorithm') - .with.property('code', jwtUtil.JwtErrorCode.INVALID_SIGNATURE); + .with.property('code', JwtErrorCode.INVALID_SIGNATURE); }); it('should be rejected given an expired token', () => { @@ -237,18 +277,18 @@ describe('verifyJwtSignature', () => { clock.tick((ONE_HOUR_IN_SECONDS * 1000) - 1); // token should still be valid - return jwtUtil.verifyJwtSignature(mockIdToken, mocks.keyPairs[0].public, - { algorithms: [jwtUtil.ALGORITHM_RS256] }) + return verifyJwtSignature(mockIdToken, mocks.keyPairs[0].public, + { algorithms: [ALGORITHM_RS256] }) .then(() => { clock!.tick(1); // token should now be invalid - return jwtUtil.verifyJwtSignature(mockIdToken, mocks.keyPairs[0].public, - { algorithms: [jwtUtil.ALGORITHM_RS256] }) + return verifyJwtSignature(mockIdToken, mocks.keyPairs[0].public, + { algorithms: [ALGORITHM_RS256] }) .should.eventually.be.rejectedWith( 'The provided token has expired. Get a fresh token from your client app and try again.' ) - .with.property('code', jwtUtil.JwtErrorCode.TOKEN_EXPIRED); + .with.property('code', JwtErrorCode.TOKEN_EXPIRED); }); }); @@ -257,10 +297,10 @@ describe('verifyJwtSignature', () => { const getKeyCallback = (_: any, callback: any): void => callback(new Error('key fetch failed.')); - return jwtUtil.verifyJwtSignature(mockIdToken, getKeyCallback, - { algorithms: [jwtUtil.ALGORITHM_RS256] }) + return verifyJwtSignature(mockIdToken, getKeyCallback, + { algorithms: [ALGORITHM_RS256] }) .should.eventually.be.rejectedWith('key fetch failed.') - .with.property('code', jwtUtil.JwtErrorCode.KEY_FETCH_ERROR); + .with.property('code', JwtErrorCode.KEY_FETCH_ERROR); }); it('should be rejected with correct no matching key id found error.', () => { @@ -268,43 +308,49 @@ describe('verifyJwtSignature', () => { const getKeyCallback = (_: any, callback: any): void => callback(new Error('no-matching-kid-error')); - return jwtUtil.verifyJwtSignature(mockIdToken, getKeyCallback, - { algorithms: [jwtUtil.ALGORITHM_RS256] }) + return verifyJwtSignature(mockIdToken, getKeyCallback, + { algorithms: [ALGORITHM_RS256] }) .should.eventually.be.rejectedWith('no-matching-kid-error') - .with.property('code', jwtUtil.JwtErrorCode.NO_MATCHING_KID); + .with.property('code', JwtErrorCode.NO_MATCHING_KID); }); it('should be rejected given a public key that does not match the token.', () => { const mockIdToken = mocks.generateIdToken(); - return jwtUtil.verifyJwtSignature(mockIdToken, mocks.keyPairs[1].public, - { algorithms: [jwtUtil.ALGORITHM_RS256] }) + return verifyJwtSignature(mockIdToken, mocks.keyPairs[1].public, + { algorithms: [ALGORITHM_RS256] }) .should.eventually.be.rejectedWith('invalid signature') - .with.property('code', jwtUtil.JwtErrorCode.INVALID_SIGNATURE); + .with.property('code', JwtErrorCode.INVALID_SIGNATURE); }); it('should be rejected given an invalid JWT.', () => { - return jwtUtil.verifyJwtSignature('invalid-token', mocks.keyPairs[0].public) + return verifyJwtSignature('invalid-token', mocks.keyPairs[0].public) .should.eventually.be.rejectedWith('jwt malformed') - .with.property('code', jwtUtil.JwtErrorCode.INVALID_SIGNATURE); + .with.property('code', JwtErrorCode.INVALID_SIGNATURE); }); }); describe('PublicKeySignatureVerifier', () => { let stubs: sinon.SinonStub[] = []; - const verifier = new jwtUtil.PublicKeySignatureVerifier( - new jwtUtil.UrlKeyFetcher('https://www.example.com/publicKeys')); + let clock: sinon.SinonFakeTimers | undefined; + const verifier = new PublicKeySignatureVerifier( + new UrlKeyFetcher('https://www.example.com/publicKeys')); afterEach(() => { _.forEach(stubs, (stub) => stub.restore()); stubs = []; + + if (clock) { + clock.restore(); + clock = undefined; + } }); describe('Constructor', () => { it('should not throw when valid key fetcher is provided', () => { expect(() => { - new jwtUtil.PublicKeySignatureVerifier( - new jwtUtil.UrlKeyFetcher('https://www.example.com/publicKeys')); + new PublicKeySignatureVerifier( + new UrlKeyFetcher('https://www.example.com/publicKeys')); }).not.to.throw(); }); @@ -312,17 +358,27 @@ describe('PublicKeySignatureVerifier', () => { invalidKeyFetchers.forEach((invalidKeyFetcher) => { it('should throw given an invalid key fetcher: ' + JSON.stringify(invalidKeyFetcher), () => { expect(() => { - new jwtUtil.PublicKeySignatureVerifier(invalidKeyFetchers as any); + new PublicKeySignatureVerifier(invalidKeyFetchers as any); }).to.throw('The provided key fetcher is not an object or null.'); }); }); }); describe('withCertificateUrl', () => { - it('should return a PublicKeySignatureVerifier instance when a valid cert url is provided', () => { - expect( - jwtUtil.PublicKeySignatureVerifier.withCertificateUrl('https://www.example.com/publicKeys') - ).to.be.an.instanceOf(jwtUtil.PublicKeySignatureVerifier); + it('should return a PublicKeySignatureVerifier instance with a UrlKeyFetcher when a ' + + 'valid cert url is provided', () => { + const verifier = PublicKeySignatureVerifier.withCertificateUrl('https://www.example.com/publicKeys'); + expect(verifier).to.be.an.instanceOf(PublicKeySignatureVerifier); + expect((verifier as any).keyFetcher).to.be.an.instanceOf(UrlKeyFetcher); + }); + }); + + describe('withJwksUrl', () => { + it('should return a PublicKeySignatureVerifier instance with a JwksFetcher when a ' + + 'valid jwks url is provided', () => { + const verifier = PublicKeySignatureVerifier.withJwksUrl('https://www.example.com/publicKeys'); + expect(verifier).to.be.an.instanceOf(PublicKeySignatureVerifier); + expect((verifier as any).keyFetcher).to.be.an.instanceOf(JwksFetcher); }); }); @@ -346,7 +402,7 @@ describe('PublicKeySignatureVerifier', () => { }); it('should be fullfilled given a valid token', () => { - const keyFetcherStub = sinon.stub(jwtUtil.UrlKeyFetcher.prototype, 'fetchPublicKeys') + const keyFetcherStub = sinon.stub(UrlKeyFetcher.prototype, 'fetchPublicKeys') .resolves(VALID_PUBLIC_KEYS_RESPONSE); stubs.push(keyFetcherStub); const mockIdToken = mocks.generateIdToken(); @@ -354,8 +410,41 @@ describe('PublicKeySignatureVerifier', () => { return verifier.verify(mockIdToken).should.eventually.be.fulfilled; }); + it('should be fullfilled given a valid token without a kid (should check against all the keys)', () => { + const keyFetcherStub = sinon.stub(UrlKeyFetcher.prototype, 'fetchPublicKeys') + .resolves({ 'kid-other': 'key-other', ...VALID_PUBLIC_KEYS_RESPONSE }); + stubs.push(keyFetcherStub); + const mockIdToken = mocks.generateIdToken({ + header: {} + }); + + return verifier.verify(mockIdToken).should.eventually.be.fulfilled; + }); + + it('should be rejected given an expired token without a kid (should check against all the keys)', () => { + const keyFetcherStub = sinon.stub(UrlKeyFetcher.prototype, 'fetchPublicKeys') + .resolves({ 'kid-other': 'key-other', ...VALID_PUBLIC_KEYS_RESPONSE }); + stubs.push(keyFetcherStub); + clock = sinon.useFakeTimers(1000); + const mockIdToken = mocks.generateIdToken({ + header: {} + }); + clock.tick((ONE_HOUR_IN_SECONDS * 1000) - 1); + + // token should still be valid + return verifier.verify(mockIdToken) + .then(() => { + clock!.tick(1); + + // token should now be invalid + return verifier.verify(mockIdToken).should.eventually.be.rejectedWith( + 'The provided token has expired. Get a fresh token from your client app and try again.') + .with.property('code', JwtErrorCode.TOKEN_EXPIRED); + }); + }); + it('should be rejected given a token with an incorrect algorithm', () => { - const keyFetcherStub = sinon.stub(jwtUtil.UrlKeyFetcher.prototype, 'fetchPublicKeys') + const keyFetcherStub = sinon.stub(UrlKeyFetcher.prototype, 'fetchPublicKeys') .resolves(VALID_PUBLIC_KEYS_RESPONSE); stubs.push(keyFetcherStub); const mockIdToken = mocks.generateIdToken({ @@ -364,36 +453,36 @@ describe('PublicKeySignatureVerifier', () => { return verifier.verify(mockIdToken).should.eventually.be .rejectedWith('invalid algorithm') - .with.property('code', jwtUtil.JwtErrorCode.INVALID_SIGNATURE); + .with.property('code', JwtErrorCode.INVALID_SIGNATURE); }); // tests to cover the private getKeyCallback function. it('should reject when no matching kid found', () => { - const keyFetcherStub = sinon.stub(jwtUtil.UrlKeyFetcher.prototype, 'fetchPublicKeys') + const keyFetcherStub = sinon.stub(UrlKeyFetcher.prototype, 'fetchPublicKeys') .resolves({ 'not-a-matching-key': 'public-key' }); stubs.push(keyFetcherStub); const mockIdToken = mocks.generateIdToken(); return verifier.verify(mockIdToken).should.eventually.be .rejectedWith('no-matching-kid-error') - .with.property('code', jwtUtil.JwtErrorCode.NO_MATCHING_KID); + .with.property('code', JwtErrorCode.NO_MATCHING_KID); }); it('should reject when an error occurs while fetching the keys', () => { - const keyFetcherStub = sinon.stub(jwtUtil.UrlKeyFetcher.prototype, 'fetchPublicKeys') + const keyFetcherStub = sinon.stub(UrlKeyFetcher.prototype, 'fetchPublicKeys') .rejects(new Error('Error fetching public keys.')); stubs.push(keyFetcherStub); const mockIdToken = mocks.generateIdToken(); return verifier.verify(mockIdToken).should.eventually.be .rejectedWith('Error fetching public keys.') - .with.property('code', jwtUtil.JwtErrorCode.KEY_FETCH_ERROR); + .with.property('code', JwtErrorCode.KEY_FETCH_ERROR); }); }); }); describe('EmulatorSignatureVerifier', () => { - const emulatorVerifier = new jwtUtil.EmulatorSignatureVerifier(); + const emulatorVerifier = new EmulatorSignatureVerifier(); describe('verify', () => { it('should be fullfilled given a valid unsigned (emulator) token', () => { @@ -415,12 +504,12 @@ describe('EmulatorSignatureVerifier', () => { describe('UrlKeyFetcher', () => { const agent = new https.Agent(); - let keyFetcher: jwtUtil.UrlKeyFetcher; + let keyFetcher: UrlKeyFetcher; let clock: sinon.SinonFakeTimers | undefined; let httpsSpy: sinon.SinonSpy; beforeEach(() => { - keyFetcher = new jwtUtil.UrlKeyFetcher( + keyFetcher = new UrlKeyFetcher( 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com', agent); httpsSpy = sinon.spy(https, 'request'); @@ -441,7 +530,7 @@ describe('UrlKeyFetcher', () => { describe('Constructor', () => { it('should not throw when valid key parameters are provided', () => { expect(() => { - new jwtUtil.UrlKeyFetcher('https://www.example.com/publicKeys', agent); + new UrlKeyFetcher('https://www.example.com/publicKeys', agent); }).not.to.throw(); }); @@ -449,7 +538,7 @@ describe('UrlKeyFetcher', () => { invalidCertURLs.forEach((invalidCertUrl) => { it('should throw given a non-URL public cert: ' + JSON.stringify(invalidCertUrl), () => { expect(() => { - new jwtUtil.UrlKeyFetcher(invalidCertUrl as any, agent); + new UrlKeyFetcher(invalidCertUrl as any, agent); }).to.throw('The provided public client certificate URL is not a valid URL.'); }); }); @@ -465,7 +554,7 @@ describe('UrlKeyFetcher', () => { it('should use the given HTTP Agent', () => { const agent = new https.Agent(); - const urlKeyFetcher = new jwtUtil.UrlKeyFetcher('https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com', agent); + const urlKeyFetcher = new UrlKeyFetcher('https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com', agent); mockedRequests.push(mockFetchPublicKeys()); return urlKeyFetcher.fetchPublicKeys() @@ -478,7 +567,7 @@ describe('UrlKeyFetcher', () => { it('should not fetch the public keys until the first time fetchPublicKeys() is called', () => { mockedRequests.push(mockFetchPublicKeys()); - const urlKeyFetcher = new jwtUtil.UrlKeyFetcher('https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com', agent); + const urlKeyFetcher = new UrlKeyFetcher('https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com', agent); expect(https.request).not.to.have.been.called; return urlKeyFetcher.fetchPublicKeys() @@ -539,3 +628,121 @@ describe('UrlKeyFetcher', () => { }); }); }); + +describe('JwksFetcher', () => { + let keyFetcher: JwksFetcher; + let clock: sinon.SinonFakeTimers | undefined; + let httpsSpy: sinon.SinonSpy; + + beforeEach(() => { + keyFetcher = new JwksFetcher( + 'https://firebaseappcheck.googleapis.com/v1alpha/jwks' + ); + httpsSpy = sinon.spy(https, 'request'); + }); + + afterEach(() => { + if (clock) { + clock.restore(); + clock = undefined; + } + httpsSpy.restore(); + }); + + after(() => { + nock.cleanAll(); + }); + + describe('Constructor', () => { + it('should not throw when valid url is provided', () => { + expect(() => { + new JwksFetcher('https://www.example.com/publicKeys'); + }).not.to.throw(); + }); + + const invalidJwksURLs = [null, NaN, 0, 1, true, false, [], {}, { a: 1 }, _.noop, 'file://invalid']; + invalidJwksURLs.forEach((invalidJwksURL) => { + it('should throw given a non-URL jwks endpoint: ' + JSON.stringify(invalidJwksURL), () => { + expect(() => { + new JwksFetcher(invalidJwksURL as any); + }).to.throw('The provided JWKS URL is not a valid URL.'); + }); + }); + }); + + describe('fetchPublicKeys', () => { + let mockedRequests: nock.Scope[] = []; + + afterEach(() => { + _.forEach(mockedRequests, (mockedRequest) => mockedRequest.done()); + mockedRequests = []; + }); + + it('should not fetch the public keys until the first time fetchPublicKeys() is called', () => { + mockedRequests.push(mockFetchJsonWebKeys()); + + const jwksFetcher = new JwksFetcher('https://firebaseappcheck.googleapis.com/v1alpha/jwks'); + expect(https.request).not.to.have.been.called; + + return jwksFetcher.fetchPublicKeys() + .then((result) => { + expect(https.request).to.have.been.calledOnce; + expect(result).to.have.key(mocks.jwksResponse.keys[0].kid); + }); + }); + + it('should not re-fetch the public keys every time fetchPublicKeys() is called', () => { + mockedRequests.push(mockFetchJsonWebKeys()); + + return keyFetcher.fetchPublicKeys().then(() => { + expect(https.request).to.have.been.calledOnce; + return keyFetcher.fetchPublicKeys(); + }).then(() => expect(https.request).to.have.been.calledOnce); + }); + + it('should refresh the public keys after the previous set of keys expire', () => { + mockedRequests.push(mockFetchJsonWebKeys()); + mockedRequests.push(mockFetchJsonWebKeys()); + mockedRequests.push(mockFetchJsonWebKeys()); + + clock = sinon.useFakeTimers(1000); + + return keyFetcher.fetchPublicKeys().then(() => { + expect(https.request).to.have.been.calledOnce; + clock!.tick((ONE_DAY_IN_SECONDS - 1) * 1000); + return keyFetcher.fetchPublicKeys(); + }).then(() => { + expect(https.request).to.have.been.calledOnce; + clock!.tick(ONE_DAY_IN_SECONDS * 1000); // 24 hours in milliseconds + return keyFetcher.fetchPublicKeys(); + }).then(() => { + // App check keys do not contain cache headers so we cache the keys for 24 hours. + // 24 hours has passed + expect(https.request).to.have.been.calledTwice; + clock!.tick((ONE_DAY_IN_SECONDS - 1) * 1000); + return keyFetcher.fetchPublicKeys(); + }).then(() => { + expect(https.request).to.have.been.calledTwice; + clock!.tick(ONE_DAY_IN_SECONDS * 1000); + return keyFetcher.fetchPublicKeys(); + }).then(() => { + // 48 hours have passed + expect(https.request).to.have.been.calledThrice; + }); + }); + + it('should be rejected if fetching the public keys fails', () => { + mockedRequests.push(mockFailedFetchJsonWebKeys()); + + return keyFetcher.fetchPublicKeys() + .should.eventually.be.rejectedWith('message'); + }); + + it('should be rejected if fetching the public keys returns a response with an error message', () => { + mockedRequests.push(mockFetchJsonWebKeysWithErrorResponse()); + + return keyFetcher.fetchPublicKeys() + .should.eventually.be.rejectedWith('Error fetching Json Web Keys'); + }); + }); +}); From ab034f5f4317d50ed49c62817286143a1f7d6ad9 Mon Sep 17 00:00:00 2001 From: Nikhil Agarwal <54072321+nikhilag@users.noreply.github.com> Date: Tue, 11 May 2021 03:14:55 +0530 Subject: [PATCH 035/102] Fix @types/node conflict with grpc and port type (#1258) Upgraded the @types/node dependency to v12.12.47 --- package-lock.json | 6 +++--- package.json | 2 +- src/utils/api-request.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 99e55cda11..4dfd37a11e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1035,9 +1035,9 @@ } }, "@types/node": { - "version": "10.17.26", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.26.tgz", - "integrity": "sha512-myMwkO2Cr82kirHY8uknNRHEVtn0wV3DTQfkrjx17jmkstDRZ24gNUdl8AHXVyVclTYI/bNjgTPTAWvWLqXqkw==" + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.2.tgz", + "integrity": "sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA==" }, "@types/qs": { "version": "6.9.6", diff --git a/package.json b/package.json index d384d64579..950cf1e5a2 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "dependencies": { "@firebase/database": "^0.10.0", "@firebase/database-types": "^0.7.2", - "@types/node": "^10.10.0", + "@types/node": ">=12.12.47", "dicer": "^0.3.0", "jsonwebtoken": "^8.5.1", "jwks-rsa": "^2.0.2", diff --git a/src/utils/api-request.ts b/src/utils/api-request.ts index a41a2f9b48..5b1b153288 100644 --- a/src/utils/api-request.ts +++ b/src/utils/api-request.ts @@ -728,7 +728,7 @@ class HttpRequestConfigImpl implements HttpRequestConfig { public buildRequestOptions(): https.RequestOptions { const parsed = this.buildUrl(); const protocol = parsed.protocol; - let port: string | undefined = parsed.port; + let port: string | null = parsed.port; if (!port) { const isHttps = protocol === 'https:'; port = isHttps ? '443' : '80'; From e5dc9f85bbf8f785e762dd5b314db40059fd62cc Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Mon, 10 May 2021 18:53:29 -0400 Subject: [PATCH 036/102] [chore] Release 9.8.0 (#1266) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 950cf1e5a2..f64b553668 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-admin", - "version": "9.7.0", + "version": "9.8.0", "description": "Firebase admin SDK for Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", From e759958d5d5f48ed89c7477acbc47811ce305ecb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 May 2021 16:14:15 -0700 Subject: [PATCH 037/102] build(deps): bump handlebars from 4.7.6 to 4.7.7 (#1253) Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.7.6 to 4.7.7. - [Release notes](https://github.com/wycats/handlebars.js/releases) - [Changelog](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md) - [Commits](https://github.com/wycats/handlebars.js/compare/v4.7.6...v4.7.7) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4dfd37a11e..c701b2bb4d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firebase-admin", - "version": "9.7.0", + "version": "9.8.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -4245,9 +4245,9 @@ } }, "handlebars": { - "version": "4.7.6", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", - "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", + "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, "requires": { "minimist": "^1.2.5", From e7155eacddd59cb449991476227c3a38681176cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 May 2021 16:28:17 -0700 Subject: [PATCH 038/102] build(deps): bump jose from 2.0.4 to 2.0.5 (#1265) Bumps [jose](https://github.com/panva/jose) from 2.0.4 to 2.0.5. - [Release notes](https://github.com/panva/jose/releases) - [Changelog](https://github.com/panva/jose/blob/v2.0.5/CHANGELOG.md) - [Commits](https://github.com/panva/jose/compare/v2.0.4...v2.0.5) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c701b2bb4d..a1e2c7999d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5122,9 +5122,9 @@ "dev": true }, "jose": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.4.tgz", - "integrity": "sha512-EArN9f6aq1LT/fIGGsfghOnNXn4noD+3dG5lL/ljY3LcRjw1u9w+4ahu/4ahsN6N0kRLyyW6zqdoYk7LNx3+YQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.5.tgz", + "integrity": "sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA==", "requires": { "@panva/asn1.js": "^1.0.0" } From fd23ad010c2763525bb71c7dc1895cdf0084bfd3 Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Tue, 18 May 2021 14:47:58 -0700 Subject: [PATCH 039/102] fix: Revert regression introduced in #1257 (#1277) --- test/unit/storage/storage.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/storage/storage.spec.ts b/test/unit/storage/storage.spec.ts index e7d2360ee9..e37bffd0e4 100644 --- a/test/unit/storage/storage.spec.ts +++ b/test/unit/storage/storage.spec.ts @@ -114,7 +114,7 @@ describe('Storage', () => { }); }); - describe.only('Emulator mode', () => { + describe('Emulator mode', () => { const VALID_EMULATOR_HOST = 'localhost:9199'; const INVALID_EMULATOR_HOST = 'https://localhost:9199'; @@ -136,7 +136,7 @@ describe('Storage', () => { expect(() => new Storage(mockApp)).to.throw( 'FIREBASE_STORAGE_EMULATOR_HOST should not contain a protocol'); }); - + after(() => { delete process.env.STORAGE_EMULATOR_HOST; delete process.env.FIREBASE_STORAGE_EMULATOR_HOST; From 3b48235bf49d51d2d0f40b298ddaa71ce7432a20 Mon Sep 17 00:00:00 2001 From: bojeil-google Date: Tue, 18 May 2021 16:00:43 -0700 Subject: [PATCH 040/102] fix(auth): make MFA uid optional for updateUser operations (#1278) * fix(auth): make MFA uid optional for updateUser operations MFA `uid` should be optional for `updateUser` operations. When not specified, the backend will provision a `uid` for the enrolled second factor. Fixes https://github.com/firebase/firebase-admin-node/issues/1276 --- src/auth/auth-api-request.ts | 9 ++++----- test/unit/auth/auth-api-request.spec.ts | 8 ++++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/auth/auth-api-request.ts b/src/auth/auth-api-request.ts index d3ecf73fc7..bd4b2a5ce8 100644 --- a/src/auth/auth-api-request.ts +++ b/src/auth/auth-api-request.ts @@ -257,9 +257,8 @@ class AuthHttpClient extends AuthorizedHttpClient { * an error is thrown. * * @param request The AuthFactorInfo request object. - * @param writeOperationType The write operation type. */ -function validateAuthFactorInfo(request: AuthFactorInfo, writeOperationType: WriteOperationType): void { +function validateAuthFactorInfo(request: AuthFactorInfo): void { const validKeys = { mfaEnrollmentId: true, displayName: true, @@ -275,8 +274,8 @@ function validateAuthFactorInfo(request: AuthFactorInfo, writeOperationType: Wri // No enrollment ID is available for signupNewUser. Use another identifier. const authFactorInfoIdentifier = request.mfaEnrollmentId || request.phoneInfo || JSON.stringify(request); - const uidRequired = writeOperationType !== WriteOperationType.Create; - if ((typeof request.mfaEnrollmentId !== 'undefined' || uidRequired) && + // Enrollment uid may or may not be specified for update operations. + if (typeof request.mfaEnrollmentId !== 'undefined' && !validator.isNonEmptyString(request.mfaEnrollmentId)) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_UID, @@ -573,7 +572,7 @@ function validateCreateEditRequest(request: any, writeOperationType: WriteOperat throw new FirebaseAuthError(AuthClientErrorCode.INVALID_ENROLLED_FACTORS); } enrollments.forEach((authFactorInfoEntry: AuthFactorInfo) => { - validateAuthFactorInfo(authFactorInfoEntry, writeOperationType); + validateAuthFactorInfo(authFactorInfoEntry); }); } } diff --git a/test/unit/auth/auth-api-request.spec.ts b/test/unit/auth/auth-api-request.spec.ts index 2df01889dd..0ca9bd5a85 100644 --- a/test/unit/auth/auth-api-request.spec.ts +++ b/test/unit/auth/auth-api-request.spec.ts @@ -2071,6 +2071,11 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { phoneNumber: '+16505551000', factorId: 'phone', } as UpdateMultiFactorInfoRequest, + { + // No error should be thrown when no uid is specified. + phoneNumber: '+16505551234', + factorId: 'phone', + } as UpdateMultiFactorInfoRequest, ], }, }; @@ -2096,6 +2101,9 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { mfaEnrollmentId: 'enrolledSecondFactor2', phoneInfo: '+16505551000', }, + { + phoneInfo: '+16505551234', + }, ], }, }; From 3a2d2fa929be3ae81c140fe3f6630cf9884bafe1 Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Thu, 20 May 2021 10:28:49 -0700 Subject: [PATCH 041/102] chore: Enabled dependabot (#1279) --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..aff82a102d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" From 6bf2aeeb1d1bbdd2e6f4056d904afe9974c2c549 Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Thu, 20 May 2021 12:00:43 -0700 Subject: [PATCH 042/102] chore: Remove gulp-replace dependency (#1285) --- package-lock.json | 56 +++++------------------------------------------ package.json | 1 - 2 files changed, 6 insertions(+), 51 deletions(-) diff --git a/package-lock.json b/package-lock.json index a1e2c7999d..fd6431ecd8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -971,7 +971,7 @@ }, "@types/firebase-token-generator": { "version": "2.0.28", - "resolved": "https://registry.npmjs.org/@types/firebase-token-generator/-/firebase-token-generator-2.0.28.tgz", + "resolved": "http://registry.npmjs.org/@types/firebase-token-generator/-/firebase-token-generator-2.0.28.tgz", "integrity": "sha1-Z1VIHZMk4mt6XItFXWgUg3aCw5Y=", "dev": true }, @@ -1728,12 +1728,6 @@ "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", "dev": true }, - "binaryextensions": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-1.0.1.tgz", - "integrity": "sha1-HmN0iLNbWL2l9HdL+WpSEqjJB1U=", - "dev": true - }, "bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", @@ -3433,7 +3427,7 @@ }, "firebase-token-generator": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/firebase-token-generator/-/firebase-token-generator-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/firebase-token-generator/-/firebase-token-generator-2.0.0.tgz", "integrity": "sha1-l2fXWewTq9yZuhFf1eqZ2Lk9EgY=", "dev": true }, @@ -3854,7 +3848,7 @@ }, "globby": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "resolved": "http://registry.npmjs.org/globby/-/globby-5.0.0.tgz", "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", "dev": true, "requires": { @@ -4096,17 +4090,6 @@ } } }, - "gulp-replace": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-0.5.4.tgz", - "integrity": "sha1-aaZ5FLvRPFYr/xT1BKQDeWqg2qk=", - "dev": true, - "requires": { - "istextorbinary": "1.0.2", - "readable-stream": "^2.0.1", - "replacestream": "^4.0.0" - } - }, "gulp-typescript": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/gulp-typescript/-/gulp-typescript-5.0.1.tgz", @@ -5105,16 +5088,6 @@ "html-escaper": "^2.0.0" } }, - "istextorbinary": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-1.0.2.tgz", - "integrity": "sha1-rOGTVNGpoBc+/rEITOD4ewrX3s8=", - "dev": true, - "requires": { - "binaryextensions": "~1.0.0", - "textextensions": "~1.0.0" - } - }, "jju": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", @@ -7279,7 +7252,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, @@ -7441,7 +7414,7 @@ }, "pretty-hrtime": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "resolved": "http://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", "dev": true }, @@ -7742,17 +7715,6 @@ "remove-trailing-separator": "^1.1.0" } }, - "replacestream": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/replacestream/-/replacestream-4.0.3.tgz", - "integrity": "sha512-AC0FiLS352pBBiZhd4VXB1Ab/lh0lEgpP+GGvZqbQh8a5cmXVoTe5EX/YeTFArnp4SRGTHh1qCHu9lGs1qG8sA==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.3", - "object-assign": "^4.0.1", - "readable-stream": "^2.0.2" - } - }, "request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -7984,7 +7946,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -8806,12 +8768,6 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "textextensions": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-1.0.2.tgz", - "integrity": "sha1-ZUhjk+4fK7A5pgy7oFsLaL2VAdI=", - "dev": true - }, "thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", diff --git a/package.json b/package.json index f64b553668..fee6d72a09 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,6 @@ "gulp": "^4.0.2", "gulp-filter": "^6.0.0", "gulp-header": "^1.8.8", - "gulp-replace": "^0.5.4", "gulp-typescript": "^5.0.1", "http-message-parser": "^0.0.34", "jsdom": "^15.0.0", From 11f2fb11929a23be97688b70ff1acdd5bd73d477 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 May 2021 12:11:01 -0700 Subject: [PATCH 043/102] build(deps-dev): bump gulp-header from 1.8.12 to 2.0.9 (#1283) Bumps [gulp-header](https://github.com/tracker1/gulp-header) from 1.8.12 to 2.0.9. - [Release notes](https://github.com/tracker1/gulp-header/releases) - [Changelog](https://github.com/gulp-community/gulp-header/blob/master/changelog.md) - [Commits](https://github.com/tracker1/gulp-header/compare/v1.8.12...v2.0.9) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 17 ++++++++++++----- package.json | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index fd6431ecd8..b5ddc137cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4068,13 +4068,14 @@ } }, "gulp-header": { - "version": "1.8.12", - "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-1.8.12.tgz", - "integrity": "sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-2.0.9.tgz", + "integrity": "sha512-LMGiBx+qH8giwrOuuZXSGvswcIUh0OiioNkUpLhNyvaC6/Ga8X6cfAeme2L5PqsbXMhL8o8b/OmVqIQdxprhcQ==", "dev": true, "requires": { - "concat-with-sourcemaps": "*", - "lodash.template": "^4.4.0", + "concat-with-sourcemaps": "^1.1.0", + "lodash.template": "^4.5.0", + "map-stream": "0.0.7", "through2": "^2.0.0" }, "dependencies": { @@ -5746,6 +5747,12 @@ "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", "dev": true }, + "map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", + "dev": true + }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", diff --git a/package.json b/package.json index fee6d72a09..5d19d0cab5 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "firebase-token-generator": "^2.0.0", "gulp": "^4.0.2", "gulp-filter": "^6.0.0", - "gulp-header": "^1.8.8", + "gulp-header": "^2.0.9", "gulp-typescript": "^5.0.1", "http-message-parser": "^0.0.34", "jsdom": "^15.0.0", From df3b3986a5086fdcdf8270d21fc3805f31d593a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 May 2021 12:43:41 -0700 Subject: [PATCH 044/102] build(deps-dev): bump run-sequence from 1.2.2 to 2.2.1 (#1282) Bumps [run-sequence](https://github.com/OverZealous/run-sequence) from 1.2.2 to 2.2.1. - [Release notes](https://github.com/OverZealous/run-sequence/releases) - [Changelog](https://github.com/OverZealous/run-sequence/blob/master/CHANGELOG.md) - [Commits](https://github.com/OverZealous/run-sequence/compare/v1.2.2...v2.2.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 338 +++++++++++----------------------------------- package.json | 2 +- 2 files changed, 77 insertions(+), 263 deletions(-) diff --git a/package-lock.json b/package-lock.json index b5ddc137cf..991b60bc98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1263,6 +1263,15 @@ "ansi-wrap": "^0.1.0" } }, + "ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, "ansi-escapes": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", @@ -1289,6 +1298,15 @@ "ansi-wrap": "0.1.0" } }, + "ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -1414,12 +1432,6 @@ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, - "array-differ": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", - "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", - "dev": true - }, "array-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", @@ -1710,12 +1722,6 @@ "tweetnacl": "^0.14.3" } }, - "beeper": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", - "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", - "dev": true - }, "bignumber.js": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", @@ -2423,12 +2429,6 @@ "integrity": "sha512-EFTCh9zRSEpGPmJaexg7HTuzZHh6cnJj1ui7IGCFNXzd2QdpsNh05Db5TF3xzJm30YN+A8/6xHSuRcQqoc3kFA==", "optional": true }, - "dateformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", - "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", - "dev": true - }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -2645,41 +2645,6 @@ "is-obj": "^2.0.0" } }, - "duplexer2": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", - "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", - "dev": true, - "requires": { - "readable-stream": "~1.1.9" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -4119,106 +4084,6 @@ } } }, - "gulp-util": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", - "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", - "dev": true, - "requires": { - "array-differ": "^1.0.0", - "array-uniq": "^1.0.2", - "beeper": "^1.0.0", - "chalk": "^1.0.0", - "dateformat": "^2.0.0", - "fancy-log": "^1.1.0", - "gulplog": "^1.0.0", - "has-gulplog": "^0.1.0", - "lodash._reescape": "^3.0.0", - "lodash._reevaluate": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.template": "^3.0.0", - "minimist": "^1.1.0", - "multipipe": "^0.1.2", - "object-assign": "^3.0.0", - "replace-ext": "0.0.1", - "through2": "^2.0.0", - "vinyl": "^0.5.0" - }, - "dependencies": { - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true - }, - "clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", - "dev": true - }, - "lodash.template": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", - "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", - "dev": true, - "requires": { - "lodash._basecopy": "^3.0.0", - "lodash._basetostring": "^3.0.0", - "lodash._basevalues": "^3.0.0", - "lodash._isiterateecall": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0", - "lodash.keys": "^3.0.0", - "lodash.restparam": "^3.0.0", - "lodash.templatesettings": "^3.0.0" - } - }, - "lodash.templatesettings": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", - "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", - "dev": true, - "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0" - } - }, - "object-assign": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", - "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", - "dev": true - }, - "replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", - "dev": true - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "vinyl": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", - "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", - "dev": true, - "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", - "replace-ext": "0.0.1" - } - } - } - }, "gulplog": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", @@ -4289,15 +4154,6 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "has-gulplog": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", - "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", - "dev": true, - "requires": { - "sparkles": "^1.0.0" - } - }, "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", @@ -5424,60 +5280,12 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "lodash._basecopy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", - "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", - "dev": true - }, - "lodash._basetostring": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", - "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=", - "dev": true - }, - "lodash._basevalues": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", - "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=", - "dev": true - }, - "lodash._getnative": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", - "dev": true - }, - "lodash._isiterateecall": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", - "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", - "dev": true - }, - "lodash._reescape": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", - "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=", - "dev": true - }, - "lodash._reevaluate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", - "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=", - "dev": true - }, "lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", "dev": true }, - "lodash._root": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", - "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", - "dev": true - }, "lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -5489,15 +5297,6 @@ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" }, - "lodash.escape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", - "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", - "dev": true, - "requires": { - "lodash._root": "^3.0.0" - } - }, "lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", @@ -5515,18 +5314,6 @@ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" }, - "lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", - "dev": true - }, - "lodash.isarray": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", - "dev": true - }, "lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", @@ -5558,28 +5345,11 @@ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" }, - "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", - "dev": true, - "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" - } - }, "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, - "lodash.restparam": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", - "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", - "dev": true - }, "lodash.set": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", @@ -6355,15 +6125,6 @@ } } }, - "multipipe": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", - "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", - "dev": true, - "requires": { - "duplexer2": "0.0.2" - } - }, "mute-stdout": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", @@ -7928,13 +7689,66 @@ "dev": true }, "run-sequence": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/run-sequence/-/run-sequence-1.2.2.tgz", - "integrity": "sha1-UJWgvr6YczsBQL0I3YDsAw3azes=", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/run-sequence/-/run-sequence-2.2.1.tgz", + "integrity": "sha512-qkzZnQWMZjcKbh3CNly2srtrkaO/2H/SI5f2eliMCapdRD3UhMrwjfOAZJAnZ2H8Ju4aBzFZkBGXUqFs9V0yxw==", "dev": true, "requires": { - "chalk": "*", - "gulp-util": "*" + "chalk": "^1.1.3", + "fancy-log": "^1.3.2", + "plugin-error": "^0.1.2" + }, + "dependencies": { + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "^1.1.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "requires": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + } + } } }, "rxjs": { diff --git a/package.json b/package.json index 5d19d0cab5..561f1bb8c0 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "nyc": "^14.1.0", "request": "^2.75.0", "request-promise": "^4.1.1", - "run-sequence": "^1.1.5", + "run-sequence": "^2.2.1", "sinon": "^9.0.0", "sinon-chai": "^3.0.0", "ts-node": "^9.0.0", From 89387c1970c3b4b5990a47dee77d147a9348246e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 10:04:25 -0700 Subject: [PATCH 045/102] build(deps-dev): bump sinon from 9.0.2 to 9.2.4 (#1289) Bumps [sinon](https://github.com/sinonjs/sinon) from 9.0.2 to 9.2.4. - [Release notes](https://github.com/sinonjs/sinon/releases) - [Changelog](https://github.com/sinonjs/sinon/blob/master/CHANGELOG.md) - [Commits](https://github.com/sinonjs/sinon/compare/v9.0.2...v9.2.4) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 59 +++++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 38 deletions(-) diff --git a/package-lock.json b/package-lock.json index 991b60bc98..76724dcb1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -813,9 +813,9 @@ } }, "@sinonjs/commons": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.0.tgz", - "integrity": "sha512-wEj54PfsZ5jGSwMX68G8ZXFawcSglQSXqCftWX3ec8MDUzQdHgcKvw97awHbY0efQEL5iKUOAmmVtoYgmrSG4Q==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", "dev": true, "requires": { "type-detect": "4.0.8" @@ -830,20 +830,10 @@ "@sinonjs/commons": "^1.7.0" } }, - "@sinonjs/formatio": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-5.0.1.tgz", - "integrity": "sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^5.0.2" - } - }, "@sinonjs/samsam": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.0.3.tgz", - "integrity": "sha512-QucHkc2uMJ0pFGjJUDP3F9dq5dx8QIaqISl9QgwLOh6P9yv877uONPGXh/OH/0zmM3tW1JjuJltAZV2l7zU+uQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.1.tgz", + "integrity": "sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg==", "dev": true, "requires": { "@sinonjs/commons": "^1.6.0", @@ -5131,9 +5121,9 @@ "dev": true }, "just-extend": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz", - "integrity": "sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", "dev": true }, "jwa": { @@ -6233,9 +6223,9 @@ "dev": true }, "nise": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/nise/-/nise-4.0.4.tgz", - "integrity": "sha512-bTTRUNlemx6deJa+ZyoCUTRvH3liK5+N6VQZ4NIw90AgDXY6iPnsqplNFf6STcj+ePk0H/xqxnP75Lr0J0Fq3A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/nise/-/nise-4.1.0.tgz", + "integrity": "sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA==", "dev": true, "requires": { "@sinonjs/commons": "^1.7.0", @@ -7884,26 +7874,19 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, "sinon": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.0.2.tgz", - "integrity": "sha512-0uF8Q/QHkizNUmbK3LRFqx5cpTttEVXudywY9Uwzy8bTfZUhljZ7ARzSxnRHWYWtVTeh4Cw+tTb3iU21FQVO9A==", + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", + "integrity": "sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==", "dev": true, "requires": { - "@sinonjs/commons": "^1.7.2", + "@sinonjs/commons": "^1.8.1", "@sinonjs/fake-timers": "^6.0.1", - "@sinonjs/formatio": "^5.0.1", - "@sinonjs/samsam": "^5.0.3", + "@sinonjs/samsam": "^5.3.1", "diff": "^4.0.2", - "nise": "^4.0.1", + "nise": "^4.0.4", "supports-color": "^7.1.0" }, "dependencies": { - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -7911,9 +7894,9 @@ "dev": true }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "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, "requires": { "has-flag": "^4.0.0" From a549a6c5a0d33e456826d2f507c30f4fa410986d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 10:18:09 -0700 Subject: [PATCH 046/102] build(deps-dev): bump nyc from 14.1.1 to 15.1.0 (#1290) Bumps [nyc](https://github.com/istanbuljs/nyc) from 14.1.1 to 15.1.0. - [Release notes](https://github.com/istanbuljs/nyc/releases) - [Changelog](https://github.com/istanbuljs/nyc/blob/master/CHANGELOG.md) - [Commits](https://github.com/istanbuljs/nyc/compare/v14.1.1...v15.1.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 1371 ++++++++++++++++++++++++++++----------------- package.json | 2 +- 2 files changed, 863 insertions(+), 510 deletions(-) diff --git a/package-lock.json b/package-lock.json index 76724dcb1f..2d20ad73ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,45 +13,214 @@ "@babel/highlight": "^7.10.4" } }, + "@babel/compat-data": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.0.tgz", + "integrity": "sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==", + "dev": true + }, + "@babel/core": { + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.3.tgz", + "integrity": "sha512-jB5AmTKOCSJIZ72sd78ECEhuPiDMKlQdDI/4QRI6lzYATx5SSogS1oQA2AoPecRCknm30gHi2l+QVvNUu3wZAg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.14.3", + "@babel/helper-compilation-targets": "^7.13.16", + "@babel/helper-module-transforms": "^7.14.2", + "@babel/helpers": "^7.14.0", + "@babel/parser": "^7.14.3", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.14.2", + "@babel/types": "^7.14.2", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "dependencies": { + "@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, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", + "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", + "dev": true + }, + "@babel/highlight": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", + "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "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, + "requires": { + "color-convert": "^1.9.0" + } + }, + "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, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "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, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "@babel/generator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.4.tgz", - "integrity": "sha512-toLIHUIAgcQygFZRAQcsLQV3CBuX6yOIru1kJk/qqqvcRmZrYe6WavZTSG+bB8MxhnL9YPf+pKQfuiP161q7ng==", + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.3.tgz", + "integrity": "sha512-bn0S6flG/j0xtQdz3hsjJ624h3W0r3llttBMfyHX3YrZ/KtLYr15bjA0FXkgW7FpvrDuTuElXeVjiKlYRpnOFA==", "dev": true, "requires": { - "@babel/types": "^7.10.4", + "@babel/types": "^7.14.2", "jsesc": "^2.5.1", - "lodash": "^4.17.13", "source-map": "^0.5.0" } }, + "@babel/helper-compilation-targets": { + "version": "7.13.16", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz", + "integrity": "sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.13.15", + "@babel/helper-validator-option": "^7.12.17", + "browserslist": "^4.14.5", + "semver": "^6.3.0" + } + }, "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz", + "integrity": "sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.14.2" } }, "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "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, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", + "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", + "dev": true, + "requires": { + "@babel/types": "^7.13.12" + } + }, + "@babel/helper-module-imports": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz", + "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", + "dev": true, + "requires": { + "@babel/types": "^7.13.12" + } + }, + "@babel/helper-module-transforms": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.2.tgz", + "integrity": "sha512-OznJUda/soKXv0XhpvzGWDnml4Qnwp16GN+D/kZIdLsWoHj05kyu8Rm5kXmMef+rVJZ0+4pSGLkeixdqNUATDA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.13.12", + "@babel/helper-replace-supers": "^7.13.12", + "@babel/helper-simple-access": "^7.13.12", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.14.0", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.14.2", + "@babel/types": "^7.14.2" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", + "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", + "dev": true + } + } + }, + "@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, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-replace-supers": { + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.3.tgz", + "integrity": "sha512-Rlh8qEWZSTfdz+tgNV/N4gz1a0TMNwCUcENhMjHTHKp3LseYH5Jha0NSlyTQWMnjbYcwFt+bqAMqSLHVXkQ6UA==", "dev": true, "requires": { - "@babel/types": "^7.10.4" + "@babel/helper-member-expression-to-functions": "^7.13.12", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.14.2", + "@babel/types": "^7.14.2" + } + }, + "@babel/helper-simple-access": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz", + "integrity": "sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==", + "dev": true, + "requires": { + "@babel/types": "^7.13.12" } }, "@babel/helper-split-export-declaration": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz", - "integrity": "sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg==", + "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, "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.13" } }, "@babel/helper-validator-identifier": { @@ -60,6 +229,23 @@ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", "dev": true }, + "@babel/helper-validator-option": { + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", + "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.0.tgz", + "integrity": "sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg==", + "dev": true, + "requires": { + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.14.0", + "@babel/types": "^7.14.0" + } + }, "@babel/highlight": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", @@ -103,56 +289,174 @@ } }, "@babel/parser": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.4.tgz", - "integrity": "sha512-8jHII4hf+YVDsskTF6WuMB3X4Eh+PsUkC2ljq22so5rHvH+T8BzyL94VOdyFLNR8tBSVXOTbNHOKpR4TfRxVtA==", + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.3.tgz", + "integrity": "sha512-7MpZDIfI7sUC5zWo2+foJ50CSI5lcqDehZ0lVgIhSi4bFEk94fLAKlF3Q0nzSQQ+ca0lm+O6G9ztKVBeu8PMRQ==", "dev": true }, "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + }, + "dependencies": { + "@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, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", + "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", + "dev": true + }, + "@babel/highlight": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", + "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "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, + "requires": { + "color-convert": "^1.9.0" + } + }, + "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, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "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, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "@babel/traverse": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.4.tgz", - "integrity": "sha512-aSy7p5THgSYm4YyxNGz6jZpXf+Ok40QF3aA2LyIONkDHpAcJzDUqlCKXv6peqYUs2gmic849C/t2HKw2a2K20Q==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.2.tgz", + "integrity": "sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.10.4", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4", + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.14.2", + "@babel/helper-function-name": "^7.14.2", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.14.2", + "@babel/types": "^7.14.2", "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" + "globals": "^11.1.0" }, "dependencies": { + "@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, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", + "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", + "dev": true + }, + "@babel/highlight": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", + "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "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, + "requires": { + "color-convert": "^1.9.0" + } + }, + "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, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, "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 + }, + "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, + "requires": { + "has-flag": "^3.0.0" + } } } }, "@babel/types": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.4.tgz", - "integrity": "sha512-UTCFOxC3FsFHb7lkRMVvgLzaRVamXuAs2Tz4wajva4WxtVY82eZeaUBtC2Zt95FU9TiznuC0Zk35tsim8jeVpg==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.2.tgz", + "integrity": "sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.13", + "@babel/helper-validator-identifier": "^7.14.0", "to-fast-properties": "^2.0.0" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", + "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", + "dev": true + } } }, "@firebase/app": { @@ -609,6 +913,55 @@ "protobufjs": "^6.8.6" } }, + "@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, + "requires": { + "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" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "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, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "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 + }, + "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 + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, "@microsoft/api-extractor": { "version": "7.11.2", "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.11.2.tgz", @@ -1232,6 +1585,16 @@ "debug": "4" } }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, "ajv": { "version": "6.12.3", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", @@ -1341,12 +1704,12 @@ } }, "append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", "dev": true, "requires": { - "default-require-extensions": "^2.0.0" + "default-require-extensions": "^3.0.0" } }, "aproba": { @@ -1791,6 +2154,19 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + } + }, "buffer": { "version": "4.9.2", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", @@ -1836,50 +2212,15 @@ } }, "caching-transform": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", - "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", "dev": true, "requires": { - "hasha": "^3.0.0", - "make-dir": "^2.0.0", - "package-hash": "^3.0.0", - "write-file-atomic": "^2.4.2" - }, - "dependencies": { - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - } + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" } }, "callsites": { @@ -1894,6 +2235,12 @@ "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", "dev": true }, + "caniuse-lite": { + "version": "1.0.30001228", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz", + "integrity": "sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A==", + "dev": true + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -2037,6 +2384,12 @@ } } }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -2140,6 +2493,12 @@ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true }, + "colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "dev": true + }, "colors": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", @@ -2289,43 +2648,6 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "cp-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", - "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "make-dir": "^2.0.0", - "nested-error-stacks": "^2.0.0", - "pify": "^4.0.1", - "safe-buffer": "^5.0.1" - }, - "dependencies": { - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, "cross-spawn": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", @@ -2477,18 +2799,18 @@ } }, "default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", "dev": true, "requires": { - "strip-bom": "^3.0.0" + "strip-bom": "^4.0.0" }, "dependencies": { "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "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 } } @@ -2674,6 +2996,12 @@ "safe-buffer": "^5.0.1" } }, + "electron-to-chromium": { + "version": "1.3.736", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.736.tgz", + "integrity": "sha512-DY8dA7gR51MSo66DqitEQoUMQ0Z+A2DSXFi7tK304bdTVqczCAfUuyQw6Wdg8hIoo5zIxkU1L24RQtUce1Ioig==", + "dev": true + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -3311,38 +3639,14 @@ } }, "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", "dev": true, "requires": { "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "dependencies": { - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" } }, "find-up": { @@ -3452,13 +3756,56 @@ } }, "foreground-child": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", - "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", "dev": true, "requires": { - "cross-spawn": "^4", - "signal-exit": "^3.0.0" + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "dependencies": { + "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, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "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 + }, + "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, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "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 + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } } }, "forever-agent": { @@ -3487,6 +3834,12 @@ "map-cache": "^0.2.2" } }, + "fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true + }, "fs-extra": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", @@ -3638,6 +3991,12 @@ "stream-events": "^1.0.4" } }, + "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 + }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", @@ -3650,6 +4009,12 @@ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, + "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 + }, "get-prop": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/get-prop/-/get-prop-0.0.10.tgz", @@ -4195,20 +4560,13 @@ "optional": true }, "hasha": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", - "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", "dev": true, "requires": { - "is-stream": "^1.0.1" - }, - "dependencies": { - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - } + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" } }, "he": { @@ -4368,6 +4726,12 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -4736,8 +5100,7 @@ "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==", - "optional": true + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" }, "is-stream-ended": { "version": "0.1.4", @@ -4810,114 +5173,144 @@ "dev": true }, "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "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 }, "istanbul-lib-hook": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", - "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", "dev": true, "requires": { - "append-transform": "^1.0.0" + "append-transform": "^2.0.0" } }, "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "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, "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" } }, - "istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "istanbul-lib-processinfo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", + "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", "dev": true, "requires": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" + "archy": "^1.0.0", + "cross-spawn": "^7.0.0", + "istanbul-lib-coverage": "^3.0.0-alpha.1", + "make-dir": "^3.0.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^3.3.3" }, "dependencies": { - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "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, "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "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 }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "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, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "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 + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "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, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "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 }, "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "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, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } } } }, "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "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, "requires": { "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", + "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" }, "dependencies": { - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -4927,12 +5320,13 @@ } }, "istanbul-reports": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", - "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", "dev": true, "requires": { - "html-escaper": "^2.0.0" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" } }, "jju": { @@ -5050,6 +5444,15 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, + "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, + "requires": { + "minimist": "^1.2.5" + } + }, "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -5247,21 +5650,12 @@ } }, "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "dependencies": { - "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 - } + "p-locate": "^4.1.0" } }, "lodash": { @@ -5481,7 +5875,6 @@ "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==", - "optional": true, "requires": { "semver": "^6.0.0" } @@ -5569,23 +5962,6 @@ "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", "dev": true }, - "merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "requires": { - "source-map": "^0.6.1" - }, - "dependencies": { - "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 - } - } - }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -6204,12 +6580,6 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "nested-error-stacks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", - "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==", - "dev": true - }, "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -6290,6 +6660,21 @@ } } }, + "node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "requires": { + "process-on-spawn": "^1.0.0" + } + }, + "node-releases": { + "version": "1.1.72", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz", + "integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==", + "dev": true + }, "node-version": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/node-version/-/node-version-1.2.0.tgz", @@ -6516,51 +6901,53 @@ "dev": true }, "nyc": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", - "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", - "dev": true, - "requires": { - "archy": "^1.0.0", - "caching-transform": "^3.0.2", - "convert-source-map": "^1.6.0", - "cp-file": "^6.2.0", - "find-cache-dir": "^2.1.0", - "find-up": "^3.0.0", - "foreground-child": "^1.5.6", - "glob": "^7.1.3", - "istanbul-lib-coverage": "^2.0.5", - "istanbul-lib-hook": "^2.0.7", - "istanbul-lib-instrument": "^3.3.0", - "istanbul-lib-report": "^2.0.8", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^2.2.4", - "js-yaml": "^3.13.1", - "make-dir": "^2.1.0", - "merge-source-map": "^1.1.0", - "resolve-from": "^4.0.0", - "rimraf": "^2.6.3", + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "requires": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", "signal-exit": "^3.0.2", - "spawn-wrap": "^1.4.2", - "test-exclude": "^5.2.3", - "uuid": "^3.3.2", - "yargs": "^13.2.2", - "yargs-parser": "^13.0.0" + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" }, "dependencies": { "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, "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==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" } }, "camelcase": { @@ -6570,29 +6957,39 @@ "dev": true }, "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" } }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "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, + "requires": { + "color-name": "~1.1.4" + } + }, + "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 }, "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } }, "get-caller-file": { @@ -6602,25 +6999,15 @@ "dev": true }, "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=", + "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 }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "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 }, "require-main-filename": { @@ -6629,38 +7016,41 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "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 }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^5.0.0" } }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - }, "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", @@ -6668,38 +7058,39 @@ "dev": true }, "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "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, "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } }, "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "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, "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", + "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": "^3.0.0", + "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" + "yargs-parser": "^18.1.2" } }, "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -6904,12 +7295,12 @@ } }, "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==", + "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, "requires": { - "p-limit": "^2.0.0" + "p-limit": "^2.2.0" }, "dependencies": { "p-limit": { @@ -6923,19 +7314,28 @@ } } }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, "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==" }, "package-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", - "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", "dev": true, "requires": { "graceful-fs": "^4.1.15", - "hasha": "^3.0.0", + "hasha": "^5.0.0", "lodash.flattendeep": "^4.4.0", "release-zalgo": "^1.0.0" } @@ -7121,22 +7521,29 @@ } }, "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "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, "requires": { - "find-up": "^3.0.0" + "find-up": "^4.0.0" }, "dependencies": { "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } + }, + "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 } } }, @@ -7181,6 +7588,15 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "requires": { + "fromentries": "^1.2.0" + } + }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -8116,17 +8532,37 @@ "dev": true }, "spawn-wrap": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", - "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", "dev": true, "requires": { - "foreground-child": "^1.5.6", - "mkdirp": "^0.5.0", - "os-homedir": "^1.0.1", - "rimraf": "^2.6.2", + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", "signal-exit": "^3.0.2", - "which": "^1.3.0" + "which": "^2.0.1" + }, + "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } } }, "spdx-correct": { @@ -8474,96 +8910,14 @@ } }, "test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "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, "requires": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" - }, - "dependencies": { - "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, - "requires": { - "locate-path": "^3.0.0" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - }, - "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 - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" } }, "text-table": { @@ -9317,7 +9671,6 @@ "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==", - "optional": true, "requires": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", diff --git a/package.json b/package.json index 561f1bb8c0..d776317acc 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "mz": "^2.7.0", "nock": "^13.0.0", "npm-run-all": "^4.1.5", - "nyc": "^14.1.0", + "nyc": "^15.1.0", "request": "^2.75.0", "request-promise": "^4.1.1", "run-sequence": "^2.2.1", From d6dcf9e53579461c8b490e53f34c63908ba58c9e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 11:46:10 -0700 Subject: [PATCH 047/102] build(deps-dev): bump chalk from 1.1.3 to 4.1.1 (#1288) Bumps [chalk](https://github.com/chalk/chalk) from 1.1.3 to 4.1.1. - [Release notes](https://github.com/chalk/chalk/releases) - [Commits](https://github.com/chalk/chalk/compare/v1.1.3...v4.1.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 85 ++++++++++++++++++++++++++++++++++++++--------- package.json | 2 +- 2 files changed, 70 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2d20ad73ff..230bcefe4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1667,10 +1667,30 @@ "dev": true }, "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + }, + "dependencies": { + "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, + "requires": { + "color-name": "~1.1.4" + } + }, + "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 + } + } }, "ansi-wrap": { "version": "0.1.0", @@ -2271,16 +2291,13 @@ } }, "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, "chardet": { @@ -8105,6 +8122,12 @@ "plugin-error": "^0.1.2" }, "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, "arr-diff": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", @@ -8127,6 +8150,19 @@ "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", "dev": true }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, "extend-shallow": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", @@ -8154,6 +8190,12 @@ "arr-union": "^2.0.1", "extend-shallow": "^1.1.2" } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true } } }, @@ -8808,10 +8850,21 @@ "optional": true }, "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "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, + "requires": { + "has-flag": "^4.0.0" + }, + "dependencies": { + "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 + } + } }, "sver-compat": { "version": "1.5.0", diff --git a/package.json b/package.json index d776317acc..7e64ac8ffa 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "bcrypt": "^5.0.0", "chai": "^4.2.0", "chai-as-promised": "^7.0.0", - "chalk": "^1.1.3", + "chalk": "^4.1.1", "child-process-promise": "^2.2.1", "del": "^2.2.1", "eslint": "^6.8.0", From 9d87537b4c2dcb407a2c216aba28a10925713bab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 11:52:06 -0700 Subject: [PATCH 048/102] build(deps-dev): bump @microsoft/api-extractor from 7.11.2 to 7.15.2 (#1291) Bumps [@microsoft/api-extractor](https://github.com/microsoft/rushstack) from 7.11.2 to 7.15.2. - [Release notes](https://github.com/microsoft/rushstack/releases) - [Commits](https://github.com/microsoft/rushstack/compare/@microsoft/api-extractor_v7.11.2...@microsoft/api-extractor_v7.15.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 168 +++++++++++++++++++++++++++++++++------------- 1 file changed, 122 insertions(+), 46 deletions(-) diff --git a/package-lock.json b/package-lock.json index 230bcefe4e..11c7ced866 100644 --- a/package-lock.json +++ b/package-lock.json @@ -963,29 +963,42 @@ "dev": true }, "@microsoft/api-extractor": { - "version": "7.11.2", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.11.2.tgz", - "integrity": "sha512-iZPv22j9K02cbwIDblOgF1MxZG+KWovp3CQpWCD6UC/+YYO4DfLxX5uZYVNzfgT4vU8fN0rugJmGm85rHX6Ouw==", + "version": "7.15.2", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.15.2.tgz", + "integrity": "sha512-/Y/n+QOc1vM6Vg3OAUByT/wXdZciE7jV3ay33+vxl3aKva5cNsuOauL14T7XQWUiLko3ilPwrcnFcEjzXpLsuA==", "dev": true, "requires": { - "@microsoft/api-extractor-model": "7.10.8", - "@microsoft/tsdoc": "0.12.19", - "@rushstack/node-core-library": "3.34.7", - "@rushstack/rig-package": "0.2.7", - "@rushstack/ts-command-line": "4.7.6", + "@microsoft/api-extractor-model": "7.13.2", + "@microsoft/tsdoc": "0.13.2", + "@microsoft/tsdoc-config": "~0.15.2", + "@rushstack/node-core-library": "3.38.0", + "@rushstack/rig-package": "0.2.12", + "@rushstack/ts-command-line": "4.7.10", "colors": "~1.2.1", "lodash": "~4.17.15", "resolve": "~1.17.0", "semver": "~7.3.0", "source-map": "~0.6.1", - "typescript": "~4.0.5" + "typescript": "~4.2.4" }, "dependencies": { + "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, + "requires": { + "yallist": "^4.0.0" + } + }, "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } }, "source-map": { "version": "0.6.1", @@ -994,29 +1007,72 @@ "dev": true }, "typescript": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz", - "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", + "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true } } }, "@microsoft/api-extractor-model": { - "version": "7.10.8", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.10.8.tgz", - "integrity": "sha512-9TfiCTPnkUeLaYywZeg9rYbVPX9Tj6AAkO6ThnjSE0tTPLjMcL3RiHkqn0BJ4+aGcl56APwo32zj5+kG+NqxYA==", + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.13.2.tgz", + "integrity": "sha512-gA9Q8q5TPM2YYk7rLinAv9KqcodrmRC13BVmNzLswjtFxpz13lRh0BmrqD01/sddGpGMIuWFYlfUM4VSWxnggA==", "dev": true, "requires": { - "@microsoft/tsdoc": "0.12.19", - "@rushstack/node-core-library": "3.34.7" + "@microsoft/tsdoc": "0.13.2", + "@microsoft/tsdoc-config": "~0.15.2", + "@rushstack/node-core-library": "3.38.0" } }, "@microsoft/tsdoc": { - "version": "0.12.19", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.12.19.tgz", - "integrity": "sha512-IpgPxHrNxZiMNUSXqR1l/gePKPkfAmIKoDRP9hp7OwjU29ZR8WCJsOJ8iBKgw0Qk+pFwR+8Y1cy8ImLY6e9m4A==", + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz", + "integrity": "sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg==", "dev": true }, + "@microsoft/tsdoc-config": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.15.2.tgz", + "integrity": "sha512-mK19b2wJHSdNf8znXSMYVShAHktVr/ib0Ck2FA3lsVBSEhSI/TfXT7DJQkAYgcztTuwazGcg58ZjYdk0hTCVrA==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.13.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "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, + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + } + } + }, "@panva/asn1.js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz", @@ -1087,9 +1143,9 @@ "optional": true }, "@rushstack/node-core-library": { - "version": "3.34.7", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.34.7.tgz", - "integrity": "sha512-7FwJ0jmZsh7bDIZ1IqDNphY9Kc6aAi1D2K8jiq+da4flMyL84HNeq2KxvwFLzjLwu3eMr88X+oBpgxCTD5Y57Q==", + "version": "3.38.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.38.0.tgz", + "integrity": "sha512-cmvl0yQx8sSmbuXwiRYJi8TO+jpTtrLJQ8UmFHhKvgPVJAW8cV8dnpD1Xx/BvTGrJZ2XtRAIkAhBS9okBnap4w==", "dev": true, "requires": { "@types/node": "10.17.13", @@ -1120,31 +1176,42 @@ "universalify": "^0.1.0" } }, + "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, + "requires": { + "yallist": "^4.0.0" + } + }, "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true } } }, "@rushstack/rig-package": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.2.7.tgz", - "integrity": "sha512-hI1L0IIzCHqH/uW64mKqEQ0/MANA/IklVId3jGpj1kt9RJcBdeNUIlzDtHl437LZRAuEA8CyotRHzG6YDgWlTw==", + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.2.12.tgz", + "integrity": "sha512-nbePcvF8hQwv0ql9aeQxcaMPK/h1OLAC00W7fWCRWIvD2MchZOE8jumIIr66HGrfG2X1sw++m/ZYI4D+BM5ovQ==", "dev": true, "requires": { - "@types/node": "10.17.13", "resolve": "~1.17.0", "strip-json-comments": "~3.1.1" }, "dependencies": { - "@types/node": { - "version": "10.17.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz", - "integrity": "sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==", - "dev": true - }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -1154,9 +1221,9 @@ } }, "@rushstack/ts-command-line": { - "version": "4.7.6", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.7.6.tgz", - "integrity": "sha512-falJVNfpJtsL3gJaY77JXXycfzhzB9VkKhqEfjRWD69/f6ezMUorPR6Nc90MnIaWgePTcdTJPZibxOQrNpu1Uw==", + "version": "4.7.10", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.7.10.tgz", + "integrity": "sha512-8t042g8eerypNOEcdpxwRA3uCmz0duMo21rG4Z2mdz7JxJeylDmzjlU3wDdef2t3P1Z61JCdZB6fbm1Mh0zi7w==", "dev": true, "requires": { "@types/argparse": "1.0.38", @@ -2532,9 +2599,9 @@ } }, "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "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, "optional": true }, @@ -4950,6 +5017,15 @@ "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", "dev": true }, + "is-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", From 7afaf6c87b9ee1994b6a17dc6ee45dea213981b9 Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Mon, 24 May 2021 14:54:20 -0700 Subject: [PATCH 049/102] chore: Teporarily disabling sendToDeviceGroup integration test (#1292) --- test/integration/messaging.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/messaging.spec.ts b/test/integration/messaging.spec.ts index e67a81602b..d8703b5335 100644 --- a/test/integration/messaging.spec.ts +++ b/test/integration/messaging.spec.ts @@ -172,7 +172,7 @@ describe('admin.messaging', () => { }); }); - it('sendToDeviceGroup() returns a response with success count', () => { + xit('sendToDeviceGroup() returns a response with success count', () => { return admin.messaging().sendToDeviceGroup(notificationKey, payload, options) .then((response) => { expect(typeof response.successCount).to.equal('number'); From 1d2ff694ddbcab2effa46813def446aa26e58f67 Mon Sep 17 00:00:00 2001 From: Xin Li Date: Tue, 25 May 2021 14:18:32 -0700 Subject: [PATCH 050/102] feat(auth): Added code flow support for OIDC flow. (#1220) * OIDC codeflow support * improve configs to simulate the real cases * update for changes in signiture * resolve comments * improve validator logic * remove unnecessary logic * add tests and fix errors * add auth-api-request rests --- etc/firebase-admin.api.md | 8 ++ src/auth/auth-config.ts | 81 +++++++++++++++ src/auth/index.ts | 40 ++++++++ src/utils/error.ts | 8 ++ test/integration/auth.spec.ts | 76 ++++++++++---- test/unit/auth/auth-api-request.spec.ts | 71 +++++++++++++ test/unit/auth/auth-config.spec.ts | 129 ++++++++++++++++++++++++ 7 files changed, 396 insertions(+), 17 deletions(-) diff --git a/etc/firebase-admin.api.md b/etc/firebase-admin.api.md index 39c413b7ec..1b9e7aa407 100644 --- a/etc/firebase-admin.api.md +++ b/etc/firebase-admin.api.md @@ -241,15 +241,23 @@ export namespace auth { export interface MultiFactorUpdateSettings { enrolledFactors: UpdateMultiFactorInfoRequest[] | null; } + export interface OAuthResponseType { + code?: boolean; + idToken?: boolean; + } export interface OIDCAuthProviderConfig extends AuthProviderConfig { clientId: string; + clientSecret?: string; issuer: string; + responseType?: OAuthResponseType; } export interface OIDCUpdateAuthProviderRequest { clientId?: string; + clientSecret?: string; displayName?: string; enabled?: boolean; issuer?: string; + responseType?: OAuthResponseType; } export interface PhoneIdentifier { // (undocumented) diff --git a/src/auth/auth-config.ts b/src/auth/auth-config.ts index 26db94126e..059d866574 100644 --- a/src/auth/auth-config.ts +++ b/src/auth/auth-config.ts @@ -24,6 +24,7 @@ import MultiFactorConfigState = auth.MultiFactorConfigState; import AuthFactorType = auth.AuthFactorType; import EmailSignInProviderConfig = auth.EmailSignInProviderConfig; import OIDCAuthProviderConfig = auth.OIDCAuthProviderConfig; +import OAuthResponseType = auth.OAuthResponseType; import SAMLAuthProviderConfig = auth.SAMLAuthProviderConfig; /** A maximum of 10 test phone number / code pairs can be configured. */ @@ -75,6 +76,8 @@ export interface OIDCConfigServerRequest { issuer?: string; displayName?: string; enabled?: boolean; + clientSecret?: string; + responseType?: OAuthResponseType; [key: string]: any; } @@ -87,6 +90,8 @@ export interface OIDCConfigServerResponse { issuer?: string; displayName?: string; enabled?: boolean; + clientSecret?: string; + responseType?: OAuthResponseType; } /** The server side email configuration request interface. */ @@ -650,6 +655,8 @@ export class OIDCConfig implements OIDCAuthProviderConfig { public readonly providerId: string; public readonly issuer: string; public readonly clientId: string; + public readonly clientSecret?: string; + public readonly responseType: OAuthResponseType; /** * Converts a client side request to a OIDCConfigServerRequest which is the format @@ -676,6 +683,12 @@ export class OIDCConfig implements OIDCAuthProviderConfig { request.displayName = options.displayName; request.issuer = options.issuer; request.clientId = options.clientId; + if (typeof options.clientSecret !== 'undefined') { + request.clientSecret = options.clientSecret; + } + if (typeof options.responseType !== 'undefined') { + request.responseType = options.responseType; + } return request; } @@ -715,6 +728,12 @@ export class OIDCConfig implements OIDCAuthProviderConfig { providerId: true, clientId: true, issuer: true, + clientSecret: true, + responseType: true, + }; + const validResponseTypes = { + idToken: true, + code: true, }; if (!validator.isNonNullObject(options)) { throw new FirebaseAuthError( @@ -773,6 +792,59 @@ export class OIDCConfig implements OIDCAuthProviderConfig { '"OIDCAuthProviderConfig.displayName" must be a valid string.', ); } + if (typeof options.clientSecret !== 'undefined' && + !validator.isNonEmptyString(options.clientSecret)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + '"OIDCAuthProviderConfig.clientSecret" must be a valid string.', + ); + } + if (validator.isNonNullObject(options.responseType) && typeof options.responseType !== 'undefined') { + Object.keys(options.responseType).forEach((key) => { + if (!(key in validResponseTypes)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_CONFIG, + `"${key}" is not a valid OAuthResponseType parameter.`, + ); + } + }); + + const idToken = options.responseType.idToken; + if (typeof idToken !== 'undefined' && !validator.isBoolean(idToken)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, + '"OIDCAuthProviderConfig.responseType.idToken" must be a boolean.', + ); + } + + const code = options.responseType.code; + if (typeof code !== 'undefined') { + if (!validator.isBoolean(code)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, + '"OIDCAuthProviderConfig.responseType.code" must be a boolean.', + ); + } + + // If code flow is enabled, client secret must be provided. + if (code && typeof options.clientSecret === 'undefined') { + throw new FirebaseAuthError( + AuthClientErrorCode.MISSING_OAUTH_CLIENT_SECRET, + 'The OAuth configuration client secret is required to enable OIDC code flow.', + ); + } + } + + const allKeys = Object.keys(options.responseType).length; + const enabledCount = Object.values(options.responseType).filter(Boolean).length; + // Only one of OAuth response types can be set to true. + if (allKeys > 1 && enabledCount != 1) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_OAUTH_RESPONSETYPE, + 'Only exactly one OAuth responseType should be set to true.', + ); + } + } } /** @@ -806,6 +878,13 @@ export class OIDCConfig implements OIDCAuthProviderConfig { // When enabled is undefined, it takes its default value of false. this.enabled = !!response.enabled; this.displayName = response.displayName; + + if (typeof response.clientSecret !== 'undefined') { + this.clientSecret = response.clientSecret; + } + if (typeof response.responseType !== 'undefined') { + this.responseType = response.responseType; + } } /** @return {OIDCAuthProviderConfig} The plain object representation of the OIDCConfig. */ @@ -816,6 +895,8 @@ export class OIDCConfig implements OIDCAuthProviderConfig { providerId: this.providerId, issuer: this.issuer, clientId: this.clientId, + clientSecret: deepCopy(this.clientSecret), + responseType: deepCopy(this.responseType), }; } } diff --git a/src/auth/index.ts b/src/auth/index.ts index b1bc47f725..83fb622d67 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -1289,6 +1289,25 @@ export namespace auth { callbackURL?: string; } + /** + * The interface representing OIDC provider's response object for OAuth + * authorization flow. + * We need either of them to be true, there are two cases: + * If set code to true, then we are doing code flow. + * If set idToken to true, then we are doing idToken flow. + */ + export interface OAuthResponseType { + /** + * Whether ID token is returned from IdP's authorization endpoint. + */ + idToken?: boolean; + + /** + * Whether authorization code is returned from IdP's authorization endpoint. + */ + code?: boolean; + } + /** * The [OIDC](https://openid.net/specs/openid-connect-core-1_0-final.html) Auth * provider configuration interface. An OIDC provider can be created via @@ -1321,6 +1340,16 @@ export namespace auth { * [spec](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation). */ issuer: string; + + /** + * The OIDC provider's client secret to enable OIDC code flow. + */ + clientSecret?: string; + + /** + * The OIDC provider's response object for OAuth authorization flow. + */ + responseType?: OAuthResponseType; } /** @@ -1403,6 +1432,17 @@ export namespace auth { * configuration's value is not modified. */ issuer?: string; + + /** + * The OIDC provider's client secret to enable OIDC code flow. + * If not provided, the existing configuration's value is not modified. + */ + clientSecret?: string; + + /** + * The OIDC provider's response object for OAuth authorization flow. + */ + responseType?: OAuthResponseType; } /** diff --git a/src/utils/error.ts b/src/utils/error.ts index adc1b852b8..caa781e8f3 100644 --- a/src/utils/error.ts +++ b/src/utils/error.ts @@ -525,6 +525,10 @@ export class AuthClientErrorCode { code: 'invalid-provider-uid', message: 'The providerUid must be a valid provider uid string.', }; + public static INVALID_OAUTH_RESPONSETYPE = { + code: 'invalid-oauth-responsetype', + message: 'Only exactly one OAuth responseType should be set to true.', + }; public static INVALID_SESSION_COOKIE_DURATION = { code: 'invalid-session-cookie-duration', message: 'The session cookie duration must be a valid number in milliseconds ' + @@ -597,6 +601,10 @@ export class AuthClientErrorCode { code: 'missing-oauth-client-id', message: 'The OAuth/OIDC configuration client ID must not be empty.', }; + public static MISSING_OAUTH_CLIENT_SECRET = { + code: 'missing-oauth-client-secret', + message: 'The OAuth configuration client secret is required to enable OIDC code flow.', + }; public static MISSING_PROVIDER_ID = { code: 'missing-provider-id', message: 'A valid provider ID must be provided in the request.', diff --git a/test/integration/auth.spec.ts b/test/integration/auth.spec.ts index 29060b4cdd..075eacdca7 100644 --- a/test/integration/auth.spec.ts +++ b/test/integration/auth.spec.ts @@ -1334,12 +1334,21 @@ describe('admin.auth', () => { enabled: true, issuer: 'https://oidc.com/issuer1', clientId: 'CLIENT_ID1', + responseType: { + idToken: true, + code: false, + }, }; const modifiedConfigOptions = { displayName: 'OIDC_DISPLAY_NAME3', enabled: false, issuer: 'https://oidc.com/issuer3', clientId: 'CLIENT_ID3', + clientSecret: 'CLIENT_SECRET', + responseType: { + idToken: false, + code: true, + }, }; before(function() { @@ -1633,6 +1642,9 @@ describe('admin.auth', () => { enabled: true, issuer: 'https://oidc.com/issuer1', clientId: 'CLIENT_ID1', + responseType: { + idToken: true, + }, }; const authProviderConfig2 = { providerId: randomOidcProviderId(), @@ -1640,6 +1652,10 @@ describe('admin.auth', () => { enabled: true, issuer: 'https://oidc.com/issuer2', clientId: 'CLIENT_ID2', + clientSecret: 'CLIENT_SECRET', + responseType: { + code: true, + }, }; const removeTempConfigs = (): Promise => { @@ -1706,39 +1722,65 @@ describe('admin.auth', () => { }); }); - it('updateProviderConfig() successfully overwrites an OIDC config', () => { + it('updateProviderConfig() successfully partially modifies an OIDC config', () => { + const deltaChanges = { + displayName: 'OIDC_DISPLAY_NAME3', + enabled: false, + issuer: 'https://oidc.com/issuer3', + clientId: 'CLIENT_ID3', + clientSecret: 'CLIENT_SECRET', + responseType: { + idToken: false, + code: true, + }, + }; + // Only above fields should be modified. const modifiedConfigOptions = { + providerId: authProviderConfig1.providerId, displayName: 'OIDC_DISPLAY_NAME3', enabled: false, issuer: 'https://oidc.com/issuer3', clientId: 'CLIENT_ID3', + clientSecret: 'CLIENT_SECRET', + responseType: { + code: true, + }, }; - return admin.auth().updateProviderConfig(authProviderConfig1.providerId, modifiedConfigOptions) + return admin.auth().updateProviderConfig(authProviderConfig1.providerId, deltaChanges) .then((config) => { - const modifiedConfig = deepExtend( - { providerId: authProviderConfig1.providerId }, modifiedConfigOptions); - assertDeepEqualUnordered(modifiedConfig, config); + assertDeepEqualUnordered(modifiedConfigOptions, config); }); }); - it('updateProviderConfig() successfully partially modifies an OIDC config', () => { + it('updateProviderConfig() with invalid oauth response type should be rejected', () => { const deltaChanges = { displayName: 'OIDC_DISPLAY_NAME4', + enabled: false, issuer: 'https://oidc.com/issuer4', + clientId: 'CLIENT_ID4', + clientSecret: 'CLIENT_SECRET', + responseType: { + idToken: false, + code: false, + }, }; - // Only above fields should be modified. - const modifiedConfigOptions = { - displayName: 'OIDC_DISPLAY_NAME4', + return admin.auth().updateProviderConfig(authProviderConfig1.providerId, deltaChanges). + should.eventually.be.rejected.and.have.property('code', 'auth/invalid-oauth-responsetype'); + }); + + it('updateProviderConfig() code flow with no client secret should be rejected', () => { + const deltaChanges = { + displayName: 'OIDC_DISPLAY_NAME5', enabled: false, - issuer: 'https://oidc.com/issuer4', - clientId: 'CLIENT_ID3', + issuer: 'https://oidc.com/issuer5', + clientId: 'CLIENT_ID5', + responseType: { + idToken: false, + code: true, + }, }; - return admin.auth().updateProviderConfig(authProviderConfig1.providerId, deltaChanges) - .then((config) => { - const modifiedConfig = deepExtend( - { providerId: authProviderConfig1.providerId }, modifiedConfigOptions); - assertDeepEqualUnordered(modifiedConfig, config); - }); + return admin.auth().updateProviderConfig(authProviderConfig1.providerId, deltaChanges). + should.eventually.be.rejected.and.have.property('code', 'auth/missing-oauth-client-secret'); }); it('deleteProviderConfig() successfully deletes an existing OIDC config', () => { diff --git a/test/unit/auth/auth-api-request.spec.ts b/test/unit/auth/auth-api-request.spec.ts index 0ca9bd5a85..373982a6f6 100644 --- a/test/unit/auth/auth-api-request.spec.ts +++ b/test/unit/auth/auth-api-request.spec.ts @@ -3505,6 +3505,8 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const providerId = 'oidc.provider'; const path = handler.path('v2', `/oauthIdpConfigs?oauthIdpConfigId=${providerId}`, 'project_id'); const expectedHttpMethod = 'POST'; + const clientSecret = 'CLIENT_SECRET'; + const responseType = { code: true }; const configOptions = { providerId, displayName: 'OIDC_DISPLAY_NAME', @@ -3521,6 +3523,26 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const expectedResult = utils.responseFrom(deepExtend({ name: `projects/project1/oauthIdpConfigs/${providerId}`, }, expectedRequest)); + const expectedCodeFlowOptions = { + providerId, + displayName: 'OIDC_DISPLAY_NAME', + enabled: true, + clientId: 'CLIENT_ID', + issuer: 'https://oidc.com/issuer', + clientSecret, + responseType, + }; + const expectedCodeFlowRequest = { + displayName: 'OIDC_DISPLAY_NAME', + enabled: true, + clientId: 'CLIENT_ID', + issuer: 'https://oidc.com/issuer', + clientSecret, + responseType, + }; + const expectedCodeFlowResult = utils.responseFrom(deepExtend({ + name: `projects/project1/oauthIdpConfigs/${providerId}`, + }, expectedCodeFlowRequest)); it('should be fulfilled given valid parameters', () => { const stub = sinon.stub(HttpClient.prototype, 'send').resolves(expectedResult); @@ -3535,6 +3557,19 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); }); + it('should be fulfilled given valid parameters for OIDC code flow', () => { + const stub = sinon.stub(HttpClient.prototype, 'send').resolves(expectedCodeFlowResult); + stubs.push(stub); + + const requestHandler = handler.init(mockApp); + return requestHandler.createOAuthIdpConfig(expectedCodeFlowOptions) + .then((response) => { + expect(response).to.deep.equal(expectedCodeFlowResult.data); + expect(stub).to.have.been.calledOnce.and.calledWith( + callParams(path, expectedHttpMethod, expectedCodeFlowRequest)); + }); + }); + it('should be rejected given invalid parameters', () => { const expectedError = new FirebaseAuthError( AuthClientErrorCode.INVALID_CONFIG, @@ -3597,6 +3632,8 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const providerId = 'oidc.provider'; const path = handler.path('v2', `/oauthIdpConfigs/${providerId}`, 'project_id'); const expectedHttpMethod = 'PATCH'; + const clientSecret = 'CLIENT_SECRET'; + const responseType = { code: true }; const configOptions = { displayName: 'OIDC_DISPLAY_NAME', enabled: true, @@ -3620,6 +3657,26 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { clientId: 'NEW_CLIENT_ID', issuer: 'https://oidc.com/issuer2', })); + const expectedCodeFlowOptions = { + providerId, + displayName: 'OIDC_DISPLAY_NAME', + enabled: true, + clientId: 'CLIENT_ID', + issuer: 'https://oidc.com/issuer', + clientSecret, + responseType, + }; + const expectedCodeFlowRequest = { + displayName: 'OIDC_DISPLAY_NAME', + enabled: true, + clientId: 'CLIENT_ID', + issuer: 'https://oidc.com/issuer', + clientSecret, + responseType, + }; + const expectedCodeFlowResult = utils.responseFrom(deepExtend({ + name: `projects/project1/oauthIdpConfigs/${providerId}`, + }, expectedCodeFlowRequest)); it('should be fulfilled given full parameters', () => { const expectedPath = path + '?updateMask=enabled,displayName,issuer,clientId'; @@ -3635,6 +3692,20 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); }); + it('should be fulfilled given full parameters for OIDC code flow', () => { + const expectedPath = path + '?updateMask=enabled,displayName,issuer,clientId,clientSecret,responseType.code'; + const stub = sinon.stub(HttpClient.prototype, 'send').resolves(expectedCodeFlowResult); + stubs.push(stub); + + const requestHandler = handler.init(mockApp); + return requestHandler.updateOAuthIdpConfig(providerId, expectedCodeFlowOptions) + .then((response) => { + expect(response).to.deep.equal(expectedCodeFlowResult.data); + expect(stub).to.have.been.calledOnce.and.calledWith( + callParams(expectedPath, expectedHttpMethod, expectedCodeFlowRequest)); + }); + }); + it('should be fulfilled given partial parameters', () => { const expectedPath = path + '?updateMask=enabled,clientId'; const stub = sinon.stub(HttpClient.prototype, 'send').resolves(expectedPartialResult); diff --git a/test/unit/auth/auth-config.spec.ts b/test/unit/auth/auth-config.spec.ts index ad81c5e62c..d9e6ce4abc 100644 --- a/test/unit/auth/auth-config.spec.ts +++ b/test/unit/auth/auth-config.spec.ts @@ -727,6 +727,11 @@ describe('OIDCConfig', () => { issuer: 'https://oidc.com/issuer', displayName: 'oidcProviderName', enabled: true, + clientSecret: 'CLIENT_SECRET', + responseType: { + idToken: false, + code: true, + }, }; const serverResponse: OIDCConfigServerResponse = { name: 'projects/project_id/oauthIdpConfigs/oidc.provider', @@ -734,6 +739,10 @@ describe('OIDCConfig', () => { issuer: 'https://oidc.com/issuer', displayName: 'oidcProviderName', enabled: true, + clientSecret: 'CLIENT_SECRET', + responseType: { + code: true, + }, }; const clientRequest: OIDCAuthProviderConfig = { providerId: 'oidc.provider', @@ -741,6 +750,11 @@ describe('OIDCConfig', () => { issuer: 'https://oidc.com/issuer', displayName: 'oidcProviderName', enabled: true, + clientSecret: 'CLIENT_SECRET', + responseType: { + idToken: false, + code: true, + }, }; const config = new OIDCConfig(serverResponse); @@ -769,6 +783,21 @@ describe('OIDCConfig', () => { expect(config.enabled).to.be.true; }); + it('should set readonly property clientSecret', () => { + expect(config.clientSecret).to.equal('CLIENT_SECRET'); + }); + + it('should set readonly property expected responseType', () => { + expect(config.responseType).to.deep.equal({ code: true }); + }); + + it('should not throw on no responseType and clientSecret', () => { + const testResponse = deepCopy(serverResponse); + delete testResponse.clientSecret; + delete testResponse.responseType; + expect(() => new OIDCConfig(testResponse)).not.to.throw(); + }); + it('should throw on missing issuer', () => { const invalidResponse = deepCopy(serverResponse); delete invalidResponse.issuer; @@ -831,6 +860,10 @@ describe('OIDCConfig', () => { providerId: 'oidc.provider', issuer: 'https://oidc.com/issuer', clientId: 'CLIENT_ID', + clientSecret: 'CLIENT_SECRET', + responseType: { + code: true, + }, }); }); }); @@ -844,12 +877,22 @@ describe('OIDCConfig', () => { const updateRequest: OIDCUpdateAuthProviderRequest = { clientId: 'CLIENT_ID', displayName: 'OIDC_PROVIDER_DISPLAY_NAME', + clientSecret: 'CLIENT_SECRET', + responseType: { + idToken: false, + code: true, + } }; const updateServerRequest: OIDCConfigServerRequest = { clientId: 'CLIENT_ID', displayName: 'OIDC_PROVIDER_DISPLAY_NAME', issuer: undefined, enabled: undefined, + clientSecret: 'CLIENT_SECRET', + responseType: { + idToken: false, + code: true, + } }; expect(OIDCConfig.buildServerRequest(updateRequest, true)).to.deep.equal(updateServerRequest); }); @@ -892,6 +935,62 @@ describe('OIDCConfig', () => { expect(() => OIDCConfig.validate(partialRequest, true)).not.to.throw(); }); + it('should throw on OAuth responseType contains invalid parameters', () => { + const invalidRequest = deepCopy(clientRequest) as any; + invalidRequest.responseType.unknownField = true; + expect(() => OIDCConfig.validate(invalidRequest, true)) + .to.throw('"unknownField" is not a valid OAuthResponseType parameter.'); + }); + + it('should not throw when exactly one OAuth responseType is true', () => { + const validRequest = deepCopy(clientRequest) as any; + validRequest.responseType.code = false; + validRequest.responseType.idToken = true; + expect(() => OIDCConfig.validate(validRequest, true)).not.to.throw(); + }); + + it('should not throw when only idToken responseType is set to true', () => { + const validRequest = deepCopy(clientRequest) as any; + validRequest.responseType = { idToken: true }; + expect(() => OIDCConfig.validate(validRequest, true)).not.to.throw(); + }); + + it('should not throw when only code responseType is set to true', () => { + const validRequest = deepCopy(clientRequest) as any; + const validResponseType = { code: true }; + validRequest.responseType = validResponseType; + expect(() => OIDCConfig.validate(validRequest, true)).not.to.throw(); + }); + + it('should throw on two OAuth responseTypes set to true', () => { + const invalidRequest = deepCopy(clientRequest) as any; + invalidRequest.responseType.idToken = true; + invalidRequest.responseType.code = true; + expect(() => OIDCConfig.validate(invalidRequest, true)) + .to.throw('Only exactly one OAuth responseType should be set to true.'); + }); + + it('should throw on no OAuth responseType set to true', () => { + const invalidRequest = deepCopy(clientRequest) as any; + invalidRequest.responseType.idToken = false; + invalidRequest.responseType.code = false; + expect(() => OIDCConfig.validate(invalidRequest, true)) + .to.throw('Only exactly one OAuth responseType should be set to true.'); + }); + + it('should not throw when responseType is empty', () => { + const testRequest = deepCopy(clientRequest) as any; + testRequest.responseType = {}; + expect(() => OIDCConfig.validate(testRequest, true)).not.to.throw(); + }); + + it('should throw on no client secret when OAuth responseType code flow set to true', () => { + const invalidRequest = deepCopy(clientRequest) as any; + delete invalidRequest.clientSecret; + expect(() => OIDCConfig.validate(invalidRequest, true)) + .to.throw('The OAuth configuration client secret is required to enable OIDC code flow.'); + }); + const nonObjects = [null, NaN, 0, 1, true, false, '', 'a', [], [1, 'a'], _.noop]; nonObjects.forEach((request) => { it('should throw on non-null OIDCAuthProviderConfig object:' + JSON.stringify(request), () => { @@ -957,5 +1056,35 @@ describe('OIDCConfig', () => { .to.throw('"OIDCAuthProviderConfig.displayName" must be a valid string.'); }); }); + + const invalidClientSecrets = [null, NaN, 0, 1, true, false, '', [], [1, 'a'], {}, { a: 1 }, _.noop]; + invalidClientSecrets.forEach((invalidClientSecret) => { + it('should throw on invalid clientSecret:' + JSON.stringify(invalidClientSecret), () => { + const invalidClientRequest = deepCopy(clientRequest) as any; + invalidClientRequest.clientSecret = invalidClientSecret; + expect(() => OIDCConfig.validate(invalidClientRequest)) + .to.throw('"OIDCAuthProviderConfig.clientSecret" must be a valid string.'); + }); + }); + + const invalidOAuthResponseIdTokenBooleans = [null, NaN, 0, 1, 'invalid', '', [], [1, 'a'], {}, { a: 1 }, _.noop]; + invalidOAuthResponseIdTokenBooleans.forEach((invalidOAuthResponseIdTokenBoolean) => { + it('should throw on invalid responseType.idToken:' + JSON.stringify(invalidOAuthResponseIdTokenBoolean), () => { + const invalidClientRequest = deepCopy(clientRequest) as any; + invalidClientRequest.responseType.idToken = invalidOAuthResponseIdTokenBoolean; + expect(() => OIDCConfig.validate(invalidClientRequest)) + .to.throw('"OIDCAuthProviderConfig.responseType.idToken" must be a boolean.'); + }); + }); + + const invalidOAuthResponseCodeBooleans = [null, NaN, 0, 1, 'invalid', '', [], [1, 'a'], {}, { a: 1 }, _.noop]; + invalidOAuthResponseCodeBooleans.forEach((invalidOAuthResponseCodeBoolean) => { + it('should throw on invalid responseType.code:' + JSON.stringify(invalidOAuthResponseCodeBoolean), () => { + const invalidClientRequest = deepCopy(clientRequest) as any; + invalidClientRequest.responseType.code = invalidOAuthResponseCodeBoolean; + expect(() => OIDCConfig.validate(invalidClientRequest)) + .to.throw('"OIDCAuthProviderConfig.responseType.code" must be a boolean.'); + }); + }); }); }); From 03c66d822c23462cd3479f944f405da67cbf444e Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Wed, 26 May 2021 16:14:08 -0400 Subject: [PATCH 051/102] Update supported Node version to 10.13.0v (#1300) --- CONTRIBUTING.md | 2 +- README.md | 2 +- package-lock.json | 94 ++++++++++++----------------------------------- package.json | 2 +- 4 files changed, 27 insertions(+), 73 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7ac6a71cb1..a3ad29f277 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -87,7 +87,7 @@ information on using pull requests. ### Prerequisites -1. Node.js 10.10.0 or higher. +1. Node.js 10.13.0 or higher. 2. NPM 5 or higher (NPM 6 recommended). 3. Google Cloud SDK ([`gcloud`](https://cloud.google.com/sdk/downloads) utility) diff --git a/README.md b/README.md index 4e9b7df5e0..59a22770ff 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ requests, code review feedback, and also pull requests. ## Supported Environments -We support Node.js 10.10.0 and higher. +We support Node.js 10.13.0 and higher. Please also note that the Admin SDK should only be used in server-side/back-end environments controlled by the app developer. diff --git a/package-lock.json b/package-lock.json index 11c7ced866..a83343390f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -665,8 +665,7 @@ "@google-cloud/promisify": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz", - "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==", - "optional": true + "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==" }, "@google-cloud/storage": { "version": "5.3.0", @@ -730,8 +729,7 @@ "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "optional": true + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" }, "ansi-styles": { "version": "4.3.0", @@ -793,8 +791,7 @@ "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==", - "optional": true + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "locate-path": { "version": "5.0.0", @@ -839,7 +836,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "optional": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -850,7 +846,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "optional": true, "requires": { "ansi-regex": "^5.0.0" } @@ -1081,32 +1076,27 @@ "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=", - "optional": true + "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" }, "@protobufjs/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "optional": true + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" }, "@protobufjs/codegen": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "optional": true + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" }, "@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=", - "optional": true + "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" }, "@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", - "optional": true, "requires": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" @@ -1115,32 +1105,27 @@ "@protobufjs/float": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=", - "optional": true + "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" }, "@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=", - "optional": true + "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" }, "@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=", - "optional": true + "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" }, "@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=", - "optional": true + "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" }, "@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=", - "optional": true + "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" }, "@rushstack/node-core-library": { "version": "3.38.0", @@ -1409,8 +1394,7 @@ "@types/long": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", - "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==", - "optional": true + "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" }, "@types/mime": { "version": "1.3.2", @@ -1602,7 +1586,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "optional": true, "requires": { "event-target-shim": "^5.0.0" } @@ -1647,7 +1630,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.1.tgz", "integrity": "sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg==", - "optional": true, "requires": { "debug": "4" } @@ -2165,8 +2147,7 @@ "bignumber.js": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", - "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==", - "optional": true + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" }, "binary-extensions": { "version": "1.13.1", @@ -3444,8 +3425,7 @@ "event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "optional": true + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" }, "expand-brackets": { "version": "2.1.4", @@ -3663,8 +3643,7 @@ "fast-text-encoding": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz", - "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==", - "optional": true + "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==" }, "faye-websocket": { "version": "0.11.3", @@ -4041,7 +4020,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-3.0.3.tgz", "integrity": "sha512-PkzQludeIFhd535/yucALT/Wxyj/y2zLyrMwPcJmnLHDugmV49NvAi/vb+VUq/eWztATZCNcb8ue+ywPG+oLuw==", - "optional": true, "requires": { "abort-controller": "^3.0.0", "extend": "^3.0.2", @@ -4054,7 +4032,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.1.0.tgz", "integrity": "sha512-r57SV28+olVsflPlKyVig3Muo/VDlcsObMtvDGOEtEJXj+DDE8bEl0coIkXh//hbkSDTvo+f5lbihZOndYXQQQ==", - "optional": true, "requires": { "gaxios": "^3.0.0", "json-bigint": "^0.3.0" @@ -4285,7 +4262,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.0.3.tgz", "integrity": "sha512-2Np6ojPmaJGXHSMsBhtTQEKfSMdLc8hefoihv7N2cwEr8E5bq39fhoat6TcXHwa0XoGO5Guh9sp3nxHFPmibMw==", - "optional": true, "requires": { "arrify": "^2.0.0", "base64-js": "^1.3.0", @@ -4345,7 +4321,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.1.tgz", "integrity": "sha512-VlQgtozgNVVVcYTXS36eQz4PXPt9gIPqLOhHN0QiV6W6h4qSCNVKPtKC5INtJsaHHF2r7+nOIa26MJeJMTaZEQ==", - "optional": true, "requires": { "node-forge": "^0.9.0" }, @@ -4353,8 +4328,7 @@ "node-forge": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.2.tgz", - "integrity": "sha512-naKSScof4Wn+aoHU6HBsifh92Zeicm1GDQKd1vp3Y/kOi8ub0DozCa9KpvYNCXslFHYRmLNiqRopGdTGwNLpNw==", - "optional": true + "integrity": "sha512-naKSScof4Wn+aoHU6HBsifh92Zeicm1GDQKd1vp3Y/kOi8ub0DozCa9KpvYNCXslFHYRmLNiqRopGdTGwNLpNw==" } } }, @@ -4373,7 +4347,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.0.1.tgz", "integrity": "sha512-33w4FNDkUcyIOq/TqyC+drnKdI4PdXmWp9lZzssyEQKuvu9ZFN3KttaSnDKo52U3E51oujVGop93mKxmqO8HHg==", - "optional": true, "requires": { "gaxios": "^3.0.0", "google-p12-pem": "^3.0.0", @@ -4753,7 +4726,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "optional": true, "requires": { "agent-base": "6", "debug": "4" @@ -5502,7 +5474,6 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.1.tgz", "integrity": "sha512-DGWnSzmusIreWlEupsUelHrhwmPPE+FiQvg+drKfk2p+bdEYa5mp4PJ8JsCWqae0M2jQNb0HPvnwvf1qOTThzQ==", - "optional": true, "requires": { "bignumber.js": "^9.0.0" } @@ -5626,7 +5597,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", - "optional": true, "requires": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", @@ -5649,7 +5619,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", - "optional": true, "requires": { "jwa": "^2.0.0", "safe-buffer": "^5.0.1" @@ -5766,8 +5735,7 @@ "lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", - "optional": true + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" }, "lodash.clonedeep": { "version": "4.5.0", @@ -5921,14 +5889,12 @@ "long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", - "optional": true + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, "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==", - "optional": true, "requires": { "yallist": "^3.0.2" } @@ -6079,8 +6045,7 @@ "mime": { "version": "2.4.6", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", - "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", - "optional": true + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" }, "mime-db": { "version": "1.44.0", @@ -6719,8 +6684,7 @@ "node-fetch": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "optional": true + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, "node-forge": { "version": "0.10.0", @@ -7712,7 +7676,6 @@ "version": "6.10.1", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.1.tgz", "integrity": "sha512-pb8kTchL+1Ceg4lFd5XUpK8PdWacbvV5SK2ULH2ebrYtl4GjJmS24m6CKME67jzV53tbJxHlnNOSqQHbTsR9JQ==", - "optional": true, "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -7732,8 +7695,7 @@ "@types/node": { "version": "13.13.30", "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.30.tgz", - "integrity": "sha512-HmqFpNzp3TSELxU/bUuRK+xzarVOAsR00hzcvM0TXrMlt/+wcSLa5q6YhTb6/cA6wqDCZLDcfd8fSL95x5h7AA==", - "optional": true + "integrity": "sha512-HmqFpNzp3TSELxU/bUuRK+xzarVOAsR00hzcvM0TXrMlt/+wcSLa5q6YhTb6/cA6wqDCZLDcfd8fSL95x5h7AA==" } } }, @@ -7752,7 +7714,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "optional": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -7762,7 +7723,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", - "optional": true, "requires": { "duplexify": "^4.1.1", "inherits": "^2.0.3", @@ -7773,7 +7733,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", - "optional": true, "requires": { "end-of-stream": "^1.4.1", "inherits": "^2.0.3", @@ -7785,7 +7744,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "optional": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -8790,7 +8748,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", - "optional": true, "requires": { "stubs": "^3.0.0" } @@ -8922,8 +8879,7 @@ "stubs": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", - "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=", - "optional": true + "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=" }, "supports-color": { "version": "7.2.0", @@ -9309,7 +9265,6 @@ "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==", - "optional": true, "requires": { "is-typedarray": "^1.0.0" } @@ -9816,8 +9771,7 @@ "xdg-basedir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "optional": true + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" }, "xml-name-validator": { "version": "3.0.0", diff --git a/package.json b/package.json index 7e64ac8ffa..5628206dd1 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "license": "Apache-2.0", "homepage": "https://firebase.google.com/", "engines": { - "node": ">=10.10.0" + "node": ">=10.13.0" }, "scripts": { "build": "gulp build", From f914f928da7d72fc49b286d5bf6576cd1cb5a780 Mon Sep 17 00:00:00 2001 From: Xin Li Date: Wed, 26 May 2021 13:57:11 -0700 Subject: [PATCH 052/102] Fixed integration test failure of skipped tests (#1299) * Fix integration test failure of skipped testss * Trigger integration tests --- test/integration/auth.spec.ts | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/test/integration/auth.spec.ts b/test/integration/auth.spec.ts index 075eacdca7..55b78fa232 100644 --- a/test/integration/auth.spec.ts +++ b/test/integration/auth.spec.ts @@ -1336,10 +1336,9 @@ describe('admin.auth', () => { clientId: 'CLIENT_ID1', responseType: { idToken: true, - code: false, }, }; - const modifiedConfigOptions = { + const deltaChanges = { displayName: 'OIDC_DISPLAY_NAME3', enabled: false, issuer: 'https://oidc.com/issuer3', @@ -1350,6 +1349,17 @@ describe('admin.auth', () => { code: true, }, }; + const modifiedConfigOptions = { + providerId: authProviderConfig.providerId, + displayName: 'OIDC_DISPLAY_NAME3', + enabled: false, + issuer: 'https://oidc.com/issuer3', + clientId: 'CLIENT_ID3', + clientSecret: 'CLIENT_SECRET', + responseType: { + code: true, + }, + }; before(function() { if (!createdTenantId) { @@ -1378,12 +1388,10 @@ describe('admin.auth', () => { .then((config) => { assertDeepEqualUnordered(authProviderConfig, config); return tenantAwareAuth.updateProviderConfig( - authProviderConfig.providerId, modifiedConfigOptions); + authProviderConfig.providerId, deltaChanges); }) .then((config) => { - const modifiedConfig = deepExtend( - { providerId: authProviderConfig.providerId }, modifiedConfigOptions); - assertDeepEqualUnordered(modifiedConfig, config); + assertDeepEqualUnordered(modifiedConfigOptions, config); return tenantAwareAuth.deleteProviderConfig(authProviderConfig.providerId); }) .then(() => { From c52949612608c50bca574da5435efbff60102372 Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Wed, 26 May 2021 17:48:45 -0400 Subject: [PATCH 053/102] [chore] Release 9.9.0 (#1302) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5628206dd1..0b981dfe5c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-admin", - "version": "9.8.0", + "version": "9.9.0", "description": "Firebase admin SDK for Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", From 2ffc177c1d8c2b3a4cc5162089e8b80cb2b61e9d Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Thu, 27 May 2021 14:27:19 -0400 Subject: [PATCH 054/102] Update OIDC reference docs (#1305) --- src/auth/index.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/auth/index.ts b/src/auth/index.ts index 83fb622d67..d0ab8415ab 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -1292,9 +1292,11 @@ export namespace auth { /** * The interface representing OIDC provider's response object for OAuth * authorization flow. - * We need either of them to be true, there are two cases: - * If set code to true, then we are doing code flow. - * If set idToken to true, then we are doing idToken flow. + * One of the following settings is required: + *
    + *
  • Set code to true for the code flow.
  • + *
  • Set idToken to true for the ID token flow.
  • + *
*/ export interface OAuthResponseType { /** From f4a9a166d2e4c34eb37dd8280961f39f4e466d08 Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Thu, 27 May 2021 14:44:40 -0400 Subject: [PATCH 055/102] Add OAuthResponseType to ToC (#1303) --- docgen/content-sources/node/toc.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docgen/content-sources/node/toc.yaml b/docgen/content-sources/node/toc.yaml index aaf1eb562d..bc19ced9d6 100644 --- a/docgen/content-sources/node/toc.yaml +++ b/docgen/content-sources/node/toc.yaml @@ -64,6 +64,8 @@ toc: path: /docs/reference/admin/node/admin.auth.MultiFactorSettings - title: "MultiFactorUpdateSettings" path: /docs/reference/admin/node/admin.auth.MultiFactorUpdateSettings + - title: "OAuthResponseType" + path: /docs/reference/admin/node/admin.auth.OAuthResponseType - title: "OIDCAuthProviderConfig" path: /docs/reference/admin/node/admin.auth.OIDCAuthProviderConfig - title: "OIDCUpdateAuthProviderRequest" From 29271ad4586f7ba297ef6909d08264895f542d73 Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Thu, 27 May 2021 14:35:59 -0700 Subject: [PATCH 056/102] fix(auth): Better type hierarchies for Auth API (#1294) * fix(auth): Better type heirarchies for Auth API * fix: Moved factorId back to the base types * fix: Updated API report * fix: Fixed a grammar error in comment * fix: Update to comment text --- etc/firebase-admin.api.md | 35 +++++++++-------- src/auth/auth-api-request.ts | 13 +++++-- src/auth/index.ts | 50 +++++++++++++++++-------- test/unit/auth/auth-api-request.spec.ts | 6 --- 4 files changed, 62 insertions(+), 42 deletions(-) diff --git a/etc/firebase-admin.api.md b/etc/firebase-admin.api.md index 1b9e7aa407..b16b7127cb 100644 --- a/etc/firebase-admin.api.md +++ b/etc/firebase-admin.api.md @@ -113,11 +113,7 @@ export namespace auth { tenantManager(): TenantManager; } export type AuthFactorType = 'phone'; - export interface AuthProviderConfig { - displayName?: string; - enabled: boolean; - providerId: string; - } + export type AuthProviderConfig = SAMLAuthProviderConfig | OIDCAuthProviderConfig; export interface AuthProviderConfigFilter { maxResults?: number; pageToken?: string; @@ -151,11 +147,23 @@ export namespace auth { verifyIdToken(idToken: string, checkRevoked?: boolean): Promise; verifySessionCookie(sessionCookie: string, checkForRevocation?: boolean): Promise; } - export interface CreateMultiFactorInfoRequest { + export interface BaseAuthProviderConfig { + displayName?: string; + enabled: boolean; + providerId: string; + } + export interface BaseCreateMultiFactorInfoRequest { + displayName?: string; + factorId: string; + } + export interface BaseUpdateMultiFactorInfoRequest { displayName?: string; + enrollmentTime?: string; factorId: string; + uid?: string; } - export interface CreatePhoneMultiFactorInfoRequest extends CreateMultiFactorInfoRequest { + export type CreateMultiFactorInfoRequest = CreatePhoneMultiFactorInfoRequest; + export interface CreatePhoneMultiFactorInfoRequest extends BaseCreateMultiFactorInfoRequest { phoneNumber: string; } export interface CreateRequest extends UpdateRequest { @@ -245,7 +253,7 @@ export namespace auth { code?: boolean; idToken?: boolean; } - export interface OIDCAuthProviderConfig extends AuthProviderConfig { + export interface OIDCAuthProviderConfig extends BaseAuthProviderConfig { clientId: string; clientSecret?: string; issuer: string; @@ -272,7 +280,7 @@ export namespace auth { // (undocumented) providerUid: string; } - export interface SAMLAuthProviderConfig extends AuthProviderConfig { + export interface SAMLAuthProviderConfig extends BaseAuthProviderConfig { callbackURL?: string; idpEntityId: string; rpEntityId: string; @@ -323,13 +331,8 @@ export namespace auth { } // (undocumented) export type UpdateAuthProviderRequest = SAMLUpdateAuthProviderRequest | OIDCUpdateAuthProviderRequest; - export interface UpdateMultiFactorInfoRequest { - displayName?: string; - enrollmentTime?: string; - factorId: string; - uid?: string; - } - export interface UpdatePhoneMultiFactorInfoRequest extends UpdateMultiFactorInfoRequest { + export type UpdateMultiFactorInfoRequest = UpdatePhoneMultiFactorInfoRequest; + export interface UpdatePhoneMultiFactorInfoRequest extends BaseUpdateMultiFactorInfoRequest { phoneNumber: string; } export interface UpdateRequest { diff --git a/src/auth/auth-api-request.ts b/src/auth/auth-api-request.ts index bd4b2a5ce8..e7f429f811 100644 --- a/src/auth/auth-api-request.ts +++ b/src/auth/auth-api-request.ts @@ -1480,7 +1480,12 @@ export abstract class AbstractAuthRequestHandler { } // Build the signupNewUser request. - const request: any = deepCopy(properties); + type SignUpNewUserRequest = CreateRequest & { + photoUrl?: string | null; + localId?: string; + mfaInfo?: AuthFactorInfo[]; + }; + const request: SignUpNewUserRequest = deepCopy(properties); // Rewrite photoURL to photoUrl. if (typeof request.photoURL !== 'undefined') { request.photoUrl = request.photoURL; @@ -1496,14 +1501,14 @@ export abstract class AbstractAuthRequestHandler { if (validator.isNonEmptyArray(request.multiFactor.enrolledFactors)) { const mfaInfo: AuthFactorInfo[] = []; try { - request.multiFactor.enrolledFactors.forEach((multiFactorInfo: any) => { + request.multiFactor.enrolledFactors.forEach((multiFactorInfo) => { // Enrollment time and uid are not allowed for signupNewUser endpoint. // They will automatically be provisioned server side. - if (multiFactorInfo.enrollmentTime) { + if ('enrollmentTime' in multiFactorInfo) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_ARGUMENT, '"enrollmentTime" is not supported when adding second factors via "createUser()"'); - } else if (multiFactorInfo.uid) { + } else if ('uid' in multiFactorInfo) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_ARGUMENT, '"uid" is not supported when adding second factors via "createUser()"'); diff --git a/src/auth/index.ts b/src/auth/index.ts index d0ab8415ab..6193336beb 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -112,7 +112,7 @@ export namespace auth { } /** - * Interface representing the common properties of a user enrolled second factor. + * Interface representing the common properties of a user-enrolled second factor. */ export interface MultiFactorInfo { @@ -143,7 +143,7 @@ export namespace auth { } /** - * Interface representing a phone specific user enrolled second factor. + * Interface representing a phone specific user-enrolled second factor. */ export interface PhoneMultiFactorInfo extends MultiFactorInfo { @@ -336,10 +336,10 @@ export namespace auth { } /** - * Interface representing common properties of a user enrolled second factor + * Interface representing common properties of a user-enrolled second factor * for an `UpdateRequest`. */ - export interface UpdateMultiFactorInfoRequest { + export interface BaseUpdateMultiFactorInfoRequest { /** * The ID of the enrolled second factor. This ID is unique to the user. When not provided, @@ -364,10 +364,10 @@ export namespace auth { } /** - * Interface representing a phone specific user enrolled second factor + * Interface representing a phone specific user-enrolled second factor * for an `UpdateRequest`. */ - export interface UpdatePhoneMultiFactorInfoRequest extends UpdateMultiFactorInfoRequest { + export interface UpdatePhoneMultiFactorInfoRequest extends BaseUpdateMultiFactorInfoRequest { /** * The phone number associated with a phone second factor. @@ -375,6 +375,12 @@ export namespace auth { phoneNumber: string; } + /** + * Type representing the properties of a user-enrolled second factor + * for an `UpdateRequest`. + */ + export type UpdateMultiFactorInfoRequest = | UpdatePhoneMultiFactorInfoRequest; + /** * Interface representing the properties to update on the provided user. */ @@ -443,10 +449,10 @@ export namespace auth { } /** - * Interface representing base properties of a user enrolled second factor for a + * Interface representing base properties of a user-enrolled second factor for a * `CreateRequest`. */ - export interface CreateMultiFactorInfoRequest { + export interface BaseCreateMultiFactorInfoRequest { /** * The optional display name for an enrolled second factor. @@ -460,10 +466,10 @@ export namespace auth { } /** - * Interface representing a phone specific user enrolled second factor for a + * Interface representing a phone specific user-enrolled second factor for a * `CreateRequest`. */ - export interface CreatePhoneMultiFactorInfoRequest extends CreateMultiFactorInfoRequest { + export interface CreatePhoneMultiFactorInfoRequest extends BaseCreateMultiFactorInfoRequest { /** * The phone number associated with a phone second factor. @@ -471,6 +477,12 @@ export namespace auth { phoneNumber: string; } + /** + * Type representing the properties of a user-enrolled second factor + * for a `CreateRequest`. + */ + export type CreateMultiFactorInfoRequest = | CreatePhoneMultiFactorInfoRequest; + /** * Interface representing the properties to set on a new user record to be * created. @@ -1221,7 +1233,7 @@ export namespace auth { /** * The base Auth provider configuration interface. */ - export interface AuthProviderConfig { + export interface BaseAuthProviderConfig { /** * The provider ID defined by the developer. @@ -1249,7 +1261,7 @@ export namespace auth { * Auth provider configuration interface. A SAML provider can be created via * {@link auth.Auth.createProviderConfig `createProviderConfig()`}. */ - export interface SAMLAuthProviderConfig extends AuthProviderConfig { + export interface SAMLAuthProviderConfig extends BaseAuthProviderConfig { /** * The SAML IdP entity identifier. @@ -1301,7 +1313,7 @@ export namespace auth { export interface OAuthResponseType { /** * Whether ID token is returned from IdP's authorization endpoint. - */ + */ idToken?: boolean; /** @@ -1315,7 +1327,7 @@ export namespace auth { * provider configuration interface. An OIDC provider can be created via * {@link auth.Auth.createProviderConfig `createProviderConfig()`}. */ - export interface OIDCAuthProviderConfig extends AuthProviderConfig { + export interface OIDCAuthProviderConfig extends BaseAuthProviderConfig { /** * This is the required client ID used to confirm the audience of an OIDC @@ -1347,13 +1359,19 @@ export namespace auth { * The OIDC provider's client secret to enable OIDC code flow. */ clientSecret?: string; - + /** * The OIDC provider's response object for OAuth authorization flow. */ responseType?: OAuthResponseType; } + /** + * The Auth provider configuration type. + * {@link auth.Auth.createProviderConfig `createProviderConfig()`}. + */ + export type AuthProviderConfig = SAMLAuthProviderConfig | OIDCAuthProviderConfig; + /** * The request interface for updating a SAML Auth provider. This is used * when updating a SAML provider's configuration via @@ -1440,7 +1458,7 @@ export namespace auth { * If not provided, the existing configuration's value is not modified. */ clientSecret?: string; - + /** * The OIDC provider's response object for OAuth authorization flow. */ diff --git a/test/unit/auth/auth-api-request.spec.ts b/test/unit/auth/auth-api-request.spec.ts index 373982a6f6..29ccf6eb4d 100644 --- a/test/unit/auth/auth-api-request.spec.ts +++ b/test/unit/auth/auth-api-request.spec.ts @@ -1425,12 +1425,6 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { factorId: 'phone', enrollmentTime: new Date().toUTCString(), }, - { - uid: 'mfaUid2', - phoneNumber: '+16505550002', - displayName: 'Personal phone number', - factorId: 'phone', - }, ], }, customClaims: { admin: true }, From 5d5cea27cda1a4ce42bac04bba367dc477039b2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 10:03:27 -0700 Subject: [PATCH 057/102] build(deps-dev): bump nock from 13.0.5 to 13.0.11 (#1311) Bumps [nock](https://github.com/nock/nock) from 13.0.5 to 13.0.11. - [Release notes](https://github.com/nock/nock/releases) - [Changelog](https://github.com/nock/nock/blob/main/CHANGELOG.md) - [Commits](https://github.com/nock/nock/compare/v13.0.5...v13.0.11) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 101 +++++++++++++++++++++++++++++++++------------- 1 file changed, 73 insertions(+), 28 deletions(-) diff --git a/package-lock.json b/package-lock.json index a83343390f..fbe7d2dc9a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firebase-admin", - "version": "9.8.0", + "version": "9.9.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -665,7 +665,8 @@ "@google-cloud/promisify": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz", - "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==" + "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==", + "optional": true }, "@google-cloud/storage": { "version": "5.3.0", @@ -729,7 +730,8 @@ "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "optional": true }, "ansi-styles": { "version": "4.3.0", @@ -791,7 +793,8 @@ "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==" + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "optional": true }, "locate-path": { "version": "5.0.0", @@ -836,6 +839,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "optional": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -846,6 +850,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "optional": true, "requires": { "ansi-regex": "^5.0.0" } @@ -1076,27 +1081,32 @@ "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" + "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=", + "optional": true }, "@protobufjs/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "optional": true }, "@protobufjs/codegen": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "optional": true }, "@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" + "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=", + "optional": true }, "@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "optional": true, "requires": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" @@ -1105,27 +1115,32 @@ "@protobufjs/float": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" + "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=", + "optional": true }, "@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" + "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=", + "optional": true }, "@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" + "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=", + "optional": true }, "@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" + "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=", + "optional": true }, "@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" + "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=", + "optional": true }, "@rushstack/node-core-library": { "version": "3.38.0", @@ -1394,7 +1409,8 @@ "@types/long": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", - "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" + "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==", + "optional": true }, "@types/mime": { "version": "1.3.2", @@ -1586,6 +1602,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "optional": true, "requires": { "event-target-shim": "^5.0.0" } @@ -1630,6 +1647,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.1.tgz", "integrity": "sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg==", + "optional": true, "requires": { "debug": "4" } @@ -2147,7 +2165,8 @@ "bignumber.js": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", - "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==", + "optional": true }, "binary-extensions": { "version": "1.13.1", @@ -3425,7 +3444,8 @@ "event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "optional": true }, "expand-brackets": { "version": "2.1.4", @@ -3643,7 +3663,8 @@ "fast-text-encoding": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz", - "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==" + "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==", + "optional": true }, "faye-websocket": { "version": "0.11.3", @@ -4020,6 +4041,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-3.0.3.tgz", "integrity": "sha512-PkzQludeIFhd535/yucALT/Wxyj/y2zLyrMwPcJmnLHDugmV49NvAi/vb+VUq/eWztATZCNcb8ue+ywPG+oLuw==", + "optional": true, "requires": { "abort-controller": "^3.0.0", "extend": "^3.0.2", @@ -4032,6 +4054,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.1.0.tgz", "integrity": "sha512-r57SV28+olVsflPlKyVig3Muo/VDlcsObMtvDGOEtEJXj+DDE8bEl0coIkXh//hbkSDTvo+f5lbihZOndYXQQQ==", + "optional": true, "requires": { "gaxios": "^3.0.0", "json-bigint": "^0.3.0" @@ -4262,6 +4285,7 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.0.3.tgz", "integrity": "sha512-2Np6ojPmaJGXHSMsBhtTQEKfSMdLc8hefoihv7N2cwEr8E5bq39fhoat6TcXHwa0XoGO5Guh9sp3nxHFPmibMw==", + "optional": true, "requires": { "arrify": "^2.0.0", "base64-js": "^1.3.0", @@ -4321,6 +4345,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.1.tgz", "integrity": "sha512-VlQgtozgNVVVcYTXS36eQz4PXPt9gIPqLOhHN0QiV6W6h4qSCNVKPtKC5INtJsaHHF2r7+nOIa26MJeJMTaZEQ==", + "optional": true, "requires": { "node-forge": "^0.9.0" }, @@ -4328,7 +4353,8 @@ "node-forge": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.2.tgz", - "integrity": "sha512-naKSScof4Wn+aoHU6HBsifh92Zeicm1GDQKd1vp3Y/kOi8ub0DozCa9KpvYNCXslFHYRmLNiqRopGdTGwNLpNw==" + "integrity": "sha512-naKSScof4Wn+aoHU6HBsifh92Zeicm1GDQKd1vp3Y/kOi8ub0DozCa9KpvYNCXslFHYRmLNiqRopGdTGwNLpNw==", + "optional": true } } }, @@ -4347,6 +4373,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.0.1.tgz", "integrity": "sha512-33w4FNDkUcyIOq/TqyC+drnKdI4PdXmWp9lZzssyEQKuvu9ZFN3KttaSnDKo52U3E51oujVGop93mKxmqO8HHg==", + "optional": true, "requires": { "gaxios": "^3.0.0", "google-p12-pem": "^3.0.0", @@ -4726,6 +4753,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "optional": true, "requires": { "agent-base": "6", "debug": "4" @@ -5474,6 +5502,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.1.tgz", "integrity": "sha512-DGWnSzmusIreWlEupsUelHrhwmPPE+FiQvg+drKfk2p+bdEYa5mp4PJ8JsCWqae0M2jQNb0HPvnwvf1qOTThzQ==", + "optional": true, "requires": { "bignumber.js": "^9.0.0" } @@ -5597,6 +5626,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "optional": true, "requires": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", @@ -5619,6 +5649,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "optional": true, "requires": { "jwa": "^2.0.0", "safe-buffer": "^5.0.1" @@ -5735,7 +5766,8 @@ "lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", + "optional": true }, "lodash.clonedeep": { "version": "4.5.0", @@ -5889,12 +5921,14 @@ "long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "optional": true }, "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==", + "optional": true, "requires": { "yallist": "^3.0.2" } @@ -6045,7 +6079,8 @@ "mime": { "version": "2.4.6", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", - "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", + "optional": true }, "mime-db": { "version": "1.44.0", @@ -6664,9 +6699,9 @@ } }, "nock": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.5.tgz", - "integrity": "sha512-1ILZl0zfFm2G4TIeJFW0iHknxr2NyA+aGCMTjDVUsBY4CkMRispF1pfIYkTRdAR/3Bg+UzdEuK0B6HczMQZcCg==", + "version": "13.0.11", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.11.tgz", + "integrity": "sha512-sKZltNkkWblkqqPAsjYW0bm3s9DcHRPiMOyKO/PkfJ+ANHZ2+LA2PLe22r4lLrKgXaiSaDQwW3qGsJFtIpQIeQ==", "dev": true, "requires": { "debug": "^4.1.0", @@ -6684,7 +6719,8 @@ "node-fetch": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "optional": true }, "node-forge": { "version": "0.10.0", @@ -7676,6 +7712,7 @@ "version": "6.10.1", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.1.tgz", "integrity": "sha512-pb8kTchL+1Ceg4lFd5XUpK8PdWacbvV5SK2ULH2ebrYtl4GjJmS24m6CKME67jzV53tbJxHlnNOSqQHbTsR9JQ==", + "optional": true, "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -7695,7 +7732,8 @@ "@types/node": { "version": "13.13.30", "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.30.tgz", - "integrity": "sha512-HmqFpNzp3TSELxU/bUuRK+xzarVOAsR00hzcvM0TXrMlt/+wcSLa5q6YhTb6/cA6wqDCZLDcfd8fSL95x5h7AA==" + "integrity": "sha512-HmqFpNzp3TSELxU/bUuRK+xzarVOAsR00hzcvM0TXrMlt/+wcSLa5q6YhTb6/cA6wqDCZLDcfd8fSL95x5h7AA==", + "optional": true } } }, @@ -7714,6 +7752,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "optional": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -7723,6 +7762,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", + "optional": true, "requires": { "duplexify": "^4.1.1", "inherits": "^2.0.3", @@ -7733,6 +7773,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", + "optional": true, "requires": { "end-of-stream": "^1.4.1", "inherits": "^2.0.3", @@ -7744,6 +7785,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "optional": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -8748,6 +8790,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "optional": true, "requires": { "stubs": "^3.0.0" } @@ -8879,7 +8922,8 @@ "stubs": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", - "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=" + "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=", + "optional": true }, "supports-color": { "version": "7.2.0", @@ -9771,7 +9815,8 @@ "xdg-basedir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "optional": true }, "xml-name-validator": { "version": "3.0.0", From 09e82c046224adc5ff62c96f75aba4ba8f1d581f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 10:18:36 -0700 Subject: [PATCH 058/102] build(deps): bump ws from 7.3.1 to 7.4.6 (#1309) Bumps [ws](https://github.com/websockets/ws) from 7.3.1 to 7.4.6. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/7.3.1...7.4.6) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index fbe7d2dc9a..c3396276e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9807,9 +9807,9 @@ } }, "ws": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", - "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", "dev": true }, "xdg-basedir": { From 312cdbb4397973f034f5e9ae246f4163bbe7fbe7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 10:24:30 -0700 Subject: [PATCH 059/102] build(deps-dev): bump del from 2.2.2 to 6.0.0 (#1310) Bumps [del](https://github.com/sindresorhus/del) from 2.2.2 to 6.0.0. - [Release notes](https://github.com/sindresorhus/del/releases) - [Commits](https://github.com/sindresorhus/del/compare/v2.2.2...v6.0.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 266 +++++++++++++++++++++++++++++++++++----------- package.json | 2 +- 2 files changed, 207 insertions(+), 61 deletions(-) diff --git a/package-lock.json b/package-lock.json index c3396276e3..e8419efd36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1073,6 +1073,32 @@ } } }, + "@nodelib/fs.scandir": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", + "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.4", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", + "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", + "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.4", + "fastq": "^1.6.0" + } + }, "@panva/asn1.js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz", @@ -1945,18 +1971,9 @@ } }, "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, "array-unique": { @@ -2956,18 +2973,39 @@ } }, "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", + "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", "dev": true, "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "dependencies": { + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "delayed-stream": { @@ -3008,6 +3046,23 @@ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + }, + "dependencies": { + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + } + } + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -3648,6 +3703,73 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "fast-glob": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "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, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "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 + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "dependencies": { + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + } + } + }, + "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, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, "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", @@ -3666,6 +3788,15 @@ "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==", "optional": true }, + "fastq": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", + "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, "faye-websocket": { "version": "0.11.3", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", @@ -4251,23 +4382,23 @@ } }, "globby": { - "version": "5.0.0", - "resolved": "http://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "version": "11.0.3", + "resolved": "http://registry.npmjs.org/globby/-/globby-11.0.3.tgz", + "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", "dev": true, "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" }, "dependencies": { - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true } } @@ -5134,28 +5265,16 @@ "optional": true }, "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", "dev": true }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "requires": { - "is-path-inside": "^1.0.0" - } - }, "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true }, "is-plain-obj": { "version": "2.1.0", @@ -6055,6 +6174,12 @@ "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", "dev": true }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -7507,12 +7632,6 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, - "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 - }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", @@ -7806,6 +7925,12 @@ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -8172,6 +8297,12 @@ "through2": "^3.0.1" } }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -8187,6 +8318,15 @@ "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", "dev": true }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, "run-sequence": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/run-sequence/-/run-sequence-2.2.1.tgz", @@ -8444,6 +8584,12 @@ "integrity": "sha512-IifbusYiQBpUxxFJkR3wTU68xzBN0+bxCScEaKMjBvAQERg6FnTTc1F17rseLb1tjmkJ23730AXpFI0c47FgAg==", "dev": true }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, "slice-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", diff --git a/package.json b/package.json index 0b981dfe5c..478e0fe0c8 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "chai-as-promised": "^7.0.0", "chalk": "^4.1.1", "child-process-promise": "^2.2.1", - "del": "^2.2.1", + "del": "^6.0.0", "eslint": "^6.8.0", "firebase-token-generator": "^2.0.0", "gulp": "^4.0.2", From 278bfc9785e87292cd02817036330a3f3d87c8a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 10:40:48 -0700 Subject: [PATCH 060/102] build(deps-dev): bump @types/jsonwebtoken from 8.5.0 to 8.5.1 (#1315) Bumps [@types/jsonwebtoken](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jsonwebtoken) from 8.5.0 to 8.5.1. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jsonwebtoken) --- updated-dependencies: - dependency-name: "@types/jsonwebtoken" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e8419efd36..27852ad0be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1418,9 +1418,9 @@ "dev": true }, "@types/jsonwebtoken": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz", - "integrity": "sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-rNAPdomlIUX0i0cg2+I+Q1wOUr531zHBQ+cV/28PJ39bSPKjahatZZ2LMuhiguETkCgLVzfruw/ZvNMNkKoSzw==", "dev": true, "requires": { "@types/node": "*" From 2256f031adc2e28fa9899b5681171608f0473ac7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 10:50:10 -0700 Subject: [PATCH 061/102] build(deps-dev): bump nock from 13.0.11 to 13.1.0 (#1313) Bumps [nock](https://github.com/nock/nock) from 13.0.11 to 13.1.0. - [Release notes](https://github.com/nock/nock/releases) - [Changelog](https://github.com/nock/nock/blob/main/CHANGELOG.md) - [Commits](https://github.com/nock/nock/compare/v13.0.11...v13.1.0) --- updated-dependencies: - dependency-name: nock dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 27852ad0be..1f621c03e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6824,9 +6824,9 @@ } }, "nock": { - "version": "13.0.11", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.11.tgz", - "integrity": "sha512-sKZltNkkWblkqqPAsjYW0bm3s9DcHRPiMOyKO/PkfJ+ANHZ2+LA2PLe22r4lLrKgXaiSaDQwW3qGsJFtIpQIeQ==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.1.0.tgz", + "integrity": "sha512-3N3DUY8XYrxxzWazQ+nSBpiaJ3q6gcpNh4gXovC/QBxrsvNp4tq+wsLHF6mJ3nrn3lPLn7KCJqKxy/9aD+0fdw==", "dev": true, "requires": { "debug": "^4.1.0", From 2e6e5d330f5a85c0179e9203848f8b336e11a04f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 12:27:53 -0700 Subject: [PATCH 062/102] build(deps-dev): bump @types/sinon-chai from 3.2.4 to 3.2.5 (#1316) Bumps [@types/sinon-chai](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/sinon-chai) from 3.2.4 to 3.2.5. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/sinon-chai) --- updated-dependencies: - dependency-name: "@types/sinon-chai" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1f621c03e7..f153eee1ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1526,9 +1526,9 @@ } }, "@types/sinon-chai": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.4.tgz", - "integrity": "sha512-xq5KOWNg70PRC7dnR2VOxgYQ6paumW+4pTZP+6uTSdhpYsAUEeeT5bw6rRHHQrZ4KyR+M5ojOR+lje6TGSpUxA==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.5.tgz", + "integrity": "sha512-bKQqIpew7mmIGNRlxW6Zli/QVyc3zikpGzCa797B/tRnD9OtHvZ/ts8sYXV+Ilj9u3QRaUEM8xrjgd1gwm1BpQ==", "dev": true, "requires": { "@types/chai": "*", From 8548bb3765dc8e31bc3de8a065ba9f6e4825f942 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 10:01:17 -0700 Subject: [PATCH 063/102] build(deps-dev): bump bcrypt from 5.0.0 to 5.0.1 (#1324) Bumps [bcrypt](https://github.com/kelektiv/node.bcrypt.js) from 5.0.0 to 5.0.1. - [Release notes](https://github.com/kelektiv/node.bcrypt.js/releases) - [Changelog](https://github.com/kelektiv/node.bcrypt.js/blob/master/CHANGELOG.md) - [Commits](https://github.com/kelektiv/node.bcrypt.js/compare/v5.0.0...v5.0.1) --- updated-dependencies: - dependency-name: bcrypt dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 291 ++++++++++++++++++---------------------------- 1 file changed, 116 insertions(+), 175 deletions(-) diff --git a/package-lock.json b/package-lock.json index f153eee1ec..24064ee240 100644 --- a/package-lock.json +++ b/package-lock.json @@ -962,6 +962,49 @@ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, + "@mapbox/node-pre-gyp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz", + "integrity": "sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==", + "dev": true, + "requires": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.1", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "rimraf": "^3.0.2", + "semver": "^7.3.4", + "tar": "^6.1.0" + }, + "dependencies": { + "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, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, "@microsoft/api-extractor": { "version": "7.15.2", "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.15.2.tgz", @@ -2161,13 +2204,13 @@ "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" }, "bcrypt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.0.tgz", - "integrity": "sha512-jB0yCBl4W/kVHM2whjfyqnxTmOHkCX4kHEa5nYKSoGeYe8YrjTYTc87/6bwt1g8cmV0QrbhKriETg9jWtcREhg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.1.tgz", + "integrity": "sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw==", "dev": true, "requires": { - "node-addon-api": "^3.0.0", - "node-pre-gyp": "0.15.0" + "@mapbox/node-pre-gyp": "^1.0.0", + "node-addon-api": "^3.1.0" } }, "bcrypt-pbkdf": { @@ -2457,9 +2500,9 @@ } }, "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true }, "class-utils": { @@ -2870,12 +2913,6 @@ "type-detect": "^4.0.0" } }, - "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==", - "dev": true - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -4094,12 +4131,12 @@ } }, "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, "requires": { - "minipass": "^2.6.0" + "minipass": "^3.0.0" } }, "fs-mkdirp-stream": { @@ -4884,7 +4921,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "optional": true, "requires": { "agent-base": "6", "debug": "4" @@ -4911,15 +4947,6 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "dev": true, - "requires": { - "minimatch": "^3.0.4" - } - }, "import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", @@ -6241,22 +6268,38 @@ "dev": true }, "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", "dev": true, "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } } }, "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, "requires": { - "minipass": "^2.9.0" + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } } }, "mixin-deep": { @@ -6770,28 +6813,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "needle": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.0.tgz", - "integrity": "sha512-o/qITSDR0JCyCKEQ1/1bnUXMmznxabbwi/Y4WwJElf+evwJNFNwIDMCCt5IigFVxgeGBJESLohGtIS9gEzo1fA==", - "dev": true, - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, "neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -6836,48 +6857,21 @@ } }, "node-addon-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.0.0.tgz", - "integrity": "sha512-sSHCgWfJ+Lui/u+0msF3oyCgvdkhxDbkCS6Q8uiJquzOimkJBvX6hl5aSSA7DR1XbMpdM8r7phjcF63sF4rkKg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", "dev": true }, "node-fetch": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "optional": true + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, "node-forge": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" }, - "node-pre-gyp": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.15.0.tgz", - "integrity": "sha512-7QcZa8/fpaU/BKenjcaeFF9hLz2+7S9AqyXFhlH/rilsQ/hPZKK32RtR5EQHJElgu+q5RfbJ34KriI79UWaorA==", - "dev": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.3", - "needle": "^2.5.0", - "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.4.2" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, "node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", @@ -6900,13 +6894,12 @@ "dev": true }, "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", "dev": true, "requires": { - "abbrev": "1", - "osenv": "^0.1.4" + "abbrev": "1" } }, "normalize-package-data": { @@ -6947,32 +6940,6 @@ "once": "^1.3.2" } }, - "npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "dev": true, - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "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==", - "dev": true - }, - "npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "dev": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, "npm-run-all": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", @@ -7473,12 +7440,6 @@ "readable-stream": "^2.0.1" } }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, "os-locale": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", @@ -7494,16 +7455,6 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, "p-limit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", @@ -7940,18 +7891,6 @@ "safe-buffer": "^5.1.0" } }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", @@ -8304,9 +8243,9 @@ "dev": true }, "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" @@ -8444,12 +8383,6 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, "saxes": { "version": "3.1.11", "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", @@ -9059,12 +8992,6 @@ "is-utf8": "^0.2.0" } }, - "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=", - "dev": true - }, "stubs": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", @@ -9157,18 +9084,31 @@ } }, "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", + "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", "dev": true, "requires": { - "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" + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } } }, "teeny-request": { @@ -9996,7 +9936,8 @@ "yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "optional": true }, "yargs": { "version": "16.1.0", From eb04602acea5022c43ff3c8ad9fffbf91f02072c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 10:08:38 -0700 Subject: [PATCH 064/102] build(deps): bump @google-cloud/firestore from 4.5.0 to 4.12.2 (#1325) Bumps [@google-cloud/firestore](https://github.com/googleapis/nodejs-firestore) from 4.5.0 to 4.12.2. - [Release notes](https://github.com/googleapis/nodejs-firestore/releases) - [Changelog](https://github.com/googleapis/nodejs-firestore/blob/master/CHANGELOG.md) - [Commits](https://github.com/googleapis/nodejs-firestore/compare/v4.5.0...v4.12.2) --- updated-dependencies: - dependency-name: "@google-cloud/firestore" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 344 ++++++++++++++++++++++------------------------ 1 file changed, 161 insertions(+), 183 deletions(-) diff --git a/package-lock.json b/package-lock.json index 24064ee240..51138b75eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -636,14 +636,15 @@ } }, "@google-cloud/firestore": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-4.5.0.tgz", - "integrity": "sha512-sExt4E+TlBqyv4l/av6kBZ4YGS99Cc3P5UvLRNj9z41mT9ekPGhIzptbj4K6O7h0KCyDIDOiJdi4gUPH9lip4g==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-4.12.2.tgz", + "integrity": "sha512-5rurTAJXQ0SANEf8K9eA2JAB5zAh+pu4tGRnkZx5gBWQLZXdBFdtepS+irvKuSXw1KbeAQOuRANSc/nguys6SQ==", "optional": true, "requires": { "fast-deep-equal": "^3.1.1", "functional-red-black-tree": "^1.0.1", - "google-gax": "^2.2.0" + "google-gax": "^2.12.0", + "protobufjs": "^6.8.6" } }, "@google-cloud/paginator": { @@ -697,91 +698,42 @@ } }, "@grpc/grpc-js": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.1.8.tgz", - "integrity": "sha512-64hg5rmEm6F/NvlWERhHmmgxbWU8nD2TMWE+9TvG7/WcOrFT3fzg/Uu631pXRFwmJ4aWO/kp9vVSlr8FUjBDLA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.3.2.tgz", + "integrity": "sha512-UXepkOKCATJrhHGsxt+CGfpZy9zUn1q9mop5kfcXq1fBkTePxVNPOdnISlCbJFlCtld+pSLGyZCzr9/zVprFKA==", "optional": true, "requires": { - "@grpc/proto-loader": "^0.6.0-pre14", - "@types/node": "^12.12.47", - "google-auth-library": "^6.0.0", - "semver": "^6.2.0" + "@types/node": ">=12.12.47" + } + }, + "@grpc/proto-loader": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.2.tgz", + "integrity": "sha512-q2Qle60Ht2OQBCp9S5hv1JbI4uBBq6/mqSevFNK3ZEgRDBCAkWqZPUhD/K9gXOHrHKluliHiVq2L9sw1mVyAIg==", + "optional": true, + "requires": { + "@types/long": "^4.0.1", + "lodash.camelcase": "^4.3.0", + "long": "^4.0.0", + "protobufjs": "^6.10.0", + "yargs": "^16.1.1" }, "dependencies": { - "@grpc/proto-loader": { - "version": "0.6.0-pre9", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.0-pre9.tgz", - "integrity": "sha512-oM+LjpEjNzW5pNJjt4/hq1HYayNeQT+eGrOPABJnYHv7TyNPDNzkQ76rDYZF86X5swJOa4EujEMzQ9iiTdPgww==", - "optional": true, - "requires": { - "@types/long": "^4.0.1", - "lodash.camelcase": "^4.3.0", - "long": "^4.0.0", - "protobufjs": "^6.9.0", - "yargs": "^15.3.1" - } - }, - "@types/node": { - "version": "12.19.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.3.tgz", - "integrity": "sha512-8Jduo8wvvwDzEVJCOvS/G6sgilOLvvhn1eMmK3TW8/T217O7u1jdrK6ImKLv80tVryaPSVeKu6sjDEiFjd4/eg==", - "optional": true - }, "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "optional": true }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "optional": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "optional": true - }, "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "optional": true, "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "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==", - "optional": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "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==", - "optional": true - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "optional": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "wrap-ansi": "^7.0.0" } }, "get-caller-file": { @@ -796,49 +748,10 @@ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "optional": true }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "optional": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "optional": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "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==", - "optional": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "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==", - "optional": true - }, - "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==", - "optional": true - }, "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==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "optional": true, "requires": { "emoji-regex": "^8.0.0", @@ -855,16 +768,10 @@ "ansi-regex": "^5.0.0" } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "optional": true - }, "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==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "optional": true, "requires": { "ansi-styles": "^4.0.0", @@ -872,47 +779,35 @@ "strip-ansi": "^6.0.0" } }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "optional": true + }, "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "optional": true, "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "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" + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" } }, "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "optional": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "optional": true } } }, - "@grpc/proto-loader": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.5.tgz", - "integrity": "sha512-WwN9jVNdHRQoOBo9FDH7qU+mgfjPc8GygPYms3M+y3fbQLfnCe/Kv/E01t7JRgnrsOHH8euvSbed3mIalXhwqQ==", - "optional": true, - "requires": { - "lodash.camelcase": "^4.3.0", - "protobufjs": "^6.8.6" - } - }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1806,7 +1701,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "requires": { "color-convert": "^2.0.1" }, @@ -1815,7 +1709,6 @@ "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, "requires": { "color-name": "~1.1.4" } @@ -1823,8 +1716,7 @@ "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 + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" } } }, @@ -2896,7 +2788,8 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true }, "decode-uri-component": { "version": "0.2.0", @@ -3289,8 +3182,7 @@ "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, "escape-string-regexp": { "version": "1.0.5", @@ -4467,20 +4359,22 @@ } }, "google-gax": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.9.1.tgz", - "integrity": "sha512-KQ7HiMTB/PAzKv3OU00x6tC1H7MHvSxQfon5BSyW5o+lkMgRA8xoqvlxZCBC1dlW1azOPGF8vScy8QgFmhaQ9Q==", + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.14.1.tgz", + "integrity": "sha512-I5RDEN7MEptrCxeHX3ht7nKFGfyjgYX4hQKI9eVMBohMzVbFSwWUndo0CcKXu8es7NhB4gt2XYLm1AHkXhtHpA==", "optional": true, "requires": { - "@grpc/grpc-js": "~1.1.1", - "@grpc/proto-loader": "^0.5.1", + "@grpc/grpc-js": "~1.3.0", + "@grpc/proto-loader": "^0.6.1", "@types/long": "^4.0.0", "abort-controller": "^3.0.0", "duplexify": "^4.0.0", - "google-auth-library": "^6.0.0", + "fast-text-encoding": "^1.0.3", + "google-auth-library": "^7.0.2", "is-stream-ended": "^0.1.4", "node-fetch": "^2.6.1", - "protobufjs": "^6.9.0", + "object-hash": "^2.1.1", + "protobufjs": "^6.10.2", "retry-request": "^4.0.0" }, "dependencies": { @@ -4496,6 +4390,84 @@ "stream-shift": "^1.0.0" } }, + "gaxios": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.3.0.tgz", + "integrity": "sha512-pHplNbslpwCLMyII/lHPWFQbJWOX0B3R1hwBEOvzYi1GmdKZruuEHK4N9V6f7tf1EaPYyF80mui1+344p6SmLg==", + "optional": true, + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.3.0" + } + }, + "gcp-metadata": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz", + "integrity": "sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==", + "optional": true, + "requires": { + "gaxios": "^4.0.0", + "json-bigint": "^1.0.0" + } + }, + "google-auth-library": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.1.1.tgz", + "integrity": "sha512-+Q1linq/To3DYLyPz4UTEkQ0v5EOXadMM/S+taLV3W9611hq9zqg8kgGApqbTQnggtwdO9yU1y2YT7+83wdTRg==", + "optional": true, + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^4.0.0", + "gcp-metadata": "^4.2.0", + "gtoken": "^5.0.4", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + } + }, + "google-p12-pem": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz", + "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==", + "optional": true, + "requires": { + "node-forge": "^0.10.0" + } + }, + "gtoken": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.2.1.tgz", + "integrity": "sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw==", + "optional": true, + "requires": { + "gaxios": "^4.0.0", + "google-p12-pem": "^3.0.3", + "jws": "^4.0.0" + } + }, + "json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "optional": true, + "requires": { + "bignumber.js": "^9.0.0" + } + }, + "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==", + "optional": true, + "requires": { + "yallist": "^4.0.0" + } + }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -4506,6 +4478,12 @@ "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "optional": true } } }, @@ -7327,6 +7305,12 @@ } } }, + "object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "optional": true + }, "object-inspect": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", @@ -7779,9 +7763,9 @@ "dev": true }, "protobufjs": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.1.tgz", - "integrity": "sha512-pb8kTchL+1Ceg4lFd5XUpK8PdWacbvV5SK2ULH2ebrYtl4GjJmS24m6CKME67jzV53tbJxHlnNOSqQHbTsR9JQ==", + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", + "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", "optional": true, "requires": { "@protobufjs/aspromise": "^1.1.2", @@ -7795,16 +7779,8 @@ "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/long": "^4.0.1", - "@types/node": "^13.7.0", + "@types/node": ">=13.7.0", "long": "^4.0.0" - }, - "dependencies": { - "@types/node": { - "version": "13.13.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.30.tgz", - "integrity": "sha512-HmqFpNzp3TSELxU/bUuRK+xzarVOAsR00hzcvM0TXrMlt/+wcSLa5q6YhTb6/cA6wqDCZLDcfd8fSL95x5h7AA==", - "optional": true - } } }, "pseudomap": { @@ -8418,7 +8394,8 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true }, "set-value": { "version": "2.0.1", @@ -9931,7 +9908,8 @@ "y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true }, "yallist": { "version": "3.1.1", From 3c78a7365700947c9493ca5e4701e7155bb03651 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 10:16:13 -0700 Subject: [PATCH 065/102] build(deps-dev): bump @types/mocha from 2.2.48 to 8.2.2 (#1323) Bumps [@types/mocha](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/mocha) from 2.2.48 to 8.2.2. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/mocha) --- updated-dependencies: - dependency-name: "@types/mocha" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 51138b75eb..972c8cd674 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1394,9 +1394,9 @@ "dev": true }, "@types/mocha": { - "version": "2.2.48", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.48.tgz", - "integrity": "sha512-nlK/iyETgafGli8Zh9zJVCTicvU3iajSkRwOh3Hhiva598CMqNJ4NcVCGMTGKpGpTYj/9R8RLzS9NAykSSCqGw==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz", + "integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==", "dev": true }, "@types/nock": { diff --git a/package.json b/package.json index 478e0fe0c8..04aedf765b 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "@types/jsonwebtoken": "^8.5.0", "@types/lodash": "^4.14.104", "@types/minimist": "^1.2.0", - "@types/mocha": "^2.2.48", + "@types/mocha": "^8.2.2", "@types/nock": "^9.1.0", "@types/request": "^2.47.0", "@types/request-promise": "^4.1.41", From 2a4de46364cffc7d277d843df38ff65c970bee59 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jun 2021 06:58:33 -0700 Subject: [PATCH 066/102] build(deps-dev): bump @firebase/app from 0.6.21 to 0.6.26 (#1329) Bumps [@firebase/app](https://github.com/firebase/firebase-js-sdk/tree/HEAD/packages/app) from 0.6.21 to 0.6.26. - [Release notes](https://github.com/firebase/firebase-js-sdk/releases) - [Changelog](https://github.com/firebase/firebase-js-sdk/blob/@firebase/app@0.6.26/packages/app/CHANGELOG.md) - [Commits](https://github.com/firebase/firebase-js-sdk/commits/@firebase/app@0.6.26/packages/app) --- updated-dependencies: - dependency-name: "@firebase/app" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 972c8cd674..7a3b7c1358 100644 --- a/package-lock.json +++ b/package-lock.json @@ -460,13 +460,13 @@ } }, "@firebase/app": { - "version": "0.6.21", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.6.21.tgz", - "integrity": "sha512-SpWXdy/U06gTOEofSjhcsFGUtYmZim7ty6U4eMUQH0ObtymeVdTiK4tJcohMT5XoihQw4CLS2YvDySwx3+BlWg==", + "version": "0.6.26", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.6.26.tgz", + "integrity": "sha512-y4tpb+uiYLQC5+/AHBtIGZMaTjJ2BHQEsXmPqxyhfVFDzWMcXFsc//RVxA/0OejajhJR6GeqDcIS3m47mUD+Aw==", "dev": true, "requires": { "@firebase/app-types": "0.6.2", - "@firebase/component": "0.5.0", + "@firebase/component": "0.5.2", "@firebase/logger": "0.2.6", "@firebase/util": "1.1.0", "dom-storage": "2.1.0", @@ -475,9 +475,9 @@ }, "dependencies": { "tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", "dev": true } } @@ -508,9 +508,9 @@ "dev": true }, "@firebase/component": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.0.tgz", - "integrity": "sha512-v18csWtXb0ri+3m7wuGLY/UDgcb89vuMlZGQ//+7jEPLIQeLbylvZhol1uzW9WzoOpxMxOS2W5qyVGX36wZvEA==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.2.tgz", + "integrity": "sha512-QT+o6VaBCz/k8wmC/DErU9dQK2QeIoHtkBkryZVTSRkrvulglEWNIpbPp86UbuqZZd1wwzoh6m7BL6JbdEp9SQ==", "dev": true, "requires": { "@firebase/util": "1.1.0", @@ -518,9 +518,9 @@ }, "dependencies": { "tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", "dev": true } } @@ -586,9 +586,9 @@ }, "dependencies": { "tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", "dev": true } } From 24d3b91a163ab8d0c6adb12c89e449b4c898b153 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jun 2021 07:09:20 -0700 Subject: [PATCH 067/102] build(deps): bump @firebase/database from 0.10.0 to 0.10.4 (#1328) Bumps [@firebase/database](https://github.com/firebase/firebase-js-sdk/tree/HEAD/packages/database) from 0.10.0 to 0.10.4. - [Release notes](https://github.com/firebase/firebase-js-sdk/releases) - [Changelog](https://github.com/firebase/firebase-js-sdk/blob/master/packages/database/CHANGELOG.md) - [Commits](https://github.com/firebase/firebase-js-sdk/commits/@firebase/database@0.10.4/packages/database) --- updated-dependencies: - dependency-name: "@firebase/database" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 39 +++++++++------------------------------ 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7a3b7c1358..c5e365b466 100644 --- a/package-lock.json +++ b/package-lock.json @@ -511,7 +511,6 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.2.tgz", "integrity": "sha512-QT+o6VaBCz/k8wmC/DErU9dQK2QeIoHtkBkryZVTSRkrvulglEWNIpbPp86UbuqZZd1wwzoh6m7BL6JbdEp9SQ==", - "dev": true, "requires": { "@firebase/util": "1.1.0", "tslib": "^2.1.0" @@ -520,18 +519,17 @@ "tslib": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", - "dev": true + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" } } }, "@firebase/database": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.10.0.tgz", - "integrity": "sha512-GsHvuES83Edtboij2h3txKg+yV/TD4b5Owc01SgXEQtvj1lulkHt4Ufmd9OZz1WreWQJMIqKpbVowIDHjlkZJQ==", + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.10.4.tgz", + "integrity": "sha512-Mi6fJGzv9JH+GoYhgzSQAxsUhanW4jU6lqe/9kTyxNxHd+asphoJXJcKDs97uxRaowmSzu5LSAkGlWe63vJ7wA==", "requires": { "@firebase/auth-interop-types": "0.1.6", - "@firebase/component": "0.5.0", + "@firebase/component": "0.5.2", "@firebase/database-types": "0.7.2", "@firebase/logger": "0.2.6", "@firebase/util": "1.1.0", @@ -539,27 +537,10 @@ "tslib": "^2.1.0" }, "dependencies": { - "@firebase/component": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.0.tgz", - "integrity": "sha512-v18csWtXb0ri+3m7wuGLY/UDgcb89vuMlZGQ//+7jEPLIQeLbylvZhol1uzW9WzoOpxMxOS2W5qyVGX36wZvEA==", - "requires": { - "@firebase/util": "1.1.0", - "tslib": "^2.1.0" - } - }, - "@firebase/util": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.1.0.tgz", - "integrity": "sha512-lfuSASuPKNdfebuFR8rjFamMQUPH9iiZHcKS755Rkm/5gRT0qC7BMhCh3ZkHf7NVbplzIc/GhmX2jM+igDRCag==", - "requires": { - "tslib": "^2.1.0" - } - }, "tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" } } }, @@ -580,7 +561,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.1.0.tgz", "integrity": "sha512-lfuSASuPKNdfebuFR8rjFamMQUPH9iiZHcKS755Rkm/5gRT0qC7BMhCh3ZkHf7NVbplzIc/GhmX2jM+igDRCag==", - "dev": true, "requires": { "tslib": "^2.1.0" }, @@ -588,8 +568,7 @@ "tslib": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", - "dev": true + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" } } }, From f09bd6428483754bfbe4dcfe78b053fbc56d6635 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jun 2021 07:33:30 -0700 Subject: [PATCH 068/102] build(deps-dev): bump request-promise from 4.2.5 to 4.2.6 (#1331) Bumps [request-promise](https://github.com/request/request-promise) from 4.2.5 to 4.2.6. - [Release notes](https://github.com/request/request-promise/releases) - [Commits](https://github.com/request/request-promise/compare/v4.2.5...v4.2.6) --- updated-dependencies: - dependency-name: request-promise dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index c5e365b466..be2d0e85b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8059,17 +8059,26 @@ } }, "request-promise": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.5.tgz", - "integrity": "sha512-ZgnepCykFdmpq86fKGwqntyTiUrHycALuGggpyCZwMvGaZWgxW6yagT0FHkgo5LzYvOaCNvxYwWYIjevSH1EDg==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz", + "integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==", "dev": true, "requires": { "bluebird": "^3.5.0", - "request-promise-core": "1.1.3", + "request-promise-core": "1.1.4", "stealthy-require": "^1.1.1", "tough-cookie": "^2.3.3" }, "dependencies": { + "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, + "requires": { + "lodash": "^4.17.19" + } + }, "tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", From 9539a50d4f02437e820f73a461146b11d8fda219 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Jun 2021 17:08:00 -0700 Subject: [PATCH 069/102] build(deps-dev): bump gulp-filter from 6.0.0 to 7.0.0 (#1334) Bumps [gulp-filter](https://github.com/sindresorhus/gulp-filter) from 6.0.0 to 7.0.0. - [Release notes](https://github.com/sindresorhus/gulp-filter/releases) - [Commits](https://github.com/sindresorhus/gulp-filter/compare/v6.0.0...v7.0.0) --- updated-dependencies: - dependency-name: gulp-filter dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 43 ++++++++++++++++++------------------------- package.json | 2 +- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index be2d0e85b5..4658e77f30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1361,9 +1361,9 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==", "dev": true }, "@types/minimist": { @@ -1812,6 +1812,12 @@ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, + "array-differ": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", + "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", + "dev": true + }, "array-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", @@ -4586,14 +4592,15 @@ } }, "gulp-filter": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gulp-filter/-/gulp-filter-6.0.0.tgz", - "integrity": "sha512-veQFW93kf6jBdWdF/RxMEIlDK2mkjHyPftM381DID2C9ImTVngwYpyyThxm4/EpgcNOT37BLefzMOjEKbyYg0Q==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/gulp-filter/-/gulp-filter-7.0.0.tgz", + "integrity": "sha512-ZGWtJo0j1mHfP77tVuhyqem4MRA5NfNRjoVe6VAkLGeQQ/QGo2VsFwp7zfPTGDsd1rwzBmoDHhxpE6f5B3Zuaw==", "dev": true, "requires": { - "multimatch": "^4.0.0", + "multimatch": "^5.0.0", "plugin-error": "^1.0.1", - "streamfilter": "^3.0.0" + "streamfilter": "^3.0.0", + "to-absolute-glob": "^2.0.2" } }, "gulp-header": { @@ -6683,9 +6690,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "multimatch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-4.0.0.tgz", - "integrity": "sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz", + "integrity": "sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==", "dev": true, "requires": { "@types/minimatch": "^3.0.3", @@ -6693,20 +6700,6 @@ "array-union": "^2.1.0", "arrify": "^2.0.1", "minimatch": "^3.0.4" - }, - "dependencies": { - "array-differ": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", - "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - } } }, "mute-stdout": { diff --git a/package.json b/package.json index 04aedf765b..11b2c6ce63 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "eslint": "^6.8.0", "firebase-token-generator": "^2.0.0", "gulp": "^4.0.2", - "gulp-filter": "^6.0.0", + "gulp-filter": "^7.0.0", "gulp-header": "^2.0.9", "gulp-typescript": "^5.0.1", "http-message-parser": "^0.0.34", From 9b0d7ef581f19b618ac8b66dfec319e0ed7be87e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Jun 2021 17:32:20 -0700 Subject: [PATCH 070/102] build(deps-dev): bump @types/minimist from 1.2.0 to 1.2.1 (#1336) Bumps [@types/minimist](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/minimist) from 1.2.0 to 1.2.1. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/minimist) --- updated-dependencies: - dependency-name: "@types/minimist" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4658e77f30..d10b61f0f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1367,9 +1367,9 @@ "dev": true }, "@types/minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=", + "version": "1.2.1", + "resolved": "http://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", + "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", "dev": true }, "@types/mocha": { From 9872b9bcaf9af6e71f404caf10114155149da80d Mon Sep 17 00:00:00 2001 From: NothingEverHappens Date: Tue, 22 Jun 2021 14:45:32 -0400 Subject: [PATCH 071/102] fix(docs): replace all global.html -> admin.html (#1341) Co-authored-by: Hiranya Jayathilaka --- docgen/generate-docs.js | 1 + 1 file changed, 1 insertion(+) diff --git a/docgen/generate-docs.js b/docgen/generate-docs.js index 12f93706b7..619526dff8 100644 --- a/docgen/generate-docs.js +++ b/docgen/generate-docs.js @@ -117,6 +117,7 @@ function fixLinks(file) { return fs.readFile(file, 'utf8').then(data => { const flattenedLinks = data .replace(/\.\.\//g, '') + .replace(/globals\.html/g, 'admin.html') .replace(/(modules|interfaces|classes|enums)\//g, ''); let caseFixedLinks = flattenedLinks; for (const lower in lowerToUpperLookup) { From 0f9a7defd8873e630bb27374f4f3d29c90af59aa Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Wed, 23 Jun 2021 12:04:23 -0700 Subject: [PATCH 072/102] feat(fis): Adding the admin.installations() API for deleting Firebase installation IDs (#1187) * feat(fis): Added admin.installations() API * fix: Marked IID APIs deprecated * fix: Deprecated App.instanceId() method; Added more unit and integration tests * fix: Throwing FirebaseInstallationsError from constructor * fix: Some docs updates * fix: Minor update to API doc comment * fix: Added Installations class to API ref toc * fix: Minor doc updates --- docgen/content-sources/node/toc.yaml | 6 + etc/firebase-admin.api.md | 16 ++ src/firebase-app.ts | 15 ++ src/firebase-namespace-api.ts | 3 + src/firebase-namespace.d.ts | 1 + src/firebase-namespace.ts | 14 ++ src/installations/index.ts | 85 ++++++++ .../installations-request-handler.ts} | 42 ++-- src/installations/installations.ts | 68 +++++++ src/instance-id/index.ts | 22 +- src/instance-id/instance-id.ts | 26 ++- src/utils/error.ts | 34 +++- test/integration/installations.spec.ts | 31 +++ test/integration/instance-id.spec.ts | 2 +- test/unit/firebase-app.spec.ts | 28 +++ test/unit/firebase-namespace.spec.ts | 43 ++++ test/unit/index.spec.ts | 5 +- .../installations-request-handler.spec.ts} | 38 ++-- test/unit/installations/installations.spec.ts | 190 ++++++++++++++++++ test/unit/instance-id/instance-id.spec.ts | 23 ++- 20 files changed, 617 insertions(+), 75 deletions(-) create mode 100644 src/installations/index.ts rename src/{instance-id/instance-id-request-internal.ts => installations/installations-request-handler.ts} (71%) create mode 100644 src/installations/installations.ts create mode 100644 test/integration/installations.spec.ts rename test/unit/{instance-id/instance-id-request.spec.ts => installations/installations-request-handler.spec.ts} (72%) create mode 100644 test/unit/installations/installations.spec.ts diff --git a/docgen/content-sources/node/toc.yaml b/docgen/content-sources/node/toc.yaml index bc19ced9d6..fa5808c5f7 100644 --- a/docgen/content-sources/node/toc.yaml +++ b/docgen/content-sources/node/toc.yaml @@ -144,6 +144,12 @@ toc: - title: "admin.firestore" path: /docs/reference/admin/node/admin.firestore +- title: "admin.installations" + path: /docs/reference/admin/node/admin.installations + section: + - title: "Installations" + path: /docs/reference/admin/node/admin.installations.Installations-1 + - title: "admin.instanceId" path: /docs/reference/admin/node/admin.instanceId section: diff --git a/etc/firebase-admin.api.md b/etc/firebase-admin.api.md index b16b7127cb..6017b0c72a 100644 --- a/etc/firebase-admin.api.md +++ b/etc/firebase-admin.api.md @@ -25,6 +25,8 @@ export namespace app { // (undocumented) firestore(): firestore.Firestore; // (undocumented) + installations(): installations.Installations; + // @deprecated (undocumented) instanceId(): instanceId.InstanceId; // (undocumented) machineLearning(): machineLearning.MachineLearning; @@ -542,13 +544,27 @@ export interface GoogleOAuthAccessToken { export function initializeApp(options?: AppOptions, name?: string): app.App; // @public +export function installations(app?: app.App): installations.Installations; + +// @public (undocumented) +export namespace installations { + export interface Installations { + // (undocumented) + app: app.App; + deleteInstallation(fid: string): Promise; + } +} + +// @public @deprecated export function instanceId(app?: app.App): instanceId.InstanceId; // @public (undocumented) export namespace instanceId { + // @deprecated export interface InstanceId { // (undocumented) app: app.App; + // @deprecated deleteInstanceId(instanceId: string): Promise; } } diff --git a/src/firebase-app.ts b/src/firebase-app.ts index 84b30a52c5..29afbb75d2 100644 --- a/src/firebase-app.ts +++ b/src/firebase-app.ts @@ -31,6 +31,7 @@ import { database } from './database/index'; import { DatabaseService } from './database/database-internal'; import { Firestore } from '@google-cloud/firestore'; import { FirestoreService } from './firestore/firestore-internal'; +import { Installations } from './installations/installations'; import { InstanceId } from './instance-id/instance-id'; import { ProjectManagement } from './project-management/project-management'; import { SecurityRules } from './security-rules/security-rules'; @@ -256,9 +257,23 @@ export class FirebaseApp implements app.App { return service.client; } + /** + * Returns the `Installations` service instance associated with this app. + * + * @return The `Installations` service instance of this app. + */ + public installations(): Installations { + return this.ensureService_('installations', () => { + const fisService: typeof Installations = require('./installations/installations').Installations; + return new fisService(this); + }); + } + /** * Returns the InstanceId service instance associated with this app. * + * This API is deprecated. Use the `installations()` API instead. + * * @return The InstanceId service instance of this app. */ public instanceId(): InstanceId { diff --git a/src/firebase-namespace-api.ts b/src/firebase-namespace-api.ts index 6507fa3a88..509a2d1d18 100644 --- a/src/firebase-namespace-api.ts +++ b/src/firebase-namespace-api.ts @@ -20,6 +20,7 @@ import { auth } from './auth/index'; import { credential } from './credential/index'; import { database } from './database/index'; import { firestore } from './firestore/index'; +import { installations } from './installations/index'; import { instanceId } from './instance-id/index'; import { machineLearning } from './machine-learning/index'; import { messaging } from './messaging/index'; @@ -227,6 +228,8 @@ export namespace app { auth(): auth.Auth; database(url?: string): database.Database; firestore(): firestore.Firestore; + installations(): installations.Installations; + /** @deprecated */ instanceId(): instanceId.InstanceId; machineLearning(): machineLearning.MachineLearning; messaging(): messaging.Messaging; diff --git a/src/firebase-namespace.d.ts b/src/firebase-namespace.d.ts index ab013c3cac..bff64ccdf3 100644 --- a/src/firebase-namespace.d.ts +++ b/src/firebase-namespace.d.ts @@ -20,6 +20,7 @@ export * from './app-check/index'; export * from './auth/index'; export * from './database/index'; export * from './firestore/index'; +export * from './installations/index'; export * from './instance-id/index'; export * from './machine-learning/index'; export * from './messaging/index'; diff --git a/src/firebase-namespace.ts b/src/firebase-namespace.ts index fa1a409b48..311807301a 100644 --- a/src/firebase-namespace.ts +++ b/src/firebase-namespace.ts @@ -27,6 +27,7 @@ import { appCheck } from './app-check/index'; import { auth } from './auth/index'; import { database } from './database/index'; import { firestore } from './firestore/index'; +import { installations } from './installations/index'; import { instanceId } from './instance-id/index'; import { machineLearning } from './machine-learning/index'; import { messaging } from './messaging/index'; @@ -43,6 +44,7 @@ import AppCheck = appCheck.AppCheck; import Auth = auth.Auth; import Database = database.Database; import Firestore = firestore.Firestore; +import Installations = installations.Installations; import InstanceId = instanceId.InstanceId; import MachineLearning = machineLearning.MachineLearning; import Messaging = messaging.Messaging; @@ -311,6 +313,18 @@ export class FirebaseNamespace { return Object.assign(fn, { MachineLearning: machineLearning }); } + /** + * Gets the `Installations` service namespace. The returned namespace can be used to get the + * `Installations` service for the default app or an explicitly specified app. + */ + get installations(): FirebaseServiceNamespace { + const fn: FirebaseServiceNamespace = (app?: App) => { + return this.ensureApp(app).installations(); + }; + const installations = require('./installations/installations').Installations; + return Object.assign(fn, { Installations: installations }); + } + /** * Gets the `InstanceId` service namespace. The returned namespace can be used to get the * `Instance` service for the default app or an explicitly specified app. diff --git a/src/installations/index.ts b/src/installations/index.ts new file mode 100644 index 0000000000..373b904c9c --- /dev/null +++ b/src/installations/index.ts @@ -0,0 +1,85 @@ +/*! + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { app } from '../firebase-namespace-api'; + +/** + * Gets the {@link installations.Installations `Installations`} service for the + * default app or a given app. + * + * `admin.installations()` can be called with no arguments to access the default + * app's {@link installations.Installations `Installations`} service or as + * `admin.installations(app)` to access the + * {@link installations.Installations `Installations`} service associated with a + * specific app. + * + * @example + * ```javascript + * // Get the Installations service for the default app + * var defaultInstallations = admin.installations(); + * ``` + * + * @example + * ```javascript + * // Get the Installations service for a given app + * var otherInstallations = admin.installations(otherApp); + *``` + * + * @param app Optional app whose `Installations` service to + * return. If not provided, the default `Installations` service is + * returned. + * + * @return The default `Installations` service if + * no app is provided or the `Installations` service associated with the + * provided app. + */ +export declare function installations(app?: app.App): installations.Installations; + +/* eslint-disable @typescript-eslint/no-namespace */ +export namespace installations { + /** + * Gets the {@link Installations `Installations`} service for the + * current app. + * + * @example + * ```javascript + * var installations = app.installations(); + * // The above is shorthand for: + * // var installations = admin.installations(app); + * ``` + * + * @return The `Installations` service for the + * current app. + */ + export interface Installations { + app: app.App; + + /** + * Deletes the specified installation ID and the associated data from Firebase. + * + * Note that Google Analytics for Firebase uses its own form of Instance ID to + * keep track of analytics data. Therefore deleting a Firebase installation ID does + * not delete Analytics data. See + * [Delete a Firebase installation](/docs/projects/manage-installations#delete-installation) + * for more information. + * + * @param fid The Firebase installation ID to be deleted. + * + * @return A promise fulfilled when the installation ID is deleted. + */ + deleteInstallation(fid: string): Promise; + } +} diff --git a/src/instance-id/instance-id-request-internal.ts b/src/installations/installations-request-handler.ts similarity index 71% rename from src/instance-id/instance-id-request-internal.ts rename to src/installations/installations-request-handler.ts index e0d404d602..299fd3136e 100644 --- a/src/instance-id/instance-id-request-internal.ts +++ b/src/installations/installations-request-handler.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2021 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ */ import { FirebaseApp } from '../firebase-app'; -import { FirebaseInstanceIdError, InstanceIdClientErrorCode } from '../utils/error'; +import { FirebaseInstallationsError, InstallationsClientErrorCode } from '../utils/error'; import { ApiSettings, AuthorizedHttpClient, HttpRequestConfig, HttpError, } from '../utils/api-request'; @@ -33,10 +33,10 @@ const FIREBASE_IID_TIMEOUT = 10000; /** HTTP error codes raised by the backend server. */ const ERROR_CODES: {[key: number]: string} = { - 400: 'Malformed instance ID argument.', + 400: 'Malformed installation ID argument.', 401: 'Request not authorized.', - 403: 'Project does not match instance ID or the client does not have sufficient privileges.', - 404: 'Failed to find the instance ID.', + 403: 'Project does not match installation ID or the client does not have sufficient privileges.', + 404: 'Failed to find the installation ID.', 409: 'Already deleted.', 429: 'Request throttled out by the backend server.', 500: 'Internal server error.', @@ -44,9 +44,9 @@ const ERROR_CODES: {[key: number]: string} = { }; /** - * Class that provides mechanism to send requests to the Firebase Instance ID backend endpoints. + * Class that provides mechanism to send requests to the FIS backend endpoints. */ -export class FirebaseInstanceIdRequestHandler { +export class FirebaseInstallationsRequestHandler { private readonly host: string = FIREBASE_IID_HOST; private readonly timeout: number = FIREBASE_IID_TIMEOUT; @@ -54,7 +54,7 @@ export class FirebaseInstanceIdRequestHandler { private path: string; /** - * @param {FirebaseApp} app The app used to fetch access tokens to sign API requests. + * @param app The app used to fetch access tokens to sign API requests. * * @constructor */ @@ -62,21 +62,21 @@ export class FirebaseInstanceIdRequestHandler { this.httpClient = new AuthorizedHttpClient(app); } - public deleteInstanceId(instanceId: string): Promise { - if (!validator.isNonEmptyString(instanceId)) { - return Promise.reject(new FirebaseInstanceIdError( - InstanceIdClientErrorCode.INVALID_INSTANCE_ID, - 'Instance ID must be a non-empty string.', + public deleteInstallation(fid: string): Promise { + if (!validator.isNonEmptyString(fid)) { + return Promise.reject(new FirebaseInstallationsError( + InstallationsClientErrorCode.INVALID_INSTALLATION_ID, + 'Installation ID must be a non-empty string.', )); } - return this.invokeRequestHandler(new ApiSettings(instanceId, 'DELETE')); + return this.invokeRequestHandler(new ApiSettings(fid, 'DELETE')); } /** * Invokes the request handler based on the API settings object passed. * - * @param {ApiSettings} apiSettings The API endpoint settings to apply to request and response. - * @return {Promise} A promise that resolves when the request is complete. + * @param apiSettings The API endpoint settings to apply to request and response. + * @return A promise that resolves when the request is complete. */ private invokeRequestHandler(apiSettings: ApiSettings): Promise { return this.getPathPrefix() @@ -98,8 +98,8 @@ export class FirebaseInstanceIdRequestHandler { response.data.error : response.text; const template: string = ERROR_CODES[response.status]; const message: string = template ? - `Instance ID "${apiSettings.getEndpoint()}": ${template}` : errorMessage; - throw new FirebaseInstanceIdError(InstanceIdClientErrorCode.API_ERROR, message); + `Installation ID "${apiSettings.getEndpoint()}": ${template}` : errorMessage; + throw new FirebaseInstallationsError(InstallationsClientErrorCode.API_ERROR, message); } // In case of timeouts and other network errors, the HttpClient returns a // FirebaseError wrapped in the response. Simply throw it here. @@ -116,9 +116,9 @@ export class FirebaseInstanceIdRequestHandler { .then((projectId) => { if (!validator.isNonEmptyString(projectId)) { // Assert for an explicit projct ID (either via AppOptions or the cert itself). - throw new FirebaseInstanceIdError( - InstanceIdClientErrorCode.INVALID_PROJECT_ID, - 'Failed to determine project ID for InstanceId. Initialize the ' + throw new FirebaseInstallationsError( + InstallationsClientErrorCode.INVALID_PROJECT_ID, + 'Failed to determine project ID for Installations. Initialize the ' + 'SDK with service account credentials or set project ID as an app option. ' + 'Alternatively set the GOOGLE_CLOUD_PROJECT environment variable.', ); diff --git a/src/installations/installations.ts b/src/installations/installations.ts new file mode 100644 index 0000000000..a55e5af295 --- /dev/null +++ b/src/installations/installations.ts @@ -0,0 +1,68 @@ +/*! + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { FirebaseApp } from '../firebase-app'; +import { FirebaseInstallationsError, InstallationsClientErrorCode } from '../utils/error'; +import { FirebaseInstallationsRequestHandler } from './installations-request-handler'; +import { installations } from './index'; +import * as validator from '../utils/validator'; + +import InstallationsInterface = installations.Installations; + +/** + * The `Installations` service for the current app. + */ +export class Installations implements InstallationsInterface { + + private app_: FirebaseApp; + private requestHandler: FirebaseInstallationsRequestHandler; + + /** + * @param app The app for this Installations service. + * @constructor + */ + constructor(app: FirebaseApp) { + if (!validator.isNonNullObject(app) || !('options' in app)) { + throw new FirebaseInstallationsError( + InstallationsClientErrorCode.INVALID_ARGUMENT, + 'First argument passed to admin.installations() must be a valid Firebase app instance.', + ); + } + + this.app_ = app; + this.requestHandler = new FirebaseInstallationsRequestHandler(app); + } + + /** + * Deletes the specified installation ID and the associated data from Firebase. + * + * @param fid The Firebase installation ID to be deleted. + * + * @return A promise fulfilled when the installation ID is deleted. + */ + public deleteInstallation(fid: string): Promise { + return this.requestHandler.deleteInstallation(fid); + } + + /** + * Returns the app associated with this Installations instance. + * + * @return The app associated with this Installations instance. + */ + get app(): FirebaseApp { + return this.app_; + } +} diff --git a/src/instance-id/index.ts b/src/instance-id/index.ts index 09cbfe4022..a5ea77eb8b 100644 --- a/src/instance-id/index.ts +++ b/src/instance-id/index.ts @@ -38,6 +38,9 @@ import { app } from '../firebase-namespace-api'; * var otherInstanceId = admin.instanceId(otherApp); *``` * + * This API is deprecated. Developers are advised to use the `admin.installations()` + * API to delete their instance IDs and Firebase installation IDs. + * * @param app Optional app whose `InstanceId` service to * return. If not provided, the default `InstanceId` service will be * returned. @@ -45,24 +48,18 @@ import { app } from '../firebase-namespace-api'; * @return The default `InstanceId` service if * no app is provided or the `InstanceId` service associated with the * provided app. + * + * @deprecated */ export declare function instanceId(app?: app.App): instanceId.InstanceId; /* eslint-disable @typescript-eslint/no-namespace */ export namespace instanceId { /** - * Gets the {@link InstanceId `InstanceId`} service for the + * The {@link InstanceId `InstanceId`} service for the * current app. * - * @example - * ```javascript - * var instanceId = app.instanceId(); - * // The above is shorthand for: - * // var instanceId = admin.instanceId(app); - * ``` - * - * @return The `InstanceId` service for the - * current app. + * @deprecated */ export interface InstanceId { app: app.App; @@ -76,9 +73,14 @@ export namespace instanceId { * [Delete an Instance ID](/support/privacy/manage-iids#delete_an_instance_id) * for more information. * + * This API is deprecated. Developers are advised to use the `Installations.deleteInstallation()` + * API instead. + * * @param instanceId The instance ID to be deleted. * * @return A promise fulfilled when the instance ID is deleted. + * + * @deprecated */ deleteInstanceId(instanceId: string): Promise; } diff --git a/src/instance-id/instance-id.ts b/src/instance-id/instance-id.ts index 80afaeae48..80937ffc0c 100644 --- a/src/instance-id/instance-id.ts +++ b/src/instance-id/instance-id.ts @@ -15,8 +15,9 @@ */ import { FirebaseApp } from '../firebase-app'; -import { FirebaseInstanceIdError, InstanceIdClientErrorCode } from '../utils/error'; -import { FirebaseInstanceIdRequestHandler } from './instance-id-request-internal'; +import { + FirebaseInstallationsError, FirebaseInstanceIdError, InstallationsClientErrorCode, InstanceIdClientErrorCode, +} from '../utils/error'; import { instanceId } from './index'; import * as validator from '../utils/validator'; @@ -39,10 +40,9 @@ import InstanceIdInterface = instanceId.InstanceId; export class InstanceId implements InstanceIdInterface { private app_: FirebaseApp; - private requestHandler: FirebaseInstanceIdRequestHandler; /** - * @param {FirebaseApp} app The app for this InstanceId service. + * @param app The app for this InstanceId service. * @constructor */ constructor(app: FirebaseApp) { @@ -54,7 +54,6 @@ export class InstanceId implements InstanceIdInterface { } this.app_ = app; - this.requestHandler = new FirebaseInstanceIdRequestHandler(app); } /** @@ -71,16 +70,25 @@ export class InstanceId implements InstanceIdInterface { * @return A promise fulfilled when the instance ID is deleted. */ public deleteInstanceId(instanceId: string): Promise { - return this.requestHandler.deleteInstanceId(instanceId) - .then(() => { - // Return nothing on success + return this.app.installations().deleteInstallation(instanceId) + .catch((err) => { + if (err instanceof FirebaseInstallationsError) { + let code = err.code.replace('installations/', ''); + if (code === InstallationsClientErrorCode.INVALID_INSTALLATION_ID.code) { + code = InstanceIdClientErrorCode.INVALID_INSTANCE_ID.code; + } + + throw new FirebaseInstanceIdError({ code, message: err.message }); + } + + throw err; }); } /** * Returns the app associated with this InstanceId instance. * - * @return {FirebaseApp} The app associated with this InstanceId instance. + * @return The app associated with this InstanceId instance. */ get app(): FirebaseApp { return this.app_; diff --git a/src/utils/error.ts b/src/utils/error.ts index caa781e8f3..fcec6d5fd3 100644 --- a/src/utils/error.ts +++ b/src/utils/error.ts @@ -224,6 +224,23 @@ export class FirebaseInstanceIdError extends FirebaseError { constructor(info: ErrorInfo, message?: string) { // Override default message if custom message provided. super({ code: 'instance-id/' + info.code, message: message || info.message }); + (this as any).__proto__ = FirebaseInstanceIdError.prototype; + } +} + +/** + * Firebase Installations service error code structure. This extends `FirebaseError`. + * + * @param info The error code info. + * @param message The error message. This will override the default + * message if provided. + * @constructor + */ +export class FirebaseInstallationsError extends FirebaseError { + constructor(info: ErrorInfo, message?: string) { + // Override default message if custom message provided. + super({ code: 'installations/' + info.code, message: message || info.message }); + (this as any).__proto__ = FirebaseInstallationsError.prototype; } } @@ -808,7 +825,7 @@ export class MessagingClientErrorCode { }; } -export class InstanceIdClientErrorCode { +export class InstallationsClientErrorCode { public static INVALID_ARGUMENT = { code: 'invalid-argument', message: 'Invalid argument provided.', @@ -817,13 +834,20 @@ export class InstanceIdClientErrorCode { code: 'invalid-project-id', message: 'Invalid project ID provided.', }; - public static INVALID_INSTANCE_ID = { - code: 'invalid-instance-id', - message: 'Invalid instance ID provided.', + public static INVALID_INSTALLATION_ID = { + code: 'invalid-installation-id', + message: 'Invalid installation ID provided.', }; public static API_ERROR = { code: 'api-error', - message: 'Instance ID API call failed.', + message: 'Installation ID API call failed.', + }; +} + +export class InstanceIdClientErrorCode extends InstallationsClientErrorCode { + public static INVALID_INSTANCE_ID = { + code: 'invalid-instance-id', + message: 'Invalid instance ID provided.', }; } diff --git a/test/integration/installations.spec.ts b/test/integration/installations.spec.ts new file mode 100644 index 0000000000..982bd3a218 --- /dev/null +++ b/test/integration/installations.spec.ts @@ -0,0 +1,31 @@ +/*! + * Copyright 2021 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as admin from '../../lib/index'; +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; + +chai.should(); +chai.use(chaiAsPromised); + +describe('admin.installations', () => { + it('deleteInstallation() fails when called with fictive-ID0 instance ID', () => { + // instance ids have to conform to /[cdef][A-Za-z0-9_-]{9}[AEIMQUYcgkosw048]/ + return admin.installations().deleteInstallation('fictive-ID0') + .should.eventually.be + .rejectedWith('Installation ID "fictive-ID0": Failed to find the installation ID.'); + }); +}); diff --git a/test/integration/instance-id.spec.ts b/test/integration/instance-id.spec.ts index a7d6eb6df2..d98c2802f6 100644 --- a/test/integration/instance-id.spec.ts +++ b/test/integration/instance-id.spec.ts @@ -26,6 +26,6 @@ describe('admin.instanceId', () => { // instance ids have to conform to /[cdef][A-Za-z0-9_-]{9}[AEIMQUYcgkosw048]/ return admin.instanceId().deleteInstanceId('fictive-ID0') .should.eventually.be - .rejectedWith('Instance ID "fictive-ID0": Failed to find the instance ID.'); + .rejectedWith('Installation ID "fictive-ID0": Failed to find the installation ID.'); }); }); diff --git a/test/unit/firebase-app.spec.ts b/test/unit/firebase-app.spec.ts index 0989e718d0..1929637f94 100644 --- a/test/unit/firebase-app.spec.ts +++ b/test/unit/firebase-app.spec.ts @@ -37,6 +37,7 @@ import { machineLearning } from '../../src/machine-learning/index'; import { storage } from '../../src/storage/index'; import { firestore } from '../../src/firestore/index'; import { database } from '../../src/database/index'; +import { installations } from '../../src/installations/index'; import { instanceId } from '../../src/instance-id/index'; import { projectManagement } from '../../src/project-management/index'; import { securityRules } from '../../src/security-rules/index'; @@ -50,6 +51,7 @@ import Messaging = messaging.Messaging; import MachineLearning = machineLearning.MachineLearning; import Storage = storage.Storage; import Firestore = firestore.Firestore; +import Installations = installations.Installations; import InstanceId = instanceId.InstanceId; import ProjectManagement = projectManagement.ProjectManagement; import SecurityRules = securityRules.SecurityRules; @@ -567,6 +569,32 @@ describe('FirebaseApp', () => { }); }); + describe('installations()', () => { + it('should throw if the app has already been deleted', () => { + const app = firebaseNamespace.initializeApp(mocks.appOptions, mocks.appName); + + return app.delete().then(() => { + expect(() => { + return app.installations(); + }).to.throw(`Firebase app named "${mocks.appName}" has already been deleted.`); + }); + }); + + it('should return the InstanceId client', () => { + const app = firebaseNamespace.initializeApp(mocks.appOptions, mocks.appName); + + const fis: Installations = app.installations(); + expect(fis).not.be.null; + }); + + it('should return a cached version of InstanceId on subsequent calls', () => { + const app = firebaseNamespace.initializeApp(mocks.appOptions, mocks.appName); + const service1: Installations = app.installations(); + const service2: Installations = app.installations(); + expect(service1).to.equal(service2); + }); + }); + describe('instanceId()', () => { it('should throw if the app has already been deleted', () => { const app = firebaseNamespace.initializeApp(mocks.appOptions, mocks.appName); diff --git a/test/unit/firebase-namespace.spec.ts b/test/unit/firebase-namespace.spec.ts index e2efb84f4e..45047df4a7 100644 --- a/test/unit/firebase-namespace.spec.ts +++ b/test/unit/firebase-namespace.spec.ts @@ -52,6 +52,7 @@ import { machineLearning } from '../../src/machine-learning/index'; import { storage } from '../../src/storage/index'; import { firestore } from '../../src/firestore/index'; import { database } from '../../src/database/index'; +import { installations } from '../../src/installations/index'; import { instanceId } from '../../src/instance-id/index'; import { projectManagement } from '../../src/project-management/index'; import { securityRules } from '../../src/security-rules/index'; @@ -60,6 +61,7 @@ import { appCheck } from '../../src/app-check/index'; import { AppCheck as AppCheckImpl } from '../../src/app-check/app-check'; import { Auth as AuthImpl } from '../../src/auth/auth'; +import { Installations as InstallationsImpl } from '../../src/installations/installations'; import { InstanceId as InstanceIdImpl } from '../../src/instance-id/instance-id'; import { MachineLearning as MachineLearningImpl } from '../../src/machine-learning/machine-learning'; import { Messaging as MessagingImpl } from '../../src/messaging/messaging'; @@ -73,6 +75,7 @@ import AppCheck = appCheck.AppCheck; import Auth = auth.Auth; import Database = database.Database; import Firestore = firestore.Firestore; +import Installations = installations.Installations; import InstanceId = instanceId.InstanceId; import MachineLearning = machineLearning.MachineLearning; import Messaging = messaging.Messaging; @@ -603,6 +606,46 @@ describe('FirebaseNamespace', () => { }); }); + describe('#installations()', () => { + it('should throw when called before initializing an app', () => { + expect(() => { + firebaseNamespace.installations(); + }).to.throw(DEFAULT_APP_NOT_FOUND); + }); + + it('should throw when default app is not initialized', () => { + firebaseNamespace.initializeApp(mocks.appOptions, 'testApp'); + expect(() => { + firebaseNamespace.installations(); + }).to.throw(DEFAULT_APP_NOT_FOUND); + }); + + it('should return a valid namespace when the default app is initialized', () => { + const app: App = firebaseNamespace.initializeApp(mocks.appOptions); + const fis: Installations = firebaseNamespace.installations(); + expect(fis).to.not.be.null; + expect(fis.app).to.be.deep.equal(app); + }); + + it('should return a valid namespace when the named app is initialized', () => { + const app: App = firebaseNamespace.initializeApp(mocks.appOptions, 'testApp'); + const fis: Installations = firebaseNamespace.installations(app); + expect(fis).to.not.be.null; + expect(fis.app).to.be.deep.equal(app); + }); + + it('should return a reference to Installations type', () => { + expect(firebaseNamespace.installations.Installations).to.be.deep.equal(InstallationsImpl); + }); + + it('should return a cached version of Installations on subsequent calls', () => { + firebaseNamespace.initializeApp(mocks.appOptions); + const service1: Installations = firebaseNamespace.installations(); + const service2: Installations = firebaseNamespace.installations(); + expect(service1).to.equal(service2); + }); + }); + describe('#instanceId()', () => { it('should throw when called before initializing an app', () => { expect(() => { diff --git a/test/unit/index.spec.ts b/test/unit/index.spec.ts index 48c87e24e4..1cccc1def5 100644 --- a/test/unit/index.spec.ts +++ b/test/unit/index.spec.ts @@ -60,9 +60,12 @@ import './storage/storage.spec'; // Firestore import './firestore/firestore.spec'; +// Installations +import './installations/installations.spec'; +import './installations/installations-request-handler.spec'; + // InstanceId import './instance-id/instance-id.spec'; -import './instance-id/instance-id-request.spec'; // ProjectManagement import './project-management/project-management.spec'; diff --git a/test/unit/instance-id/instance-id-request.spec.ts b/test/unit/installations/installations-request-handler.spec.ts similarity index 72% rename from test/unit/instance-id/instance-id-request.spec.ts rename to test/unit/installations/installations-request-handler.spec.ts index dd28f0f8ef..dcc32b464b 100644 --- a/test/unit/instance-id/instance-id-request.spec.ts +++ b/test/unit/installations/installations-request-handler.spec.ts @@ -28,7 +28,7 @@ import * as mocks from '../../resources/mocks'; import { FirebaseApp } from '../../../src/firebase-app'; import { HttpClient } from '../../../src/utils/api-request'; -import { FirebaseInstanceIdRequestHandler } from '../../../src/instance-id/instance-id-request-internal'; +import { FirebaseInstallationsRequestHandler } from '../../../src/installations/installations-request-handler'; chai.should(); chai.use(sinonChai); @@ -36,7 +36,7 @@ chai.use(chaiAsPromised); const expect = chai.expect; -describe('FirebaseInstanceIdRequestHandler', () => { +describe('FirebaseInstallationsRequestHandler', () => { const projectId = 'project_id'; const mockAccessToken: string = utils.generateRandomAccessToken(); let stubs: sinon.SinonStub[] = []; @@ -69,24 +69,24 @@ describe('FirebaseInstanceIdRequestHandler', () => { describe('Constructor', () => { it('should succeed with a FirebaseApp instance', () => { expect(() => { - return new FirebaseInstanceIdRequestHandler(mockApp); + return new FirebaseInstallationsRequestHandler(mockApp); }).not.to.throw(Error); }); }); - describe('deleteInstanceId', () => { + describe('deleteInstallation', () => { const httpMethod = 'DELETE'; const host = 'console.firebase.google.com'; - const path = `/v1/project/${projectId}/instanceId/test-iid`; + const path = `/v1/project/${projectId}/instanceId/test-fid`; const timeout = 10000; - it('should be fulfilled given a valid instance ID', () => { + it('should be fulfilled given a valid installation ID', () => { const stub = sinon.stub(HttpClient.prototype, 'send') .resolves(utils.responseFrom('')); stubs.push(stub); - const requestHandler = new FirebaseInstanceIdRequestHandler(mockApp); - return requestHandler.deleteInstanceId('test-iid') + const requestHandler = new FirebaseInstallationsRequestHandler(mockApp); + return requestHandler.deleteInstallation('test-fid') .then(() => { expect(stub).to.have.been.calledOnce.and.calledWith({ method: httpMethod, @@ -102,14 +102,14 @@ describe('FirebaseInstanceIdRequestHandler', () => { .rejects(utils.errorFrom({}, 404)); stubs.push(stub); - const requestHandler = new FirebaseInstanceIdRequestHandler(mockApp); - return requestHandler.deleteInstanceId('test-iid') + const requestHandler = new FirebaseInstallationsRequestHandler(mockApp); + return requestHandler.deleteInstallation('test-fid') .then(() => { throw new Error('Unexpected success'); }) .catch((error) => { - expect(error.code).to.equal('instance-id/api-error'); - expect(error.message).to.equal('Instance ID "test-iid": Failed to find the instance ID.'); + expect(error.code).to.equal('installations/api-error'); + expect(error.message).to.equal('Installation ID "test-fid": Failed to find the installation ID.'); }); }); @@ -118,14 +118,14 @@ describe('FirebaseInstanceIdRequestHandler', () => { .rejects(utils.errorFrom({}, 409)); stubs.push(stub); - const requestHandler = new FirebaseInstanceIdRequestHandler(mockApp); - return requestHandler.deleteInstanceId('test-iid') + const requestHandler = new FirebaseInstallationsRequestHandler(mockApp); + return requestHandler.deleteInstallation('test-fid') .then(() => { throw new Error('Unexpected success'); }) .catch((error) => { - expect(error.code).to.equal('instance-id/api-error'); - expect(error.message).to.equal('Instance ID "test-iid": Already deleted.'); + expect(error.code).to.equal('installations/api-error'); + expect(error.message).to.equal('Installation ID "test-fid": Already deleted.'); }); }); @@ -135,13 +135,13 @@ describe('FirebaseInstanceIdRequestHandler', () => { .rejects(utils.errorFrom(expectedResult, 511)); stubs.push(stub); - const requestHandler = new FirebaseInstanceIdRequestHandler(mockApp); - return requestHandler.deleteInstanceId('test-iid') + const requestHandler = new FirebaseInstallationsRequestHandler(mockApp); + return requestHandler.deleteInstallation('test-fid') .then(() => { throw new Error('Unexpected success'); }) .catch((error) => { - expect(error.code).to.equal('instance-id/api-error'); + expect(error.code).to.equal('installations/api-error'); expect(error.message).to.equal('test error'); }); }); diff --git a/test/unit/installations/installations.spec.ts b/test/unit/installations/installations.spec.ts new file mode 100644 index 0000000000..e7816fa556 --- /dev/null +++ b/test/unit/installations/installations.spec.ts @@ -0,0 +1,190 @@ +/*! + * @license + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +import * as _ from 'lodash'; +import * as chai from 'chai'; +import * as sinon from 'sinon'; +import * as sinonChai from 'sinon-chai'; +import * as chaiAsPromised from 'chai-as-promised'; + +import * as utils from '../utils'; +import * as mocks from '../../resources/mocks'; + +import { Installations } from '../../../src/installations/installations'; +import { FirebaseInstallationsRequestHandler } from '../../../src/installations/installations-request-handler'; +import { FirebaseApp } from '../../../src/firebase-app'; +import { FirebaseInstallationsError, InstallationsClientErrorCode } from '../../../src/utils/error'; + +chai.should(); +chai.use(sinonChai); +chai.use(chaiAsPromised); + +const expect = chai.expect; + +describe('Installations', () => { + let fis: Installations; + let mockApp: FirebaseApp; + let mockCredentialApp: FirebaseApp; + let getTokenStub: sinon.SinonStub; + + let nullAccessTokenClient: Installations; + let malformedAccessTokenClient: Installations; + let rejectedPromiseAccessTokenClient: Installations; + + let googleCloudProject: string | undefined; + let gcloudProject: string | undefined; + + const noProjectIdError = 'Failed to determine project ID for Installations. Initialize the SDK ' + + 'with service account credentials or set project ID as an app option. Alternatively set the ' + + 'GOOGLE_CLOUD_PROJECT environment variable.'; + + beforeEach(() => { + mockApp = mocks.app(); + getTokenStub = utils.stubGetAccessToken(undefined, mockApp); + mockCredentialApp = mocks.mockCredentialApp(); + fis = new Installations(mockApp); + + googleCloudProject = process.env.GOOGLE_CLOUD_PROJECT; + gcloudProject = process.env.GCLOUD_PROJECT; + + nullAccessTokenClient = new Installations(mocks.appReturningNullAccessToken()); + malformedAccessTokenClient = new Installations(mocks.appReturningMalformedAccessToken()); + rejectedPromiseAccessTokenClient = new Installations(mocks.appRejectedWhileFetchingAccessToken()); + }); + + afterEach(() => { + getTokenStub.restore(); + process.env.GOOGLE_CLOUD_PROJECT = googleCloudProject; + process.env.GCLOUD_PROJECT = gcloudProject; + return mockApp.delete(); + }); + + + describe('Constructor', () => { + const invalidApps = [null, NaN, 0, 1, true, false, '', 'a', [], [1, 'a'], {}, { a: 1 }, _.noop]; + invalidApps.forEach((invalidApp) => { + it('should throw given invalid app: ' + JSON.stringify(invalidApp), () => { + expect(() => { + const iidAny: any = Installations; + return new iidAny(invalidApp); + }).to.throw('First argument passed to admin.installations() must be a valid Firebase app instance.'); + }); + }); + + it('should throw given no app', () => { + expect(() => { + const iidAny: any = Installations; + return new iidAny(); + }).to.throw('First argument passed to admin.installations() must be a valid Firebase app instance.'); + }); + + it('should reject given an invalid credential without project ID', () => { + // Project ID not set in the environment. + delete process.env.GOOGLE_CLOUD_PROJECT; + delete process.env.GCLOUD_PROJECT; + const installations = new Installations(mockCredentialApp); + return installations.deleteInstallation('iid') + .should.eventually.rejectedWith(noProjectIdError); + }); + + it('should not throw given a valid app', () => { + expect(() => { + return new Installations(mockApp); + }).not.to.throw(); + }); + }); + + describe('app', () => { + it('returns the app from the constructor', () => { + // We expect referential equality here + expect(fis.app).to.equal(mockApp); + }); + + it('is read-only', () => { + expect(() => { + (fis as any).app = mockApp; + }).to.throw('Cannot set property app of # which has only a getter'); + }); + }); + + describe('deleteInstallation()', () => { + + // Stubs used to simulate underlying api calls. + let stubs: sinon.SinonStub[] = []; + const expectedError = new FirebaseInstallationsError(InstallationsClientErrorCode.API_ERROR); + const testInstallationId = 'test-iid'; + + afterEach(() => { + _.forEach(stubs, (stub) => stub.restore()); + stubs = []; + }); + + it('should be rejected given no installation ID', () => { + return (fis as any).deleteInstallation() + .should.eventually.be.rejected.and.have.property('code', 'installations/invalid-installation-id'); + }); + + it('should be rejected given an invalid installation ID', () => { + return fis.deleteInstallation('') + .should.eventually.be.rejected.and.have.property('code', 'installations/invalid-installation-id'); + }); + + it('should be rejected given an app which returns null access tokens', () => { + return nullAccessTokenClient.deleteInstallation(testInstallationId) + .should.eventually.be.rejected.and.have.property('code', 'app/invalid-credential'); + }); + + it('should be rejected given an app which returns invalid access tokens', () => { + return malformedAccessTokenClient.deleteInstallation(testInstallationId) + .should.eventually.be.rejected.and.have.property('code', 'app/invalid-credential'); + }); + + it('should be rejected given an app which fails to generate access tokens', () => { + return rejectedPromiseAccessTokenClient.deleteInstallation(testInstallationId) + .should.eventually.be.rejected.and.have.property('code', 'app/invalid-credential'); + }); + + it('should resolve without errors on success', () => { + const stub = sinon.stub(FirebaseInstallationsRequestHandler.prototype, 'deleteInstallation') + .resolves(); + stubs.push(stub); + return fis.deleteInstallation(testInstallationId) + .then(() => { + // Confirm underlying API called with expected parameters. + expect(stub).to.have.been.calledOnce.and.calledWith(testInstallationId); + }); + }); + + it('should throw an error when the backend returns an error', () => { + // Stub deleteInstallation to throw a backend error. + const stub = sinon.stub(FirebaseInstallationsRequestHandler.prototype, 'deleteInstallation') + .rejects(expectedError); + stubs.push(stub); + return fis.deleteInstallation(testInstallationId) + .then(() => { + throw new Error('Unexpected success'); + }, (error) => { + // Confirm underlying API called with expected parameters. + expect(stub).to.have.been.calledOnce.and.calledWith(testInstallationId); + // Confirm expected error returned. + expect(error).to.equal(expectedError); + }); + }); + }); +}); diff --git a/test/unit/instance-id/instance-id.spec.ts b/test/unit/instance-id/instance-id.spec.ts index b68f8b3ac1..a35d2a01e2 100644 --- a/test/unit/instance-id/instance-id.spec.ts +++ b/test/unit/instance-id/instance-id.spec.ts @@ -27,9 +27,12 @@ import * as utils from '../utils'; import * as mocks from '../../resources/mocks'; import { InstanceId } from '../../../src/instance-id/instance-id'; -import { FirebaseInstanceIdRequestHandler } from '../../../src/instance-id/instance-id-request-internal'; +import { Installations } from '../../../src/installations/installations'; import { FirebaseApp } from '../../../src/firebase-app'; -import { FirebaseInstanceIdError, InstanceIdClientErrorCode } from '../../../src/utils/error'; +import { + FirebaseInstallationsError, FirebaseInstanceIdError, + InstallationsClientErrorCode, InstanceIdClientErrorCode, +} from '../../../src/utils/error'; chai.should(); chai.use(sinonChai); @@ -50,7 +53,7 @@ describe('InstanceId', () => { let googleCloudProject: string | undefined; let gcloudProject: string | undefined; - const noProjectIdError = 'Failed to determine project ID for InstanceId. Initialize the SDK ' + const noProjectIdError = 'Failed to determine project ID for Installations. Initialize the SDK ' + 'with service account credentials or set project ID as an app option. Alternatively set the ' + 'GOOGLE_CLOUD_PROJECT environment variable.'; @@ -127,7 +130,6 @@ describe('InstanceId', () => { // Stubs used to simulate underlying api calls. let stubs: sinon.SinonStub[] = []; - const expectedError = new FirebaseInstanceIdError(InstanceIdClientErrorCode.API_ERROR); const testInstanceId = 'test-iid'; afterEach(() => { @@ -161,7 +163,7 @@ describe('InstanceId', () => { }); it('should resolve without errors on success', () => { - const stub = sinon.stub(FirebaseInstanceIdRequestHandler.prototype, 'deleteInstanceId') + const stub = sinon.stub(Installations.prototype, 'deleteInstallation') .resolves(); stubs.push(stub); return iid.deleteInstanceId(testInstanceId) @@ -171,10 +173,11 @@ describe('InstanceId', () => { }); }); - it('should throw an error when the backend returns an error', () => { + it('should throw a FirebaseInstanceIdError error when the backend returns an error', () => { // Stub deleteInstanceId to throw a backend error. - const stub = sinon.stub(FirebaseInstanceIdRequestHandler.prototype, 'deleteInstanceId') - .returns(Promise.reject(expectedError)); + const originalError = new FirebaseInstallationsError(InstallationsClientErrorCode.API_ERROR); + const stub = sinon.stub(Installations.prototype, 'deleteInstallation') + .rejects(originalError); stubs.push(stub); return iid.deleteInstanceId(testInstanceId) .then(() => { @@ -183,7 +186,9 @@ describe('InstanceId', () => { // Confirm underlying API called with expected parameters. expect(stub).to.have.been.calledOnce.and.calledWith(testInstanceId); // Confirm expected error returned. - expect(error).to.equal(expectedError); + const expectedError = new FirebaseInstanceIdError(InstanceIdClientErrorCode.API_ERROR); + expect(error).to.be.instanceOf(FirebaseInstanceIdError) + expect(error).to.deep.include(expectedError); }); }); }); From 6f73c8c803ed88e32c17c9c62a644074ed6ec342 Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Wed, 23 Jun 2021 14:29:58 -0700 Subject: [PATCH 073/102] fix: Updated TOC for new Auth type aliases (#1342) --- docgen/content-sources/node/toc.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docgen/content-sources/node/toc.yaml b/docgen/content-sources/node/toc.yaml index fa5808c5f7..636f59c746 100644 --- a/docgen/content-sources/node/toc.yaml +++ b/docgen/content-sources/node/toc.yaml @@ -38,12 +38,14 @@ toc: path: /docs/reference/admin/node/admin.auth.Auth-1 - title: "ActionCodeSettings" path: /docs/reference/admin/node/admin.auth.ActionCodeSettings - - title: "AuthProviderConfig" - path: /docs/reference/admin/node/admin.auth.AuthProviderConfig - title: "AuthProviderConfigFilter" path: /docs/reference/admin/node/admin.auth.AuthProviderConfigFilter - - title: "CreateMultiFactorInfoRequest" - path: /docs/reference/admin/node/admin.auth.CreateMultiFactorInfoRequest + - title: "BaseAuthProviderConfig" + path: /docs/reference/admin/node/admin.auth.BaseAuthProviderConfig + - title: "BaseCreateMultiFactorInfoRequest" + path: /docs/reference/admin/node/admin.auth.BaseCreateMultiFactorInfoRequest + - title: "BaseUpdateMultiFactorInfoRequest" + path: /docs/reference/admin/node/admin.auth.BaseUpdateMultiFactorInfoRequest - title: "CreatePhoneMultiFactorInfoRequest" path: /docs/reference/admin/node/admin.auth.CreatePhoneMultiFactorInfoRequest - title: "CreateRequest" @@ -82,8 +84,6 @@ toc: path: /docs/reference/admin/node/admin.auth.TenantAwareAuth - title: "TenantManager" path: /docs/reference/admin/node/admin.auth.TenantManager - - title: "UpdateMultiFactorInfoRequest" - path: /docs/reference/admin/node/admin.auth.UpdateMultiFactorInfoRequest - title: "UpdatePhoneMultiFactorInfoRequest" path: /docs/reference/admin/node/admin.auth.UpdatePhoneMultiFactorInfoRequest - title: "UpdateRequest" From e51bf1db62ad6195c8b56cbf469eb322bdd147a3 Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Thu, 24 Jun 2021 10:33:56 -0700 Subject: [PATCH 074/102] [chore] Release 9.10.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 11b2c6ce63..ffa128f69a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-admin", - "version": "9.9.0", + "version": "9.10.0", "description": "Firebase admin SDK for Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", From 2ca2bcaef0e4ecee73426660007605ad2f87e01f Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Thu, 24 Jun 2021 12:14:14 -0700 Subject: [PATCH 075/102] [chore] Release 9.10.0 (#1345) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 11b2c6ce63..ffa128f69a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-admin", - "version": "9.9.0", + "version": "9.10.0", "description": "Firebase admin SDK for Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", From 2feece31422c62ba9f57751221c8e459abf931c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Jun 2021 12:46:23 -0700 Subject: [PATCH 076/102] build(deps-dev): bump @types/request-promise from 4.1.46 to 4.1.47 (#1338) Bumps [@types/request-promise](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/request-promise) from 4.1.46 to 4.1.47. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/request-promise) --- updated-dependencies: - dependency-name: "@types/request-promise" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index d10b61f0f3..aa5c241ddf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firebase-admin", - "version": "9.9.0", + "version": "9.10.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1229,9 +1229,9 @@ "dev": true }, "@types/bluebird": { - "version": "3.5.32", - "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.32.tgz", - "integrity": "sha512-dIOxFfI0C+jz89g6lQ+TqhGgPQ0MxSnh/E4xuC0blhFtyW269+mPG5QeLgbdwst/LvdP8o1y0o/Gz5EHXLec/g==", + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.35.tgz", + "integrity": "sha512-2WeeXK7BuQo7yPI4WGOBum90SzF/f8rqlvpaXx4rjeTmNssGRDHWf7fgDUH90xMB3sUOu716fUK5d+OVx0+ncQ==", "dev": true }, "@types/body-parser": { @@ -1415,9 +1415,9 @@ } }, "@types/request-promise": { - "version": "4.1.46", - "resolved": "https://registry.npmjs.org/@types/request-promise/-/request-promise-4.1.46.tgz", - "integrity": "sha512-3Thpj2Va5m0ji3spaCk8YKrjkZyZc6RqUVOphA0n/Xet66AW/AiOAs5vfXhQIL5NmkaO7Jnun7Nl9NEjJ2zBaw==", + "version": "4.1.47", + "resolved": "https://registry.npmjs.org/@types/request-promise/-/request-promise-4.1.47.tgz", + "integrity": "sha512-eRSZhAS8SMsrWOM8vbhxFGVZhTbWSJvaRKyufJTdIf4gscUouQvOBlfotPSPHbMR3S7kfkyKbhb1SWPmQdy3KQ==", "dev": true, "requires": { "@types/bluebird": "*", From dd942b0a1dce1952f8cfe7f0739e939d207106ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Jul 2021 14:05:37 -0700 Subject: [PATCH 077/102] build(deps): bump @firebase/database from 0.10.4 to 0.10.5 (#1350) Bumps [@firebase/database](https://github.com/firebase/firebase-js-sdk/tree/HEAD/packages/database) from 0.10.4 to 0.10.5. - [Release notes](https://github.com/firebase/firebase-js-sdk/releases) - [Changelog](https://github.com/firebase/firebase-js-sdk/blob/master/packages/database/CHANGELOG.md) - [Commits](https://github.com/firebase/firebase-js-sdk/commits/@firebase/database@0.10.5/packages/database) --- updated-dependencies: - dependency-name: "@firebase/database" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index aa5c241ddf..8db66cfb9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -511,6 +511,7 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.2.tgz", "integrity": "sha512-QT+o6VaBCz/k8wmC/DErU9dQK2QeIoHtkBkryZVTSRkrvulglEWNIpbPp86UbuqZZd1wwzoh6m7BL6JbdEp9SQ==", + "dev": true, "requires": { "@firebase/util": "1.1.0", "tslib": "^2.1.0" @@ -519,17 +520,18 @@ "tslib": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "dev": true } } }, "@firebase/database": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.10.4.tgz", - "integrity": "sha512-Mi6fJGzv9JH+GoYhgzSQAxsUhanW4jU6lqe/9kTyxNxHd+asphoJXJcKDs97uxRaowmSzu5LSAkGlWe63vJ7wA==", + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.10.5.tgz", + "integrity": "sha512-/KAFZGSvvL3J4EytZsl5kgqhZwEV+ZTz6mCS3VPigkkECzT1E/JRm9h8DY5/VWmoyfqc5O2F3kqrrLf7AovoHg==", "requires": { "@firebase/auth-interop-types": "0.1.6", - "@firebase/component": "0.5.2", + "@firebase/component": "0.5.3", "@firebase/database-types": "0.7.2", "@firebase/logger": "0.2.6", "@firebase/util": "1.1.0", @@ -537,6 +539,15 @@ "tslib": "^2.1.0" }, "dependencies": { + "@firebase/component": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.3.tgz", + "integrity": "sha512-/TzwmlK35Mnr31zA9D4X0Obln7waAtV7nDLuNVtWhlXl0sSYRxnGES4dOhSXi0yWRneaNr+OiRBZ2gsc9PWWRg==", + "requires": { + "@firebase/util": "1.1.0", + "tslib": "^2.1.0" + } + }, "tslib": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", From bf959e31ef0e04f7039db1c8f41c2fb70605757f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Jul 2021 14:24:26 -0700 Subject: [PATCH 078/102] build(deps-dev): bump @types/nock from 9.3.1 to 11.1.0 (#1351) Bumps [@types/nock](https://github.com/nock/nock) from 9.3.1 to 11.1.0. - [Release notes](https://github.com/nock/nock/releases) - [Changelog](https://github.com/nock/nock/blob/main/CHANGELOG.md) - [Commits](https://github.com/nock/nock/compare/v9.3.1...v11.1.0) --- updated-dependencies: - dependency-name: "@types/nock" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8db66cfb9c..4bac8d278b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1390,12 +1390,12 @@ "dev": true }, "@types/nock": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/@types/nock/-/nock-9.3.1.tgz", - "integrity": "sha512-eOVHXS5RnWOjTVhu3deCM/ruy9E6JCgeix2g7wpFiekQh3AaEAK1cz43tZDukKmtSmQnwvSySq7ubijCA32I7Q==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@types/nock/-/nock-11.1.0.tgz", + "integrity": "sha512-jI/ewavBQ7X5178262JQR0ewicPAcJhXS/iFaNJl0VHLfyosZ/kwSrsa6VNQNSO8i9d8SqdRgOtZSOKJ/+iNMw==", "dev": true, "requires": { - "@types/node": "*" + "nock": "*" } }, "@types/node": { diff --git a/package.json b/package.json index ffa128f69a..2b39dd8949 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "@types/lodash": "^4.14.104", "@types/minimist": "^1.2.0", "@types/mocha": "^8.2.2", - "@types/nock": "^9.1.0", + "@types/nock": "^11.1.0", "@types/request": "^2.47.0", "@types/request-promise": "^4.1.41", "@types/sinon": "^9.0.0", From e9cd6bfbccb54ad07492e2a5c191162a85fe20be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Jul 2021 14:36:34 -0700 Subject: [PATCH 079/102] build(deps-dev): bump @types/sinon from 9.0.4 to 10.0.2 (#1326) Bumps [@types/sinon](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/sinon) from 9.0.4 to 10.0.2. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/sinon) --- updated-dependencies: - dependency-name: "@types/sinon" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 25 +++++++++++++++---------- package.json | 2 +- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4bac8d278b..87b8450643 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1445,12 +1445,23 @@ } }, "@types/sinon": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.4.tgz", - "integrity": "sha512-sJmb32asJZY6Z2u09bl0G2wglSxDlROlAejCjsnor+LzBMz17gu8IU7vKC/vWDnv9zEq2wqADHVXFjf4eE8Gdw==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.2.tgz", + "integrity": "sha512-BHn8Bpkapj8Wdfxvh2jWIUoaYB/9/XhsL0oOvBfRagJtKlSl9NWPcFOz2lRukI9szwGxFtYZCTejJSqsGDbdmw==", "dev": true, "requires": { - "@types/sinonjs__fake-timers": "*" + "@sinonjs/fake-timers": "^7.1.0" + }, + "dependencies": { + "@sinonjs/fake-timers": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", + "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + } } }, "@types/sinon-chai": { @@ -1463,12 +1474,6 @@ "@types/sinon": "*" } }, - "@types/sinonjs__fake-timers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.1.tgz", - "integrity": "sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA==", - "dev": true - }, "@types/tough-cookie": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz", diff --git a/package.json b/package.json index 2b39dd8949..2d43a5b8be 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "@types/nock": "^11.1.0", "@types/request": "^2.47.0", "@types/request-promise": "^4.1.41", - "@types/sinon": "^9.0.0", + "@types/sinon": "^10.0.2", "@types/sinon-chai": "^3.0.0", "@typescript-eslint/eslint-plugin": "^2.20.0", "@typescript-eslint/parser": "^2.20.0", From 068ea8031292bc608ad6fdfe2e11c77de88aa690 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jul 2021 11:35:53 -0700 Subject: [PATCH 080/102] build(deps): bump @firebase/database from 0.10.5 to 0.10.6 (#1356) Bumps [@firebase/database](https://github.com/firebase/firebase-js-sdk/tree/HEAD/packages/database) from 0.10.5 to 0.10.6. - [Release notes](https://github.com/firebase/firebase-js-sdk/releases) - [Changelog](https://github.com/firebase/firebase-js-sdk/blob/master/packages/database/CHANGELOG.md) - [Commits](https://github.com/firebase/firebase-js-sdk/commits/@firebase/database@0.10.6/packages/database) --- updated-dependencies: - dependency-name: "@firebase/database" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 87b8450643..07077f845f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -526,12 +526,12 @@ } }, "@firebase/database": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.10.5.tgz", - "integrity": "sha512-/KAFZGSvvL3J4EytZsl5kgqhZwEV+ZTz6mCS3VPigkkECzT1E/JRm9h8DY5/VWmoyfqc5O2F3kqrrLf7AovoHg==", + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.10.6.tgz", + "integrity": "sha512-AGxRnKaJQd4Pq7sblrWI39XM5N2u/pZOeopMxVRja38Cubxp6P5T7lzpp0xNSOQ/RszAoHskGIlCfIz+teaXSQ==", "requires": { "@firebase/auth-interop-types": "0.1.6", - "@firebase/component": "0.5.3", + "@firebase/component": "0.5.4", "@firebase/database-types": "0.7.2", "@firebase/logger": "0.2.6", "@firebase/util": "1.1.0", @@ -540,9 +540,9 @@ }, "dependencies": { "@firebase/component": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.3.tgz", - "integrity": "sha512-/TzwmlK35Mnr31zA9D4X0Obln7waAtV7nDLuNVtWhlXl0sSYRxnGES4dOhSXi0yWRneaNr+OiRBZ2gsc9PWWRg==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.4.tgz", + "integrity": "sha512-KoLDPTsvxWr6FT9kn/snffJItaWXZLHLJlZVKiiw+flKE6MVA8Eec+ctvM2zcsMZzC2Z47gFnVqywfBlOevmpQ==", "requires": { "@firebase/util": "1.1.0", "tslib": "^2.1.0" From b8e837b1ca2ab88e4703a3193f2cce769fd56778 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Jul 2021 15:39:21 -0700 Subject: [PATCH 081/102] build(deps): bump jwks-rsa from 2.0.2 to 2.0.3 (#1361) Bumps [jwks-rsa](https://github.com/auth0/node-jwks-rsa) from 2.0.2 to 2.0.3. - [Release notes](https://github.com/auth0/node-jwks-rsa/releases) - [Changelog](https://github.com/auth0/node-jwks-rsa/blob/master/CHANGELOG.md) - [Commits](https://github.com/auth0/node-jwks-rsa/compare/v2.0.2...v2.0.3) --- updated-dependencies: - dependency-name: jwks-rsa dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 07077f845f..609148a606 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1296,9 +1296,9 @@ "dev": true }, "@types/express": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz", - "integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==", + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.12.tgz", + "integrity": "sha512-pTYas6FrP15B1Oa0bkN5tQMNqOcVXa9j4FTFtO8DWI9kppKib+6NJtfTOOLcwxuuYvcX2+dVG6et1SxW/Kc17Q==", "requires": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.18", @@ -1316,9 +1316,9 @@ } }, "@types/express-serve-static-core": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz", - "integrity": "sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA==", + "version": "4.17.22", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.22.tgz", + "integrity": "sha512-WdqmrUsRS4ootGha6tVwk/IVHM1iorU8tGehftQD2NWiPniw/sm7xdJOIlXLwqdInL9wBw/p7oO8vaYEF3NDmA==", "requires": { "@types/node": "*", "@types/qs": "*", @@ -5760,13 +5760,13 @@ } }, "jwks-rsa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-2.0.2.tgz", - "integrity": "sha512-oRnlZvmP21LxqEgEFiPycLn3jyw/QuynyaERe7GMxR4TlTg7BRGBgEyEN+rRN4xGHMekXur1RY/MSt8UJBiSgA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-2.0.3.tgz", + "integrity": "sha512-/rkjXRWAp0cS00tunsHResw68P5iTQru8+jHufLNv3JHc4nObFEndfEUSuPugh09N+V9XYxKUqi7QrkmCHSSSg==", "requires": { "@types/express-jwt": "0.0.42", "debug": "^4.1.0", - "jose": "^2.0.2", + "jose": "^2.0.5", "limiter": "^1.1.5", "lru-memoizer": "^2.1.2" } From c87f6409908aedf7d87245f3e3a9e709210115d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Jul 2021 16:24:22 -0700 Subject: [PATCH 082/102] build(deps-dev): bump yargs from 16.1.0 to 17.0.1 (#1357) Bumps [yargs](https://github.com/yargs/yargs) from 16.1.0 to 17.0.1. - [Release notes](https://github.com/yargs/yargs/releases) - [Changelog](https://github.com/yargs/yargs/blob/master/CHANGELOG.md) - [Commits](https://github.com/yargs/yargs/compare/v16.1.0...v17.0.1) --- updated-dependencies: - dependency-name: yargs dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 50 ++++++++++++----------------------------------- package.json | 2 +- 2 files changed, 14 insertions(+), 38 deletions(-) diff --git a/package-lock.json b/package-lock.json index 609148a606..f4b09cca9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9915,9 +9915,9 @@ "optional": true }, "yargs": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.1.0.tgz", - "integrity": "sha512-upWFJOmDdHN0syLuESuvXDmrRcWd1QafJolHskzaw79uZa7/x53gxQKiR07W59GWY1tFhhU/Th9DrtSfpS782g==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz", + "integrity": "sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==", "dev": true, "requires": { "cliui": "^7.0.2", @@ -9925,7 +9925,7 @@ "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", - "y18n": "^5.0.2", + "y18n": "^5.0.5", "yargs-parser": "^20.2.2" }, "dependencies": { @@ -9935,19 +9935,10 @@ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, - "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, - "requires": { - "color-convert": "^2.0.1" - } - }, "cliui": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.3.tgz", - "integrity": "sha512-Gj3QHTkVMPKqwP3f7B4KPkBZRMR9r4rfi5bXFpg1a+Svvj8l7q5CnkBkVQzfxT5DFSsGk2+PascOgL0JYkL2kw==", + "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, "requires": { "string-width": "^4.2.0", @@ -9955,21 +9946,6 @@ "wrap-ansi": "^7.0.0" } }, - "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, - "requires": { - "color-name": "~1.1.4" - } - }, - "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 - }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -9983,9 +9959,9 @@ "dev": true }, "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==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, "requires": { "emoji-regex": "^8.0.0", @@ -10020,9 +9996,9 @@ "dev": true }, "yargs-parser": { - "version": "20.2.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.3.tgz", - "integrity": "sha512-emOFRT9WVHw03QSvN5qor9QQT9+sw5vwxfYweivSMHTcAXPefwVae2FjO7JJjj8hCE4CzPOPeFM83VwT29HCww==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true } } diff --git a/package.json b/package.json index 2d43a5b8be..73e58f6d9b 100644 --- a/package.json +++ b/package.json @@ -117,6 +117,6 @@ "ts-node": "^9.0.0", "typedoc": "^0.19.2", "typescript": "^3.7.3", - "yargs": "^16.0.0" + "yargs": "^17.0.1" } } From 0b45481fd14d7920a7d7b0e85459c503458f2b1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Jul 2021 16:30:02 -0700 Subject: [PATCH 083/102] build(deps-dev): bump @types/chai from 4.2.11 to 4.2.21 (#1365) Bumps [@types/chai](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/chai) from 4.2.11 to 4.2.21. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/chai) --- updated-dependencies: - dependency-name: "@types/chai" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f4b09cca9c..b9568838f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1261,9 +1261,9 @@ "dev": true }, "@types/chai": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.11.tgz", - "integrity": "sha512-t7uW6eFafjO+qJ3BIV2gGUyZs27egcNRkUdalkud+Qa3+kg//f129iuOFivHDXQ+vnU3fDXuwgv0cqMCbcE8sw==", + "version": "4.2.21", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.21.tgz", + "integrity": "sha512-yd+9qKmJxm496BOV9CMNaey8TWsikaZOwMRwPHQIjcOJM9oV+fi9ZMNw3JsVnbEEbo2gRTDnGEBv8pjyn67hNg==", "dev": true }, "@types/chai-as-promised": { From bfaec79efd521a18fbfd01a96c9fb02e6ca4eb1e Mon Sep 17 00:00:00 2001 From: Daniel Hritzkiv Date: Mon, 12 Jul 2021 18:37:11 -0400 Subject: [PATCH 084/102] Update index.ts (#1367) Fix typo in comments: `sendMulticase` -> `sendMulticast` --- src/messaging/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/messaging/index.ts b/src/messaging/index.ts index 6c074cea7c..fc0a38338d 100644 --- a/src/messaging/index.ts +++ b/src/messaging/index.ts @@ -77,7 +77,7 @@ export namespace messaging { export type Message = TokenMessage | TopicMessage | ConditionMessage; /** - * Payload for the admin.messaing.sendMulticase() method. The payload contains all the fields + * Payload for the admin.messaing.sendMulticast() method. The payload contains all the fields * in the BaseMessage type, and a list of tokens. */ export interface MulticastMessage extends BaseMessage { From 760cd6a6a976fae6abfc213210b7b1950d69cdce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jul 2021 16:35:01 -0700 Subject: [PATCH 085/102] build(deps): bump @google-cloud/firestore from 4.12.2 to 4.13.1 (#1369) Bumps [@google-cloud/firestore](https://github.com/googleapis/nodejs-firestore) from 4.12.2 to 4.13.1. - [Release notes](https://github.com/googleapis/nodejs-firestore/releases) - [Changelog](https://github.com/googleapis/nodejs-firestore/blob/master/CHANGELOG.md) - [Commits](https://github.com/googleapis/nodejs-firestore/compare/v4.12.2...v4.13.1) --- updated-dependencies: - dependency-name: "@google-cloud/firestore" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 58 +++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index b9568838f7..d9016d918c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -626,14 +626,14 @@ } }, "@google-cloud/firestore": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-4.12.2.tgz", - "integrity": "sha512-5rurTAJXQ0SANEf8K9eA2JAB5zAh+pu4tGRnkZx5gBWQLZXdBFdtepS+irvKuSXw1KbeAQOuRANSc/nguys6SQ==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-4.13.1.tgz", + "integrity": "sha512-LtxboFZQ3MGwy1do8a0ykMJocM+TFgOpZoAihMwW498UDd641DJgJu0Kw0CD0bPpEaYUfhbeAUBq2ZO63DOz7g==", "optional": true, "requires": { "fast-deep-equal": "^3.1.1", "functional-red-black-tree": "^1.0.1", - "google-gax": "^2.12.0", + "google-gax": "^2.17.0", "protobufjs": "^6.8.6" } }, @@ -688,18 +688,18 @@ } }, "@grpc/grpc-js": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.3.2.tgz", - "integrity": "sha512-UXepkOKCATJrhHGsxt+CGfpZy9zUn1q9mop5kfcXq1fBkTePxVNPOdnISlCbJFlCtld+pSLGyZCzr9/zVprFKA==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.3.4.tgz", + "integrity": "sha512-AxtZcm0mArQhY9z8T3TynCYVEaSKxNCa9mVhVwBCUnsuUEe8Zn94bPYYKVQSLt+hJJ1y0ukr3mUvtWfcATL/IQ==", "optional": true, "requires": { "@types/node": ">=12.12.47" } }, "@grpc/proto-loader": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.2.tgz", - "integrity": "sha512-q2Qle60Ht2OQBCp9S5hv1JbI4uBBq6/mqSevFNK3ZEgRDBCAkWqZPUhD/K9gXOHrHKluliHiVq2L9sw1mVyAIg==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.4.tgz", + "integrity": "sha512-7xvDvW/vJEcmLUltCUGOgWRPM8Oofv0eCFSVMuKqaqWJaXSzmB+m9hiyqe34QofAl4WAzIKUZZlinIF9FOHyTQ==", "optional": true, "requires": { "@types/long": "^4.0.1", @@ -791,9 +791,9 @@ } }, "yargs-parser": { - "version": "20.2.7", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", - "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "optional": true } } @@ -4360,9 +4360,9 @@ } }, "google-gax": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.14.1.tgz", - "integrity": "sha512-I5RDEN7MEptrCxeHX3ht7nKFGfyjgYX4hQKI9eVMBohMzVbFSwWUndo0CcKXu8es7NhB4gt2XYLm1AHkXhtHpA==", + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.17.1.tgz", + "integrity": "sha512-CoR7OYuEzIDt3mp7cLYL+oGPmYdImS1WEwIvjF0zk0LhEBBmvRjWHTpBwazLGsT8hz+6zPh/4fjIjNaUxzIvzg==", "optional": true, "requires": { "@grpc/grpc-js": "~1.3.0", @@ -4371,7 +4371,7 @@ "abort-controller": "^3.0.0", "duplexify": "^4.0.0", "fast-text-encoding": "^1.0.3", - "google-auth-library": "^7.0.2", + "google-auth-library": "^7.3.0", "is-stream-ended": "^0.1.4", "node-fetch": "^2.6.1", "object-hash": "^2.1.1", @@ -4405,9 +4405,9 @@ } }, "gcp-metadata": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz", - "integrity": "sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.0.tgz", + "integrity": "sha512-L9XQUpvKJCM76YRSmcxrR4mFPzPGsgZUH+GgHMxAET8qc6+BhRJq63RLhWakgEO2KKVgeSDVfyiNjkGSADwNTA==", "optional": true, "requires": { "gaxios": "^4.0.0", @@ -4415,9 +4415,9 @@ } }, "google-auth-library": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.1.1.tgz", - "integrity": "sha512-+Q1linq/To3DYLyPz4UTEkQ0v5EOXadMM/S+taLV3W9611hq9zqg8kgGApqbTQnggtwdO9yU1y2YT7+83wdTRg==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.3.0.tgz", + "integrity": "sha512-MPeeMlnsYnoiiVFMwX3hgaS684aiXrSqKoDP+xL4Ejg4Z0qLvIeg4XsaChemyFI8ZUO7ApwDAzNtgmhWSDNh5w==", "optional": true, "requires": { "arrify": "^2.0.0", @@ -4432,18 +4432,18 @@ } }, "google-p12-pem": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz", - "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.1.0.tgz", + "integrity": "sha512-JUtEHXL4DY/N+xhlm7TC3qL797RPAtk0ZGXNs3/gWyiDHYoA/8Rjes0pztkda+sZv4ej1EoO2KhWgW5V9KTrSQ==", "optional": true, "requires": { "node-forge": "^0.10.0" } }, "gtoken": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.2.1.tgz", - "integrity": "sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.3.0.tgz", + "integrity": "sha512-mCcISYiaRZrJpfqOs0QWa6lfEM/C1V9ASkzFmuz43XBb5s1Vynh+CZy1ECeeJXVGx2PRByjYzb4Y4/zr1byr0w==", "optional": true, "requires": { "gaxios": "^4.0.0", From 4e816f44a3f3a67fcf912b6013c5beccb2210f8b Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Wed, 14 Jul 2021 11:33:35 -0400 Subject: [PATCH 086/102] feat(fac): Add custom TTL options for App Check (#1363) * Add custom ttl options for App Check * PR fixes * Add integration tests * PR fixes --- etc/firebase-admin.api.md | 5 +- src/app-check/app-check.ts | 10 +- src/app-check/index.ts | 16 ++- src/app-check/token-generator.ts | 46 ++++++++- src/messaging/messaging-internal.ts | 27 +---- src/utils/index.ts | 26 +++++ test/integration/app-check.spec.ts | 14 +++ test/unit/app-check/app-check.spec.ts | 9 ++ test/unit/app-check/token-generator.spec.ts | 104 +++++++++++++++++++- test/unit/utils/index.spec.ts | 15 ++- 10 files changed, 232 insertions(+), 40 deletions(-) diff --git a/etc/firebase-admin.api.md b/etc/firebase-admin.api.md index 6017b0c72a..427bc43099 100644 --- a/etc/firebase-admin.api.md +++ b/etc/firebase-admin.api.md @@ -53,13 +53,16 @@ export namespace appCheck { export interface AppCheck { // (undocumented) app: app.App; - createToken(appId: string): Promise; + createToken(appId: string, options?: AppCheckTokenOptions): Promise; verifyToken(appCheckToken: string): Promise; } export interface AppCheckToken { token: string; ttlMillis: number; } + export interface AppCheckTokenOptions { + ttlMillis?: number; + } export interface DecodedAppCheckToken { // (undocumented) [key: string]: any; diff --git a/src/app-check/app-check.ts b/src/app-check/app-check.ts index 42d8391043..175d023419 100644 --- a/src/app-check/app-check.ts +++ b/src/app-check/app-check.ts @@ -26,6 +26,7 @@ import { cryptoSignerFromApp } from '../utils/crypto-signer'; import AppCheckInterface = appCheck.AppCheck; import AppCheckToken = appCheck.AppCheckToken; +import AppCheckTokenOptions = appCheck.AppCheckTokenOptions; import VerifyAppCheckTokenResponse = appCheck.VerifyAppCheckTokenResponse; /** @@ -56,18 +57,19 @@ export class AppCheck implements AppCheckInterface { * back to a client. * * @param appId The app ID to use as the JWT app_id. + * @param options Optional options object when creating a new App Check Token. * - * @return A promise that fulfills with a `AppCheckToken`. + * @returns A promise that fulfills with a `AppCheckToken`. */ - public createToken(appId: string): Promise { - return this.tokenGenerator.createCustomToken(appId) + public createToken(appId: string, options?: AppCheckTokenOptions): Promise { + return this.tokenGenerator.createCustomToken(appId, options) .then((customToken) => { return this.client.exchangeToken(customToken, appId); }); } /** - * Veifies an App Check token. + * Verifies an App Check token. * * @param appCheckToken The App Check token to verify. * diff --git a/src/app-check/index.ts b/src/app-check/index.ts index 6552d9208d..295f49e4be 100644 --- a/src/app-check/index.ts +++ b/src/app-check/index.ts @@ -61,10 +61,11 @@ export namespace appCheck { * back to a client. * * @param appId The App ID of the Firebase App the token belongs to. + * @param options Optional options object when creating a new App Check Token. * - * @return A promise that fulfills with a `AppCheckToken`. + * @returns A promise that fulfills with a `AppCheckToken`. */ - createToken(appId: string): Promise; + createToken(appId: string, options?: AppCheckTokenOptions): Promise; /** * Verifies a Firebase App Check token (JWT). If the token is valid, the promise is @@ -95,6 +96,17 @@ export namespace appCheck { ttlMillis: number; } + /** + * Interface representing App Check token options. + */ + export interface AppCheckTokenOptions { + /** + * The length of time, in milliseconds, for which the App Check token will + * be valid. This value must be between 30 minutes and 7 days, inclusive. + */ + ttlMillis?: number; + } + /** * Interface representing a decoded Firebase App Check token, returned from the * {@link appCheck.AppCheck.verifyToken `verifyToken()`} method. diff --git a/src/app-check/token-generator.ts b/src/app-check/token-generator.ts index 1b557438bb..6c5b9eeda6 100644 --- a/src/app-check/token-generator.ts +++ b/src/app-check/token-generator.ts @@ -15,8 +15,10 @@ * limitations under the License. */ +import { appCheck } from './index'; + import * as validator from '../utils/validator'; -import { toWebSafeBase64 } from '../utils'; +import { toWebSafeBase64, transformMillisecondsToSecondsString } from '../utils'; import { CryptoSigner, CryptoSignerError, CryptoSignerErrorCode } from '../utils/crypto-signer'; import { @@ -26,7 +28,11 @@ import { } from './app-check-api-client-internal'; import { HttpError } from '../utils/api-request'; +import AppCheckTokenOptions = appCheck.AppCheckTokenOptions; + const ONE_HOUR_IN_SECONDS = 60 * 60; +const ONE_MINUTE_IN_MILLIS = 60 * 1000; +const ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1000; // Audience to use for Firebase App Check Custom tokens const FIREBASE_APP_CHECK_AUDIENCE = 'https://firebaseappcheck.googleapis.com/google.firebase.appcheck.v1beta.TokenExchangeService'; @@ -63,12 +69,16 @@ export class AppCheckTokenGenerator { * @return A Promise fulfilled with a custom token signed with a service account key * that can be exchanged to an App Check token. */ - public createCustomToken(appId: string): Promise { + public createCustomToken(appId: string, options?: AppCheckTokenOptions): Promise { if (!validator.isNonEmptyString(appId)) { throw new FirebaseAppCheckError( 'invalid-argument', '`appId` must be a non-empty string.'); } + let customOptions = {}; + if (typeof options !== 'undefined') { + customOptions = this.validateTokenOptions(options); + } return this.signer.getAccountId().then((account) => { const header = { alg: this.signer.algorithm, @@ -83,6 +93,7 @@ export class AppCheckTokenGenerator { aud: FIREBASE_APP_CHECK_AUDIENCE, exp: iat + ONE_HOUR_IN_SECONDS, iat, + ...customOptions, }; const token = `${this.encodeSegment(header)}.${this.encodeSegment(body)}`; return this.signer.sign(Buffer.from(token)) @@ -98,6 +109,35 @@ export class AppCheckTokenGenerator { const buffer: Buffer = (segment instanceof Buffer) ? segment : Buffer.from(JSON.stringify(segment)); return toWebSafeBase64(buffer).replace(/=+$/, ''); } + + /** + * Checks if a given `AppCheckTokenOptions` object is valid. If successful, returns an object with + * custom properties. + * + * @param options An options object to be validated. + * @returns A custom object with ttl converted to protobuf Duration string format. + */ + private validateTokenOptions(options: AppCheckTokenOptions): {[key: string]: any} { + if (!validator.isNonNullObject(options)) { + throw new FirebaseAppCheckError( + 'invalid-argument', + 'AppCheckTokenOptions must be a non-null object.'); + } + if (typeof options.ttlMillis !== 'undefined') { + if (!validator.isNumber(options.ttlMillis)) { + throw new FirebaseAppCheckError('invalid-argument', + 'ttlMillis must be a duration in milliseconds.'); + } + // ttlMillis must be between 30 minutes and 7 days (inclusive) + if (options.ttlMillis < (ONE_MINUTE_IN_MILLIS * 30) || options.ttlMillis > (ONE_DAY_IN_MILLIS * 7)) { + throw new FirebaseAppCheckError( + 'invalid-argument', + 'ttlMillis must be a duration in milliseconds between 30 minutes and 7 days (inclusive).'); + } + return { ttl: transformMillisecondsToSecondsString(options.ttlMillis) }; + } + return {}; + } } /** @@ -123,7 +163,7 @@ export function appCheckErrorFromCryptoSignerError(err: Error): Error { code = APP_CHECK_ERROR_CODE_MAPPING[status]; } return new FirebaseAppCheckError(code, - `Error returned from server while siging a custom token: ${description}` + `Error returned from server while signing a custom token: ${description}` ); } return new FirebaseAppCheckError('internal-error', diff --git a/src/messaging/messaging-internal.ts b/src/messaging/messaging-internal.ts index 227f894eea..d84328e722 100644 --- a/src/messaging/messaging-internal.ts +++ b/src/messaging/messaging-internal.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { renameProperties } from '../utils/index'; +import { renameProperties, transformMillisecondsToSecondsString } from '../utils/index'; import { MessagingClientErrorCode, FirebaseMessagingError, } from '../utils/error'; import { messaging } from './index'; import * as validator from '../utils/validator'; @@ -589,28 +589,3 @@ function validateAndroidFcmOptions(fcmOptions: AndroidFcmOptions | undefined): v MessagingClientErrorCode.INVALID_PAYLOAD, 'analyticsLabel must be a string value'); } } - -/** - * Transforms milliseconds to the format expected by FCM service. - * Returns the duration in seconds with up to nine fractional - * digits, terminated by 's'. Example: "3.5s". - * - * @param {number} milliseconds The duration in milliseconds. - * @return {string} The resulting formatted string in seconds with up to nine fractional - * digits, terminated by 's'. - */ -function transformMillisecondsToSecondsString(milliseconds: number): string { - let duration: string; - const seconds = Math.floor(milliseconds / 1000); - const nanos = (milliseconds - seconds * 1000) * 1000000; - if (nanos > 0) { - let nanoString = nanos.toString(); - while (nanoString.length < 9) { - nanoString = '0' + nanoString; - } - duration = `${seconds}.${nanoString}s`; - } else { - duration = `${seconds}s`; - } - return duration; -} diff --git a/src/utils/index.ts b/src/utils/index.ts index b8cfa2faf0..d047397667 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -190,3 +190,29 @@ export function generateUpdateMask( } return updateMask; } + +/** + * Transforms milliseconds to a protobuf Duration type string. + * Returns the duration in seconds with up to nine fractional + * digits, terminated by 's'. Example: "3 seconds 0 nano seconds as 3s, + * 3 seconds 1 nano seconds as 3.000000001s". + * + * @param milliseconds The duration in milliseconds. + * @returns The resulting formatted string in seconds with up to nine fractional + * digits, terminated by 's'. + */ +export function transformMillisecondsToSecondsString(milliseconds: number): string { + let duration: string; + const seconds = Math.floor(milliseconds / 1000); + const nanos = Math.floor((milliseconds - seconds * 1000) * 1000000); + if (nanos > 0) { + let nanoString = nanos.toString(); + while (nanoString.length < 9) { + nanoString = '0' + nanoString; + } + duration = `${seconds}.${nanoString}s`; + } else { + duration = `${seconds}s`; + } + return duration; +} diff --git a/test/integration/app-check.spec.ts b/test/integration/app-check.spec.ts index 32386f32bc..82ad153498 100644 --- a/test/integration/app-check.spec.ts +++ b/test/integration/app-check.spec.ts @@ -53,6 +53,20 @@ describe('admin.appCheck', () => { expect(token).to.have.keys(['token', 'ttlMillis']); expect(token.token).to.be.a('string').and.to.not.be.empty; expect(token.ttlMillis).to.be.a('number'); + expect(token.ttlMillis).to.equals(3600000); + }); + }); + + it('should succeed with a valid token and a custom ttl', function() { + if (!appId) { + this.skip(); + } + return admin.appCheck().createToken(appId as string, { ttlMillis: 1800000 }) + .then((token) => { + expect(token).to.have.keys(['token', 'ttlMillis']); + expect(token.token).to.be.a('string').and.to.not.be.empty; + expect(token.ttlMillis).to.be.a('number'); + expect(token.ttlMillis).to.equals(1800000); }); }); diff --git a/test/unit/app-check/app-check.spec.ts b/test/unit/app-check/app-check.spec.ts index c30970bc13..05c598f301 100644 --- a/test/unit/app-check/app-check.spec.ts +++ b/test/unit/app-check/app-check.spec.ts @@ -147,6 +147,15 @@ describe('AppCheck', () => { .should.eventually.be.rejected.and.deep.equal(INTERNAL_ERROR); }); + it('should propagate API errors with custom options', () => { + const stub = sinon + .stub(AppCheckApiClient.prototype, 'exchangeToken') + .rejects(INTERNAL_ERROR); + stubs.push(stub); + return appCheck.createToken(APP_ID, { ttlMillis: 1800000 }) + .should.eventually.be.rejected.and.deep.equal(INTERNAL_ERROR); + }); + it('should resolve with AppCheckToken on success', () => { const response = { token: 'token', ttlMillis: 3000 }; const stub = sinon diff --git a/test/unit/app-check/token-generator.spec.ts b/test/unit/app-check/token-generator.spec.ts index 66bb7cffba..2cf10b8bc5 100644 --- a/test/unit/app-check/token-generator.spec.ts +++ b/test/unit/app-check/token-generator.spec.ts @@ -122,11 +122,58 @@ describe('AppCheckTokenGenerator', () => { }).to.throw(FirebaseAppCheckError).with.property('code', 'app-check/invalid-argument'); }); - it('should be fulfilled with a Firebase Custom JWT', () => { + const invalidOptions = [null, NaN, 0, 1, true, false, [], _.noop]; + invalidOptions.forEach((invalidOption) => { + it('should throw given an invalid options: ' + JSON.stringify(invalidOption), () => { + expect(() => { + tokenGenerator.createCustomToken(APP_ID, invalidOption as any); + }).to.throw(FirebaseAppCheckError).with.property('message', 'AppCheckTokenOptions must be a non-null object.'); + }); + }); + + const invalidTtls = [null, NaN, '0', 'abc', '', true, false, [], {}, { a: 1 }, _.noop]; + invalidTtls.forEach((invalidTtl) => { + it('should throw given an options object with invalid ttl: ' + JSON.stringify(invalidTtl), () => { + expect(() => { + tokenGenerator.createCustomToken(APP_ID, { ttlMillis: invalidTtl as any }); + }).to.throw(FirebaseAppCheckError).with.property('message', + 'ttlMillis must be a duration in milliseconds.'); + }); + }); + + const THIRTY_MIN_IN_MS = 1800000; + const SEVEN_DAYS_IN_MS = 604800000; + [-100, -1, 0, 10, THIRTY_MIN_IN_MS - 1, SEVEN_DAYS_IN_MS + 1, SEVEN_DAYS_IN_MS * 2].forEach((ttlMillis) => { + it('should throw given options with ttl < 30 minutes or ttl > 7 days:' + JSON.stringify(ttlMillis), () => { + expect(() => { + tokenGenerator.createCustomToken(APP_ID, { ttlMillis }); + }).to.throw(FirebaseAppCheckError).with.property( + 'message', 'ttlMillis must be a duration in milliseconds between 30 minutes and 7 days (inclusive).'); + }); + }); + + it('should be fulfilled with a Firebase Custom JWT with only an APP ID', () => { return tokenGenerator.createCustomToken(APP_ID) .should.eventually.be.a('string').and.not.be.empty; }); + [ + [THIRTY_MIN_IN_MS, '1800s'], + [THIRTY_MIN_IN_MS + 1, '1800.001000000s'], + [SEVEN_DAYS_IN_MS / 2, '302400s'], + [SEVEN_DAYS_IN_MS - 1, '604799.999000000s'], + [SEVEN_DAYS_IN_MS, '604800s'] + ].forEach((ttl) => { + it('should be fulfilled with a Firebase Custom JWT with a valid custom ttl' + JSON.stringify(ttl[0]), () => { + return tokenGenerator.createCustomToken(APP_ID, { ttlMillis: ttl[0] as number }) + .then((token) => { + const decoded = jwt.decode(token) as { [key: string]: any }; + + expect(decoded['ttl']).to.equal(ttl[1]); + }); + }); + }); + it('should be fulfilled with a JWT with the correct decoded payload', () => { clock = sinon.useFakeTimers(1000); @@ -147,6 +194,57 @@ describe('AppCheckTokenGenerator', () => { }); }); + [{}, { ttlMillis: undefined }, { a: 123 }].forEach((options) => { + it('should be fulfilled with no ttl in the decoded payload when ttl is not provided in options', () => { + clock = sinon.useFakeTimers(1000); + + return tokenGenerator.createCustomToken(APP_ID, options) + .then((token) => { + const decoded = jwt.decode(token); + const expected: { [key: string]: any } = { + // eslint-disable-next-line @typescript-eslint/camelcase + app_id: APP_ID, + iat: 1, + exp: ONE_HOUR_IN_SECONDS + 1, + aud: FIREBASE_APP_CHECK_AUDIENCE, + iss: mocks.certificateObject.client_email, + sub: mocks.certificateObject.client_email, + }; + + expect(decoded).to.deep.equal(expected); + }); + }); + }); + + [ + [1800000.000001, '1800.000000001s'], + [1800000.001, '1800.000000999s'], + [172800000, '172800s'], + [604799999, '604799.999000000s'], + [604800000, '604800s'] + ].forEach((ttl) => { + it('should be fulfilled with a JWT with custom ttl in decoded payload', () => { + clock = sinon.useFakeTimers(1000); + + return tokenGenerator.createCustomToken(APP_ID, { ttlMillis: ttl[0] as number }) + .then((token) => { + const decoded = jwt.decode(token); + const expected: { [key: string]: any } = { + // eslint-disable-next-line @typescript-eslint/camelcase + app_id: APP_ID, + iat: 1, + exp: ONE_HOUR_IN_SECONDS + 1, + aud: FIREBASE_APP_CHECK_AUDIENCE, + iss: mocks.certificateObject.client_email, + sub: mocks.certificateObject.client_email, + ttl: ttl[1], + }; + + expect(decoded).to.deep.equal(expected); + }); + }); + }); + it('should be fulfilled with a JWT with the correct header', () => { clock = sinon.useFakeTimers(1000); @@ -225,7 +323,7 @@ describe('AppCheckTokenGenerator', () => { expect(appCheckError).to.be.an.instanceof(FirebaseAppCheckError); expect(appCheckError).to.have.property('code', 'app-check/unknown-error'); expect(appCheckError).to.have.property('message', - 'Error returned from server while siging a custom token: server error.'); + 'Error returned from server while signing a custom token: server error.'); }); it('should convert CryptoSignerError HttpError with no error.message to FirebaseAppCheckError', () => { @@ -240,7 +338,7 @@ describe('AppCheckTokenGenerator', () => { expect(appCheckError).to.be.an.instanceof(FirebaseAppCheckError); expect(appCheckError).to.have.property('code', 'app-check/unknown-error'); expect(appCheckError).to.have.property('message', - 'Error returned from server while siging a custom token: '+ + 'Error returned from server while signing a custom token: '+ '{"status":500,"headers":{},"data":{"error":{}},"text":"{\\"error\\":{}}"}'); }); diff --git a/test/unit/utils/index.spec.ts b/test/unit/utils/index.spec.ts index e63993fb83..9729dc7817 100644 --- a/test/unit/utils/index.spec.ts +++ b/test/unit/utils/index.spec.ts @@ -22,7 +22,7 @@ import * as sinon from 'sinon'; import * as mocks from '../../resources/mocks'; import { addReadonlyGetter, getExplicitProjectId, findProjectId, - toWebSafeBase64, formatString, generateUpdateMask, + toWebSafeBase64, formatString, generateUpdateMask, transformMillisecondsToSecondsString, } from '../../../src/utils/index'; import { isNonEmptyString } from '../../../src/utils/validator'; import { FirebaseApp } from '../../../src/firebase-app'; @@ -383,3 +383,16 @@ describe('generateUpdateMask()', () => { .to.deep.equal(['b', 'c', 'd', 'e', 'f', 'k', 'l', 'n']); }); }); + + +describe('transformMillisecondsToSecondsString()', () => { + [ + [3000.000001, '3s'], [3000.001, '3.000001000s'], + [3000, '3s'], [3500, '3.500000000s'] + ].forEach((duration) => { + it('should transform to protobuf duration string when provided milliseconds:' + JSON.stringify(duration[0]), + () => { + expect(transformMillisecondsToSecondsString(duration[0] as number)).to.equal(duration[1]); + }); + }); +}); From c2b126b3b18e4854646a7c85de770c353c3b7414 Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Wed, 14 Jul 2021 13:55:12 -0400 Subject: [PATCH 087/102] Reduce App Check custom token exp to 5 mins (#1372) --- src/app-check/token-generator.ts | 6 +++--- test/unit/app-check/token-generator.spec.ts | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/app-check/token-generator.ts b/src/app-check/token-generator.ts index 6c5b9eeda6..86745b793c 100644 --- a/src/app-check/token-generator.ts +++ b/src/app-check/token-generator.ts @@ -30,8 +30,8 @@ import { HttpError } from '../utils/api-request'; import AppCheckTokenOptions = appCheck.AppCheckTokenOptions; -const ONE_HOUR_IN_SECONDS = 60 * 60; -const ONE_MINUTE_IN_MILLIS = 60 * 1000; +const ONE_MINUTE_IN_SECONDS = 60; +const ONE_MINUTE_IN_MILLIS = ONE_MINUTE_IN_SECONDS * 1000; const ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1000; // Audience to use for Firebase App Check Custom tokens @@ -91,7 +91,7 @@ export class AppCheckTokenGenerator { // eslint-disable-next-line @typescript-eslint/camelcase app_id: appId, aud: FIREBASE_APP_CHECK_AUDIENCE, - exp: iat + ONE_HOUR_IN_SECONDS, + exp: iat + (ONE_MINUTE_IN_SECONDS * 5), iat, ...customOptions, }; diff --git a/test/unit/app-check/token-generator.spec.ts b/test/unit/app-check/token-generator.spec.ts index 2cf10b8bc5..2c612c1b64 100644 --- a/test/unit/app-check/token-generator.spec.ts +++ b/test/unit/app-check/token-generator.spec.ts @@ -43,7 +43,7 @@ chai.use(chaiAsPromised); const expect = chai.expect; const ALGORITHM = 'RS256'; -const ONE_HOUR_IN_SECONDS = 60 * 60; +const FIVE_MIN_IN_SECONDS = 60 * 5; const FIREBASE_APP_CHECK_AUDIENCE = 'https://firebaseappcheck.googleapis.com/google.firebase.appcheck.v1beta.TokenExchangeService'; /** @@ -184,7 +184,7 @@ describe('AppCheckTokenGenerator', () => { // eslint-disable-next-line @typescript-eslint/camelcase app_id: APP_ID, iat: 1, - exp: ONE_HOUR_IN_SECONDS + 1, + exp: FIVE_MIN_IN_SECONDS + 1, aud: FIREBASE_APP_CHECK_AUDIENCE, iss: mocks.certificateObject.client_email, sub: mocks.certificateObject.client_email, @@ -205,7 +205,7 @@ describe('AppCheckTokenGenerator', () => { // eslint-disable-next-line @typescript-eslint/camelcase app_id: APP_ID, iat: 1, - exp: ONE_HOUR_IN_SECONDS + 1, + exp: FIVE_MIN_IN_SECONDS + 1, aud: FIREBASE_APP_CHECK_AUDIENCE, iss: mocks.certificateObject.client_email, sub: mocks.certificateObject.client_email, @@ -233,7 +233,7 @@ describe('AppCheckTokenGenerator', () => { // eslint-disable-next-line @typescript-eslint/camelcase app_id: APP_ID, iat: 1, - exp: ONE_HOUR_IN_SECONDS + 1, + exp: FIVE_MIN_IN_SECONDS + 1, aud: FIREBASE_APP_CHECK_AUDIENCE, iss: mocks.certificateObject.client_email, sub: mocks.certificateObject.client_email, @@ -275,7 +275,7 @@ describe('AppCheckTokenGenerator', () => { }); }); - it('should be fulfilled with a JWT which expires after one hour', () => { + it('should be fulfilled with a JWT which expires after five minutes', () => { clock = sinon.useFakeTimers(1000); let token: string; @@ -283,7 +283,7 @@ describe('AppCheckTokenGenerator', () => { .then((result) => { token = result; - clock!.tick((ONE_HOUR_IN_SECONDS * 1000) - 1); + clock!.tick((FIVE_MIN_IN_SECONDS * 1000) - 1); // Token should still be valid return verifyToken(token, mocks.keyPairs[0].public); From ba07e12578c5f63562594c1fce632418376f2ef4 Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Thu, 15 Jul 2021 13:58:22 -0400 Subject: [PATCH 088/102] Add AppCheckTokenOptions type to ToC (#1375) Add `AppCheckTokenOptions` to `ToC` --- docgen/content-sources/node/toc.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docgen/content-sources/node/toc.yaml b/docgen/content-sources/node/toc.yaml index 636f59c746..c563a522ba 100644 --- a/docgen/content-sources/node/toc.yaml +++ b/docgen/content-sources/node/toc.yaml @@ -26,6 +26,8 @@ toc: path: /docs/reference/admin/node/admin.appCheck.AppCheck-1 - title: "AppCheckToken" path: /docs/reference/admin/node/admin.appCheck.AppCheckToken + - title: "AppCheckTokenOptions" + path: /docs/reference/admin/node/admin.appCheck.AppCheckTokenOptions - title: "DecodedAppCheckToken" path: /docs/reference/admin/node/admin.appCheck.DecodedAppCheckToken - title: "VerifyAppCheckTokenResponse" From 21869eef732679c6f6535acf810d191d7bcaa75e Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Thu, 15 Jul 2021 15:35:01 -0400 Subject: [PATCH 089/102] Fix typo and formatting in docs (#1378) - Fix typo and add back-ticks --- src/messaging/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/messaging/index.ts b/src/messaging/index.ts index fc0a38338d..0ed93e01e9 100644 --- a/src/messaging/index.ts +++ b/src/messaging/index.ts @@ -71,14 +71,14 @@ export namespace messaging { } /** - * Payload for the admin.messaging.send() operation. The payload contains all the fields - * in the BaseMessage type, and exactly one of token, topic or condition. + * Payload for the `admin.messaging.send()` operation. The payload contains all the fields + * in the `BaseMessage` type, and exactly one of token, topic or condition. */ export type Message = TokenMessage | TopicMessage | ConditionMessage; /** - * Payload for the admin.messaing.sendMulticast() method. The payload contains all the fields - * in the BaseMessage type, and a list of tokens. + * Payload for the `admin.messaging.sendMulticast()` method. The payload contains all the fields + * in the `BaseMessage` type, and a list of tokens. */ export interface MulticastMessage extends BaseMessage { tokens: string[]; From 20dc4626104ff728feda467b769203330aa55f7a Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Thu, 15 Jul 2021 17:18:51 -0400 Subject: [PATCH 090/102] [chore] Release 9.11.0 (#1376) - Release 9.11.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 73e58f6d9b..01ee014918 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-admin", - "version": "9.10.0", + "version": "9.11.0", "description": "Firebase admin SDK for Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", From db13ec1a58ff20d01e8392b813961e91ca038fdd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Jul 2021 15:26:31 -0700 Subject: [PATCH 091/102] build(deps-dev): bump nock from 13.1.0 to 13.1.1 (#1370) Bumps [nock](https://github.com/nock/nock) from 13.1.0 to 13.1.1. - [Release notes](https://github.com/nock/nock/releases) - [Changelog](https://github.com/nock/nock/blob/main/CHANGELOG.md) - [Commits](https://github.com/nock/nock/compare/v13.1.0...v13.1.1) --- updated-dependencies: - dependency-name: nock dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index d9016d918c..f56e5b68a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firebase-admin", - "version": "9.10.0", + "version": "9.11.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -6811,9 +6811,9 @@ } }, "nock": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.1.0.tgz", - "integrity": "sha512-3N3DUY8XYrxxzWazQ+nSBpiaJ3q6gcpNh4gXovC/QBxrsvNp4tq+wsLHF6mJ3nrn3lPLn7KCJqKxy/9aD+0fdw==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.1.1.tgz", + "integrity": "sha512-YKTR9MjfK3kS9/l4nuTxyYm30cgOExRHzkLNhL8nhEUyU4f8Za/dRxOqjhVT1vGs0svWo3dDnJTUX1qxYeWy5w==", "dev": true, "requires": { "debug": "^4.1.0", From 25429b31cf92de5a5a1b2ca97534c0b0a5f4a3bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jul 2021 16:19:28 -0700 Subject: [PATCH 092/102] build(deps-dev): bump @types/bcrypt from 2.0.0 to 5.0.0 (#1384) Bumps [@types/bcrypt](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/bcrypt) from 2.0.0 to 5.0.0. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/bcrypt) --- updated-dependencies: - dependency-name: "@types/bcrypt" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 11 +++++++---- package.json | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index f56e5b68a5..126f32db8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1234,10 +1234,13 @@ "dev": true }, "@types/bcrypt": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-2.0.0.tgz", - "integrity": "sha512-/r/ihQBlYMUYHqcFXix76I3OLYTaUcU8xV2agtB2hCds2rfJI56UyKu0e2LkAW2/4HHmQKmQRFXqM8D6y3Tc5g==", - "dev": true + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz", + "integrity": "sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==", + "dev": true, + "requires": { + "@types/node": "*" + } }, "@types/bluebird": { "version": "3.5.35", diff --git a/package.json b/package.json index 01ee014918..b16846c7b3 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "@firebase/auth": "^0.16.5", "@firebase/auth-types": "^0.10.3", "@microsoft/api-extractor": "^7.11.2", - "@types/bcrypt": "^2.0.0", + "@types/bcrypt": "^5.0.0", "@types/chai": "^4.0.0", "@types/chai-as-promised": "^7.1.0", "@types/firebase-token-generator": "^2.0.28", From b403c1539fa1e60e1e8329b55b22e007427f9d0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jul 2021 16:50:08 -0700 Subject: [PATCH 093/102] build(deps): bump @firebase/database from 0.10.6 to 0.10.7 (#1385) Bumps [@firebase/database](https://github.com/firebase/firebase-js-sdk/tree/HEAD/packages/database) from 0.10.6 to 0.10.7. - [Release notes](https://github.com/firebase/firebase-js-sdk/releases) - [Changelog](https://github.com/firebase/firebase-js-sdk/blob/master/packages/database/CHANGELOG.md) - [Commits](https://github.com/firebase/firebase-js-sdk/commits/@firebase/database@0.10.7/packages/database) --- updated-dependencies: - dependency-name: "@firebase/database" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 126f32db8d..97c1353e7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -526,9 +526,9 @@ } }, "@firebase/database": { - "version": "0.10.6", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.10.6.tgz", - "integrity": "sha512-AGxRnKaJQd4Pq7sblrWI39XM5N2u/pZOeopMxVRja38Cubxp6P5T7lzpp0xNSOQ/RszAoHskGIlCfIz+teaXSQ==", + "version": "0.10.7", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.10.7.tgz", + "integrity": "sha512-7BFj8LFhGL+TmLiPOffOVfkrO2wm44mGcT0jqrkTkt1KydapmjABFJBRvONvlLij5LoWrJK1cSuE8wYDQrDq2Q==", "requires": { "@firebase/auth-interop-types": "0.1.6", "@firebase/component": "0.5.4", From 8bfbadf3eb24517b7d9c4c2555235290b5a227fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jul 2021 18:37:58 -0700 Subject: [PATCH 094/102] build(deps-dev): bump @types/lodash from 4.14.157 to 4.14.171 (#1386) Bumps [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash) from 4.14.157 to 4.14.171. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash) --- updated-dependencies: - dependency-name: "@types/lodash" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 97c1353e7e..3bb45c2b46 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1358,9 +1358,9 @@ } }, "@types/lodash": { - "version": "4.14.157", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.157.tgz", - "integrity": "sha512-Ft5BNFmv2pHDgxV5JDsndOWTRJ+56zte0ZpYLowp03tW+K+t8u8YMOzAnpuqPgzX6WO1XpDIUm7u04M8vdDiVQ==", + "version": "4.14.171", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.171.tgz", + "integrity": "sha512-7eQ2xYLLI/LsicL2nejW9Wyko3lcpN6O/z0ZLHrEQsg280zIdCv1t/0m6UtBjUHokCGBQ3gYTbHzDkZ1xOBwwg==", "dev": true }, "@types/long": { From 6841d4cf1b3f08cd08483becfa760c4d6e45d28a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jul 2021 10:12:17 -0700 Subject: [PATCH 095/102] build(deps-dev): bump @types/request from 2.48.5 to 2.48.6 (#1387) Bumps [@types/request](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/request) from 2.48.5 to 2.48.6. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/request) --- updated-dependencies: - dependency-name: "@types/request" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3bb45c2b46..b53e65b906 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1417,9 +1417,9 @@ "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" }, "@types/request": { - "version": "2.48.5", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz", - "integrity": "sha512-/LO7xRVnL3DxJ1WkPGDQrp4VTV1reX9RkC85mJ+Qzykj2Bdw+mG15aAfDahc76HtknjzE16SX/Yddn6MxVbmGQ==", + "version": "2.48.6", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.6.tgz", + "integrity": "sha512-vrZaV3Ij7j/l/3hz6OttZFtpRCu7zlq7XgkYHJP6FwVEAZkGQ095WqyJV08/GlW9eyXKVcp/xmtruHm8eHpw1g==", "dev": true, "requires": { "@types/caseless": "*", @@ -1478,9 +1478,9 @@ } }, "@types/tough-cookie": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz", - "integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz", + "integrity": "sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg==", "dev": true }, "@typescript-eslint/eslint-plugin": { From 9ad3be7319823f596a69ef0c577651a16e1db597 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jul 2021 10:20:22 -0700 Subject: [PATCH 096/102] build(deps-dev): bump @types/minimist from 1.2.1 to 1.2.2 (#1388) Bumps [@types/minimist](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/minimist) from 1.2.1 to 1.2.2. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/minimist) --- updated-dependencies: - dependency-name: "@types/minimist" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b53e65b906..910c3d2323 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1381,9 +1381,9 @@ "dev": true }, "@types/minimist": { - "version": "1.2.1", - "resolved": "http://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", - "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", + "version": "1.2.2", + "resolved": "http://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", "dev": true }, "@types/mocha": { From 2e5e473281c7d9e350f00cd87ab0145bdee75573 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Jul 2021 10:43:12 -0700 Subject: [PATCH 097/102] build(deps): bump jwks-rsa from 2.0.3 to 2.0.4 (#1393) Bumps [jwks-rsa](https://github.com/auth0/node-jwks-rsa) from 2.0.3 to 2.0.4. - [Release notes](https://github.com/auth0/node-jwks-rsa/releases) - [Changelog](https://github.com/auth0/node-jwks-rsa/blob/master/CHANGELOG.md) - [Commits](https://github.com/auth0/node-jwks-rsa/compare/v2.0.3...2.0.4) --- updated-dependencies: - dependency-name: jwks-rsa dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 68 +++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index 910c3d2323..303628ec40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1249,9 +1249,9 @@ "dev": true }, "@types/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg==", "requires": { "@types/connect": "*", "@types/node": "*" @@ -1285,9 +1285,9 @@ "dev": true }, "@types/connect": { - "version": "3.4.34", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", - "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==", + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", "requires": { "@types/node": "*" } @@ -1299,9 +1299,9 @@ "dev": true }, "@types/express": { - "version": "4.17.12", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.12.tgz", - "integrity": "sha512-pTYas6FrP15B1Oa0bkN5tQMNqOcVXa9j4FTFtO8DWI9kppKib+6NJtfTOOLcwxuuYvcX2+dVG6et1SxW/Kc17Q==", + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", "requires": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.18", @@ -1319,9 +1319,9 @@ } }, "@types/express-serve-static-core": { - "version": "4.17.22", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.22.tgz", - "integrity": "sha512-WdqmrUsRS4ootGha6tVwk/IVHM1iorU8tGehftQD2NWiPniw/sm7xdJOIlXLwqdInL9wBw/p7oO8vaYEF3NDmA==", + "version": "4.17.24", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz", + "integrity": "sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA==", "requires": { "@types/node": "*", "@types/qs": "*", @@ -1329,9 +1329,9 @@ } }, "@types/express-unless": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.1.tgz", - "integrity": "sha512-5fuvg7C69lemNgl0+v+CUxDYWVPSfXHhJPst4yTLcqi4zKJpORCxnDrnnilk3k0DTq/WrAUdvXFs01+vUqUZHw==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.2.tgz", + "integrity": "sha512-Q74UyYRX/zIgl1HSp9tUX2PlG8glkVm+59r7aK4KGKzC5jqKIOX6rrVLRQrzpZUQ84VukHtRoeAuon2nIssHPQ==", "requires": { "@types/express": "*" } @@ -1407,14 +1407,14 @@ "integrity": "sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA==" }, "@types/qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==" + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" }, "@types/range-parser": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", - "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, "@types/request": { "version": "2.48.6", @@ -1439,9 +1439,9 @@ } }, "@types/serve-static": { - "version": "1.13.9", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz", - "integrity": "sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==", + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", "requires": { "@types/mime": "^1", "@types/node": "*" @@ -5763,15 +5763,25 @@ } }, "jwks-rsa": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-2.0.3.tgz", - "integrity": "sha512-/rkjXRWAp0cS00tunsHResw68P5iTQru8+jHufLNv3JHc4nObFEndfEUSuPugh09N+V9XYxKUqi7QrkmCHSSSg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-2.0.4.tgz", + "integrity": "sha512-iJqVCECYZZ+3oPmY1qXv3Fq+3ywDtuNEVBvG41pPlaR0zyGxa12nC0beAOBBUhETJmc05puS50mRQN4NkCGhmg==", "requires": { "@types/express-jwt": "0.0.42", - "debug": "^4.1.0", + "debug": "^4.3.2", "jose": "^2.0.5", "limiter": "^1.1.5", - "lru-memoizer": "^2.1.2" + "lru-memoizer": "^2.1.4" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "requires": { + "ms": "2.1.2" + } + } } }, "jws": { From 63d12e01614ac36bd8b5a8298637c7cf78025c80 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Aug 2021 14:02:04 -0700 Subject: [PATCH 098/102] build(deps-dev): bump @microsoft/api-extractor from 7.15.2 to 7.18.4 (#1379) * build(deps-dev): bump @microsoft/api-extractor from 7.15.2 to 7.18.4 Bumps [@microsoft/api-extractor](https://github.com/microsoft/rushstack) from 7.15.2 to 7.18.4. - [Release notes](https://github.com/microsoft/rushstack/releases) - [Commits](https://github.com/microsoft/rushstack/compare/@microsoft/api-extractor_v7.15.2...@microsoft/api-extractor_v7.18.4) --- updated-dependencies: - dependency-name: "@microsoft/api-extractor" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * fix: Updated API report with the new API Extractor version Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Hiranya Jayathilaka --- etc/firebase-admin.api.md | 5 +-- package-lock.json | 68 +++++++++++++++++++-------------------- 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/etc/firebase-admin.api.md b/etc/firebase-admin.api.md index 427bc43099..9ccd7cc067 100644 --- a/etc/firebase-admin.api.md +++ b/etc/firebase-admin.api.md @@ -4,6 +4,8 @@ ```ts +/// + import { Agent } from 'http'; import { Bucket } from '@google-cloud/storage'; import * as _firestore from '@google-cloud/firestore'; @@ -923,7 +925,7 @@ export namespace messaging { title?: string; vibrate?: number | number[]; } - {}; + {}; } // @public @@ -1138,7 +1140,6 @@ export namespace storage { } } - // (No @packageDocumentation comment for this package) ``` diff --git a/package-lock.json b/package-lock.json index 303628ec40..3dabe07ec6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -891,23 +891,23 @@ } }, "@microsoft/api-extractor": { - "version": "7.15.2", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.15.2.tgz", - "integrity": "sha512-/Y/n+QOc1vM6Vg3OAUByT/wXdZciE7jV3ay33+vxl3aKva5cNsuOauL14T7XQWUiLko3ilPwrcnFcEjzXpLsuA==", + "version": "7.18.4", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.18.4.tgz", + "integrity": "sha512-Wx45VuIAu09Pk9Qwzt0I57OX31BaWO2r6+mfSXqYFsJjYTqwUkdFh92G1GKYgvuR9oF/ai7w10wrFpx5WZYbGg==", "dev": true, "requires": { - "@microsoft/api-extractor-model": "7.13.2", + "@microsoft/api-extractor-model": "7.13.4", "@microsoft/tsdoc": "0.13.2", "@microsoft/tsdoc-config": "~0.15.2", - "@rushstack/node-core-library": "3.38.0", - "@rushstack/rig-package": "0.2.12", - "@rushstack/ts-command-line": "4.7.10", + "@rushstack/node-core-library": "3.39.1", + "@rushstack/rig-package": "0.2.13", + "@rushstack/ts-command-line": "4.8.1", "colors": "~1.2.1", "lodash": "~4.17.15", "resolve": "~1.17.0", "semver": "~7.3.0", "source-map": "~0.6.1", - "typescript": "~4.2.4" + "typescript": "~4.3.5" }, "dependencies": { "lru-cache": { @@ -935,9 +935,9 @@ "dev": true }, "typescript": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", - "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", "dev": true }, "yallist": { @@ -949,14 +949,14 @@ } }, "@microsoft/api-extractor-model": { - "version": "7.13.2", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.13.2.tgz", - "integrity": "sha512-gA9Q8q5TPM2YYk7rLinAv9KqcodrmRC13BVmNzLswjtFxpz13lRh0BmrqD01/sddGpGMIuWFYlfUM4VSWxnggA==", + "version": "7.13.4", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.13.4.tgz", + "integrity": "sha512-NYaR3hJinh089/Gkee8fvmEFf9zKkoUvNxgkqUlKBCDXH2+Ou4tNDuL8G6zjhKBPicHkp2VcL8l7q9H6txUkjQ==", "dev": true, "requires": { "@microsoft/tsdoc": "0.13.2", "@microsoft/tsdoc-config": "~0.15.2", - "@rushstack/node-core-library": "3.38.0" + "@rushstack/node-core-library": "3.39.1" } }, "@microsoft/tsdoc": { @@ -1097,9 +1097,9 @@ "optional": true }, "@rushstack/node-core-library": { - "version": "3.38.0", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.38.0.tgz", - "integrity": "sha512-cmvl0yQx8sSmbuXwiRYJi8TO+jpTtrLJQ8UmFHhKvgPVJAW8cV8dnpD1Xx/BvTGrJZ2XtRAIkAhBS9okBnap4w==", + "version": "3.39.1", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.39.1.tgz", + "integrity": "sha512-HHgMEHZTXQ3NjpQzWd5+fSt2Eod9yFwj6qBPbaeaNtDNkOL8wbLoxVimQNtcH0Qhn4wxF5u2NTDNFsxf2yd1jw==", "dev": true, "requires": { "@types/node": "10.17.13", @@ -1157,27 +1157,19 @@ } }, "@rushstack/rig-package": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.2.12.tgz", - "integrity": "sha512-nbePcvF8hQwv0ql9aeQxcaMPK/h1OLAC00W7fWCRWIvD2MchZOE8jumIIr66HGrfG2X1sw++m/ZYI4D+BM5ovQ==", + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.2.13.tgz", + "integrity": "sha512-qQMAFKvfb2ooaWU9DrGIK9d8QfyHy/HiuITJbWenlKgzcDXQvQgEduk57YF4Y7LLasDJ5ZzLaaXwlfX8qCRe5Q==", "dev": true, "requires": { "resolve": "~1.17.0", "strip-json-comments": "~3.1.1" - }, - "dependencies": { - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - } } }, "@rushstack/ts-command-line": { - "version": "4.7.10", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.7.10.tgz", - "integrity": "sha512-8t042g8eerypNOEcdpxwRA3uCmz0duMo21rG4Z2mdz7JxJeylDmzjlU3wDdef2t3P1Z61JCdZB6fbm1Mh0zi7w==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.8.1.tgz", + "integrity": "sha512-rmxvYdCNRbyRs+DYAPye3g6lkCkWHleqO40K8UPvUAzFqEuj6+YCVssBiOmrUDCoM5gaegSNT0wFDYhz24DWtw==", "dev": true, "requires": { "@types/argparse": "1.0.38", @@ -5159,9 +5151,9 @@ "dev": true }, "is-core-module": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", - "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz", + "integrity": "sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==", "dev": true, "requires": { "has": "^1.0.3" @@ -8979,6 +8971,12 @@ "is-utf8": "^0.2.0" } }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, "stubs": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", From 8474194f44a7abf62d5e3b218f0cee66b4e78ee6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Aug 2021 14:10:23 -0700 Subject: [PATCH 099/102] build(deps): bump tar from 6.1.0 to 6.1.3 (#1399) Bumps [tar](https://github.com/npm/node-tar) from 6.1.0 to 6.1.3. - [Release notes](https://github.com/npm/node-tar/releases) - [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/npm/node-tar/compare/v6.1.0...v6.1.3) --- updated-dependencies: - dependency-name: tar dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3dabe07ec6..00a34b2e28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9069,9 +9069,9 @@ } }, "tar": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.3.tgz", + "integrity": "sha512-3rUqwucgVZXTeyJyL2jqtUau8/8r54SioM1xj3AmTX3HnWQdj2AydfJ2qYYayPyIIznSplcvU9mhBb7dR2XF3w==", "dev": true, "requires": { "chownr": "^2.0.0", From a9d8b410848babfa9b1dfeedbf1ae00572deea57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Aug 2021 15:19:13 -0700 Subject: [PATCH 100/102] build(deps-dev): bump ts-node from 9.0.0 to 10.2.0 (#1402) Bumps [ts-node](https://github.com/TypeStrong/ts-node) from 9.0.0 to 10.2.0. - [Release notes](https://github.com/TypeStrong/ts-node/releases) - [Commits](https://github.com/TypeStrong/ts-node/compare/v9.0.0...v10.2.0) --- updated-dependencies: - dependency-name: ts-node dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 96 +++++++++++++++++++++++++++++++++++------------ package.json | 2 +- 2 files changed, 73 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index 00a34b2e28..0d58731bb5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -459,6 +459,21 @@ } } }, + "@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "dev": true + }, + "@cspotcode/source-map-support": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.6.1.tgz", + "integrity": "sha512-DX3Z+T5dt1ockmPdobJS/FAsQPW4V4SrWEhD2iYQT2Cb2tQsiMnYxrcUH9By/Z3B+v0S5LMBkQtV/XOBbpLEOg==", + "dev": true, + "requires": { + "@cspotcode/source-map-consumer": "0.8.0" + } + }, "@firebase/app": { "version": "0.6.26", "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.6.26.tgz", @@ -1219,6 +1234,30 @@ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "optional": true }, + "@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, "@types/argparse": { "version": "1.0.38", "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", @@ -2680,6 +2719,12 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "cross-spawn": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", @@ -8677,24 +8722,6 @@ "urix": "^0.1.0" } }, - "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, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "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 - } - } - }, "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", @@ -9302,16 +9329,37 @@ } }, "ts-node": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz", - "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==", - "dev": true, - "requires": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.0.tgz", + "integrity": "sha512-FstYHtQz6isj8rBtYMN4bZdnXN1vq4HCbqn9vdNQcInRqtB86PePJQIxE6es0PhxKWhj2PHuwbG40H+bxkZPmg==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "0.6.1", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", "arg": "^4.1.0", + "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "source-map-support": "^0.5.17", "yn": "3.1.1" + }, + "dependencies": { + "acorn": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", + "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", + "dev": true + }, + "acorn-walk": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.1.tgz", + "integrity": "sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w==", + "dev": true + } } }, "tslib": { diff --git a/package.json b/package.json index b16846c7b3..82605b068f 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,7 @@ "run-sequence": "^2.2.1", "sinon": "^9.0.0", "sinon-chai": "^3.0.0", - "ts-node": "^9.0.0", + "ts-node": "^10.2.0", "typedoc": "^0.19.2", "typescript": "^3.7.3", "yargs": "^17.0.1" From 9c41224d27f72b5254c36f0109989eb4504321b8 Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Mon, 16 Aug 2021 14:34:13 -0700 Subject: [PATCH 101/102] fix: Regenerated package-lock file --- package-lock.json | 1445 ++++-------------- src/auth/auth.ts | 3 +- src/installations/installations-namespace.ts | 2 +- 3 files changed, 310 insertions(+), 1140 deletions(-) diff --git a/package-lock.json b/package-lock.json index 52833bfe5b..a32e9e19e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,10 +1,6 @@ { "name": "firebase-admin", -<<<<<<< HEAD - "version": "9.100.0-alpha.0", -======= - "version": "9.11.0", ->>>>>>> master + "version": "9.100.0-alpha.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -17,43 +13,6 @@ "@babel/highlight": "^7.12.13" } }, - "@babel/compat-data": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.4.tgz", - "integrity": "sha512-i2wXrWQNkH6JplJQGn3Rd2I4Pij8GdHkXwHMxm+zV5YG/Jci+bCNrWZEWC4o+umiDkRrRs4dVzH3X4GP7vyjQQ==", - "dev": true - }, - "@babel/core": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.3.tgz", - "integrity": "sha512-jB5AmTKOCSJIZ72sd78ECEhuPiDMKlQdDI/4QRI6lzYATx5SSogS1oQA2AoPecRCknm30gHi2l+QVvNUu3wZAg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.3", - "@babel/helper-compilation-targets": "^7.13.16", - "@babel/helper-module-transforms": "^7.14.2", - "@babel/helpers": "^7.14.0", - "@babel/parser": "^7.14.3", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.2", - "@babel/types": "^7.14.2", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, "@babel/compat-data": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.0.tgz", @@ -129,6 +88,33 @@ "supports-color": "^5.3.0" } }, + "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, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -159,18 +145,6 @@ } } }, - "@babel/helper-compilation-targets": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.4.tgz", - "integrity": "sha512-JgdzOYZ/qGaKTVkn5qEDV/SXAh8KcyUVkCoSWGN8T3bwrgd6m+/dJa2kVGi6RJYJgEYPBdZ84BZp9dUjNWkBaA==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.14.4", - "@babel/helper-validator-option": "^7.12.17", - "browserslist": "^4.16.6", - "semver": "^6.3.0" - } - }, "@babel/helper-compilation-targets": { "version": "7.13.16", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz", @@ -198,28 +172,6 @@ "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==", -<<<<<<< HEAD - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", - "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", - "dev": true, - "requires": { - "@babel/types": "^7.13.12" - } - }, - "@babel/helper-module-imports": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz", - "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", - "dev": true, - "requires": { -======= "dev": true, "requires": { "@babel/types": "^7.12.13" @@ -240,7 +192,6 @@ "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", "dev": true, "requires": { ->>>>>>> master "@babel/types": "^7.13.12" } }, @@ -258,8 +209,6 @@ "@babel/template": "^7.12.13", "@babel/traverse": "^7.14.2", "@babel/types": "^7.14.2" -<<<<<<< HEAD -======= }, "dependencies": { "@babel/helper-validator-identifier": { @@ -268,7 +217,6 @@ "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", "dev": true } ->>>>>>> master } }, "@babel/helper-optimise-call-expression": { @@ -281,25 +229,15 @@ } }, "@babel/helper-replace-supers": { -<<<<<<< HEAD - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.4.tgz", - "integrity": "sha512-zZ7uHCWlxfEAAOVDYQpEf/uyi1dmeC7fX4nCf2iz9drnCwi1zvwXL3HwWWNXUQEJ1k23yVn3VbddiI9iJEXaTQ==", -======= "version": "7.14.3", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.3.tgz", "integrity": "sha512-Rlh8qEWZSTfdz+tgNV/N4gz1a0TMNwCUcENhMjHTHKp3LseYH5Jha0NSlyTQWMnjbYcwFt+bqAMqSLHVXkQ6UA==", ->>>>>>> master "dev": true, "requires": { "@babel/helper-member-expression-to-functions": "^7.13.12", "@babel/helper-optimise-call-expression": "^7.12.13", "@babel/traverse": "^7.14.2", -<<<<<<< HEAD - "@babel/types": "^7.14.4" -======= "@babel/types": "^7.14.2" ->>>>>>> master } }, "@babel/helper-simple-access": { @@ -407,15 +345,9 @@ } }, "@babel/parser": { -<<<<<<< HEAD - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.4.tgz", - "integrity": "sha512-ArliyUsWDUqEGfWcmzpGUzNfLxTdTp6WU4IuP6QFSp9gGfWS6boxFCkJSJ/L4+RG8z/FnIU3WxCk6hPL9SSWeA==", -======= "version": "7.14.3", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.3.tgz", "integrity": "sha512-7MpZDIfI7sUC5zWo2+foJ50CSI5lcqDehZ0lVgIhSi4bFEk94fLAKlF3Q0nzSQQ+ca0lm+O6G9ztKVBeu8PMRQ==", ->>>>>>> master "dev": true }, "@babel/template": { @@ -427,8 +359,6 @@ "@babel/code-frame": "^7.12.13", "@babel/parser": "^7.12.13", "@babel/types": "^7.12.13" -<<<<<<< HEAD -======= }, "dependencies": { "@babel/code-frame": { @@ -477,6 +407,27 @@ "supports-color": "^5.3.0" } }, + "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, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -486,7 +437,6 @@ "has-flag": "^3.0.0" } } ->>>>>>> master } }, "@babel/traverse": { @@ -551,12 +501,33 @@ "supports-color": "^5.3.0" } }, + "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, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, "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 }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -569,15 +540,9 @@ } }, "@babel/types": { -<<<<<<< HEAD - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.4.tgz", - "integrity": "sha512-lCj4aIs0xUefJFQnwwQv2Bxg7Omd6bgquZ6LGC+gGMh6/s5qDVfjuCMlDmYQ15SLsWHd9n+X3E75lKIhl5Lkiw==", -======= "version": "7.14.2", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.2.tgz", "integrity": "sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw==", ->>>>>>> master "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.14.0", @@ -621,18 +586,67 @@ "js-yaml": "4.0.0", "resolve": "~1.17.0", "tslib": "^2.1.0" + }, + "dependencies": { + "@microsoft/tsdoc": { + "version": "0.12.24", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.12.24.tgz", + "integrity": "sha512-Mfmij13RUTmHEMi9vRUhMXD7rnGR2VvxeNYtaGtaJ4redwwjT4UXYJ+nzmVJF7hhd4pn/Fx5sncDKxMVFJSWPg==", + "dev": true + }, + "@rushstack/node-core-library": { + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.36.0.tgz", + "integrity": "sha512-bID2vzXpg8zweXdXgQkKToEdZwVrVCN9vE9viTRk58gqzYaTlz4fMId6V3ZfpXN6H0d319uGi2KDlm+lUEeqCg==", + "dev": true, + "requires": { + "@types/node": "10.17.13", + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "timsort": "~0.3.0", + "z-schema": "~3.18.3" + } + }, + "@rushstack/ts-command-line": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.7.8.tgz", + "integrity": "sha512-8ghIWhkph7NnLCMDJtthpsb7TMOsVGXVDvmxjE/CeklTqjbbUFBjGXizJfpbEkRQTELuZQ2+vGn7sGwIWKN2uA==", + "dev": true, + "requires": { + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "colors": "~1.2.1", + "string-argv": "~0.3.1" + } + }, + "@types/node": { + "version": "10.17.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz", + "integrity": "sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + } } }, "@firebase/app": { -<<<<<<< HEAD - "version": "0.6.22", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.6.22.tgz", - "integrity": "sha512-9E0KP7Z+LpBOx/oQauLYvf3XleYpbfoi058wStADUtP+eOX5GIImAFNDTOO4ZNuJfLgyrHpKi7Cct6mDdxrz+g==", - "dev": true, - "requires": { - "@firebase/app-types": "0.6.2", - "@firebase/component": "0.5.0", -======= "version": "0.6.26", "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.6.26.tgz", "integrity": "sha512-y4tpb+uiYLQC5+/AHBtIGZMaTjJ2BHQEsXmPqxyhfVFDzWMcXFsc//RVxA/0OejajhJR6GeqDcIS3m47mUD+Aw==", @@ -640,7 +654,6 @@ "requires": { "@firebase/app-types": "0.6.2", "@firebase/component": "0.5.2", ->>>>>>> master "@firebase/logger": "0.2.6", "@firebase/util": "1.1.0", "dom-storage": "2.1.0", @@ -662,15 +675,9 @@ "integrity": "sha512-2VXvq/K+n8XMdM4L2xy5bYp2ZXMawJXluUIDzUBvMthVR+lhxK4pfFiqr1mmDbv9ydXvEAuFsD+6DpcZuJcSSw==" }, "@firebase/auth": { -<<<<<<< HEAD - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.16.6.tgz", - "integrity": "sha512-1Lj3AY40Z2weCK6FuJqUEkeVJpRaaCo1LT6P5s3VIR99PDYLHeMm2m02rBaskE7ralJA975Vkv7sHrpykRfDrA==", -======= "version": "0.16.5", "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.16.5.tgz", "integrity": "sha512-Cgs/TlVot2QkbJyEphvKmu+2qxYlNN+Q2+29aqZwryrnn1eLwlC7nT89K6O91/744HJRtiThm02bMj2Wh61E3Q==", ->>>>>>> master "dev": true, "requires": { "@firebase/auth-types": "0.10.3" @@ -688,23 +695,6 @@ "dev": true }, "@firebase/component": { -<<<<<<< HEAD - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.0.tgz", - "integrity": "sha512-v18csWtXb0ri+3m7wuGLY/UDgcb89vuMlZGQ//+7jEPLIQeLbylvZhol1uzW9WzoOpxMxOS2W5qyVGX36wZvEA==", - "requires": { - "@firebase/util": "1.1.0", - "tslib": "^2.1.0" - } - }, - "@firebase/database": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.10.2.tgz", - "integrity": "sha512-jMGtl5eBES9k0rOIZd6/EAuVB6m3LzRei1lvEiqWWBje2Xoz//7sjZcxOYtAKCCLldEI1EUrzW8Tv5yEAoPPpg==", - "requires": { - "@firebase/auth-interop-types": "0.1.6", - "@firebase/component": "0.5.0", -======= "version": "0.5.2", "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.2.tgz", "integrity": "sha512-QT+o6VaBCz/k8wmC/DErU9dQK2QeIoHtkBkryZVTSRkrvulglEWNIpbPp86UbuqZZd1wwzoh6m7BL6JbdEp9SQ==", @@ -729,14 +719,11 @@ "requires": { "@firebase/auth-interop-types": "0.1.6", "@firebase/component": "0.5.4", ->>>>>>> master "@firebase/database-types": "0.7.2", "@firebase/logger": "0.2.6", "@firebase/util": "1.1.0", "faye-websocket": "0.11.3", "tslib": "^2.1.0" -<<<<<<< HEAD -======= }, "dependencies": { "@firebase/component": { @@ -753,7 +740,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" } ->>>>>>> master } }, "@firebase/database-types": { @@ -775,8 +761,6 @@ "integrity": "sha512-lfuSASuPKNdfebuFR8rjFamMQUPH9iiZHcKS755Rkm/5gRT0qC7BMhCh3ZkHf7NVbplzIc/GhmX2jM+igDRCag==", "requires": { "tslib": "^2.1.0" -<<<<<<< HEAD -======= }, "dependencies": { "tslib": { @@ -784,7 +768,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" } ->>>>>>> master } }, "@google-cloud/common": { @@ -805,24 +788,14 @@ } }, "@google-cloud/firestore": { -<<<<<<< HEAD - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-4.12.2.tgz", - "integrity": "sha512-5rurTAJXQ0SANEf8K9eA2JAB5zAh+pu4tGRnkZx5gBWQLZXdBFdtepS+irvKuSXw1KbeAQOuRANSc/nguys6SQ==", -======= "version": "4.13.1", "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-4.13.1.tgz", "integrity": "sha512-LtxboFZQ3MGwy1do8a0ykMJocM+TFgOpZoAihMwW498UDd641DJgJu0Kw0CD0bPpEaYUfhbeAUBq2ZO63DOz7g==", ->>>>>>> master "optional": true, "requires": { "fast-deep-equal": "^3.1.1", "functional-red-black-tree": "^1.0.1", -<<<<<<< HEAD - "google-gax": "^2.12.0", -======= "google-gax": "^2.17.0", ->>>>>>> master "protobufjs": "^6.8.6" } }, @@ -878,30 +851,18 @@ } }, "@grpc/grpc-js": { -<<<<<<< HEAD - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.3.2.tgz", - "integrity": "sha512-UXepkOKCATJrhHGsxt+CGfpZy9zUn1q9mop5kfcXq1fBkTePxVNPOdnISlCbJFlCtld+pSLGyZCzr9/zVprFKA==", -======= "version": "1.3.4", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.3.4.tgz", "integrity": "sha512-AxtZcm0mArQhY9z8T3TynCYVEaSKxNCa9mVhVwBCUnsuUEe8Zn94bPYYKVQSLt+hJJ1y0ukr3mUvtWfcATL/IQ==", ->>>>>>> master "optional": true, "requires": { "@types/node": ">=12.12.47" } }, "@grpc/proto-loader": { -<<<<<<< HEAD - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.2.tgz", - "integrity": "sha512-q2Qle60Ht2OQBCp9S5hv1JbI4uBBq6/mqSevFNK3ZEgRDBCAkWqZPUhD/K9gXOHrHKluliHiVq2L9sw1mVyAIg==", -======= "version": "0.6.4", "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.4.tgz", "integrity": "sha512-7xvDvW/vJEcmLUltCUGOgWRPM8Oofv0eCFSVMuKqaqWJaXSzmB+m9hiyqe34QofAl4WAzIKUZZlinIF9FOHyTQ==", ->>>>>>> master "optional": true, "requires": { "@types/long": "^4.0.1", @@ -910,248 +871,43 @@ "protobufjs": "^6.10.0", "yargs": "^16.1.1" }, - "dependencies": { -<<<<<<< HEAD - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "optional": true, - "requires": { - "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" - } - } - } - }, - "@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, - "requires": { - "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" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "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, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" -======= - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "optional": true - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "optional": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" ->>>>>>> master - } - }, - "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, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, -<<<<<<< HEAD - "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, - "requires": { - "p-locate": "^4.1.0" - } - }, - "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, - "requires": { - "p-try": "^2.0.0" - } - }, - "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, - "requires": { - "p-limit": "^2.2.0" - } - }, - "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 - }, - "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 - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@mapbox/node-pre-gyp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz", - "integrity": "sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==", - "dev": true, - "requires": { - "detect-libc": "^1.0.3", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.1", - "nopt": "^5.0.0", - "npmlog": "^4.1.2", - "rimraf": "^3.0.2", - "semver": "^7.3.4", - "tar": "^6.1.0" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, -======= - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "optional": true, ->>>>>>> master - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@microsoft/api-extractor": { - "version": "7.15.2", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.15.2.tgz", - "integrity": "sha512-/Y/n+QOc1vM6Vg3OAUByT/wXdZciE7jV3ay33+vxl3aKva5cNsuOauL14T7XQWUiLko3ilPwrcnFcEjzXpLsuA==", - "dev": true, - "requires": { - "@microsoft/api-extractor-model": "7.13.2", - "@microsoft/tsdoc": "0.13.2", - "@microsoft/tsdoc-config": "~0.15.2", - "@rushstack/node-core-library": "3.38.0", - "@rushstack/rig-package": "0.2.12", - "@rushstack/ts-command-line": "4.7.10", - "colors": "~1.2.1", - "lodash": "~4.17.15", - "resolve": "~1.17.0", - "semver": "~7.3.0", - "source-map": "~0.6.1", - "typescript": "~4.2.4" - }, - "dependencies": { - "@microsoft/tsdoc": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz", - "integrity": "sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg==", - "dev": true + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" }, - "@rushstack/node-core-library": { - "version": "3.38.0", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.38.0.tgz", - "integrity": "sha512-cmvl0yQx8sSmbuXwiRYJi8TO+jpTtrLJQ8UmFHhKvgPVJAW8cV8dnpD1Xx/BvTGrJZ2XtRAIkAhBS9okBnap4w==", - "dev": true, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "optional": true, "requires": { - "@types/node": "10.17.13", - "colors": "~1.2.1", - "fs-extra": "~7.0.1", - "import-lazy": "~4.0.0", - "jju": "~1.4.0", - "resolve": "~1.17.0", - "semver": "~7.3.0", - "timsort": "~0.3.0", - "z-schema": "~3.18.3" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, -<<<<<<< HEAD - "@rushstack/ts-command-line": { - "version": "4.7.10", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.7.10.tgz", - "integrity": "sha512-8t042g8eerypNOEcdpxwRA3uCmz0duMo21rG4Z2mdz7JxJeylDmzjlU3wDdef2t3P1Z61JCdZB6fbm1Mh0zi7w==", - "dev": true, -======= - "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==", - "optional": true, ->>>>>>> master + "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==", "requires": { - "@types/argparse": "1.0.38", - "argparse": "~1.0.9", - "colors": "~1.2.1", - "string-argv": "~0.3.1" + "argparse": "^1.0.7", + "esprima": "^4.0.0" } }, -<<<<<<< HEAD - "@types/node": { - "version": "10.17.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz", - "integrity": "sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==", - "dev": true - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "optional": true, "requires": { - "lru-cache": "^6.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, - "typescript": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", - "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", -======= "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -1172,12 +928,6 @@ "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "optional": true } } }, @@ -1210,6 +960,16 @@ "path-exists": "^4.0.0" } }, + "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, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -1269,29 +1029,10 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", ->>>>>>> master "dev": true } } }, -<<<<<<< HEAD - "@microsoft/api-extractor-model": { - "version": "7.13.2", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.13.2.tgz", - "integrity": "sha512-gA9Q8q5TPM2YYk7rLinAv9KqcodrmRC13BVmNzLswjtFxpz13lRh0BmrqD01/sddGpGMIuWFYlfUM4VSWxnggA==", - "dev": true, - "requires": { - "@microsoft/tsdoc": "0.13.2", - "@microsoft/tsdoc-config": "~0.15.2", - "@rushstack/node-core-library": "3.38.0" - }, - "dependencies": { - "@microsoft/tsdoc": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz", - "integrity": "sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg==", - "dev": true -======= "@microsoft/api-extractor": { "version": "7.18.4", "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.18.4.tgz", @@ -1312,29 +1053,10 @@ "typescript": "~4.3.5" }, "dependencies": { - "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, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } ->>>>>>> master - }, "@rushstack/node-core-library": { - "version": "3.38.0", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.38.0.tgz", - "integrity": "sha512-cmvl0yQx8sSmbuXwiRYJi8TO+jpTtrLJQ8UmFHhKvgPVJAW8cV8dnpD1Xx/BvTGrJZ2XtRAIkAhBS9okBnap4w==", + "version": "3.39.1", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.39.1.tgz", + "integrity": "sha512-HHgMEHZTXQ3NjpQzWd5+fSt2Eod9yFwj6qBPbaeaNtDNkOL8wbLoxVimQNtcH0Qhn4wxF5u2NTDNFsxf2yd1jw==", "dev": true, "requires": { "@types/node": "10.17.13", @@ -1348,24 +1070,20 @@ "z-schema": "~3.18.3" } }, -<<<<<<< HEAD "@types/node": { "version": "10.17.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz", "integrity": "sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==", -======= - "typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", "dev": true }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", ->>>>>>> master - "dev": true + "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, + "requires": { + "yallist": "^4.0.0" + } }, "semver": { "version": "7.3.5", @@ -1375,51 +1093,21 @@ "requires": { "lru-cache": "^6.0.0" } - } - } - }, -<<<<<<< HEAD - "@microsoft/tsdoc": { - "version": "0.12.24", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.12.24.tgz", - "integrity": "sha512-Mfmij13RUTmHEMi9vRUhMXD7rnGR2VvxeNYtaGtaJ4redwwjT4UXYJ+nzmVJF7hhd4pn/Fx5sncDKxMVFJSWPg==", - "dev": true - }, - "@microsoft/tsdoc-config": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.15.2.tgz", - "integrity": "sha512-mK19b2wJHSdNf8znXSMYVShAHktVr/ib0Ck2FA3lsVBSEhSI/TfXT7DJQkAYgcztTuwazGcg58ZjYdk0hTCVrA==", - "dev": true, - "requires": { - "@microsoft/tsdoc": "0.13.2", - "ajv": "~6.12.6", - "jju": "~1.4.0", - "resolve": "~1.19.0" - }, - "dependencies": { - "@microsoft/tsdoc": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz", - "integrity": "sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg==", + }, + "typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", "dev": true }, - "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, - "requires": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - } + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, - "@panva/asn1.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz", - "integrity": "sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==" -======= "@microsoft/api-extractor-model": { "version": "7.13.4", "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.13.4.tgz", @@ -1488,7 +1176,6 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", "dev": true ->>>>>>> master }, "@nodelib/fs.walk": { "version": "1.2.6", @@ -1570,15 +1257,9 @@ "optional": true }, "@rushstack/node-core-library": { -<<<<<<< HEAD - "version": "3.36.0", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.36.0.tgz", - "integrity": "sha512-bID2vzXpg8zweXdXgQkKToEdZwVrVCN9vE9viTRk58gqzYaTlz4fMId6V3ZfpXN6H0d319uGi2KDlm+lUEeqCg==", -======= "version": "3.39.1", "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.39.1.tgz", "integrity": "sha512-HHgMEHZTXQ3NjpQzWd5+fSt2Eod9yFwj6qBPbaeaNtDNkOL8wbLoxVimQNtcH0Qhn4wxF5u2NTDNFsxf2yd1jw==", ->>>>>>> master "dev": true, "requires": { "@types/node": "10.17.13", @@ -1598,28 +1279,6 @@ "integrity": "sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==", "dev": true }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } -<<<<<<< HEAD - } - } - }, - "@rushstack/rig-package": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.2.12.tgz", - "integrity": "sha512-nbePcvF8hQwv0ql9aeQxcaMPK/h1OLAC00W7fWCRWIvD2MchZOE8jumIIr66HGrfG2X1sw++m/ZYI4D+BM5ovQ==", - "dev": true, - "requires": { - "resolve": "~1.17.0", - "strip-json-comments": "~3.1.1" -======= - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -1644,7 +1303,6 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true } ->>>>>>> master } }, "@rushstack/rig-package": { @@ -1658,15 +1316,9 @@ } }, "@rushstack/ts-command-line": { -<<<<<<< HEAD - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.7.8.tgz", - "integrity": "sha512-8ghIWhkph7NnLCMDJtthpsb7TMOsVGXVDvmxjE/CeklTqjbbUFBjGXizJfpbEkRQTELuZQ2+vGn7sGwIWKN2uA==", -======= "version": "4.8.1", "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.8.1.tgz", "integrity": "sha512-rmxvYdCNRbyRs+DYAPye3g6lkCkWHleqO40K8UPvUAzFqEuj6+YCVssBiOmrUDCoM5gaegSNT0wFDYhz24DWtw==", ->>>>>>> master "dev": true, "requires": { "@types/argparse": "1.0.38", @@ -1762,15 +1414,9 @@ "dev": true }, "@types/body-parser": { -<<<<<<< HEAD - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", -======= "version": "1.19.1", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz", "integrity": "sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg==", ->>>>>>> master "requires": { "@types/connect": "*", "@types/node": "*" @@ -1783,15 +1429,9 @@ "dev": true }, "@types/chai": { -<<<<<<< HEAD - "version": "4.2.18", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.18.tgz", - "integrity": "sha512-rS27+EkB/RE1Iz3u0XtVL5q36MGDWbgYe7zWiodyKNUnthxY0rukK5V36eiUCtCisB7NN8zKYH6DO2M37qxFEQ==", -======= "version": "4.2.21", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.21.tgz", "integrity": "sha512-yd+9qKmJxm496BOV9CMNaey8TWsikaZOwMRwPHQIjcOJM9oV+fi9ZMNw3JsVnbEEbo2gRTDnGEBv8pjyn67hNg==", ->>>>>>> master "dev": true }, "@types/chai-as-promised": { @@ -1803,14 +1443,6 @@ "@types/chai": "*" } }, - "@types/connect": { - "version": "3.4.34", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", - "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==", - "requires": { - "@types/node": "*" - } - }, "@types/connect": { "version": "3.4.35", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", @@ -1826,15 +1458,9 @@ "dev": true }, "@types/express": { -<<<<<<< HEAD - "version": "4.17.12", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.12.tgz", - "integrity": "sha512-pTYas6FrP15B1Oa0bkN5tQMNqOcVXa9j4FTFtO8DWI9kppKib+6NJtfTOOLcwxuuYvcX2+dVG6et1SxW/Kc17Q==", -======= "version": "4.17.13", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", ->>>>>>> master "requires": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.18", @@ -1852,15 +1478,9 @@ } }, "@types/express-serve-static-core": { -<<<<<<< HEAD - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.21.tgz", - "integrity": "sha512-gwCiEZqW6f7EoR8TTEfalyEhb1zA5jQJnRngr97+3pzMaO1RKoI1w2bw07TK72renMUVWcWS5mLI6rk1NqN0nA==", -======= "version": "4.17.24", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz", "integrity": "sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA==", ->>>>>>> master "requires": { "@types/node": "*", "@types/qs": "*", @@ -1868,22 +1488,16 @@ } }, "@types/express-unless": { -<<<<<<< HEAD - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.1.tgz", - "integrity": "sha512-5fuvg7C69lemNgl0+v+CUxDYWVPSfXHhJPst4yTLcqi4zKJpORCxnDrnnilk3k0DTq/WrAUdvXFs01+vUqUZHw==", -======= "version": "0.5.2", "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.2.tgz", "integrity": "sha512-Q74UyYRX/zIgl1HSp9tUX2PlG8glkVm+59r7aK4KGKzC5jqKIOX6rrVLRQrzpZUQ84VukHtRoeAuon2nIssHPQ==", ->>>>>>> master "requires": { "@types/express": "*" } }, "@types/firebase-token-generator": { "version": "2.0.28", - "resolved": "https://registry.npmjs.org/@types/firebase-token-generator/-/firebase-token-generator-2.0.28.tgz", + "resolved": "http://registry.npmjs.org/@types/firebase-token-generator/-/firebase-token-generator-2.0.28.tgz", "integrity": "sha1-Z1VIHZMk4mt6XItFXWgUg3aCw5Y=", "dev": true }, @@ -1903,15 +1517,9 @@ } }, "@types/lodash": { -<<<<<<< HEAD - "version": "4.14.170", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.170.tgz", - "integrity": "sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q==", -======= "version": "4.14.171", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.171.tgz", "integrity": "sha512-7eQ2xYLLI/LsicL2nejW9Wyko3lcpN6O/z0ZLHrEQsg280zIdCv1t/0m6UtBjUHokCGBQ3gYTbHzDkZ1xOBwwg==", ->>>>>>> master "dev": true }, "@types/long": { @@ -1932,15 +1540,9 @@ "dev": true }, "@types/minimist": { -<<<<<<< HEAD - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", - "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", -======= "version": "1.2.2", "resolved": "http://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", ->>>>>>> master "dev": true }, "@types/mocha": { @@ -1959,21 +1561,6 @@ } }, "@types/node": { -<<<<<<< HEAD - "version": "15.12.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.0.tgz", - "integrity": "sha512-+aHJvoCsVhO2ZCuT4o5JtcPrCPyDE3+1nvbDprYes+pPkEsbjH7AGUCNtjMOXS0fqH14t+B7yLzaqSz92FPWyw==" - }, - "@types/qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==" - }, - "@types/range-parser": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", - "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" -======= "version": "15.0.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.2.tgz", "integrity": "sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA==" @@ -1987,7 +1574,6 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" ->>>>>>> master }, "@types/request": { "version": "2.48.6", @@ -2012,30 +1598,18 @@ } }, "@types/serve-static": { -<<<<<<< HEAD - "version": "1.13.9", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz", - "integrity": "sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==", -======= "version": "1.13.10", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", ->>>>>>> master "requires": { "@types/mime": "^1", "@types/node": "*" } }, "@types/sinon": { -<<<<<<< HEAD - "version": "9.0.11", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.11.tgz", - "integrity": "sha512-PwP4UY33SeeVKodNE37ZlOsR9cReypbMJOhZ7BVE0lB+Hix3efCOxiJWiE5Ia+yL9Cn2Ch72EjFTRze8RZsNtg==", -======= "version": "10.0.2", "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.2.tgz", "integrity": "sha512-BHn8Bpkapj8Wdfxvh2jWIUoaYB/9/XhsL0oOvBfRagJtKlSl9NWPcFOz2lRukI9szwGxFtYZCTejJSqsGDbdmw==", ->>>>>>> master "dev": true, "requires": { "@sinonjs/fake-timers": "^7.1.0" @@ -2062,15 +1636,6 @@ "@types/sinon": "*" } }, -<<<<<<< HEAD - "@types/sinonjs__fake-timers": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz", - "integrity": "sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg==", - "dev": true - }, -======= ->>>>>>> master "@types/tough-cookie": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz", @@ -2266,8 +1831,6 @@ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "requires": { "color-convert": "^2.0.1" -<<<<<<< HEAD -======= }, "dependencies": { "color-convert": { @@ -2283,7 +1846,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" } ->>>>>>> master } }, "ansi-wrap": { @@ -2327,6 +1889,46 @@ "requires": { "@microsoft/tsdoc": "0.12.24", "@rushstack/node-core-library": "3.36.0" + }, + "dependencies": { + "@microsoft/tsdoc": { + "version": "0.12.24", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.12.24.tgz", + "integrity": "sha512-Mfmij13RUTmHEMi9vRUhMXD7rnGR2VvxeNYtaGtaJ4redwwjT4UXYJ+nzmVJF7hhd4pn/Fx5sncDKxMVFJSWPg==", + "dev": true + }, + "@rushstack/node-core-library": { + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.36.0.tgz", + "integrity": "sha512-bID2vzXpg8zweXdXgQkKToEdZwVrVCN9vE9viTRk58gqzYaTlz4fMId6V3ZfpXN6H0d319uGi2KDlm+lUEeqCg==", + "dev": true, + "requires": { + "@types/node": "10.17.13", + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "timsort": "~0.3.0", + "z-schema": "~3.18.3" + } + }, + "@types/node": { + "version": "10.17.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz", + "integrity": "sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "append-buffer": { @@ -2411,7 +2013,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -2877,7 +2478,6 @@ "make-dir": "^3.0.0", "package-hash": "^4.0.0", "write-file-atomic": "^3.0.0" -<<<<<<< HEAD } }, "call-bind": { @@ -2888,8 +2488,6 @@ "requires": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" -======= ->>>>>>> master } }, "callsites": { @@ -2905,15 +2503,9 @@ "dev": true }, "caniuse-lite": { -<<<<<<< HEAD - "version": "1.0.30001233", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001233.tgz", - "integrity": "sha512-BmkbxLfStqiPA7IEzQpIk0UFZFf3A4E6fzjPJ6OR+bFC2L8ES9J8zGA/asoi47p8XDVkev+WJo2I2Nc8c/34Yg==", -======= "version": "1.0.30001228", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz", "integrity": "sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A==", ->>>>>>> master "dev": true }, "caseless": { @@ -3075,6 +2667,7 @@ "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, "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -3169,19 +2762,6 @@ "object-visit": "^1.0.0" } }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", @@ -3559,21 +3139,6 @@ "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", "dev": true, "requires": { -<<<<<<< HEAD - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" - }, - "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", -======= "globby": "^11.0.1", "graceful-fs": "^4.2.4", "is-glob": "^4.0.1", @@ -3597,7 +3162,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", ->>>>>>> master "dev": true, "requires": { "glob": "^7.1.3" @@ -3725,15 +3289,9 @@ } }, "electron-to-chromium": { -<<<<<<< HEAD - "version": "1.3.746", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.746.tgz", - "integrity": "sha512-3ffyGODL38apwSsIgXaWnAKNXChsjXhAmBTjbqCbrv1fBbVltuNLWh0zdrQbwK/oxPQ/Gss/kYfFAPPGu9mszQ==", -======= "version": "1.3.736", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.736.tgz", "integrity": "sha512-DY8dA7gR51MSo66DqitEQoUMQ0Z+A2DSXFi7tK304bdTVqczCAfUuyQw6Wdg8hIoo5zIxkU1L24RQtUce1Ioig==", ->>>>>>> master "dev": true }, "emoji-regex": { @@ -4066,8 +3624,7 @@ "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 + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esquery": { "version": "1.4.0", @@ -4520,7 +4077,7 @@ }, "firebase-token-generator": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/firebase-token-generator/-/firebase-token-generator-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/firebase-token-generator/-/firebase-token-generator-2.0.0.tgz", "integrity": "sha1-l2fXWewTq9yZuhFf1eqZ2Lk9EgY=", "dev": true }, @@ -4859,15 +4416,6 @@ "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==", -<<<<<<< HEAD -======= - "dev": true - }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", ->>>>>>> master "dev": true }, "get-caller-file": { @@ -4881,7 +4429,6 @@ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, -<<<<<<< HEAD "get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -4893,8 +4440,6 @@ "has-symbols": "^1.0.1" } }, -======= ->>>>>>> master "get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -5102,15 +4647,9 @@ } }, "globby": { -<<<<<<< HEAD - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", -======= "version": "11.0.3", "resolved": "http://registry.npmjs.org/globby/-/globby-11.0.3.tgz", "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", ->>>>>>> master "dev": true, "requires": { "array-union": "^2.1.0", @@ -5156,15 +4695,9 @@ } }, "google-gax": { -<<<<<<< HEAD - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.14.1.tgz", - "integrity": "sha512-I5RDEN7MEptrCxeHX3ht7nKFGfyjgYX4hQKI9eVMBohMzVbFSwWUndo0CcKXu8es7NhB4gt2XYLm1AHkXhtHpA==", -======= "version": "2.17.1", "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.17.1.tgz", "integrity": "sha512-CoR7OYuEzIDt3mp7cLYL+oGPmYdImS1WEwIvjF0zk0LhEBBmvRjWHTpBwazLGsT8hz+6zPh/4fjIjNaUxzIvzg==", ->>>>>>> master "optional": true, "requires": { "@grpc/grpc-js": "~1.3.0", @@ -5173,18 +4706,12 @@ "abort-controller": "^3.0.0", "duplexify": "^4.0.0", "fast-text-encoding": "^1.0.3", -<<<<<<< HEAD - "google-auth-library": "^7.0.2", -======= "google-auth-library": "^7.3.0", ->>>>>>> master "is-stream-ended": "^0.1.4", "node-fetch": "^2.6.1", "object-hash": "^2.1.1", "protobufjs": "^6.10.2", "retry-request": "^4.0.0" -<<<<<<< HEAD -======= }, "dependencies": { "duplexify": { @@ -5294,7 +4821,6 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "optional": true } ->>>>>>> master } }, "google-p12-pem": { @@ -5389,7 +4915,6 @@ "yargs": "^7.1.0" } }, -<<<<<<< HEAD "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", @@ -5429,8 +4954,6 @@ "strip-ansi": "^3.0.1" } }, -======= ->>>>>>> master "y18n": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", @@ -5492,10 +5015,38 @@ "lodash.template": "^4.5.0", "map-stream": "0.0.7", "through2": "^2.0.0" -<<<<<<< HEAD -======= }, "dependencies": { + "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==", + "dev": true, + "requires": { + "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" + } + }, + "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==", + "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==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -5506,7 +5057,6 @@ "xtend": "~4.0.1" } } ->>>>>>> master } }, "gulp-typescript": { @@ -5534,7 +5084,6 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", "dev": true -<<<<<<< HEAD }, "through2": { "version": "3.0.2", @@ -5545,8 +5094,6 @@ "inherits": "^2.0.4", "readable-stream": "2 || 3" } -======= ->>>>>>> master } } }, @@ -5559,30 +5106,6 @@ "glogg": "^1.0.0" } }, -<<<<<<< HEAD -======= - "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, - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - }, - "dependencies": { - "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 - } - } - }, ->>>>>>> master "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -5631,15 +5154,12 @@ "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", "dev": true }, -<<<<<<< HEAD "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 }, -======= ->>>>>>> master "has-symbols": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", @@ -5951,15 +5471,9 @@ "dev": true }, "is-core-module": { -<<<<<<< HEAD - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", - "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", -======= "version": "2.5.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz", "integrity": "sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==", ->>>>>>> master "dev": true, "requires": { "has": "^1.0.3" @@ -6267,8 +5781,6 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, -<<<<<<< HEAD -======= "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -6278,7 +5790,6 @@ "glob": "^7.1.3" } }, ->>>>>>> master "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6292,23 +5803,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", -<<<<<<< HEAD - "dev": true - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" -======= "dev": true }, "uuid": { @@ -6352,35 +5846,10 @@ "dev": true, "requires": { "has-flag": "^4.0.0" ->>>>>>> master } } } }, -<<<<<<< HEAD - "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, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "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, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, -======= "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", @@ -6400,7 +5869,6 @@ } } }, ->>>>>>> master "istanbul-reports": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", @@ -6596,17 +6064,6 @@ } }, "jwks-rsa": { -<<<<<<< HEAD - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-2.0.3.tgz", - "integrity": "sha512-/rkjXRWAp0cS00tunsHResw68P5iTQru8+jHufLNv3JHc4nObFEndfEUSuPugh09N+V9XYxKUqi7QrkmCHSSSg==", - "requires": { - "@types/express-jwt": "0.0.42", - "debug": "^4.1.0", - "jose": "^2.0.5", - "limiter": "^1.1.5", - "lru-memoizer": "^2.1.2" -======= "version": "2.0.4", "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-2.0.4.tgz", "integrity": "sha512-iJqVCECYZZ+3oPmY1qXv3Fq+3ywDtuNEVBvG41pPlaR0zyGxa12nC0beAOBBUhETJmc05puS50mRQN4NkCGhmg==", @@ -6626,7 +6083,6 @@ "ms": "2.1.2" } } ->>>>>>> master } }, "jws": { @@ -6759,21 +6215,12 @@ } }, "locate-path": { -<<<<<<< HEAD - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" -======= "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { "p-locate": "^4.1.0" ->>>>>>> master } }, "lodash": { @@ -6898,6 +6345,13 @@ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "requires": { "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } } }, "lru-memoizer": { @@ -6924,15 +6378,6 @@ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" } } -<<<<<<< HEAD -======= - }, - "lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true ->>>>>>> master }, "make-dir": { "version": "3.1.0", @@ -7019,15 +6464,12 @@ "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", "dev": true }, -<<<<<<< HEAD -======= "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, ->>>>>>> master "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -7095,8 +6537,6 @@ "dev": true, "requires": { "yallist": "^4.0.0" -<<<<<<< HEAD -======= }, "dependencies": { "yallist": { @@ -7105,7 +6545,6 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true } ->>>>>>> master } }, "minizlib": { @@ -7116,8 +6555,6 @@ "requires": { "minipass": "^3.0.0", "yallist": "^4.0.0" -<<<<<<< HEAD -======= }, "dependencies": { "yallist": { @@ -7126,7 +6563,6 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true } ->>>>>>> master } }, "mixin-deep": { @@ -7150,12 +6586,6 @@ } } }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, "mocha": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", @@ -7297,12 +6727,30 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -7345,26 +6793,22 @@ "isexe": "^2.0.0" } }, -<<<<<<< HEAD -======= "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 + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, "wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==" + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true }, ->>>>>>> master "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -7404,17 +6848,6 @@ "array-union": "^2.1.0", "arrify": "^2.0.1", "minimatch": "^3.0.4" -<<<<<<< HEAD - }, - "dependencies": { - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - } -======= ->>>>>>> master } }, "mute-stdout": { @@ -7478,15 +6911,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, -<<<<<<< HEAD -======= - "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 - }, ->>>>>>> master "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -7513,15 +6937,9 @@ } }, "nock": { -<<<<<<< HEAD - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.1.0.tgz", - "integrity": "sha512-3N3DUY8XYrxxzWazQ+nSBpiaJ3q6gcpNh4gXovC/QBxrsvNp4tq+wsLHF6mJ3nrn3lPLn7KCJqKxy/9aD+0fdw==", -======= "version": "13.1.1", "resolved": "https://registry.npmjs.org/nock/-/nock-13.1.1.tgz", "integrity": "sha512-YKTR9MjfK3kS9/l4nuTxyYm30cgOExRHzkLNhL8nhEUyU4f8Za/dRxOqjhVT1vGs0svWo3dDnJTUX1qxYeWy5w==", ->>>>>>> master "dev": true, "requires": { "debug": "^4.1.0", @@ -7806,8 +7224,6 @@ "yargs": "^15.0.2" }, "dependencies": { -<<<<<<< HEAD -======= "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", @@ -7823,7 +7239,6 @@ "color-convert": "^2.0.1" } }, ->>>>>>> master "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -7841,8 +7256,6 @@ "wrap-ansi": "^6.2.0" } }, -<<<<<<< HEAD -======= "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -7858,7 +7271,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, ->>>>>>> master "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -7869,53 +7281,6 @@ "path-exists": "^4.0.0" } }, -<<<<<<< HEAD - "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, - "requires": { - "p-locate": "^4.1.0" - } - }, - "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, - "requires": { - "p-try": "^2.0.0" - } - }, - "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, - "requires": { - "p-limit": "^2.2.0" - } - }, - "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 - }, - "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 - }, - "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 - }, -======= "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -7975,7 +7340,6 @@ "ansi-regex": "^5.0.0" } }, ->>>>>>> master "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", @@ -7993,15 +7357,6 @@ "strip-ansi": "^6.0.0" } }, -<<<<<<< HEAD - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, -======= ->>>>>>> master "yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", @@ -8243,37 +7598,13 @@ "dev": true }, "p-limit": { -<<<<<<< HEAD - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, -======= "version": "3.0.2", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", ->>>>>>> master "requires": { - "p-limit": "^3.0.2" + "p-try": "^2.0.0" } }, -<<<<<<< HEAD - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" -======= "p-locate": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", @@ -8292,7 +7623,6 @@ "p-try": "^2.0.0" } } ->>>>>>> master } }, "p-map": { @@ -8307,8 +7637,7 @@ "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 + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, "package-hash": { "version": "4.0.0", @@ -8386,7 +7715,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, @@ -8503,44 +7832,10 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", -<<<<<<< HEAD - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "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, - "requires": { - "p-locate": "^4.1.0" - } - }, - "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, - "requires": { - "p-try": "^2.0.0" - } - }, - "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, - "requires": { - "p-limit": "^2.2.0" -======= "dev": true, "requires": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" ->>>>>>> master } }, "path-exists": { @@ -8577,7 +7872,7 @@ }, "pretty-hrtime": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "resolved": "http://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", "dev": true }, @@ -8596,15 +7891,6 @@ "fromentries": "^1.2.0" } }, - "process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", - "dev": true, - "requires": { - "fromentries": "^1.2.0" - } - }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -8924,8 +8210,6 @@ "request-promise-core": "1.1.4", "stealthy-require": "^1.1.1", "tough-cookie": "^2.3.3" -<<<<<<< HEAD -======= }, "dependencies": { "request-promise-core": { @@ -8947,16 +8231,6 @@ "punycode": "^2.1.1" } } ->>>>>>> master - } - }, - "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, - "requires": { - "lodash": "^4.17.19" } }, "require-directory": { @@ -9082,15 +8356,12 @@ "plugin-error": "^0.1.2" }, "dependencies": { -<<<<<<< HEAD "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, -======= ->>>>>>> master "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", @@ -9160,7 +8431,6 @@ "extend-shallow": "^1.1.2" } }, -<<<<<<< HEAD "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -9170,8 +8440,6 @@ "ansi-regex": "^2.0.0" } }, -======= ->>>>>>> master "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -9204,7 +8472,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -9217,18 +8485,6 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, -<<<<<<< HEAD -======= - "saxes": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", - "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==", - "dev": true, - "requires": { - "xmlchars": "^2.1.1" - } - }, ->>>>>>> master "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -9321,13 +8577,12 @@ "supports-color": "^7.1.0" }, "dependencies": { -<<<<<<< HEAD "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true -======= + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9342,7 +8597,6 @@ "requires": { "has-flag": "^4.0.0" } ->>>>>>> master } } }, @@ -9554,19 +8808,6 @@ "urix": "^0.1.0" } }, -<<<<<<< HEAD - "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, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, -======= ->>>>>>> master "source-map-url": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", @@ -9593,8 +8834,6 @@ "which": "^2.0.1" }, "dependencies": { -<<<<<<< HEAD -======= "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -9604,7 +8843,6 @@ "glob": "^7.1.3" } }, ->>>>>>> master "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -9660,8 +8898,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sshpk": { "version": "1.16.1", @@ -9844,8 +9081,6 @@ "dev": true, "requires": { "has-flag": "^4.0.0" -<<<<<<< HEAD -======= }, "dependencies": { "has-flag": { @@ -9854,7 +9089,6 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true } ->>>>>>> master } }, "sver-compat": { @@ -9920,15 +9154,9 @@ } }, "tar": { -<<<<<<< HEAD - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", -======= "version": "6.1.3", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.3.tgz", "integrity": "sha512-3rUqwucgVZXTeyJyL2jqtUau8/8r54SioM1xj3AmTX3HnWQdj2AydfJ2qYYayPyIIznSplcvU9mhBb7dR2XF3w==", ->>>>>>> master "dev": true, "requires": { "chownr": "^2.0.0", @@ -9937,8 +9165,6 @@ "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" -<<<<<<< HEAD -======= }, "dependencies": { "mkdirp": { @@ -9953,7 +9179,6 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true } ->>>>>>> master } }, "teeny-request": { @@ -10161,13 +9386,6 @@ } }, "ts-node": { -<<<<<<< HEAD - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", - "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", - "dev": true, - "requires": { -======= "version": "10.2.0", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.0.tgz", "integrity": "sha512-FstYHtQz6isj8rBtYMN4bZdnXN1vq4HCbqn9vdNQcInRqtB86PePJQIxE6es0PhxKWhj2PHuwbG40H+bxkZPmg==", @@ -10180,7 +9398,6 @@ "@tsconfig/node16": "^1.0.2", "acorn": "^8.4.1", "acorn-walk": "^8.1.1", ->>>>>>> master "arg": "^4.1.0", "create-require": "^1.1.0", "diff": "^4.0.1", @@ -10188,12 +9405,6 @@ "yn": "3.1.1" }, "dependencies": { -<<<<<<< HEAD - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", -======= "acorn": { "version": "8.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", @@ -10204,23 +9415,16 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.1.tgz", "integrity": "sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w==", ->>>>>>> master + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true } } }, - "tslib": { -<<<<<<< HEAD - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" -======= - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", - "dev": true ->>>>>>> master - }, "tsutils": { "version": "3.21.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", @@ -10786,15 +9990,6 @@ "typedarray-to-buffer": "^3.1.5" } }, -<<<<<<< HEAD -======= - "ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", - "dev": true - }, ->>>>>>> master "xdg-basedir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", @@ -10814,28 +10009,11 @@ "dev": true }, "y18n": { -<<<<<<< HEAD - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" -======= "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "optional": true ->>>>>>> master - }, "yargs": { "version": "17.0.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz", @@ -10849,8 +10027,6 @@ "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" -<<<<<<< HEAD -======= }, "dependencies": { "ansi-regex": { @@ -10925,13 +10101,13 @@ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true } ->>>>>>> master } }, "yargs-parser": { "version": "20.2.7", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", - "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==" + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "optional": true }, "yargs-unparser": { "version": "2.0.0", @@ -10965,11 +10141,6 @@ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true }, - "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==" - }, "z-schema": { "version": "3.18.4", "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.18.4.tgz", diff --git a/src/auth/auth.ts b/src/auth/auth.ts index 519b139a70..4ad29f6d20 100644 --- a/src/auth/auth.ts +++ b/src/auth/auth.ts @@ -16,7 +16,6 @@ */ import { App } from '../app/index'; -import { FirebaseApp } from '../app/firebase-app'; import { AuthRequestHandler } from './auth-api-request'; import { TenantManager } from './tenant-manager'; import { BaseAuth } from './base-auth'; @@ -36,7 +35,7 @@ export class Auth extends BaseAuth { * @internal */ constructor(app: App) { - super(app, new AuthRequestHandler(app as FirebaseApp)); + super(app, new AuthRequestHandler(app)); this.app_ = app; this.tenantManager_ = new TenantManager(app); } diff --git a/src/installations/installations-namespace.ts b/src/installations/installations-namespace.ts index f86580f8b1..dc51e08476 100644 --- a/src/installations/installations-namespace.ts +++ b/src/installations/installations-namespace.ts @@ -55,4 +55,4 @@ export namespace installations { * Type alias to {@link firebase-admin.installations#Installations}. */ export type Installations = TInstallations; -} \ No newline at end of file +} From 3d8a8abcdedf9911b6dccd9bb27a920ef975084b Mon Sep 17 00:00:00 2001 From: Hiranya Jayathilaka Date: Mon, 16 Aug 2021 15:03:14 -0700 Subject: [PATCH 102/102] fix: Fixing a typo in doc comment --- src/instance-id/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/instance-id/index.ts b/src/instance-id/index.ts index ec93d52715..7a1d5226f8 100644 --- a/src/instance-id/index.ts +++ b/src/instance-id/index.ts @@ -30,7 +30,7 @@ export { InstanceId }; * Gets the {@link InstanceId} service for the default app or a given app. * * This API is deprecated. Developers are advised to use the - * {@link firebase-admin.insallations#getInstallations}. + * {@link firebase-admin.installations#getInstallations} * API to delete their instance IDs and Firebase installation IDs. * * `getInstanceId()` can be called with no arguments to access the default