From 7b3e5b968fb8760198c0b7572c193c99dd77cb34 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Thu, 15 Jan 2026 13:00:59 +0200 Subject: [PATCH 01/31] upgrade to node 24 --- .github/workflows/reusable-test-npm.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-test-npm.yml b/.github/workflows/reusable-test-npm.yml index 083c307..bfb352b 100644 --- a/.github/workflows/reusable-test-npm.yml +++ b/.github/workflows/reusable-test-npm.yml @@ -24,7 +24,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 24 - run: npm install - run: npm run ${{ inputs.npm-command }} \ No newline at end of file From ad1aba5bbf5fd32e30a33c8a4e64b168c75eb47b Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Thu, 15 Jan 2026 13:02:11 +0200 Subject: [PATCH 02/31] upgrade react-router --- client/package-lock.json | 6 +++--- client/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index da6825f..70aa36c 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -4240,9 +4240,9 @@ } }, "node_modules/react-router": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.6.tgz", - "integrity": "sha512-Y1tUp8clYRXpfPITyuifmSoE2vncSME18uVLgaqyxh9H35JWpIfzHo+9y3Fzh5odk/jxPW29IgLgzcdwxGqyNA==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.12.0.tgz", + "integrity": "sha512-kTPDYPFzDVGIIGNLS5VJykK0HfHLY5MF3b+xj0/tTyNYL1gF1qs7u67Z9jEhQk2sQ98SUaHxlG31g1JtF7IfVw==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", diff --git a/client/package.json b/client/package.json index cab03c3..5d11857 100644 --- a/client/package.json +++ b/client/package.json @@ -12,7 +12,7 @@ "dependencies": { "react": "^19.2.0", "react-dom": "^19.2.0", - "react-router": "^7.9.6", + "react-router": "^7.12.0", "axios": "^1.13.2" }, "devDependencies": { From cc8beb6f9781b715256a97fb8bfd798fe4c0b650 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Thu, 15 Jan 2026 20:04:00 +0200 Subject: [PATCH 03/31] remove eslint from prod commands will not be installed on server (dev dependency) --- client/package.json | 2 +- server/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/package.json b/client/package.json index 5d11857..24c4f91 100644 --- a/client/package.json +++ b/client/package.json @@ -7,7 +7,7 @@ "lint": "eslint .", "dev": "eslint . && vite", "build": "eslint . && vite build", - "preview": "eslint . && vite preview" + "preview": "vite preview" }, "dependencies": { "react": "^19.2.0", diff --git a/server/package.json b/server/package.json index 4e38aa9..666802b 100644 --- a/server/package.json +++ b/server/package.json @@ -4,7 +4,7 @@ "version": "0.0.1", "type": "module", "scripts": { - "start": "eslint . && node server", + "start": "node server", "dev": "eslint . && nodemon server", "lint": "eslint ." }, From fc55aeb8fbd14abf4b6b01f6c379118d641c97ea Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Thu, 15 Jan 2026 23:52:46 +0200 Subject: [PATCH 04/31] upgrade server packages --- server/package-lock.json | 94 +++++++++++++++++++++++++--------------- server/package.json | 12 ++--- 2 files changed, 65 insertions(+), 41 deletions(-) diff --git a/server/package-lock.json b/server/package-lock.json index 2871d8d..a1f65c8 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -11,16 +11,16 @@ "dependencies": { "cors": "^2.8.5", "dotenv": "^17.2.3", - "express": "^5.1.0" + "express": "^5.2.1" }, "devDependencies": { - "@eslint/js": "^9.39.1", - "brace-expansion": "^1.1.12", - "eslint": "^9.39.1", + "@eslint/js": "^9.39.2", + "brace-expansion": "^4.0.1", + "eslint": "^9.39.2", "eslint-plugin-import": "^2.32.0", - "eslint-plugin-n": "^17.23.1", + "eslint-plugin-n": "^17.23.2", "eslint-plugin-promise": "^7.2.1", - "globals": "^16.5.0", + "globals": "^17.0.0", "nodemon": "^3.1.11" } }, @@ -145,9 +145,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.39.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", - "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", "dev": true, "license": "MIT", "engines": { @@ -504,11 +504,14 @@ } }, "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==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-3.0.1.tgz", + "integrity": "sha512-vjtV3hiLqYDNRoiAv0zC4QaGAMPomEoq83PRmYIofPswwZurCeWR5LByXm7SyoL0Zh5+2z0+HC7jG8gSZJUh0w==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 16" + } }, "node_modules/binary-extensions": { "version": "2.3.0", @@ -548,14 +551,16 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-4.0.1.tgz", + "integrity": "sha512-YClrbvTCXGe70pU2JiEiPLYXO9gQkyxYeKpJIQHVS/gOs6EWMQP2RYBwjFLNT322Ji8TOC3IMPfsYCedNpzKfA==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^3.0.0" + }, + "engines": { + "node": ">= 18" } }, "node_modules/braces": { @@ -1155,9 +1160,9 @@ } }, "node_modules/eslint": { - "version": "9.39.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", - "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", "dependencies": { @@ -1167,7 +1172,7 @@ "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.1", + "@eslint/js": "9.39.2", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -1357,9 +1362,9 @@ } }, "node_modules/eslint-plugin-n": { - "version": "17.23.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.23.1.tgz", - "integrity": "sha512-68PealUpYoHOBh332JLLD9Sj7OQUDkFpmcfqt8R9sySfFSeuGJjMTJQvCRRB96zO3A/PELRLkPrzsHmzEFQQ5A==", + "version": "17.23.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.23.2.tgz", + "integrity": "sha512-RhWBeb7YVPmNa2eggvJooiuehdL76/bbfj/OJewyoGT80qn5PXdz8zMOTO6YHOsI7byPt7+Ighh/i/4a5/v7hw==", "dev": true, "license": "MIT", "dependencies": { @@ -1532,18 +1537,19 @@ } }, "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", "dependencies": { "accepts": "^2.0.0", - "body-parser": "^2.2.0", + "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", + "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", @@ -1846,9 +1852,9 @@ } }, "node_modules/globals": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", - "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.0.0.tgz", + "integrity": "sha512-gv5BeD2EssA793rlFWVPMMCqefTlpusw6/2TbAVMy0FzcG8wKJn4O+NqJ4+XWmmwrayJgw5TzrmWjFgmz1XPqw==", "dev": true, "license": "MIT", "engines": { @@ -2682,6 +2688,24 @@ "node": "*" } }, + "node_modules/minimatch/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, + "license": "MIT" + }, + "node_modules/minimatch/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -3070,9 +3094,9 @@ } }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" diff --git a/server/package.json b/server/package.json index 666802b..02d8d43 100644 --- a/server/package.json +++ b/server/package.json @@ -14,15 +14,15 @@ "dependencies": { "cors": "^2.8.5", "dotenv": "^17.2.3", - "express": "^5.1.0" + "express": "^5.2.1" }, "devDependencies": { - "globals": "^16.5.0", + "globals": "^17.0.0", "nodemon": "^3.1.11", - "eslint": "^9.39.1", - "brace-expansion": "^1.1.12", - "@eslint/js": "^9.39.1", - "eslint-plugin-n": "^17.23.1", + "eslint": "^9.39.2", + "brace-expansion": "^4.0.1", + "@eslint/js": "^9.39.2", + "eslint-plugin-n": "^17.23.2", "eslint-plugin-import": "^2.32.0", "eslint-plugin-promise": "^7.2.1" } From 12c8dac5e7b684fefa314aa97e7b667790cb981d Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Thu, 15 Jan 2026 23:56:57 +0200 Subject: [PATCH 05/31] upgrade client dependencies --- client/package-lock.json | 337 +++++++++++++++++++++------------------ client/package.json | 18 +-- 2 files changed, 189 insertions(+), 166 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 70aa36c..7b08c90 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -9,22 +9,22 @@ "version": "0.0.1", "dependencies": { "axios": "^1.13.2", - "react": "^19.2.0", - "react-dom": "^19.2.0", - "react-router": "^7.9.6" + "react": "^19.2.3", + "react-dom": "^19.2.3", + "react-router": "^7.12.0" }, "devDependencies": { - "@eslint/js": "^9.39.1", - "@vitejs/plugin-react": "^5.1.1", - "brace-expansion": "^1.1.12", - "eslint": "^9.39.1", + "@eslint/js": "^9.39.2", + "@vitejs/plugin-react": "^5.1.2", + "brace-expansion": "^4.0.1", + "eslint": "^9.39.2", "eslint-plugin-import": "^2.32.0", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.0.1", - "eslint-plugin-react-refresh": "^0.4.24", - "globals": "^16.5.0", - "vite": "^7.2.4" + "eslint-plugin-react-refresh": "^0.4.26", + "globals": "^17.0.0", + "vite": "^7.3.1" } }, "node_modules/@babel/code-frame": { @@ -310,9 +310,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", "cpu": [ "ppc64" ], @@ -327,9 +327,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", "cpu": [ "arm" ], @@ -344,9 +344,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", "cpu": [ "arm64" ], @@ -361,9 +361,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", "cpu": [ "x64" ], @@ -378,9 +378,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", "cpu": [ "arm64" ], @@ -395,9 +395,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", "cpu": [ "x64" ], @@ -412,9 +412,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", "cpu": [ "arm64" ], @@ -429,9 +429,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", "cpu": [ "x64" ], @@ -446,9 +446,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", "cpu": [ "arm" ], @@ -463,9 +463,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", "cpu": [ "arm64" ], @@ -480,9 +480,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", "cpu": [ "ia32" ], @@ -497,9 +497,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", "cpu": [ "loong64" ], @@ -514,9 +514,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", "cpu": [ "mips64el" ], @@ -531,9 +531,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", "cpu": [ "ppc64" ], @@ -548,9 +548,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", "cpu": [ "riscv64" ], @@ -565,9 +565,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", "cpu": [ "s390x" ], @@ -582,9 +582,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", "cpu": [ "x64" ], @@ -599,9 +599,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", "cpu": [ "arm64" ], @@ -616,9 +616,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", "cpu": [ "x64" ], @@ -633,9 +633,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", "cpu": [ "arm64" ], @@ -650,9 +650,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", "cpu": [ "x64" ], @@ -667,9 +667,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", "cpu": [ "arm64" ], @@ -684,9 +684,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", "cpu": [ "x64" ], @@ -701,9 +701,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", "cpu": [ "arm64" ], @@ -718,9 +718,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", "cpu": [ "ia32" ], @@ -735,9 +735,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", "cpu": [ "x64" ], @@ -872,9 +872,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.39.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", - "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", "dev": true, "license": "MIT", "engines": { @@ -1025,9 +1025,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.47", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.47.tgz", - "integrity": "sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==", + "version": "1.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", + "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==", "dev": true, "license": "MIT" }, @@ -1413,16 +1413,16 @@ "license": "MIT" }, "node_modules/@vitejs/plugin-react": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.1.tgz", - "integrity": "sha512-WQfkSw0QbQ5aJ2CHYw23ZGkqnRwqKHD/KYsMeTkZzPT4Jcf0DcBxBtwMJxnu6E7oxw5+JC6ZAiePgh28uJ1HBA==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz", + "integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-beta.47", + "@rolldown/pluginutils": "1.0.0-beta.53", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, @@ -1727,11 +1727,14 @@ } }, "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==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-3.0.1.tgz", + "integrity": "sha512-vjtV3hiLqYDNRoiAv0zC4QaGAMPomEoq83PRmYIofPswwZurCeWR5LByXm7SyoL0Zh5+2z0+HC7jG8gSZJUh0w==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 16" + } }, "node_modules/baseline-browser-mapping": { "version": "2.8.32", @@ -1744,14 +1747,16 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-4.0.1.tgz", + "integrity": "sha512-YClrbvTCXGe70pU2JiEiPLYXO9gQkyxYeKpJIQHVS/gOs6EWMQP2RYBwjFLNT322Ji8TOC3IMPfsYCedNpzKfA==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^3.0.0" + }, + "engines": { + "node": ">= 18" } }, "node_modules/browserslist": { @@ -2301,9 +2306,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2314,32 +2319,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" } }, "node_modules/escalade": { @@ -2366,9 +2371,9 @@ } }, "node_modules/eslint": { - "version": "9.39.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", - "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", "dependencies": { @@ -2378,7 +2383,7 @@ "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.1", + "@eslint/js": "9.39.2", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -2603,9 +2608,9 @@ } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.24.tgz", - "integrity": "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==", + "version": "0.4.26", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", + "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -3000,9 +3005,9 @@ } }, "node_modules/globals": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", - "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.0.0.tgz", + "integrity": "sha512-gv5BeD2EssA793rlFWVPMMCqefTlpusw6/2TbAVMy0FzcG8wKJn4O+NqJ4+XWmmwrayJgw5TzrmWjFgmz1XPqw==", "dev": true, "license": "MIT", "engines": { @@ -3823,6 +3828,24 @@ "node": "*" } }, + "node_modules/minimatch/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, + "license": "MIT" + }, + "node_modules/minimatch/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -4202,24 +4225,24 @@ } }, "node_modules/react": { - "version": "19.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", - "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", + "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", - "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.2.0" + "react": "^19.2.3" } }, "node_modules/react-is": { @@ -4984,13 +5007,13 @@ } }, "node_modules/vite": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.4.tgz", - "integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.25.0", + "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", diff --git a/client/package.json b/client/package.json index 24c4f91..71c03e9 100644 --- a/client/package.json +++ b/client/package.json @@ -10,22 +10,22 @@ "preview": "vite preview" }, "dependencies": { - "react": "^19.2.0", - "react-dom": "^19.2.0", + "react": "^19.2.3", + "react-dom": "^19.2.3", "react-router": "^7.12.0", "axios": "^1.13.2" }, "devDependencies": { - "@vitejs/plugin-react": "^5.1.1", - "eslint": "^9.39.1", - "brace-expansion": "^1.1.12", - "@eslint/js": "^9.39.1", + "@vitejs/plugin-react": "^5.1.2", + "eslint": "^9.39.2", + "brace-expansion": "^4.0.1", + "@eslint/js": "^9.39.2", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.0.1", - "eslint-plugin-react-refresh": "^0.4.24", + "eslint-plugin-react-refresh": "^0.4.26", "eslint-plugin-import": "^2.32.0", "eslint-plugin-jsx-a11y": "^6.10.2", - "globals": "^16.5.0", - "vite": "^7.2.4" + "globals": "^17.0.0", + "vite": "^7.3.1" } } From 6e34b00e1612e3b88843f71784eb44cc071aae2f Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Thu, 15 Jan 2026 23:57:39 +0200 Subject: [PATCH 06/31] normalize npm scripts --- client/package.json | 6 +++--- server/package.json | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/client/package.json b/client/package.json index 71c03e9..c883afc 100644 --- a/client/package.json +++ b/client/package.json @@ -5,9 +5,9 @@ "type": "module", "scripts": { "lint": "eslint .", - "dev": "eslint . && vite", - "build": "eslint . && vite build", - "preview": "vite preview" + "build": "vite build", + "preview": "vite preview", + "dev": "eslint . && vite" }, "dependencies": { "react": "^19.2.3", diff --git a/server/package.json b/server/package.json index 02d8d43..6600068 100644 --- a/server/package.json +++ b/server/package.json @@ -4,9 +4,10 @@ "version": "0.0.1", "type": "module", "scripts": { + "lint": "eslint .", + "build": "", "start": "node server", - "dev": "eslint . && nodemon server", - "lint": "eslint ." + "dev": "eslint . && nodemon server" }, "keywords": [], "author": "", From def5e3b148fbbd47df42a728c8dc52144c5f18f4 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Fri, 16 Jan 2026 01:23:02 +0200 Subject: [PATCH 07/31] official build --- .github/workflows/official-build.yml | 11 +++++ .github/workflows/pr-validation.yml | 17 ++----- .github/workflows/reusable-build.yml | 61 +++++++++++++++++++++++++ .github/workflows/reusable-npm.yml | 45 ++++++++++++++++++ .github/workflows/reusable-test-npm.yml | 30 ------------ .gitignore | 2 + package.ps1 | 22 +++++++++ 7 files changed, 146 insertions(+), 42 deletions(-) create mode 100644 .github/workflows/official-build.yml create mode 100644 .github/workflows/reusable-build.yml create mode 100644 .github/workflows/reusable-npm.yml delete mode 100644 .github/workflows/reusable-test-npm.yml create mode 100644 package.ps1 diff --git a/.github/workflows/official-build.yml b/.github/workflows/official-build.yml new file mode 100644 index 0000000..f8e2508 --- /dev/null +++ b/.github/workflows/official-build.yml @@ -0,0 +1,11 @@ +name: Official Build + +on: + workflow_dispatch: + push: + branches: + - main + +jobs: + build: + uses: ./.github/workflows/reusable-build.yml \ No newline at end of file diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml index 8fec1d3..3a78206 100644 --- a/.github/workflows/pr-validation.yml +++ b/.github/workflows/pr-validation.yml @@ -1,16 +1,9 @@ name: PR Validation -on: [pull_request, workflow_dispatch] +on: + workflow_dispatch: + pull_request: jobs: - client: - uses: ./.github/workflows/reusable-test-npm.yml - with: - folder: ./client - npm-command: build - - server: - uses: ./.github/workflows/reusable-test-npm.yml - with: - folder: ./server - npm-command: lint \ No newline at end of file + build: + uses: ./.github/workflows/reusable-build.yml \ No newline at end of file diff --git a/.github/workflows/reusable-build.yml b/.github/workflows/reusable-build.yml new file mode 100644 index 0000000..a46255b --- /dev/null +++ b/.github/workflows/reusable-build.yml @@ -0,0 +1,61 @@ +on: + workflow_call + +jobs: + client-lint: + uses: ./.github/workflows/reusable-npm.yml + with: + folder: ./client + npm-command: lint + + server-lint: + uses: ./.github/workflows/reusable-npm.yml + with: + folder: ./server + npm-command: lint + + client-build: + uses: ./.github/workflows/reusable-npm.yml + with: + folder: ./client + npm-command: build + artifact-name: client-build + + server-build: + uses: ./.github/workflows/reusable-npm.yml + with: + folder: ./server + npm-ci-args: "--omit=dev" + npm-command: build + artifact-name: server-build + + package: + needs: + - client-build + - server-build + runs-on: ubuntu-latest + steps: + - name: Download client build artifact + uses: actions/download-artifact@v7 + with: + name: client-build + path: ./build/client + + - name: Download server build artifact + uses: actions/download-artifact@v7 + with: + name: server-build + path: ./build/server + + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Package application + shell: pwsh + run: ./package.ps1 -BuildFolder ./build + + - name: Upload package artifact + uses: actions/upload-artifact@v6 + with: + name: application-package + path: ./package \ No newline at end of file diff --git a/.github/workflows/reusable-npm.yml b/.github/workflows/reusable-npm.yml new file mode 100644 index 0000000..ebee769 --- /dev/null +++ b/.github/workflows/reusable-npm.yml @@ -0,0 +1,45 @@ +name: Test NPM project (reusable) + +on: + workflow_call: + inputs: + folder: + required: true + type: string + npm-ci-args: + required: false + type: string + default: "" + npm-command: + required: true + type: string + artifact-name: + required: false + type: string + default: "" + +jobs: + test-npm: + defaults: + run: + working-directory: ${{ inputs.folder }} + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - name: Use Node.js + uses: actions/setup-node@v6 + with: + node-version: 24 + + - run: npm ci ${{ inputs.npm-ci-args }} + - run: npm run ${{ inputs.npm-command }} + + - name: Publish build artifacts + if: ${{ inputs.artifact-name != '' }} + uses: actions/upload-artifact@v6 + with: + name: ${{ inputs.artifact-name }} + path: ${{ inputs.folder }} \ No newline at end of file diff --git a/.github/workflows/reusable-test-npm.yml b/.github/workflows/reusable-test-npm.yml deleted file mode 100644 index bfb352b..0000000 --- a/.github/workflows/reusable-test-npm.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Test NPM project (reusable) - -on: - workflow_call: - inputs: - folder: - required: true - type: string - npm-command: - required: true - type: string - -jobs: - test-npm: - defaults: - run: - working-directory: ${{ inputs.folder }} - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Use Node.js - uses: actions/setup-node@v4 - with: - node-version: 24 - - - run: npm install - - run: npm run ${{ inputs.npm-command }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index c6bba59..cc5fabc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +package/ + # Logs logs *.log diff --git a/package.ps1 b/package.ps1 new file mode 100644 index 0000000..fb60738 --- /dev/null +++ b/package.ps1 @@ -0,0 +1,22 @@ +param( + $BuildFolder = $PSScriptRoot, + [switch]$SkipPackageCleanupConfirmation +) + +$ErrorActionPreference = 'Stop' + +$pkgDir = "$PSScriptRoot/package" +Write-Host "Preparing package folder at '$pkgDir'..." + +if (Test-Path -LiteralPath $pkgDir) { + Write-Host "Cleaning up existing package folder..." + Remove-Item -LiteralPath $pkgDir -Recurse -Force -Confirm:(!$SkipPackageCleanupConfirmation) +} + +New-Item -ItemType Directory -Path $pkgDir -Verbose + +Write-Host "Copying built files to package folder..." +Copy-Item -Recurse -Force "$BuildFolder/client/dist" $pkgDir +Copy-Item -Recurse -Force "$BuildFolder/server" $pkgDir + +Write-Host "Production bundle ready at '$pkgDir'" \ No newline at end of file From 659dc1e175c32e88c7467d7972aedc8c01175768 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Fri, 16 Jan 2026 17:45:12 +0200 Subject: [PATCH 08/31] print build files in package script --- package.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.ps1 b/package.ps1 index fb60738..c0e9cb4 100644 --- a/package.ps1 +++ b/package.ps1 @@ -5,6 +5,10 @@ param( $ErrorActionPreference = 'Stop' +Write-Host "Packaging build files from '$BuildFolder'..." +$buildFiles = Get-ChildItem -LiteralPath $BuildFolder -Recurse -Force +Write-Host "Build files: $($buildFiles.FullName -join ', ')" + $pkgDir = "$PSScriptRoot/package" Write-Host "Preparing package folder at '$pkgDir'..." From fa8c8d81bc039d2b45e81f564198ae33f1b93c52 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Fri, 16 Jan 2026 17:46:16 +0200 Subject: [PATCH 09/31] reusable npm template - meaningful names --- .github/workflows/reusable-npm.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/reusable-npm.yml b/.github/workflows/reusable-npm.yml index ebee769..788961a 100644 --- a/.github/workflows/reusable-npm.yml +++ b/.github/workflows/reusable-npm.yml @@ -1,4 +1,4 @@ -name: Test NPM project (reusable) +name: Run NPM command (reusable) on: workflow_call: @@ -19,7 +19,8 @@ on: default: "" jobs: - test-npm: + run-npm: +name: "Run NPM command '${{ inputs.npm-command }}' in: ${{ inputs.folder }}" defaults: run: working-directory: ${{ inputs.folder }} From ff6cd9f5f40964b316a816dae0f11c43aeb6b086 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Fri, 16 Jan 2026 17:49:21 +0200 Subject: [PATCH 10/31] package client dist only --- .github/workflows/reusable-build.yml | 1 + .github/workflows/reusable-npm.yml | 8 ++++++-- package.ps1 | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/reusable-build.yml b/.github/workflows/reusable-build.yml index a46255b..88a0534 100644 --- a/.github/workflows/reusable-build.yml +++ b/.github/workflows/reusable-build.yml @@ -20,6 +20,7 @@ jobs: folder: ./client npm-command: build artifact-name: client-build + artifact-path: ./client/dist server-build: uses: ./.github/workflows/reusable-npm.yml diff --git a/.github/workflows/reusable-npm.yml b/.github/workflows/reusable-npm.yml index 788961a..06da95b 100644 --- a/.github/workflows/reusable-npm.yml +++ b/.github/workflows/reusable-npm.yml @@ -17,10 +17,14 @@ on: required: false type: string default: "" + artifact-path: + required: false + type: string + default: "" jobs: run-npm: -name: "Run NPM command '${{ inputs.npm-command }}' in: ${{ inputs.folder }}" + name: "Run NPM command '${{ inputs.npm-command }}' in: ${{ inputs.folder }}" defaults: run: working-directory: ${{ inputs.folder }} @@ -43,4 +47,4 @@ name: "Run NPM command '${{ inputs.npm-command }}' in: ${{ inputs.folder }}" uses: actions/upload-artifact@v6 with: name: ${{ inputs.artifact-name }} - path: ${{ inputs.folder }} \ No newline at end of file + path: ${{ inputs.artifact-path != '' && inputs.artifact-path || inputs.folder }} \ No newline at end of file diff --git a/package.ps1 b/package.ps1 index c0e9cb4..efc060d 100644 --- a/package.ps1 +++ b/package.ps1 @@ -20,7 +20,7 @@ if (Test-Path -LiteralPath $pkgDir) { New-Item -ItemType Directory -Path $pkgDir -Verbose Write-Host "Copying built files to package folder..." -Copy-Item -Recurse -Force "$BuildFolder/client/dist" $pkgDir +Copy-Item -Recurse -Force "$BuildFolder/client" $pkgDir Copy-Item -Recurse -Force "$BuildFolder/server" $pkgDir Write-Host "Production bundle ready at '$pkgDir'" \ No newline at end of file From efa8150dffbb833cf70d7eea44bb44142a6ea49c Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Fri, 16 Jan 2026 18:26:31 +0200 Subject: [PATCH 11/31] normalize package folder structure --- .github/workflows/reusable-build.yml | 18 +++++++++--------- package.ps1 | 20 ++++++++++++-------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/.github/workflows/reusable-build.yml b/.github/workflows/reusable-build.yml index 88a0534..663bb64 100644 --- a/.github/workflows/reusable-build.yml +++ b/.github/workflows/reusable-build.yml @@ -36,24 +36,24 @@ jobs: - server-build runs-on: ubuntu-latest steps: - - name: Download client build artifact - uses: actions/download-artifact@v7 - with: - name: client-build - path: ./build/client + - name: Checkout repository + uses: actions/checkout@v6 - name: Download server build artifact uses: actions/download-artifact@v7 with: name: server-build path: ./build/server - - - name: Checkout repository - uses: actions/checkout@v6 + + - name: Download client build artifact + uses: actions/download-artifact@v7 + with: + name: client-build + path: ./build/client - name: Package application shell: pwsh - run: ./package.ps1 -BuildFolder ./build + run: ./package.ps1 -ServerBuild ./build/server -ClientBuild ./build/client - name: Upload package artifact uses: actions/upload-artifact@v6 diff --git a/package.ps1 b/package.ps1 index efc060d..be75ceb 100644 --- a/package.ps1 +++ b/package.ps1 @@ -1,26 +1,30 @@ param( - $BuildFolder = $PSScriptRoot, + $ServerBuild = "$PSScriptRoot/server", + $ClientBuild = "$PSScriptRoot/client/dist", [switch]$SkipPackageCleanupConfirmation ) $ErrorActionPreference = 'Stop' -Write-Host "Packaging build files from '$BuildFolder'..." -$buildFiles = Get-ChildItem -LiteralPath $BuildFolder -Recurse -Force -Write-Host "Build files: $($buildFiles.FullName -join ', ')" +$buildFilesServer = Get-ChildItem -LiteralPath $ServerBuild -Recurse -Force +Write-Host "Server build files: $($buildFilesServer.FullName -join ', ')" + +$buildFilesClient = Get-ChildItem -LiteralPath $ClientBuild -Recurse -Force +Write-Host "Client build files: $($buildFilesClient.FullName -join ', ')" $pkgDir = "$PSScriptRoot/package" +$publicDir = "$pkgDir/public" Write-Host "Preparing package folder at '$pkgDir'..." if (Test-Path -LiteralPath $pkgDir) { Write-Host "Cleaning up existing package folder..." - Remove-Item -LiteralPath $pkgDir -Recurse -Force -Confirm:(!$SkipPackageCleanupConfirmation) + Remove-Item -LiteralPath $pkgDir -Recurse -Force -Confirm:(!$SkipPackageCleanupConfirmation) -Verbose } -New-Item -ItemType Directory -Path $pkgDir -Verbose +New-Item -ItemType Directory -Path $publicDir -Force -Verbose Write-Host "Copying built files to package folder..." -Copy-Item -Recurse -Force "$BuildFolder/client" $pkgDir -Copy-Item -Recurse -Force "$BuildFolder/server" $pkgDir +Copy-Item -Path "$ServerBuild/*" -Destination $pkgDir -Recurse -Force -Verbose +Copy-Item -Path "$ClientBuild/*" -Destination $publicDir -Recurse -Force -Verbose Write-Host "Production bundle ready at '$pkgDir'" \ No newline at end of file From f366b0505d2941c6d0fde20e2eb20b51e45b137c Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Fri, 16 Jan 2026 18:28:32 +0200 Subject: [PATCH 12/31] minor job name change --- .github/workflows/reusable-npm.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-npm.yml b/.github/workflows/reusable-npm.yml index 06da95b..9b69b0e 100644 --- a/.github/workflows/reusable-npm.yml +++ b/.github/workflows/reusable-npm.yml @@ -24,7 +24,7 @@ on: jobs: run-npm: - name: "Run NPM command '${{ inputs.npm-command }}' in: ${{ inputs.folder }}" + name: "npm '${{ inputs.npm-command }}' (${{ inputs.folder }})" defaults: run: working-directory: ${{ inputs.folder }} From 8b0525004bf0a1b7a9c9585118dfd3d3aae74c35 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 00:33:22 +0200 Subject: [PATCH 13/31] add npm test placeholders --- .github/workflows/reusable-build.yml | 12 ++++++++++++ client/package.json | 1 + server/package.json | 3 ++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/reusable-build.yml b/.github/workflows/reusable-build.yml index 663bb64..661f09b 100644 --- a/.github/workflows/reusable-build.yml +++ b/.github/workflows/reusable-build.yml @@ -14,6 +14,18 @@ jobs: folder: ./server npm-command: lint +client-test: + uses: ./.github/workflows/reusable-npm.yml + with: + folder: ./client + npm-command: test + + server-test: + uses: ./.github/workflows/reusable-npm.yml + with: + folder: ./server + npm-command: test + client-build: uses: ./.github/workflows/reusable-npm.yml with: diff --git a/client/package.json b/client/package.json index c883afc..73eacd5 100644 --- a/client/package.json +++ b/client/package.json @@ -6,6 +6,7 @@ "scripts": { "lint": "eslint .", "build": "vite build", +"test": "echo 'insert test script here'", "preview": "vite preview", "dev": "eslint . && vite" }, diff --git a/server/package.json b/server/package.json index 6600068..b366aa3 100644 --- a/server/package.json +++ b/server/package.json @@ -5,7 +5,8 @@ "type": "module", "scripts": { "lint": "eslint .", - "build": "", + "build": "echo 'insert build script here'", + "test": "echo 'insert test script here'", "start": "node server", "dev": "eslint . && nodemon server" }, From fade2991dfc575266d56d2d6b1fb543daf9bb939 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 00:33:40 +0200 Subject: [PATCH 14/31] improved node setup --- .github/workflows/reusable-npm.yml | 3 +++ client/package.json | 3 ++- server/package.json | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/reusable-npm.yml b/.github/workflows/reusable-npm.yml index 9b69b0e..46e21e5 100644 --- a/.github/workflows/reusable-npm.yml +++ b/.github/workflows/reusable-npm.yml @@ -38,6 +38,9 @@ jobs: uses: actions/setup-node@v6 with: node-version: 24 + check-latest: true + architecture: 'x86' + cache-dependency-path: ${{ inputs.folder }}/package-lock.json - run: npm ci ${{ inputs.npm-ci-args }} - run: npm run ${{ inputs.npm-command }} diff --git a/client/package.json b/client/package.json index 73eacd5..c47e0d3 100644 --- a/client/package.json +++ b/client/package.json @@ -3,10 +3,11 @@ "private": true, "version": "0.0.1", "type": "module", + "packageManager": "npm@11.7.0", "scripts": { "lint": "eslint .", "build": "vite build", -"test": "echo 'insert test script here'", + "test": "echo 'insert test script here'", "preview": "vite preview", "dev": "eslint . && vite" }, diff --git a/server/package.json b/server/package.json index b366aa3..1d760e9 100644 --- a/server/package.json +++ b/server/package.json @@ -3,6 +3,7 @@ "private": true, "version": "0.0.1", "type": "module", + "packageManager": "npm@11.7.0", "scripts": { "lint": "eslint .", "build": "echo 'insert build script here'", From 5a19c3e14bcb3e06841610da674320a9dbc5b44c Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 00:34:15 +0200 Subject: [PATCH 15/31] minor - shorten app package artifact name --- .github/workflows/reusable-build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/reusable-build.yml b/.github/workflows/reusable-build.yml index 661f09b..3cdc13f 100644 --- a/.github/workflows/reusable-build.yml +++ b/.github/workflows/reusable-build.yml @@ -13,8 +13,8 @@ jobs: with: folder: ./server npm-command: lint - -client-test: + + client-test: uses: ./.github/workflows/reusable-npm.yml with: folder: ./client @@ -70,5 +70,5 @@ client-test: - name: Upload package artifact uses: actions/upload-artifact@v6 with: - name: application-package + name: app-package path: ./package \ No newline at end of file From 6119b3447aa36120f59316e9c50297b8dc4f8f98 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 00:37:53 +0200 Subject: [PATCH 16/31] minor comment --- .github/workflows/reusable-npm.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/reusable-npm.yml b/.github/workflows/reusable-npm.yml index 46e21e5..7a49e34 100644 --- a/.github/workflows/reusable-npm.yml +++ b/.github/workflows/reusable-npm.yml @@ -39,7 +39,8 @@ jobs: with: node-version: 24 check-latest: true - architecture: 'x86' + architecture: 'x86' # Azure App Service Free is limited to x86: + # https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits#azure-app-service-limits cache-dependency-path: ${{ inputs.folder }}/package-lock.json - run: npm ci ${{ inputs.npm-ci-args }} From 74f731b07d625a30693f390f729e742a2f797025 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 01:05:12 +0200 Subject: [PATCH 17/31] Normalize workflow names --- .github/workflows/official-build.yml | 2 +- .github/workflows/pr-validation.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/official-build.yml b/.github/workflows/official-build.yml index f8e2508..a064713 100644 --- a/.github/workflows/official-build.yml +++ b/.github/workflows/official-build.yml @@ -1,4 +1,4 @@ -name: Official Build +name: Official-Build on: workflow_dispatch: diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml index 3a78206..c57d096 100644 --- a/.github/workflows/pr-validation.yml +++ b/.github/workflows/pr-validation.yml @@ -1,4 +1,4 @@ -name: PR Validation +name: PR-Validation on: workflow_dispatch: From 69d5f77f119c8676891a6cd6e3e0544c68df0f27 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 02:14:42 +0200 Subject: [PATCH 18/31] fail if no files were found for artifact --- .github/workflows/reusable-npm.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/reusable-npm.yml b/.github/workflows/reusable-npm.yml index 7a49e34..b5a71d0 100644 --- a/.github/workflows/reusable-npm.yml +++ b/.github/workflows/reusable-npm.yml @@ -51,4 +51,5 @@ jobs: uses: actions/upload-artifact@v6 with: name: ${{ inputs.artifact-name }} - path: ${{ inputs.artifact-path != '' && inputs.artifact-path || inputs.folder }} \ No newline at end of file + path: ${{ inputs.artifact-path != '' && inputs.artifact-path || inputs.folder }} + if-no-files-found: error \ No newline at end of file From bb570b8d4ce7a00064c202462c364caa39fca16d Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 02:48:07 +0200 Subject: [PATCH 19/31] combined deployment --- client/src/components/RandomDuck/RandomDuck.jsx | 2 +- client/src/services/api.js | 2 +- server/server.js | 8 ++------ 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/client/src/components/RandomDuck/RandomDuck.jsx b/client/src/components/RandomDuck/RandomDuck.jsx index 2d93857..b3be72d 100644 --- a/client/src/components/RandomDuck/RandomDuck.jsx +++ b/client/src/components/RandomDuck/RandomDuck.jsx @@ -3,7 +3,7 @@ import styles from './RandomDuck.module.css'; import { DuckContext } from '../../context/DuckContext'; import FirstButton from '../common/FirstButton/FirstButton.jsx'; -const apiUrl = import.meta.env.VITE_SERVER_API_URL; +const apiUrl = import.meta.env.VITE_SERVER_API_URL ?? ''; const RandomDuck = () => { const { duck, getRandomDuck } = useContext(DuckContext); diff --git a/client/src/services/api.js b/client/src/services/api.js index a7a70ff..080031f 100644 --- a/client/src/services/api.js +++ b/client/src/services/api.js @@ -1,5 +1,5 @@ import axios from 'axios'; -const apiUrl = import.meta.env.VITE_SERVER_API_URL; +const apiUrl = import.meta.env.VITE_SERVER_API_URL ?? ''; // Create an instance of Axios with default configurations const axiosInstance = axios.create({ diff --git a/server/server.js b/server/server.js index f8cc227..3d56403 100644 --- a/server/server.js +++ b/server/server.js @@ -1,25 +1,21 @@ import express from 'express'; -import path from 'path'; -import { fileURLToPath } from 'url'; import cors from 'cors'; import dotenv from 'dotenv'; import rubberDuckRoutes from './routes/rubberDucks.js'; // Import the routes -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); dotenv.config(); const app = express(); app.use(express.json()); -app.use('/images', express.static(path.join(__dirname, 'images'))); // Serve static images app.use(cors({ origin: process.env.CLIENT_URL })); -// Use the routes file for all `/ducks` routes +app.use(express.static('public')); // Serve client in combined deployment +app.use('/images', express.static('images')); app.use('/ducks', rubberDuckRoutes); // Start server From 4d6e9bccfe69175766fab99eb15f5e6dbb585bb4 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 02:49:28 +0200 Subject: [PATCH 20/31] deploy webapp --- .github/workflows/deploy-prod.yml | 15 +++++++ .github/workflows/deploy-stage.yml | 15 +++++++ .github/workflows/deploy-test.yml | 15 +++++++ .github/workflows/reusable-deploy-webapp.yml | 45 ++++++++++++++++++++ client/package.json | 4 ++ server/package.json | 4 ++ 6 files changed, 98 insertions(+) create mode 100644 .github/workflows/deploy-prod.yml create mode 100644 .github/workflows/deploy-stage.yml create mode 100644 .github/workflows/deploy-test.yml create mode 100644 .github/workflows/reusable-deploy-webapp.yml diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml new file mode 100644 index 0000000..2302d88 --- /dev/null +++ b/.github/workflows/deploy-prod.yml @@ -0,0 +1,15 @@ +name: Deploy-Prod + +on: + workflow_dispatch: + inputs: + build-run-id: + required: true + type: string + +jobs: + deploy-webapp: + uses: ./.github/workflows/reusable-deploy-webapp.yml + with: + build-run-id: ${{ inputs.build-run-id }} + environment: prod \ No newline at end of file diff --git a/.github/workflows/deploy-stage.yml b/.github/workflows/deploy-stage.yml new file mode 100644 index 0000000..e474655 --- /dev/null +++ b/.github/workflows/deploy-stage.yml @@ -0,0 +1,15 @@ +name: Deploy-Stage + +on: + workflow_run: + workflows: ["Official-Build"] + types: + - completed + +jobs: + deploy-webapp: + if: ${{ github.event.workflow_run.conclusion == 'success' }} + uses: ./.github/workflows/reusable-deploy-webapp.yml + with: + build-run-id: ${{ github.event.workflow_run.id }} + environment: stage \ No newline at end of file diff --git a/.github/workflows/deploy-test.yml b/.github/workflows/deploy-test.yml new file mode 100644 index 0000000..e09c84b --- /dev/null +++ b/.github/workflows/deploy-test.yml @@ -0,0 +1,15 @@ +name: Deploy-Test + +on: + workflow_dispatch: + inputs: + build-run-id: + required: true + type: string + +jobs: + deploy-webapp: + uses: ./.github/workflows/reusable-deploy-webapp.yml + with: + build-run-id: ${{ inputs.build-run-id }} + environment: test \ No newline at end of file diff --git a/.github/workflows/reusable-deploy-webapp.yml b/.github/workflows/reusable-deploy-webapp.yml new file mode 100644 index 0000000..33bf581 --- /dev/null +++ b/.github/workflows/reusable-deploy-webapp.yml @@ -0,0 +1,45 @@ +name: Deploy Web App + +on: + workflow_call: + inputs: + build-run-id: + required: true + type: string + environment: + required: true + type: string + +permissions: + id-token: write + contents: read + +jobs: + deploy: + environment: ${{ inputs.environment }} + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Download app package artifact + uses: actions/download-artifact@v7 + with: + name: app-package + run-id: ${{ inputs.build-run-id }} + github-token: ${{ secrets.GH_PAT_ACTIONS_READONLY }} + path: package + + - name: 'Azure Login (OIDC)' + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: 'Deploy web app' + uses: azure/webapps-deploy@v3 + with: + app-name: ${{ vars.AZURE_WEBAPP_NAME }} + package: package \ No newline at end of file diff --git a/client/package.json b/client/package.json index c47e0d3..6132a77 100644 --- a/client/package.json +++ b/client/package.json @@ -4,6 +4,10 @@ "version": "0.0.1", "type": "module", "packageManager": "npm@11.7.0", + "engines": { + "node": ">=24.0.0", + "npm": ">=11.0.0" + }, "scripts": { "lint": "eslint .", "build": "vite build", diff --git a/server/package.json b/server/package.json index 1d760e9..40093f2 100644 --- a/server/package.json +++ b/server/package.json @@ -4,6 +4,10 @@ "version": "0.0.1", "type": "module", "packageManager": "npm@11.7.0", + "engines": { + "node": ">=24.0.0", + "npm": ">=11.0.0" + }, "scripts": { "lint": "eslint .", "build": "echo 'insert build script here'", From 8dbdbc5dba6898e63d6c6a5250fd77e9f83db4fc Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 02:54:49 +0200 Subject: [PATCH 21/31] remove x86 node (doesn't exist) --- .github/workflows/reusable-npm.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/reusable-npm.yml b/.github/workflows/reusable-npm.yml index b5a71d0..f67f087 100644 --- a/.github/workflows/reusable-npm.yml +++ b/.github/workflows/reusable-npm.yml @@ -39,8 +39,6 @@ jobs: with: node-version: 24 check-latest: true - architecture: 'x86' # Azure App Service Free is limited to x86: - # https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits#azure-app-service-limits cache-dependency-path: ${{ inputs.folder }}/package-lock.json - run: npm ci ${{ inputs.npm-ci-args }} From b0249f5746fb9c1cdd3f0dc1f7832d768734aa27 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 03:05:06 +0200 Subject: [PATCH 22/31] download-artifact - specify repo param --- .github/workflows/reusable-deploy-webapp.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/reusable-deploy-webapp.yml b/.github/workflows/reusable-deploy-webapp.yml index 33bf581..79c4f2d 100644 --- a/.github/workflows/reusable-deploy-webapp.yml +++ b/.github/workflows/reusable-deploy-webapp.yml @@ -29,6 +29,7 @@ jobs: name: app-package run-id: ${{ inputs.build-run-id }} github-token: ${{ secrets.GH_PAT_ACTIONS_READONLY }} + repository: ${{ github.repository }} path: package - name: 'Azure Login (OIDC)' From 2a013cfcdf12c5e7e7e4f3011c4dd48748260199 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 03:09:36 +0200 Subject: [PATCH 23/31] downgrade download-artifact to 6 match with upload-artifact er FAQ --- .github/workflows/reusable-deploy-webapp.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-deploy-webapp.yml b/.github/workflows/reusable-deploy-webapp.yml index 79c4f2d..faf205d 100644 --- a/.github/workflows/reusable-deploy-webapp.yml +++ b/.github/workflows/reusable-deploy-webapp.yml @@ -24,7 +24,7 @@ jobs: uses: actions/checkout@v6 - name: Download app package artifact - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v6 with: name: app-package run-id: ${{ inputs.build-run-id }} From c079cb2f5af9f7652c8e1f4b91793b069a1ef835 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 03:18:05 +0200 Subject: [PATCH 24/31] allow manual execution of stage deploy --- .github/workflows/deploy-stage.yml | 7 ++++++- .github/workflows/reusable-deploy-webapp.yml | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-stage.yml b/.github/workflows/deploy-stage.yml index e474655..f793290 100644 --- a/.github/workflows/deploy-stage.yml +++ b/.github/workflows/deploy-stage.yml @@ -5,11 +5,16 @@ on: workflows: ["Official-Build"] types: - completed + workflow_dispatch: + inputs: + build-run-id: + required: true + type: string jobs: deploy-webapp: if: ${{ github.event.workflow_run.conclusion == 'success' }} uses: ./.github/workflows/reusable-deploy-webapp.yml with: - build-run-id: ${{ github.event.workflow_run.id }} + build-run-id: ${{ github.event.workflow_run.id || inputs.build-run-id }} environment: stage \ No newline at end of file diff --git a/.github/workflows/reusable-deploy-webapp.yml b/.github/workflows/reusable-deploy-webapp.yml index faf205d..79c4f2d 100644 --- a/.github/workflows/reusable-deploy-webapp.yml +++ b/.github/workflows/reusable-deploy-webapp.yml @@ -24,7 +24,7 @@ jobs: uses: actions/checkout@v6 - name: Download app package artifact - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v7 with: name: app-package run-id: ${{ inputs.build-run-id }} From 4d828b6b80f59055562c164b01034f359b7067b1 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 03:35:25 +0200 Subject: [PATCH 25/31] pass GH PAT secret to reusable deploy workflow --- .github/workflows/deploy-prod.yml | 4 +++- .github/workflows/deploy-stage.yml | 4 +++- .github/workflows/deploy-test.yml | 4 +++- .github/workflows/reusable-deploy-webapp.yml | 3 +++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index 2302d88..6244fc4 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -12,4 +12,6 @@ jobs: uses: ./.github/workflows/reusable-deploy-webapp.yml with: build-run-id: ${{ inputs.build-run-id }} - environment: prod \ No newline at end of file + environment: prod + secrets: + GH_PAT_ACTIONS_READONLY: ${{ secrets.GH_PAT_ACTIONS_READONLY }} \ No newline at end of file diff --git a/.github/workflows/deploy-stage.yml b/.github/workflows/deploy-stage.yml index f793290..fac8306 100644 --- a/.github/workflows/deploy-stage.yml +++ b/.github/workflows/deploy-stage.yml @@ -17,4 +17,6 @@ jobs: uses: ./.github/workflows/reusable-deploy-webapp.yml with: build-run-id: ${{ github.event.workflow_run.id || inputs.build-run-id }} - environment: stage \ No newline at end of file + environment: stage + secrets: + GH_PAT_ACTIONS_READONLY: ${{ secrets.GH_PAT_ACTIONS_READONLY }} \ No newline at end of file diff --git a/.github/workflows/deploy-test.yml b/.github/workflows/deploy-test.yml index e09c84b..859f6d2 100644 --- a/.github/workflows/deploy-test.yml +++ b/.github/workflows/deploy-test.yml @@ -12,4 +12,6 @@ jobs: uses: ./.github/workflows/reusable-deploy-webapp.yml with: build-run-id: ${{ inputs.build-run-id }} - environment: test \ No newline at end of file + environment: test + secrets: + GH_PAT_ACTIONS_READONLY: ${{ secrets.GH_PAT_ACTIONS_READONLY }} \ No newline at end of file diff --git a/.github/workflows/reusable-deploy-webapp.yml b/.github/workflows/reusable-deploy-webapp.yml index 79c4f2d..3dcfa44 100644 --- a/.github/workflows/reusable-deploy-webapp.yml +++ b/.github/workflows/reusable-deploy-webapp.yml @@ -9,6 +9,9 @@ on: environment: required: true type: string + secrets: + GH_PAT_ACTIONS_READONLY: + required: true permissions: id-token: write From a7affbc9714c9c3b3537b4adb9cc0366e5409159 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 03:38:43 +0200 Subject: [PATCH 26/31] reusable deploy workflow - inherit secrets Azure IDs are also required --- .github/workflows/deploy-prod.yml | 3 +-- .github/workflows/deploy-stage.yml | 3 +-- .github/workflows/deploy-test.yml | 3 +-- .github/workflows/reusable-deploy-webapp.yml | 3 --- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index 6244fc4..8343c75 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -13,5 +13,4 @@ jobs: with: build-run-id: ${{ inputs.build-run-id }} environment: prod - secrets: - GH_PAT_ACTIONS_READONLY: ${{ secrets.GH_PAT_ACTIONS_READONLY }} \ No newline at end of file + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/deploy-stage.yml b/.github/workflows/deploy-stage.yml index fac8306..e367fda 100644 --- a/.github/workflows/deploy-stage.yml +++ b/.github/workflows/deploy-stage.yml @@ -18,5 +18,4 @@ jobs: with: build-run-id: ${{ github.event.workflow_run.id || inputs.build-run-id }} environment: stage - secrets: - GH_PAT_ACTIONS_READONLY: ${{ secrets.GH_PAT_ACTIONS_READONLY }} \ No newline at end of file + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/deploy-test.yml b/.github/workflows/deploy-test.yml index 859f6d2..f6df572 100644 --- a/.github/workflows/deploy-test.yml +++ b/.github/workflows/deploy-test.yml @@ -13,5 +13,4 @@ jobs: with: build-run-id: ${{ inputs.build-run-id }} environment: test - secrets: - GH_PAT_ACTIONS_READONLY: ${{ secrets.GH_PAT_ACTIONS_READONLY }} \ No newline at end of file + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/reusable-deploy-webapp.yml b/.github/workflows/reusable-deploy-webapp.yml index 3dcfa44..79c4f2d 100644 --- a/.github/workflows/reusable-deploy-webapp.yml +++ b/.github/workflows/reusable-deploy-webapp.yml @@ -9,9 +9,6 @@ on: environment: required: true type: string - secrets: - GH_PAT_ACTIONS_READONLY: - required: true permissions: id-token: write From ae2fbc2d1f7c6a67f65710be30c5fc695c5e79b5 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 03:58:20 +0200 Subject: [PATCH 27/31] update package locks with engines --- client/package-lock.json | 4 ++++ server/package-lock.json | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/client/package-lock.json b/client/package-lock.json index 7b08c90..1c33da2 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -25,6 +25,10 @@ "eslint-plugin-react-refresh": "^0.4.26", "globals": "^17.0.0", "vite": "^7.3.1" + }, + "engines": { + "node": ">=24.0.0", + "npm": ">=11.0.0" } }, "node_modules/@babel/code-frame": { diff --git a/server/package-lock.json b/server/package-lock.json index a1f65c8..87a2882 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -22,6 +22,10 @@ "eslint-plugin-promise": "^7.2.1", "globals": "^17.0.0", "nodemon": "^3.1.11" + }, + "engines": { + "node": ">=24.0.0", + "npm": ">=11.0.0" } }, "node_modules/@eslint-community/eslint-utils": { From 41526d33c92554308071346f9089b3ba95259fe7 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 05:11:38 +0200 Subject: [PATCH 28/31] Readme (#1) * PR validation on main * readme - CI-CD stub * protect branch * README - explain CI/CD --- .github/workflows/pr-validation.yml | 2 ++ .github/workflows/reusable-deploy-webapp.yml | 1 - README.md | 21 ++++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml index c57d096..2890246 100644 --- a/.github/workflows/pr-validation.yml +++ b/.github/workflows/pr-validation.yml @@ -3,6 +3,8 @@ name: PR-Validation on: workflow_dispatch: pull_request: + branches: + - main jobs: build: diff --git a/.github/workflows/reusable-deploy-webapp.yml b/.github/workflows/reusable-deploy-webapp.yml index 79c4f2d..33bf581 100644 --- a/.github/workflows/reusable-deploy-webapp.yml +++ b/.github/workflows/reusable-deploy-webapp.yml @@ -29,7 +29,6 @@ jobs: name: app-package run-id: ${{ inputs.build-run-id }} github-token: ${{ secrets.GH_PAT_ACTIONS_READONLY }} - repository: ${{ github.repository }} path: package - name: 'Azure Login (OIDC)' diff --git a/README.md b/README.md index 7388ddb..d226b80 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,27 @@ Contains the Node.js / Express backend application. - `images/`: Contains the duck images referenced by the duck data above - `routes/`: Defines the API endpoints and maps them to controller functions. +## Setting up Continuous Integration +- Protect your `main` branch against direct pushes so only PRs are allowed. + - A sample PR validation workflow is provided at `.github/workflows/pr-validation.yml` - note that it contains 7 jobs which can be added as checks. + - Recommended - block merge commits and only allow squash (rebase) PR merges. + - Docs: https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches +- Upon each merge to `main`, a package will be built by `.github/workflows/official-build.yml` + - The same package is also created by the PR validation workflow, to validate that packaging hasn't been broken and to allow test env deployment (see below) + +## Deploying the app +The repo includes a sample deployment workflow to Azure App Services Web App (which includes a free tier): https://azure.microsoft.com/en-us/products/app-service/web +- The model in this repo assumes 3 GitHub environments: https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments + - `Test` - used to deploy PR build artifacts (manually dispatched via `.github/workflows/deploy-test.yml`) + - `Stage` - used for continuous deployment (automatically dispatched upon official build completion via `.github/workflows/deploy-stage.yml`) + - `Prod` - used for production deployment of official build artifacts (manually dispatched via `.github/workflows/deploy-prod.yml`) +- Note that each of the above environments requires its own web app (so you'd have 3 in total) + - Under each GitHub environment, specify the corresponding web app's name in the `AZURE_WEBAPP_NAME` environment variable +- In order for deployment to work, you will need to follow the docs (specifically, create the repo secrets mentioned there for OIDC login): https://learn.microsoft.com/en-us/azure/app-service/deploy-github-actions?tabs=openid%2Caspnetcore +- In order for the deployment job to be able to fetch the built package from the build workflow, you'll need to create a `GH_PAT_ACTIONS_READONLY` repo secret containing a GitHub Personal Access Token (PAT) with `actions:read` permissions on your repo: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens + - In order for the server's CORS configuration to allow the deployed client, you'll need to set the `CLIENT_URL` environment variable in each of your web apps to its domain (e.g. `https://qbsafe.azurewebsites.net`) + + ## Best practices & Teamwork [Full guide](BestPractices.md) From 292c7e4bca404c7f4f6b22480ba57c0c26a271de Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 05:34:53 +0200 Subject: [PATCH 29/31] add local build script + explanation (#2) --- README.md | 3 +++ localBuild.ps1 | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 localBuild.ps1 diff --git a/README.md b/README.md index d226b80..01f8ab6 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,9 @@ Contains the Node.js / Express backend application. ## Deploying the app The repo includes a sample deployment workflow to Azure App Services Web App (which includes a free tier): https://azure.microsoft.com/en-us/products/app-service/web +- When deployed to a web app, the express server statically exposes the vite-compiled client. + - To emulate this setup locally for debugging, run `localBuild.ps1` followed by `package.ps1` to create a `package` folder similar to the one generated in the workflows. + - Then simply run `npm run start` in the `package` folder to have both API and client served on http://localhost:5000/ (you should be able to navigate to that address directly). - The model in this repo assumes 3 GitHub environments: https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments - `Test` - used to deploy PR build artifacts (manually dispatched via `.github/workflows/deploy-test.yml`) - `Stage` - used for continuous deployment (automatically dispatched upon official build completion via `.github/workflows/deploy-stage.yml`) diff --git a/localBuild.ps1 b/localBuild.ps1 new file mode 100644 index 0000000..b7afade --- /dev/null +++ b/localBuild.ps1 @@ -0,0 +1,19 @@ +$ErrorActionPreference = "Stop" +$PSNativeCommandUseErrorActionPreference = $true + +Push-Location -LiteralPath "$PSScriptRoot/client" +try { + npm install + npm run build +} +finally { + Pop-Location +} + +Push-Location -LiteralPath "$PSScriptRoot/server" +try { + npm install --omit=dev +} +finally { + Pop-Location +} \ No newline at end of file From e6c58f3bd98844511fff10b23ef4e49b69063e55 Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 05:46:00 +0200 Subject: [PATCH 30/31] remove CORS in prod (#3) --- server/server.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/server/server.js b/server/server.js index 3d56403..a73eb23 100644 --- a/server/server.js +++ b/server/server.js @@ -10,9 +10,14 @@ const app = express(); app.use(express.json()); -app.use(cors({ - origin: process.env.CLIENT_URL -})); +if (process.env.CLIENT_URL) { + // Dev / cross-origin scenario + app.use(cors({ + origin: process.env.CLIENT_URL + })); +} else { + // Prod / same-origin: do nothing, browser considers requests same-origin +} app.use(express.static('public')); // Serve client in combined deployment app.use('/images', express.static('images')); From b574b48f3b72ef1ca2e8399ee8536790737c2eab Mon Sep 17 00:00:00 2001 From: Ohad Schneider Date: Sat, 17 Jan 2026 05:47:43 +0200 Subject: [PATCH 31/31] remove unnecessary web app instructions prod doesn't need CORS since the client is served from the same domain as the API --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 01f8ab6..eb8ff08 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,6 @@ The repo includes a sample deployment workflow to Azure App Services Web App (wh - Under each GitHub environment, specify the corresponding web app's name in the `AZURE_WEBAPP_NAME` environment variable - In order for deployment to work, you will need to follow the docs (specifically, create the repo secrets mentioned there for OIDC login): https://learn.microsoft.com/en-us/azure/app-service/deploy-github-actions?tabs=openid%2Caspnetcore - In order for the deployment job to be able to fetch the built package from the build workflow, you'll need to create a `GH_PAT_ACTIONS_READONLY` repo secret containing a GitHub Personal Access Token (PAT) with `actions:read` permissions on your repo: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens - - In order for the server's CORS configuration to allow the deployed client, you'll need to set the `CLIENT_URL` environment variable in each of your web apps to its domain (e.g. `https://qbsafe.azurewebsites.net`) ## Best practices & Teamwork