From 302b3a05c21ba2b033a7ff9eee82b7db744f9d83 Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Mon, 16 Mar 2026 00:46:14 +0900 Subject: [PATCH 01/20] =?UTF-8?q?docs:=20=EA=B5=AC=ED=98=84=ED=95=A0=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EB=AA=A9=EB=A1=9D=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/READEME.md | 38 ++ package-lock.json | 1120 +++++++++++++++++++++++++++++++-------------- 2 files changed, 815 insertions(+), 343 deletions(-) create mode 100644 docs/READEME.md diff --git a/docs/READEME.md b/docs/READEME.md new file mode 100644 index 00000000..febfa03f --- /dev/null +++ b/docs/READEME.md @@ -0,0 +1,38 @@ +# 과제1 요구사항 + +1. 컴퓨터 랜덤 숫자 생성 (1~9, 서로 다른 3자리) + - Random.pickNumberInRange(1, 9)를 3번 호출하여 한 자리씩 확정 + - 중복된 숫자가 나오면 다시 뽑기 +2. 사용자 입력 처리 +3. 입력값 검증 → 실패 시 alert으로 에러 메시지를 보여주고, 다시 입력할 수 있게 한다 + - 숫자가 아닌 경우 + - 세 글자가 아닌 경우 + - 중복된 숫자가 있는 경우 + - 0이 포함된 경우 +4. 볼/스트라이크 판정 — play(computerInputNumbers, userInputNumbers) + - 같은 수가 같은 자리 → 스트라이크 + - 같은 수가 다른 자리 → 볼 + - 같은 수가 전혀 없으면 → 낫싱 + - 결과 String 반환 (볼 먼저, 스트라이크 나중) +5. 결과 화면 출력 +6. 게임 종료 처리 (3스트라이크 → 게임 종료 + 재시작 버튼 노출) +7. 게임 재시작 (재시작 버튼 클릭 → 새 랜덤 숫자로 초기화) + +## 사용 라이브러리 + +- MissionUtils (index.html에 포함, import 불필요) + - Random.pickNumberInRange(1, 9) — 랜덤 숫자 생성 +- 외부 라이브러리(jQuery, Lodash 등) 사용 금지, 순수 Vanilla JS만 사용 + +## 코딩 컨벤션 + +- var 사용 금지, const와 let 사용 +- Airbnb 자바스크립트 코드 컨벤션 준수 +- indent depth 2 이하 +- 함수(메소드)는 한 가지 일만 하도록 최대한 작게 +- 함수(메소드) 길이 15라인 이하 +- import 문으로 모듈화 + +## 검증 + +npm run test diff --git a/package-lock.json b/package-lock.json index ba108683..2f8c91a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,27 @@ { "name": "javascript-baseball-precourse", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@cypress/request": { + "packages": { + "": { + "name": "javascript-baseball-precourse", + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "cypress": "8.7.0" + }, + "engines": { + "node": ">=14.0.0", + "npm": ">=6.0.0" + } + }, + "node_modules/@cypress/request": { "version": "2.88.7", "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.7.tgz", "integrity": "sha512-FTULIP2rnDJvZDT9t6B4nSfYR40ue19tVmv3wUcY05R9/FPCoMl1nAPJkzWzBCo7ltVn5ThQTbxiMoGBN7k0ig==", "dev": true, - "requires": { + "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", "caseless": "~0.12.0", @@ -29,374 +41,481 @@ "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^8.3.2" + }, + "engines": { + "node": ">= 6" } }, - "@cypress/xvfb": { + "node_modules/@cypress/xvfb": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", "dev": true, - "requires": { + "dependencies": { "debug": "^3.1.0", "lodash.once": "^4.1.1" - }, + } + }, + "node_modules/@cypress/xvfb/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } + "ms": "^2.1.1" } }, - "@types/node": { + "node_modules/@types/node": { "version": "14.17.34", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.34.tgz", "integrity": "sha512-USUftMYpmuMzeWobskoPfzDi+vkpe0dvcOBRNOscFrGxVp4jomnRxWuVohgqBow2xyIPC0S3gjxV/5079jhmDg==", "dev": true }, - "@types/sinonjs__fake-timers": { + "node_modules/@types/sinonjs__fake-timers": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz", "integrity": "sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A==", "dev": true }, - "@types/sizzle": { + "node_modules/@types/sizzle": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", "dev": true }, - "@types/yauzl": { + "node_modules/@types/yauzl": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz", "integrity": "sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==", "dev": true, "optional": true, - "requires": { + "dependencies": { "@types/node": "*" } }, - "aggregate-error": { + "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, - "requires": { + "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "ajv": { + "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "requires": { + "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "ansi-colors": { + "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "ansi-escapes": { + "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, - "requires": { + "dependencies": { "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "ansi-regex": { + "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "ansi-styles": { + "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "requires": { + "dependencies": { "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "arch": { + "node_modules/arch": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", - "dev": true + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "asn1": { + "node_modules/asn1": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, - "requires": { + "dependencies": { "safer-buffer": "~2.1.0" } }, - "assert-plus": { + "node_modules/assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.8" + } }, - "astral-regex": { + "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "async": { + "node_modules/async": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==", "dev": true }, - "asynckit": { + "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, - "at-least-node": { + "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true + "dev": true, + "engines": { + "node": ">= 4.0.0" + } }, - "aws-sign2": { + "node_modules/aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true + "dev": true, + "engines": { + "node": "*" + } }, - "aws4": { + "node_modules/aws4": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, - "balanced-match": { + "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "bcrypt-pbkdf": { + "node_modules/bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "dev": true, - "requires": { + "dependencies": { "tweetnacl": "^0.14.3" } }, - "blob-util": { + "node_modules/blob-util": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", "dev": true }, - "bluebird": { + "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, - "brace-expansion": { + "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "requires": { + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "buffer-crc32": { + "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true + "dev": true, + "engines": { + "node": "*" + } }, - "cachedir": { + "node_modules/cachedir": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "caseless": { + "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, - "chalk": { + "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "check-more-types": { + "node_modules/check-more-types": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.8.0" + } }, - "ci-info": { + "node_modules/ci-info": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", "integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==", "dev": true }, - "clean-stack": { + "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "cli-cursor": { + "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, - "requires": { + "dependencies": { "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" } }, - "cli-table3": { + "node_modules/cli-table3": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", "dev": true, - "requires": { - "colors": "^1.1.2", + "dependencies": { "object-assign": "^4.1.0", "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "colors": "^1.1.2" } }, - "cli-truncate": { + "node_modules/cli-truncate": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, - "requires": { + "dependencies": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "clone": { + "node_modules/clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.8" + } }, - "color-convert": { + "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "requires": { + "dependencies": { "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "color-name": { + "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "colorette": { + "node_modules/colorette": { "version": "2.0.16", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", "dev": true }, - "colors": { + "node_modules/colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true, - "optional": true + "optional": true, + "engines": { + "node": ">=0.1.90" + } }, - "combined-stream": { + "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, - "requires": { + "dependencies": { "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "commander": { + "node_modules/commander": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true + "dev": true, + "engines": { + "node": ">= 6" + } }, - "common-tags": { + "node_modules/common-tags": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", - "dev": true + "dev": true, + "engines": { + "node": ">=4.0.0" + } }, - "concat-map": { + "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "core-util-is": { + "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, - "cross-spawn": { + "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, - "requires": { + "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "cypress": { + "node_modules/cypress": { "version": "8.7.0", "resolved": "https://registry.npmjs.org/cypress/-/cypress-8.7.0.tgz", "integrity": "sha512-b1bMC3VQydC6sXzBMFnSqcvwc9dTZMgcaOzT0vpSD+Gq1yFc+72JDWi55sfUK5eIeNLAtWOGy1NNb6UlhMvB+Q==", "dev": true, - "requires": { + "hasInstallScript": true, + "dependencies": { "@cypress/request": "^2.88.6", "@cypress/xvfb": "^1.2.4", "@types/node": "^14.14.31", @@ -439,98 +558,122 @@ "untildify": "^4.0.0", "url": "^0.11.0", "yauzl": "^2.10.0" + }, + "bin": { + "cypress": "bin/cypress" + }, + "engines": { + "node": ">=12.0.0" } }, - "dashdash": { + "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, - "requires": { + "dependencies": { "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" } }, - "dayjs": { + "node_modules/dayjs": { "version": "1.10.7", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz", "integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig==", "dev": true }, - "debug": { + "node_modules/debug": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, - "requires": { + "dependencies": { "ms": "2.1.2" }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true } } }, - "delayed-stream": { + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.4.0" + } }, - "ecc-jsbn": { + "node_modules/ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "dev": true, - "requires": { + "dependencies": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" } }, - "emoji-regex": { + "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "end-of-stream": { + "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, - "requires": { + "dependencies": { "once": "^1.4.0" } }, - "enquirer": { + "node_modules/enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dev": true, - "requires": { + "dependencies": { "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" } }, - "escape-string-regexp": { + "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.8.0" + } }, - "eventemitter2": { + "node_modules/eventemitter2": { "version": "6.4.5", "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.5.tgz", "integrity": "sha512-bXE7Dyc1i6oQElDG0jMRZJrRAn9QR2xyyFGmBdZleNmyQX0FqGYmhZIrIrpPfm/w//LTo4tVQGOGQcGCb5q9uw==", "dev": true }, - "execa": { + "node_modules/execa": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, - "requires": { + "dependencies": { "cross-spawn": "^7.0.0", "get-stream": "^5.0.0", "human-signals": "^1.1.1", @@ -540,348 +683,461 @@ "onetime": "^5.1.0", "signal-exit": "^3.0.2", "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "executable": { + "node_modules/executable": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", "dev": true, - "requires": { + "dependencies": { "pify": "^2.2.0" + }, + "engines": { + "node": ">=4" } }, - "extend": { + "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, - "extract-zip": { + "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, - "requires": { - "@types/yauzl": "^2.9.1", + "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" } }, - "extsprintf": { + "node_modules/extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true + "dev": true, + "engines": [ + "node >=0.6.0" + ] }, - "fast-deep-equal": { + "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "fast-json-stable-stringify": { + "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, - "fd-slicer": { + "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", "dev": true, - "requires": { + "dependencies": { "pend": "~1.2.0" } }, - "figures": { + "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, - "requires": { + "dependencies": { "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "forever-agent": { + "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true + "dev": true, + "engines": { + "node": "*" + } }, - "form-data": { + "node_modules/form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, - "requires": { + "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" } }, - "fs-extra": { + "node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, - "requires": { + "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" } }, - "fs.realpath": { + "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "get-stream": { + "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, - "requires": { + "dependencies": { "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "getos": { + "node_modules/getos": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", "dev": true, - "requires": { + "dependencies": { "async": "^3.2.0" } }, - "getpass": { + "node_modules/getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, - "requires": { + "dependencies": { "assert-plus": "^1.0.0" } }, - "glob": { + "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, - "requires": { + "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "global-dirs": { + "node_modules/global-dirs": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", "dev": true, - "requires": { + "dependencies": { "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "graceful-fs": { + "node_modules/graceful-fs": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", "dev": true }, - "har-schema": { + "node_modules/har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "har-validator": { + "node_modules/har-validator": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", "dev": true, - "requires": { + "dependencies": { "ajv": "^6.12.3", "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" } }, - "has-flag": { + "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "http-signature": { + "node_modules/http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, - "requires": { + "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" } }, - "human-signals": { + "node_modules/human-signals": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true + "dev": true, + "engines": { + "node": ">=8.12.0" + } }, - "indent-string": { + "node_modules/indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "inflight": { + "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, - "requires": { + "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "ini": { + "node_modules/ini": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } }, - "is-ci": { + "node_modules/is-ci": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", "dev": true, - "requires": { + "dependencies": { "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" } }, - "is-fullwidth-code-point": { + "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "is-installed-globally": { + "node_modules/is-installed-globally": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", "dev": true, - "requires": { + "dependencies": { "global-dirs": "^3.0.0", "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "is-path-inside": { + "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "is-stream": { + "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "is-typedarray": { + "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, - "is-unicode-supported": { + "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "isexe": { + "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "isstream": { + "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, - "jsbn": { + "node_modules/jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true }, - "json-schema": { + "node_modules/json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", "dev": true }, - "json-schema-traverse": { + "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "json-stringify-safe": { + "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, - "jsonfile": { + "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "requires": { - "graceful-fs": "^4.1.6", + "dependencies": { "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "jsprim": { + "node_modules/jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", "dev": true, - "requires": { + "engines": [ + "node >=0.6.0" + ], + "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", "json-schema": "0.2.3", "verror": "1.10.0" } }, - "lazy-ass": { + "node_modules/lazy-ass": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=", - "dev": true + "dev": true, + "engines": { + "node": "> 0.8" + } }, - "listr2": { + "node_modules/listr2": { "version": "3.13.4", "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.13.4.tgz", "integrity": "sha512-lZ1Rut1DSIRwbxQbI8qaUBfOWJ1jEYRgltIM97j6kKOCI2pHVWMyxZvkU/JKmRBWcIYgDS2PK+yDgVqm7u3crw==", "dev": true, - "requires": { + "dependencies": { "cli-truncate": "^2.1.0", "clone": "^2.1.2", "colorette": "^2.0.16", @@ -890,331 +1146,449 @@ "rxjs": "^7.4.0", "through": "^2.3.8", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } } }, - "lodash": { + "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "lodash.once": { + "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", "dev": true }, - "log-symbols": { + "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, - "requires": { + "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "log-update": { + "node_modules/log-update": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", "dev": true, - "requires": { + "dependencies": { "ansi-escapes": "^4.3.0", "cli-cursor": "^3.1.0", "slice-ansi": "^4.0.0", "wrap-ansi": "^6.2.0" }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, "dependencies": { - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, - "merge-stream": { + "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "mime-db": { + "node_modules/mime-db": { "version": "1.51.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.6" + } }, - "mime-types": { + "node_modules/mime-types": { "version": "2.1.34", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", "dev": true, - "requires": { + "dependencies": { "mime-db": "1.51.0" + }, + "engines": { + "node": ">= 0.6" } }, - "mimic-fn": { + "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "minimatch": { + "node_modules/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "requires": { + "dependencies": { "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "minimist": { + "node_modules/minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, - "ms": { + "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "npm-run-path": { + "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, - "requires": { + "dependencies": { "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "object-assign": { + "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "once": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "requires": { + "dependencies": { "wrappy": "1" } }, - "onetime": { + "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, - "requires": { + "dependencies": { "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "ospath": { + "node_modules/ospath": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", "integrity": "sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=", "dev": true }, - "p-map": { + "node_modules/p-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, - "requires": { + "dependencies": { "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "path-is-absolute": { + "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "path-key": { + "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "pend": { + "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", "dev": true }, - "performance-now": { + "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, - "pify": { + "node_modules/pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "pretty-bytes": { + "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "proxy-from-env": { + "node_modules/proxy-from-env": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", "dev": true }, - "psl": { + "node_modules/psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", "dev": true }, - "pump": { + "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, - "requires": { + "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, - "punycode": { + "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "qs": { + "node_modules/qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.6" + } }, - "querystring": { + "node_modules/querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "dev": true, + "engines": { + "node": ">=0.4.x" + } }, - "ramda": { + "node_modules/ramda": { "version": "0.27.1", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", "dev": true }, - "request-progress": { + "node_modules/request-progress": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", "dev": true, - "requires": { + "dependencies": { "throttleit": "^1.0.0" } }, - "restore-cursor": { + "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, - "requires": { + "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" } }, - "rimraf": { + "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, - "requires": { + "dependencies": { "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "rxjs": { + "node_modules/rxjs": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", "dev": true, - "requires": { + "dependencies": { "tslib": "~2.1.0" } }, - "safe-buffer": { + "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "safer-buffer": { + "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "shebang-command": { + "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "requires": { + "dependencies": { "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "shebang-regex": { + "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "signal-exit": { + "node_modules/signal-exit": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", "dev": true }, - "slice-ansi": { + "node_modules/slice-ansi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "sshpk": { + "node_modules/sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", "dev": true, - "requires": { + "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", "bcrypt-pbkdf": "^1.0.0", @@ -1224,189 +1598,249 @@ "jsbn": "~0.1.0", "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" } }, - "string-width": { + "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "requires": { + "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "strip-ansi": { + "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "requires": { + "dependencies": { "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "strip-final-newline": { + "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "supports-color": { + "node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "requires": { + "dependencies": { "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "throttleit": { + "node_modules/throttleit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", "dev": true }, - "through": { + "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, - "tmp": { + "node_modules/tmp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", "dev": true, - "requires": { + "dependencies": { "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" } }, - "tough-cookie": { + "node_modules/tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, - "requires": { + "dependencies": { "psl": "^1.1.28", "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" } }, - "tslib": { + "node_modules/tslib": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", "dev": true }, - "tunnel-agent": { + "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, - "requires": { + "dependencies": { "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" } }, - "tweetnacl": { + "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "dev": true }, - "type-fest": { + "node_modules/type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "universalify": { + "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true + "dev": true, + "engines": { + "node": ">= 10.0.0" + } }, - "untildify": { + "node_modules/untildify": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "uri-js": { + "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "requires": { + "dependencies": { "punycode": "^2.1.0" } }, - "url": { + "node_modules/url": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", "dev": true, - "requires": { + "dependencies": { "punycode": "1.3.2", "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } } }, - "uuid": { + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + }, + "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } }, - "verror": { + "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "dev": true, - "requires": { + "engines": [ + "node >=0.6.0" + ], + "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, - "which": { + "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "requires": { + "dependencies": { "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "wrap-ansi": { + "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, - "yauzl": { + "node_modules/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", "dev": true, - "requires": { + "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } From dbc4d4ec8dfa71b517a68d2f4d07a7c89ea2b41a Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Mon, 16 Mar 2026 01:40:06 +0900 Subject: [PATCH 02/20] =?UTF-8?q?feat=20:=20=EB=9E=9C=EB=8D=A4=20=EC=88=AB?= =?UTF-8?q?=EC=9E=90=20=EC=83=9D=EC=84=B1=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/index.js diff --git a/src/index.js b/src/index.js new file mode 100644 index 00000000..9b59a683 --- /dev/null +++ b/src/index.js @@ -0,0 +1,29 @@ +// 구현해야할 함수 +// 1. 게임 결과 함수 +// 2. 랜덤 숫자 생성 함수 +// 3. 입력 값 검증 함수 + +export default class BaseballGame { + // 1부터 9까지의 숫자 중에서 중복되지 않는 3개의 숫자를 랜덤으로 생성하여 배열로 반환 + randomNumbers() { + // 컴퓨터가 랜덤 숫자 배열 + const numbers = []; + while (numbers.length < 3) { + const randomNum = Random.pickNumberInRange(1, 9); + if (!numbers.includes(randomNum)) { + numbers.push(randomNum); + } + } + return numbers; + } +} + +// 예시 +// play(123, 456); // '낫싱' +// play(123, 345); // '1볼' +// play(123, 432); // '2볼' +// play(123, 312); // '3볼' +// play(123, 145); // '1스트라이크' +// play(123, 134); // '1볼 1스트라이크' +// play(123, 132); // '2볼 1스트라이크' +// play(123, 124); // '2스트라이크' From 5f561ae15d98356817b489fa74af51e515991eee Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Mon, 16 Mar 2026 01:40:24 +0900 Subject: [PATCH 03/20] =?UTF-8?q?feat=20:=20=EC=9E=85=EB=A0=A5=EA=B0=92=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/index.js b/src/index.js index 9b59a683..c05ca096 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,40 @@ // 3. 입력 값 검증 함수 export default class BaseballGame { + // 유저 입력값 검증 함수 + // 1. 숫자가 아닌경우 + // 2. 3자리 숫자가 아닌경우 + // 3. 중복된 숫자가 있는 경우 + // 4. 1부터 9까지의 숫자가 아닌 경우 + // try-catch문으로 play함수에서 호출해서 에러메시지 출력 + + validateInput(userInputNumbers) { + const userNumbers = userInputNumbers.toString().split("").map(Number); + + // 1. 숫자가 아닌 경우 + if (isNaN(userInputNumbers)) { + throw new Error("숫자만 입력해주세요."); + } + + // 2. 3자리 숫자가 아닌 경우 + if (userNumbers.length !== 3) { + throw new Error("3자리 숫자를 입력해주세요."); + } + + // 3. 중복된 숫자가 있는 경우 + const uniqueNumbers = new Set(userNumbers); + if (uniqueNumbers.size !== userNumbers.length) { + throw new Error("중복된 숫자는 입력할 수 없습니다."); + } + + // 4. 1부터 9까지의 숫자가 아닌 경우 + for (const num of userNumbers) { + if (num < 1 || num > 9) { + throw new Error("1부터 9까지의 숫자만 입력해주세요."); + } + } + } + // 1부터 9까지의 숫자 중에서 중복되지 않는 3개의 숫자를 랜덤으로 생성하여 배열로 반환 randomNumbers() { // 컴퓨터가 랜덤 숫자 배열 From d035523fd8110d7a1e8db91b7db20948a751606f Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Mon, 16 Mar 2026 01:52:20 +0900 Subject: [PATCH 04/20] =?UTF-8?q?refactor:=20=EC=88=AB=EC=9E=90=20?= =?UTF-8?q?=EB=B3=80=ED=99=98=20=EB=A1=9C=EC=A7=81=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - toNumberArray 함수를 만들어서 반복 패턴 줄임 --- src/index.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index c05ca096..6ef471d3 100644 --- a/src/index.js +++ b/src/index.js @@ -4,15 +4,27 @@ // 3. 입력 값 검증 함수 export default class BaseballGame { + // 컴퓨터 랜덤값과 유저값을 비교해서 결과 반환하는 함수 + play(computerInputNumbers, userInputNumbers) { + // 숫자로 이루어진 문자열을 배열로 변환후 split으로 나눈후 map으로 숫자로 변환 + const computerNumbers = this.toNumberArray(computerInputNumbers); + const userNumbers = this.toNumberArray(userInputNumbers); + + return "결과 값 String"; + } + + toNumberArray(numbers) { + return input.toString().split("").map(Number); + } + // 유저 입력값 검증 함수 // 1. 숫자가 아닌경우 // 2. 3자리 숫자가 아닌경우 // 3. 중복된 숫자가 있는 경우 // 4. 1부터 9까지의 숫자가 아닌 경우 // try-catch문으로 play함수에서 호출해서 에러메시지 출력 - validateInput(userInputNumbers) { - const userNumbers = userInputNumbers.toString().split("").map(Number); + const userNumbers = this.toNumberArray(userInputNumbers); // 1. 숫자가 아닌 경우 if (isNaN(userInputNumbers)) { From 38837ae318dd719da5e8233e537c779f99befa63 Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Mon, 16 Mar 2026 02:10:05 +0900 Subject: [PATCH 05/20] =?UTF-8?q?feat:=20play=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EB=B3=BC,=EC=8A=A4=ED=8A=B8=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=ED=81=AC=20=ED=8C=90=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.js | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/index.js b/src/index.js index 6ef471d3..9561894f 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,9 @@ // 구현해야할 함수 -// 1. 게임 결과 함수 -// 2. 랜덤 숫자 생성 함수 -// 3. 입력 값 검증 함수 +// 1. 게임 결과 함수 play(컴퓨터 입력값, 유저 입력값) => 결과 문자열 반환 +// 2. 랜덤 숫자 생성 함수 randomNumbers() => 랜덤 숫자 배열 반환 +// 3. 입력 값 검증 함수 validateInput (유저 입력값) => 에러 메시지 반환 +// 4. 결과 반환 함수 getResult(스트라이크, 볼) => 결과 문자열 반환 +// 5. 숫자 문자열을 배열로 변환하는 함수 toNumberArray(문자열) => 숫자 배열 반환 export default class BaseballGame { // 컴퓨터 랜덤값과 유저값을 비교해서 결과 반환하는 함수 @@ -10,7 +12,33 @@ export default class BaseballGame { const computerNumbers = this.toNumberArray(computerInputNumbers); const userNumbers = this.toNumberArray(userInputNumbers); - return "결과 값 String"; + let strikes = 0; + let balls = 0; + + for (let i = 0; i < 3; i++) { + // 같은 위치에 같은 숫자가 있는 경우 스트라이크 + if (computerNumbers[i] === userNumbers[i]) { + strikes++; + } + // 다른 위치에 같은 숫자가 있는 경우 볼 + else if (computerNumbers.includes(userNumbers[i])) { + balls++; + } + } + + return this.getResult(strikes, balls); + } + + getResult(strikes, balls) { + if (strikes === 0 && balls === 0) { + return "낫싱"; + } else if (strikes > 0 && balls > 0) { + return `${balls}볼 ${strikes}스트라이크`; + } else if (strikes > 0) { + return `${strikes}스트라이크`; + } else { + return `${balls}볼`; + } } toNumberArray(numbers) { @@ -56,6 +84,7 @@ export default class BaseballGame { const numbers = []; while (numbers.length < 3) { const randomNum = Random.pickNumberInRange(1, 9); + // 중복되지 않는 랜덤숫자만 배열에 추가 if (!numbers.includes(randomNum)) { numbers.push(randomNum); } From 61eecba9a613755c5a8446304507b10062e2aa94 Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Mon, 16 Mar 2026 13:33:19 +0900 Subject: [PATCH 06/20] =?UTF-8?q?feat:=20DOM=20=EC=9A=94=EC=86=8C=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=EB=B0=8F=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - id(#). class(.) 등 유연하게 선택자 사용할 수 있게 getElementById 말고 querySelector DOM 요소 선택 --- src/index.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 9561894f..69977f1b 100644 --- a/src/index.js +++ b/src/index.js @@ -5,6 +5,11 @@ // 4. 결과 반환 함수 getResult(스트라이크, 볼) => 결과 문자열 반환 // 5. 숫자 문자열을 배열로 변환하는 함수 toNumberArray(문자열) => 숫자 배열 반환 +// id(#). class(.) 등 유연하게 선택자 사용해서 getElementById 말고 querySelector DOM 요소 선택 +const userInput = document.querySelector("#user-input"); +const submitButton = document.querySelector("#submit"); +const resultDiv = document.querySelector("#result"); +const restartButton = document.querySelector("#game-restart-button"); export default class BaseballGame { // 컴퓨터 랜덤값과 유저값을 비교해서 결과 반환하는 함수 play(computerInputNumbers, userInputNumbers) { @@ -42,7 +47,7 @@ export default class BaseballGame { } toNumberArray(numbers) { - return input.toString().split("").map(Number); + return numbers.toString().split("").map(Number); } // 유저 입력값 검증 함수 From a0144295b29e9926061f616e9df64f3b1b339dfb Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Mon, 16 Mar 2026 14:19:29 +0900 Subject: [PATCH 07/20] =?UTF-8?q?feat:=20DOM=20=EC=97=B0=EA=B2=B0=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B2=8C=EC=9E=84=20=ED=9D=90=EB=A6=84=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.js | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 69977f1b..9986103c 100644 --- a/src/index.js +++ b/src/index.js @@ -5,11 +5,12 @@ // 4. 결과 반환 함수 getResult(스트라이크, 볼) => 결과 문자열 반환 // 5. 숫자 문자열을 배열로 변환하는 함수 toNumberArray(문자열) => 숫자 배열 반환 -// id(#). class(.) 등 유연하게 선택자 사용해서 getElementById 말고 querySelector DOM 요소 선택 +// id(#). class(.) 등 유연하게 선택자 사용할 수 있게 getElementById 말고 querySelector DOM 요소 선택 const userInput = document.querySelector("#user-input"); const submitButton = document.querySelector("#submit"); const resultDiv = document.querySelector("#result"); const restartButton = document.querySelector("#game-restart-button"); + export default class BaseballGame { // 컴퓨터 랜덤값과 유저값을 비교해서 결과 반환하는 함수 play(computerInputNumbers, userInputNumbers) { @@ -98,6 +99,31 @@ export default class BaseballGame { } } +const game = new BaseballGame(); + +// JS의 함수문법 +// element.addEventListener("이벤트종류", () => { +// // 이벤트 발생 시 실행할 코드 +// }); + +restartButton.addEventListener("click", () => { + userInput.value = ""; // 입력창 초기화 + resultDiv.textContent = ""; // 결과창 초기화 +}); + +submitButton.addEventListener("click", () => { + try { + const userInputValue = userInput.value; + game.validateInput(userInputValue); //userInput.value는 문자열(text)이므로 validateInput에서 숫자 배열로 변환해서 검증 + const computerNumbers = game.randomNumbers(); + const result = game.play(computerNumbers, userInputValue); + resultDiv.textContent = result; + } catch (error) { + //validateInput에서 발생한 에러 메시지 alert로 출력 + alert(error.message); + } +}); + // 예시 // play(123, 456); // '낫싱' // play(123, 345); // '1볼' From c288fae011094e64dd987023e7603b5d5a1c9b7f Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Mon, 16 Mar 2026 14:37:09 +0900 Subject: [PATCH 08/20] =?UTF-8?q?fix:=20MissionUtils.Random=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=9E=9C=EB=8D=A4=20=ED=95=A8=EC=88=98=20=ED=98=B8?= =?UTF-8?q?=EC=B6=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/woowacourse-projects/javascript-mission-utils#mission-utils README 에 적힌 const randomNumber = Random.pickNumberInRange(1, 9); 이게 아니라. 문서 안에 있는 사용법 참고결과 앞에 MissionUtils 붙여야함. --- src/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 9986103c..37f15d7e 100644 --- a/src/index.js +++ b/src/index.js @@ -89,12 +89,13 @@ export default class BaseballGame { // 컴퓨터가 랜덤 숫자 배열 const numbers = []; while (numbers.length < 3) { - const randomNum = Random.pickNumberInRange(1, 9); + const randomNum = MissionUtils.Random.pickNumberInRange(1, 9); // 중복되지 않는 랜덤숫자만 배열에 추가 if (!numbers.includes(randomNum)) { numbers.push(randomNum); } } + console.log("컴퓨터 랜덤 숫자:", numbers); // 디버깅용 로그 return numbers; } } From 4cc90d3605091098a2129754bd236e2644126b49 Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Mon, 16 Mar 2026 14:41:13 +0900 Subject: [PATCH 09/20] =?UTF-8?q?fix:=20=ED=8F=BC=20=EC=A0=9C=EC=B6=9C=20?= =?UTF-8?q?=EC=8B=9C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=83=88=EB=A1=9C?= =?UTF-8?q?=EA=B3=A0=EC=B9=A8=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HTML form의 기본 동작은 버튼 클릭 시 서버로 데이터를 전송하고 페이지를 새로고침함. 서버 없이 JS로만 처리하므로 e.preventDefault()로 새로고침 막음. --- src/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 37f15d7e..ddb9309e 100644 --- a/src/index.js +++ b/src/index.js @@ -112,7 +112,8 @@ restartButton.addEventListener("click", () => { resultDiv.textContent = ""; // 결과창 초기화 }); -submitButton.addEventListener("click", () => { +submitButton.addEventListener("click", (e) => { + e.preventDefault(); // 폼 제출 시 페이지 새로고침 방지 try { const userInputValue = userInput.value; game.validateInput(userInputValue); //userInput.value는 문자열(text)이므로 validateInput에서 숫자 배열로 변환해서 검증 From 35ecf6c0a8aaee0351092312f927af6b349d6283 Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Mon, 16 Mar 2026 14:54:43 +0900 Subject: [PATCH 10/20] =?UTF-8?q?fix:=20=EB=9E=9C=EB=8D=A4=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=ED=98=B8=EC=B6=9C=20=EC=8B=9C=EC=A0=90=EC=9D=84=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=20=EC=8B=9C=EC=9E=91=20=EC=8B=9C=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit submitButton 클릭 시 랜덤 함수가 호출되어, 확인할 때마다 정답이 바뀌는 문제 수정. 랜덤 함수 호출 시점을 게임 시작 시(첫 게임 및 재시작 버튼 클릭 시)로 변경. --- src/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index ddb9309e..952b1021 100644 --- a/src/index.js +++ b/src/index.js @@ -101,6 +101,7 @@ export default class BaseballGame { } const game = new BaseballGame(); +let computerNumbers = game.randomNumbers(); //게임 첫 시작 시 컴퓨터 랜덤 숫자 생성 // JS의 함수문법 // element.addEventListener("이벤트종류", () => { @@ -108,6 +109,7 @@ const game = new BaseballGame(); // }); restartButton.addEventListener("click", () => { + computerNumbers = game.randomNumbers(); userInput.value = ""; // 입력창 초기화 resultDiv.textContent = ""; // 결과창 초기화 }); @@ -117,7 +119,6 @@ submitButton.addEventListener("click", (e) => { try { const userInputValue = userInput.value; game.validateInput(userInputValue); //userInput.value는 문자열(text)이므로 validateInput에서 숫자 배열로 변환해서 검증 - const computerNumbers = game.randomNumbers(); const result = game.play(computerNumbers, userInputValue); resultDiv.textContent = result; } catch (error) { From 2cd3bd619ee3763d5ae7379888d394846a915659 Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Mon, 16 Mar 2026 15:08:43 +0900 Subject: [PATCH 11/20] =?UTF-8?q?=20fix:=20=EC=BB=B4=ED=93=A8=ED=84=B0=20?= =?UTF-8?q?=EB=9E=9C=EB=8D=A4=20=EC=88=AB=EC=9E=90=EC=97=90=20toNumberArra?= =?UTF-8?q?y=20=EC=A0=81=EC=9A=A9=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 컴퓨터 랜덤 숫자는 이미 숫자 배열로 생성되므로 toNumberArray 적용시 split으로 인해 [1, NaN, 2, NaN, 3] 같은 잘못된 값이 도출되는 문제 수정 --- src/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 952b1021..6d4017a4 100644 --- a/src/index.js +++ b/src/index.js @@ -15,11 +15,13 @@ export default class BaseballGame { // 컴퓨터 랜덤값과 유저값을 비교해서 결과 반환하는 함수 play(computerInputNumbers, userInputNumbers) { // 숫자로 이루어진 문자열을 배열로 변환후 split으로 나눈후 map으로 숫자로 변환 - const computerNumbers = this.toNumberArray(computerInputNumbers); + const computerNumbers = computerInputNumbers; const userNumbers = this.toNumberArray(userInputNumbers); let strikes = 0; let balls = 0; + console.log("컴퓨터 숫자 배열:", computerNumbers); // 디버깅용 로그 + console.log("유저 숫자 배열:", userNumbers); // 디버깅용 로그 for (let i = 0; i < 3; i++) { // 같은 위치에 같은 숫자가 있는 경우 스트라이크 From 0ae29f99e8403656ad162c0cee48893b48ed24fb Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Mon, 16 Mar 2026 15:09:33 +0900 Subject: [PATCH 12/20] =?UTF-8?q?docs=20:=20=ED=8C=8C=EC=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/{READEME.md => README.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{READEME.md => README.md} (100%) diff --git a/docs/READEME.md b/docs/README.md similarity index 100% rename from docs/READEME.md rename to docs/README.md From fae174cc4d3f47c20a65beac553d6e53401f72ce Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Mon, 16 Mar 2026 15:31:43 +0900 Subject: [PATCH 13/20] =?UTF-8?q?feat:=20=EC=9E=AC=EC=8B=9C=EC=9E=91=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=203=EC=8A=A4=ED=8A=B8=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=ED=81=AC=20=EC=8B=9C=EC=97=90=EB=A7=8C=20=EB=85=B8=EC=B6=9C?= =?UTF-8?q?=EB=90=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 52 +++++++++++++++++++++++++--------------------------- src/index.js | 10 ++++++---- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/index.html b/index.html index c85d50be..8375cb9b 100644 --- a/index.html +++ b/index.html @@ -1,30 +1,28 @@ - + + + + 숫자 야구 게임 + - - - 숫자 야구 게임 - - - -
-

