From fce7ae9d1422bdd577ed4552471f485da9752f7d Mon Sep 17 00:00:00 2001 From: Gabriel Pinto Date: Fri, 10 May 2024 19:08:20 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Real=20CLI;=20Use=20flee?= =?UTF-8?q?k=20functions=20instead=20of=20the=20network=20gateway;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- global.d.ts | 1 + locales/en.d.ts | 3 + locales/en.json | 49 + package.json | 28 +- pnpm-lock.yaml | 951 +++++++++++++++++- src/bundler/esbuild.ts | 13 - src/bundler/types.ts | 6 - src/cli.ts | 70 ++ src/commands/build/fleek/index.ts | 317 ++++++ src/{ => commands/build}/fleek/types.ts | 0 src/commands/build/index.ts | 91 ++ src/commands/build/next/index.ts | 28 + src/{ => commands/build}/next/types.ts | 0 src/commands/build/open-next/index.ts | 72 ++ .../build}/open-next/open-next.config.ts | 4 +- src/{ => commands/build}/open-next/types.ts | 0 .../prompts/getPersonalAccessTokenOrPrompt.ts | 15 + .../build/prompts/getProjectIdOrPrompt.ts | 15 + .../build/prompts/getProjectPathOrPrompt.ts | 24 + src/{ => commands/build}/proxy/index.ts | 0 src/errors/FleekDirUploadError.ts | 14 + src/errors/FleekError.ts | 16 + src/errors/FleekFileUploadError.ts | 12 + src/errors/MissingExpectedDataError.ts | 8 + src/errors/MissingPersonalAccessTokenError.ts | 13 + src/errors/MissingProjectIdError.ts | 13 + src/errors/NextjsBuildError.ts | 14 + src/errors/NextjsBundleError.ts | 14 + src/errors/PackageJsonInvalidFileError.ts | 11 + src/errors/PackageJsonInvalidPathError.ts | 11 + src/errors/PackageJsonMissingAccessError.ts | 11 + src/errors/PackageJsonUnknownError.ts | 12 + src/errors/ProjectInvalidDirError.ts | 11 + src/errors/ProjectInvalidPathError.ts | 11 + src/errors/ProjectPathMissingAccessError.ts | 11 + src/errors/ProjectPathUnknownError.ts | 12 + src/errors/UnauthenticatedError.ts | 13 + src/fleek/index.ts | 278 +---- src/fleek/sdk.ts | 24 + src/index.ts | 70 +- src/next/index.ts | 19 - src/open-next/index.ts | 51 - src/output/Output.ts | 196 ++++ src/output/utils/eraseLines.ts | 5 + src/output/utils/wait.ts | 43 + src/prompts/prompt.ts | 22 + src/prompts/textPrompt.ts | 9 + src/secrets.ts | 9 + src/utils.ts | 13 +- src/utils/json.ts | 30 + src/utils/packageManager.ts | 9 +- src/utils/translation.ts | 57 ++ src/utils/update-notifier.ts | 51 + src/validation/isProjectPathValid.ts | 58 ++ tsconfig.json | 5 +- 55 files changed, 2416 insertions(+), 427 deletions(-) create mode 100644 global.d.ts create mode 100644 locales/en.d.ts create mode 100644 locales/en.json delete mode 100644 src/bundler/esbuild.ts delete mode 100644 src/bundler/types.ts create mode 100644 src/cli.ts create mode 100644 src/commands/build/fleek/index.ts rename src/{ => commands/build}/fleek/types.ts (100%) create mode 100644 src/commands/build/index.ts create mode 100644 src/commands/build/next/index.ts rename src/{ => commands/build}/next/types.ts (100%) create mode 100644 src/commands/build/open-next/index.ts rename src/{ => commands/build}/open-next/open-next.config.ts (98%) rename src/{ => commands/build}/open-next/types.ts (100%) create mode 100644 src/commands/build/prompts/getPersonalAccessTokenOrPrompt.ts create mode 100644 src/commands/build/prompts/getProjectIdOrPrompt.ts create mode 100644 src/commands/build/prompts/getProjectPathOrPrompt.ts rename src/{ => commands/build}/proxy/index.ts (100%) create mode 100644 src/errors/FleekDirUploadError.ts create mode 100644 src/errors/FleekError.ts create mode 100644 src/errors/FleekFileUploadError.ts create mode 100644 src/errors/MissingExpectedDataError.ts create mode 100644 src/errors/MissingPersonalAccessTokenError.ts create mode 100644 src/errors/MissingProjectIdError.ts create mode 100644 src/errors/NextjsBuildError.ts create mode 100644 src/errors/NextjsBundleError.ts create mode 100644 src/errors/PackageJsonInvalidFileError.ts create mode 100644 src/errors/PackageJsonInvalidPathError.ts create mode 100644 src/errors/PackageJsonMissingAccessError.ts create mode 100644 src/errors/PackageJsonUnknownError.ts create mode 100644 src/errors/ProjectInvalidDirError.ts create mode 100644 src/errors/ProjectInvalidPathError.ts create mode 100644 src/errors/ProjectPathMissingAccessError.ts create mode 100644 src/errors/ProjectPathUnknownError.ts create mode 100644 src/errors/UnauthenticatedError.ts create mode 100644 src/fleek/sdk.ts delete mode 100644 src/next/index.ts delete mode 100644 src/open-next/index.ts create mode 100644 src/output/Output.ts create mode 100644 src/output/utils/eraseLines.ts create mode 100644 src/output/utils/wait.ts create mode 100644 src/prompts/prompt.ts create mode 100644 src/prompts/textPrompt.ts create mode 100644 src/secrets.ts create mode 100644 src/utils/json.ts create mode 100644 src/utils/translation.ts create mode 100644 src/utils/update-notifier.ts create mode 100644 src/validation/isProjectPathValid.ts diff --git a/global.d.ts b/global.d.ts new file mode 100644 index 0000000..2704cdf --- /dev/null +++ b/global.d.ts @@ -0,0 +1 @@ +declare module 'update-notifier-cjs'; diff --git a/locales/en.d.ts b/locales/en.d.ts new file mode 100644 index 0000000..1f6db8d --- /dev/null +++ b/locales/en.d.ts @@ -0,0 +1,3 @@ +import en from './en.json'; + +export type En = keyof typeof en; diff --git a/locales/en.json b/locales/en.json new file mode 100644 index 0000000..be1c53d --- /dev/null +++ b/locales/en.json @@ -0,0 +1,49 @@ +{ + "spinnerInvokedDelay": "Spinner invoked ({message}) with a {delay}ms delay", + "spinnerStopped": "Spinner stopped ({spinnerMessage})", + "success": "Success", + "warning": "Warning", + "error": "Error", + "mistake": "Oops", + "ready": "Ready", + "aboutFleek": "Build lightning fast Web3 Apps on the Edge", + "appBuildDescription": "Build and deploy your Next.js app to Fleek", + "enableDebugMode": "Enable debug mode", + "printVersionDetails": "Display the version details", + "printHelp": "Print help", + "buildingApp": "Building App", + "buildingNextjsApp": "Building Next.js App", + "building": "Building...", + "bundlingApp": "Bundling App", + "bundlingNextjsApp": "Bundling App", + "bundling": "Bundling...", + "bundlingSuccess": "Next.js app successfully bundled", + "invalidPath": "The path {path} is not valid, you must provide a valid path to your Next.js app's root directory", + "invalidDir": "The path {path} is not a valid directory, you must provide a valid path to your Next.js app's root directory", + "enterProjectName": "Please enter the path to your Next.js project's root directory", + "enterPersonalAccessToken": "Please enter your Fleek personal access token", + "enterProjectId": "Please enter your Fleek project ID", + "appBuildSuccess": "Your Next.js app has been successfully deployed to Fleek", + "creatingAssets": "Assets", + "assetsCreated": "Assets - {url}", + "assetsCreatedDryRun": "Assets - (dry run)", + "creatingFunction": "λ Deploying {name}", + "functionCreated": "λ Deployed {name}", + "functionCreatedDryRun": "λ Deployed {name} - (dry run)", + "nextjsBuildStarted": "Building Next.js app...", + "nextjsBuildSuccess": "Next.js app successfully built", + "nextjsBuildErrorIncludeError": "Failed to build Next.js app: {error}", + "nextjsBuildError": "Failed to build Next.js app.", + "bundlingStarted": "Bundling Next.js app...", + "bundlingErrorIncludeError": "Failed to bundle Next.js app: {error}", + "bundlingError": "Failed to bundle Next.js app.", + "fleekFileUploadErrorIncludeError": "Failed to upload file to Fleek: {error}", + "fleekFileUploadError": "Failed to upload file to Fleek.", + "fleekDirUploadErrorIncludeError": "Failed to upload directory to Fleek: {error}", + "fleekDirUploadError": "Failed to upload directory to Fleek.", + "projectPath": "Project path: {projectPath}", + "buildCommand": "Build command: {buildCommand}", + "fleekSdkAuth": "Authenticating Fleek SDK...", + "fleekSdkAuthError": "Failed to authenticate the Fleek SDK.", + "fleekSdkAuthSuccess": "Fleek SDK authenticated." +} diff --git a/package.json b/package.json index 5213ea8..ae2e5f2 100644 --- a/package.json +++ b/package.json @@ -5,12 +5,13 @@ "bin": { "fleek-next": "./dist/index.js" }, - "main": "dist/index.js", - "types": "dist/index.d.ts", - "module": "dist/module/index.js", + "main": "./dist/cli.js", + "types": "./dist/cli.d.ts", + "module": "./dist/cli.js", "repository": "https://github.com/fleekxyz/fleek-next-cli", "homepage": "https://github.com/fleekxyz/fleek-next-cli/#readme", "license": "MIT", + "type": "module", "keywords": [ "nextjs", "next", @@ -33,12 +34,27 @@ "eslint" ] }, + "engines": { + "node": ">=18" + }, "dependencies": { "@fleekxyz/open-next": "3.0.1", "@fleekxyz/proxy": "0.0.8", - "@fleekxyz/sdk": "0.7.3", + "@fleekxyz/sdk": "2.0.0-staging.1", + "ansi-escapes": "^7.0.0", + "as-table": "^1.0.55", + "boxen": "^7.1.1", + "chalk": "^5.3.0", + "commander": "^12.0.0", + "conf": "^12.0.0", "esbuild": "^0.20.2", - "esbuild-plugin-alias": "^0.2.1" + "esbuild-plugin-alias": "^0.2.1", + "files-from-path": "^1.0.4", + "ora": "^8.0.1", + "prompts": "^2.4.2", + "semver": "^7.6.2", + "update-notifier": "^7.0.0", + "update-notifier-cjs": "^5.1.6" }, "devDependencies": { "@commitlint/cli": "^19.3.0", @@ -46,6 +62,8 @@ "@eslint/js": "^9.2.0", "@types/jest": "^29.5.12", "@types/node": "^20.12.11", + "@types/prompts": "^2.4.9", + "@types/semver": "^7.5.8", "eslint": "^9.2.0", "husky": "^9.0.11", "jest": "^29.7.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 980efb2..194c6df 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,14 +15,50 @@ importers: specifier: 0.0.8 version: 0.0.8 '@fleekxyz/sdk': - specifier: 0.7.3 - version: 0.7.3 + specifier: 2.0.0-staging.1 + version: 2.0.0-staging.1 + ansi-escapes: + specifier: ^7.0.0 + version: 7.0.0 + as-table: + specifier: ^1.0.55 + version: 1.0.55 + boxen: + specifier: ^7.1.1 + version: 7.1.1 + chalk: + specifier: ^5.3.0 + version: 5.3.0 + commander: + specifier: ^12.0.0 + version: 12.0.0 + conf: + specifier: ^12.0.0 + version: 12.0.0 esbuild: specifier: ^0.20.2 version: 0.20.2 esbuild-plugin-alias: specifier: ^0.2.1 version: 0.2.1 + files-from-path: + specifier: ^1.0.4 + version: 1.0.4 + ora: + specifier: ^8.0.1 + version: 8.0.1 + prompts: + specifier: ^2.4.2 + version: 2.4.2 + semver: + specifier: ^7.6.2 + version: 7.6.2 + update-notifier: + specifier: ^7.0.0 + version: 7.0.0 + update-notifier-cjs: + specifier: ^5.1.6 + version: 5.1.6 devDependencies: '@commitlint/cli': specifier: ^19.3.0 @@ -39,6 +75,12 @@ importers: '@types/node': specifier: ^20.12.11 version: 20.12.11 + '@types/prompts': + specifier: ^2.4.9 + version: 2.4.9 + '@types/semver': + specifier: ^7.5.8 + version: 7.5.8 eslint: specifier: ^9.2.0 version: 9.2.0 @@ -667,8 +709,8 @@ packages: '@fleekxyz/proxy@0.0.8': resolution: {integrity: sha512-+PFh+Zha66XBN1LCUF7fVBHZ+Qth9pTpDK9rvpayx2a+koX0ZRxLywXQpY4TYiDU3SpukKATf8Vctv4CrNpQ/A==} - '@fleekxyz/sdk@0.7.3': - resolution: {integrity: sha512-iYaKOltqXLKBbg0HRlnQrPIlnzgesSz4PwWFOErE/Ygu+D93WN7owxvjgyK5JbsylLp4wDpgdsFVCM0Ql0/bAg==} + '@fleekxyz/sdk@2.0.0-staging.1': + resolution: {integrity: sha512-fEaW/5yvaj6FpBiIB71kw8Kn13gQpOgw1JigREaS0K7ROSnKkN6GydfJU4JAuZhPBQ4busSQ04ijYf36cTU9ow==} engines: {node: '>=18.0.0'} '@humanwhocodes/config-array@0.13.0': @@ -808,9 +850,25 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@pnpm/config.env-replace@1.1.0': + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + + '@pnpm/network.ca-file@1.0.2': + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} + + '@pnpm/npm-conf@2.2.2': + resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} + engines: {node: '>=12'} + '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + '@sindresorhus/is@5.6.0': + resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} + engines: {node: '>=14.16'} + '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} @@ -1008,6 +1066,10 @@ packages: resolution: {integrity: sha512-IHk53BVw6MPMi2Gsn+hCng8rFA3ZmR3Rk7GllxDUW9qFJl/hiSvskn7XldkECapQVkIg/1dHpMAxI9xSTaLLSA==} engines: {node: '>=14.0.0'} + '@szmarczak/http-timer@5.0.1': + resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} + engines: {node: '>=14.16'} + '@tsconfig/node18@1.0.3': resolution: {integrity: sha512-RbwvSJQsuN9TB04AQbGULYfOGE/RnSFk/FLQ5b0NmDf5Kx2q/lABZbHQPKCO1vZ6Fiwkplu+yb9pGdLy1iGseQ==} @@ -1029,6 +1091,9 @@ packages: '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + '@types/http-cache-semantics@4.0.4': + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} @@ -1047,6 +1112,9 @@ packages: '@types/node@20.12.11': resolution: {integrity: sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==} + '@types/prompts@2.4.9': + resolution: {integrity: sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==} + '@types/resolve@1.20.6': resolution: {integrity: sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==} @@ -1134,12 +1202,23 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} ajv@8.13.0: resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==} + ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} @@ -1148,6 +1227,10 @@ packages: resolution: {integrity: sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==} engines: {node: '>=14.16'} + ansi-escapes@7.0.0: + resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} + engines: {node: '>=18'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1189,6 +1272,12 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + as-table@1.0.55: + resolution: {integrity: sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==} + + atomically@2.0.3: + resolution: {integrity: sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw==} + babel-jest@29.7.0: resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1220,6 +1309,14 @@ packages: bowser@2.11.0: resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} + boxen@5.1.2: + resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==} + engines: {node: '>=10'} + + boxen@7.1.1: + resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} + engines: {node: '>=14.16'} + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -1245,6 +1342,14 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + cacheable-lookup@7.0.0: + resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} + engines: {node: '>=14.16'} + + cacheable-request@10.2.14: + resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} + engines: {node: '>=14.16'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -1257,6 +1362,10 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} + camelcase@7.0.1: + resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} + engines: {node: '>=14.16'} + caniuse-lite@1.0.30001616: resolution: {integrity: sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==} @@ -1276,6 +1385,9 @@ packages: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} + ci-info@2.0.0: + resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} + ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} @@ -1283,10 +1395,22 @@ packages: cjs-module-lexer@1.3.1: resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} + cli-boxes@2.2.1: + resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==} + engines: {node: '>=6'} + + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + cli-cursor@4.0.0: resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + cli-truncate@4.0.0: resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} engines: {node: '>=18'} @@ -1322,6 +1446,10 @@ packages: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} + commander@12.0.0: + resolution: {integrity: sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==} + engines: {node: '>=18'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -1331,9 +1459,24 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + conf@12.0.0: + resolution: {integrity: sha512-fIWyWUXrJ45cHCIQX+Ck1hrZDIf/9DR0P0Zewn3uNht28hbt5OfGUq8rRWsxi96pZWPyBEd0eY9ama01JTaknA==} + engines: {node: '>=18'} + confbox@0.1.7: resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + + configstore@5.0.1: + resolution: {integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==} + engines: {node: '>=8'} + + configstore@6.0.0: + resolution: {integrity: sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==} + engines: {node: '>=12'} + conventional-changelog-angular@7.0.0: resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} engines: {node: '>=16'} @@ -1376,10 +1519,22 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + crypto-random-string@2.0.0: + resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} + engines: {node: '>=8'} + + crypto-random-string@4.0.0: + resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} + engines: {node: '>=12'} + dargs@8.1.0: resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} engines: {node: '>=12'} + debounce-fn@5.1.2: + resolution: {integrity: sha512-Sr4SdOZ4vw6eQDvPYNxHogvrxmCIld/VenC5JbNrFwMiwd7lY/Z18ZFfo+EWNG4DD9nFlAujWAo/wGuOPHmy5A==} + engines: {node: '>=12'} + debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -1389,6 +1544,10 @@ packages: supports-color: optional: true + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + dedent@1.5.3: resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} peerDependencies: @@ -1397,6 +1556,10 @@ packages: babel-plugin-macros: optional: true + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -1404,6 +1567,10 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} + defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} @@ -1420,9 +1587,20 @@ packages: resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} engines: {node: '>=8'} + dot-prop@6.0.1: + resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} + engines: {node: '>=10'} + + dot-prop@8.0.2: + resolution: {integrity: sha512-xaBe6ZT4DHPkg0k4Ytbvn5xoxgpG0jOS1dYxSOwAHPuNLjP3/OzN0gH55SrLqpx8cBfSaVt91lXYkApjb+nYdQ==} + engines: {node: '>=16'} + duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + electron-to-chromium@1.4.758: resolution: {integrity: sha512-/o9x6TCdrYZBMdGeTifAP3wlF/gVT+TtWJe3BSmtNh92Mw81U9hrYwW9OAGUh+sEOX/yz5e34sksqRruZbjYrw==} @@ -1436,10 +1614,21 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} + env-paths@3.0.0: + resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + environment@1.0.0: + resolution: {integrity: sha512-u6cXAuofxkVkkXHsBFBsYhRYjwGe5K16gv5j55kB872jJnw0dPm7WzMZ4pTeEIaXD2+G8PsQukvJ2rzqEluT/A==} + engines: {node: '>=18'} + error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} @@ -1461,6 +1650,14 @@ packages: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} + escape-goat@2.1.1: + resolution: {integrity: sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==} + engines: {node: '>=8'} + + escape-goat@4.0.0: + resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} + engines: {node: '>=12'} + escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -1561,6 +1758,10 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + files-from-path@1.0.4: + resolution: {integrity: sha512-sMNIVdpRh1uCSIaat3qnM3E6aA1C5FVn5/B16z8sN3gIMjZPkxtVCorkEL07xTcCIxVwTXzjU1Ota7Wif6RfQQ==} + engines: {node: '>=18'} + fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -1584,6 +1785,10 @@ packages: flatted@3.3.1: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + form-data-encoder@2.1.4: + resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} + engines: {node: '>= 14.17'} + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -1643,6 +1848,10 @@ packages: resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} engines: {node: '>=18'} + global-dirs@3.0.1: + resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} + engines: {node: '>=10'} + globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} @@ -1655,6 +1864,13 @@ packages: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} + got@12.6.1: + resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} + engines: {node: '>=14.16'} + + graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -1673,6 +1889,10 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + has-yarn@2.1.0: + resolution: {integrity: sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==} + engines: {node: '>=8'} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -1680,6 +1900,13 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + + http2-wrapper@2.2.1: + resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} + engines: {node: '>=10.19.0'} + human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -1701,6 +1928,14 @@ packages: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} + import-lazy@2.1.0: + resolution: {integrity: sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==} + engines: {node: '>=4'} + + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + import-local@3.1.0: resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} engines: {node: '>=8'} @@ -1719,6 +1954,13 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ini@2.0.0: + resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} + engines: {node: '>=10'} + ini@4.1.1: resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -1726,6 +1968,10 @@ packages: is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-ci@2.0.0: + resolution: {integrity: sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==} + hasBin: true + is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} @@ -1753,6 +1999,27 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-in-ci@0.1.0: + resolution: {integrity: sha512-d9PXLEY0v1iJ64xLiQMJ51J128EYHAaOR4yZqQi8aHGfw6KgifM3/Viw1oZZ1GCVmb3gBuyhLyHj0HgR2DhSXQ==} + engines: {node: '>=18'} + hasBin: true + + is-installed-globally@0.4.0: + resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} + engines: {node: '>=10'} + + is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + + is-npm@5.0.0: + resolution: {integrity: sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==} + engines: {node: '>=10'} + + is-npm@6.0.0: + resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -1777,9 +2044,26 @@ packages: resolution: {integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==} engines: {node: '>=8'} + is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + + is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + + is-unicode-supported@2.0.0: + resolution: {integrity: sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==} + engines: {node: '>=18'} + + is-yarn-global@0.3.0: + resolution: {integrity: sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isomorphic-fetch@3.0.0: + resolution: {integrity: sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==} + istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -1965,6 +2249,9 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-typed@8.0.1: + resolution: {integrity: sha512-XQmWYj2Sm4kn4WeTYvmpKEbyPsL7nBsb647c7pMe6l02/yx2+Jfc4dT6UZkEXnIUb5LhD55r2HPsJ1milQ4rDg==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -1984,6 +2271,10 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} + latest-version@7.0.0: + resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} + engines: {node: '>=14.16'} + leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -2054,10 +2345,18 @@ packages: lodash.upperfirst@4.3.1: resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} + log-symbols@6.0.0: + resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} + engines: {node: '>=18'} + log-update@6.0.0: resolution: {integrity: sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==} engines: {node: '>=18'} + lowercase-keys@3.0.0: + resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + lru-cache@10.2.2: resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} engines: {node: 14 || >=16.14} @@ -2065,6 +2364,10 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -2098,6 +2401,14 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + mimic-response@4.0.0: + resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -2137,6 +2448,15 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} @@ -2147,6 +2467,10 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + normalize-url@8.0.1: + resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} + engines: {node: '>=14.16'} + npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -2173,6 +2497,14 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + ora@8.0.1: + resolution: {integrity: sha512-ANIvzobt1rls2BDny5fWZ3ZVKyD6nscLvfFRpQgfWsythlcsVUC9kL0zq6j2Z5z9wwp1kd7wpsD/T9qNPVLCaQ==} + engines: {node: '>=18'} + + p-cancelable@3.0.0: + resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} + engines: {node: '>=12.20'} + p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -2201,6 +2533,10 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} + package-json@8.1.1: + resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} + engines: {node: '>=14.16'} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -2282,6 +2618,9 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + printable-characters@1.0.42: + resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==} + promise.series@0.2.0: resolution: {integrity: sha512-VWQJyU2bcDTgZw8kpfBpB/ejZASlCrzwz5f2hjb/zlujOEB4oeiAhHygAWq8ubsX2GVkD4kCU5V2dwOTaCY5EQ==} engines: {node: '>=0.12'} @@ -2290,19 +2629,50 @@ packages: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + pupa@2.1.1: + resolution: {integrity: sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==} + engines: {node: '>=8'} + + pupa@3.1.0: + resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} + engines: {node: '>=12.20'} + pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + registry-auth-token@5.0.2: + resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} + engines: {node: '>=14'} + + registry-url@5.1.0: + resolution: {integrity: sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==} + engines: {node: '>=8'} + + registry-url@6.0.1: + resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} + engines: {node: '>=12'} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -2311,6 +2681,9 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + resolve-cwd@3.0.0: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} engines: {node: '>=8'} @@ -2331,6 +2704,10 @@ packages: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true + responselike@3.0.0: + resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} + engines: {node: '>=14.16'} + restore-cursor@4.0.0: resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2345,12 +2722,20 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + semver-diff@3.1.1: + resolution: {integrity: sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==} + engines: {node: '>=8'} + + semver-diff@4.0.0: + resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} + engines: {node: '>=12'} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.6.1: - resolution: {integrity: sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA==} + semver@7.6.2: + resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} engines: {node: '>=10'} hasBin: true @@ -2405,6 +2790,10 @@ packages: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} + stdin-discarder@0.2.2: + resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} + engines: {node: '>=18'} + string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} @@ -2417,6 +2806,10 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + string-width@7.1.0: resolution: {integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==} engines: {node: '>=18'} @@ -2441,6 +2834,10 @@ packages: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -2448,6 +2845,9 @@ packages: strnum@1.0.5: resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + stubborn-fs@1.2.5: + resolution: {integrity: sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g==} + supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -2494,6 +2894,9 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + ts-api-utils@1.3.0: resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} engines: {node: '>=16'} @@ -2535,10 +2938,29 @@ packages: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} + type-fest@1.4.0: + resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} + engines: {node: '>=10'} + + type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + + type-fest@3.13.1: + resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} + engines: {node: '>=14.16'} + + typedarray-to-buffer@3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + typescript-eslint@7.8.0: resolution: {integrity: sha512-sheFG+/D8N/L7gC3WT0Q8sB97Nm573Yfr+vZFzl/4nBdYcmviBPtwGSX9TJ7wpVg28ocerKVOt+k2eGmHzcgVA==} engines: {node: ^18.18.0 || >=20.0.0} @@ -2557,6 +2979,10 @@ packages: ufo@1.5.3: resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + uint8array-extras@0.3.0: + resolution: {integrity: sha512-erJsJwQ0tKdwuqI0359U8ijkFmfiTcq25JvvzRVc1VP+2son1NJRXhxcAKJmAW3ajM8JSGAfsAXye8g4s+znxA==} + engines: {node: '>=18'} + undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} @@ -2564,12 +2990,28 @@ packages: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} + unique-string@2.0.0: + resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} + engines: {node: '>=8'} + + unique-string@3.0.0: + resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} + engines: {node: '>=12'} + update-browserslist-db@1.0.15: resolution: {integrity: sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' + update-notifier-cjs@5.1.6: + resolution: {integrity: sha512-wgxdSBWv3x/YpMzsWz5G4p4ec7JWD0HCl8W6bmNB6E5Gwo+1ym5oN4hiXpLf0mPySVEJEIsYlkshnplkg2OP9A==} + engines: {node: '>=14'} + + update-notifier@7.0.0: + resolution: {integrity: sha512-Hv25Bh+eAbOLlsjJreVPOs4vd51rrtCrmhyOJtbpAojro34jS4KQaEp4/EvlHJX7jSO42VvEFpkastVyXyIsdQ==} + engines: {node: '>=18'} + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -2584,11 +3026,31 @@ packages: walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-fetch@3.6.20: + resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + when-exit@2.1.2: + resolution: {integrity: sha512-u9J+toaf3CCxCAzM/484qNAxQE75rFdVgiFEEV8Xps2gzYhf0tx73s1WXDQhkwV17E3MxRMz40m7Ekd2/121Lg==} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true + widest-line@3.1.0: + resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} + engines: {node: '>=8'} + + widest-line@4.0.1: + resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} + engines: {node: '>=12'} + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -2597,6 +3059,10 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + wrap-ansi@9.0.0: resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} engines: {node: '>=18'} @@ -2604,10 +3070,21 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + write-file-atomic@3.0.3: + resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + write-file-atomic@4.0.2: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + xdg-basedir@4.0.0: + resolution: {integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==} + engines: {node: '>=8'} + + xdg-basedir@5.1.0: + resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} + engines: {node: '>=12'} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -3559,7 +4036,7 @@ snapshots: '@commitlint/is-ignored@19.2.2': dependencies: '@commitlint/types': 19.0.3 - semver: 7.6.1 + semver: 7.6.2 '@commitlint/lint@19.2.2': dependencies: @@ -3751,7 +4228,7 @@ snapshots: '@fleekxyz/proxy@0.0.8': {} - '@fleekxyz/sdk@0.7.3': {} + '@fleekxyz/sdk@2.0.0-staging.1': {} '@humanwhocodes/config-array@0.13.0': dependencies: @@ -3990,8 +4467,22 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 + '@pnpm/config.env-replace@1.1.0': {} + + '@pnpm/network.ca-file@1.0.2': + dependencies: + graceful-fs: 4.2.10 + + '@pnpm/npm-conf@2.2.2': + dependencies: + '@pnpm/config.env-replace': 1.1.0 + '@pnpm/network.ca-file': 1.0.2 + config-chain: 1.1.13 + '@sinclair/typebox@0.27.8': {} + '@sindresorhus/is@5.6.0': {} + '@sinonjs/commons@3.0.1': dependencies: type-detect: 4.0.8 @@ -4314,6 +4805,10 @@ snapshots: '@smithy/types': 2.12.0 tslib: 2.6.2 + '@szmarczak/http-timer@5.0.1': + dependencies: + defer-to-connect: 2.0.1 + '@tsconfig/node18@1.0.3': {} '@types/babel__core@7.20.5': @@ -4345,6 +4840,8 @@ snapshots: dependencies: '@types/node': 20.12.11 + '@types/http-cache-semantics@4.0.4': {} + '@types/istanbul-lib-coverage@2.0.6': {} '@types/istanbul-lib-report@3.0.3': @@ -4366,6 +4863,11 @@ snapshots: dependencies: undici-types: 5.26.5 + '@types/prompts@2.4.9': + dependencies: + '@types/node': 20.12.11 + kleur: 3.0.3 + '@types/resolve@1.20.6': {} '@types/semver@7.5.8': {} @@ -4391,7 +4893,7 @@ snapshots: graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 - semver: 7.6.1 + semver: 7.6.2 ts-api-utils: 1.3.0(typescript@5.4.5) optionalDependencies: typescript: 5.4.5 @@ -4438,7 +4940,7 @@ snapshots: globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.4 - semver: 7.6.1 + semver: 7.6.2 ts-api-utils: 1.3.0(typescript@5.4.5) optionalDependencies: typescript: 5.4.5 @@ -4454,7 +4956,7 @@ snapshots: '@typescript-eslint/types': 7.8.0 '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.5) eslint: 9.2.0 - semver: 7.6.1 + semver: 7.6.2 transitivePeerDependencies: - supports-color - typescript @@ -4475,6 +4977,10 @@ snapshots: acorn@8.11.3: {} + ajv-formats@2.1.1(ajv@8.13.0): + optionalDependencies: + ajv: 8.13.0 + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -4489,12 +4995,20 @@ snapshots: require-from-string: 2.0.2 uri-js: 4.4.1 + ansi-align@3.0.1: + dependencies: + string-width: 4.2.3 + ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 ansi-escapes@6.2.1: {} + ansi-escapes@7.0.0: + dependencies: + environment: 1.0.0 + ansi-regex@5.0.1: {} ansi-regex@6.0.1: {} @@ -4526,6 +5040,15 @@ snapshots: array-union@2.1.0: {} + as-table@1.0.55: + dependencies: + printable-characters: 1.0.42 + + atomically@2.0.3: + dependencies: + stubborn-fs: 1.2.5 + when-exit: 2.1.2 + babel-jest@29.7.0(@babel/core@7.24.5): dependencies: '@babel/core': 7.24.5 @@ -4582,6 +5105,28 @@ snapshots: bowser@2.11.0: {} + boxen@5.1.2: + dependencies: + ansi-align: 3.0.1 + camelcase: 6.3.0 + chalk: 4.1.2 + cli-boxes: 2.2.1 + string-width: 4.2.3 + type-fest: 0.20.2 + widest-line: 3.1.0 + wrap-ansi: 7.0.0 + + boxen@7.1.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 7.0.1 + chalk: 5.3.0 + cli-boxes: 3.0.0 + string-width: 5.1.2 + type-fest: 2.19.0 + widest-line: 4.0.1 + wrap-ansi: 8.1.0 + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -4612,12 +5157,26 @@ snapshots: buffer-from@1.1.2: {} + cacheable-lookup@7.0.0: {} + + cacheable-request@10.2.14: + dependencies: + '@types/http-cache-semantics': 4.0.4 + get-stream: 6.0.1 + http-cache-semantics: 4.1.1 + keyv: 4.5.4 + mimic-response: 4.0.0 + normalize-url: 8.0.1 + responselike: 3.0.0 + callsites@3.1.0: {} camelcase@5.3.1: {} camelcase@6.3.0: {} + camelcase@7.0.1: {} + caniuse-lite@1.0.30001616: {} chalk@2.4.2: @@ -4635,14 +5194,22 @@ snapshots: char-regex@1.0.2: {} + ci-info@2.0.0: {} + ci-info@3.9.0: {} cjs-module-lexer@1.3.1: {} + cli-boxes@2.2.1: {} + + cli-boxes@3.0.0: {} + cli-cursor@4.0.0: dependencies: restore-cursor: 4.0.0 + cli-spinners@2.9.2: {} + cli-truncate@4.0.0: dependencies: slice-ansi: 5.0.0 @@ -4674,6 +5241,8 @@ snapshots: commander@11.1.0: {} + commander@12.0.0: {} + commander@2.20.3: {} compare-func@2.0.0: @@ -4683,8 +5252,42 @@ snapshots: concat-map@0.0.1: {} + conf@12.0.0: + dependencies: + ajv: 8.13.0 + ajv-formats: 2.1.1(ajv@8.13.0) + atomically: 2.0.3 + debounce-fn: 5.1.2 + dot-prop: 8.0.2 + env-paths: 3.0.0 + json-schema-typed: 8.0.1 + semver: 7.6.2 + uint8array-extras: 0.3.0 + confbox@0.1.7: {} + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + + configstore@5.0.1: + dependencies: + dot-prop: 5.3.0 + graceful-fs: 4.2.11 + make-dir: 3.1.0 + unique-string: 2.0.0 + write-file-atomic: 3.0.3 + xdg-basedir: 4.0.0 + + configstore@6.0.0: + dependencies: + dot-prop: 6.0.1 + graceful-fs: 4.2.11 + unique-string: 3.0.0 + write-file-atomic: 3.0.3 + xdg-basedir: 5.1.0 + conventional-changelog-angular@7.0.0: dependencies: compare-func: 2.0.0 @@ -4739,18 +5342,36 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + crypto-random-string@2.0.0: {} + + crypto-random-string@4.0.0: + dependencies: + type-fest: 1.4.0 + dargs@8.1.0: {} + debounce-fn@5.1.2: + dependencies: + mimic-fn: 4.0.0 + debug@4.3.4: dependencies: ms: 2.1.2 + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + dedent@1.5.3: {} + deep-extend@0.6.0: {} + deep-is@0.1.4: {} deepmerge@4.3.1: {} + defer-to-connect@2.0.1: {} + detect-newline@3.1.0: {} diff-sequences@29.6.3: {} @@ -4763,8 +5384,18 @@ snapshots: dependencies: is-obj: 2.0.0 + dot-prop@6.0.1: + dependencies: + is-obj: 2.0.0 + + dot-prop@8.0.2: + dependencies: + type-fest: 3.13.1 + duplexer@0.1.2: {} + eastasianwidth@0.2.0: {} + electron-to-chromium@1.4.758: {} emittery@0.13.1: {} @@ -4773,8 +5404,14 @@ snapshots: emoji-regex@8.0.0: {} + emoji-regex@9.2.2: {} + env-paths@2.2.1: {} + env-paths@3.0.0: {} + + environment@1.0.0: {} + error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 @@ -4816,6 +5453,10 @@ snapshots: escalade@3.1.2: {} + escape-goat@2.1.1: {} + + escape-goat@4.0.0: {} + escape-string-regexp@1.0.5: {} escape-string-regexp@2.0.0: {} @@ -4956,6 +5597,10 @@ snapshots: dependencies: flat-cache: 4.0.1 + files-from-path@1.0.4: + dependencies: + graceful-fs: 4.2.11 + fill-range@7.0.1: dependencies: to-regex-range: 5.0.1 @@ -4983,6 +5628,8 @@ snapshots: flatted@3.3.1: {} + form-data-encoder@2.1.4: {} + fs.realpath@1.0.0: {} fsevents@2.3.3: @@ -5036,6 +5683,10 @@ snapshots: dependencies: ini: 4.1.1 + global-dirs@3.0.1: + dependencies: + ini: 2.0.0 + globals@11.12.0: {} globals@14.0.0: {} @@ -5049,6 +5700,22 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 + got@12.6.1: + dependencies: + '@sindresorhus/is': 5.6.0 + '@szmarczak/http-timer': 5.0.1 + cacheable-lookup: 7.0.0 + cacheable-request: 10.2.14 + decompress-response: 6.0.0 + form-data-encoder: 2.1.4 + get-stream: 6.0.1 + http2-wrapper: 2.2.1 + lowercase-keys: 3.0.0 + p-cancelable: 3.0.0 + responselike: 3.0.0 + + graceful-fs@4.2.10: {} + graceful-fs@4.2.11: {} graphemer@1.4.0: {} @@ -5061,12 +5728,21 @@ snapshots: has-flag@4.0.0: {} + has-yarn@2.1.0: {} + hasown@2.0.2: dependencies: function-bind: 1.1.2 html-escaper@2.0.2: {} + http-cache-semantics@4.1.1: {} + + http2-wrapper@2.2.1: + dependencies: + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + human-signals@2.1.0: {} human-signals@5.0.0: {} @@ -5080,6 +5756,10 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 + import-lazy@2.1.0: {} + + import-lazy@4.0.0: {} + import-local@3.1.0: dependencies: pkg-dir: 4.2.0 @@ -5096,10 +5776,18 @@ snapshots: inherits@2.0.4: {} + ini@1.3.8: {} + + ini@2.0.0: {} + ini@4.1.1: {} is-arrayish@0.2.1: {} + is-ci@2.0.0: + dependencies: + ci-info: 2.0.0 + is-core-module@2.13.1: dependencies: hasown: 2.0.2 @@ -5120,6 +5808,19 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-in-ci@0.1.0: {} + + is-installed-globally@0.4.0: + dependencies: + global-dirs: 3.0.1 + is-path-inside: 3.0.3 + + is-interactive@2.0.0: {} + + is-npm@5.0.0: {} + + is-npm@6.0.0: {} + is-number@7.0.0: {} is-obj@2.0.0: {} @@ -5134,8 +5835,23 @@ snapshots: dependencies: text-extensions: 2.4.0 + is-typedarray@1.0.0: {} + + is-unicode-supported@1.3.0: {} + + is-unicode-supported@2.0.0: {} + + is-yarn-global@0.3.0: {} + isexe@2.0.0: {} + isomorphic-fetch@3.0.0: + dependencies: + node-fetch: 2.7.0 + whatwg-fetch: 3.6.20 + transitivePeerDependencies: + - encoding + istanbul-lib-coverage@3.2.2: {} istanbul-lib-instrument@5.2.1: @@ -5154,7 +5870,7 @@ snapshots: '@babel/parser': 7.24.5 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 - semver: 7.6.1 + semver: 7.6.2 transitivePeerDependencies: - supports-color @@ -5433,7 +6149,7 @@ snapshots: jest-util: 29.7.0 natural-compare: 1.4.0 pretty-format: 29.7.0 - semver: 7.6.1 + semver: 7.6.2 transitivePeerDependencies: - supports-color @@ -5508,6 +6224,8 @@ snapshots: json-schema-traverse@1.0.0: {} + json-schema-typed@8.0.1: {} + json-stable-stringify-without-jsonify@1.0.1: {} json5@2.2.3: {} @@ -5520,6 +6238,10 @@ snapshots: kleur@3.0.3: {} + latest-version@7.0.0: + dependencies: + package-json: 8.1.1 + leven@3.1.0: {} levn@0.4.1: @@ -5592,6 +6314,11 @@ snapshots: lodash.upperfirst@4.3.1: {} + log-symbols@6.0.0: + dependencies: + chalk: 5.3.0 + is-unicode-supported: 1.3.0 + log-update@6.0.0: dependencies: ansi-escapes: 6.2.1 @@ -5600,15 +6327,21 @@ snapshots: strip-ansi: 7.1.0 wrap-ansi: 9.0.0 + lowercase-keys@3.0.0: {} + lru-cache@10.2.2: {} lru-cache@5.1.1: dependencies: yallist: 3.1.1 + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + make-dir@4.0.0: dependencies: - semver: 7.6.1 + semver: 7.6.2 make-error@1.3.6: {} @@ -5631,6 +6364,10 @@ snapshots: mimic-fn@4.0.0: {} + mimic-response@3.1.0: {} + + mimic-response@4.0.0: {} + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -5666,12 +6403,18 @@ snapshots: natural-compare@1.4.0: {} + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + node-int64@0.4.0: {} node-releases@2.0.14: {} normalize-path@3.0.0: {} + normalize-url@8.0.1: {} + npm-run-path@4.0.1: dependencies: path-key: 3.1.1 @@ -5703,6 +6446,20 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + ora@8.0.1: + dependencies: + chalk: 5.3.0 + cli-cursor: 4.0.0 + cli-spinners: 2.9.2 + is-interactive: 2.0.0 + is-unicode-supported: 2.0.0 + log-symbols: 6.0.0 + stdin-discarder: 0.2.2 + string-width: 7.1.0 + strip-ansi: 7.1.0 + + p-cancelable@3.0.0: {} + p-limit@2.3.0: dependencies: p-try: 2.2.0 @@ -5729,6 +6486,13 @@ snapshots: p-try@2.2.0: {} + package-json@8.1.1: + dependencies: + got: 12.6.1 + registry-auth-token: 5.0.2 + registry-url: 6.0.1 + semver: 7.6.2 + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -5791,6 +6555,8 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 + printable-characters@1.0.42: {} + promise.series@0.2.0: {} prompts@2.4.2: @@ -5798,18 +6564,51 @@ snapshots: kleur: 3.0.3 sisteransi: 1.0.5 + proto-list@1.2.4: {} + punycode@2.3.1: {} + pupa@2.1.1: + dependencies: + escape-goat: 2.1.1 + + pupa@3.1.0: + dependencies: + escape-goat: 4.0.0 + pure-rand@6.1.0: {} queue-microtask@1.2.3: {} + quick-lru@5.1.1: {} + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + react-is@18.3.1: {} + registry-auth-token@5.0.2: + dependencies: + '@pnpm/npm-conf': 2.2.2 + + registry-url@5.1.0: + dependencies: + rc: 1.2.8 + + registry-url@6.0.1: + dependencies: + rc: 1.2.8 + require-directory@2.1.1: {} require-from-string@2.0.2: {} + resolve-alpn@1.2.1: {} + resolve-cwd@3.0.0: dependencies: resolve-from: 5.0.0 @@ -5826,6 +6625,10 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + responselike@3.0.0: + dependencies: + lowercase-keys: 3.0.0 + restore-cursor@4.0.0: dependencies: onetime: 5.1.2 @@ -5839,9 +6642,17 @@ snapshots: dependencies: queue-microtask: 1.2.3 + semver-diff@3.1.1: + dependencies: + semver: 6.3.1 + + semver-diff@4.0.0: + dependencies: + semver: 7.6.2 + semver@6.3.1: {} - semver@7.6.1: {} + semver@7.6.2: {} shebang-command@2.0.0: dependencies: @@ -5887,6 +6698,8 @@ snapshots: dependencies: escape-string-regexp: 2.0.0 + stdin-discarder@0.2.2: {} + string-argv@0.3.2: {} string-length@4.0.2: @@ -5900,6 +6713,12 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + string-width@7.1.0: dependencies: emoji-regex: 10.3.0 @@ -5920,10 +6739,14 @@ snapshots: strip-final-newline@3.0.0: {} + strip-json-comments@2.0.1: {} + strip-json-comments@3.1.1: {} strnum@1.0.5: {} + stubborn-fs@1.2.5: {} + supports-color@5.5.0: dependencies: has-flag: 3.0.0 @@ -5965,6 +6788,8 @@ snapshots: dependencies: is-number: 7.0.0 + tr46@0.0.3: {} + ts-api-utils@1.3.0(typescript@5.4.5): dependencies: typescript: 5.4.5 @@ -5978,7 +6803,7 @@ snapshots: json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 - semver: 7.6.1 + semver: 7.6.2 typescript: 5.4.5 yargs-parser: 21.1.1 optionalDependencies: @@ -5997,8 +6822,20 @@ snapshots: type-detect@4.0.8: {} + type-fest@0.20.2: {} + type-fest@0.21.3: {} + type-fest@1.4.0: {} + + type-fest@2.19.0: {} + + type-fest@3.13.1: {} + + typedarray-to-buffer@3.1.5: + dependencies: + is-typedarray: 1.0.0 + typescript-eslint@7.8.0(eslint@9.2.0)(typescript@5.4.5): dependencies: '@typescript-eslint/eslint-plugin': 7.8.0(@typescript-eslint/parser@7.8.0(eslint@9.2.0)(typescript@5.4.5))(eslint@9.2.0)(typescript@5.4.5) @@ -6014,16 +6851,62 @@ snapshots: ufo@1.5.3: {} + uint8array-extras@0.3.0: {} + undici-types@5.26.5: {} unicorn-magic@0.1.0: {} + unique-string@2.0.0: + dependencies: + crypto-random-string: 2.0.0 + + unique-string@3.0.0: + dependencies: + crypto-random-string: 4.0.0 + update-browserslist-db@1.0.15(browserslist@4.23.0): dependencies: browserslist: 4.23.0 escalade: 3.1.2 picocolors: 1.0.0 + update-notifier-cjs@5.1.6: + dependencies: + boxen: 5.1.2 + chalk: 4.1.2 + configstore: 5.0.1 + has-yarn: 2.1.0 + import-lazy: 2.1.0 + is-ci: 2.0.0 + is-installed-globally: 0.4.0 + is-npm: 5.0.0 + is-yarn-global: 0.3.0 + isomorphic-fetch: 3.0.0 + pupa: 2.1.1 + registry-auth-token: 5.0.2 + registry-url: 5.1.0 + semver: 7.6.2 + semver-diff: 3.1.1 + xdg-basedir: 4.0.0 + transitivePeerDependencies: + - encoding + + update-notifier@7.0.0: + dependencies: + boxen: 7.1.1 + chalk: 5.3.0 + configstore: 6.0.0 + import-lazy: 4.0.0 + is-in-ci: 0.1.0 + is-installed-globally: 0.4.0 + is-npm: 6.0.0 + latest-version: 7.0.0 + pupa: 3.1.0 + semver: 7.6.2 + semver-diff: 4.0.0 + xdg-basedir: 5.1.0 + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -6040,10 +6923,29 @@ snapshots: dependencies: makeerror: 1.0.12 + webidl-conversions@3.0.1: {} + + whatwg-fetch@3.6.20: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + when-exit@2.1.2: {} + which@2.0.2: dependencies: isexe: 2.0.0 + widest-line@3.1.0: + dependencies: + string-width: 4.2.3 + + widest-line@4.0.1: + dependencies: + string-width: 5.1.2 + word-wrap@1.2.5: {} wrap-ansi@7.0.0: @@ -6052,6 +6954,12 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + wrap-ansi@9.0.0: dependencies: ansi-styles: 6.2.1 @@ -6060,11 +6968,22 @@ snapshots: wrappy@1.0.2: {} + write-file-atomic@3.0.3: + dependencies: + imurmurhash: 0.1.4 + is-typedarray: 1.0.0 + signal-exit: 3.0.7 + typedarray-to-buffer: 3.1.5 + write-file-atomic@4.0.2: dependencies: imurmurhash: 0.1.4 signal-exit: 3.0.7 + xdg-basedir@4.0.0: {} + + xdg-basedir@5.1.0: {} + y18n@5.0.8: {} yallist@3.1.1: {} diff --git a/src/bundler/esbuild.ts b/src/bundler/esbuild.ts deleted file mode 100644 index 6b98b32..0000000 --- a/src/bundler/esbuild.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { BundleFn, BundleFnProps } from './types'; -import { build as esbuild } from 'esbuild'; - -export const bundle: BundleFn = async (props: BundleFnProps) => { - const { filePath, outputPath } = props; - - esbuild({ - entryPoints: [filePath], - bundle: true, - minify: true, - outfile: outputPath, - }); -}; diff --git a/src/bundler/types.ts b/src/bundler/types.ts deleted file mode 100644 index fab1f13..0000000 --- a/src/bundler/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type BundleFnProps = { - filePath: string; - outputPath: string; -}; - -export type BundleFn = (props: BundleFnProps) => Promise; diff --git a/src/cli.ts b/src/cli.ts new file mode 100644 index 0000000..b3eb6a5 --- /dev/null +++ b/src/cli.ts @@ -0,0 +1,70 @@ +#!/usr/bin/env node + +import { Command } from 'commander'; +import { t } from './utils/translation.js'; +import { Output } from './output/Output.js'; +import cmdBuild from './commands/build/index.js'; + +const isDebugging = process.argv.includes('--debug'); +export const output = new Output({ + stream: process.stdout, + debug: isDebugging, +}); + +type InitArgs = { + version: string; + parser: (program: Command) => void; +}; + +const logo = ` + + ad88 88 88 + d8" 88 88 + 88 88 88 + MM88MMM 88 ,adPPYba, ,adPPYba, 88 ,d8 + 88 88 a8P_____88 a8P_____88 88 ,a8" + 88 88 8PP""""""" 8PP""""""" 8888[ + 88 88 "8b, ,aa "8b, ,aa 88'"Yba, + 88 88 '"Ybbd8" '"Ybbd8" 88 'Y8a + + ⚡ ${t('aboutFleek')} ⚡ +`; + +export const init = ({ version, parser }: InitArgs) => { + const program: Command = new Command() + .name('fleek-next') + .option('--debug', t('enableDebugMode')) + .option('-h, --help', t('printHelp')) + .action(() => program.outputHelp()) + .version(version, '-v, --version', t('printVersionDetails')); + + program.addHelpText('beforeAll', logo).showHelpAfterError(); + + // Initialise commands + const commands = [cmdBuild]; + + for (const cmd of commands) { + cmd(program); + } + + // Init parser (unawaited) + parser(program); + + return program; +}; + +export const asyncParser = async (program: Command) => { + try { + await program.parseAsync(process.argv); + + process.exit(0); + } catch (err) { + console.error((err as Error).message || err); + + if ((err as Error).stack) { + console.error((err as Error).stack); + } + + process.exit(1); + } +}; diff --git a/src/commands/build/fleek/index.ts b/src/commands/build/fleek/index.ts new file mode 100644 index 0000000..824f7ba --- /dev/null +++ b/src/commands/build/fleek/index.ts @@ -0,0 +1,317 @@ +import { FleekFunction, FleekSdk } from '@fleekxyz/sdk'; +import { BaseFunction, OpenNextOutput } from '../open-next/types'; +import path from 'node:path'; +import { Routes } from '@fleekxyz/proxy'; +import { build } from 'esbuild'; +import * as fs from 'node:fs/promises'; +import alias from 'esbuild-plugin-alias'; +import { readdirSync } from 'node:fs'; +import { createRequire } from 'node:module'; + +import { copyDir } from '../../../utils.js'; +import { FunctionManifest, MiddlewareManifest } from '../next/types.js'; +import { Origin } from './types.js'; +import { proxyFunctionTemplate } from '../proxy/index.js'; +import { output } from '../../../cli.js'; +import { t } from '../../../utils/translation.js'; +import { uploadDirectory, uploadFunction } from '../../../fleek/index.js'; +import { FleekFileUploadError } from '../../../errors/FleekFileUploadError.js'; +import { FleekDirUploadError } from '../../../errors/FleekDirUploadError.js'; + +const require = createRequire(import.meta.url); + +export async function uploadFunctionFile(props: { + functionName: string; + filePath: string; + remotePath: string; + fleekSdk: FleekSdk; + dryRun?: boolean; +}): Promise { + const { functionName, filePath, remotePath, fleekSdk, dryRun } = props; + + let data = await fs.readFile(filePath, 'utf8'); + + if (remotePath === 'function') { + // TODO: Look into Next.js's bad bundling instead of doing this hack + data = data.replaceAll(/([\w\d]{1,3}).socket/g, '$1?.socket'); + fs.writeFile(filePath, data); + } + + if (dryRun) { + return { + id: 'dry-run', + name: 'dry-run', + slug: 'dry-run', + }; + } + + try { + return uploadFunction({ filePath, fileName: path.basename(filePath), name: functionName, fleekSdk }); + } catch (error) { + if (error instanceof Error) { + output.error(t('fleekFileUploadErrorIncludeError', { error: error.message })); + throw new FleekFileUploadError({ error }); + } + output.error(t('fleekFileUploadError')); + throw new FleekFileUploadError({}); + } +} + +export async function createFunction(props: { + projectName: string; + openNextPath: string; + origin: BaseFunction; + fleekSdk: FleekSdk; + key: string; + bundle?: boolean; + filename?: string; + dryRun?: boolean; +}): Promise { + const functionName = props.key === '' ? 'default' : props.key; + + output.spinner(`${t('creatingFunction', { name: functionName })}...`); + const filename = props.filename ?? 'index.mjs'; + + const filePath = path.join(props.openNextPath, props.origin.bundle, filename); + + const result = await uploadFunctionFile({ + functionName: `${props.projectName}-${props.key}`, + filePath, + remotePath: 'function', + fleekSdk: props.fleekSdk, + dryRun: props.dryRun, + }); + + const response: Origin = { + url: `https://${result.slug}.functions.stg.on-fleek-test.app`, + type: 'functions', + name: props.key, + }; + + if (!props.dryRun) { + output.success(t('functionCreated', { name: functionName, url: response.url })); + } else { + output.success(t('functionCreatedDryRun', { name: functionName })); + } + + return response; +} + +export async function createOrigins(props: { + projectName: string; + projectPath: string; + openNextOutput: OpenNextOutput; + fleekSdk: FleekSdk; + dryRun?: boolean; +}): Promise> { + const { imageOptimizer: imageOrigin, ...restOrigins } = props.openNextOutput.origins; + + const edgeFunctions = props.openNextOutput.edgeFunctions ?? {}; + + output.box(['Uploading assets']); + const s3 = await uploadAssets({ + openNextPath: props.projectPath, + openNextOutput: props.openNextOutput, + fleekSdk: props.fleekSdk, + dryRun: props.dryRun, + }); + + output.box(['Creating edge functions']); + const edgeFunctionOrigins = await Object.entries(edgeFunctions).reduce( + async (acc, [key, value]) => { + const acc2 = await acc; + acc2[key] = await createFunction({ + projectName: props.projectName, + openNextPath: props.projectPath, + origin: value, + fleekSdk: props.fleekSdk, + key, + bundle: false, + dryRun: props.dryRun, + }); + return acc2; + }, + {} as Promise>, + ); + + return { + s3, + imageOptimizer: await createFunction({ + projectName: props.projectName, + openNextPath: props.projectPath, + origin: imageOrigin, + fleekSdk: props.fleekSdk, + key: 'imageOptimizer', + dryRun: props.dryRun, + }), + ...(await Object.entries(restOrigins) + .filter(([key]) => key !== 'default') + .reduce( + async (acc, [key, value]) => { + const acc2 = await acc; + if (value.type === 'function') { + acc2[key] = await createFunction({ + projectName: props.projectName, + openNextPath: props.projectPath, + origin: value, + fleekSdk: props.fleekSdk, + key, + dryRun: props.dryRun, + }); + } + return acc2; + }, + {} as Promise>, + )), + ...edgeFunctionOrigins, + }; +} + +export async function uploadAssets(props: { + openNextPath: string; + openNextOutput: OpenNextOutput; + fleekSdk: FleekSdk; + dryRun?: boolean; +}): Promise { + output.spinner(`${t('creatingAssets')}...`); + const assetsDirPath = path.join(props.openNextPath, '.open-next', '_assets'); + + try { + await fs.mkdir(assetsDirPath, { recursive: true }); + + for (const copyOperation of props.openNextOutput.origins.s3.copy) { + const { from } = copyOperation; + const sourceDir = path.join(props.openNextPath, from); + const destinationDir = assetsDirPath; + + await fs.mkdir(destinationDir, { recursive: true }); + + await copyDir({ src: sourceDir, dest: destinationDir }); + } + + let response: Origin; + + if (!props.dryRun) { + const result = await uploadDirectory({ + path: assetsDirPath, + fleekSdk: props.fleekSdk, + }); + + response = { url: `https://${result}.ipfs.cf-ipfs.com/`, type: 'middleware', name: '/' }; + + output.success(t('assetsCreated', { url: response.url })); + } else { + output.success(t('assetsCreatedDryRun')); + response = { url: '(dry run)', type: 'middleware', name: '/' }; + } + + return response; + } catch (error) { + if (error instanceof Error) { + output.error(t('fleekDirUploadErrorIncludeError', { error: error.message })); + throw new FleekDirUploadError({ error }); + } + output.error(t('fleekDirUploadError')); + throw new FleekDirUploadError({}); + } +} + +export async function createProxyFunction(props: { + projectName: string; + projectPath: string; + middlewareManifest: MiddlewareManifest; + origins: Record; + fleekSdk: FleekSdk; + dryRun?: boolean; +}): Promise { + const { + projectPath, + middlewareManifest: { functions }, + origins, + fleekSdk, + } = props; + + output.spinner(t('creatingFunction', { name: 'routing' })); + + const imageOptimizerPath = '^_next/image$'; + let defaultRoute; + + const routes: Routes = Object.entries(origins) + .filter(([, value]) => value.type === 'functions') + .map(([key, value]) => { + let functionManifest: FunctionManifest; + if (key === '') { + functionManifest = functions['/page']; + defaultRoute = value.url; + } else if (key === 'imageOptimizer') { + return { [imageOptimizerPath]: value.url }; + } else { + const auxKey = `/${key}/page`; + functionManifest = functions[auxKey]; + } + + if (!functionManifest?.matchers?.length) { + return {}; + } + + const matcher = functionManifest.matchers[0]; + + return { [matcher.regexp]: `${value.url}${matcher.originalSource}` }; + }) + .reduce((acc, curr) => ({ ...acc, ...curr }), {} as Record); + + const assetRoute = readdirSync(path.join(projectPath, '.open-next', 'assets')) + .map((file) => { + return escapeRegex(file); + }) + .join('|'); + + routes[`^/(${assetRoute})`] = `${origins.s3.url}$1`; + + const functionCode = proxyFunctionTemplate({ + routes, + default: defaultRoute ?? '', + }); + + await build({ + outfile: path.join(projectPath, '.open-next', 'fleek', 'routing.js'), + bundle: true, + minify: true, + stdin: { + contents: functionCode, + loader: 'ts', + }, + treeShaking: true, + conditions: ['module'], + mainFields: ['module', 'main'], + target: 'es2022', + format: 'esm', + platform: 'neutral', + plugins: [ + alias({ + '@fleekxyz/proxy': require.resolve('@fleekxyz/proxy'), + }), + ], + }); + + const result = await uploadFunctionFile({ + functionName: `${props.projectName}-routing`, + filePath: path.join(projectPath, '.open-next', 'fleek', 'routing.js'), + remotePath: 'function', + fleekSdk, + }); + + const url = `https://${result.slug}.functions.stg.on-fleek-test.app`; + + if (!props.dryRun) { + output.success(t('functionCreated', { name: 'routing' })); + } else { + output.success(t('functionCreatedDryRun', { name: 'routing' })); + } + + return { name: 'routing', type: 'functions', url }; +} + +function escapeRegex(str: string) { + return str.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&'); +} diff --git a/src/fleek/types.ts b/src/commands/build/fleek/types.ts similarity index 100% rename from src/fleek/types.ts rename to src/commands/build/fleek/types.ts diff --git a/src/commands/build/index.ts b/src/commands/build/index.ts new file mode 100644 index 0000000..ddaba7f --- /dev/null +++ b/src/commands/build/index.ts @@ -0,0 +1,91 @@ +import { Command } from 'commander'; +import { output } from '../../cli.js'; + +import { readMiddlewareManifest, readOpenNextOutput } from '../../utils.js'; +import { getBuildCommand } from '../../utils/packageManager.js'; +import { t } from '../../utils/translation.js'; +import { createOrigins, createProxyFunction } from './fleek/index.js'; +import { buildApp as buildNextjsApp } from './next/index.js'; +import { buildOpenNextConfig, bundleApp as executeOpenNext } from './open-next/index.js'; +import { UnauthenticatedError } from '../../errors/UnauthenticatedError.js'; +import { getProjectPathOrPrompt } from './prompts/getProjectPathOrPrompt.js'; +import { getSdkClient } from '../../fleek/sdk.js'; +import { loadJSONFromPath } from '../../utils/json.js'; +import path from 'path'; + +type BuildArgs = { + projectPath?: string; + dryRun?: boolean; +}; + +const buildAction = async (opts: { args?: BuildArgs }) => { + const { args } = opts; + + const projectPath = await getProjectPathOrPrompt({ path: args?.projectPath }); + + output.spinner(t('fleekSdkAuth')); + const sdk = await getSdkClient(); + + if (!sdk) { + output.error(t('fleekSdkAuthError')); + throw new UnauthenticatedError(); + } + output.success(t('fleekSdkAuthSuccess')); + + output.box([t('buildingApp')]); + + const packageJson = loadJSONFromPath(path.join(projectPath, 'package.json')); + const buildCommand = getBuildCommand({ + projectPath, + }); + + output.log(t('projectPath', { projectPath })); + output.log(t('buildCommand', { buildCommand })); + + buildNextjsApp({ projectPath, buildCommand }); + + output.box([t('bundlingApp')]); + + output.log('Reading middleware manifest'); + const middlewareManifest = readMiddlewareManifest(projectPath); + output.log('Building open-next configuration file'); + const openNextConfigPath = buildOpenNextConfig({ projectPath, middlewareManifest, buildCommand }); + + executeOpenNext({ projectPath, openNextConfigPath }); + + const openNextOutput = readOpenNextOutput(projectPath); + + const origins = await createOrigins({ + projectName: packageJson.name, + projectPath, + openNextOutput, + fleekSdk: sdk, + dryRun: args?.dryRun ?? false, + }); + + const routingOrigin = await createProxyFunction({ + projectName: packageJson.name, + projectPath, + middlewareManifest, + origins, + fleekSdk: sdk, + dryRun: args?.dryRun ?? false, + }); + + origins['routing'] = routingOrigin; + + output.table(Object.values(origins)); + + output.success(t('appBuildSuccess')); +}; + +export default (program: Command) => { + const cmd = program.command('build').description(t('appBuildDescription')); + + cmd.action(buildAction); + + cmd + .command('help') + .description(t('printHelp')) + .action(() => program.help()); +}; diff --git a/src/commands/build/next/index.ts b/src/commands/build/next/index.ts new file mode 100644 index 0000000..ee41031 --- /dev/null +++ b/src/commands/build/next/index.ts @@ -0,0 +1,28 @@ +import { execSync } from 'child_process'; +import { getBuildEnvVars } from '../../../utils.js'; +import { output } from '../../../cli.js'; +import { t } from '../../../utils/translation.js'; +import { NextjsBuildError } from '../../../errors/NextjsBuildError.js'; + +export function buildApp(opts: { projectPath: string; buildCommand: string; environment?: Record }) { + const { projectPath, buildCommand } = opts; + + output.log(t('buildingNextjsApp')); + output.spinner(`${t('building')}`); + try { + // will throw if build fails - which is desired + execSync(buildCommand, { + cwd: projectPath, + env: getBuildEnvVars(opts), + }); + + output.success('Next.js app successfully built.'); + } catch (error) { + if (error instanceof Error) { + output.error(`Failed to build Next.js app: ${error.message}`); + throw new NextjsBuildError({ error }); + } + output.error(`Failed to build Next.js app`); + throw new NextjsBuildError({}); + } +} diff --git a/src/next/types.ts b/src/commands/build/next/types.ts similarity index 100% rename from src/next/types.ts rename to src/commands/build/next/types.ts diff --git a/src/commands/build/open-next/index.ts b/src/commands/build/open-next/index.ts new file mode 100644 index 0000000..c3f184a --- /dev/null +++ b/src/commands/build/open-next/index.ts @@ -0,0 +1,72 @@ +import * as path from 'path'; +import { execSync } from 'child_process'; +import { writeFileSync } from 'fs'; +import { getBuildEnvVars } from '../../../utils.js'; +import { MiddlewareManifest } from '../next/types.js'; +import { templateOpenNextConfig } from './open-next.config.js'; +import { NextjsBundleError } from '../../../errors/NextjsBundleError.js'; +import { output } from '../../../cli.js'; +import { t } from '../../../utils/translation.js'; + +export function bundleApp(opts: { + projectPath: string; + openNextConfigPath: string; + environment?: Record; +}) { + const { projectPath, openNextConfigPath } = opts; + output.log(`${t('bundlingNextjsApp')}`); + output.spinner(`${t('bundling')}`); + + // TODO: update the open-next fork to export the build function + const localBinPath = path.join(projectPath, 'node_modules', '@fleekxyz', 'next', 'node_modules', '.bin'); + const bin = path.join(localBinPath, 'open-next'); + + try { + execSync(`${bin} build --config-path=${openNextConfigPath} --skip-build=true --standalone-mode=false`, { + cwd: projectPath, + env: getBuildEnvVars(opts), + }); + output.success(t('bundlingSuccess')); + } catch (error) { + if (error instanceof Error) { + output.error(t('bundlingErrorIncludeError', { error: error.message })); + throw new NextjsBundleError({ error }); + } + output.error(t('bundlingError')); + throw new NextjsBundleError({}); + } +} + +export function buildOpenNextConfig(opts: { + projectPath: string; + middlewareManifest: MiddlewareManifest; + buildCommand: string; +}) { + const { projectPath, middlewareManifest, buildCommand } = opts; + + const functionConfigs = Object.entries(middlewareManifest.functions) + .map(([name, fn]) => { + const key = name === '/page' ? '' : name.replace(/\/([\w\d-@/[\]]*)\/page/, '$1'); + return ` + "${key}": { + runtime: 'edge', + routes: ["${fn.name}"], + patterns: ["${fn.page}"], + minify: true, + placement: 'global', + override: { + converter: async () => converter, + wrapper: async () => wrapper, + }, + }, + `; + }) + .join(''); + + const openNextConfig = templateOpenNextConfig({ functionConfigs, buildCommand }); + const openNextConfigPath = path.join(projectPath, 'open-next.config.ts'); + + writeFileSync(path.join(projectPath, 'open-next.config.ts'), openNextConfig); + + return openNextConfigPath; +} diff --git a/src/open-next/open-next.config.ts b/src/commands/build/open-next/open-next.config.ts similarity index 98% rename from src/open-next/open-next.config.ts rename to src/commands/build/open-next/open-next.config.ts index 148d1c4..ad479ed 100644 --- a/src/open-next/open-next.config.ts +++ b/src/commands/build/open-next/open-next.config.ts @@ -1,5 +1,6 @@ export const templateOpenNextConfig = (opts: { functionConfigs: string; + buildCommand: string; }) => `export type HttpMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'CONNECT' | 'OPTIONS' | 'TRACE' | 'PATCH'; export type FleekRequest = { @@ -158,7 +159,6 @@ export const converter = { const config = { default: { runtime: "edge", - experimentalBundledNextServer: true, minify: true, placement: "global", override: { @@ -178,7 +178,7 @@ const config = { wrapper: async () => wrapper, }, }, - buildCommand: "npm run build", + buildCommand: "${opts.buildCommand}", dangerous: { disableIncrementalCache: true, disableTagCache: true, diff --git a/src/open-next/types.ts b/src/commands/build/open-next/types.ts similarity index 100% rename from src/open-next/types.ts rename to src/commands/build/open-next/types.ts diff --git a/src/commands/build/prompts/getPersonalAccessTokenOrPrompt.ts b/src/commands/build/prompts/getPersonalAccessTokenOrPrompt.ts new file mode 100644 index 0000000..a07af14 --- /dev/null +++ b/src/commands/build/prompts/getPersonalAccessTokenOrPrompt.ts @@ -0,0 +1,15 @@ +import { textPrompt } from '../../../prompts/textPrompt.js'; +import { t } from '../../../utils/translation.js'; +import { secrets } from '../../../secrets.js'; + +export const getPersonalAccessTokenOrPrompt = async () => { + let personalAccessToken = secrets.FLEEK_TOKEN; + + if (!personalAccessToken) { + personalAccessToken = await textPrompt({ + message: `${t('enterPersonalAccessToken')}:`, + }); + } + + return personalAccessToken; +}; diff --git a/src/commands/build/prompts/getProjectIdOrPrompt.ts b/src/commands/build/prompts/getProjectIdOrPrompt.ts new file mode 100644 index 0000000..5102d8c --- /dev/null +++ b/src/commands/build/prompts/getProjectIdOrPrompt.ts @@ -0,0 +1,15 @@ +import { textPrompt } from '../../../prompts/textPrompt.js'; +import { t } from '../../../utils/translation.js'; +import { secrets } from '../../../secrets.js'; + +export const getProjectIdOrPrompt = async () => { + let projectId = secrets.FLEEK_PROJECT_ID; + + if (!projectId) { + projectId = await textPrompt({ + message: `${t('enterProjectId')}:`, + }); + } + + return projectId; +}; diff --git a/src/commands/build/prompts/getProjectPathOrPrompt.ts b/src/commands/build/prompts/getProjectPathOrPrompt.ts new file mode 100644 index 0000000..819e2c4 --- /dev/null +++ b/src/commands/build/prompts/getProjectPathOrPrompt.ts @@ -0,0 +1,24 @@ +import { textPrompt } from '../../../prompts/textPrompt.js'; +import { t } from '../../../utils/translation.js'; +import { validateProjectPath } from '../../../validation/isProjectPathValid.js'; + +type GetProjectPathOrPromptArgs = { + path?: string; +}; + +export const getProjectPathOrPrompt = async ({ path }: GetProjectPathOrPromptArgs) => { + let projectPath; + if (!path) { + projectPath = process.cwd(); + try { + await validateProjectPath({ projectPath }); + } catch (err) { + projectPath = await textPrompt({ message: `${t('enterProjectPath')}:` }); + } + } else { + projectPath = path; + await validateProjectPath({ projectPath }); + } + + return projectPath; +}; diff --git a/src/proxy/index.ts b/src/commands/build/proxy/index.ts similarity index 100% rename from src/proxy/index.ts rename to src/commands/build/proxy/index.ts diff --git a/src/errors/FleekDirUploadError.ts b/src/errors/FleekDirUploadError.ts new file mode 100644 index 0000000..695bd39 --- /dev/null +++ b/src/errors/FleekDirUploadError.ts @@ -0,0 +1,14 @@ +import { FleekError } from './FleekError.js'; + +type FleekDirUploadErrorOptions = { + error?: Error; +}; + +export class FleekDirUploadError extends FleekError { + public name = 'FleekDirUploadError'; + + toString = () => + this.data.error + ? `Failed to upload directory to Fleek: ${this.data.error}` + : `Failed to upload directory to Fleek.`; +} diff --git a/src/errors/FleekError.ts b/src/errors/FleekError.ts new file mode 100644 index 0000000..339d701 --- /dev/null +++ b/src/errors/FleekError.ts @@ -0,0 +1,16 @@ +export abstract class FleekError extends Error { + public abstract name: string; + public data: T; + + constructor(data: T) { + super(); + + this.data = data; + } + + get message() { + return this.toString(); + } + + abstract toString(): string; +} diff --git a/src/errors/FleekFileUploadError.ts b/src/errors/FleekFileUploadError.ts new file mode 100644 index 0000000..3e7da35 --- /dev/null +++ b/src/errors/FleekFileUploadError.ts @@ -0,0 +1,12 @@ +import { FleekError } from './FleekError.js'; + +type FleekFileUploadErrorOptions = { + error?: Error; +}; + +export class FleekFileUploadError extends FleekError { + public name = 'FleekFileUploadError'; + + toString = () => + this.data.error ? `Failed to upload file to Fleek: ${this.data.error}` : `Failed to upload file to Fleek.`; +} diff --git a/src/errors/MissingExpectedDataError.ts b/src/errors/MissingExpectedDataError.ts new file mode 100644 index 0000000..37700fe --- /dev/null +++ b/src/errors/MissingExpectedDataError.ts @@ -0,0 +1,8 @@ +import { FleekError } from './FleekError.js'; + +export class MissingExpectedDataError extends FleekError { + public name = 'MissingExpectedDataError'; + + toString = () => + 'Oops! This is embarassing but the app is missing important data. Please report this issue to the team.'; +} diff --git a/src/errors/MissingPersonalAccessTokenError.ts b/src/errors/MissingPersonalAccessTokenError.ts new file mode 100644 index 0000000..bac0c63 --- /dev/null +++ b/src/errors/MissingPersonalAccessTokenError.ts @@ -0,0 +1,13 @@ +import { FleekError } from './FleekError.js'; + +type MissingPersonalAccessTokenErrorOptions = Record; + +export class MissingPersonalAccessTokenError extends FleekError { + public name = 'MissingPersonalAccessTokenError'; + + constructor() { + super({}); + } + + toString = () => `Your Fleek personal access token is missing.`; +} diff --git a/src/errors/MissingProjectIdError.ts b/src/errors/MissingProjectIdError.ts new file mode 100644 index 0000000..d617b89 --- /dev/null +++ b/src/errors/MissingProjectIdError.ts @@ -0,0 +1,13 @@ +import { FleekError } from './FleekError.js'; + +type MissingProjectIdErrorOptions = Record; + +export class MissingProjectIdError extends FleekError { + public name = 'MissingProjectIdError'; + + constructor() { + super({}); + } + + toString = () => `Your Fleek project ID is missing.`; +} diff --git a/src/errors/NextjsBuildError.ts b/src/errors/NextjsBuildError.ts new file mode 100644 index 0000000..6bd57cb --- /dev/null +++ b/src/errors/NextjsBuildError.ts @@ -0,0 +1,14 @@ +import { FleekError } from './FleekError.js'; + +type NextjsBuildErrorOptions = { + error?: Error; +}; + +export class NextjsBuildError extends FleekError { + public name = 'NextjsBuildError'; + + toString = () => + this.data.error + ? `Failed to build Next.js application: ${this.data.error}` + : 'Failed to build Next.js application.'; +} diff --git a/src/errors/NextjsBundleError.ts b/src/errors/NextjsBundleError.ts new file mode 100644 index 0000000..582c5b6 --- /dev/null +++ b/src/errors/NextjsBundleError.ts @@ -0,0 +1,14 @@ +import { FleekError } from './FleekError.js'; + +type NextjsBundleErrorOptions = { + error?: Error; +}; + +export class NextjsBundleError extends FleekError { + public name = 'NextjsBundleError'; + + toString = () => + this.data.error + ? `Failed to bundle Next.js application: ${this.data.error}` + : `Failed to bundle Next.js application.`; +} diff --git a/src/errors/PackageJsonInvalidFileError.ts b/src/errors/PackageJsonInvalidFileError.ts new file mode 100644 index 0000000..0ad0331 --- /dev/null +++ b/src/errors/PackageJsonInvalidFileError.ts @@ -0,0 +1,11 @@ +import { FleekError } from './FleekError.js'; + +type PackageJsonInvalidFileErrorOptions = { + name: string; +}; + +export class PackageJsonInvalidFileError extends FleekError { + public name = 'PackageJsonInvalidDirError'; + + toString = () => `The package.json at ${this.data.name} is not a file.`; +} diff --git a/src/errors/PackageJsonInvalidPathError.ts b/src/errors/PackageJsonInvalidPathError.ts new file mode 100644 index 0000000..681c585 --- /dev/null +++ b/src/errors/PackageJsonInvalidPathError.ts @@ -0,0 +1,11 @@ +import { FleekError } from './FleekError.js'; + +type PackageJsonInvalidPathErrorOptions = { + name: string; +}; + +export class PackageJsonInvalidPathError extends FleekError { + public name = 'PackageJsonInvalidPathError'; + + toString = () => `package.json not found at ${this.data.name}.`; +} diff --git a/src/errors/PackageJsonMissingAccessError.ts b/src/errors/PackageJsonMissingAccessError.ts new file mode 100644 index 0000000..f4f7f95 --- /dev/null +++ b/src/errors/PackageJsonMissingAccessError.ts @@ -0,0 +1,11 @@ +import { FleekError } from './FleekError.js'; + +type PackageJsonMissingAccessErrorOptions = { + name: string; +}; + +export class PackageJsonMissingAccessError extends FleekError { + public name = 'PackageJsonMissingAccessError'; + + toString = () => `Missing permisisons to access the package.json at ${this.data.name}.`; +} diff --git a/src/errors/PackageJsonUnknownError.ts b/src/errors/PackageJsonUnknownError.ts new file mode 100644 index 0000000..0fe6b6e --- /dev/null +++ b/src/errors/PackageJsonUnknownError.ts @@ -0,0 +1,12 @@ +import { FleekError } from './FleekError.js'; + +type PackageJsonUnknownErrorOptions = { + name: string; + code?: string; +}; + +export class PackageJsonUnknownError extends FleekError { + public name = 'PackageJsonUnknownError'; + + toString = () => `Failed to access package.json at ${this.data.name} with error code ${this.data.code}.`; +} diff --git a/src/errors/ProjectInvalidDirError.ts b/src/errors/ProjectInvalidDirError.ts new file mode 100644 index 0000000..68538f6 --- /dev/null +++ b/src/errors/ProjectInvalidDirError.ts @@ -0,0 +1,11 @@ +import { FleekError } from './FleekError.js'; + +type ProjectInvalidDirErrorOptions = { + name: string; +}; + +export class ProjectInvalidDirError extends FleekError { + public name = 'ProjectInvalidPathError'; + + toString = () => `The project path ${this.data.name} is not a directory.`; +} diff --git a/src/errors/ProjectInvalidPathError.ts b/src/errors/ProjectInvalidPathError.ts new file mode 100644 index 0000000..1215062 --- /dev/null +++ b/src/errors/ProjectInvalidPathError.ts @@ -0,0 +1,11 @@ +import { FleekError } from './FleekError.js'; + +type ProjectInvalidPathErrorOptions = { + name: string; +}; + +export class ProjectInvalidPathError extends FleekError { + public name = 'ProjectInvalidPathError'; + + toString = () => `The project path ${this.data.name} is invalid.`; +} diff --git a/src/errors/ProjectPathMissingAccessError.ts b/src/errors/ProjectPathMissingAccessError.ts new file mode 100644 index 0000000..f22ec3d --- /dev/null +++ b/src/errors/ProjectPathMissingAccessError.ts @@ -0,0 +1,11 @@ +import { FleekError } from './FleekError.js'; + +type ProjectPathMissingAccessErrorOptions = { + name: string; +}; + +export class ProjectPathMissingAccessError extends FleekError { + public name = 'ProjectPathMissingAccessError'; + + toString = () => `Missing permisisons to access the project path ${this.data.name}.`; +} diff --git a/src/errors/ProjectPathUnknownError.ts b/src/errors/ProjectPathUnknownError.ts new file mode 100644 index 0000000..b42476c --- /dev/null +++ b/src/errors/ProjectPathUnknownError.ts @@ -0,0 +1,12 @@ +import { FleekError } from './FleekError.js'; + +type ProjectPathUnknownErrorOptions = { + name: string; + code?: string; +}; + +export class ProjectPathUnknownError extends FleekError { + public name = 'ProjectPathUnknownError'; + + toString = () => `Failed to access project at path ${this.data.name} with error code ${this.data.code}.`; +} diff --git a/src/errors/UnauthenticatedError.ts b/src/errors/UnauthenticatedError.ts new file mode 100644 index 0000000..d848e5c --- /dev/null +++ b/src/errors/UnauthenticatedError.ts @@ -0,0 +1,13 @@ +import { FleekError } from './FleekError.js'; + +type UnauthenticatedErrorOptions = Record; + +export class UnauthenticatedError extends FleekError { + public name = 'UnauthenticatedError'; + + constructor() { + super({}); + } + + toString = () => `The request is not authenticated.`; +} diff --git a/src/fleek/index.ts b/src/fleek/index.ts index 974f909..b565eac 100644 --- a/src/fleek/index.ts +++ b/src/fleek/index.ts @@ -1,271 +1,37 @@ -import { FleekSdk } from '@fleekxyz/sdk'; -import { BaseFunction, OpenNextOutput } from '../open-next/types'; -import path from 'node:path'; -import { copyDir, printHeader } from '../utils'; +import { FleekFunction, FleekSdk } from '@fleekxyz/sdk'; +import { filesFromPaths } from 'files-from-path'; -import * as fs from 'node:fs/promises'; -import { FunctionManifest, MiddlewareManifest } from '../next/types'; -import { Origin } from './types'; -import { proxyFunctionTemplate } from '../proxy'; -import { Routes } from '@fleekxyz/proxy'; -import { build } from 'esbuild'; -import alias from 'esbuild-plugin-alias'; -import { readdirSync } from 'node:fs'; +export async function uploadDirectory(props: { path: string; fleekSdk: FleekSdk }): Promise { + const result = await props.fleekSdk.storage().uploadDirectory({ path: props.path }); -export async function uploadFile(props: { filePath: string; remotePath: string; fleekSdk: FleekSdk }): Promise { - const { filePath, remotePath, fleekSdk } = props; - - const data = await fs.readFile(filePath, 'utf8'); - - if (remotePath === 'function') { - // TODO: Fix Next.js's bad bundling instead of doing this hack - const modifiedData = data.replaceAll(/([\w\d]{1,3}).socket/g, '$1?.socket'); - return uploadData({ data: modifiedData, fileName: path.basename(filePath), fleekSdk }); - } else { - return uploadData({ data, fileName: path.basename(filePath), fleekSdk }); - } -} - -async function uploadData(props: { data: string; fileName: string; fleekSdk: FleekSdk }): Promise { - try { - const result = await props.fleekSdk.ipfs().add({ - path: props.fileName, - content: new TextEncoder().encode(props.data), - }); - - return result.cid.toV1().toString(); - } catch (error: unknown) { - console.error(`Failed to upload file at ${props.fileName}`, process.env.DEBUG ? error : ''); - throw new Error(`Failed to upload file at ${props.fileName}`); - } -} - -export async function uploadDir(props: { path: string; fleekSdk: FleekSdk }): Promise { - try { - const result = await props.fleekSdk.ipfs().addFromPath(props.path, { wrapWithDirectory: true }); - - const root = result.find((r) => r.path === ''); - - if (!root) { - throw new Error('Failed to find root dir CID'); - } - - return root.cid.toV1().toString(); - } catch (error: unknown) { - console.error(`Failed to upload dir at ${path}`, process.env.DEBUG ? error : ''); - throw new Error(`Failed to upload dir at ${path}`); - } -} - -export async function createFunction(props: { - openNextPath: string; - origin: BaseFunction; - fleekSdk: FleekSdk; - key: string; - bundle?: boolean; - filename?: string; -}): Promise { - const filename = props.filename ?? 'index.mjs'; - - const filePath = path.join(props.openNextPath, props.origin.bundle, filename); - - const result = await uploadFile({ - filePath, - remotePath: 'function', - fleekSdk: props.fleekSdk, - }); - - const response: Origin = { - url: `http://fleek-test.network/services/1/ipfs/${result}`, - type: 'functions', - name: props.key, - }; - - console.log(`Function ${props.key === '' ? 'default' : props.key}`, response.url); - - return response; + return result.pin.cid; } -export async function createOrigins(props: { - openNextPath: string; - openNextOutput: OpenNextOutput; +export async function uploadFunction(props: { + filePath: string; + fileName: string; + name: string; fleekSdk: FleekSdk; -}): Promise> { - const { imageOptimizer: imageOrigin, ...restOrigins } = props.openNextOutput.origins; +}): Promise { + const files = await filesFromPaths([props.filePath]); - const edgeFunctions = props.openNextOutput.edgeFunctions ?? {}; - - printHeader('Uploading assets'); - const s3 = await uploadAssets({ - openNextPath: props.openNextPath, - openNextOutput: props.openNextOutput, - fleekSdk: props.fleekSdk, + const uploadFileResult = await props.fleekSdk.storage().uploadFile({ + file: files[0], }); - printHeader('Creating edge functions'); - const edgeFunctionOrigins = await Object.entries(edgeFunctions).reduce( - async (acc, [key, value]) => { - const acc2 = await acc; - acc2[key] = await createFunction({ - openNextPath: props.openNextPath, - origin: value, - fleekSdk: props.fleekSdk, - key, - bundle: false, - }); - return acc2; - }, - {} as Promise>, - ); - - return { - s3, - imageOptimizer: await createFunction({ - openNextPath: props.openNextPath, - origin: imageOrigin, - fleekSdk: props.fleekSdk, - key: 'imageOptimizer', - }), - ...(await Object.entries(restOrigins) - .filter(([key]) => key !== 'default') - .reduce( - async (acc, [key, value]) => { - const acc2 = await acc; - if (value.type === 'function') { - acc2[key] = await createFunction({ - openNextPath: props.openNextPath, - origin: value, - fleekSdk: props.fleekSdk, - key, - }); - } - return acc2; - }, - {} as Promise>, - )), - ...edgeFunctionOrigins, - }; -} - -export async function uploadAssets(props: { - openNextPath: string; - openNextOutput: OpenNextOutput; - fleekSdk: FleekSdk; -}): Promise { - const assetsDirPath = path.join(props.openNextPath, '.open-next', '_assets'); - + let fleekFunction: FleekFunction; try { - await fs.mkdir(assetsDirPath, { recursive: true }); - - for (const copyOperation of props.openNextOutput.origins.s3.copy) { - const { from } = copyOperation; - const sourceDir = path.join(props.openNextPath, from); - const destinationDir = assetsDirPath; - - await fs.mkdir(destinationDir, { recursive: true }); - - await copyDir({ src: sourceDir, dest: destinationDir }); - } - - const result = await uploadDir({ - path: assetsDirPath, - fleekSdk: props.fleekSdk, - }); - - const response: Origin = { url: `https://${result}.ipfs.cf-ipfs.com/`, type: 'middleware', name: '/' }; - console.log('Assets', response.url); - return response; + fleekFunction = await props.fleekSdk.functions().get({ name: props.name }); } catch (error) { - console.error('Failed to upload assets:', error); - throw error; + fleekFunction = await props.fleekSdk.functions().create({ + name: props.name, + }); } -} - -export async function createProxyFunction(props: { - openNextPath: string; - middlewareManifest: MiddlewareManifest; - origins: Record; - fleekSdk: FleekSdk; -}) { - const { - openNextPath, - middlewareManifest: { functions }, - origins, - fleekSdk, - } = props; - - printHeader('Creating proxy function'); - const imageOptimizerPath = '^_next/image$'; - let defaultRoute; - - const routes: Routes = Object.entries(origins) - .filter(([, value]) => value.type === 'functions') - .map(([key, value]) => { - let functionManifest: FunctionManifest; - if (key === '') { - functionManifest = functions['/page']; - defaultRoute = value.url; - } else if (key === 'imageOptimizer') { - return { [imageOptimizerPath]: value.url }; - } else { - const auxKey = `/${key}/page`; - functionManifest = functions[auxKey]; - } - - if (!functionManifest?.matchers?.length) { - return {}; - } - - const matcher = functionManifest.matchers[0]; - - return { [matcher.regexp]: `${value.url}${matcher.originalSource}` }; - }) - .reduce((acc, curr) => ({ ...acc, ...curr }), {} as Record); - - const assetRoute = readdirSync(path.join(openNextPath, '.open-next', 'assets')) - .map((file) => { - return escapeRegex(file); - }) - .join('|'); - - routes[`^/(${assetRoute})`] = `${origins.s3.url}$1`; - - const functionCode = proxyFunctionTemplate({ - routes, - default: defaultRoute ?? '', + await props.fleekSdk.functions().deploy({ + functionId: fleekFunction.id, + cid: uploadFileResult.pin.cid, }); - await build({ - outfile: path.join(openNextPath, '.open-next', 'fleek', 'routing.js'), - bundle: true, - minify: true, - stdin: { - contents: functionCode, - loader: 'ts', - }, - treeShaking: true, - conditions: ['module'], - mainFields: ['module', 'main'], - target: 'es2022', - format: 'esm', - platform: 'neutral', - plugins: [ - alias({ - '@fleekxyz/proxy': require.resolve('@fleekxyz/proxy'), - }), - ], - }); - - const result = await uploadFile({ - filePath: path.join(openNextPath, '.open-next', 'fleek', 'routing.js'), - remotePath: 'function', - fleekSdk, - }); - - console.log(`Function routing http://fleek-test.network/services/1/ipfs/${result}`); -} - -function escapeRegex(str: string) { - return str.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&'); + return fleekFunction; } diff --git a/src/fleek/sdk.ts b/src/fleek/sdk.ts new file mode 100644 index 0000000..8c3410e --- /dev/null +++ b/src/fleek/sdk.ts @@ -0,0 +1,24 @@ +import { FleekSdk, PersonalAccessTokenService } from '@fleekxyz/sdk'; +import { getPersonalAccessTokenOrPrompt } from '../commands/build/prompts/getPersonalAccessTokenOrPrompt.js'; +import { getProjectIdOrPrompt } from '../commands/build/prompts/getProjectIdOrPrompt.js'; +import { MissingPersonalAccessTokenError } from '../errors/MissingPersonalAccessTokenError.js'; +import { MissingProjectIdError } from '../errors/MissingProjectIdError.js'; + +export async function getSdkClient(): Promise { + const personalAccessToken = await getPersonalAccessTokenOrPrompt(); + + if (!personalAccessToken) { + throw new MissingPersonalAccessTokenError(); + } + + const projectId = await getProjectIdOrPrompt(); + + if (!projectId) { + throw new MissingProjectIdError(); + } + + const accessTokenService = new PersonalAccessTokenService({ projectId, personalAccessToken }); + const sdk = new FleekSdk({ accessTokenService }); + + return sdk; +} diff --git a/src/index.ts b/src/index.ts index 9a8de23..5f28bb7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,57 +1,29 @@ #!/usr/bin/env node -import { readMiddlewareManifest, readOpenNextOutput } from './utils'; +'use strict'; -import { FleekSdk, PersonalAccessTokenService } from '@fleekxyz/sdk'; -import { findPackageManager } from './utils/packageManager'; -import { buildApp } from './next'; -import { buildOpenNextConfig, bundleApp } from './open-next'; -import { createOrigins, createProxyFunction } from './fleek'; +import * as semver from 'semver'; -(async () => { - const fleekSdk = new FleekSdk({ - projectId: process.env.FLEEK_PROJECT_ID!, - accessTokenService: new PersonalAccessTokenService({ - personalAccessToken: process.env.FLEEK_PAT!, - projectId: process.env.FLEEK_PROJECT_ID!, - }), - }); +import { asyncParser, init } from './cli.js'; +import { loadJSONFromPackageRoot } from './utils/json.js'; +import { checkForPackageUpdates } from './utils/update-notifier.js'; - const openNextPath = process.cwd(); - const packageManager = findPackageManager(openNextPath); +const pkgJson = loadJSONFromPackageRoot('package.json'); +const pkgVersion = pkgJson.version; +const requiredVersion = pkgJson.engines.node; - buildApp({ - openNextPath, - environment: {}, - packageManager, - }); +if (!semver.satisfies(process.version, requiredVersion)) { + // TODO: Pick text from locales + console.error(`⚠️ You are using Node ${process.version}. We require Node ${requiredVersion} or higher!`); + process.exit(1); +} - const middlewareManifest = readMiddlewareManifest(openNextPath); +// Inform users of updates in a non-blocking manner +checkForPackageUpdates(pkgJson); - buildOpenNextConfig({ - openNextPath, - middlewareManifest, - }); +// Catch uncaught exception(s) +process.once('uncaughtException', (error) => { + throw error; +}); - bundleApp({ - openNextPath, - }); - - const openNextOutput = readOpenNextOutput(openNextPath); - - const props = { - openNextPath, - openNextOutput, - fleekSdk, - }; - - const origins = await createOrigins(props); - - await createProxyFunction({ - openNextPath, - middlewareManifest, - origins, - fleekSdk, - }); - - process.exit(0); -})(); +// Initialise +init({ version: pkgVersion, parser: asyncParser }); diff --git a/src/next/index.ts b/src/next/index.ts deleted file mode 100644 index 8a4a561..0000000 --- a/src/next/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { execSync } from 'child_process'; -import { getBuildEnvVars } from '../utils'; -import { PackageManager } from '../utils/packageManager'; - -export function buildApp(opts: { - openNextPath: string; - environment?: Record; - packageManager: PackageManager; -}) { - const buildPath = opts.openNextPath; - const buildCommand = `${opts.packageManager} build`; - - // will throw if build fails - which is desired - execSync(buildCommand, { - cwd: buildPath, - env: getBuildEnvVars(opts), - stdio: 'inherit', - }); -} diff --git a/src/open-next/index.ts b/src/open-next/index.ts deleted file mode 100644 index 8205c88..0000000 --- a/src/open-next/index.ts +++ /dev/null @@ -1,51 +0,0 @@ -import * as path from 'path'; -import { execSync } from 'child_process'; -import { getBuildEnvVars } from '../utils'; -import { MiddlewareManifest } from '../next/types'; -import { templateOpenNextConfig } from './open-next.config'; -import { writeFileSync } from 'fs'; - -export function bundleApp(opts: { openNextPath: string; environment?: Record }) { - const buildPath = opts.openNextPath; - const openNextConfigPath = path.join(opts.openNextPath, 'open-next.config.ts'); - const localBinPath = path.join(__dirname, '..', '..', 'node_modules', '.bin'); - const bin = path.join(localBinPath, 'open-next'); - - try { - execSync(`${bin} build --config-path=${openNextConfigPath} --skip-build=true --standalone-mode=false`, { - cwd: buildPath, - env: getBuildEnvVars(opts), - stdio: 'inherit', - }); - } catch (error) { - console.error('Failed to run open-next build:', error); - throw error; - } -} - -export function buildOpenNextConfig(opts: { openNextPath: string; middlewareManifest: MiddlewareManifest }) { - const { openNextPath, middlewareManifest } = opts; - - const functionConfigs = Object.entries(middlewareManifest.functions) - .map(([name, fn]) => { - const key = name === '/page' ? '' : name.replace(/\/([\w\d-@/[\]]*)\/page/, '$1'); - return ` - "${key}": { - runtime: 'edge', - routes: ["${fn.name}"], - patterns: ["${fn.page}"], - minify: true, - placement: 'global', - override: { - converter: async () => converter, - wrapper: async () => wrapper, - }, - }, - `; - }) - .join(''); - - const openNextConfig = templateOpenNextConfig({ functionConfigs }); - - writeFileSync(path.join(openNextPath, 'open-next.config.ts'), openNextConfig); -} diff --git a/src/output/Output.ts b/src/output/Output.ts new file mode 100644 index 0000000..d6a12f0 --- /dev/null +++ b/src/output/Output.ts @@ -0,0 +1,196 @@ +import type { Writable } from 'node:stream'; + +import asTable from 'as-table'; +import boxen, { Options } from 'boxen'; +import type { ForegroundColor } from 'chalk'; +import chalk from 'chalk'; + +import { t } from '../utils/translation.js'; +import { Waiter } from './utils/wait.js'; + +export type OutputOptions = { + debug?: boolean; + stream: Writable; +}; + +export const enum Icons { + Checkmark = '✅', + ChequeredFlag = '🏁', + Cross = '❌', + Chain = '🔗', + Devil = '👹', + Lamp = '💡', + Robot = '🤖', + Warning = '⚠️', +} + +export class Output { + private stream: Writable; + public debugEnabled: boolean; + private spinnerMessage = ''; + private _spinner: Waiter | null = null; + + constructor( + { stream, debug: debugEnabled = false }: OutputOptions = { + stream: process.stdout, + }, + ) { + this.stream = stream; + this.debugEnabled = debugEnabled; + } + + public print = ( + message: string, + options: { + prefix?: { + message: string; + color?: ForegroundColor; + bold?: boolean; + }; + } = {}, + ) => { + this.stopSpinner(); + + // Disable colors for tests + if (this.debugEnabled) { + chalk.level = 0; + } + + const preparedMessage = options.prefix + ? `${options.prefix.color ? chalk[options.prefix.color](options.prefix.message) : options.prefix.message} ${ + options.prefix.bold ? chalk.bold(message) : message + }` + : message; + + return this.stream.write(preparedMessage); + }; + + public printNewLine = (count = 1) => { + this.print(`\n`.repeat(count)); + }; + + public log = (message: string) => { + // TODO: Given that console backgrounds can be of any colour + // certain colours such as grey might not be the most accessible e.g. white background + // should be verified against brighter consoles and possibly disable colour + this.print(message, { prefix: { color: 'gray', message: '>' } }); + this.printNewLine(); + }; + + public chore = (message: string) => { + this.print(message, { prefix: { message: Icons.Robot, bold: false } }); + this.printNewLine(); + }; + + public hint = (message: string) => { + this.print(message, { prefix: { message: Icons.Lamp, bold: true } }); + this.printNewLine(); + }; + + public warn = (message: string) => { + this.print(message, { prefix: { message: `${Icons.Warning} ${t('warning')}!` } }); + this.printNewLine(); + }; + + public mistake = (message: string) => { + this.print(message, { + prefix: { message: `${Icons.Devil} ${t('mistake')}!`, bold: false }, + }); + this.printNewLine(); + }; + + public error = (message: string) => { + this.print(message, { prefix: { message: `${Icons.Cross} ${t('error')}:` } }); + this.printNewLine(); + }; + + public ready = (message: string) => { + this.print(message, { + prefix: { message: `${Icons.ChequeredFlag} ${t('ready')}!` }, + }); + this.printNewLine(); + }; + + public success = (message: string) => { + this.print(message, { prefix: { message: `${Icons.Checkmark} ${t('success')}!` } }); + this.printNewLine(); + }; + + public link = (url: string) => { + this.print(`${Icons.Chain} ${chalk.cyan.underline(url)}`); + this.printNewLine(); + }; + + public debug = (message: string) => { + if (this.debugEnabled) { + // TODO: Given that console backgrounds can be of any colour + // certain colours such as grey might not be the most accessible e.g. white background + // should be verified against brighter consoles and possibly disable colour + this.print(message, { prefix: { color: 'gray', message: `${t('debug')}:` } }); + this.printNewLine(); + } + }; + + public table = (data: { [key: string]: string | number | undefined | null | Date }[]) => { + this.printNewLine(); + this.print(asTable(data)); + this.printNewLine(2); + }; + + public box = (lines: string[], options: Options = {}) => { + const defaultOptions: Options = { + textAlignment: 'center', + margin: 1, + padding: 1, + float: 'left', + borderColor: 'yellow', + }; + + this.printNewLine(); + this.print(boxen(lines.join('\n'), { ...defaultOptions, ...options })); + this.printNewLine(); + }; + + public textColor = (message: string, color: ForegroundColor) => chalk[color](message); + + public quoted = (message: string) => `"${message}"`; + + public spinner = (message: string, delay: number = 300): void => { + if (this.debugEnabled) { + this.debug(t('spinnerInvokedDelay', { message, delay: delay.toString() })); + + return; + } + + this.spinnerMessage = message; + + if (this._spinner) { + this._spinner.setText(message); + } else { + this._spinner = new Waiter({ + opts: { + text: message, + stream: this.stream, + }, + delay, + }); + } + }; + + public stopSpinner = () => { + if (this.debugEnabled && this.spinnerMessage) { + this.debug(t('spinnerStopped', { spinnerMessage: this.spinnerMessage })); + this.spinnerMessage = ''; + } + + if (this._spinner) { + this._spinner.stop(); + this._spinner = null; + this.spinnerMessage = ''; + } + }; + + public raw = (msg: string) => { + this.stream.write(msg); + }; +} diff --git a/src/output/utils/eraseLines.ts b/src/output/utils/eraseLines.ts new file mode 100644 index 0000000..fd90305 --- /dev/null +++ b/src/output/utils/eraseLines.ts @@ -0,0 +1,5 @@ +import ansiEscapes from 'ansi-escapes'; + +export const eraseLines = (numberOfLines: number) => { + return ansiEscapes.eraseLines(numberOfLines); +}; diff --git a/src/output/utils/wait.ts b/src/output/utils/wait.ts new file mode 100644 index 0000000..65053d2 --- /dev/null +++ b/src/output/utils/wait.ts @@ -0,0 +1,43 @@ +import chalk from 'chalk'; +import ora, { Options, Ora } from 'ora'; + +import { eraseLines } from './eraseLines.js'; + +type WaiterOptions = { + opts: Options; + delay?: number; +}; + +export class Waiter { + private spinner: Ora | null; + private text: string; + private timeout: NodeJS.Timeout; + constructor({ opts, delay = 300 }: WaiterOptions) { + this.spinner = null; + this.text = opts.text?.slice() ?? ''; + this.timeout = setTimeout(() => { + this.spinner = ora(opts); + this.spinner.text = chalk.cyan(this.text); + this.spinner.color = 'cyan'; + this.spinner.start(); + }, delay); + } + + public stop() { + clearTimeout(this.timeout); + + if (this.spinner) { + this.spinner.stop(); + this.spinner = null; + process.stderr.write(eraseLines(1)); + } + } + + public setText(newText: string) { + this.text = newText; + + if (this.spinner) { + this.spinner.text = chalk.gray(newText); + } + } +} diff --git a/src/prompts/prompt.ts b/src/prompts/prompt.ts new file mode 100644 index 0000000..93ad688 --- /dev/null +++ b/src/prompts/prompt.ts @@ -0,0 +1,22 @@ +import prompts, { PromptObject } from 'prompts'; + +export type PromptArgs = Omit & { + onCancel?: () => void; +}; + +export const prompt = async ({ onCancel, ...args }: PromptArgs): Promise => { + const { value }: { value?: T } = await prompts( + { + ...args, + name: 'value', + }, + { + onCancel: () => { + onCancel?.(); + process.exit(0); + }, + }, + ); + + return value as T; +}; diff --git a/src/prompts/textPrompt.ts b/src/prompts/textPrompt.ts new file mode 100644 index 0000000..3e6f87c --- /dev/null +++ b/src/prompts/textPrompt.ts @@ -0,0 +1,9 @@ +import { prompt, PromptArgs } from './prompt.js'; + +type TextPromptArgs = Omit & { + initial?: string; +}; + +export const textPrompt = async ({ message, initial, validate, onCancel }: TextPromptArgs): Promise => { + return prompt({ type: 'text', message, initial, validate, onCancel }); +}; diff --git a/src/secrets.ts b/src/secrets.ts new file mode 100644 index 0000000..2e67a9f --- /dev/null +++ b/src/secrets.ts @@ -0,0 +1,9 @@ +type Secrets = { + FLEEK_TOKEN?: string; + FLEEK_PROJECT_ID?: string; +}; + +export const secrets: Secrets = { + FLEEK_TOKEN: process.env.FLEEK_TOKEN, + FLEEK_PROJECT_ID: process.env.FLEEK_PROJECT_ID, +}; diff --git a/src/utils.ts b/src/utils.ts index 90e1e50..bb4b7c0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,8 +1,9 @@ import { readFileSync } from 'node:fs'; import * as path from 'node:path'; import * as fs from 'node:fs/promises'; -import { OpenNextOutput } from './open-next/types'; -import { MiddlewareManifest } from './next/types'; + +import { MiddlewareManifest } from './commands/build/next/types.js'; +import { OpenNextOutput } from './commands/build/open-next/types.js'; export async function copyDir(props: { src: string; dest: string }) { const { src, dest } = props; @@ -44,15 +45,15 @@ export function getSubstitutionValue(v: string): string { return `{{ ${v} }}`; } -export function readMiddlewareManifest(openNextPath: string): MiddlewareManifest { +export function readMiddlewareManifest(projectPath: string): MiddlewareManifest { return JSON.parse( - readFileSync(path.join(openNextPath, '.next', 'server', 'middleware-manifest.json'), 'utf-8'), + readFileSync(path.join(projectPath, '.next', 'server', 'middleware-manifest.json'), 'utf-8'), ) as MiddlewareManifest; } -export function readOpenNextOutput(openNextPath: string): OpenNextOutput { +export function readOpenNextOutput(projectPath: string): OpenNextOutput { return JSON.parse( - readFileSync(path.join(openNextPath, '.open-next', 'open-next.output.json'), 'utf-8'), + readFileSync(path.join(projectPath, '.open-next', 'open-next.output.json'), 'utf-8'), ) as OpenNextOutput; } diff --git a/src/utils/json.ts b/src/utils/json.ts new file mode 100644 index 0000000..20f32eb --- /dev/null +++ b/src/utils/json.ts @@ -0,0 +1,30 @@ +// Warning this file may be included from outside `/src` +// For example, see `/bin/index.js` +import { readFileSync } from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +// The build distribution target directory +const BUILD_DIST_PATHNAME = '/dist'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const leadingSlash = (str: string) => (str.startsWith('/') ? str : '/' + str); + +const resolvePath = (filename: string) => { + return path.join(__dirname.split(BUILD_DIST_PATHNAME)[0], leadingSlash(filename)); +}; + +// JSON files should live outside `src` +// help prevent tsc from generating the directory `/dist/src` +// as current setup prefers surface files from `/src` into `/dist` +export const loadJSONFromPackageRoot = (filename: string) => { + const resolved = resolvePath(filename); + + return loadJSONFromPath(resolved); +}; + +export const loadJSONFromPath = (filePath: string) => { + return JSON.parse(readFileSync(filePath, 'utf-8')); +}; diff --git a/src/utils/packageManager.ts b/src/utils/packageManager.ts index c4b5f1c..028b64f 100644 --- a/src/utils/packageManager.ts +++ b/src/utils/packageManager.ts @@ -15,8 +15,13 @@ const packageManagers = [ { file: 'bun.lockb', packager: PackageManager.bun }, ]; -export function findPackageManager(appPath: string) { - const found = packageManagers.find((packageManager) => existsSync(path.join(appPath, packageManager.file))); +export function findPackageManager(projectPath: string) { + const found = packageManagers.find((packageManager) => existsSync(path.join(projectPath, packageManager.file))); return found?.packager ?? PackageManager.npm; } + +export function getBuildCommand(opts: { projectPath: string }) { + const packageManager = findPackageManager(opts.projectPath); + return `${packageManager} build`; +} diff --git a/src/utils/translation.ts b/src/utils/translation.ts new file mode 100644 index 0000000..5487661 --- /dev/null +++ b/src/utils/translation.ts @@ -0,0 +1,57 @@ +import { loadJSONFromPackageRoot } from './json.js'; +const en: Record = loadJSONFromPackageRoot('locales/en.json'); +import chalk from 'chalk'; + +import type { En } from '../../locales/en.js'; + +type AnsiOptions = { + bold: boolean; +}; + +type Values = Record & { + options?: AnsiOptions; +}; + +// TODO: Refactor the color system to be stricter to a small amount of well defined colours and meanings. See `Output.ts`, `update-notifier.ts` and wherever its applied + +// Ansi escape code handlers +// these are ansi level, so do not confuse with the +// cli `Output.ts` +const _b = (text: string) => chalk['bold'](text); + +const _t = (key: string, values?: Values) => { + const txt = (en as Record)[key]; + + if (!txt) { + console.error(`Missing ${key}`); + + return `[ERROR: Missing ${key}]`; + } + + const matches = [...txt.matchAll(/{(.*?)}/g)]; + + let transl = txt; + + if (matches.length && values) { + transl = matches.reduce((acc, curr) => { + const txt = values[curr[1]]; + + // Skip non-matches + // e.g. the update-notifier uses the same placeholder + // convention as the localization as {placeholder} + if (typeof txt !== 'string') { + return acc; + } + + const val = values?.options?.bold ? _b(txt) : txt; + acc = acc.replace(curr[0], val); + + return acc; + }, txt); + } + + return transl; +}; + +// TODO: create util for plural e.g. project -> projects +export const t = (key: En, values?: Values) => _t(key as string, values); diff --git a/src/utils/update-notifier.ts b/src/utils/update-notifier.ts new file mode 100644 index 0000000..3efd767 --- /dev/null +++ b/src/utils/update-notifier.ts @@ -0,0 +1,51 @@ +import chalk from 'chalk'; +import updateNotifier from 'update-notifier-cjs'; + +import { t } from '../utils/translation.js'; + +const hours = 4; // Number of hours for the interval +const updateCheckInterval = 1000 * 60 * 60 * hours; + +type CheckForPackageUpdatesArgs = { + pkg: string; + updateCheckInternal: number; +}; + +export const checkForPackageUpdates = async (pkg: CheckForPackageUpdatesArgs) => { + const notifier = updateNotifier({ pkg, updateCheckInterval }); + + if (!notifier.update) { + return; + } + + const { current, latest, name } = notifier.update; + + const installCmd = chalk['yellow'](`npm i -g ${name}`); + const verifyCmd = chalk['yellow']('fleek version'); + + const message = t('updateAvailable', { + updateRequired: t('updateRequired'), + howToUpdate: t('howToUpdate'), + whyUpdate: t('whyUpdate'), + installCmd, + verifyCmd, + // The following are overrides: packageName, currentVersion and LatestVersion. Since the update-notifier uses the same placeholder convention {placeholder}, it'd fallback to the update-notifier computed text. Thus, we have an opportunity to customise the values provides from the registry + packageName: name, + currentVersion: chalk['red'](current), + latestVersion: chalk['green'](latest), + options: { + bold: true, + }, + }); + + notifier.notify({ + message, + boxenOptions: { + padding: 1, + margin: 1, + align: 'left', + borderColor: 'yellow', + borderStyle: 'round', + }, + }); +}; diff --git a/src/validation/isProjectPathValid.ts b/src/validation/isProjectPathValid.ts new file mode 100644 index 0000000..13ce550 --- /dev/null +++ b/src/validation/isProjectPathValid.ts @@ -0,0 +1,58 @@ +import { statSync } from 'fs'; +import * as path from 'path'; +import { ProjectInvalidPathError } from '../errors/ProjectInvalidPathError.js'; +import { ProjectPathUnknownError } from '../errors/ProjectPathUnknownError.js'; +import { ProjectInvalidDirError } from '../errors/ProjectInvalidDirError.js'; +import { ProjectPathMissingAccessError } from '../errors/ProjectPathMissingAccessError.js'; +import { PackageJsonInvalidPathError } from '../errors/PackageJsonInvalidPathError.js'; +import { PackageJsonMissingAccessError } from '../errors/PackageJsonMissingAccessError.js'; +import { PackageJsonUnknownError } from '../errors/PackageJsonUnknownError.js'; +import { PackageJsonInvalidFileError } from '../errors/PackageJsonInvalidFileError.js'; + +type IsProjectPathValidArgs = { + projectPath: string; +}; + +export const validateProjectPath = ({ projectPath }: IsProjectPathValidArgs) => { + const packageJsonPath = path.join(projectPath, 'package.json'); + + let projectPathStat; + try { + projectPathStat = statSync(projectPath); + } catch (err) { + if ((err as NodeJS.ErrnoException).code === 'ENOENT') { + throw new ProjectInvalidPathError({ name: projectPath }); + } + + if ((err as NodeJS.ErrnoException).code === 'EACCES') { + throw new ProjectPathMissingAccessError({ name: projectPath }); + } + + throw new ProjectPathUnknownError({ name: projectPath, code: (err as NodeJS.ErrnoException).code }); + } + + if (!projectPathStat.isDirectory()) { + throw new ProjectInvalidDirError({ name: projectPath }); + } + + let packageJsonStat; + try { + packageJsonStat = statSync(packageJsonPath); + } catch (err) { + if ((err as NodeJS.ErrnoException).code === 'ENOENT') { + throw new PackageJsonInvalidPathError({ name: projectPath }); + } + + if ((err as NodeJS.ErrnoException).code === 'EACCES') { + throw new PackageJsonMissingAccessError({ name: projectPath }); + } + + throw new PackageJsonUnknownError({ name: projectPath, code: (err as NodeJS.ErrnoException).code }); + } + + if (!packageJsonStat.isFile()) { + throw new PackageJsonInvalidFileError({ name: projectPath }); + } + + return true; +}; diff --git a/tsconfig.json b/tsconfig.json index 5d6b060..ba65b7d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,13 +2,14 @@ "compilerOptions": { "outDir": "dist", "rootDir": "src", - "module": "commonjs", + "module": "ESNext", "target": "ESNext", + "moduleResolution": "node", "declaration": true, "strict": true, "skipLibCheck": true, "esModuleInterop": true }, - "include": ["src/**/*"], + "include": ["src/**/*", "global.d.ts", "locales/*.d.ts", "dist/index.ts"], "exclude": ["node_modules/**", "coverage", "src/**/*.spec.ts"] }