Skip to content
Snippets Groups Projects
Unverified Commit 21f90968 authored by yisding's avatar yisding Committed by GitHub
Browse files

Merge pull request #262 from run-llama/ms/create-llama-add-e2e

Feat: added e2e for create-llama (thanks @himself65)
parents 9830dc29 c99c5eb0
No related branches found
No related tags found
No related merge requests found
Showing
with 396 additions and 23 deletions
name: E2E Tests
on:
push:
branches: [main]
pull_request:
paths:
- "packages/create-llama/**"
- ".github/workflows/e2e.yml"
branches: [main]
jobs:
e2e:
name: create-llama
timeout-minutes: 60
strategy:
fail-fast: true
matrix:
node-version: [18, 20]
os: [macos-latest] # add windows-latest after timeout issue is fixed (see https://github.com/run-llama/LlamaIndexTS/issues/263)
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Install Playwright Browsers
run: pnpm exec playwright install --with-deps
working-directory: ./packages/create-llama
- name: Build create-llama
run: pnpm run build
working-directory: ./packages/create-llama
- name: Run Playwright tests
run: pnpm exec playwright test
working-directory: ./packages/create-llama
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: ./packages/create-llama/playwright-report/
retention-days: 30
...@@ -40,3 +40,9 @@ dist/ ...@@ -40,3 +40,9 @@ dist/
# vs code # vs code
.vscode/launch.json .vscode/launch.json
.cache
test-results/
playwright-report/
blob-report/
playwright/.cache/
...@@ -29,7 +29,7 @@ export async function createApp({ ...@@ -29,7 +29,7 @@ export async function createApp({
packageManager, packageManager,
eslint, eslint,
frontend, frontend,
openAIKey, openAiKey,
model, model,
communityProjectPath, communityProjectPath,
}: InstallAppArgs): Promise<void> { }: InstallAppArgs): Promise<void> {
...@@ -68,7 +68,7 @@ export async function createApp({ ...@@ -68,7 +68,7 @@ export async function createApp({
packageManager, packageManager,
isOnline, isOnline,
eslint, eslint,
openAIKey, openAiKey,
model, model,
communityProjectPath, communityProjectPath,
}; };
......
/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from "@playwright/test";
import type {
TemplateEngine,
TemplateFramework,
TemplateType,
TemplateUI,
} from "../templates";
import { createTestDir, runApp, runCreateLlama, type AppType } from "./utils";
const templateTypes: TemplateType[] = ["streaming", "simple"];
const templateFrameworks: TemplateFramework[] = ["nextjs", "express"];
const templateEngines: TemplateEngine[] = ["simple", "context"];
const templateUIs: TemplateUI[] = ["shadcn", "html"];
for (const templateType of templateTypes) {
for (const templateFramework of templateFrameworks) {
for (const templateEngine of templateEngines) {
for (const templateUI of templateUIs) {
if (templateFramework === "nextjs" && templateType === "simple") {
// nextjs doesn't support simple templates - skip tests
continue;
}
if (templateEngine === "context") {
// we don't test context templates because it needs OPEN_AI_KEY
continue;
}
const appType: AppType =
templateFramework === "express" || templateFramework === "fastapi"
? templateType === "simple"
? "--no-frontend" // simple templates don't have frontends
: "--frontend"
: "";
test(`try create-llama ${templateType} ${templateFramework} ${templateEngine} ${templateUI} ${appType}`, async ({
page,
}) => {
const cwd = await createTestDir();
const name = runCreateLlama(
cwd,
templateType,
templateFramework,
templateEngine,
templateUI,
appType,
);
const port = Math.floor(Math.random() * 10000) + 10000;
const cps = await runApp(cwd, name, appType, port);
// test frontend
if (appType !== "--no-frontend") {
await page.goto(`http://localhost:${port}`);
await expect(page.getByText("Built by LlamaIndex")).toBeVisible();
}
// TODO: test backend using curl (would need OpenAI key)
// clean processes
cps.forEach((cp) => cp.kill());
});
}
}
}
}
import { ChildProcess, exec, execSync } from "child_process";
import crypto from "node:crypto";
import { mkdir } from "node:fs/promises";
import * as path from "path";
import waitPort from "wait-port";
export type AppType = "--frontend" | "--no-frontend" | "";
const MODEL = "gpt-3.5-turbo";
export async function runApp(
cwd: string,
name: string,
appType: AppType,
port: number,
): Promise<ChildProcess[]> {
const cps: ChildProcess[] = [];
try {
switch (appType) {
case "--frontend":
cps.push(
await createProcess(
"npm run dev",
path.join(cwd, name, "backend"),
port + 1,
),
);
cps.push(
await createProcess(
"npm run dev",
path.join(cwd, name, "frontend"),
port,
),
);
break;
default:
cps.push(
await createProcess("npm run dev", path.join(cwd, name), port),
);
break;
}
} catch (e) {
cps.forEach((cp) => cp.kill());
throw e;
}
return cps;
}
async function createProcess(command: string, cwd: string, port: number) {
const cp = exec(command, {
cwd,
env: {
...process.env,
PORT: `${port}`,
},
});
if (!cp) throw new Error(`Can't start process ${command} in ${cwd}`);
await waitPort({
host: "localhost",
port,
timeout: 1000 * 60,
});
return cp;
}
export function runCreateLlama(
cwd: string,
templateType: string,
templateFramework: string,
templateEngine: string,
templateUI: string,
appType: AppType,
) {
const createLlama = path.join(__dirname, "..", "dist", "index.js");
const name = [
templateType,
templateFramework,
templateEngine,
templateUI,
appType,
].join("-");
const command = [
"node",
createLlama,
name,
"--template",
templateType,
"--framework",
templateFramework,
"--engine",
templateEngine,
"--ui",
templateUI,
"--model",
MODEL,
"--open-ai-key",
"testKey",
appType,
"--eslint",
"--use-npm",
].join(" ");
console.log(`running command '${command}' in ${cwd}`);
execSync(command, {
stdio: "inherit",
cwd,
});
return name;
}
export async function createTestDir() {
const cwd = path.join(__dirname, ".cache", crypto.randomUUID());
await mkdir(cwd, { recursive: true });
return cwd;
}
...@@ -61,6 +61,55 @@ const program = new Commander.Command(packageJson.name) ...@@ -61,6 +61,55 @@ const program = new Commander.Command(packageJson.name)
` `
Explicitly tell the CLI to reset any stored preferences Explicitly tell the CLI to reset any stored preferences
`,
)
.option(
"--template <template>",
`
Select a template to bootstrap the application with.
`,
)
.option(
"--engine <engine>",
`
Select a chat engine to bootstrap the application with.
`,
)
.option(
"--framework <framework>",
`
Select a framework to bootstrap the application with.
`,
)
.option(
"--open-ai-key <key>",
`
Provide an OpenAI API key.
`,
)
.option(
"--ui <ui>",
`
Select a UI to bootstrap the application with.
`,
)
.option(
"--frontend",
`
Whether to generate a frontend for your backend.
`,
)
.option(
"--model",
`
Select OpenAI model to use. E.g. gpt-3.5-turbo.
`, `,
) )
.allowUnknownOption() .allowUnknownOption()
...@@ -113,7 +162,7 @@ async function run(): Promise<void> { ...@@ -113,7 +162,7 @@ async function run(): Promise<void> {
"\nPlease specify the project directory:\n" + "\nPlease specify the project directory:\n" +
` ${cyan(program.name())} ${green("<project-directory>")}\n` + ` ${cyan(program.name())} ${green("<project-directory>")}\n` +
"For example:\n" + "For example:\n" +
` ${cyan(program.name())} ${green("my-next-app")}\n\n` + ` ${cyan(program.name())} ${green("my-app")}\n\n` +
`Run ${cyan(`${program.name()} --help`)} to see all options.`, `Run ${cyan(`${program.name()} --help`)} to see all options.`,
); );
process.exit(1); process.exit(1);
...@@ -157,7 +206,7 @@ async function run(): Promise<void> { ...@@ -157,7 +206,7 @@ async function run(): Promise<void> {
packageManager, packageManager,
eslint: program.eslint, eslint: program.eslint,
frontend: program.frontend, frontend: program.frontend,
openAIKey: program.openAIKey, openAiKey: program.openAiKey,
model: program.model, model: program.model,
communityProjectPath: program.communityProjectPath, communityProjectPath: program.communityProjectPath,
}); });
......
...@@ -23,9 +23,11 @@ ...@@ -23,9 +23,11 @@
"dev": "ncc build ./index.ts -w -o dist/", "dev": "ncc build ./index.ts -w -o dist/",
"build": "ncc build ./index.ts -o ./dist/ --minify --no-cache --no-source-map-register", "build": "ncc build ./index.ts -o ./dist/ --minify --no-cache --no-source-map-register",
"lint": "eslint . --ignore-pattern dist", "lint": "eslint . --ignore-pattern dist",
"e2e": "playwright test --reporter=list",
"prepublishOnly": "cd ../../ && turbo run build" "prepublishOnly": "cd ../../ && turbo run build"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.40.0",
"@types/async-retry": "1.4.2", "@types/async-retry": "1.4.2",
"@types/ci-info": "2.0.0", "@types/ci-info": "2.0.0",
"@types/cross-spawn": "6.0.0", "@types/cross-spawn": "6.0.0",
...@@ -47,9 +49,10 @@ ...@@ -47,9 +49,10 @@
"tar": "6.1.15", "tar": "6.1.15",
"terminal-link": "^3.0.0", "terminal-link": "^3.0.0",
"update-check": "1.5.4", "update-check": "1.5.4",
"validate-npm-package-name": "3.0.0" "validate-npm-package-name": "3.0.0",
"wait-port": "^1.1.0"
}, },
"engines": { "engines": {
"node": ">=16.14.0" "node": ">=16.14.0"
} }
} }
\ No newline at end of file
/* eslint-disable turbo/no-undeclared-env-vars */
import { defineConfig, devices } from "@playwright/test";
export default defineConfig({
testDir: "./e2e",
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
timeout: 1000 * 60 * 5,
reporter: "html",
use: {
trace: "on-first-retry",
},
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
],
});
...@@ -14,7 +14,7 @@ const defaults: QuestionArgs = { ...@@ -14,7 +14,7 @@ const defaults: QuestionArgs = {
ui: "html", ui: "html",
eslint: true, eslint: true,
frontend: false, frontend: false,
openAIKey: "", openAiKey: "",
model: "gpt-3.5-turbo", model: "gpt-3.5-turbo",
communityProjectPath: "", communityProjectPath: "",
}; };
...@@ -131,8 +131,11 @@ export const askQuestions = async ( ...@@ -131,8 +131,11 @@ export const askQuestions = async (
} }
if (program.framework === "express" || program.framework === "fastapi") { if (program.framework === "express" || program.framework === "fastapi") {
if (process.argv.includes("--no-frontend")) {
program.frontend = false;
}
// if a backend-only framework is selected, ask whether we should create a frontend // if a backend-only framework is selected, ask whether we should create a frontend
if (!program.frontend) { if (program.frontend === undefined) {
if (ciInfo.isCI) { if (ciInfo.isCI) {
program.frontend = getPrefOrDefault("frontend"); program.frontend = getPrefOrDefault("frontend");
} else { } else {
...@@ -157,6 +160,9 @@ export const askQuestions = async ( ...@@ -157,6 +160,9 @@ export const askQuestions = async (
preferences.frontend = Boolean(frontend); preferences.frontend = Boolean(frontend);
} }
} }
} else {
// single project if framework is nextjs
program.frontend = false;
} }
if (program.framework === "nextjs" || program.frontend) { if (program.framework === "nextjs" || program.frontend) {
...@@ -239,7 +245,7 @@ export const askQuestions = async ( ...@@ -239,7 +245,7 @@ export const askQuestions = async (
} }
} }
if (!program.openAIKey) { if (!program.openAiKey) {
const { key } = await prompts( const { key } = await prompts(
{ {
type: "text", type: "text",
...@@ -248,8 +254,8 @@ export const askQuestions = async ( ...@@ -248,8 +254,8 @@ export const askQuestions = async (
}, },
handlers, handlers,
); );
program.openAIKey = key; program.openAiKey = key;
preferences.openAIKey = key; preferences.openAiKey = key;
} }
if ( if (
...@@ -274,4 +280,10 @@ export const askQuestions = async ( ...@@ -274,4 +280,10 @@ export const askQuestions = async (
preferences.eslint = Boolean(eslint); preferences.eslint = Boolean(eslint);
} }
} }
// TODO: consider using zod to validate the input (doesn't work like this as not every option is required)
// templateUISchema.parse(program.ui);
// templateEngineSchema.parse(program.engine);
// templateFrameworkSchema.parse(program.framework);
// templateTypeSchema.parse(program.template);``
}; };
...@@ -16,12 +16,12 @@ import { ...@@ -16,12 +16,12 @@ import {
TemplateFramework, TemplateFramework,
} from "./types"; } from "./types";
const createEnvLocalFile = async (root: string, openAIKey?: string) => { const createEnvLocalFile = async (root: string, openAiKey?: string) => {
if (openAIKey) { if (openAiKey) {
const envFileName = ".env"; const envFileName = ".env";
await fs.writeFile( await fs.writeFile(
path.join(root, envFileName), path.join(root, envFileName),
`OPENAI_API_KEY=${openAIKey}\n`, `OPENAI_API_KEY=${openAiKey}\n`,
); );
console.log(`Created '${envFileName}' file containing OPENAI_API_KEY`); console.log(`Created '${envFileName}' file containing OPENAI_API_KEY`);
} }
...@@ -32,7 +32,7 @@ const copyTestData = async ( ...@@ -32,7 +32,7 @@ const copyTestData = async (
framework: TemplateFramework, framework: TemplateFramework,
packageManager?: PackageManager, packageManager?: PackageManager,
engine?: TemplateEngine, engine?: TemplateEngine,
openAIKey?: string, openAiKey?: string,
) => { ) => {
if (framework === "nextjs") { if (framework === "nextjs") {
// XXX: This is a hack to make the build for nextjs work with pdf-parse // XXX: This is a hack to make the build for nextjs work with pdf-parse
...@@ -53,7 +53,7 @@ const copyTestData = async ( ...@@ -53,7 +53,7 @@ const copyTestData = async (
} }
if (packageManager && engine === "context") { if (packageManager && engine === "context") {
if (openAIKey || process.env["OPENAI_API_KEY"]) { if (openAiKey || process.env["OPENAI_API_KEY"]) {
console.log( console.log(
`\nRunning ${cyan( `\nRunning ${cyan(
`${packageManager} run generate`, `${packageManager} run generate`,
...@@ -341,7 +341,7 @@ export const installTemplate = async ( ...@@ -341,7 +341,7 @@ export const installTemplate = async (
// This is a backend, so we need to copy the test data and create the env file. // This is a backend, so we need to copy the test data and create the env file.
// Copy the environment file to the target directory. // Copy the environment file to the target directory.
await createEnvLocalFile(props.root, props.openAIKey); await createEnvLocalFile(props.root, props.openAiKey);
// Copy test pdf file // Copy test pdf file
await copyTestData( await copyTestData(
...@@ -349,7 +349,7 @@ export const installTemplate = async ( ...@@ -349,7 +349,7 @@ export const installTemplate = async (
props.framework, props.framework,
props.packageManager, props.packageManager,
props.engine, props.engine,
props.openAIKey, props.openAiKey,
); );
} }
}; };
......
...@@ -16,7 +16,7 @@ export interface InstallTemplateArgs { ...@@ -16,7 +16,7 @@ export interface InstallTemplateArgs {
ui: TemplateUI; ui: TemplateUI;
eslint: boolean; eslint: boolean;
customApiPath?: string; customApiPath?: string;
openAIKey?: string; openAiKey?: string;
forBackend?: string; forBackend?: string;
model: string; model: string;
communityProjectPath?: string; communityProjectPath?: string;
......
/* eslint-disable turbo/no-undeclared-env-vars */
import cors from "cors"; import cors from "cors";
import "dotenv/config"; import "dotenv/config";
import express, { Express, Request, Response } from "express"; import express, { Express, Request, Response } from "express";
import chatRouter from "./src/routes/chat.route"; import chatRouter from "./src/routes/chat.route";
const app: Express = express(); const app: Express = express();
const port = 8000; const port = parseInt(process.env.PORT || "8000");
const env = process.env["NODE_ENV"]; const env = process.env["NODE_ENV"];
const isDevelopment = !env || env === "development"; const isDevelopment = !env || env === "development";
......
/* eslint-disable turbo/no-undeclared-env-vars */
import cors from "cors"; import cors from "cors";
import "dotenv/config"; import "dotenv/config";
import express, { Express, Request, Response } from "express"; import express, { Express, Request, Response } from "express";
import chatRouter from "./src/routes/chat.route"; import chatRouter from "./src/routes/chat.route";
const app: Express = express(); const app: Express = express();
const port = 8000; const port = parseInt(process.env.PORT || "8000");
const env = process.env["NODE_ENV"]; const env = process.env["NODE_ENV"];
const isDevelopment = !env || env === "development"; const isDevelopment = !env || env === "development";
......
...@@ -7,5 +7,8 @@ ...@@ -7,5 +7,8 @@
"esModuleInterop": true, "esModuleInterop": true,
"skipLibCheck": false "skipLibCheck": false
}, },
"exclude": ["templates", "dist"] "exclude": [
} "templates",
"dist"
]
}
\ No newline at end of file
...@@ -240,6 +240,9 @@ importers: ...@@ -240,6 +240,9 @@ importers:
   
packages/create-llama: packages/create-llama:
devDependencies: devDependencies:
'@playwright/test':
specifier: ^1.40.0
version: 1.40.0
'@types/async-retry': '@types/async-retry':
specifier: 1.4.2 specifier: 1.4.2
version: 1.4.2 version: 1.4.2
...@@ -306,6 +309,9 @@ importers: ...@@ -306,6 +309,9 @@ importers:
validate-npm-package-name: validate-npm-package-name:
specifier: 3.0.0 specifier: 3.0.0
version: 3.0.0 version: 3.0.0
wait-port:
specifier: ^1.1.0
version: 1.1.0
   
packages/eslint-config-custom: packages/eslint-config-custom:
dependencies: dependencies:
...@@ -3903,6 +3909,14 @@ packages: ...@@ -3903,6 +3909,14 @@ packages:
tslib: 2.6.1 tslib: 2.6.1
dev: false dev: false
   
/@playwright/test@1.40.0:
resolution: {integrity: sha512-PdW+kn4eV99iP5gxWNSDQCbhMaDVej+RXL5xr6t04nbKLCBwYtA046t7ofoczHOm8u6c+45hpDKQVZqtqwkeQg==}
engines: {node: '>=16'}
hasBin: true
dependencies:
playwright: 1.40.0
dev: true
/@polka/url@1.0.0-next.23: /@polka/url@1.0.0-next.23:
resolution: {integrity: sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==} resolution: {integrity: sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==}
dev: false dev: false
...@@ -6326,6 +6340,11 @@ packages: ...@@ -6326,6 +6340,11 @@ packages:
engines: {node: '>= 12'} engines: {node: '>= 12'}
dev: false dev: false
   
/commander@9.5.0:
resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
engines: {node: ^12.20.0 || >=14}
dev: true
/commondir@1.0.1: /commondir@1.0.1:
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
dev: false dev: false
...@@ -8559,6 +8578,14 @@ packages: ...@@ -8559,6 +8578,14 @@ packages:
/fs.realpath@1.0.0: /fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
   
/fsevents@2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
dev: true
optional: true
/fsevents@2.3.3: /fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
...@@ -12147,6 +12174,22 @@ packages: ...@@ -12147,6 +12174,22 @@ packages:
resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==}
dev: false dev: false
   
/playwright-core@1.40.0:
resolution: {integrity: sha512-fvKewVJpGeca8t0ipM56jkVSU6Eo0RmFvQ/MaCQNDYm+sdvKkMBBWTE1FdeMqIdumRaXXjZChWHvIzCGM/tA/Q==}
engines: {node: '>=16'}
hasBin: true
dev: true
/playwright@1.40.0:
resolution: {integrity: sha512-gyHAgQjiDf1m34Xpwzaqb76KgfzYrhK7iih+2IzcOCoZWr/8ZqmdBw+t0RU85ZmfJMgtgAiNtBQ/KS2325INXw==}
engines: {node: '>=16'}
hasBin: true
dependencies:
playwright-core: 1.40.0
optionalDependencies:
fsevents: 2.3.2
dev: true
/portkey-ai@0.1.16: /portkey-ai@0.1.16:
resolution: {integrity: sha512-EY4FRp6PZSD75Q1o1qc08DfPNTG9FnkUPN3Z1/lEvaq9iFpSO5UekcagUZaKSVhao311qjBjns+kF0rS9ht7iA==} resolution: {integrity: sha512-EY4FRp6PZSD75Q1o1qc08DfPNTG9FnkUPN3Z1/lEvaq9iFpSO5UekcagUZaKSVhao311qjBjns+kF0rS9ht7iA==}
dependencies: dependencies:
...@@ -15655,6 +15698,18 @@ packages: ...@@ -15655,6 +15698,18 @@ packages:
- debug - debug
dev: false dev: false
   
/wait-port@1.1.0:
resolution: {integrity: sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==}
engines: {node: '>=10'}
hasBin: true
dependencies:
chalk: 4.1.2
commander: 9.5.0
debug: 4.3.4
transitivePeerDependencies:
- supports-color
dev: true
/walker@1.0.8: /walker@1.0.8:
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
dependencies: dependencies:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment