From 37f1da38b40aaeaa14ff085f6dacd2d0bd9ff1a5 Mon Sep 17 00:00:00 2001 From: Ben Polinsky <78756012+ben-polinsky@users.noreply.github.com> Date: Tue, 22 Aug 2023 12:08:43 -0400 Subject: [PATCH] Add static loopback pages (#202) Co-authored-by: Ben Polinsky --- ...-5263eced-a690-4d92-b59d-be3e53caf8af.json | 7 + ...-eb8d8df9-ceb5-48f8-b62b-067e591495f7.json | 7 + packages/electron/package.json | 8 +- .../electron/src/main/LoopbackWebServer.ts | 91 +++++++-- .../electron/src/static/auth-template.html | 110 ++++++++++ packages/node-cli/package.json | 6 +- packages/node-cli/src/Client.ts | 81 ++++++-- .../node-cli/src/static/auth-template.html | 110 ++++++++++ pnpm-lock.yaml | 188 +++++++++++++----- 9 files changed, 514 insertions(+), 94 deletions(-) create mode 100644 change/@itwin-electron-authorization-5263eced-a690-4d92-b59d-be3e53caf8af.json create mode 100644 change/@itwin-node-cli-authorization-eb8d8df9-ceb5-48f8-b62b-067e591495f7.json create mode 100644 packages/electron/src/static/auth-template.html create mode 100644 packages/node-cli/src/static/auth-template.html diff --git a/change/@itwin-electron-authorization-5263eced-a690-4d92-b59d-be3e53caf8af.json b/change/@itwin-electron-authorization-5263eced-a690-4d92-b59d-be3e53caf8af.json new file mode 100644 index 00000000..02680ad4 --- /dev/null +++ b/change/@itwin-electron-authorization-5263eced-a690-4d92-b59d-be3e53caf8af.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "add styled html loopback pages", + "packageName": "@itwin/electron-authorization", + "email": "ben-polinsky@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/@itwin-node-cli-authorization-eb8d8df9-ceb5-48f8-b62b-067e591495f7.json b/change/@itwin-node-cli-authorization-eb8d8df9-ceb5-48f8-b62b-067e591495f7.json new file mode 100644 index 00000000..4485162b --- /dev/null +++ b/change/@itwin-node-cli-authorization-eb8d8df9-ceb5-48f8-b62b-067e591495f7.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "add styled html loopback pages", + "packageName": "@itwin/node-cli-authorization", + "email": "ben-polinsky@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/electron/package.json b/packages/electron/package.json index 0449de58..42d5a05b 100644 --- a/packages/electron/package.json +++ b/packages/electron/package.json @@ -13,9 +13,10 @@ } }, "scripts": { - "build": "npm run -s build:cjs && npm run -s build:esm", + "build": "pnpm run -s build:cjs && pnpm run -s build:esm && pnpm run copy:assets", "build:cjs": "tsc 1>&2 -p tsconfig.cjs.json", "build:esm": "tsc 1>&2 -p tsconfig.esm.json", + "copy:assets": "cpx src/static/* dist/main/static && cpx src/static/* lib/cjs/main/static && cpx src/static/* lib/esm/main/static", "cover": "nyc npm test", "clean": "rimraf lib", "docs": "RUSHSTACK_FILE_ERROR_BASE_FOLDER='../..' betools docs --includes=../../generated-docs/extract --json=../../generated-docs/auth-clients/electron-authorization/file.json --tsIndexFile=./docsIndex.ts --onlyJson", @@ -50,13 +51,14 @@ "@itwin/core-bentley": "^3.7.0", "@itwin/eslint-plugin": "^3.3.0", "@playwright/test": "~1.35.1", - "@types/chai-as-promised": "^7.1.1", "@types/chai": "^4.2.22", + "@types/chai-as-promised": "^7.1.1", "@types/mocha": "^8.2.3", "@types/node": "^16.0.0", "@types/sinon": "^10.0.13", - "chai-as-promised": "^7.1.1", "chai": "^4.2.22", + "chai-as-promised": "^7.1.1", + "cpx2": "^5.0.0", "dotenv": "~16.0.3", "electron": "^26.0.0", "eslint": "^7.32.0", diff --git a/packages/electron/src/main/LoopbackWebServer.ts b/packages/electron/src/main/LoopbackWebServer.ts index 2f12d09a..162f0678 100644 --- a/packages/electron/src/main/LoopbackWebServer.ts +++ b/packages/electron/src/main/LoopbackWebServer.ts @@ -5,9 +5,11 @@ // Code based on the blog article @ https://authguidance.com import * as Http from "http"; +import * as path from "path"; +import { readFileSync } from "fs"; import type { AuthorizationErrorJson, AuthorizationResponseJson } from "@openid/appauth"; import type { ElectronAuthorizationEvents } from "./Events"; -import { Logger } from "@itwin/core-bentley"; +import { assert, Logger } from "@itwin/core-bentley"; const loggerCategory = "electron-auth"; type StateEventsPair = [string, ElectronAuthorizationEvents]; @@ -34,6 +36,20 @@ class AuthorizationState { } } +interface HtmlTemplateParams { + pageTitle: string; + contentTitle: string; + contentMessage: string; +} + +interface OidcUrlSearchParams { + state: string | null; + code: string | null; + error: string | null; + errorUri: string | null; + errorDescription: string | null; +} + /** * Web server to listen to authorization requests/responses for the DesktopAuthorizationClient * @internal @@ -76,7 +92,6 @@ export class LoopbackWebServer { else LoopbackWebServer._httpServer = undefined; }); - } /** Listen/Handle browser events */ @@ -84,17 +99,11 @@ export class LoopbackWebServer { if (!httpRequest.url) return; - // Parse the request URL to determine the authorization code, state and errors if any - const redirectedUrl = new URL(httpRequest.url, LoopbackWebServer._redirectUri); - const searchParams = redirectedUrl.searchParams; + const { state, code, error, errorUri, errorDescription } = LoopbackWebServer.parseUrlSearchParams(httpRequest.url); - const state = searchParams.get("state") || undefined; - const code = searchParams.get("code"); - const error = searchParams.get("error"); - if (!state) { - // ignore irrelevant requests (e.g. favicon.ico) + // ignore irrelevant requests (e.g. favicon.ico) + if (!state) return; - } // Look up context for the corresponding outgoing request const authorizationEvents = LoopbackWebServer._authState.getEvents(state); @@ -104,24 +113,64 @@ export class LoopbackWebServer { // Notify listeners of the code response or error let authorizationResponse: AuthorizationResponseJson | null = null; let authorizationError: AuthorizationErrorJson | null = null; + let httpResponseContent: HtmlTemplateParams; + + httpResponse.writeHead(200, { "Content-Type": "text/html" }); // eslint-disable-line @typescript-eslint/naming-convention + if (error) { - const errorUri = searchParams.get("error_uri") || undefined; - const errorDescription = searchParams.get("error_description") || undefined; - authorizationError = { error, error_description: errorDescription, error_uri: errorUri, state }; // eslint-disable-line @typescript-eslint/naming-convention - httpResponse.write("

Sign in error!

"); // TODO: Needs localization - httpResponse.end(); + authorizationError = { error, error_description: errorDescription ?? undefined, error_uri: errorUri ?? undefined, state }; // eslint-disable-line @typescript-eslint/naming-convention + httpResponseContent = { + pageTitle: "iTwin Auth Sign in error", + contentTitle: "Sign in Error", + contentMessage: "Please check your application's error console.", + }; + // TODO: Needs localization } else { - authorizationResponse = { code: code!, state }; - httpResponse.writeHead(200, { "Content-Type": "text/html" }); // eslint-disable-line @typescript-eslint/naming-convention - httpResponse.write("

Sign in was successful!

You can close this browser window and return to the application"); // TODO: Needs localization - httpResponse.end(); + assert(!!code, "Auth response code is not present"); + authorizationResponse = { code, state }; + httpResponseContent = { + pageTitle: "iTwin Auth - Sign in successful", + contentTitle: "Sign in was successful!", + contentMessage: "You can close this browser window and return to the application.", + }; } + + const html = LoopbackWebServer.getHtmlTemplate( + httpResponseContent + ); + + httpResponse.write(html); + httpResponse.end(); authorizationEvents.onAuthorizationResponse.raiseEvent(authorizationError, authorizationResponse); - // Handle the authorization completed event authorizationEvents.onAuthorizationResponseCompleted.addOnce((_authCompletedError?: AuthorizationErrorJson) => { // Stop the web server now that the signin attempt has finished LoopbackWebServer.stop(); }); } + + private static parseUrlSearchParams(url: string): OidcUrlSearchParams { + // Parse the request URL to determine the authorization code, state and errors if any + const redirectedUrl = new URL(url, LoopbackWebServer._redirectUri); + const searchParams = redirectedUrl.searchParams; + + const state = searchParams.get("state"); + const code = searchParams.get("code"); + const error = searchParams.get("error"); + const errorUri = searchParams.get("error_uri"); + const errorDescription = searchParams.get("error_description"); + + return { + state, code, error, errorUri, errorDescription, + }; + } + + private static getHtmlTemplate({ pageTitle, contentTitle, contentMessage }: HtmlTemplateParams): string { + let template = readFileSync(path.resolve(__filename, "..", "static", "auth-template.html"), "utf-8"); + template = template.replace("{--pageTitle--}", pageTitle); + template = template.replace("{--contentTitle--}", contentTitle); + template = template.replace("{--contentMessage--}", contentMessage); + + return template; + } } diff --git a/packages/electron/src/static/auth-template.html b/packages/electron/src/static/auth-template.html new file mode 100644 index 00000000..c8781813 --- /dev/null +++ b/packages/electron/src/static/auth-template.html @@ -0,0 +1,110 @@ + + + + {--pageTitle--} + + + + + + + + + + +
+
+ Bentley Systems logo +
+ +
+

{--contentTitle--}

+ {--contentMessage--} +
+ + +
+ + + \ No newline at end of file diff --git a/packages/node-cli/package.json b/packages/node-cli/package.json index 984081d8..53600e2b 100644 --- a/packages/node-cli/package.json +++ b/packages/node-cli/package.json @@ -6,7 +6,8 @@ "description": "Node.js command-line authorization client for iTwin platform", "scripts": { "build": "npm run -s build:cjs", - "build:cjs": "tsc 1>&2 --outDir lib/cjs", + "build:cjs": "tsc 1>&2 --outDir lib/cjs && npm run copy:assets", + "copy:assets": "cpx src/static/* lib/cjs/static", "clean": "rimraf lib", "docs": "RUSHSTACK_FILE_ERROR_BASE_FOLDER='../..' betools docs --includes=../../generated-docs/extract --json=../../generated-docs/auth-clients/node-cli-authorization/file.json --tsIndexFile=./index.ts --onlyJson", "lint": "eslint -f visualstudio \"./src/**/*.ts\" 1>&2", @@ -43,11 +44,12 @@ "@types/node": "^18.11.5", "chai": "^4.2.22", "chai-as-promised": "^7.1.1", - "source-map-support": "^0.5.9", + "cpx2": "^5.0.0", "eslint": "^7.32.0", "mocha": "^8.2.3", "nyc": "^15.1.0", "rimraf": "^3.0.2", + "source-map-support": "^0.5.9", "typescript": "~5.0.2" }, "peerDependencies": { diff --git a/packages/node-cli/src/Client.ts b/packages/node-cli/src/Client.ts index 3222ee82..e5192d7d 100644 --- a/packages/node-cli/src/Client.ts +++ b/packages/node-cli/src/Client.ts @@ -8,6 +8,8 @@ * @module Authorization */ +import { readFileSync } from "fs"; +import * as path from "path"; import * as Http from "http"; import * as open from "open"; import { assert, BeEvent, BentleyError, Logger } from "@itwin/core-bentley"; @@ -59,6 +61,20 @@ export interface NodeCliAuthorizationConfiguration { readonly expiryBuffer?: number; } +interface HtmlTemplateParams { + pageTitle: string; + contentTitle: string; + contentMessage: string; +} + +interface OidcUrlSearchParams { + state: string | null; + code: string | null; + error: string | null; + errorUri: string | null; + errorDescription: string | null; +} + /** * Utility to generate OIDC/OAuth tokens for command-line applications * This client is intended for developer tooling and does not aspire to provide the full set of functionality @@ -174,7 +190,7 @@ export class NodeCliAuthorizationClient implements AuthorizationClient { await authorizationRequest.setupCodeVerifier(); const authorizationEvents = new NodeCliAuthorizationEvents(); - this.startLoopbackWebServer(this._bakedConfig.redirectUri, authorizationRequest.state, authorizationEvents); + this.startLoopbackWebServer(authorizationRequest.state, authorizationEvents); const authorizationHandler = new NodeCliAuthorizationRequestHandler(authorizationEvents); const notifier = new AuthorizationNotifier(); @@ -250,38 +266,46 @@ export class NodeCliAuthorizationClient implements AuthorizationClient { return tokenHandler.performTokenRequest(this._configuration, tokenRequest); } - private startLoopbackWebServer(redirectUrl: string, authState: string, authEvents: NodeCliAuthorizationEvents) { + private startLoopbackWebServer(authState: string, authEvents: NodeCliAuthorizationEvents) { const onBrowserRequest = (httpRequest: Http.IncomingMessage, httpResponse: Http.ServerResponse) => { if (!httpRequest.url) return; // Parse the request URL to determine the authorization code, state and errors if any - const redirectedUrl = new URL(httpRequest.url, redirectUrl); - const searchParams = redirectedUrl.searchParams; + const { state, code, error, errorUri, errorDescription } = this.parseUrlSearchParams(httpRequest.url); - const state = searchParams.get("state") || undefined; if (!state || state !== authState) return; // ignore irrelevant requests (e.g. favicon.ico) // Notify listeners of the code response or error let authorizationResponse: AuthorizationResponseJson | null = null; let authorizationError: AuthorizationErrorJson | null = null; - const error = searchParams.get("error"); + let httpResponseContent: HtmlTemplateParams; + + httpResponse.writeHead(200, { "Content-Type": "text/html" }); // eslint-disable-line @typescript-eslint/naming-convention + if (error) { - const errorUri = searchParams.get("error_uri") || undefined; - const errorDescription = searchParams.get("error_description") || undefined; - authorizationError = { error, error_description: errorDescription, error_uri: errorUri, state }; // eslint-disable-line @typescript-eslint/naming-convention - httpResponse.write("

Sign in error!

"); - httpResponse.end(); + authorizationError = { error, error_description: errorDescription ?? undefined, error_uri: errorUri ?? undefined, state }; // eslint-disable-line @typescript-eslint/naming-convention + httpResponseContent = { + pageTitle: "iTwin Auth Sign in error", + contentTitle: "Sign in Error", + contentMessage: "Please check your application's error console.", + }; } else { - const code = searchParams.get("code"); if (!code) throw new Error("Unexpected failure - AuthorizationResponse is missing a code value"); + authorizationResponse = { code, state }; - httpResponse.writeHead(200, { "Content-Type": "text/html" }); // eslint-disable-line @typescript-eslint/naming-convention - httpResponse.write("

Sign in was successful!

You can close this browser window and return to the application"); - httpResponse.end(); + httpResponseContent = { + pageTitle: "iTwin Auth - Sign in successful", + contentTitle: "Sign in was successful!", + contentMessage: "You can close this browser window and return to the application.", + }; } + + const template = this.getHtmlTemplate(httpResponseContent); + httpResponse.write(template); + httpResponse.end(); authEvents.onAuthorizationResponse.raiseEvent(authorizationError, authorizationResponse); // Stop the web server when the signin attempt has finished @@ -289,7 +313,32 @@ export class NodeCliAuthorizationClient implements AuthorizationClient { }; const httpServer = Http.createServer(onBrowserRequest); - httpServer.listen(new URL(redirectUrl).port); + httpServer.listen(new URL(this._bakedConfig.redirectUri).port); + } + + private parseUrlSearchParams(url: string): OidcUrlSearchParams { + // Parse the request URL to determine the authorization code, state and errors if any + const redirectedUrl = new URL(url, this._bakedConfig.redirectUri); + const searchParams = redirectedUrl.searchParams; + + const state = searchParams.get("state"); + const code = searchParams.get("code"); + const error = searchParams.get("error"); + const errorUri = searchParams.get("error_uri"); + const errorDescription = searchParams.get("error_description"); + + return { + state, code, error, errorUri, errorDescription, + }; + } + + private getHtmlTemplate({ pageTitle, contentTitle, contentMessage }: HtmlTemplateParams): string { + let template = readFileSync(path.resolve(__dirname, "static", "auth-template.html"), "utf-8"); + template = template.replace("{--pageTitle--}", pageTitle); + template = template.replace("{--contentTitle--}", contentTitle); + template = template.replace("{--contentMessage--}", contentMessage); + + return template; } } diff --git a/packages/node-cli/src/static/auth-template.html b/packages/node-cli/src/static/auth-template.html new file mode 100644 index 00000000..c8781813 --- /dev/null +++ b/packages/node-cli/src/static/auth-template.html @@ -0,0 +1,110 @@ + + + + {--pageTitle--} + + + + + + + + + + +
+
+ Bentley Systems logo +
+ +
+

{--contentTitle--}

+ {--contentMessage--} +
+ + +
+ + + \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a1dadb8d..f4000433 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -72,6 +72,7 @@ importers: '@types/sinon': ^10.0.13 chai: ^4.2.22 chai-as-promised: ^7.1.1 + cpx2: ^5.0.0 dotenv: ~16.0.3 electron: ^26.0.0 electron-store: ^8.1.0 @@ -103,6 +104,7 @@ importers: '@types/sinon': 10.0.16 chai: 4.3.7 chai-as-promised: 7.1.1_chai@4.3.7 + cpx2: 5.0.0 dotenv: 16.0.3 electron: 26.0.0 eslint: 7.32.0 @@ -129,6 +131,7 @@ importers: '@types/node': ^18.11.5 chai: ^4.2.22 chai-as-promised: ^7.1.1 + cpx2: ^5.0.0 eslint: ^7.32.0 keytar: ^7.8.0 mocha: ^8.2.3 @@ -154,6 +157,7 @@ importers: '@types/node': 18.17.1 chai: 4.3.7 chai-as-promised: 7.1.1_chai@4.3.7 + cpx2: 5.0.0 eslint: 7.32.0 mocha: 8.4.0 nyc: 15.1.0 @@ -680,7 +684,7 @@ packages: dev: false /@itwin/core-bentley/3.7.12: - resolution: {integrity: sha512-QwOTXxqeR3Oofh3QYOvbadJZG10JFu5n96j/H5o2aDGyTIuJoBaan51u5lCD7kiNl+CC60bPqekr0/gx91L7zQ==} + resolution: {integrity: sha1-UE6Q9Q1lxzH+/rtZ89WcN1ZYyqI=} /@itwin/core-common/4.0.6_@itwin+core-bentley@3.7.12: resolution: {integrity: sha512-1N92kv3bqY/01Mdto/GCKgO5dG6dli9d06lp1KpHTUuwPpWVob1oIF/w5ij+0C4QvHq54SZlCxDfNaC7FH6lKQ==} @@ -1873,7 +1877,7 @@ packages: fs-extra: 7.0.1 import-lazy: 4.0.0 jju: 1.4.0 - resolve: 1.22.2 + resolve: 1.22.3 semver: 7.3.8 z-schema: 5.0.5 dev: true @@ -1881,7 +1885,7 @@ packages: /@rushstack/rig-package/0.3.18: resolution: {integrity: sha512-SGEwNTwNq9bI3pkdd01yCaH+gAsHqs0uxfGvtw9b0LJXH52qooWXnrFTRRLG1aL9pf+M2CARdrA9HLHJys3jiQ==} dependencies: - resolve: 1.22.2 + resolve: 1.22.3 strip-json-comments: 3.1.1 dev: true @@ -2188,7 +2192,7 @@ packages: dev: true /@types/node/18.17.1: - resolution: {integrity: sha512-xlR1jahfizdplZYRU59JlUx9uzF1ARa8jbhM11ccpCJya8kvos5jwdm2ZAgxSCwOl0fq21svP18EVwPBXMQudw==} + resolution: {integrity: sha1-hMMpA786CfeHjDkdMf8I9v59gzU=} /@types/parse-json/4.0.0: resolution: {integrity: sha1-L4u0QUNNFjs1+4/9zNcTiSf/uMA=} @@ -2762,7 +2766,7 @@ packages: concat-map: 0.0.1 /brace-expansion/2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + resolution: {integrity: sha1-HtxFng8MVISG7Pn8mfIiE2S5oK4=} dependencies: balanced-match: 1.0.2 @@ -2951,7 +2955,7 @@ packages: normalize-path: 3.0.0 readdirp: 3.5.0 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /chokidar/3.5.3: @@ -2966,7 +2970,7 @@ packages: normalize-path: 3.0.0 readdirp: 3.6.0 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 /chownr/1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} @@ -3139,7 +3143,7 @@ packages: dev: true /cpx2/3.0.2: - resolution: {integrity: sha512-xVmdulZJVGSV+c8KkZ9IQY+RgyL9sGeVqScI2e7NtsEY9SVKcQXM4v0/9OLU0W0YtL9nmmqrtWjs5rpvgHn9Hg==} + resolution: {integrity: sha1-YlvJ5p0Mog6Y8Fx1VbqzWYlmgOU=} engines: {node: '>=6.5'} hasBin: true dependencies: @@ -3159,6 +3163,28 @@ packages: - supports-color dev: true + /cpx2/5.0.0: + resolution: {integrity: sha1-vFyaP1rLfjLHBh7p8OpaglVbdIc=} + engines: {node: '>=16'} + hasBin: true + dependencies: + debounce: 1.2.1 + debug: 4.3.4 + duplexer: 0.1.2 + fs-extra: 11.1.1 + glob-gitignore: 1.0.14 + glob2base: 0.0.12 + ignore: 5.2.4 + minimatch: 9.0.3 + p-map: 6.0.0 + resolve: 1.22.3 + safe-buffer: 5.2.1 + shell-quote: 1.8.1 + subarg: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /cross-fetch/3.1.5: resolution: {integrity: sha1-4TifRNnnunZ5B/evhFR4eVKrU08=} dependencies: @@ -3237,7 +3263,7 @@ packages: dev: false /debounce/1.2.1: - resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} + resolution: {integrity: sha1-OIgdj0FmpcWEgCDBGCe4NLyz4KU=} dev: true /debug/2.6.9: @@ -3275,7 +3301,7 @@ packages: dev: true /debug/4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + resolution: {integrity: sha1-Exn2V5NX8jONMzfSzdSRS7XcyGU=} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -3286,7 +3312,7 @@ packages: ms: 2.1.2 /debug/4.3.4_supports-color@8.1.1: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + resolution: {integrity: sha1-Exn2V5NX8jONMzfSzdSRS7XcyGU=} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -3307,7 +3333,7 @@ packages: engines: {node: '>=10'} /decompress-response/6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + resolution: {integrity: sha1-yjh2Et234QS9FthaqwDV7PCcZvw=} engines: {node: '>=10'} dependencies: mimic-response: 3.1.0 @@ -3345,7 +3371,7 @@ packages: dev: false /define-properties/1.2.0: - resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} + resolution: {integrity: sha1-UpiFcGcMnqzt2AZPSpkPJAWEm9U=} engines: {node: '>= 0.4'} dependencies: has-property-descriptors: 1.0.0 @@ -3486,7 +3512,7 @@ packages: dev: true /duplexer/0.1.2: - resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + resolution: {integrity: sha1-Or5DrvODX4rgd9E23c4PJ2sEAOY=} dev: true /ecdsa-sig-formatter/1.0.11: @@ -3536,7 +3562,7 @@ packages: dev: false /end-of-stream/1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + resolution: {integrity: sha1-WuZKX0UFe682JuwU2gyl5LJDHrA=} dependencies: once: 1.4.0 @@ -3654,14 +3680,14 @@ packages: dev: true /escape-string-regexp/4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + resolution: {integrity: sha1-FLqDpdNz49MR5a/KKc9b+tllvzQ=} engines: {node: '>=10'} /eslint-import-resolver-node/0.3.4: - resolution: {integrity: sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==} + resolution: {integrity: sha1-hf+oGULCUBLYIxCW3fZ5wDBCxxc=} dependencies: debug: 2.6.9 - resolve: 1.22.2 + resolve: 1.22.3 transitivePeerDependencies: - supports-color dev: true @@ -3830,7 +3856,7 @@ packages: dev: true /eslint-plugin-prefer-arrow/1.2.3_eslint@7.32.0: - resolution: {integrity: sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==} + resolution: {integrity: sha1-5/uz+kzYT/EBW5xRrYZVDlUEEEE=} peerDependencies: eslint: '>=2.0.0' dependencies: @@ -4164,7 +4190,7 @@ packages: dev: true /find-index/0.1.1: - resolution: {integrity: sha512-uJ5vWrfBKMcE6y2Z8834dwEZj9mNGxYa3t3I53OwFeuZ8D9oc2E5zcsrkuhX6h4iYrjhiv0T3szQmxlAV9uxDg==} + resolution: {integrity: sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ=} dev: true /find-up/3.0.0: @@ -4273,8 +4299,17 @@ packages: universalify: 2.0.0 dev: true + /fs-extra/11.1.1: + resolution: {integrity: sha1-2mn3w587ACN4sJVLtq5+/cCHbi0=} + engines: {node: '>=14.14'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: true + /fs-extra/7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + resolution: {integrity: sha1-TxicRKoSO4lfcigE9V6iPq3DSOk=} engines: {node: '>=6 <7 || >=8'} dependencies: graceful-fs: 4.2.11 @@ -4283,7 +4318,7 @@ packages: dev: true /fs-extra/8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + resolution: {integrity: sha1-SdQ8RaiM2Wd2aMt74bRu/bjS4cA=} engines: {node: '>=6 <7 || >=8'} dependencies: graceful-fs: 4.2.11 @@ -4301,8 +4336,15 @@ packages: requiresBuild: true optional: true + /fsevents/2.3.3: + resolution: {integrity: sha1-ysZAd4XQNnWipeGlMFxpezR9kNY=} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + optional: true + /function-bind/1.1.1: - resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + resolution: {integrity: sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=} /function.prototype.name/1.1.5: resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} @@ -4336,7 +4378,7 @@ packages: dev: true /get-intrinsic/1.2.1: - resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} + resolution: {integrity: sha1-0pVkT+1FBfyc3pUsN+4StHeoPYI=} dependencies: function-bind: 1.1.1 has: 1.0.3 @@ -4395,6 +4437,18 @@ packages: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} dev: false + /glob-gitignore/1.0.14: + resolution: {integrity: sha1-i3CMsCnnO9OI0i9/k1ITqpOh58I=} + engines: {node: '>= 6'} + dependencies: + glob: 7.2.3 + ignore: 5.2.4 + lodash.difference: 4.5.0 + lodash.union: 4.6.0 + make-array: 1.0.5 + util.inherits: 1.0.3 + dev: true + /glob-hasher-darwin-arm64/1.3.0: resolution: {integrity: sha1-mN0xp7CMPJlAqZ6jka9AV3gGXXk=} engines: {node: '>= 10'} @@ -4490,7 +4544,7 @@ packages: path-is-absolute: 1.0.1 /glob2base/0.0.12: - resolution: {integrity: sha512-ZyqlgowMbfj2NPjxaZZ/EtsXlOch28FRXgMd64vqZWk1bT9+wvSRLYD1om9M7QfQru51zJPAT17qXm4/zd+9QA==} + resolution: {integrity: sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=} engines: {node: '>= 0.10'} dependencies: find-index: 0.1.1 @@ -4523,7 +4577,7 @@ packages: dev: true /globalthis/1.0.3: - resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + resolution: {integrity: sha1-WFKIKlK4DcMBsGYCc+HtCC8LbM8=} engines: {node: '>= 0.4'} dependencies: define-properties: 1.2.0 @@ -4604,17 +4658,17 @@ packages: engines: {node: '>=8'} /has-property-descriptors/1.0.0: - resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + resolution: {integrity: sha1-YQcIYAYG02lh7QTBlhk7amB/qGE=} dependencies: get-intrinsic: 1.2.1 dev: true /has-proto/1.0.1: - resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + resolution: {integrity: sha1-GIXBMFU4lYr/Rp/vN5N8InlUCOA=} engines: {node: '>= 0.4'} /has-symbols/1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + resolution: {integrity: sha1-u3ssQ0klHc6HsSX3vfh0qnyLOfg=} engines: {node: '>= 0.4'} /has-tostringtag/1.0.0: @@ -4625,7 +4679,7 @@ packages: dev: true /has/1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + resolution: {integrity: sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=} engines: {node: '>= 0.4.0'} dependencies: function-bind: 1.1.1 @@ -4847,7 +4901,7 @@ packages: dev: true /is-core-module/2.12.1: - resolution: {integrity: sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==} + resolution: {integrity: sha1-DAtohbb4ABHHFUHOFcjWbPWk+f0=} dependencies: has: 1.0.3 dev: true @@ -5170,7 +5224,7 @@ packages: dev: true /jsonfile/4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + resolution: {integrity: sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=} optionalDependencies: graceful-fs: 4.2.11 dev: true @@ -5264,7 +5318,7 @@ packages: dependencies: glob-hasher: 1.3.0 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /language-subtag-registry/0.3.22: @@ -5428,6 +5482,10 @@ packages: resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} dev: false + /lodash.difference/4.5.0: + resolution: {integrity: sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=} + dev: true + /lodash.flattendeep/4.4.0: resolution: {integrity: sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==} dev: true @@ -5448,6 +5506,10 @@ packages: resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} dev: true + /lodash.union/4.6.0: + resolution: {integrity: sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=} + dev: true + /lodash/4.17.21: resolution: {integrity: sha1-Z5WRxWTDv/quhFTPCz3zcMPWkRw=} @@ -5518,6 +5580,11 @@ packages: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} dev: true + /make-array/1.0.5: + resolution: {integrity: sha1-Mmp2NcdWqfYc4LKm/dXMNGBBm8s=} + engines: {node: '>=0.10.0'} + dev: true + /make-dir/3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} @@ -5670,8 +5737,15 @@ packages: brace-expansion: 2.0.1 dev: true + /minimatch/9.0.3: + resolution: {integrity: sha1-puAMPeRMOlQr+q5wq/wiQgptqCU=} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minimist/1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + resolution: {integrity: sha1-waRk52kzAuCCoHXO4MBXdBrEdyw=} /mkdirp-classic/0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} @@ -5758,10 +5832,10 @@ packages: dev: true /ms/2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=} /ms/2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + resolution: {integrity: sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=} /ms/2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -5963,7 +6037,7 @@ packages: resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} /object-keys/1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + resolution: {integrity: sha1-HEfyct8nfzsdrwYWd9nILiMixg4=} engines: {node: '>= 0.4'} dev: true @@ -6036,7 +6110,7 @@ packages: dev: false /once/1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=} dependencies: wrappy: 1.0.2 @@ -6139,6 +6213,11 @@ packages: aggregate-error: 3.1.0 dev: true + /p-map/6.0.0: + resolution: {integrity: sha1-TZxA0xcWMvhsR2AbcJ9LSs1w/tQ=} + engines: {node: '>=16'} + dev: true + /p-try/2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -6221,7 +6300,7 @@ packages: dev: false /path-browserify/1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + resolution: {integrity: sha1-2YRUqcN1PVeQhg8W9ohnueRr4f0=} dev: true /path-exists/3.0.0: @@ -6248,7 +6327,7 @@ packages: dev: true /path-parse/1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + resolution: {integrity: sha1-+8EUtgykKzDZ2vWFjkvWi77bZzU=} dev: true /path-to-regexp/0.1.7: @@ -6364,12 +6443,12 @@ packages: dev: true /process/0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + resolution: {integrity: sha1-czIwDoQBYb2j5podHZGn1LwW8YI=} engines: {node: '>= 0.6.0'} dev: true /progress/2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + resolution: {integrity: sha1-foz42PW48jnBvGi+tOt4Vn1XLvg=} engines: {node: '>=0.4.0'} /prompts/2.4.2: @@ -6409,7 +6488,7 @@ packages: dev: false /pump/3.0.0: - resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + resolution: {integrity: sha1-tKIRaBW94vTh6mAjVOjHVWUQemQ=} dependencies: end-of-stream: 1.4.4 once: 1.4.0 @@ -6587,7 +6666,7 @@ packages: dev: true /resolve/1.19.0: - resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} + resolution: {integrity: sha1-GvW/YwQJc0oGfK4pMYqsf6KaJnw=} dependencies: is-core-module: 2.12.1 path-parse: 1.0.7 @@ -6603,7 +6682,7 @@ packages: dev: true /resolve/1.22.3: - resolution: {integrity: sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw==} + resolution: {integrity: sha1-S0BVNJ/7liYAly2h/cM8RqTrMoM=} hasBin: true dependencies: is-core-module: 2.12.1 @@ -6674,7 +6753,7 @@ packages: dev: true /safe-buffer/5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + resolution: {integrity: sha1-Hq+fqb2x/dTsdfWPnNtOa3gn7sY=} /safe-regex-test/1.0.0: resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} @@ -6699,7 +6778,7 @@ packages: dev: false /semver/6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + resolution: {integrity: sha1-VW0u+GiRRuRtzqS/3QlfNDTf/LQ=} hasBin: true dev: true @@ -6803,7 +6882,7 @@ packages: dev: true /shell-quote/1.8.1: - resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + resolution: {integrity: sha1-bb9Nt1UVrVusY7TxiUw6FUx2ZoA=} dev: true /shiki/0.14.3: @@ -7035,7 +7114,7 @@ packages: engines: {node: '>=8'} /subarg/1.0.0: - resolution: {integrity: sha512-RIrIdRY0X1xojthNcVtgT9sjpOGagEUKpZdgBUi054OEPFo282yg+zE+t1Rj3+RqKq2xStL7uUHhY+AjbC4BXg==} + resolution: {integrity: sha1-9izxdYHplrSPyWVpn1TAauJouNI=} dependencies: minimist: 1.2.8 dev: true @@ -7069,7 +7148,7 @@ packages: has-flag: 4.0.0 /supports-preserve-symlinks-flag/1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + resolution: {integrity: sha1-btpL00SjyUrqN21MwxvHcxEDngk=} engines: {node: '>= 0.4'} dev: true @@ -7342,7 +7421,7 @@ packages: dev: false /universalify/0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + resolution: {integrity: sha1-tkb2m+OULavOzJ1mOcgNwQXvqmY=} engines: {node: '>= 4.0.0'} dev: true @@ -7384,6 +7463,11 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: false + /util.inherits/1.0.3: + resolution: {integrity: sha1-qcYmoNBtNIKdR7pWyrEnjXRfnOY=} + engines: {node: '>=4'} + dev: true + /utility-types/3.10.0: resolution: {integrity: sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==} engines: {node: '>= 4'} @@ -7523,7 +7607,7 @@ packages: strip-ansi: 6.0.1 /wrappy/1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=} /write-file-atomic/3.0.3: resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==}