⚾ 숫자 야구 게임

-

- 1~9까지의 수를 중복없이 - 3개 입력해주세요.
- 올바른 예) 139
- 틀린 예) 122 -

-
- - -
-

📄 결과

-
1볼 1스트라이크
- -
- - - - + +
+

⚾ 숫자 야구 게임

+

+ 1~9까지의 수를 중복없이 + 3개 입력해주세요.
+ 올바른 예) 139
+ 틀린 예) 122 +

+
+ + +
+

📄 결과

+
1볼 1스트라이크
+ +
+ + + diff --git a/src/index.js b/src/index.js index 6d4017a4..0e6de695 100644 --- a/src/index.js +++ b/src/index.js @@ -61,23 +61,19 @@ export default class BaseballGame { // try-catch문으로 play함수에서 호출해서 에러메시지 출력 validateInput(userInputNumbers) { const userNumbers = this.toNumberArray(userInputNumbers); - // 1. 숫자가 아닌 경우 if (isNaN(userInputNumbers)) { throw new Error("숫자만 입력해주세요."); } - // 2. 3자리 숫자가 아닌 경우 if (userNumbers.length !== 3) { throw new Error("3자리 숫자를 입력해주세요."); } - // 3. 중복된 숫자가 있는 경우 const uniqueNumbers = new Set(userNumbers); if (uniqueNumbers.size !== userNumbers.length) { throw new Error("중복된 숫자는 입력할 수 없습니다."); } - // 4. 1부터 9까지의 숫자가 아닌 경우 for (const num of userNumbers) { if (num < 1 || num > 9) { @@ -114,6 +110,7 @@ restartButton.addEventListener("click", () => { computerNumbers = game.randomNumbers(); userInput.value = ""; // 입력창 초기화 resultDiv.textContent = ""; // 결과창 초기화 + restartButton.style.display = "none"; // 재시작 버튼 초기화 (숨기기) }); submitButton.addEventListener("click", (e) => { @@ -123,6 +120,11 @@ submitButton.addEventListener("click", (e) => { game.validateInput(userInputValue); //userInput.value는 문자열(text)이므로 validateInput에서 숫자 배열로 변환해서 검증 const result = game.play(computerNumbers, userInputValue); resultDiv.textContent = result; + + // 3스트라이크시 재시작 버튼 노출 + if (result === "3스트라이크") { + restartButton.style.display = "block"; + } } catch (error) { //validateInput에서 발생한 에러 메시지 alert로 출력 alert(error.message); From 87ccce863669919fcc4cafa6ca038278e95be307 Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Mon, 16 Mar 2026 18:03:41 +0900 Subject: [PATCH 14/20] =?UTF-8?q?chore:=20=EC=BD=98=EC=86=94=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=20=EB=B0=8F=20=EC=A3=BC=EC=84=9D=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/index.js b/src/index.js index 0e6de695..81e937e9 100644 --- a/src/index.js +++ b/src/index.js @@ -14,14 +14,11 @@ const restartButton = document.querySelector("#game-restart-button"); export default class BaseballGame { // 컴퓨터 랜덤값과 유저값을 비교해서 결과 반환하는 함수 play(computerInputNumbers, userInputNumbers) { - // 숫자로 이루어진 문자열을 배열로 변환후 split으로 나눈후 map으로 숫자로 변환 const computerNumbers = computerInputNumbers; const userNumbers = this.toNumberArray(userInputNumbers); let strikes = 0; let balls = 0; - console.log("컴퓨터 숫자 배열:", computerNumbers); // 디버깅용 로그 - console.log("유저 숫자 배열:", userNumbers); // 디버깅용 로그 for (let i = 0; i < 3; i++) { // 같은 위치에 같은 숫자가 있는 경우 스트라이크 @@ -93,7 +90,6 @@ export default class BaseballGame { numbers.push(randomNum); } } - console.log("컴퓨터 랜덤 숫자:", numbers); // 디버깅용 로그 return numbers; } } @@ -130,13 +126,3 @@ submitButton.addEventListener("click", (e) => { alert(error.message); } }); - -// 예시 -// play(123, 456); // '낫싱' -// play(123, 345); // '1볼' -// play(123, 432); // '2볼' -// play(123, 312); // '3볼' -// play(123, 145); // '1스트라이크' -// play(123, 134); // '1볼 1스트라이크' -// play(123, 132); // '2볼 1스트라이크' -// play(123, 124); // '2스트라이크' From ef12034b1b589a8182f0889cc9ef206a57a7aea9 Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Mon, 16 Mar 2026 19:04:20 +0900 Subject: [PATCH 15/20] =?UTF-8?q?feat=20:=20MVC=20=ED=8C=A8=ED=84=B4=20?= =?UTF-8?q?=EC=B4=88=EA=B8=B0=EA=B5=AC=EC=A1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 31 +++++++++++++++++++++++ index.html | 1 + src/App.js | 9 +++++++ src/controllers/BaseballGameController.js | 12 +++++++++ src/models/BaseballGameModel.js | 17 +++++++++++++ src/views/BaseballGameView.js | 16 ++++++++++++ 6 files changed, 86 insertions(+) create mode 100644 src/App.js create mode 100644 src/controllers/BaseballGameController.js create mode 100644 src/models/BaseballGameModel.js create mode 100644 src/views/BaseballGameView.js diff --git a/docs/README.md b/docs/README.md index febfa03f..32af6616 100644 --- a/docs/README.md +++ b/docs/README.md @@ -36,3 +36,34 @@ ## 검증 npm run test + +# MVC 패턴 리펙토링 + +## Model + +- Model은 Controller와 View에 의존하지 않아야 한다. +- 데이터 저장 및 처리 + +현재 웹에서 다루는 데이터 + +- 컴퓨터 랜덤 값 +- 사용자 입력 값 +- 스트라이크 +- 볼 + +--- 모두에게 같게 보여지는 값인가? --- + +- 컴퓨터 랜덤 값은 사용자가 입력하는 상태에 상관 없이 같은 값을 입력받는다. + +## View + +- View는 Model에만 의존해야 하고, Controller에는 의존하면 안된다. +- View가 Model로부터 데이터를 받을 때는, 사용자마다 다르게 보여주어야 하는 데이터에 대해서만 받아야 한다. +- View가 Model로 부터 데이터를 받을 때, 반드시 Controller에서 받아야 한다. +- 입력을 Controller로 전달 + +## Controller + +- Controller는 Model과 View를 연결하는 역할 +- Controller는 Model과 View에 의존해도 된다. +- 사용자 요청 처리 및 모델과 뷰 연결 diff --git a/index.html b/index.html index 8375cb9b..7c1d6db5 100644 --- a/index.html +++ b/index.html @@ -24,5 +24,6 @@

📄 결과

+ diff --git a/src/App.js b/src/App.js new file mode 100644 index 00000000..14c07a79 --- /dev/null +++ b/src/App.js @@ -0,0 +1,9 @@ +import BaseballGameView from "./views/BaseballGameView"; +import BaseballGameModel from "./models/BaseballGameModel"; +import BaseballGameController from "./controllers/BaseballGameController"; + +// MVC 패턴을 적용하여 게임을 구성 +const model = new BaseballGameModel(); +const view = new BaseballGameView(); + +const controller = new BaseballGameController(model, view); diff --git a/src/controllers/BaseballGameController.js b/src/controllers/BaseballGameController.js new file mode 100644 index 00000000..a6b812ed --- /dev/null +++ b/src/controllers/BaseballGameController.js @@ -0,0 +1,12 @@ +// Controller는 Model과 View를 연결하는 역할 +// Controller는 Model과 View에 의존해도 된다. +// 사용자 요청 처리 및 모델과 뷰 연결 + +class BaseballGameController { + constructor(model, view) { + this.model = model; + this.view = view; + } +} + +export default BaseballGameController; diff --git a/src/models/BaseballGameModel.js b/src/models/BaseballGameModel.js new file mode 100644 index 00000000..eefbfa8d --- /dev/null +++ b/src/models/BaseballGameModel.js @@ -0,0 +1,17 @@ +// Model은 Controller와 View에 의존하지 않아야 한다. +// 데이터 저장 및 처리 +class BaseballGameModel { + constructor() { + this.computerNumbers = []; + this.userNumbers = []; + this.strokes = 0; + this.balls = 0; + } + play() {} + getResult() {} + toNumberArray() {} + validateInput() {} + randomNumbers() {} +} + +export default BaseballGameModel; diff --git a/src/views/BaseballGameView.js b/src/views/BaseballGameView.js new file mode 100644 index 00000000..7b8500f1 --- /dev/null +++ b/src/views/BaseballGameView.js @@ -0,0 +1,16 @@ +// View는 Model에만 의존해야 하고, Controller에는 의존하면 안된다. +// View가 Model로부터 데이터를 받을 때는, 사용자마다 다르게 보여주어야 하는 데이터에 대해서만 받아야 한다. +// View가 Model로 부터 데이터를 받을 때, 반드시 Controller에서 받아야 한다. +// 입력을 Controller로 전달 + +class BaseballGameView { + // 생성자 에서 DOM 요소들을 선택하여 인스턴스 변수로 저장 + constructor() { + this.userInput = document.querySelector("#user-input"); + this.submitButton = document.querySelector("#submit"); + this.resultDiv = document.querySelector("#result"); + this.restartButton = document.querySelector("#game-restart-button"); + } +} + +export default BaseballGameView; From e23e6b7678ccdcbb51945ca6ceb2bb94d14cb4d6 Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Tue, 17 Mar 2026 00:31:51 +0900 Subject: [PATCH 16/20] =?UTF-8?q?feat:=20BaseballGameModel=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 2 +- src/models/BaseballGameModel.js | 92 +++++++++++++++++++++++++++++---- 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/docs/README.md b/docs/README.md index 32af6616..ae67620c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -53,7 +53,7 @@ npm run test --- 모두에게 같게 보여지는 값인가? --- -- 컴퓨터 랜덤 값은 사용자가 입력하는 상태에 상관 없이 같은 값을 입력받는다. +- 컴퓨터 랜덤 값은 사용자가 입력하는 상태에 상관 없이 같은 값을 입력받는다. (Model) ## View diff --git a/src/models/BaseballGameModel.js b/src/models/BaseballGameModel.js index eefbfa8d..84d8fa60 100644 --- a/src/models/BaseballGameModel.js +++ b/src/models/BaseballGameModel.js @@ -2,16 +2,90 @@ // 데이터 저장 및 처리 class BaseballGameModel { constructor() { - this.computerNumbers = []; - this.userNumbers = []; - this.strokes = 0; - this.balls = 0; + // 게임 내내 유지되는 데이터 + // 컴퓨터가 랜덤으로 생성한 숫자 배열; + this.computerNumbers = this.randomNumbers(); + } + // 파라미터 하나로 변경, validateInput에서 검증 후 play함수에서 호출 + play(userInputNumbers) { + const computerNumbers = this.computerNumbers; + const userNumbers = this.toNumberArray(userInputNumbers); + + let strikes = 0; + let balls = 0; + + for (let i = 0; i < 3; i++) { + // 같은 위치에 같은 숫자가 있는 경우 스트라이크 + if (computerNumbers[i] === userNumbers[i]) { + strikes++; + } + // 다른 위치에 같은 숫자가 있는 경우 볼 + else if (computerNumbers.includes(userNumbers[i])) { + balls++; + } + } + + return this.getResult(strikes, balls); + } + + getResult(strikes, balls) { + if (strikes === 0 && balls === 0) { + return "낫싱"; + } else if (strikes > 0 && balls > 0) { + return `${balls}볼 ${strikes}스트라이크`; + } else if (strikes > 0) { + return `${strikes}스트라이크`; + } else { + return `${balls}볼`; + } + } + + toNumberArray(numbers) { + return numbers.toString().split("").map(Number); + } + + // 유저 입력값 검증 함수 + // 1. 숫자가 아닌경우 + // 2. 3자리 숫자가 아닌경우 + // 3. 중복된 숫자가 있는 경우 + // 4. 1부터 9까지의 숫자가 아닌 경우 + // try-catch문으로 play함수에서 호출해서 에러메시지 출력 + validateInput(userInputNumbers) { + const userNumbers = this.toNumberArray(userInputNumbers); + // 1. 숫자가 아닌 경우 + if (isNaN(userInputNumbers)) { + throw new Error("숫자만 입력해주세요."); + } + // 2. 3자리 숫자가 아닌 경우 + if (userNumbers.length !== 3) { + throw new Error("3자리 숫자를 입력해주세요."); + } + // 3. 중복된 숫자가 있는 경우 + const uniqueNumbers = new Set(userNumbers); + if (uniqueNumbers.size !== userNumbers.length) { + throw new Error("중복된 숫자는 입력할 수 없습니다."); + } + // 4. 1부터 9까지의 숫자가 아닌 경우 + for (const num of userNumbers) { + if (num < 1 || num > 9) { + throw new Error("1부터 9까지의 숫자만 입력해주세요."); + } + } + } + + // 1부터 9까지의 숫자 중에서 중복되지 않는 3개의 숫자를 랜덤으로 생성하여 배열로 반환 + randomNumbers() { + // 컴퓨터가 랜덤 숫자 배열 + const numbers = []; + while (numbers.length < 3) { + const randomNum = MissionUtils.Random.pickNumberInRange(1, 9); + // 중복되지 않는 랜덤숫자만 배열에 추가 + if (!numbers.includes(randomNum)) { + numbers.push(randomNum); + } + } + return numbers; } - play() {} - getResult() {} - toNumberArray() {} - validateInput() {} - randomNumbers() {} } export default BaseballGameModel; From d25f1f958ad9e3e0751a2f80b0b448fdee4e1014 Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Tue, 17 Mar 2026 00:35:35 +0900 Subject: [PATCH 17/20] =?UTF-8?q?feat:=20BaseballGameView=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 2 ++ src/views/BaseballGameView.js | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/docs/README.md b/docs/README.md index ae67620c..b0904706 100644 --- a/docs/README.md +++ b/docs/README.md @@ -62,6 +62,8 @@ npm run test - View가 Model로 부터 데이터를 받을 때, 반드시 Controller에서 받아야 한다. - 입력을 Controller로 전달 +-> 재시도 display: none -> display: block + ## Controller - Controller는 Model과 View를 연결하는 역할 diff --git a/src/views/BaseballGameView.js b/src/views/BaseballGameView.js index 7b8500f1..25eaec26 100644 --- a/src/views/BaseballGameView.js +++ b/src/views/BaseballGameView.js @@ -11,6 +11,32 @@ class BaseballGameView { this.resultDiv = document.querySelector("#result"); this.restartButton = document.querySelector("#game-restart-button"); } + + showRestartButton() { + this.restartButton.style.display = "block"; + } + + restartButtonListener(callback) { + this.restartButton.addEventListener("click", callback); + } + + submitButtonListener(callback) { + this.submitButton.addEventListener("click", callback); + } + + getUserInput() { + return this.userInput.value; + } + + displayResult(result) { + this.resultDiv.textContent = result; + } + + resetView() { + this.userInput.value = ""; // 입력창 초기화 + this.resultDiv.textContent = ""; // 결과창 초기화 + this.restartButton.style.display = "none"; // 재시작 버튼 초기화 (숨기기) + } } export default BaseballGameView; From 873aca49f2aa5b2091b9cde1ec849e332e62109a Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Tue, 17 Mar 2026 00:48:21 +0900 Subject: [PATCH 18/20] =?UTF-8?q?feat:=20BaseballGameController=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 10 ++++++++ src/controllers/BaseballGameController.js | 30 +++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/docs/README.md b/docs/README.md index b0904706..6a84d7b0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -69,3 +69,13 @@ npm run test - Controller는 Model과 View를 연결하는 역할 - Controller는 Model과 View에 의존해도 된다. - 사용자 요청 처리 및 모델과 뷰 연결 + +-> 흐름 제어 로직 + +## 구현 결과 + +- Model: computerNumbers를 자체 관리 (constructor에서 초기화, randomNumbers()로 재생성) +- Model: play()는 파라미터로 userInput만 받고, computerNumbers는 this에서 사용 +- View: showRestartButton() 추가 (3스트라이크 시 버튼 노출) +- Controller: constructor에서 이벤트 리스너 자동 등록 +- App.js: new Controller(model, view) 한 줄로 조립 diff --git a/src/controllers/BaseballGameController.js b/src/controllers/BaseballGameController.js index a6b812ed..24d8e5d7 100644 --- a/src/controllers/BaseballGameController.js +++ b/src/controllers/BaseballGameController.js @@ -6,6 +6,36 @@ class BaseballGameController { constructor(model, view) { this.model = model; this.view = view; + this.restartButtonListener(); + this.submitButtonListener(); + } + + restartButtonListener() { + this.view.restartButtonListener(() => { + this.model.randomNumbers(); // 모델의 랜덤 숫자 재생성 + this.view.resetView(); // 뷰 초기화 + }); + } + + submitButtonListener() { + this.view.submitButtonListener((e) => { + e.preventDefault(); // 폼 제출 시 페이지 새로고침 방지 + const userInputValue = this.view.getUserInput(); + try { + //userInput.value는 문자열(text)이므로 validateInput에서 숫자 배열로 변환해서 검증 + this.model.validateInput(userInputValue); + const result = this.model.play(userInputValue); + this.view.displayResult(result); + + // 3스트라이크시 재시작 버튼 노출 + if (result === "3스트라이크") { + this.view.showRestartButton(); + } + } catch (error) { + //validateInput에서 발생한 에러 메시지 alert로 출력 + alert(error.message); + } + }); } } From 63585ef05e7756731d604b2daf9ccff6960157e6 Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Tue, 17 Mar 2026 00:50:20 +0900 Subject: [PATCH 19/20] =?UTF-8?q?chore:=20index.html=20script=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20App.js=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 7c1d6db5..b3d9a5a5 100644 --- a/index.html +++ b/index.html @@ -23,7 +23,7 @@

📄 결과

- - + + From 9ef620d3e0cf1ead3b33ef04aeae9b1d19772cb0 Mon Sep 17 00:00:00 2001 From: EM-H20 Date: Tue, 17 Mar 2026 11:31:54 +0900 Subject: [PATCH 20/20] =?UTF-8?q?fix:=20=EC=9E=98=EB=AA=BB=EB=90=9C=20impo?= =?UTF-8?q?rt=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/App.js b/src/App.js index 14c07a79..426a5d52 100644 --- a/src/App.js +++ b/src/App.js @@ -1,6 +1,6 @@ -import BaseballGameView from "./views/BaseballGameView"; -import BaseballGameModel from "./models/BaseballGameModel"; -import BaseballGameController from "./controllers/BaseballGameController"; +import BaseballGameView from "./views/BaseballGameView.js"; +import BaseballGameModel from "./models/BaseballGameModel.js"; +import BaseballGameController from "./controllers/BaseballGameController.js"; // MVC 패턴을 적용하여 게임을 구성 const model = new BaseballGameModel();