From 692feb4ed2b6adba270b9f32f047b178b6213c65 Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Mon, 7 Jul 2025 20:21:42 +0900 Subject: [PATCH 01/22] =?UTF-8?q?init:=20=EA=B3=BC=EC=A0=9C=20=EC=8B=9C?= =?UTF-8?q?=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pnpm-lock.yaml | 1370 ++++++++++++++++++++---------------------------- 1 file changed, 561 insertions(+), 809 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8137d4c85..ee3fc95ca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: devDependencies: '@eslint/js': specifier: ^9.16.0 - version: 9.23.0 + version: 9.30.1 '@playwright/test': specifier: latest version: 1.53.2 @@ -31,13 +31,13 @@ importers: version: 2.1.9(vitest@3.2.4) eslint: specifier: ^9.16.0 - version: 9.23.0 + version: 9.30.1 eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.0(eslint@9.23.0) + version: 9.1.0(eslint@9.30.1) eslint-plugin-prettier: specifier: ^5.2.1 - version: 5.2.3(eslint-config-prettier@9.1.0(eslint@9.23.0))(eslint@9.23.0)(prettier@3.5.3) + version: 5.5.1(eslint-config-prettier@9.1.0(eslint@9.30.1))(eslint@9.30.1)(prettier@3.6.2) globals: specifier: ^15.13.0 version: 15.15.0 @@ -49,44 +49,40 @@ importers: version: 25.0.1 lint-staged: specifier: ^15.2.11 - version: 15.5.0 + version: 15.5.2 msw: specifier: ^2.10.2 - version: 2.10.2 + version: 2.10.3 prettier: specifier: ^3.4.2 - version: 3.5.3 + version: 3.6.2 vite: specifier: npm:rolldown-vite@latest - version: rolldown-vite@6.3.21(esbuild@0.25.1)(yaml@2.7.0) + version: rolldown-vite@7.0.5(esbuild@0.25.5)(yaml@2.8.0) vitest: specifier: latest - version: 3.2.4(@vitest/ui@2.1.9)(jsdom@25.0.1)(lightningcss@1.30.1)(msw@2.10.2) + version: 3.2.4(@vitest/ui@2.1.9)(jsdom@25.0.1)(lightningcss@1.30.1)(msw@2.10.3)(yaml@2.8.0) packages: - '@adobe/css-tools@4.4.2': - resolution: {integrity: sha512-baYZExFpsdkBNuvGKTKWCwKH57HRZLVtycZS05WTQNVOiXVSeAki3nU35zlRbToeMW8aHlJfyS+1C4BOv27q0A==} + '@adobe/css-tools@4.4.3': + resolution: {integrity: sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==} '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@asamuzakjp/css-color@3.1.1': - resolution: {integrity: sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA==} + '@asamuzakjp/css-color@3.2.0': + resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} - '@babel/code-frame@7.26.2': - resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.25.9': - resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} - engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.27.1': resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} @@ -96,8 +92,8 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/runtime@7.26.10': - resolution: {integrity: sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==} + '@babel/runtime@7.27.6': + resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} engines: {node: '>=6.9.0'} '@babel/types@7.28.0': @@ -121,28 +117,28 @@ packages: resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==} engines: {node: '>=18'} - '@csstools/css-calc@2.1.2': - resolution: {integrity: sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw==} + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} engines: {node: '>=18'} peerDependencies: - '@csstools/css-parser-algorithms': ^3.0.4 - '@csstools/css-tokenizer': ^3.0.3 + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-color-parser@3.0.8': - resolution: {integrity: sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ==} + '@csstools/css-color-parser@3.0.10': + resolution: {integrity: sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==} engines: {node: '>=18'} peerDependencies: - '@csstools/css-parser-algorithms': ^3.0.4 - '@csstools/css-tokenizer': ^3.0.3 + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-parser-algorithms@3.0.4': - resolution: {integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==} + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} engines: {node: '>=18'} peerDependencies: - '@csstools/css-tokenizer': ^3.0.3 + '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-tokenizer@3.0.3': - resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==} + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} engines: {node: '>=18'} '@emnapi/core@1.4.3': @@ -154,296 +150,158 @@ packages: '@emnapi/wasi-threads@1.0.2': resolution: {integrity: sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==} - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - - '@esbuild/aix-ppc64@0.25.1': - resolution: {integrity: sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==} + '@esbuild/aix-ppc64@0.25.5': + resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.25.1': - resolution: {integrity: sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==} + '@esbuild/android-arm64@0.25.5': + resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.25.1': - resolution: {integrity: sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==} + '@esbuild/android-arm@0.25.5': + resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.25.1': - resolution: {integrity: sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==} + '@esbuild/android-x64@0.25.5': + resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.25.1': - resolution: {integrity: sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==} + '@esbuild/darwin-arm64@0.25.5': + resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.25.1': - resolution: {integrity: sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==} + '@esbuild/darwin-x64@0.25.5': + resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.25.1': - resolution: {integrity: sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==} + '@esbuild/freebsd-arm64@0.25.5': + resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.25.1': - resolution: {integrity: sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==} + '@esbuild/freebsd-x64@0.25.5': + resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.25.1': - resolution: {integrity: sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==} + '@esbuild/linux-arm64@0.25.5': + resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.25.1': - resolution: {integrity: sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==} + '@esbuild/linux-arm@0.25.5': + resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.25.1': - resolution: {integrity: sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==} + '@esbuild/linux-ia32@0.25.5': + resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.25.1': - resolution: {integrity: sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==} + '@esbuild/linux-loong64@0.25.5': + resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.25.1': - resolution: {integrity: sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==} + '@esbuild/linux-mips64el@0.25.5': + resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.25.1': - resolution: {integrity: sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==} + '@esbuild/linux-ppc64@0.25.5': + resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-riscv64@0.25.1': - resolution: {integrity: sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==} + '@esbuild/linux-riscv64@0.25.5': + resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.25.1': - resolution: {integrity: sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==} + '@esbuild/linux-s390x@0.25.5': + resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - - '@esbuild/linux-x64@0.25.1': - resolution: {integrity: sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==} + '@esbuild/linux-x64@0.25.5': + resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.1': - resolution: {integrity: sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==} + '@esbuild/netbsd-arm64@0.25.5': + resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.25.1': - resolution: {integrity: sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==} + '@esbuild/netbsd-x64@0.25.5': + resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.1': - resolution: {integrity: sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==} + '@esbuild/openbsd-arm64@0.25.5': + resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.25.1': - resolution: {integrity: sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==} + '@esbuild/openbsd-x64@0.25.5': + resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.25.1': - resolution: {integrity: sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==} + '@esbuild/sunos-x64@0.25.5': + resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-arm64@0.25.1': - resolution: {integrity: sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==} + '@esbuild/win32-arm64@0.25.5': + resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.25.1': - resolution: {integrity: sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==} + '@esbuild/win32-ia32@0.25.5': + resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.25.1': - resolution: {integrity: sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==} + '@esbuild/win32-x64@0.25.5': + resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.5.1': - resolution: {integrity: sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==} + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -452,32 +310,36 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.19.2': - resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==} + '@eslint/config-array@0.21.0': + resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.3.0': + resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.2.0': - resolution: {integrity: sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==} + '@eslint/core@0.14.0': + resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.12.0': - resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==} + '@eslint/core@0.15.1': + resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@3.3.1': resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.23.0': - resolution: {integrity: sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==} + '@eslint/js@9.30.1': + resolution: {integrity: sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.6': resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.2.7': - resolution: {integrity: sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==} + '@eslint/plugin-kit@0.3.3': + resolution: {integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@humanfs/core@0.19.1': @@ -496,12 +358,12 @@ packages: resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} engines: {node: '>=18.18'} - '@humanwhocodes/retry@0.4.2': - resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@inquirer/confirm@5.1.12': - resolution: {integrity: sha512-dpq+ielV9/bqgXRUbNH//KsY6WEw9DrGPmipkpmgC1Y46cwuBTNx7PXFWTjc3MQ+urcc0QxoVHcMI0FW4Ok0hg==} + '@inquirer/confirm@5.1.13': + resolution: {integrity: sha512-EkCtvp67ICIVVzjsquUiVSd+V5HRGOGQfsqA4E4vMWhYnB7InUL0pa0TIWt1i+OfP16Gkds8CdIu6yGZwOM1Yw==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -509,8 +371,8 @@ packages: '@types/node': optional: true - '@inquirer/core@10.1.13': - resolution: {integrity: sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA==} + '@inquirer/core@10.1.14': + resolution: {integrity: sha512-Ma+ZpOJPewtIYl6HZHZckeX1STvDnHTCB2GVINNUlSEn2Am6LddWwfPkIGY0IUFVjUUrr/93XlBwTK6mfLjf0A==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -546,8 +408,8 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/sourcemap-codec@1.5.4': + resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} '@jridgewell/trace-mapping@0.3.29': resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} @@ -568,19 +430,23 @@ packages: '@open-draft/until@2.1.0': resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} - '@oxc-project/runtime@0.73.0': - resolution: {integrity: sha512-YFvBzVQK/ix0RQxOI02ebCumehSHoiJgvb7nOU4o7xFoMnnujLdjmxnEBK/qiOQrEyXlY69gXGMEsKYVe+YZ3A==} + '@oxc-project/runtime@0.75.0': + resolution: {integrity: sha512-gzRmVI/vorsPmbDXt7GD4Uh2lD3rCOku/1xWPB4Yx48k0EP4TZmzQudWapjN4+7Vv+rgXr0RqCHQadeaMvdBuw==} engines: {node: '>=6.9.0'} - '@oxc-project/types@0.73.0': - resolution: {integrity: sha512-ZQS7dpsga43R7bjqRKHRhOeNpuIBeLBnlS3M6H3IqWIWiapGOQIxp4lpETLBYupkSd4dh85ESFn6vAvtpPdGkA==} + '@oxc-project/runtime@0.75.1': + resolution: {integrity: sha512-UH07DRi7xXqAsJ/sFbJJg0liIXnapB6P5uADXIiF1s6WQjZzcTIkKHca0s522QVxmijPxVX5ijCYxSr7eSq5CQ==} + engines: {node: '>=6.9.0'} + + '@oxc-project/types@0.75.1': + resolution: {integrity: sha512-7ZJy+51qWpZRvynaQUezeYfjCtaSdiXIWFUZIlOuTSfDXpXqnSl/m1IUPLx6XrOy6s0SFv3CLE14vcZy63bz7g==} '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@pkgr/core@0.1.2': - resolution: {integrity: sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==} + '@pkgr/core@0.2.7': + resolution: {integrity: sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} '@playwright/test@1.53.2': @@ -588,164 +454,169 @@ packages: engines: {node: '>=18'} hasBin: true - '@polka/url@1.0.0-next.28': - resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@rolldown/binding-darwin-arm64@1.0.0-beta.16': - resolution: {integrity: sha512-dzlvuodUFc/QX97jYSsPHtYysqeSeM5gBxiN+DpV93tXEYyFMWm3cECxNmShz4ZM+lrgm6eG2/txzLZ/z9qWLw==} + '@rolldown/binding-darwin-arm64@1.0.0-beta.24': + resolution: {integrity: sha512-gE4HGjIioZaMGZupq2zQQdqhlRV2b2qnjFHHkJEW50zVDmiVNWwdHjwvZDPx9JfW5y4GuHgp/zKDLZZbJlQ1/Q==} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-beta.16': - resolution: {integrity: sha512-H5604ucjaYy5AxxuOP/CoE5RV3lKCJ+btclWL5rV+hVh0qNN9dVgve+onzAYmi8h2RBPET1Novj+2KB640PC9Q==} + '@rolldown/binding-darwin-x64@1.0.0-beta.24': + resolution: {integrity: sha512-h2HfOtqmjIHIz9WdpKAJ8sBfLNGkrMlwrCfNV2MDDGu0x3YdYBYPE+ozS5PvE53Tp8y6EYn2/thNWJTGWy/N3Q==} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-beta.16': - resolution: {integrity: sha512-DDzmSFFKfAcrUJfuwK4URKl28fIgK8fT5Kp374B1iJJ9KwcqIZzN1a3s/ubjTGIwiE+vUDEclVQ3z9R0VwkGAQ==} + '@rolldown/binding-freebsd-x64@1.0.0-beta.24': + resolution: {integrity: sha512-lx3Q2TU2bbY4yDCZ6e+Wiom3VMLFlZmQswx/1CyjFd+Vv3Q+99SZm6CSfNAIZBaWD246yQRRr1Vx+iIoWCdYzQ==} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.16': - resolution: {integrity: sha512-xkCdzCXW6SSDlFYaHjzCFrsbqxxo60YKVW4B/G2ST8HYruv0Ql4qpoQw7WoGeXL+bc/3RpKWzsxIiooUKX6e9Q==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.24': + resolution: {integrity: sha512-PLtsV6uf3uS1/cNF8Wu/kitTpXT2YpOZbN6VJm7oMi5A8o5oO0vh8STCB71O5k2kwQMVycsmxHWFk2ZyEa6aMw==} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.16': - resolution: {integrity: sha512-Yrz782pZsFVfxlsqppDneV2dl7St7lGt1uCscXnLC0vXiesj59vl3sULQ45eMKKeEEqPKz7X8OAJI7ao6zLSyg==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.24': + resolution: {integrity: sha512-UxGukDkWnv7uS5R+BPVeJ4FSuwA+lgC62LRsyPPSJhJhKMNGZ2W9sQPIpEtBRlww8t0qR6QBsiD5TGLW98ktGw==} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.16': - resolution: {integrity: sha512-1M8jPk7BICBjKfqNZCMtcLvzpEFHBkySPHt+RsYGZhFuAbCb352C9ilWsjpi7WwhWBOvh6tHUNmO77NTKlLxkA==} + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.24': + resolution: {integrity: sha512-vB99yGYW9FOQe4lk3MNKa13+vRj+7waZFlRE3Ba/IpEy7RFxZ78ASkPLXkz4+kYYbUvMnRaOfk9RKX2fqYZRUg==} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.16': - resolution: {integrity: sha512-6xhZMDt4r3r3DeurJFakCqev0ct0FHU9hQPvoaHTE3EfC0yRhUp7aQmf2lsB7YVU7Zcel/KiOv/DjJQR9fntog==} + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.24': + resolution: {integrity: sha512-fAMZBWutuKWHsyvHVsKjFYRXVgTbzBfNmomzPPpog8UtdkHk5Vnb0qVEeZP4hR4TsXnKfzD2EQ98NRqFej5QYA==} cpu: [x64] os: [linux] - '@rolldown/binding-linux-x64-musl@1.0.0-beta.16': - resolution: {integrity: sha512-zYnSz4Z39kEUUA1B03KbNFGgCNykZPhaDltJGY9C3bA3zU5+Ygtr+aeaRxEgXYP4PYBqE3rhPIGmDnlTzx18wA==} + '@rolldown/binding-linux-x64-musl@1.0.0-beta.24': + resolution: {integrity: sha512-0UY/Qo8fAlpolcWOg2ZU7SCUrsCJWifdRMliV9GXlZaBKbMoVNFw0pHGDm9cj/3TWhJu/iB0peZK00dm22LlNw==} cpu: [x64] os: [linux] - '@rolldown/binding-wasm32-wasi@1.0.0-beta.16': - resolution: {integrity: sha512-gFWaCVJENQWYAWkk6yJbteyMmxdZAYE9VLB4S4YqfxOYbGvVxq0K1Dn89uPEzN4beEaLToe917YzXqLdv4tPvQ==} + '@rolldown/binding-wasm32-wasi@1.0.0-beta.24': + resolution: {integrity: sha512-7ubbtKCo6FBuAM4q6LoT5dOea7f/zj9OYXgumbwSmA0fw18mN5h8SrFTUjl7h9MpPkOyhi2uY6ss4pb39KXkcw==} engines: {node: '>=14.21.3'} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.16': - resolution: {integrity: sha512-rbXNzlc3/aZSNaIWKAx6TGGUcgSnDmBYxyHLYthtAXz1uvg2o0YsAKYJszWHk0fTrjtKnDXLxwNjua1pf87cZA==} + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.24': + resolution: {integrity: sha512-S5WKIabtRBJyzu31KnJRlbZRR6FMrTMzYRrNTnIY2hWWXfpcB1PNuHqbo+98ODLpH8knul4Vyf5sCL61okLTjA==} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.16': - resolution: {integrity: sha512-9o4nk+IEvyWkE5qsLjcN+Sic869hELVZ5FsEvDruCa9sX5qZV4A5pj5bR9Sc+x4L0Aa1kQkPdChgxRqV1tgOdw==} + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.24': + resolution: {integrity: sha512-5EW8mzHoukz3zBn/VAaTapK+i+/ZFbSSP9meDmLSuGnk6La8uLAPc7E+6S3gbJnQ6k8lSC6ipIIeXC4SPdttKQ==} cpu: [ia32] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.16': - resolution: {integrity: sha512-PJSdUi02LT2dRS5nRNmqWTAEvq11NSBfPK5DoCTUj4DaUHJd05jBBtVyLabTutjaACN53O/pLOXds73W4obZ/g==} + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.24': + resolution: {integrity: sha512-KpurHt8+B0yTg9gHroC3H/Tf2c9VfjIBsC/wVHTf7GGAe+xkw1+5iYB3Y5iSy3OaMTGq1U3/YEvTqqBdSbDMUg==} cpu: [x64] os: [win32] - '@rolldown/pluginutils@1.0.0-beta.16': - resolution: {integrity: sha512-w3f87JpF7lgIlK03I0R3XidspFgB4MsixE5o/VjBMJI+Ki4XW/Ffrykmj2AUCbVxhRD7Pi9W0Qu2XapJhB2mSA==} + '@rolldown/pluginutils@1.0.0-beta.24': + resolution: {integrity: sha512-NMiim/enJlffMP16IanVj1ajFNEg8SaMEYyxyYfJoEyt5EiFT3HUH/T2GRdeStNWp+/kg5U8DiJqnQBgLQ8uCw==} - '@rollup/rollup-android-arm-eabi@4.36.0': - resolution: {integrity: sha512-jgrXjjcEwN6XpZXL0HUeOVGfjXhPyxAbbhD0BlXUB+abTOpbPiN5Wb3kOT7yb+uEtATNYF5x5gIfwutmuBA26w==} + '@rollup/rollup-android-arm-eabi@4.44.2': + resolution: {integrity: sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.36.0': - resolution: {integrity: sha512-NyfuLvdPdNUfUNeYKUwPwKsE5SXa2J6bCt2LdB/N+AxShnkpiczi3tcLJrm5mA+eqpy0HmaIY9F6XCa32N5yzg==} + '@rollup/rollup-android-arm64@4.44.2': + resolution: {integrity: sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.36.0': - resolution: {integrity: sha512-JQ1Jk5G4bGrD4pWJQzWsD8I1n1mgPXq33+/vP4sk8j/z/C2siRuxZtaUA7yMTf71TCZTZl/4e1bfzwUmFb3+rw==} + '@rollup/rollup-darwin-arm64@4.44.2': + resolution: {integrity: sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.36.0': - resolution: {integrity: sha512-6c6wMZa1lrtiRsbDziCmjE53YbTkxMYhhnWnSW8R/yqsM7a6mSJ3uAVT0t8Y/DGt7gxUWYuFM4bwWk9XCJrFKA==} + '@rollup/rollup-darwin-x64@4.44.2': + resolution: {integrity: sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.36.0': - resolution: {integrity: sha512-KXVsijKeJXOl8QzXTsA+sHVDsFOmMCdBRgFmBb+mfEb/7geR7+C8ypAml4fquUt14ZyVXaw2o1FWhqAfOvA4sg==} + '@rollup/rollup-freebsd-arm64@4.44.2': + resolution: {integrity: sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.36.0': - resolution: {integrity: sha512-dVeWq1ebbvByI+ndz4IJcD4a09RJgRYmLccwlQ8bPd4olz3Y213uf1iwvc7ZaxNn2ab7bjc08PrtBgMu6nb4pQ==} + '@rollup/rollup-freebsd-x64@4.44.2': + resolution: {integrity: sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.36.0': - resolution: {integrity: sha512-bvXVU42mOVcF4le6XSjscdXjqx8okv4n5vmwgzcmtvFdifQ5U4dXFYaCB87namDRKlUL9ybVtLQ9ztnawaSzvg==} + '@rollup/rollup-linux-arm-gnueabihf@4.44.2': + resolution: {integrity: sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.36.0': - resolution: {integrity: sha512-JFIQrDJYrxOnyDQGYkqnNBtjDwTgbasdbUiQvcU8JmGDfValfH1lNpng+4FWlhaVIR4KPkeddYjsVVbmJYvDcg==} + '@rollup/rollup-linux-arm-musleabihf@4.44.2': + resolution: {integrity: sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.36.0': - resolution: {integrity: sha512-KqjYVh3oM1bj//5X7k79PSCZ6CvaVzb7Qs7VMWS+SlWB5M8p3FqufLP9VNp4CazJ0CsPDLwVD9r3vX7Ci4J56A==} + '@rollup/rollup-linux-arm64-gnu@4.44.2': + resolution: {integrity: sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.36.0': - resolution: {integrity: sha512-QiGnhScND+mAAtfHqeT+cB1S9yFnNQ/EwCg5yE3MzoaZZnIV0RV9O5alJAoJKX/sBONVKeZdMfO8QSaWEygMhw==} + '@rollup/rollup-linux-arm64-musl@4.44.2': + resolution: {integrity: sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.36.0': - resolution: {integrity: sha512-1ZPyEDWF8phd4FQtTzMh8FQwqzvIjLsl6/84gzUxnMNFBtExBtpL51H67mV9xipuxl1AEAerRBgBwFNpkw8+Lg==} + '@rollup/rollup-linux-loongarch64-gnu@4.44.2': + resolution: {integrity: sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.36.0': - resolution: {integrity: sha512-VMPMEIUpPFKpPI9GZMhJrtu8rxnp6mJR3ZzQPykq4xc2GmdHj3Q4cA+7avMyegXy4n1v+Qynr9fR88BmyO74tg==} + '@rollup/rollup-linux-powerpc64le-gnu@4.44.2': + resolution: {integrity: sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.36.0': - resolution: {integrity: sha512-ttE6ayb/kHwNRJGYLpuAvB7SMtOeQnVXEIpMtAvx3kepFQeowVED0n1K9nAdraHUPJ5hydEMxBpIR7o4nrm8uA==} + '@rollup/rollup-linux-riscv64-gnu@4.44.2': + resolution: {integrity: sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.36.0': - resolution: {integrity: sha512-4a5gf2jpS0AIe7uBjxDeUMNcFmaRTbNv7NxI5xOCs4lhzsVyGR/0qBXduPnoWf6dGC365saTiwag8hP1imTgag==} + '@rollup/rollup-linux-riscv64-musl@4.44.2': + resolution: {integrity: sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.44.2': + resolution: {integrity: sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.36.0': - resolution: {integrity: sha512-5KtoW8UWmwFKQ96aQL3LlRXX16IMwyzMq/jSSVIIyAANiE1doaQsx/KRyhAvpHlPjPiSU/AYX/8m+lQ9VToxFQ==} + '@rollup/rollup-linux-x64-gnu@4.44.2': + resolution: {integrity: sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.36.0': - resolution: {integrity: sha512-sycrYZPrv2ag4OCvaN5js+f01eoZ2U+RmT5as8vhxiFz+kxwlHrsxOwKPSA8WyS+Wc6Epid9QeI/IkQ9NkgYyQ==} + '@rollup/rollup-linux-x64-musl@4.44.2': + resolution: {integrity: sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.36.0': - resolution: {integrity: sha512-qbqt4N7tokFwwSVlWDsjfoHgviS3n/vZ8LK0h1uLG9TYIRuUTJC88E1xb3LM2iqZ/WTqNQjYrtmtGmrmmawB6A==} + '@rollup/rollup-win32-arm64-msvc@4.44.2': + resolution: {integrity: sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.36.0': - resolution: {integrity: sha512-t+RY0JuRamIocMuQcfwYSOkmdX9dtkr1PbhKW42AMvaDQa+jOdpUYysroTF/nuPpAaQMWp7ye+ndlmmthieJrQ==} + '@rollup/rollup-win32-ia32-msvc@4.44.2': + resolution: {integrity: sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.36.0': - resolution: {integrity: sha512-aRXd7tRZkWLqGbChgcMMDEHjOKudo1kChb1Jt1IfR8cY/KIpgNviLeJy5FUb9IpSuQj8dU2fAYNMPW/hLKOSTw==} + '@rollup/rollup-win32-x64-msvc@4.44.2': + resolution: {integrity: sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==} cpu: [x64] os: [win32] @@ -778,8 +649,8 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/estree@1.0.6': - resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -844,8 +715,8 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.14.1: - resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} hasBin: true @@ -911,8 +782,8 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} @@ -1001,23 +872,14 @@ packages: css.escape@1.5.1: resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} - cssstyle@4.3.0: - resolution: {integrity: sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ==} + cssstyle@4.6.0: + resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} engines: {node: '>=18'} data-urls@5.0.0: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} - debug@4.4.0: - resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.4.1: resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} @@ -1027,8 +889,8 @@ packages: supports-color: optional: true - decimal.js@10.5.0: - resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==} + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} @@ -1071,8 +933,8 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} environment@1.1.0: @@ -1098,13 +960,8 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} - hasBin: true - - esbuild@0.25.1: - resolution: {integrity: sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==} + esbuild@0.25.5: + resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} engines: {node: '>=18'} hasBin: true @@ -1122,13 +979,13 @@ packages: peerDependencies: eslint: '>=7.0.0' - eslint-plugin-prettier@5.2.3: - resolution: {integrity: sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==} + eslint-plugin-prettier@5.5.1: + resolution: {integrity: sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: '@types/eslint': '>=8.0.0' eslint: '>=8.0.0' - eslint-config-prettier: '*' + eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' prettier: '>=3.0.0' peerDependenciesMeta: '@types/eslint': @@ -1136,20 +993,20 @@ packages: eslint-config-prettier: optional: true - eslint-scope@8.3.0: - resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-visitor-keys@4.2.0: - resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.23.0: - resolution: {integrity: sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==} + eslint@9.30.1: + resolution: {integrity: sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1158,8 +1015,8 @@ packages: jiti: optional: true - espree@10.3.0: - resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} esquery@1.6.0: @@ -1188,8 +1045,8 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} - expect-type@1.2.1: - resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} engines: {node: '>=12.0.0'} fast-deep-equal@3.1.3: @@ -1204,14 +1061,6 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fdir@6.4.3: - resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - fdir@6.4.6: resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} peerDependencies: @@ -1246,8 +1095,8 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} - form-data@4.0.2: - resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + form-data@4.0.3: + resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==} engines: {node: '>= 6'} fsevents@2.3.2: @@ -1529,13 +1378,13 @@ packages: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} - lint-staged@15.5.0: - resolution: {integrity: sha512-WyCzSbfYGhK7cU+UuDDkzUiytbfbi0ZdPy2orwtM75P3WTtQBzmG40cCxIa8Ii2+XjfxzLH6Be46tUfWS85Xfg==} + lint-staged@15.5.2: + resolution: {integrity: sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==} engines: {node: '>=18.12.0'} hasBin: true - listr2@8.2.5: - resolution: {integrity: sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==} + listr2@8.3.3: + resolution: {integrity: sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==} engines: {node: '>=18.0.0'} locate-path@6.0.0: @@ -1552,9 +1401,6 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} - loupe@3.1.3: - resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} - loupe@3.1.4: resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==} @@ -1624,8 +1470,8 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - msw@2.10.2: - resolution: {integrity: sha512-RCKM6IZseZQCWcSWlutdf590M8nVfRHG1ImwzOtwz8IYxgT4zhUO0rfTcTvDGiaFE0Rhcc+h43lcF3Jc9gFtwQ==} + msw@2.10.3: + resolution: {integrity: sha512-rpqW4wIqISJlgDfu3tiqzuWC/d6jofSuMUsBu1rwepzSwX21aQoagsd+fjahJ8sewa6FwlYhu4no+jfGVQm2IA==} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -1650,8 +1496,8 @@ packages: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - nwsapi@2.2.19: - resolution: {integrity: sha512-94bcyI3RsqiZufXjkr3ltkI86iEl+I7uiHVDtcq9wJUTwYQJ5odHDeSzkkrRzi80jJ8MaeZgqKjH1bAWAFw9bA==} + nwsapi@2.2.20: + resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==} onetime@6.0.0: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} @@ -1683,8 +1529,8 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - parse5@7.2.1: - resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} @@ -1711,8 +1557,8 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pathval@2.0.0: - resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} engines: {node: '>= 14.16'} picocolors@1.1.1: @@ -1741,8 +1587,8 @@ packages: engines: {node: '>=18'} hasBin: true - postcss@8.5.3: - resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -1753,8 +1599,8 @@ packages: resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} engines: {node: '>=6.0.0'} - prettier@3.5.3: - resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} hasBin: true @@ -1779,9 +1625,6 @@ packages: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} - regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -1800,19 +1643,19 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rolldown-vite@6.3.21: - resolution: {integrity: sha512-mjds/3g+YPWJmT08oQic/L5sWvs/lNc4vs9vmD7uHQtGdP7qGriWtYf62Vp+6eQhd/MPeFVw71TMEEt/cH+sLQ==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + rolldown-vite@7.0.5: + resolution: {integrity: sha512-z239GrVtwr+DVgFw6s64Wa+OLjE4aG1Ux+MeVdQKXes0XaIDja5ouacPDLExgpSlplbe0zxSb1JbYtFvOr+pIw==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@types/node': ^20.19.0 || >=22.12.0 esbuild: ^0.25.0 jiti: '>=1.21.0' - less: '*' - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 @@ -1840,12 +1683,12 @@ packages: yaml: optional: true - rolldown@1.0.0-beta.16: - resolution: {integrity: sha512-ruNh01VbnTJsW0kgYywrQ80FUY0yJvXqavPVljGg0dRiwggYB7yXlypw1ptkFiomkEOnOGiwncjiviUakgPHxg==} + rolldown@1.0.0-beta.24: + resolution: {integrity: sha512-eDyipoOnoHQ5p6INkJ8g31eKGlqPSCAN9PapyOTw5HET4FYIWALZnSgpMZ67mdn+xT3jAsqGidNnBcIM6EAUhA==} hasBin: true - rollup@4.36.0: - resolution: {integrity: sha512-zwATAXNQxUcd40zgtQG0ZafcRK4g004WtEl7kbuhTWPvf07PsfohXl39jVUvPF7jvNAIkKPQ2XrsDlWuxBd++Q==} + rollup@4.44.2: + resolution: {integrity: sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -1957,8 +1800,8 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - synckit@0.9.2: - resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==} + synckit@0.11.8: + resolution: {integrity: sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==} engines: {node: ^14.18.0 || >=16.0.0} test-exclude@7.0.1: @@ -1971,10 +1814,6 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyglobby@0.2.12: - resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} - engines: {node: '>=12.0.0'} - tinyglobby@0.2.14: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} @@ -1995,11 +1834,11 @@ packages: resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} engines: {node: '>=14.0.0'} - tldts-core@6.1.84: - resolution: {integrity: sha512-NaQa1W76W2aCGjXybvnMYzGSM4x8fvG2AN/pla7qxcg0ZHbooOPhA8kctmOZUDfZyhDL27OGNbwAeig8P4p1vg==} + tldts-core@6.1.86: + resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} - tldts@6.1.84: - resolution: {integrity: sha512-aRGIbCIF3teodtUFAYSdQONVmDRy21REM3o6JnqWn5ZkQBJJ4gHxhw6OfwQ+WkSAi3ASamrS4N4nyazWx6uTYg==} + tldts@6.1.86: + resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} hasBin: true to-regex-range@5.0.1: @@ -2018,8 +1857,8 @@ packages: resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} engines: {node: '>=16'} - tr46@5.1.0: - resolution: {integrity: sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==} + tr46@5.1.1: + resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} tslib@2.8.1: @@ -2052,22 +1891,27 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite@5.4.14: - resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==} - engines: {node: ^18.0.0 || >=20.0.0} + vite@7.0.2: + resolution: {integrity: sha512-hxdyZDY1CM6SNpKI4w4lcUc3Mtkd9ej4ECWVHSMrOdSinVc2zYOAppHeGc/hzmRo3pxM5blMzkuWHOJA/3NiFw==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true + jiti: + optional: true less: optional: true lightningcss: @@ -2082,6 +1926,10 @@ packages: optional: true terser: optional: true + tsx: + optional: true + yaml: + optional: true vitest@3.2.4: resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} @@ -2161,8 +2009,8 @@ packages: resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} engines: {node: '>=18'} - ws@8.18.1: - resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -2184,9 +2032,9 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - yaml@2.7.0: - resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} - engines: {node: '>= 14'} + yaml@2.8.0: + resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} + engines: {node: '>= 14.6'} hasBin: true yargs-parser@21.1.1: @@ -2207,40 +2055,36 @@ packages: snapshots: - '@adobe/css-tools@4.4.2': {} + '@adobe/css-tools@4.4.3': {} '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.12 '@jridgewell/trace-mapping': 0.3.29 - '@asamuzakjp/css-color@3.1.1': + '@asamuzakjp/css-color@3.2.0': dependencies: - '@csstools/css-calc': 2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-color-parser': 3.0.8(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) - '@csstools/css-tokenizer': 3.0.3 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-color-parser': 3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 lru-cache: 10.4.3 - '@babel/code-frame@7.26.2': + '@babel/code-frame@7.27.1': dependencies: - '@babel/helper-validator-identifier': 7.25.9 + '@babel/helper-validator-identifier': 7.27.1 js-tokens: 4.0.0 picocolors: 1.1.1 '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.25.9': {} - '@babel/helper-validator-identifier@7.27.1': {} '@babel/parser@7.28.0': dependencies: '@babel/types': 7.28.0 - '@babel/runtime@7.26.10': - dependencies: - regenerator-runtime: 0.14.1 + '@babel/runtime@7.27.6': {} '@babel/types@7.28.0': dependencies: @@ -2264,23 +2108,23 @@ snapshots: '@csstools/color-helpers@5.0.2': {} - '@csstools/css-calc@2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': dependencies: - '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) - '@csstools/css-tokenizer': 3.0.3 + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 - '@csstools/css-color-parser@3.0.8(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + '@csstools/css-color-parser@3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': dependencies: '@csstools/color-helpers': 5.0.2 - '@csstools/css-calc': 2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) - '@csstools/css-tokenizer': 3.0.3 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 - '@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)': + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': dependencies: - '@csstools/css-tokenizer': 3.0.3 + '@csstools/css-tokenizer': 3.0.4 - '@csstools/css-tokenizer@3.0.3': {} + '@csstools/css-tokenizer@3.0.4': {} '@emnapi/core@1.4.3': dependencies: @@ -2298,176 +2142,111 @@ snapshots: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.21.5': - optional: true - - '@esbuild/aix-ppc64@0.25.1': - optional: true - - '@esbuild/android-arm64@0.21.5': - optional: true - - '@esbuild/android-arm64@0.25.1': - optional: true - - '@esbuild/android-arm@0.21.5': - optional: true - - '@esbuild/android-arm@0.25.1': - optional: true - - '@esbuild/android-x64@0.21.5': - optional: true - - '@esbuild/android-x64@0.25.1': - optional: true - - '@esbuild/darwin-arm64@0.21.5': - optional: true - - '@esbuild/darwin-arm64@0.25.1': - optional: true - - '@esbuild/darwin-x64@0.21.5': - optional: true - - '@esbuild/darwin-x64@0.25.1': - optional: true - - '@esbuild/freebsd-arm64@0.21.5': - optional: true - - '@esbuild/freebsd-arm64@0.25.1': + '@esbuild/aix-ppc64@0.25.5': optional: true - '@esbuild/freebsd-x64@0.21.5': + '@esbuild/android-arm64@0.25.5': optional: true - '@esbuild/freebsd-x64@0.25.1': + '@esbuild/android-arm@0.25.5': optional: true - '@esbuild/linux-arm64@0.21.5': + '@esbuild/android-x64@0.25.5': optional: true - '@esbuild/linux-arm64@0.25.1': + '@esbuild/darwin-arm64@0.25.5': optional: true - '@esbuild/linux-arm@0.21.5': + '@esbuild/darwin-x64@0.25.5': optional: true - '@esbuild/linux-arm@0.25.1': + '@esbuild/freebsd-arm64@0.25.5': optional: true - '@esbuild/linux-ia32@0.21.5': + '@esbuild/freebsd-x64@0.25.5': optional: true - '@esbuild/linux-ia32@0.25.1': + '@esbuild/linux-arm64@0.25.5': optional: true - '@esbuild/linux-loong64@0.21.5': + '@esbuild/linux-arm@0.25.5': optional: true - '@esbuild/linux-loong64@0.25.1': + '@esbuild/linux-ia32@0.25.5': optional: true - '@esbuild/linux-mips64el@0.21.5': + '@esbuild/linux-loong64@0.25.5': optional: true - '@esbuild/linux-mips64el@0.25.1': + '@esbuild/linux-mips64el@0.25.5': optional: true - '@esbuild/linux-ppc64@0.21.5': + '@esbuild/linux-ppc64@0.25.5': optional: true - '@esbuild/linux-ppc64@0.25.1': + '@esbuild/linux-riscv64@0.25.5': optional: true - '@esbuild/linux-riscv64@0.21.5': + '@esbuild/linux-s390x@0.25.5': optional: true - '@esbuild/linux-riscv64@0.25.1': + '@esbuild/linux-x64@0.25.5': optional: true - '@esbuild/linux-s390x@0.21.5': + '@esbuild/netbsd-arm64@0.25.5': optional: true - '@esbuild/linux-s390x@0.25.1': + '@esbuild/netbsd-x64@0.25.5': optional: true - '@esbuild/linux-x64@0.21.5': + '@esbuild/openbsd-arm64@0.25.5': optional: true - '@esbuild/linux-x64@0.25.1': + '@esbuild/openbsd-x64@0.25.5': optional: true - '@esbuild/netbsd-arm64@0.25.1': + '@esbuild/sunos-x64@0.25.5': optional: true - '@esbuild/netbsd-x64@0.21.5': + '@esbuild/win32-arm64@0.25.5': optional: true - '@esbuild/netbsd-x64@0.25.1': + '@esbuild/win32-ia32@0.25.5': optional: true - '@esbuild/openbsd-arm64@0.25.1': + '@esbuild/win32-x64@0.25.5': optional: true - '@esbuild/openbsd-x64@0.21.5': - optional: true - - '@esbuild/openbsd-x64@0.25.1': - optional: true - - '@esbuild/sunos-x64@0.21.5': - optional: true - - '@esbuild/sunos-x64@0.25.1': - optional: true - - '@esbuild/win32-arm64@0.21.5': - optional: true - - '@esbuild/win32-arm64@0.25.1': - optional: true - - '@esbuild/win32-ia32@0.21.5': - optional: true - - '@esbuild/win32-ia32@0.25.1': - optional: true - - '@esbuild/win32-x64@0.21.5': - optional: true - - '@esbuild/win32-x64@0.25.1': - optional: true - - '@eslint-community/eslint-utils@4.5.1(eslint@9.23.0)': + '@eslint-community/eslint-utils@4.7.0(eslint@9.30.1)': dependencies: - eslint: 9.23.0 + eslint: 9.30.1 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} - '@eslint/config-array@0.19.2': + '@eslint/config-array@0.21.0': dependencies: '@eslint/object-schema': 2.1.6 - debug: 4.4.0 + debug: 4.4.1 minimatch: 3.1.2 transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.2.0': {} + '@eslint/config-helpers@0.3.0': {} - '@eslint/core@0.12.0': + '@eslint/core@0.14.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.15.1': dependencies: '@types/json-schema': 7.0.15 '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.4.0 - espree: 10.3.0 + debug: 4.4.1 + espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 @@ -2477,13 +2256,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.23.0': {} + '@eslint/js@9.30.1': {} '@eslint/object-schema@2.1.6': {} - '@eslint/plugin-kit@0.2.7': + '@eslint/plugin-kit@0.3.3': dependencies: - '@eslint/core': 0.12.0 + '@eslint/core': 0.15.1 levn: 0.4.1 '@humanfs/core@0.19.1': {} @@ -2497,14 +2276,14 @@ snapshots: '@humanwhocodes/retry@0.3.1': {} - '@humanwhocodes/retry@0.4.2': {} + '@humanwhocodes/retry@0.4.3': {} - '@inquirer/confirm@5.1.12': + '@inquirer/confirm@5.1.13': dependencies: - '@inquirer/core': 10.1.13 + '@inquirer/core': 10.1.14 '@inquirer/type': 3.0.7 - '@inquirer/core@10.1.13': + '@inquirer/core@10.1.14': dependencies: '@inquirer/figures': 1.0.12 '@inquirer/type': 3.0.7 @@ -2532,17 +2311,17 @@ snapshots: '@jridgewell/gen-mapping@0.3.12': dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.4 '@jridgewell/trace-mapping': 0.3.29 '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/sourcemap-codec@1.5.4': {} '@jridgewell/trace-mapping@0.3.29': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.4 '@mswjs/interceptors@0.39.2': dependencies: @@ -2569,122 +2348,127 @@ snapshots: '@open-draft/until@2.1.0': {} - '@oxc-project/runtime@0.73.0': {} + '@oxc-project/runtime@0.75.0': {} + + '@oxc-project/runtime@0.75.1': {} - '@oxc-project/types@0.73.0': {} + '@oxc-project/types@0.75.1': {} '@pkgjs/parseargs@0.11.0': optional: true - '@pkgr/core@0.1.2': {} + '@pkgr/core@0.2.7': {} '@playwright/test@1.53.2': dependencies: playwright: 1.53.2 - '@polka/url@1.0.0-next.28': {} + '@polka/url@1.0.0-next.29': {} - '@rolldown/binding-darwin-arm64@1.0.0-beta.16': + '@rolldown/binding-darwin-arm64@1.0.0-beta.24': optional: true - '@rolldown/binding-darwin-x64@1.0.0-beta.16': + '@rolldown/binding-darwin-x64@1.0.0-beta.24': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-beta.16': + '@rolldown/binding-freebsd-x64@1.0.0-beta.24': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.16': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.24': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.16': + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.24': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.16': + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.24': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.16': + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.24': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-beta.16': + '@rolldown/binding-linux-x64-musl@1.0.0-beta.24': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-beta.16': + '@rolldown/binding-wasm32-wasi@1.0.0-beta.24': dependencies: '@napi-rs/wasm-runtime': 0.2.11 optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.16': + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.24': optional: true - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.16': + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.24': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.16': + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.24': optional: true - '@rolldown/pluginutils@1.0.0-beta.16': {} + '@rolldown/pluginutils@1.0.0-beta.24': {} + + '@rollup/rollup-android-arm-eabi@4.44.2': + optional: true - '@rollup/rollup-android-arm-eabi@4.36.0': + '@rollup/rollup-android-arm64@4.44.2': optional: true - '@rollup/rollup-android-arm64@4.36.0': + '@rollup/rollup-darwin-arm64@4.44.2': optional: true - '@rollup/rollup-darwin-arm64@4.36.0': + '@rollup/rollup-darwin-x64@4.44.2': optional: true - '@rollup/rollup-darwin-x64@4.36.0': + '@rollup/rollup-freebsd-arm64@4.44.2': optional: true - '@rollup/rollup-freebsd-arm64@4.36.0': + '@rollup/rollup-freebsd-x64@4.44.2': optional: true - '@rollup/rollup-freebsd-x64@4.36.0': + '@rollup/rollup-linux-arm-gnueabihf@4.44.2': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.36.0': + '@rollup/rollup-linux-arm-musleabihf@4.44.2': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.36.0': + '@rollup/rollup-linux-arm64-gnu@4.44.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.36.0': + '@rollup/rollup-linux-arm64-musl@4.44.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.36.0': + '@rollup/rollup-linux-loongarch64-gnu@4.44.2': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.36.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.44.2': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.36.0': + '@rollup/rollup-linux-riscv64-gnu@4.44.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.36.0': + '@rollup/rollup-linux-riscv64-musl@4.44.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.36.0': + '@rollup/rollup-linux-s390x-gnu@4.44.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.36.0': + '@rollup/rollup-linux-x64-gnu@4.44.2': optional: true - '@rollup/rollup-linux-x64-musl@4.36.0': + '@rollup/rollup-linux-x64-musl@4.44.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.36.0': + '@rollup/rollup-win32-arm64-msvc@4.44.2': optional: true - '@rollup/rollup-win32-ia32-msvc@4.36.0': + '@rollup/rollup-win32-ia32-msvc@4.44.2': optional: true - '@rollup/rollup-win32-x64-msvc@4.36.0': + '@rollup/rollup-win32-x64-msvc@4.44.2': optional: true '@testing-library/dom@10.4.0': dependencies: - '@babel/code-frame': 7.26.2 - '@babel/runtime': 7.26.10 + '@babel/code-frame': 7.27.1 + '@babel/runtime': 7.27.6 '@types/aria-query': 5.0.4 aria-query: 5.3.0 chalk: 4.1.2 @@ -2694,7 +2478,7 @@ snapshots: '@testing-library/jest-dom@6.6.3': dependencies: - '@adobe/css-tools': 4.4.2 + '@adobe/css-tools': 4.4.3 aria-query: 5.3.2 chalk: 3.0.0 css.escape: 1.5.1 @@ -2721,7 +2505,7 @@ snapshots: '@types/deep-eql@4.0.2': {} - '@types/estree@1.0.6': {} + '@types/estree@1.0.8': {} '@types/json-schema@7.0.15': {} @@ -2744,7 +2528,7 @@ snapshots: std-env: 3.9.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.4(@vitest/ui@2.1.9)(jsdom@25.0.1)(lightningcss@1.30.1)(msw@2.10.2) + vitest: 3.2.4(@vitest/ui@2.1.9)(jsdom@25.0.1)(lightningcss@1.30.1)(msw@2.10.3)(yaml@2.8.0) transitivePeerDependencies: - supports-color @@ -2756,14 +2540,14 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(msw@2.10.2)(vite@5.4.14(lightningcss@1.30.1))': + '@vitest/mocker@3.2.4(msw@2.10.3)(vite@7.0.2(lightningcss@1.30.1)(yaml@2.8.0))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - msw: 2.10.2 - vite: 5.4.14(lightningcss@1.30.1) + msw: 2.10.3 + vite: 7.0.2(lightningcss@1.30.1)(yaml@2.8.0) '@vitest/pretty-format@2.1.9': dependencies: @@ -2796,14 +2580,14 @@ snapshots: flatted: 3.3.3 pathe: 1.1.2 sirv: 3.0.1 - tinyglobby: 0.2.12 + tinyglobby: 0.2.14 tinyrainbow: 1.2.0 - vitest: 3.2.4(@vitest/ui@2.1.9)(jsdom@25.0.1)(lightningcss@1.30.1)(msw@2.10.2) + vitest: 3.2.4(@vitest/ui@2.1.9)(jsdom@25.0.1)(lightningcss@1.30.1)(msw@2.10.3)(yaml@2.8.0) '@vitest/utils@2.1.9': dependencies: '@vitest/pretty-format': 2.1.9 - loupe: 3.1.3 + loupe: 3.1.4 tinyrainbow: 1.2.0 '@vitest/utils@3.2.4': @@ -2812,11 +2596,11 @@ snapshots: loupe: 3.1.4 tinyrainbow: 2.0.0 - acorn-jsx@5.3.2(acorn@8.14.1): + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: - acorn: 8.14.1 + acorn: 8.15.0 - acorn@8.14.1: {} + acorn@8.15.0: {} agent-base@7.1.3: {} @@ -2869,7 +2653,7 @@ snapshots: balanced-match@1.0.2: {} - brace-expansion@1.1.11: + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 @@ -2897,7 +2681,7 @@ snapshots: check-error: 2.1.1 deep-eql: 5.0.2 loupe: 3.1.4 - pathval: 2.0.0 + pathval: 2.0.1 chalk@3.0.0: dependencies: @@ -2956,9 +2740,9 @@ snapshots: css.escape@1.5.1: {} - cssstyle@4.3.0: + cssstyle@4.6.0: dependencies: - '@asamuzakjp/css-color': 3.1.1 + '@asamuzakjp/css-color': 3.2.0 rrweb-cssom: 0.8.0 data-urls@5.0.0: @@ -2966,15 +2750,11 @@ snapshots: whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 - debug@4.4.0: - dependencies: - ms: 2.1.3 - debug@4.4.1: dependencies: ms: 2.1.3 - decimal.js@10.5.0: {} + decimal.js@10.6.0: {} deep-eql@5.0.2: {} @@ -3004,7 +2784,7 @@ snapshots: emoji-regex@9.2.2: {} - entities@4.5.0: {} + entities@6.0.1: {} environment@1.1.0: {} @@ -3025,110 +2805,83 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 - esbuild@0.21.5: - optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 - - esbuild@0.25.1: + esbuild@0.25.5: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.1 - '@esbuild/android-arm': 0.25.1 - '@esbuild/android-arm64': 0.25.1 - '@esbuild/android-x64': 0.25.1 - '@esbuild/darwin-arm64': 0.25.1 - '@esbuild/darwin-x64': 0.25.1 - '@esbuild/freebsd-arm64': 0.25.1 - '@esbuild/freebsd-x64': 0.25.1 - '@esbuild/linux-arm': 0.25.1 - '@esbuild/linux-arm64': 0.25.1 - '@esbuild/linux-ia32': 0.25.1 - '@esbuild/linux-loong64': 0.25.1 - '@esbuild/linux-mips64el': 0.25.1 - '@esbuild/linux-ppc64': 0.25.1 - '@esbuild/linux-riscv64': 0.25.1 - '@esbuild/linux-s390x': 0.25.1 - '@esbuild/linux-x64': 0.25.1 - '@esbuild/netbsd-arm64': 0.25.1 - '@esbuild/netbsd-x64': 0.25.1 - '@esbuild/openbsd-arm64': 0.25.1 - '@esbuild/openbsd-x64': 0.25.1 - '@esbuild/sunos-x64': 0.25.1 - '@esbuild/win32-arm64': 0.25.1 - '@esbuild/win32-ia32': 0.25.1 - '@esbuild/win32-x64': 0.25.1 - optional: true + '@esbuild/aix-ppc64': 0.25.5 + '@esbuild/android-arm': 0.25.5 + '@esbuild/android-arm64': 0.25.5 + '@esbuild/android-x64': 0.25.5 + '@esbuild/darwin-arm64': 0.25.5 + '@esbuild/darwin-x64': 0.25.5 + '@esbuild/freebsd-arm64': 0.25.5 + '@esbuild/freebsd-x64': 0.25.5 + '@esbuild/linux-arm': 0.25.5 + '@esbuild/linux-arm64': 0.25.5 + '@esbuild/linux-ia32': 0.25.5 + '@esbuild/linux-loong64': 0.25.5 + '@esbuild/linux-mips64el': 0.25.5 + '@esbuild/linux-ppc64': 0.25.5 + '@esbuild/linux-riscv64': 0.25.5 + '@esbuild/linux-s390x': 0.25.5 + '@esbuild/linux-x64': 0.25.5 + '@esbuild/netbsd-arm64': 0.25.5 + '@esbuild/netbsd-x64': 0.25.5 + '@esbuild/openbsd-arm64': 0.25.5 + '@esbuild/openbsd-x64': 0.25.5 + '@esbuild/sunos-x64': 0.25.5 + '@esbuild/win32-arm64': 0.25.5 + '@esbuild/win32-ia32': 0.25.5 + '@esbuild/win32-x64': 0.25.5 escalade@3.2.0: {} escape-string-regexp@4.0.0: {} - eslint-config-prettier@9.1.0(eslint@9.23.0): + eslint-config-prettier@9.1.0(eslint@9.30.1): dependencies: - eslint: 9.23.0 + eslint: 9.30.1 - eslint-plugin-prettier@5.2.3(eslint-config-prettier@9.1.0(eslint@9.23.0))(eslint@9.23.0)(prettier@3.5.3): + eslint-plugin-prettier@5.5.1(eslint-config-prettier@9.1.0(eslint@9.30.1))(eslint@9.30.1)(prettier@3.6.2): dependencies: - eslint: 9.23.0 - prettier: 3.5.3 + eslint: 9.30.1 + prettier: 3.6.2 prettier-linter-helpers: 1.0.0 - synckit: 0.9.2 + synckit: 0.11.8 optionalDependencies: - eslint-config-prettier: 9.1.0(eslint@9.23.0) + eslint-config-prettier: 9.1.0(eslint@9.30.1) - eslint-scope@8.3.0: + eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 eslint-visitor-keys@3.4.3: {} - eslint-visitor-keys@4.2.0: {} + eslint-visitor-keys@4.2.1: {} - eslint@9.23.0: + eslint@9.30.1: dependencies: - '@eslint-community/eslint-utils': 4.5.1(eslint@9.23.0) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.30.1) '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.19.2 - '@eslint/config-helpers': 0.2.0 - '@eslint/core': 0.12.0 + '@eslint/config-array': 0.21.0 + '@eslint/config-helpers': 0.3.0 + '@eslint/core': 0.14.0 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.23.0 - '@eslint/plugin-kit': 0.2.7 + '@eslint/js': 9.30.1 + '@eslint/plugin-kit': 0.3.3 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.2 - '@types/estree': 1.0.6 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.0 + debug: 4.4.1 escape-string-regexp: 4.0.0 - eslint-scope: 8.3.0 - eslint-visitor-keys: 4.2.0 - espree: 10.3.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 @@ -3146,11 +2899,11 @@ snapshots: transitivePeerDependencies: - supports-color - espree@10.3.0: + espree@10.4.0: dependencies: - acorn: 8.14.1 - acorn-jsx: 5.3.2(acorn@8.14.1) - eslint-visitor-keys: 4.2.0 + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 esquery@1.6.0: dependencies: @@ -3164,7 +2917,7 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.8 esutils@2.0.3: {} @@ -3182,7 +2935,7 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 - expect-type@1.2.1: {} + expect-type@1.2.2: {} fast-deep-equal@3.1.3: {} @@ -3192,10 +2945,6 @@ snapshots: fast-levenshtein@2.0.6: {} - fdir@6.4.3(picomatch@4.0.2): - optionalDependencies: - picomatch: 4.0.2 - fdir@6.4.6(picomatch@4.0.2): optionalDependencies: picomatch: 4.0.2 @@ -3227,11 +2976,12 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 - form-data@4.0.2: + form-data@4.0.3: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 + hasown: 2.0.2 mime-types: 2.1.35 fsevents@2.3.2: @@ -3310,14 +3060,14 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.3 - debug: 4.4.0 + debug: 4.4.1 transitivePeerDependencies: - supports-color https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.3 - debug: 4.4.0 + debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -3401,16 +3151,16 @@ snapshots: jsdom@25.0.1: dependencies: - cssstyle: 4.3.0 + cssstyle: 4.6.0 data-urls: 5.0.0 - decimal.js: 10.5.0 - form-data: 4.0.2 + decimal.js: 10.6.0 + form-data: 4.0.3 html-encoding-sniffer: 4.0.0 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.19 - parse5: 7.2.1 + nwsapi: 2.2.20 + parse5: 7.3.0 rrweb-cssom: 0.7.1 saxes: 6.0.0 symbol-tree: 3.2.4 @@ -3420,7 +3170,7 @@ snapshots: whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 - ws: 8.18.1 + ws: 8.18.3 xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil @@ -3489,22 +3239,22 @@ snapshots: lilconfig@3.1.3: {} - lint-staged@15.5.0: + lint-staged@15.5.2: dependencies: chalk: 5.4.1 commander: 13.1.0 - debug: 4.4.0 + debug: 4.4.1 execa: 8.0.1 lilconfig: 3.1.3 - listr2: 8.2.5 + listr2: 8.3.3 micromatch: 4.0.8 pidtree: 0.6.0 string-argv: 0.3.2 - yaml: 2.7.0 + yaml: 2.8.0 transitivePeerDependencies: - supports-color - listr2@8.2.5: + listr2@8.3.3: dependencies: cli-truncate: 4.0.0 colorette: 2.0.20 @@ -3529,8 +3279,6 @@ snapshots: strip-ansi: 7.1.0 wrap-ansi: 9.0.0 - loupe@3.1.3: {} - loupe@3.1.4: {} lru-cache@10.4.3: {} @@ -3539,7 +3287,7 @@ snapshots: magic-string@0.30.17: dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.4 magicast@0.3.5: dependencies: @@ -3574,7 +3322,7 @@ snapshots: minimatch@3.1.2: dependencies: - brace-expansion: 1.1.11 + brace-expansion: 1.1.12 minimatch@9.0.5: dependencies: @@ -3586,12 +3334,12 @@ snapshots: ms@2.1.3: {} - msw@2.10.2: + msw@2.10.3: dependencies: '@bundled-es-modules/cookie': 2.0.1 '@bundled-es-modules/statuses': 1.0.1 '@bundled-es-modules/tough-cookie': 0.1.6 - '@inquirer/confirm': 5.1.12 + '@inquirer/confirm': 5.1.13 '@mswjs/interceptors': 0.39.2 '@open-draft/deferred-promise': 2.2.0 '@open-draft/until': 2.1.0 @@ -3619,7 +3367,7 @@ snapshots: dependencies: path-key: 4.0.0 - nwsapi@2.2.19: {} + nwsapi@2.2.20: {} onetime@6.0.0: dependencies: @@ -3654,9 +3402,9 @@ snapshots: dependencies: callsites: 3.1.0 - parse5@7.2.1: + parse5@7.3.0: dependencies: - entities: 4.5.0 + entities: 6.0.1 path-exists@4.0.0: {} @@ -3675,7 +3423,7 @@ snapshots: pathe@2.0.3: {} - pathval@2.0.0: {} + pathval@2.0.1: {} picocolors@1.1.1: {} @@ -3693,7 +3441,7 @@ snapshots: optionalDependencies: fsevents: 2.3.2 - postcss@8.5.3: + postcss@8.5.6: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 @@ -3705,7 +3453,7 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier@3.5.3: {} + prettier@3.6.2: {} pretty-format@27.5.1: dependencies: @@ -3728,8 +3476,6 @@ snapshots: indent-string: 4.0.0 strip-indent: 3.0.0 - regenerator-runtime@0.14.1: {} - require-directory@2.1.1: {} requires-port@1.0.0: {} @@ -3743,63 +3489,64 @@ snapshots: rfdc@1.4.1: {} - rolldown-vite@6.3.21(esbuild@0.25.1)(yaml@2.7.0): + rolldown-vite@7.0.5(esbuild@0.25.5)(yaml@2.8.0): dependencies: - '@oxc-project/runtime': 0.73.0 + '@oxc-project/runtime': 0.75.0 fdir: 6.4.6(picomatch@4.0.2) lightningcss: 1.30.1 picomatch: 4.0.2 - postcss: 8.5.3 - rolldown: 1.0.0-beta.16 + postcss: 8.5.6 + rolldown: 1.0.0-beta.24 tinyglobby: 0.2.14 optionalDependencies: - esbuild: 0.25.1 + esbuild: 0.25.5 fsevents: 2.3.3 - yaml: 2.7.0 + yaml: 2.8.0 - rolldown@1.0.0-beta.16: + rolldown@1.0.0-beta.24: dependencies: - '@oxc-project/runtime': 0.73.0 - '@oxc-project/types': 0.73.0 - '@rolldown/pluginutils': 1.0.0-beta.16 + '@oxc-project/runtime': 0.75.1 + '@oxc-project/types': 0.75.1 + '@rolldown/pluginutils': 1.0.0-beta.24 ansis: 4.1.0 optionalDependencies: - '@rolldown/binding-darwin-arm64': 1.0.0-beta.16 - '@rolldown/binding-darwin-x64': 1.0.0-beta.16 - '@rolldown/binding-freebsd-x64': 1.0.0-beta.16 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.16 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.16 - '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.16 - '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.16 - '@rolldown/binding-linux-x64-musl': 1.0.0-beta.16 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.16 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.16 - '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.16 - '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.16 - - rollup@4.36.0: - dependencies: - '@types/estree': 1.0.6 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.24 + '@rolldown/binding-darwin-x64': 1.0.0-beta.24 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.24 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.24 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.24 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.24 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.24 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.24 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.24 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.24 + '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.24 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.24 + + rollup@4.44.2: + dependencies: + '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.36.0 - '@rollup/rollup-android-arm64': 4.36.0 - '@rollup/rollup-darwin-arm64': 4.36.0 - '@rollup/rollup-darwin-x64': 4.36.0 - '@rollup/rollup-freebsd-arm64': 4.36.0 - '@rollup/rollup-freebsd-x64': 4.36.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.36.0 - '@rollup/rollup-linux-arm-musleabihf': 4.36.0 - '@rollup/rollup-linux-arm64-gnu': 4.36.0 - '@rollup/rollup-linux-arm64-musl': 4.36.0 - '@rollup/rollup-linux-loongarch64-gnu': 4.36.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.36.0 - '@rollup/rollup-linux-riscv64-gnu': 4.36.0 - '@rollup/rollup-linux-s390x-gnu': 4.36.0 - '@rollup/rollup-linux-x64-gnu': 4.36.0 - '@rollup/rollup-linux-x64-musl': 4.36.0 - '@rollup/rollup-win32-arm64-msvc': 4.36.0 - '@rollup/rollup-win32-ia32-msvc': 4.36.0 - '@rollup/rollup-win32-x64-msvc': 4.36.0 + '@rollup/rollup-android-arm-eabi': 4.44.2 + '@rollup/rollup-android-arm64': 4.44.2 + '@rollup/rollup-darwin-arm64': 4.44.2 + '@rollup/rollup-darwin-x64': 4.44.2 + '@rollup/rollup-freebsd-arm64': 4.44.2 + '@rollup/rollup-freebsd-x64': 4.44.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.44.2 + '@rollup/rollup-linux-arm-musleabihf': 4.44.2 + '@rollup/rollup-linux-arm64-gnu': 4.44.2 + '@rollup/rollup-linux-arm64-musl': 4.44.2 + '@rollup/rollup-linux-loongarch64-gnu': 4.44.2 + '@rollup/rollup-linux-powerpc64le-gnu': 4.44.2 + '@rollup/rollup-linux-riscv64-gnu': 4.44.2 + '@rollup/rollup-linux-riscv64-musl': 4.44.2 + '@rollup/rollup-linux-s390x-gnu': 4.44.2 + '@rollup/rollup-linux-x64-gnu': 4.44.2 + '@rollup/rollup-linux-x64-musl': 4.44.2 + '@rollup/rollup-win32-arm64-msvc': 4.44.2 + '@rollup/rollup-win32-ia32-msvc': 4.44.2 + '@rollup/rollup-win32-x64-msvc': 4.44.2 fsevents: 2.3.3 rrweb-cssom@0.7.1: {} @@ -3826,7 +3573,7 @@ snapshots: sirv@3.0.1: dependencies: - '@polka/url': 1.0.0-next.28 + '@polka/url': 1.0.0-next.29 mrmime: 2.0.1 totalist: 3.0.1 @@ -3896,10 +3643,9 @@ snapshots: symbol-tree@3.2.4: {} - synckit@0.9.2: + synckit@0.11.8: dependencies: - '@pkgr/core': 0.1.2 - tslib: 2.8.1 + '@pkgr/core': 0.2.7 test-exclude@7.0.1: dependencies: @@ -3911,11 +3657,6 @@ snapshots: tinyexec@0.3.2: {} - tinyglobby@0.2.12: - dependencies: - fdir: 6.4.3(picomatch@4.0.2) - picomatch: 4.0.2 - tinyglobby@0.2.14: dependencies: fdir: 6.4.6(picomatch@4.0.2) @@ -3929,11 +3670,11 @@ snapshots: tinyspy@4.0.3: {} - tldts-core@6.1.84: {} + tldts-core@6.1.86: {} - tldts@6.1.84: + tldts@6.1.86: dependencies: - tldts-core: 6.1.84 + tldts-core: 6.1.86 to-regex-range@5.0.1: dependencies: @@ -3950,13 +3691,14 @@ snapshots: tough-cookie@5.1.2: dependencies: - tldts: 6.1.84 + tldts: 6.1.86 - tr46@5.1.0: + tr46@5.1.1: dependencies: punycode: 2.3.1 - tslib@2.8.1: {} + tslib@2.8.1: + optional: true type-check@0.4.0: dependencies: @@ -3977,15 +3719,16 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 - vite-node@3.2.4(lightningcss@1.30.1): + vite-node@3.2.4(lightningcss@1.30.1)(yaml@2.8.0): dependencies: cac: 6.7.14 debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 5.4.14(lightningcss@1.30.1) + vite: 7.0.2(lightningcss@1.30.1)(yaml@2.8.0) transitivePeerDependencies: - '@types/node' + - jiti - less - lightningcss - sass @@ -3994,21 +3737,27 @@ snapshots: - sugarss - supports-color - terser + - tsx + - yaml - vite@5.4.14(lightningcss@1.30.1): + vite@7.0.2(lightningcss@1.30.1)(yaml@2.8.0): dependencies: - esbuild: 0.21.5 - postcss: 8.5.3 - rollup: 4.36.0 + esbuild: 0.25.5 + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.6 + rollup: 4.44.2 + tinyglobby: 0.2.14 optionalDependencies: fsevents: 2.3.3 lightningcss: 1.30.1 + yaml: 2.8.0 - vitest@3.2.4(@vitest/ui@2.1.9)(jsdom@25.0.1)(lightningcss@1.30.1)(msw@2.10.2): + vitest@3.2.4(@vitest/ui@2.1.9)(jsdom@25.0.1)(lightningcss@1.30.1)(msw@2.10.3)(yaml@2.8.0): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(msw@2.10.2)(vite@5.4.14(lightningcss@1.30.1)) + '@vitest/mocker': 3.2.4(msw@2.10.3)(vite@7.0.2(lightningcss@1.30.1)(yaml@2.8.0)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -4016,7 +3765,7 @@ snapshots: '@vitest/utils': 3.2.4 chai: 5.2.0 debug: 4.4.1 - expect-type: 1.2.1 + expect-type: 1.2.2 magic-string: 0.30.17 pathe: 2.0.3 picomatch: 4.0.2 @@ -4026,13 +3775,14 @@ snapshots: tinyglobby: 0.2.14 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 5.4.14(lightningcss@1.30.1) - vite-node: 3.2.4(lightningcss@1.30.1) + vite: 7.0.2(lightningcss@1.30.1)(yaml@2.8.0) + vite-node: 3.2.4(lightningcss@1.30.1)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: '@vitest/ui': 2.1.9(vitest@3.2.4) jsdom: 25.0.1 transitivePeerDependencies: + - jiti - less - lightningcss - msw @@ -4042,6 +3792,8 @@ snapshots: - sugarss - supports-color - terser + - tsx + - yaml w3c-xmlserializer@5.0.0: dependencies: @@ -4057,7 +3809,7 @@ snapshots: whatwg-url@14.2.0: dependencies: - tr46: 5.1.0 + tr46: 5.1.1 webidl-conversions: 7.0.0 which@2.0.2: @@ -4095,7 +3847,7 @@ snapshots: string-width: 7.2.0 strip-ansi: 7.1.0 - ws@8.18.1: {} + ws@8.18.3: {} xml-name-validator@5.0.0: {} @@ -4103,7 +3855,7 @@ snapshots: y18n@5.0.8: {} - yaml@2.7.0: {} + yaml@2.8.0: {} yargs-parser@21.1.1: {} From 3303826b602031aa52dcc3f373f4ad67138beaf5 Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Mon, 7 Jul 2025 21:14:52 +0900 Subject: [PATCH 02/22] =?UTF-8?q?feat:=20main.js=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EB=A1=9C=20=EC=9D=BC=EB=8B=A8=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 + pnpm-lock.yaml | 4 + src/main.js | 1172 ++------------------------------------- src/pages/components.js | 1159 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 1201 insertions(+), 1137 deletions(-) create mode 100644 src/pages/components.js diff --git a/package.json b/package.json index 7b2368941..dbd258765 100644 --- a/package.json +++ b/package.json @@ -50,5 +50,8 @@ "workerDirectory": [ "public" ] + }, + "dependencies": { + "playwright": "^1.53.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee3fc95ca..3e31a5eae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,10 @@ settings: importers: .: + dependencies: + playwright: + specifier: ^1.53.2 + version: 1.53.2 devDependencies: '@eslint/js': specifier: ^9.16.0 diff --git a/src/main.js b/src/main.js index 4b055b89d..4133a950f 100644 --- a/src/main.js +++ b/src/main.js @@ -1,1147 +1,45 @@ +import { LoadingContent, ProductContent, toast } from "./pages/components.js"; + const enableMocking = () => import("./mocks/browser.js").then(({ worker }) => worker.start({ onUnhandledRequest: "bypass", }), ); - +let state = { + isLoading: false, + error: null, + products: [], +}; +async function getProducts() { + state.isLoading = true; + render(); + try { + const response = await fetch("/api/products"); + if (!response.ok) throw new Error("네트워크 오류"); + const data = await response.json(); + state.products = data.products; + state.error = null; + } catch (error) { + state.error = error.message; + } + state.isLoading = false; + render(); +} +function render() { + const root = document.getElementById("root"); + if (state.isLoading) { + root.innerHTML = LoadingContent(); + } + if (state.products) { + root.innerHTML = ProductContent(); + } + if (state.error) { + root.innerHTML = toast("error"); + } +} function main() { - const 상품목록_레이아웃_로딩 = ` -
-
-
-
-

- 쇼핑몰 -

-
- - -
-
-
-
-
- -
- -
-
- -
- - - -
-
-
- -
- -
-
- - -
- -
-
카테고리 로딩 중...
-
- -
- -
- -
- - -
- -
- - -
-
-
-
- -
-
- -
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
- - - - - 상품을 불러오는 중... -
-
-
-
-
-
-
-

© 2025 항해플러스 프론트엔드 쇼핑몰

-
-
-
- `; - - const 상품목록_레이아웃_로딩완료 = ` -
-
-
-
-

- 쇼핑몰 -

-
- - -
-
-
-
-
- -
- -
-
- -
- - - -
-
-
- -
- -
-
- - -
- -
- - -
- -
- -
- -
- - -
- -
- - -
-
-
-
- -
-
- -
- 총 340개의 상품 -
- -
-
- -
- PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 -
- -
-
-

- PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 -

-

-

- 220원 -

-
- - -
-
-
- -
- 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 -
- -
-
-

- 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 -

-

이지웨이건축자재

-

- 230원 -

-
- - -
-
-
- -
- 모든 상품을 확인했습니다 -
-
-
-
-
-
-

© 2025 항해플러스 프론트엔드 쇼핑몰

-
-
-
- `; - - const 상품목록_레이아웃_카테고리_1Depth = ` -
- -
- -
-
- -
- - - -
-
-
- - -
- - -
-
- - > -
-
-
- - - -
-
-
- - -
- -
- - -
- -
- - -
-
-
-
-
- `; - - const 상품목록_레이아웃_카테고리_2Depth = ` -
- -
- -
-
- -
- - - -
-
-
- - -
- - -
-
- - >>주방용품 -
-
-
- - - -
-
-
- - -
- -
- - -
- -
- - -
-
-
-
-
- `; - - const 토스트 = ` -
-
-
- - - -
-

장바구니에 추가되었습니다

- -
- -
-
- - - -
-

선택된 상품들이 삭제되었습니다

- -
- -
-
- - - -
-

오류가 발생했습니다.

- -
-
- `; - - const 장바구니_비어있음 = ` -
-
- -
-

- - - - 장바구니 -

- - -
- - -
- -
-
-
- - - -
-

장바구니가 비어있습니다

-

원하는 상품을 담아보세요!

-
-
-
-
-
- `; - - const 장바구니_선택없음 = ` -
-
- -
-

- - - - 장바구니 - (2) -

- -
- -
- -
- -
- -
-
-
- - - -
- PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 -
- -
-

- PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 -

-

- 220원 -

- -
- - - -
-
- -
-

- 440원 -

- -
-
-
- - - -
- 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 -
- -
-

- 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 -

-

- 230원 -

- -
- - - -
-
- -
-

- 230원 -

- -
-
-
-
-
- -
- - -
- 총 금액 - 670원 -
- -
-
- - -
-
-
-
-
- `; - - const 장바구니_선택있음 = ` -
-
- -
-

- - - - 장바구니 - (2) -

- -
- -
- -
- -
- -
-
-
- - - -
- PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 -
- -
-

- PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 -

-

- 220원 -

- -
- - - -
-
- -
-

- 440원 -

- -
-
-
- - - -
- 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 -
- -
-

- 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 -

-

- 230원 -

- -
- - - -
-
- -
-

- 230원 -

- -
-
-
-
-
- -
- -
- 선택한 상품 (1개) - 440원 -
- -
- 총 금액 - 670원 -
- -
- -
- - -
-
-
-
-
- `; - - const 상세페이지_로딩 = ` -
-
-
-
-
- -

상품 상세

-
-
- - -
-
-
-
-
-
-
-
-

상품 정보를 불러오는 중...

-
-
-
-
-
-

© 2025 항해플러스 프론트엔드 쇼핑몰

-
-
-
- `; - - const 상세페이지_로딩완료 = ` -
-
-
-
-
- -

상품 상세

-
-
- - -
-
-
-
-
- - - -
- -
-
- PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 -
- -
-

-

PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장

- -
-
- - - - - - - - - - - - - - - -
- 4.0 (749개 리뷰) -
- -
- 220원 -
- -
- 재고 107개 -
- -
- PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장에 대한 상세 설명입니다. 브랜드의 우수한 품질을 자랑하는 상품으로, 고객 만족도가 높은 제품입니다. -
-
-
- -
-
- 수량 -
- - - -
-
- - -
-
- -
- -
- -
-
-

관련 상품

-

같은 카테고리의 다른 상품들

-
-
-
- - -
-
-
-
-
-
-

© 2025 항해플러스 프론트엔드 쇼핑몰

-
-
-
- `; - - const _404_ = ` -
-
- - - - - - - - - - - - - 404 - - - - - - - - - 페이지를 찾을 수 없습니다 - - - - - - 홈으로 -
-
- `; - - document.body.innerHTML = ` - ${상품목록_레이아웃_로딩} -
- ${상품목록_레이아웃_로딩완료} -
- ${상품목록_레이아웃_카테고리_1Depth} -
- ${상품목록_레이아웃_카테고리_2Depth} -
- ${토스트} -
- ${장바구니_비어있음} -
- ${장바구니_선택없음} -
- ${장바구니_선택있음} -
- ${상세페이지_로딩} -
- ${상세페이지_로딩완료} -
- ${_404_} - `; + getProducts(); } // 애플리케이션 시작 diff --git a/src/pages/components.js b/src/pages/components.js new file mode 100644 index 000000000..6ace11ae6 --- /dev/null +++ b/src/pages/components.js @@ -0,0 +1,1159 @@ +function toastContent(action) { + switch (action) { + case "addCart": + return `
+
+ + + +
+

장바구니에 추가되었습니다

+ +
`; + + case "deleteCart": + return `
+
+ + + +
+

선택된 상품들이 삭제되었습니다

+ +
`; + case "error": + return `
+
+ + + +
+

오류가 발생했습니다.

+ +
`; + } +} +const actions = ["error", "deleteCart", "addCart"]; + +export function toast(action) { + if (!actions.includes(action) || !action) return; + return `
+ ${toastContent(action)} +
`; +} + +export function ProductContent() { + return ` +
+
+
+
+

+ 쇼핑몰 +

+
+ + +
+
+
+
+
+ +
+ +
+
+ +
+ + + +
+
+
+ +
+ +
+
+ + +
+ +
+ + +
+ +
+ +
+ +
+ + +
+ +
+ + +
+
+
+
+ +
+
+ +
340개의 상품
+ +
+
+ +
+ PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 +
+ +
+
+

+ PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 +

+

+

220원

+
+ + +
+
+
+ +
+ 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 +
+ +
+
+

+ 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 +

+

이지웨이건축자재

+

230원

+
+ + +
+
+
+ +
모든 상품을 확인했습니다
+
+
+
+
+
+

© 2025 항해플러스 프론트엔드 쇼핑몰

+
+
+
+ `; +} +export function LoadingContent() { + return ` +
+
+
+
+

+ 쇼핑몰 +

+
+ + +
+
+
+
+
+ +
+ +
+
+ +
+ + + +
+
+
+ +
+ +
+
+ + +
+ +
+
카테고리 로딩 중...
+
+ +
+ +
+ +
+ + +
+ +
+ + +
+
+
+
+ +
+
+ +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + 상품을 불러오는 중... +
+
+
+
+
+
+
+

© 2025 항해플러스 프론트엔드 쇼핑몰

+
+
+
+ `; +} +export function LayoutCategory1Depth() { + return ` +
+ +
+ +
+
+ +
+ + + +
+
+
+ +
+ +
+
+ + > +
+
+
+ + + +
+
+
+ +
+ +
+ + +
+ +
+ + +
+
+
+
+
+ `; +} + +export function LayoutCategory2Depth() { + return ` +
+ +
+ +
+
+ +
+ + + +
+
+
+ +
+ +
+
+ + >>주방용품 +
+
+
+ + + +
+
+
+ +
+ +
+ + +
+ +
+ + +
+
+
+
+
+ `; +} +export function EmptyCartContent() { + return ` +
+
+ +
+

+ + + + 장바구니 +

+ +
+ +
+ +
+
+
+ + + +
+

장바구니가 비어있습니다

+

원하는 상품을 담아보세요!

+
+
+
+
+
+ `; +} +export function NoSelectionCart() { + return ` +
+
+ +
+

+ + + + 장바구니 + (2) +

+ +
+ +
+ +
+ +
+ +
+
+
+ + + +
+ PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 +
+ +
+

+ PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 +

+

+ 220원 +

+ +
+ + + +
+
+ +
+

+ 440원 +

+ +
+
+
+ + + +
+ 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 +
+ +
+

+ 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 +

+

+ 230원 +

+ +
+ + + +
+
+ +
+

+ 230원 +

+ +
+
+
+
+
+ +
+ + +
+ 총 금액 + 670원 +
+ +
+
+ + +
+
+
+
+
+ `; +} +export function CartContent() { + return ` +
+
+ +
+

+ + + + 장바구니 + (2) +

+ +
+ +
+ +
+ +
+ +
+
+
+ + + +
+ PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 +
+ +
+

+ PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 +

+

+ 220원 +

+ +
+ + + +
+
+ +
+

+ 440원 +

+ +
+
+
+ + + +
+ 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 +
+ +
+

+ 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 +

+

+ 230원 +

+ +
+ + + +
+
+ +
+

+ 230원 +

+ +
+
+
+
+
+ +
+ +
+ 선택한 상품 (1개) + 440원 +
+ +
+ 총 금액 + 670원 +
+ +
+ +
+ + +
+
+
+
+
+ `; +} +export function DetailPageLoading() { + return ` +
+
+
+
+
+ +

상품 상세

+
+
+ + +
+
+
+
+
+
+
+
+

상품 정보를 불러오는 중...

+
+
+
+
+
+

© 2025 항해플러스 프론트엔드 쇼핑몰

+
+
+
+ `; +} +export function DetailPageContent() { + return ` +
+
+
+
+
+ +

상품 상세

+
+
+ + +
+
+
+
+
+ + + +
+ +
+
+ PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 +
+ +
+

+

PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장

+ +
+
+ + + + + + + + + + + + + + + +
+ 4.0 (749개 리뷰) +
+ +
+ 220원 +
+ +
+ 재고 107개 +
+ +
+ PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장에 대한 상세 설명입니다. 브랜드의 우수한 품질을 자랑하는 상품으로, 고객 만족도가 높은 제품입니다. +
+
+
+ +
+
+ 수량 +
+ + + +
+
+ + +
+
+ +
+ +
+ +
+
+

관련 상품

+

같은 카테고리의 다른 상품들

+
+
+
+ + +
+
+
+
+
+
+

© 2025 항해플러스 프론트엔드 쇼핑몰

+
+
+
+ `; +} +export function ErrorContent() { + return ` +
+
+ + + + + + + + + + + + 404 + + + + + + + 페이지를 찾을 수 없습니다 + + + + 홈으로 +
+
+ `; +} From ec3ae3d4dccfdb13c4fdf28ff0bdb093205adbf9 Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Mon, 7 Jul 2025 22:25:02 +0900 Subject: [PATCH 03/22] =?UTF-8?q?feat:=20=EC=83=81=ED=92=88=20=EC=BB=A8?= =?UTF-8?q?=ED=85=90=EC=B8=A0,=20=EC=83=81=ED=92=88=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EB=85=B8=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.js | 9 ++-- src/pages/components.js | 107 ++++++++++++++-------------------------- 2 files changed, 44 insertions(+), 72 deletions(-) diff --git a/src/main.js b/src/main.js index 4133a950f..690c2104d 100644 --- a/src/main.js +++ b/src/main.js @@ -6,11 +6,13 @@ const enableMocking = () => onUnhandledRequest: "bypass", }), ); + let state = { isLoading: false, error: null, products: [], }; + async function getProducts() { state.isLoading = true; render(); @@ -26,18 +28,19 @@ async function getProducts() { state.isLoading = false; render(); } + function render() { const root = document.getElementById("root"); if (state.isLoading) { root.innerHTML = LoadingContent(); - } - if (state.products) { - root.innerHTML = ProductContent(); + } else if (state.products) { + root.innerHTML = ProductContent(state.products); } if (state.error) { root.innerHTML = toast("error"); } } + function main() { getProducts(); } diff --git a/src/pages/components.js b/src/pages/components.js index 6ace11ae6..5e2fd795f 100644 --- a/src/pages/components.js +++ b/src/pages/components.js @@ -53,9 +53,40 @@ export function toast(action) { ${toastContent(action)} `; } - -export function ProductContent() { - return ` +export function productItem(product) { + return /* HTML */ `
+ +
+ ${product.title || +
+ +
+
+

${product.title || ""}

+

+

${product.lprice || 0}원

+
+ + +
+
`; +} +export function ProductContent(products) { + return /* HTML */ `
@@ -172,75 +203,13 @@ export function ProductContent() {
-
340개의 상품
+
+ 총 ${products?.length || 0}개의 상품 +
-
- -
- PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 -
- -
-
-

- PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 -

-

-

220원

-
- - -
-
-
- -
- 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 -
- -
-
-

- 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 -

-

이지웨이건축자재

-

230원

-
- - -
-
+ ${products.map((product) => productItem(product)).join("")}
-
모든 상품을 확인했습니다
From eefdf17fe0857211cced12cc68a1dc4c530ad4bd Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Tue, 8 Jul 2025 00:29:48 +0900 Subject: [PATCH 04/22] =?UTF-8?q?feat:=20debounce=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80,=20=EA=B2=80=EC=83=89=20=EB=B0=8F=20?= =?UTF-8?q?=EC=83=81=ED=92=88=EC=88=98=20=EC=84=A0=ED=83=9D=EA=B5=AC?= =?UTF-8?q?=ED=98=84,=20=EB=AC=B4=ED=95=9C=20=EC=8A=A4=ED=81=AC=EB=A1=A4?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.js | 67 ++++++++++++++++++++++++++++++++++++----- src/pages/components.js | 20 ++++++------ src/utils.js | 7 +++++ 3 files changed, 76 insertions(+), 18 deletions(-) create mode 100644 src/utils.js diff --git a/src/main.js b/src/main.js index 690c2104d..5d09b3bf2 100644 --- a/src/main.js +++ b/src/main.js @@ -1,5 +1,5 @@ import { LoadingContent, ProductContent, toast } from "./pages/components.js"; - +import { debounce } from "./utils.js"; const enableMocking = () => import("./mocks/browser.js").then(({ worker }) => worker.start({ @@ -7,26 +7,39 @@ const enableMocking = () => }), ); +let searchState = { + page: 1, + limit: 20, + search: "", + sort: "price_asc", + category1: "", + category2: "", +}; + let state = { isLoading: false, + isFetching: false, error: null, products: [], }; -async function getProducts() { +async function getProducts(searchState) { state.isLoading = true; render(); + const params = new URLSearchParams(searchState); try { - const response = await fetch("/api/products"); + const response = await fetch(`/api/products?${params.toString()}`); if (!response.ok) throw new Error("네트워크 오류"); const data = await response.json(); - state.products = data.products; + state.products = [...state.products, ...data.products]; + console.log(data.products); state.error = null; } catch (error) { state.error = error.message; + } finally { + state.isLoading = false; + render(); } - state.isLoading = false; - render(); } function render() { @@ -34,15 +47,53 @@ function render() { if (state.isLoading) { root.innerHTML = LoadingContent(); } else if (state.products) { - root.innerHTML = ProductContent(state.products); + root.innerHTML = ProductContent(state.products, searchState); } if (state.error) { root.innerHTML = toast("error"); } } +function setEventListener() { + const debouncedGetProducts = debounce(() => { + getProducts(searchState); + }, 1500); + + document.getElementById("root").addEventListener("input", (e) => { + if (e.target && e.target.id === "search-input") { + searchState.search = e.target.value; + debouncedGetProducts(); + } + }); + + document.getElementById("root").addEventListener("change", (e) => { + if (e.target && e.target.id === "limit-select") { + searchState.limit = parseInt(e.target.value, 10); + getProducts(searchState); + } + }); + + document.getElementById("root").addEventListener("change", (e) => { + if (e.target && e.target.id === "sort-select") { + searchState.sort = e.target.value; + getProducts(searchState); + } + }); + + window.addEventListener("scroll", () => { + const scrollTop = window.scrollY; // 현재 스크롤 위치 + const windowHeight = window.innerHeight; // 브라우저 창 높이 + const documentHeight = document.body.scrollHeight; // 전체 문서 높이 + if (scrollTop + windowHeight >= documentHeight - 10 && !state.isFetching) { + state.isFetching = true; + searchState.page = searchState.page + 1; + getProducts(searchState); + } + }); +} function main() { - getProducts(); + getProducts(searchState); + setEventListener(); } // 애플리케이션 시작 diff --git a/src/pages/components.js b/src/pages/components.js index 5e2fd795f..7321ea004 100644 --- a/src/pages/components.js +++ b/src/pages/components.js @@ -85,7 +85,7 @@ export function productItem(product) {
`; } -export function ProductContent(products) { +export function ProductContent(products, searchParams) { return /* HTML */ `
@@ -124,7 +124,7 @@ export function ProductContent(products) { type="text" id="search-input" placeholder="상품명을 검색해보세요..." - value="" + value="${searchParams.search}" class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" /> @@ -176,10 +176,10 @@ export function ProductContent(products) { id="limit-select" class="text-sm border border-gray-300 rounded px-2 py-1 focus:ring-1 focus:ring-blue-500 focus:border-blue-500" > - - - - + + + +
@@ -190,10 +190,10 @@ export function ProductContent(products) { class="text-sm border border-gray-300 rounded px-2 py-1 focus:ring-1 focus:ring-blue-500 focus:border-blue-500" > - - - - + + + + diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 000000000..4ba379730 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,7 @@ +export function debounce(fn, delay) { + let timer = null; + return function (...args) { + clearTimeout(timer); + timer = setTimeout(() => fn.apply(this, args), delay); + }; +} From c91787331fe8276cb80af1441fc9c370c93f2b02 Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Tue, 8 Jul 2025 00:55:00 +0900 Subject: [PATCH 05/22] =?UTF-8?q?refactor:=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=20state,=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=ED=95=84=ED=84=B0=20state=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.js | 55 ++++++++++++++++++++++++++--------------- src/pages/components.js | 2 +- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/main.js b/src/main.js index 5d09b3bf2..95fafc8d4 100644 --- a/src/main.js +++ b/src/main.js @@ -7,13 +7,20 @@ const enableMocking = () => }), ); -let searchState = { - page: 1, +let pageState = { + hasNext: false, + hasPrev: false, limit: 20, - search: "", - sort: "price_asc", + page: 1, + total: 0, + totalPages: 0, +}; + +let filterState = { category1: "", category2: "", + search: "", + sort: "price_asc", }; let state = { @@ -31,8 +38,11 @@ async function getProducts(searchState) { const response = await fetch(`/api/products?${params.toString()}`); if (!response.ok) throw new Error("네트워크 오류"); const data = await response.json(); - state.products = [...state.products, ...data.products]; - console.log(data.products); + const { products, pagination, filters } = data; + state.products = [...state.products, ...products]; + pageState = { ...pageState, ...pagination }; + filterState = { ...filterState, ...filters }; + // console.log(data); state.error = null; } catch (error) { state.error = error.message; @@ -47,52 +57,57 @@ function render() { if (state.isLoading) { root.innerHTML = LoadingContent(); } else if (state.products) { - root.innerHTML = ProductContent(state.products, searchState); + root.innerHTML = ProductContent(state.products, { ...filterState, ...pageState }); } if (state.error) { root.innerHTML = toast("error"); } } + function setEventListener() { const debouncedGetProducts = debounce(() => { - getProducts(searchState); + getProducts(filterState); }, 1500); document.getElementById("root").addEventListener("input", (e) => { if (e.target && e.target.id === "search-input") { - searchState.search = e.target.value; + filterState.search = e.target.value; debouncedGetProducts(); } }); document.getElementById("root").addEventListener("change", (e) => { if (e.target && e.target.id === "limit-select") { - searchState.limit = parseInt(e.target.value, 10); - getProducts(searchState); + pageState.limit = parseInt(e.target.value, 10); + getProducts(pageState); } }); document.getElementById("root").addEventListener("change", (e) => { if (e.target && e.target.id === "sort-select") { - searchState.sort = e.target.value; - getProducts(searchState); + filterState.sort = e.target.value; + getProducts(filterState); } }); window.addEventListener("scroll", () => { - const scrollTop = window.scrollY; // 현재 스크롤 위치 - const windowHeight = window.innerHeight; // 브라우저 창 높이 - const documentHeight = document.body.scrollHeight; // 전체 문서 높이 - if (scrollTop + windowHeight >= documentHeight - 10 && !state.isFetching) { + const scrollTop = window.scrollY; + const windowHeight = window.innerHeight; + const documentHeight = document.body.scrollHeight; + if ( + scrollTop + windowHeight >= documentHeight - 10 && + !state.isFetching && + pageState.hasNext // 다음 페이지가 있을 때만 요청 + ) { state.isFetching = true; - searchState.page = searchState.page + 1; - getProducts(searchState); + pageState.page += 1; // pageState에 page가 있으므로 여기서 증가 + getProducts(); } }); } function main() { - getProducts(searchState); + getProducts(filterState); setEventListener(); } diff --git a/src/pages/components.js b/src/pages/components.js index 7321ea004..c7d362d13 100644 --- a/src/pages/components.js +++ b/src/pages/components.js @@ -204,7 +204,7 @@ export function ProductContent(products, searchParams) {
- 총 ${products?.length || 0}개의 상품 + 총 ${searchParams.total || 0}개의 상품
From 7aefe147b04bd1d9cd07bd51abb53ace84f33c71 Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Tue, 8 Jul 2025 21:56:45 +0900 Subject: [PATCH 06/22] =?UTF-8?q?feat:=20=EB=AC=B4=ED=95=9C=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=20=EC=B6=94=EA=B0=80,=20getProduct=EC=8B=A4?= =?UTF-8?q?=ED=96=89=ED=95=A0=20=EB=95=8C=20=EA=B2=80=EC=83=89=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20state=20=EA=B0=80=EC=A0=B8=EC=99=80=EC=84=9C=20sear?= =?UTF-8?q?chParams=EB=A1=9C=20=ED=99=9C=EC=9A=A9...=20=EC=97=94=ED=84=B0?= =?UTF-8?q?=20=EA=B2=80=EC=83=89=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.js | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/main.js b/src/main.js index 95fafc8d4..50688e9bd 100644 --- a/src/main.js +++ b/src/main.js @@ -30,10 +30,15 @@ let state = { products: [], }; -async function getProducts(searchState) { +async function getProducts() { + const { limit, page } = pageState; + const { search, sort, category1, category2 } = filterState; + const searchParams = { limit, page, search, sort, category1, category2 }; state.isLoading = true; render(); - const params = new URLSearchParams(searchState); + const params = new URLSearchParams( + Object.entries(searchParams).filter(([k, v]) => v !== undefined && v !== null && k), + ); try { const response = await fetch(`/api/products?${params.toString()}`); if (!response.ok) throw new Error("네트워크 오류"); @@ -42,12 +47,13 @@ async function getProducts(searchState) { state.products = [...state.products, ...products]; pageState = { ...pageState, ...pagination }; filterState = { ...filterState, ...filters }; - // console.log(data); + // console.log("data:", data); state.error = null; } catch (error) { state.error = error.message; } finally { state.isLoading = false; + state.isFetching = false; render(); } } @@ -66,27 +72,42 @@ function render() { function setEventListener() { const debouncedGetProducts = debounce(() => { - getProducts(filterState); + getProducts(); }, 1500); document.getElementById("root").addEventListener("input", (e) => { if (e.target && e.target.id === "search-input") { filterState.search = e.target.value; + pageState.page = 1; + state.products = []; debouncedGetProducts(); } }); + document.getElementById("root").addEventListener("keydown", (e) => { + if (e.target && e.target.id === "search-input" && e.key === "Enter") { + e.preventDefault(); + filterState.search = e.target.value; + pageState.page = 1; + state.products = []; + getProducts(); + } + }); document.getElementById("root").addEventListener("change", (e) => { if (e.target && e.target.id === "limit-select") { pageState.limit = parseInt(e.target.value, 10); - getProducts(pageState); + pageState.page = 1; + state.products = []; + getProducts(); } }); document.getElementById("root").addEventListener("change", (e) => { if (e.target && e.target.id === "sort-select") { filterState.sort = e.target.value; - getProducts(filterState); + pageState.page = 1; + state.products = []; + getProducts(); } }); @@ -107,7 +128,7 @@ function setEventListener() { } function main() { - getProducts(filterState); + getProducts(); setEventListener(); } From 02e9cd81d22e3cf8f103c9a9966ca982f1906f42 Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Tue, 8 Jul 2025 22:54:47 +0900 Subject: [PATCH 07/22] =?UTF-8?q?fix:=20=EC=83=81=ED=92=88=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC,=20state.products=EA=B0=90=EC=A7=80=EB=90=98?= =?UTF-8?q?=EB=A9=B4=20=EC=83=9D=EC=84=B1=ED=95=98=EB=8F=84=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.js | 24 ++++++++++++++++-------- src/pages/components.js | 18 ++++++++++-------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/main.js b/src/main.js index 50688e9bd..4f58e5536 100644 --- a/src/main.js +++ b/src/main.js @@ -1,4 +1,4 @@ -import { LoadingContent, ProductContent, toast } from "./pages/components.js"; +import { LoadingContent, ProductContent, productItem, toast } from "./pages/components.js"; import { debounce } from "./utils.js"; const enableMocking = () => import("./mocks/browser.js").then(({ worker }) => @@ -62,8 +62,15 @@ function render() { const root = document.getElementById("root"); if (state.isLoading) { root.innerHTML = LoadingContent(); - } else if (state.products) { - root.innerHTML = ProductContent(state.products, { ...filterState, ...pageState }); + } else { + // 로딩 완료 + root.innerHTML = ProductContent({ ...filterState, ...pageState }); + const productGrid = document.getElementById("products-grid"); + if (state.products && productGrid) { + // console.time("productItem"); + productGrid.innerHTML = productItem(state.products); + // console.timeEnd("productItem"); + } } if (state.error) { root.innerHTML = toast("error"); @@ -73,22 +80,23 @@ function render() { function setEventListener() { const debouncedGetProducts = debounce(() => { getProducts(); - }, 1500); + }, 1000); document.getElementById("root").addEventListener("input", (e) => { if (e.target && e.target.id === "search-input") { filterState.search = e.target.value; - pageState.page = 1; state.products = []; + state.page = 1; debouncedGetProducts(); } }); + document.getElementById("root").addEventListener("keydown", (e) => { if (e.target && e.target.id === "search-input" && e.key === "Enter") { e.preventDefault(); filterState.search = e.target.value; - pageState.page = 1; state.products = []; + state.page = 1; getProducts(); } }); @@ -96,8 +104,8 @@ function setEventListener() { document.getElementById("root").addEventListener("change", (e) => { if (e.target && e.target.id === "limit-select") { pageState.limit = parseInt(e.target.value, 10); - pageState.page = 1; state.products = []; + state.page = 1; getProducts(); } }); @@ -105,8 +113,8 @@ function setEventListener() { document.getElementById("root").addEventListener("change", (e) => { if (e.target && e.target.id === "sort-select") { filterState.sort = e.target.value; - pageState.page = 1; state.products = []; + state.page = 1; getProducts(); } }); diff --git a/src/pages/components.js b/src/pages/components.js index c7d362d13..1dee8c4c4 100644 --- a/src/pages/components.js +++ b/src/pages/components.js @@ -53,10 +53,12 @@ export function toast(action) { ${toastContent(action)}
`; } -export function productItem(product) { - return /* HTML */ `
`
@@ -83,9 +85,11 @@ export function productItem(product) { 장바구니 담기
-
`; +
`, + ) + .join(""); } -export function ProductContent(products, searchParams) { +export function ProductContent(searchParams) { return /* HTML */ `
@@ -207,9 +211,7 @@ export function ProductContent(products, searchParams) { 총 ${searchParams.total || 0}개의 상품
-
- ${products.map((product) => productItem(product)).join("")} -
+
모든 상품을 확인했습니다
From 6dfc1294da3e29ce85c380eb856a092ca8697c57 Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Wed, 9 Jul 2025 02:11:14 +0900 Subject: [PATCH 08/22] =?UTF-8?q?test:=20=EB=9D=BC=EC=9A=B0=ED=84=B0=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.js | 8 +++++--- src/router/handler.js | 16 ++++++++++++++++ src/router/index.js | 12 ++++++++++++ src/router/routes.js | 8 ++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 src/router/handler.js create mode 100644 src/router/index.js create mode 100644 src/router/routes.js diff --git a/src/main.js b/src/main.js index 4f58e5536..6f01312b2 100644 --- a/src/main.js +++ b/src/main.js @@ -1,5 +1,7 @@ import { LoadingContent, ProductContent, productItem, toast } from "./pages/components.js"; +import { router } from "./router/index.js"; import { debounce } from "./utils.js"; + const enableMocking = () => import("./mocks/browser.js").then(({ worker }) => worker.start({ @@ -35,7 +37,7 @@ async function getProducts() { const { search, sort, category1, category2 } = filterState; const searchParams = { limit, page, search, sort, category1, category2 }; state.isLoading = true; - render(); + // render(); const params = new URLSearchParams( Object.entries(searchParams).filter(([k, v]) => v !== undefined && v !== null && k), ); @@ -59,6 +61,7 @@ async function getProducts() { } function render() { + console.log("render start"); const root = document.getElementById("root"); if (state.isLoading) { root.innerHTML = LoadingContent(); @@ -67,9 +70,7 @@ function render() { root.innerHTML = ProductContent({ ...filterState, ...pageState }); const productGrid = document.getElementById("products-grid"); if (state.products && productGrid) { - // console.time("productItem"); productGrid.innerHTML = productItem(state.products); - // console.timeEnd("productItem"); } } if (state.error) { @@ -136,6 +137,7 @@ function setEventListener() { } function main() { + router(); getProducts(); setEventListener(); } diff --git a/src/router/handler.js b/src/router/handler.js new file mode 100644 index 000000000..61e7b2941 --- /dev/null +++ b/src/router/handler.js @@ -0,0 +1,16 @@ +import { LoadingContent, NotFoundContent, ProductContent } from "../pages/components"; + +export function renderLoadingContent() { + const root = document.getElementById("root"); + root.innerHTML = LoadingContent(); +} + +export function renderProductContent() { + const root = document.getElementById("root"); + root.innerHTML = ProductContent(); +} + +export function renderNotFound() { + const root = document.getElementById("root"); + root.innerHTML = NotFoundContent(); +} diff --git a/src/router/index.js b/src/router/index.js new file mode 100644 index 000000000..b1aad194b --- /dev/null +++ b/src/router/index.js @@ -0,0 +1,12 @@ +import { defaultRoute, routes } from "./routes.js"; + +export function router() { + const path = window.location.pathname; + const routeHandler = routes[path] || defaultRoute; + routeHandler(); +} + +export function goTo(path) { + window.history.pushState({}, "", path); + window.dispatchEvent(new Event("popstate")); +} diff --git a/src/router/routes.js b/src/router/routes.js new file mode 100644 index 000000000..eefb16c2f --- /dev/null +++ b/src/router/routes.js @@ -0,0 +1,8 @@ +import { renderLoadingContent, renderNotFound } from "./handler"; + +export const routes = { + "/": renderLoadingContent, + // "/products": renderProducts, +}; + +export const defaultRoute = renderNotFound; From 298fc082992df1941b35d9f00cba53f8b7f2d7d7 Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Wed, 9 Jul 2025 23:07:57 +0900 Subject: [PATCH 09/22] =?UTF-8?q?refactor:=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=B6=84=EB=A6=AC,?= =?UTF-8?q?=20=EB=9D=BC=EC=9A=B0=ED=84=B0=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/cartPage.js | 349 +++++++++++++++++++++++++++++++++ src/pages/notFoundPage.js | 31 +++ src/pages/productDetailPage.js | 206 +++++++++++++++++++ src/pages/productPage.js | 339 ++++++++++++++++++++++++++++++++ src/router/handler.js | 16 -- src/router/index.js | 12 -- src/router/routes.js | 8 - src/routes/router.js | 34 ++++ 8 files changed, 959 insertions(+), 36 deletions(-) create mode 100644 src/pages/cartPage.js create mode 100644 src/pages/notFoundPage.js create mode 100644 src/pages/productDetailPage.js create mode 100644 src/pages/productPage.js delete mode 100644 src/router/handler.js delete mode 100644 src/router/index.js delete mode 100644 src/router/routes.js create mode 100644 src/routes/router.js diff --git a/src/pages/cartPage.js b/src/pages/cartPage.js new file mode 100644 index 000000000..7b6e59010 --- /dev/null +++ b/src/pages/cartPage.js @@ -0,0 +1,349 @@ +export function emptyCartContent() { + return ` +
+
+ +
+

+ + + + 장바구니 +

+ +
+ +
+ +
+
+
+ + + +
+

장바구니가 비어있습니다

+

원하는 상품을 담아보세요!

+
+
+
+
+
+ `; +} +export function noSelectionCart() { + return ` +
+
+ +
+

+ + + + 장바구니 + (2) +

+ +
+ +
+ +
+ +
+ +
+
+
+ + + +
+ PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 +
+ +
+

+ PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 +

+

+ 220원 +

+ +
+ + + +
+
+ +
+

+ 440원 +

+ +
+
+
+ + + +
+ 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 +
+ +
+

+ 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 +

+

+ 230원 +

+ +
+ + + +
+
+ +
+

+ 230원 +

+ +
+
+
+
+
+ +
+ + +
+ 총 금액 + 670원 +
+ +
+
+ + +
+
+
+
+
+ `; +} +export function cartContent() { + return ` +
+
+ +
+

+ + + + 장바구니 + (2) +

+ +
+ +
+ +
+ +
+ +
+
+
+ + + +
+ PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 +
+ +
+

+ PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 +

+

+ 220원 +

+ +
+ + + +
+
+ +
+

+ 440원 +

+ +
+
+
+ + + +
+ 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 +
+ +
+

+ 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 +

+

+ 230원 +

+ +
+ + + +
+
+ +
+

+ 230원 +

+ +
+
+
+
+
+ +
+ +
+ 선택한 상품 (1개) + 440원 +
+ +
+ 총 금액 + 670원 +
+ +
+ +
+ + +
+
+
+
+
+ `; +} diff --git a/src/pages/notFoundPage.js b/src/pages/notFoundPage.js new file mode 100644 index 000000000..322e79c7a --- /dev/null +++ b/src/pages/notFoundPage.js @@ -0,0 +1,31 @@ +export function notFoundPage() { + return ` +
+
+ + + + + + + + + + + + 404 + + + + + + + 페이지를 찾을 수 없습니다 + + + + 홈으로 +
+
+ `; +} diff --git a/src/pages/productDetailPage.js b/src/pages/productDetailPage.js new file mode 100644 index 000000000..be23a5987 --- /dev/null +++ b/src/pages/productDetailPage.js @@ -0,0 +1,206 @@ +export function productDetailPage(isLoading) { + return isLoading ? productDetailLoadingContent() : productDetailContent; +} +function productDetailLoadingContent() { + return ` +
+
+
+
+
+ +

상품 상세

+
+
+ + +
+
+
+
+
+
+
+
+

상품 정보를 불러오는 중...

+
+
+
+
+
+

© 2025 항해플러스 프론트엔드 쇼핑몰

+
+
+
+ `; +} +function productDetailContent() { + return ` +
+
+
+
+
+ +

상품 상세

+
+
+ + +
+
+
+
+
+ + + +
+ +
+
+ PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 +
+ +
+

+

PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장

+ +
+
+ + + + + + + + + + + + + + + +
+ 4.0 (749개 리뷰) +
+ +
+ 220원 +
+ +
+ 재고 107개 +
+ +
+ PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장에 대한 상세 설명입니다. 브랜드의 우수한 품질을 자랑하는 상품으로, 고객 만족도가 높은 제품입니다. +
+
+
+ +
+
+ 수량 +
+ + + +
+
+ + +
+
+ +
+ +
+ +
+
+

관련 상품

+

같은 카테고리의 다른 상품들

+
+
+
+ + +
+
+
+
+
+
+

© 2025 항해플러스 프론트엔드 쇼핑몰

+
+
+
+ `; +} diff --git a/src/pages/productPage.js b/src/pages/productPage.js new file mode 100644 index 000000000..a3b0e6d6e --- /dev/null +++ b/src/pages/productPage.js @@ -0,0 +1,339 @@ +export function productPage(isLoading, searchParams) { + return isLoading ? loadingContent() : productContent(searchParams); +} +export function productItem(products) { + return /* HTML */ products + .map( + (product) => `
+ +
+ ${product.title || +
+ +
+
+

${product.title || ""}

+

+

${product.lprice || 0}원

+
+ + +
+
`, + ) + .join(""); +} +function productContent(searchParams) { + return /* HTML */ ` +
+
+
+
+

+ 쇼핑몰 +

+
+ + +
+
+
+
+
+ +
+ +
+
+ +
+ + + +
+
+
+ +
+ +
+
+ + +
+ +
+ + +
+ +
+ +
+ +
+ + +
+ +
+ + +
+
+
+
+ +
+
+ +
+ 총 ${searchParams.total || 0}개의 상품 +
+ +
+
모든 상품을 확인했습니다
+
+
+
+
+
+

© 2025 항해플러스 프론트엔드 쇼핑몰

+
+
+
+ `; +} + +function loadingContent() { + return ` +
+
+
+
+

+ 쇼핑몰 +

+
+ + +
+
+
+
+
+ +
+ +
+
+ +
+ + + +
+
+
+ +
+ +
+
+ + +
+ +
+
카테고리 로딩 중...
+
+ +
+ +
+ +
+ + +
+ +
+ + +
+
+
+
+ +
+
+ +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + 상품을 불러오는 중... +
+
+
+
+
+
+
+

© 2025 항해플러스 프론트엔드 쇼핑몰

+
+
+
+ `; +} diff --git a/src/router/handler.js b/src/router/handler.js deleted file mode 100644 index 61e7b2941..000000000 --- a/src/router/handler.js +++ /dev/null @@ -1,16 +0,0 @@ -import { LoadingContent, NotFoundContent, ProductContent } from "../pages/components"; - -export function renderLoadingContent() { - const root = document.getElementById("root"); - root.innerHTML = LoadingContent(); -} - -export function renderProductContent() { - const root = document.getElementById("root"); - root.innerHTML = ProductContent(); -} - -export function renderNotFound() { - const root = document.getElementById("root"); - root.innerHTML = NotFoundContent(); -} diff --git a/src/router/index.js b/src/router/index.js deleted file mode 100644 index b1aad194b..000000000 --- a/src/router/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import { defaultRoute, routes } from "./routes.js"; - -export function router() { - const path = window.location.pathname; - const routeHandler = routes[path] || defaultRoute; - routeHandler(); -} - -export function goTo(path) { - window.history.pushState({}, "", path); - window.dispatchEvent(new Event("popstate")); -} diff --git a/src/router/routes.js b/src/router/routes.js deleted file mode 100644 index eefb16c2f..000000000 --- a/src/router/routes.js +++ /dev/null @@ -1,8 +0,0 @@ -import { renderLoadingContent, renderNotFound } from "./handler"; - -export const routes = { - "/": renderLoadingContent, - // "/products": renderProducts, -}; - -export const defaultRoute = renderNotFound; diff --git a/src/routes/router.js b/src/routes/router.js new file mode 100644 index 000000000..f746ab360 --- /dev/null +++ b/src/routes/router.js @@ -0,0 +1,34 @@ +import { cartPage } from "../pages/cartPage"; +import { notFoundPage } from "../pages/notFoundPage"; +import { productDetailPage } from "../pages/productDetailPage"; +import { productPage } from "../pages/productPage"; +const ROUTES = { + MAIN: "/", + PRODUCT: "/product", + PRODUCT_DETAIL: "/product/detail", + CART: "/cart", + ERROR: "/error", +}; +const URL_MAP = { + [ROUTES.MAIN]: productPage, + [ROUTES.PRODUCT]: productPage, + [ROUTES.PRODUCT_DETAIL()]: productDetailPage, + [ROUTES.CART]: cartPage, + [ROUTES.ERROR]: notFoundPage, +}; + +export function navigate(pathname, replace = false) { + history[replace ? "replaceState" : "pushState"](null, "", pathname); + render(); // 라우팅 후 화면 다시 그리기 +} + +export function render() { + const root = document.querySelector("#root"); + let { pathname } = location; + const page = URL_MAP[pathname] || notFoundPage; + root.innerHTML = page(); +} + +window.addEventListener("popstate", () => { + render(); // 뒤로/앞으로 이동 시 렌더링 +}); From e11b0b5572ec11f5d079c30002f22954e474916f Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Wed, 9 Jul 2025 23:30:54 +0900 Subject: [PATCH 10/22] =?UTF-8?q?refactor:=20productPage=EB=82=B4=EB=B6=80?= =?UTF-8?q?=EC=97=90=EC=84=9C=20api=EC=9A=94=EC=B2=AD=20=EB=B0=8F=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=9E=9C=EB=8D=94=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.js | 136 +---- src/pages/cartPage.js | 2 +- src/pages/components.js | 1130 -------------------------------------- src/pages/productPage.js | 90 ++- src/routes/router.js | 7 +- 5 files changed, 83 insertions(+), 1282 deletions(-) delete mode 100644 src/pages/components.js diff --git a/src/main.js b/src/main.js index 6f01312b2..9174c78e5 100644 --- a/src/main.js +++ b/src/main.js @@ -1,7 +1,4 @@ -import { LoadingContent, ProductContent, productItem, toast } from "./pages/components.js"; -import { router } from "./router/index.js"; -import { debounce } from "./utils.js"; - +import { render } from "./routes/router.js"; const enableMocking = () => import("./mocks/browser.js").then(({ worker }) => worker.start({ @@ -9,137 +6,8 @@ const enableMocking = () => }), ); -let pageState = { - hasNext: false, - hasPrev: false, - limit: 20, - page: 1, - total: 0, - totalPages: 0, -}; - -let filterState = { - category1: "", - category2: "", - search: "", - sort: "price_asc", -}; - -let state = { - isLoading: false, - isFetching: false, - error: null, - products: [], -}; - -async function getProducts() { - const { limit, page } = pageState; - const { search, sort, category1, category2 } = filterState; - const searchParams = { limit, page, search, sort, category1, category2 }; - state.isLoading = true; - // render(); - const params = new URLSearchParams( - Object.entries(searchParams).filter(([k, v]) => v !== undefined && v !== null && k), - ); - try { - const response = await fetch(`/api/products?${params.toString()}`); - if (!response.ok) throw new Error("네트워크 오류"); - const data = await response.json(); - const { products, pagination, filters } = data; - state.products = [...state.products, ...products]; - pageState = { ...pageState, ...pagination }; - filterState = { ...filterState, ...filters }; - // console.log("data:", data); - state.error = null; - } catch (error) { - state.error = error.message; - } finally { - state.isLoading = false; - state.isFetching = false; - render(); - } -} - -function render() { - console.log("render start"); - const root = document.getElementById("root"); - if (state.isLoading) { - root.innerHTML = LoadingContent(); - } else { - // 로딩 완료 - root.innerHTML = ProductContent({ ...filterState, ...pageState }); - const productGrid = document.getElementById("products-grid"); - if (state.products && productGrid) { - productGrid.innerHTML = productItem(state.products); - } - } - if (state.error) { - root.innerHTML = toast("error"); - } -} - -function setEventListener() { - const debouncedGetProducts = debounce(() => { - getProducts(); - }, 1000); - - document.getElementById("root").addEventListener("input", (e) => { - if (e.target && e.target.id === "search-input") { - filterState.search = e.target.value; - state.products = []; - state.page = 1; - debouncedGetProducts(); - } - }); - - document.getElementById("root").addEventListener("keydown", (e) => { - if (e.target && e.target.id === "search-input" && e.key === "Enter") { - e.preventDefault(); - filterState.search = e.target.value; - state.products = []; - state.page = 1; - getProducts(); - } - }); - - document.getElementById("root").addEventListener("change", (e) => { - if (e.target && e.target.id === "limit-select") { - pageState.limit = parseInt(e.target.value, 10); - state.products = []; - state.page = 1; - getProducts(); - } - }); - - document.getElementById("root").addEventListener("change", (e) => { - if (e.target && e.target.id === "sort-select") { - filterState.sort = e.target.value; - state.products = []; - state.page = 1; - getProducts(); - } - }); - - window.addEventListener("scroll", () => { - const scrollTop = window.scrollY; - const windowHeight = window.innerHeight; - const documentHeight = document.body.scrollHeight; - if ( - scrollTop + windowHeight >= documentHeight - 10 && - !state.isFetching && - pageState.hasNext // 다음 페이지가 있을 때만 요청 - ) { - state.isFetching = true; - pageState.page += 1; // pageState에 page가 있으므로 여기서 증가 - getProducts(); - } - }); -} - function main() { - router(); - getProducts(); - setEventListener(); + render(); } // 애플리케이션 시작 diff --git a/src/pages/cartPage.js b/src/pages/cartPage.js index 7b6e59010..92ed88af7 100644 --- a/src/pages/cartPage.js +++ b/src/pages/cartPage.js @@ -187,7 +187,7 @@ export function noSelectionCart() { `; } -export function cartContent() { +export function cartPage() { return `
diff --git a/src/pages/components.js b/src/pages/components.js deleted file mode 100644 index 1dee8c4c4..000000000 --- a/src/pages/components.js +++ /dev/null @@ -1,1130 +0,0 @@ -function toastContent(action) { - switch (action) { - case "addCart": - return `
-
- - - -
-

장바구니에 추가되었습니다

- -
`; - - case "deleteCart": - return `
-
- - - -
-

선택된 상품들이 삭제되었습니다

- -
`; - case "error": - return `
-
- - - -
-

오류가 발생했습니다.

- -
`; - } -} -const actions = ["error", "deleteCart", "addCart"]; - -export function toast(action) { - if (!actions.includes(action) || !action) return; - return `
- ${toastContent(action)} -
`; -} -export function productItem(products) { - return /* HTML */ products - .map( - (product) => `
- -
- ${product.title || -
- -
-
-

${product.title || ""}

-

-

${product.lprice || 0}원

-
- - -
-
`, - ) - .join(""); -} -export function ProductContent(searchParams) { - return /* HTML */ ` -
-
-
-
-

- 쇼핑몰 -

-
- - -
-
-
-
-
- -
- -
-
- -
- - - -
-
-
- -
- -
-
- - -
- -
- - -
- -
- -
- -
- - -
- -
- - -
-
-
-
- -
-
- -
- 총 ${searchParams.total || 0}개의 상품 -
- -
-
모든 상품을 확인했습니다
-
-
-
-
-
-

© 2025 항해플러스 프론트엔드 쇼핑몰

-
-
-
- `; -} -export function LoadingContent() { - return ` -
-
-
-
-

- 쇼핑몰 -

-
- - -
-
-
-
-
- -
- -
-
- -
- - - -
-
-
- -
- -
-
- - -
- -
-
카테고리 로딩 중...
-
- -
- -
- -
- - -
- -
- - -
-
-
-
- -
-
- -
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - 상품을 불러오는 중... -
-
-
-
-
-
-
-

© 2025 항해플러스 프론트엔드 쇼핑몰

-
-
-
- `; -} -export function LayoutCategory1Depth() { - return ` -
- -
- -
-
- -
- - - -
-
-
- -
- -
-
- - > -
-
-
- - - -
-
-
- -
- -
- - -
- -
- - -
-
-
-
-
- `; -} - -export function LayoutCategory2Depth() { - return ` -
- -
- -
-
- -
- - - -
-
-
- -
- -
-
- - >>주방용품 -
-
-
- - - -
-
-
- -
- -
- - -
- -
- - -
-
-
-
-
- `; -} -export function EmptyCartContent() { - return ` -
-
- -
-

- - - - 장바구니 -

- -
- -
- -
-
-
- - - -
-

장바구니가 비어있습니다

-

원하는 상품을 담아보세요!

-
-
-
-
-
- `; -} -export function NoSelectionCart() { - return ` -
-
- -
-

- - - - 장바구니 - (2) -

- -
- -
- -
- -
- -
-
-
- - - -
- PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 -
- -
-

- PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 -

-

- 220원 -

- -
- - - -
-
- -
-

- 440원 -

- -
-
-
- - - -
- 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 -
- -
-

- 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 -

-

- 230원 -

- -
- - - -
-
- -
-

- 230원 -

- -
-
-
-
-
- -
- - -
- 총 금액 - 670원 -
- -
-
- - -
-
-
-
-
- `; -} -export function CartContent() { - return ` -
-
- -
-

- - - - 장바구니 - (2) -

- -
- -
- -
- -
- -
-
-
- - - -
- PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 -
- -
-

- PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 -

-

- 220원 -

- -
- - - -
-
- -
-

- 440원 -

- -
-
-
- - - -
- 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 -
- -
-

- 샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이 -

-

- 230원 -

- -
- - - -
-
- -
-

- 230원 -

- -
-
-
-
-
- -
- -
- 선택한 상품 (1개) - 440원 -
- -
- 총 금액 - 670원 -
- -
- -
- - -
-
-
-
-
- `; -} -export function DetailPageLoading() { - return ` -
-
-
-
-
- -

상품 상세

-
-
- - -
-
-
-
-
-
-
-
-

상품 정보를 불러오는 중...

-
-
-
-
-
-

© 2025 항해플러스 프론트엔드 쇼핑몰

-
-
-
- `; -} -export function DetailPageContent() { - return ` -
-
-
-
-
- -

상품 상세

-
-
- - -
-
-
-
-
- - - -
- -
-
- PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 -
- -
-

-

PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장

- -
-
- - - - - - - - - - - - - - - -
- 4.0 (749개 리뷰) -
- -
- 220원 -
- -
- 재고 107개 -
- -
- PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장에 대한 상세 설명입니다. 브랜드의 우수한 품질을 자랑하는 상품으로, 고객 만족도가 높은 제품입니다. -
-
-
- -
-
- 수량 -
- - - -
-
- - -
-
- -
- -
- -
-
-

관련 상품

-

같은 카테고리의 다른 상품들

-
-
-
- - -
-
-
-
-
-
-

© 2025 항해플러스 프론트엔드 쇼핑몰

-
-
-
- `; -} -export function ErrorContent() { - return ` -
-
- - - - - - - - - - - - 404 - - - - - - - 페이지를 찾을 수 없습니다 - - - - 홈으로 -
-
- `; -} diff --git a/src/pages/productPage.js b/src/pages/productPage.js index a3b0e6d6e..2484ae915 100644 --- a/src/pages/productPage.js +++ b/src/pages/productPage.js @@ -1,8 +1,66 @@ -export function productPage(isLoading, searchParams) { - return isLoading ? loadingContent() : productContent(searchParams); +import { render } from "../routes/router"; + +let pageState = { + hasNext: false, + hasPrev: false, + limit: 20, + page: 1, + total: 0, + totalPages: 0, +}; + +let filterState = { + category1: "", + category2: "", + search: "", + sort: "price_asc", +}; + +let state = { + isLoading: false, + isFetching: false, + error: null, + products: [], +}; + +async function getProducts() { + const { limit, page } = pageState; + const { search, sort, category1, category2 } = filterState; + const searchParams = { limit, page, search, sort, category1, category2 }; + state.isLoading = true; + // render(); + const params = new URLSearchParams( + Object.entries(searchParams).filter(([k, v]) => v !== undefined && v !== null && k), + ); + try { + const response = await fetch(`/api/products?${params.toString()}`); + if (!response.ok) throw new Error("네트워크 오류"); + const data = await response.json(); + const { products, pagination, filters } = data; + console.log(data); + + state.products = [...state.products, ...products]; + pageState = { ...pageState, ...pagination }; + filterState = { ...filterState, ...filters }; + // console.log("data:", data); + state.error = null; + } catch (error) { + state.error = error.message; + } finally { + state.isLoading = false; + state.isFetching = false; + render(); + } +} + +export function productPage() { + if (!state.isLoading && state.products.length === 0) { + getProducts(); // 최초 호출 시 데이터 요청 + } + return state.isLoading ? loadingContent() : productContent(); } -export function productItem(products) { - return /* HTML */ products +export function productItem() { + return /* HTML */ state.products .map( (product) => `
@@ -76,7 +134,7 @@ function productContent(searchParams) { type="text" id="search-input" placeholder="상품명을 검색해보세요..." - value="${searchParams.search}" + value="${filterState.search}" class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" /> @@ -128,10 +186,10 @@ function productContent(searchParams) { id="limit-select" class="text-sm border border-gray-300 rounded px-2 py-1 focus:ring-1 focus:ring-blue-500 focus:border-blue-500" > - - - - + + + +
@@ -142,10 +200,10 @@ function productContent(searchParams) { class="text-sm border border-gray-300 rounded px-2 py-1 focus:ring-1 focus:ring-blue-500 focus:border-blue-500" > - - - - + + + +
@@ -156,10 +214,10 @@ function productContent(searchParams) {
- 총 ${searchParams.total || 0}개의 상품 + 총 ${pageState.total || 0}개의 상품
-
+
${productItem()}
모든 상품을 확인했습니다
diff --git a/src/routes/router.js b/src/routes/router.js index f746ab360..e38c3150e 100644 --- a/src/routes/router.js +++ b/src/routes/router.js @@ -12,7 +12,7 @@ const ROUTES = { const URL_MAP = { [ROUTES.MAIN]: productPage, [ROUTES.PRODUCT]: productPage, - [ROUTES.PRODUCT_DETAIL()]: productDetailPage, + [ROUTES.PRODUCT_DETAIL]: productDetailPage, [ROUTES.CART]: cartPage, [ROUTES.ERROR]: notFoundPage, }; @@ -24,7 +24,12 @@ export function navigate(pathname, replace = false) { export function render() { const root = document.querySelector("#root"); + console.log(root); let { pathname } = location; + if ((pathname === ROUTES.MAIN || pathname === ROUTES.PRODUCT) && pathname !== ROUTES.PRODUCT) { + return navigate(ROUTES.PRODUCT, true); // replace = true + } + const page = URL_MAP[pathname] || notFoundPage; root.innerHTML = page(); } From 4e75afd1d9ce2252a23327db58759d83fc9cbdc0 Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Thu, 10 Jul 2025 01:41:32 +0900 Subject: [PATCH 11/22] =?UTF-8?q?test:=20=20=EB=9D=BC=EC=9A=B0=ED=84=B0=20?= =?UTF-8?q?=EB=AC=B4=ED=95=9C=EB=A3=A8=ED=94=84=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=A4=91...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/productPage.js | 71 +++++++++++++--------------------------- src/routes/router.js | 33 +++++++++++++++++-- 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/src/pages/productPage.js b/src/pages/productPage.js index 2484ae915..7a5f659a5 100644 --- a/src/pages/productPage.js +++ b/src/pages/productPage.js @@ -1,21 +1,3 @@ -import { render } from "../routes/router"; - -let pageState = { - hasNext: false, - hasPrev: false, - limit: 20, - page: 1, - total: 0, - totalPages: 0, -}; - -let filterState = { - category1: "", - category2: "", - search: "", - sort: "price_asc", -}; - let state = { isLoading: false, isFetching: false, @@ -24,40 +6,33 @@ let state = { }; async function getProducts() { - const { limit, page } = pageState; - const { search, sort, category1, category2 } = filterState; - const searchParams = { limit, page, search, sort, category1, category2 }; - state.isLoading = true; - // render(); - const params = new URLSearchParams( - Object.entries(searchParams).filter(([k, v]) => v !== undefined && v !== null && k), - ); + const queryString = window.location.search; + const params = new URLSearchParams(queryString); try { const response = await fetch(`/api/products?${params.toString()}`); if (!response.ok) throw new Error("네트워크 오류"); const data = await response.json(); - const { products, pagination, filters } = data; - console.log(data); - + const { products } = data; + console.log("productPage data:", data); state.products = [...state.products, ...products]; - pageState = { ...pageState, ...pagination }; - filterState = { ...filterState, ...filters }; - // console.log("data:", data); state.error = null; } catch (error) { state.error = error.message; } finally { state.isLoading = false; state.isFetching = false; - render(); } } export function productPage() { - if (!state.isLoading && state.products.length === 0) { - getProducts(); // 최초 호출 시 데이터 요청 - } - return state.isLoading ? loadingContent() : productContent(); + getProducts(); + const params = new URLSearchParams(location.search); + const search = params.get("search") || ""; + const limit = params.get("limit") || 20; + const sort = params.get("limit") || "price_asc"; + const total = params.get("total") || 0; + const searchParams = { search, limit, sort, total }; + return state.isLoading ? loadingContent() : productContent(searchParams); } export function productItem() { return /* HTML */ state.products @@ -95,7 +70,7 @@ export function productItem() { ) .join(""); } -function productContent() { +function productContent(searchParams) { return /* HTML */ `
@@ -134,7 +109,7 @@ function productContent() { type="text" id="search-input" placeholder="상품명을 검색해보세요..." - value="${filterState.search}" + value="${searchParams.search}" class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" /> @@ -186,10 +161,10 @@ function productContent() { id="limit-select" class="text-sm border border-gray-300 rounded px-2 py-1 focus:ring-1 focus:ring-blue-500 focus:border-blue-500" > - - - - + + + +
@@ -200,10 +175,10 @@ function productContent() { class="text-sm border border-gray-300 rounded px-2 py-1 focus:ring-1 focus:ring-blue-500 focus:border-blue-500" > - - - - + + + + @@ -214,7 +189,7 @@ function productContent() {
- 총 ${pageState.total || 0}개의 상품 + 총 ${searchParams.total || 0}개의 상품
${productItem()}
diff --git a/src/routes/router.js b/src/routes/router.js index e38c3150e..bc6ee6c5c 100644 --- a/src/routes/router.js +++ b/src/routes/router.js @@ -18,6 +18,7 @@ const URL_MAP = { }; export function navigate(pathname, replace = false) { + console.log(pathname); history[replace ? "replaceState" : "pushState"](null, "", pathname); render(); // 라우팅 후 화면 다시 그리기 } @@ -26,14 +27,40 @@ export function render() { const root = document.querySelector("#root"); console.log(root); let { pathname } = location; - if ((pathname === ROUTES.MAIN || pathname === ROUTES.PRODUCT) && pathname !== ROUTES.PRODUCT) { - return navigate(ROUTES.PRODUCT, true); // replace = true - } + // if (pathname === ROUTES.MAIN) { + // // navigate(ROUTES.PRODUCT); + // } else if (pathname === ROUTES.PRODUCT) { + // // navigate(ROUTES.PRODUCT, true); + // } const page = URL_MAP[pathname] || notFoundPage; + console.log("page:", page); root.innerHTML = page(); } +function getPathWithParams() { + const pathname = location.pathname; + const params = new URLSearchParams(location.search); + return { pathname, params }; +} + +function setEventListener() { + const root = document.querySelector("#root"); + root.addEventListener("input", (e) => { + if (e.target && e.target.id === "limit-select") { + const { params, pathname } = getPathWithParams(); + if (e.target.value) { + params.set("limit", e.target.value); + } else { + params.delete("limit"); + } + navigate(`${pathname}?${params.toString()}`); + } + }); +} + +setEventListener(); + window.addEventListener("popstate", () => { render(); // 뒤로/앞으로 이동 시 렌더링 }); From ae35e2b2c58872d65f83b05a0dcb79a78d6da9b1 Mon Sep 17 00:00:00 2001 From: junilhwang Date: Thu, 10 Jul 2025 18:42:17 +0900 Subject: [PATCH 12/22] =?UTF-8?q?feat:=20easy=20/=20hard=20=EB=A5=BC=20?= =?UTF-8?q?=EA=B5=AC=EB=B6=84=ED=95=98=EC=97=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A5=BC=20=EC=8B=A4=ED=96=89=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8F=84=EB=A1=9D=20=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 47 +++- e2e/e2e-easy.advanced.spec.js | 305 ++++++++++++++++++++++++++ e2e/e2e-easy.basic.spec.js | 278 +++++++++++++++++++++++ e2e/{e2e.spec.js => e2e-hard.spec.js} | 11 - package.json | 6 +- 5 files changed, 630 insertions(+), 17 deletions(-) create mode 100644 e2e/e2e-easy.advanced.spec.js create mode 100644 e2e/e2e-easy.basic.spec.js rename e2e/{e2e.spec.js => e2e-hard.spec.js} (98%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 601a6ce26..70a9c3e74 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,45 @@ on: workflow_dispatch: jobs: - unit: + hard-basic: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + - uses: pnpm/action-setup@v4 + with: + version: latest + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + - run: | + pnpm install + pnpm run test:hard:basic + hard-advanced: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + - uses: pnpm/action-setup@v4 + with: + version: latest + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + - name: Install dependencies + run: | + pnpm install + npx playwright install --with-deps + pnpm run test:hard:advanced + easy-basic: + timeout-minutes: 60 runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -27,8 +65,9 @@ jobs: - name: Install dependencies run: | pnpm install - pnpm run test - e2e: + npx playwright install --with-deps + pnpm run test:easy:basic + easy-advanced: timeout-minutes: 60 runs-on: ubuntu-latest steps: @@ -47,4 +86,4 @@ jobs: run: | pnpm install npx playwright install --with-deps - pnpm run test:e2e + pnpm run test:easy:advanced diff --git a/e2e/e2e-easy.advanced.spec.js b/e2e/e2e-easy.advanced.spec.js new file mode 100644 index 000000000..259379c41 --- /dev/null +++ b/e2e/e2e-easy.advanced.spec.js @@ -0,0 +1,305 @@ +import { expect, test } from "@playwright/test"; + +// 테스트 설정 +test.describe.configure({ mode: "serial" }); + +// 헬퍼 함수들 +class E2EHelpers { + constructor(page) { + this.page = page; + } + + // 페이지 로딩 대기 + async waitForPageLoad() { + await this.page.waitForSelector('[data-testid="products-grid"], #products-grid', { timeout: 10000 }); + await this.page.waitForFunction(() => { + const text = document.body.textContent; + return text.includes("총") && text.includes("개"); + }); + } + + // 상품을 장바구니에 추가 + async addProductToCart(productName) { + await this.page.click( + `text=${productName} >> xpath=ancestor::*[contains(@class, 'product-card')] >> .add-to-cart-btn`, + ); + await this.page.waitForSelector("text=장바구니에 추가되었습니다", { timeout: 5000 }); + } + + // 장바구니 모달 열기 + async openCartModal() { + await this.page.click("#cart-icon-btn"); + await this.page.waitForSelector(".cart-modal-overlay", { timeout: 5000 }); + } +} + +test.describe("E2E: 쇼핑몰 전체 사용자 시나리오", () => { + test.beforeEach(async ({ page }) => { + // 로컬 스토리지 초기화 + await page.goto("/"); + await page.evaluate(() => { + localStorage.clear(); + sessionStorage.clear(); + }); + }); + + test.describe("1. 검색 및 필터링 기능이 URL과 연동된다.", () => { + test("검색어 입력 후 Enter 키로 검색하고 URL이 업데이트된다", async ({ page }) => { + const helpers = new E2EHelpers(page); + await helpers.waitForPageLoad(); + + // 검색어 입력 + await page.fill("#search-input", "젤리"); + await page.press("#search-input", "Enter"); + + // URL 업데이트 확인 + await expect(page).toHaveURL(/search=%EC%A0%A4%EB%A6%AC/); + + // 검색 결과 확인 + await expect(page.locator("text=3개")).toBeVisible(); + + // 검색어가 검색창에 유지되는지 확인 + await expect(page.locator("#search-input")).toHaveValue("젤리"); + + // 검색어 입력 + await page.fill("#search-input", "아이패드"); + await page.press("#search-input", "Enter"); + + // URL 업데이트 확인 + await expect(page).toHaveURL(/search=%EC%95%84%EC%9D%B4%ED%8C%A8%EB%93%9C/); + + // 검색 결과 확인 + await expect(page.locator("text=21개")).toBeVisible(); + + // 새로고침을 해도 유지 되는지 확인 + await page.reload(); + await helpers.waitForPageLoad(); + await expect(page.locator("text=21개")).toBeVisible(); + }); + + test("정렬 옵션 변경 시 URL이 업데이트된다", async ({ page }) => { + const helpers = new E2EHelpers(page); + await helpers.waitForPageLoad(); + + // 가격 높은순으로 정렬 + await page.selectOption("#sort-select", "price_desc"); + + // 첫 번째 상품 이 가격 높은 순으로 정렬되었는지 확인 + await expect(page.locator(".product-card").first()).toMatchAriaSnapshot(` + - img "ASUS ROG Flow Z13 GZ302EA-RU110W 64GB, 1TB" + - heading "ASUS ROG Flow Z13 GZ302EA-RU110W 64GB, 1TB" [level=3] + - paragraph: ASUS + - paragraph: 3,749,000원 + - button "장바구니 담기" + `); + + await page.selectOption("#sort-select", "name_asc"); + await expect(page.locator(".product-card").nth(1)).toMatchAriaSnapshot(` + - img "[매일출발]유로블루플러스 차량용 요소수 국내산 Adblue 호스포함" + - heading "[매일출발]유로블루플러스 차량용 요소수 국내산 Adblue 호스포함" [level=3] + - paragraph: 유로블루플러스 + - paragraph: 8,700원 + - button "장바구니 담기" + `); + + await page.selectOption("#sort-select", "name_desc"); + await expect(page.locator(".product-card").nth(1)).toMatchAriaSnapshot(` + - img "P&G 다우니 울트라 섬유유연제 에이프릴 프레쉬, 5.03L, 1개" + - heading "P&G 다우니 울트라 섬유유연제 에이프릴 프레쉬, 5.03L, 1개" [level=3] + - paragraph: 다우니 + - paragraph: 16,610원 + - button "장바구니 담기" + `); + + await page.reload(); + await helpers.waitForPageLoad(); + await expect(page.locator(".product-card").nth(1)).toMatchAriaSnapshot(` + - img "P&G 다우니 울트라 섬유유연제 에이프릴 프레쉬, 5.03L, 1개" + - heading "P&G 다우니 울트라 섬유유연제 에이프릴 프레쉬, 5.03L, 1개" [level=3] + - paragraph: 다우니 + - paragraph: 16,610원 + - button "장바구니 담기" + `); + }); + + test("페이지당 상품 수 변경 시 URL이 업데이트된다", async ({ page }) => { + const helpers = new E2EHelpers(page); + await helpers.waitForPageLoad(); + + // 10개로 변경 + await page.selectOption("#limit-select", "10"); + await expect(page).toHaveURL(/limit=10/); + await page.waitForFunction(() => { + return document.querySelectorAll(".product-card").length === 10; + }); + await expect(page.locator(".product-card").last()).toMatchAriaSnapshot( + `- heading "탈부착 방충망 자석쫄대 방풍비닐 창문방충망 셀프시공 DIY 백색 100cm" [level=3]`, + ); + + await page.selectOption("#limit-select", "20"); + await expect(page).toHaveURL(/limit=20/); + await page.waitForFunction(() => { + return document.querySelectorAll(".product-card").length === 20; + }); + await expect(page.locator(".product-card").last()).toMatchAriaSnapshot( + `- heading "고양이 난간 안전망 복층 베란다 방묘창 방묘문 방충망 캣도어 일반형검정1mx1m" [level=3]`, + ); + + await page.selectOption("#limit-select", "50"); + await expect(page).toHaveURL(/limit=50/); + await page.waitForFunction(() => { + return document.querySelectorAll(".product-card").length === 50; + }); + await expect(page.locator(".product-card").last()).toMatchAriaSnapshot( + `- heading "강아지 고양이 아이스팩 파우치 여름 베개 젤리곰 M사이즈" [level=3]`, + ); + + await page.selectOption("#limit-select", "100"); + await expect(page).toHaveURL(/limit=100/); + await page.waitForFunction(() => { + return document.querySelectorAll(".product-card").length === 100; + }); + await expect(page.locator(".product-card").last()).toMatchAriaSnapshot( + `- heading "고양이 스크래쳐 숨숨집 하우스 대형 원목 스크레쳐 A type" [level=3]`, + ); + + await page.reload(); + await helpers.waitForPageLoad(); + await expect(page.locator(".product-card").last()).toMatchAriaSnapshot( + `- heading "고양이 스크래쳐 숨숨집 하우스 대형 원목 스크레쳐 A type" [level=3]`, + ); + }); + + test("검색어와 필터 조건이 URL에서 복원된다", async ({ page }) => { + const helpers = new E2EHelpers(page); + + await page.goto("/?search=젤리&sort=price_desc&limit=10"); + await helpers.waitForPageLoad(); + + await expect(page.locator("#search-input")).toHaveValue("젤리"); + await expect(page.locator("#sort-select")).toHaveValue("price_desc"); + await expect(page.locator("#limit-select")).toHaveValue("10"); + await expect(page.getByRole("main")).toMatchAriaSnapshot(`- text: /총 3개의 상품/`); + + await page.goto("/?search=고양이&sort=name_desc&limit=50"); + await helpers.waitForPageLoad(); + + await expect(page.locator("#search-input")).toHaveValue("고양이"); + await expect(page.locator("#sort-select")).toHaveValue("name_desc"); + await expect(page.locator("#limit-select")).toHaveValue("50"); + await expect(page.getByRole("main")).toMatchAriaSnapshot(`- text: /총 84개의 상품/`); + }); + }); + + test.describe("2. 상품 상세 페이지와 URL이 연동된다.", () => { + test("상품 클릭부터 관련 상품 이동까지 전체 플로우", async ({ page }) => { + await page.evaluate(() => { + window.loadFlag = true; + window.history.pushState({}, "", "/product/85067212996"); + window.dispatchEvent(new Event("popstate")); + }); + + // 상세 페이지 로딩 확인 + await expect(page.locator("text=상품 상세")).toBeVisible(); + + // h1 태그에 상품명 확인 + await expect( + page.locator('h1:text("PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장")'), + ).toBeVisible(); + + // 관련 상품 섹션 확인 + await expect(page.locator("text=관련 상품")).toBeVisible(); + const relatedProducts = page.locator(".related-product-card"); + await expect(relatedProducts.first()).toBeVisible(); + + // 첫 번째 관련 상품 클릭 + await relatedProducts.first().click(); + + // 다른 상품의 상세 페이지로 이동했는지 확인 + await expect(page).toHaveURL("/product/86940857379"); + await expect( + page.locator('h1:text("샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이")'), + ).toBeVisible(); + + await expect(await page.evaluate(() => window.loadFlag)).toBe(true); + + await page.reload(); + + await expect( + page.locator('h1:text("샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이")'), + ).toBeVisible(); + + await expect(await page.evaluate(() => window.loadFlag)).toBe(undefined); + }); + }); + + test.describe("3. SPA 네비게이션", () => { + test("브라우저 뒤로가기/앞으로가기가 올바르게 작동한다", async ({ page }) => { + const helpers = new E2EHelpers(page); + await page.evaluate(() => { + window.loadFlag = true; + }); + await helpers.waitForPageLoad(); + + // 상품 상세 페이지로 이동 + const productCard = page + .locator("text=PVC 투명 젤리 쇼핑백") + .locator('xpath=ancestor::*[contains(@class, "product-card")]'); + await productCard.locator("img").click(); + + await expect(page).toHaveURL("/product/85067212996"); + await expect( + page.locator('h1:text("PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장")'), + ).toBeVisible(); + await expect(page.locator("text=관련 상품")).toBeVisible(); + const relatedProducts = page.locator(".related-product-card"); + await relatedProducts.first().click(); + + await expect(page).toHaveURL("/product/86940857379"); + await expect( + page.locator('h1:text("샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이")'), + ).toBeVisible(); + + // 브라우저 뒤로가기 + await page.goBack(); + await expect(page).toHaveURL("/product/85067212996"); + await expect( + page.locator('h1:text("PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장")'), + ).toBeVisible(); + + // 브라우저 앞으로가기 + await page.goForward(); + await expect(page).toHaveURL("/product/86940857379"); + await expect( + page.locator('h1:text("샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이")'), + ).toBeVisible(); + + await page.goBack(); + await page.goBack(); + await expect(page).toHaveURL("/"); + const firstProductCard = page.locator(".product-card").first(); + await expect(firstProductCard.locator("img")).toBeVisible(); + + expect(await page.evaluate(() => window.loadFlag)).toBe(true); + + await page.reload(); + expect( + await page.evaluate(() => { + return window.loadFlag; + }), + ).toBe(undefined); + }); + + // 404 페이지 테스트 + test("존재하지 않는 페이지 접근 시 404 페이지가 표시된다", async ({ page }) => { + // 존재하지 않는 경로로 이동 + await page.goto("/non-existent-page"); + + // 404 페이지 확인 + await expect(page.getByRole("main")).toMatchAriaSnapshot(` + - img: /404 페이지를 찾을 수 없습니다/ + - link "홈으로" + `); + }); + }); +}); diff --git a/e2e/e2e-easy.basic.spec.js b/e2e/e2e-easy.basic.spec.js new file mode 100644 index 000000000..9b10a55fa --- /dev/null +++ b/e2e/e2e-easy.basic.spec.js @@ -0,0 +1,278 @@ +import { expect, test } from "@playwright/test"; + +// 테스트 설정 +test.describe.configure({ mode: "serial" }); + +// 헬퍼 함수들 +class E2EHelpers { + constructor(page) { + this.page = page; + } + + // 페이지 로딩 대기 + async waitForPageLoad() { + await this.page.waitForSelector('[data-testid="products-grid"], #products-grid', { timeout: 10000 }); + await this.page.waitForFunction(() => { + const text = document.body.textContent; + return text.includes("총") && text.includes("개"); + }); + } + + // 상품을 장바구니에 추가 + async addProductToCart(productName) { + await this.page.click( + `text=${productName} >> xpath=ancestor::*[contains(@class, 'product-card')] >> .add-to-cart-btn`, + ); + } +} + +test.describe("E2E: 쇼핑몰 전체 사용자 시나리오 > 난이도 쉬움 > 기본과제", () => { + test.beforeEach(async ({ page }) => { + // 로컬 스토리지 초기화 + await page.goto("/"); + await page.evaluate(() => { + localStorage.clear(); + sessionStorage.clear(); + }); + }); + + test.describe("1. 애플리케이션 초기화 및 기본 기능", () => { + test("페이지 접속 시 로딩 상태가 표시되고 상품 목록이 정상적으로 로드된다", async ({ page }) => { + const helpers = new E2EHelpers(page); + + // 로딩 상태 확인 + await expect(page.locator("text=카테고리 로딩 중...")).toBeVisible(); + + // 상품 목록 로드 완료 대기 + await helpers.waitForPageLoad(); + + // 상품 개수 확인 (340개) + await expect(page.locator("text=340개")).toBeVisible(); + + // 기본 UI 요소들 존재 확인 + await expect(page.locator("#search-input")).toBeVisible(); + await expect(page.locator("#cart-icon-btn")).toBeVisible(); + await expect(page.locator("#limit-select")).toBeVisible(); + await expect(page.locator("#sort-select")).toBeVisible(); + }); + + test("상품 카드에 기본 정보가 올바르게 표시된다", async ({ page }) => { + const helpers = new E2EHelpers(page); + await helpers.waitForPageLoad(); + + // 첫 번째 상품 카드 확인 + const firstProductCard = page.locator(".product-card").first(); + + // 상품 이미지 존재 확인 + await expect(firstProductCard.locator("img")).toBeVisible(); + + // 상품명 확인 + await expect(firstProductCard).toContainText(/pvc 투명 젤리 쇼핑백|고양이 난간 안전망/i); + + // 가격 정보 확인 (숫자 + 원) + await expect(firstProductCard).toContainText(/\d{1,3}(,\d{3})*원/); + + // 장바구니 버튼 확인 + await expect(firstProductCard.locator(".add-to-cart-btn")).toBeVisible(); + }); + }); + + test.describe("2. 검색 및 필터링 기능", () => { + test("검색어 입력 후 Enter 키로 검색할 수 있다.", async ({ page }) => { + const helpers = new E2EHelpers(page); + await helpers.waitForPageLoad(); + + // 검색어 입력 + await page.fill("#search-input", "젤리"); + await page.press("#search-input", "Enter"); + + // 검색 결과 확인 + await expect(page.locator("text=3개")).toBeVisible(); + + // 검색어가 검색창에 유지되는지 확인 + await expect(page.locator("#search-input")).toHaveValue("젤리"); + + // 검색어 입력 + await page.fill("#search-input", "아이패드"); + await page.press("#search-input", "Enter"); + + // 검색 결과 확인 + await expect(page.locator("text=21개")).toBeVisible(); + }); + + test("상품의 정렬을 변경할 수 있다.", async ({ page }) => { + const helpers = new E2EHelpers(page); + await helpers.waitForPageLoad(); + + // 가격 높은순으로 정렬 + await page.selectOption("#sort-select", "price_desc"); + + // 첫 번째 상품 이 가격 높은 순으로 정렬되었는지 확인 + await expect(page.locator(".product-card").first()).toMatchAriaSnapshot(` + - img "ASUS ROG Flow Z13 GZ302EA-RU110W 64GB, 1TB" + - heading "ASUS ROG Flow Z13 GZ302EA-RU110W 64GB, 1TB" [level=3] + - paragraph: ASUS + - paragraph: 3,749,000원 + - button "장바구니 담기" + `); + + await page.selectOption("#sort-select", "name_asc"); + await expect(page.locator(".product-card").nth(1)).toMatchAriaSnapshot(` + - img "[매일출발]유로블루플러스 차량용 요소수 국내산 Adblue 호스포함" + - heading "[매일출발]유로블루플러스 차량용 요소수 국내산 Adblue 호스포함" [level=3] + - paragraph: 유로블루플러스 + - paragraph: 8,700원 + - button "장바구니 담기" + `); + + await page.selectOption("#sort-select", "name_desc"); + await expect(page.locator(".product-card").nth(1)).toMatchAriaSnapshot(` + - img "P&G 다우니 울트라 섬유유연제 에이프릴 프레쉬, 5.03L, 1개" + - heading "P&G 다우니 울트라 섬유유연제 에이프릴 프레쉬, 5.03L, 1개" [level=3] + - paragraph: 다우니 + - paragraph: 16,610원 + - button "장바구니 담기" + `); + + await page.reload(); + await helpers.waitForPageLoad(); + await expect(page.locator(".product-card").nth(1)).toMatchAriaSnapshot(` + - img "P&G 다우니 울트라 섬유유연제 에이프릴 프레쉬, 5.03L, 1개" + - heading "P&G 다우니 울트라 섬유유연제 에이프릴 프레쉬, 5.03L, 1개" [level=3] + - paragraph: 다우니 + - paragraph: 16,610원 + - button "장바구니 담기" + `); + }); + + test("페이지당 상품 수 변경이 가능하다.", async ({ page }) => { + const helpers = new E2EHelpers(page); + + await page.goto("/"); + await helpers.waitForPageLoad(); + + // 10개로 변경 + await page.selectOption("#limit-select", "10"); + + await page.waitForFunction(() => document.querySelectorAll(".product-card").length === 10); + await expect(page.locator(".product-card").last()).toMatchAriaSnapshot( + `- heading "탈부착 방충망 자석쫄대 방풍비닐 창문방충망 셀프시공 DIY 백색 100cm" [level=3]`, + ); + + await page.selectOption("#limit-select", "20"); + + await page.waitForFunction(() => document.querySelectorAll(".product-card").length === 20); + await expect(page.locator(".product-card").last()).toMatchAriaSnapshot( + `- heading "고양이 난간 안전망 복층 베란다 방묘창 방묘문 방충망 캣도어 일반형검정1mx1m" [level=3]`, + ); + + await page.selectOption("#limit-select", "50"); + + await page.waitForFunction(() => document.querySelectorAll(".product-card").length === 50); + await expect(page.locator(".product-card").last()).toMatchAriaSnapshot( + `- heading "강아지 고양이 아이스팩 파우치 여름 베개 젤리곰 M사이즈" [level=3]`, + ); + + await page.selectOption("#limit-select", "100"); + + await page.waitForFunction(() => document.querySelectorAll(".product-card").length === 100); + await expect(page.locator(".product-card").last()).toMatchAriaSnapshot( + `- heading "고양이 스크래쳐 숨숨집 하우스 대형 원목 스크레쳐 A type" [level=3]`, + ); + }); + }); + + test.describe("3. 장바구니 상태 유지", () => { + test("장바구니 아이콘에 상품 개수가 정확히 표시된다", async ({ page }) => { + const helpers = new E2EHelpers(page); + await helpers.waitForPageLoad(); + + // 초기에는 개수 표시가 없어야 함 + await expect(page.locator("#cart-icon-btn span")).not.toBeVisible(); + + // 첫 번째 상품 추가 + await helpers.addProductToCart("PVC 투명 젤리 쇼핑백"); + await expect(page.locator("#cart-icon-btn span")).toHaveText("1"); + + // 두 번째 상품 추가 + await helpers.addProductToCart("샷시 풍지판"); + await expect(page.locator("#cart-icon-btn span")).toHaveText("2"); + + // 첫 번째 상품 한 번 더 추가 + await helpers.addProductToCart("PVC 투명 젤리 쇼핑백"); + await expect(page.locator("#cart-icon-btn span")).toHaveText("2"); + }); + }); + + test.describe("4. 상품 상세 페이지 워크플로우", () => { + test("상품 클릭부터 관련 상품 이동까지 전체 플로우", async ({ page }) => { + const helpers = new E2EHelpers(page); + await page.evaluate(() => { + window.loadFlag = true; + }); + await helpers.waitForPageLoad(); + + // 상품 이미지 클릭하여 상세 페이지로 이동 + const productCard = page + .locator("text=PVC 투명 젤리 쇼핑백") + .locator('xpath=ancestor::*[contains(@class, "product-card")]'); + await productCard.locator("img").click(); + + // 상세 페이지 로딩 확인 + await expect(page.locator("text=상품 상세")).toBeVisible(); + + // h1 태그에 상품명 확인 + await expect( + page.locator('h1:text("PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장")'), + ).toBeVisible(); + + // 수량 조절 후 장바구니 담기 + await page.click("#quantity-increase"); + await expect(page.locator("#quantity-input")).toHaveValue("2"); + + await page.click("#add-to-cart-btn"); + + // 관련 상품 섹션 확인 + await expect(page.locator("text=관련 상품")).toBeVisible(); + + const relatedProducts = page.locator(".related-product-card"); + await expect(relatedProducts.first()).toBeVisible(); + + // 첫 번째 관련 상품 클릭 + await relatedProducts.first().click(); + + // 다른 상품의 상세 페이지로 이동했는지 확인 + await expect( + page.locator('h1:text("샷시 풍지판 창문 바람막이 베란다 문 틈막이 창틀 벌레 차단 샤시 방충망 틈새막이")'), + ).toBeVisible(); + + await expect(await page.evaluate(() => window.loadFlag)).toBe(true); + }); + }); + + test.describe("5. 무한 스크롤 기능", () => { + test("페이지 하단 스크롤 시 추가 상품이 로드된다", async ({ page }) => { + const helpers = new E2EHelpers(page); + await helpers.waitForPageLoad(); + + // 초기 상품 카드 수 확인 + const initialCards = await page.locator(".product-card").count(); + expect(initialCards).toBe(20); + + // 페이지 하단으로 스크롤 + await page.evaluate(() => { + window.scrollTo(0, document.body.scrollHeight); + }); + + // 로딩 인디케이터 확인 + await expect(page.locator("text=상품을 불러오는 중...")).toBeVisible(); + + // 추가 상품 로드 대기 + await page.waitForFunction(() => document.querySelectorAll(".product-card").length === 40); + + // 상품 수가 증가했는지 확인 + const updatedCards = await page.locator(".product-card").count(); + expect(updatedCards).toBe(40); + }); + }); +}); diff --git a/e2e/e2e.spec.js b/e2e/e2e-hard.spec.js similarity index 98% rename from e2e/e2e.spec.js rename to e2e/e2e-hard.spec.js index 23f773492..ec8d552d4 100644 --- a/e2e/e2e.spec.js +++ b/e2e/e2e-hard.spec.js @@ -26,22 +26,11 @@ class E2EHelpers { await this.page.waitForSelector("text=장바구니에 추가되었습니다", { timeout: 5000 }); } - // 토스트 메시지가 사라질 때까지 대기 - async waitForToastToDisappear() { - await this.page.waitForSelector("text=장바구니에 추가되었습니다", { state: "hidden", timeout: 5000 }); - } - // 장바구니 모달 열기 async openCartModal() { await this.page.click("#cart-icon-btn"); await this.page.waitForSelector(".cart-modal-overlay", { timeout: 5000 }); } - - // 현재 상품 개수 가져오기 - async getCurrentProductCount() { - const countText = await this.page.textContent('[data-testid="product-count"]'); - return countText ? parseInt(countText.replace(/[^\d]/g, "")) : 0; - } } test.describe("E2E: 쇼핑몰 전체 사용자 시나리오", () => { diff --git a/package.json b/package.json index 7b2368941..5ba78da82 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,10 @@ "prettier:write": "prettier --write ./src", "preview": "vite preview", "test": "vitest", - "test:basic": "vitest basic.test.js", - "test:advanced": "vitest advanced", + "test:hard:basic": "vitest --run", + "test:hard:advanced": "playwright test e2e-hard", + "test:easy:basic": "playwright test e2e-easy.basic", + "test:easy:advanced": "playwright test e2e-easy.advanced", "test:ui": "vitest --ui", "test:e2e": "playwright test", "test:e2e:ui": "playwright test --ui", From 9273cf63e5fd710097c66e228986dbd7f15db1f4 Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Thu, 10 Jul 2025 23:56:31 +0900 Subject: [PATCH 13/22] =?UTF-8?q?fix:=20=EB=9D=BC=EC=9A=B0=ED=84=B0=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95=20>=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=9E=9C=EB=8D=94=EB=A7=81=20=EC=9D=B4?= =?UTF-8?q?=ED=9B=84=20=EB=B0=94=EC=9D=B8=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/router.js | 49 ++++++++++++-------------------------------- 1 file changed, 13 insertions(+), 36 deletions(-) diff --git a/src/routes/router.js b/src/routes/router.js index bc6ee6c5c..1d0cdee75 100644 --- a/src/routes/router.js +++ b/src/routes/router.js @@ -17,50 +17,27 @@ const URL_MAP = { [ROUTES.ERROR]: notFoundPage, }; -export function navigate(pathname, replace = false) { - console.log(pathname); +export async function navigate(pathname, replace = false) { history[replace ? "replaceState" : "pushState"](null, "", pathname); - render(); // 라우팅 후 화면 다시 그리기 + await render(); } -export function render() { +export async function render() { const root = document.querySelector("#root"); - console.log(root); - let { pathname } = location; - // if (pathname === ROUTES.MAIN) { - // // navigate(ROUTES.PRODUCT); - // } else if (pathname === ROUTES.PRODUCT) { - // // navigate(ROUTES.PRODUCT, true); - // } + const { pathname } = location; - const page = URL_MAP[pathname] || notFoundPage; - console.log("page:", page); - root.innerHTML = page(); -} + // 경로만 사용하도록 쿼리스트링 제거 + const cleanPath = pathname.split("?")[0]; + const pageFn = URL_MAP[cleanPath] || notFoundPage; + // 페이지를 그냥 async로 만들어서 사용 + const content = await pageFn(); + root.innerHTML = content; -function getPathWithParams() { - const pathname = location.pathname; - const params = new URLSearchParams(location.search); - return { pathname, params }; + if (typeof pageFn.afterRender === "function") { + pageFn.afterRender(); + } } -function setEventListener() { - const root = document.querySelector("#root"); - root.addEventListener("input", (e) => { - if (e.target && e.target.id === "limit-select") { - const { params, pathname } = getPathWithParams(); - if (e.target.value) { - params.set("limit", e.target.value); - } else { - params.delete("limit"); - } - navigate(`${pathname}?${params.toString()}`); - } - }); -} - -setEventListener(); - window.addEventListener("popstate", () => { render(); // 뒤로/앞으로 이동 시 렌더링 }); From 4c1dae26de5eb226f0f6e6c3a08614e9efb3c327 Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Fri, 11 Jul 2025 00:22:38 +0900 Subject: [PATCH 14/22] =?UTF-8?q?refactor:=20=EB=A6=AC=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=9E=91=EC=97=85=20=EB=A7=88=EB=AC=B4=EB=A6=AC=20>=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EB=A6=AC=EC=8A=A4=EB=84=88=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80,=20=EB=B6=88=ED=95=84=EC=9A=94=20=EC=9D=B8?= =?UTF-8?q?=EC=9E=90=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/productPage.js | 62 ++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/src/pages/productPage.js b/src/pages/productPage.js index 7a5f659a5..d6f834c10 100644 --- a/src/pages/productPage.js +++ b/src/pages/productPage.js @@ -1,39 +1,77 @@ +import { navigate } from "../routes/router"; let state = { isLoading: false, - isFetching: false, error: null, products: [], + total: 0, }; async function getProducts() { - const queryString = window.location.search; - const params = new URLSearchParams(queryString); + const params = new URLSearchParams(location.search); try { const response = await fetch(`/api/products?${params.toString()}`); if (!response.ok) throw new Error("네트워크 오류"); + const data = await response.json(); - const { products } = data; - console.log("productPage data:", data); - state.products = [...state.products, ...products]; + state.products = data.products; + state.total = data.pagination.total || 0; state.error = null; } catch (error) { state.error = error.message; } finally { state.isLoading = false; - state.isFetching = false; } } - -export function productPage() { - getProducts(); +export async function productPage() { + if (!state.isLoading) { + state.isLoading = true; + state.error = null; + state.products = []; + await getProducts(); + } const params = new URLSearchParams(location.search); const search = params.get("search") || ""; const limit = params.get("limit") || 20; - const sort = params.get("limit") || "price_asc"; + const sort = params.get("sort") || "price_asc"; const total = params.get("total") || 0; const searchParams = { search, limit, sort, total }; return state.isLoading ? loadingContent() : productContent(searchParams); } +productPage.afterRender = () => { + const searchInput = document.getElementById("search-input"); + const limitSelect = document.getElementById("limit-select"); + const sortSelect = document.getElementById("sort-select"); + + if (searchInput) { + searchInput.addEventListener("keydown", (e) => { + if (e.key === "Enter") { + const params = new URLSearchParams(location.search); + params.set("search", e.target.value.trim()); + params.set("page", 1); // 검색 시 페이지 초기화 + navigate(`${location.pathname}?${params.toString()}`); + } + }); + } + + if (limitSelect) { + limitSelect.addEventListener("change", (e) => { + const params = new URLSearchParams(location.search); + params.set("limit", e.target.value); + params.set("page", 1); + navigate(`${location.pathname}?${params.toString()}`); + }); + } + + if (sortSelect) { + sortSelect.addEventListener("change", (e) => { + const params = new URLSearchParams(location.search); + params.set("sort", e.target.value); + params.set("page", 1); + navigate(`${location.pathname}?${params.toString()}`); + }); + } +}; + export function productItem() { return /* HTML */ state.products .map( @@ -189,7 +227,7 @@ function productContent(searchParams) {
- 총 ${searchParams.total || 0}개의 상품 + 총 ${state.total || 0}개의 상품
${productItem()}
From 1cd6b646aea0ee8cc8ae8740799705a6a61a8d0e Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Fri, 11 Jul 2025 00:47:19 +0900 Subject: [PATCH 15/22] =?UTF-8?q?feat:=20=EC=83=81=ED=92=88=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=9D=BC=EC=9A=B0?= =?UTF-8?q?=ED=8C=85>=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98...=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/productPage.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/pages/productPage.js b/src/pages/productPage.js index d6f834c10..0986e3b77 100644 --- a/src/pages/productPage.js +++ b/src/pages/productPage.js @@ -41,7 +41,7 @@ productPage.afterRender = () => { const searchInput = document.getElementById("search-input"); const limitSelect = document.getElementById("limit-select"); const sortSelect = document.getElementById("sort-select"); - + const productGrid = document.getElementById("products-grid"); if (searchInput) { searchInput.addEventListener("keydown", (e) => { if (e.key === "Enter") { @@ -70,12 +70,25 @@ productPage.afterRender = () => { navigate(`${location.pathname}?${params.toString()}`); }); } + if (productGrid) { + productGrid.addEventListener("click", (e) => { + const imageArea = e.target.closest(".product-image"); + if (!imageArea) return; + + const card = imageArea.closest(".product-card"); + const productId = card?.dataset.productId; + if (productId) { + navigate(`/product/detail?productId=${productId}`); + } + }); + } }; export function productItem() { return /* HTML */ state.products .map( (product) => `
From 3e3ec4c980fa0cee6fcc0eb20a7aa84e98e574ca Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Fri, 11 Jul 2025 02:02:08 +0900 Subject: [PATCH 16/22] =?UTF-8?q?feat:=20=EC=83=81=ED=92=88=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=BB=A8=ED=85=90=EC=B8=A0=20=EB=9E=9C=EB=8D=94=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B0=9C=EB=B3=84=EC=83=81=ED=92=88=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99,=20=EB=AA=A9=EB=A1=9D=EA=B0=80=EA=B8=B0=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/pages/productDetailPage.js | 64 +++++++++++++++++++++++++++++----- src/routes/router.js | 2 +- 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/pages/productDetailPage.js b/src/pages/productDetailPage.js index be23a5987..8961432ab 100644 --- a/src/pages/productDetailPage.js +++ b/src/pages/productDetailPage.js @@ -1,6 +1,50 @@ -export function productDetailPage(isLoading) { - return isLoading ? productDetailLoadingContent() : productDetailContent; +import { navigate, ROUTES } from "../routes/router"; + +let state = { + isLoading: false, + product: null, +}; + +async function getProductDetail() { + const params = new URLSearchParams(location.search); + const productId = params.get("productId"); + if (!productId) navigate(ROUTES.PRODUCT); + try { + const response = await fetch(`/api/products/${productId}`); + if (!response.ok) throw new Error("네트워크 오류"); + const data = await response.json(); + state.product = data; + console.log(JSON.stringify(data, null, 2)); + } catch (error) { + console.log(error); + } finally { + state.isLoading = false; + } } + +export async function productDetailPage() { + if (!state.isLoading) { + state.isLoading = true; + state.product = null; + await getProductDetail(); + } + return state.isLoading ? productDetailLoadingContent() : productDetailContent(); +} + +productDetailPage.afterRender = () => { + const backButton = document.querySelector("#back-btn"); + if (backButton) { + backButton.addEventListener("click", () => navigate(ROUTES.PRODUCT)); + } + // 개별 상품 순회하며 이벤트 + document.querySelectorAll(".related-product-card").forEach((card) => { + card.addEventListener("click", () => { + const id = card.dataset.productId; + navigate(`/product/detail?productId=${id}`); + }); + }); +}; + function productDetailLoadingContent() { return `
@@ -42,7 +86,9 @@ function productDetailLoadingContent() {
`; } + function productDetailContent() { + const { product } = state; return `
@@ -94,12 +140,12 @@ function productDetailContent() {
- PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장 + ${product.title}

-

PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장

+

${product.title}

@@ -119,19 +165,19 @@ function productDetailContent() {
- 4.0 (749개 리뷰) + ${product.rating} (${product.reviewCount || 0}개 리뷰)
- 220원 + ${product.lprice}원
- 재고 107개 + 재고 ${product.stock}개
- PVC 투명 젤리 쇼핑백 1호 와인 답례품 구디백 비닐 손잡이 미니 간식 선물포장에 대한 상세 설명입니다. 브랜드의 우수한 품질을 자랑하는 상품으로, 고객 만족도가 높은 제품입니다. + ${product.description}
@@ -165,7 +211,7 @@ function productDetailContent() {
- diff --git a/src/routes/router.js b/src/routes/router.js index 1d0cdee75..7f7be84ea 100644 --- a/src/routes/router.js +++ b/src/routes/router.js @@ -2,7 +2,7 @@ import { cartPage } from "../pages/cartPage"; import { notFoundPage } from "../pages/notFoundPage"; import { productDetailPage } from "../pages/productDetailPage"; import { productPage } from "../pages/productPage"; -const ROUTES = { +export const ROUTES = { MAIN: "/", PRODUCT: "/product", PRODUCT_DETAIL: "/product/detail", From d79a507cb9df1f93e1c525295b84faf1c7363bf8 Mon Sep 17 00:00:00 2001 From: RuBinJang Date: Sat, 12 Jul 2025 00:16:09 +0900 Subject: [PATCH 17/22] Create static.yml --- .github/workflows/static.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/static.yml diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml new file mode 100644 index 000000000..cf592d6d1 --- /dev/null +++ b/.github/workflows/static.yml @@ -0,0 +1,35 @@ +# deploy 를 해주는 workflow +name: Deploy +on: + push: + branches: + - main + - release + - develop + + +jobs: + deploy: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v4 + with: + version: latest + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + - name: Install dependencies + run: | + pnpm install + pnpm run build + + # 빌드를 한 다음에, 빌드한 결과물에 대해 배포하기 + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v3 + if: github.ref == 'refs/heads/main' + with: + github_token: ${{ secrets.MY_TOKEN }} + publish_dir: ./dist From b1fabb5cd4b3052e6d486cb2efbb9ab873e18f09 Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Sat, 12 Jul 2025 00:26:03 +0900 Subject: [PATCH 18/22] =?UTF-8?q?feat:=20=EB=B0=B0=ED=8F=AC=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A6=BD=ED=8A=B8=EC=B6=94=EA=B0=80,=20vite=20base=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 4 + pnpm-lock.yaml | 286 +++++++++++++++++++++++++++++++++++++++++++++++++ vite.config.js | 2 +- 3 files changed, 291 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 62c1eb716..dfcc7a037 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,9 @@ "type": "module", "scripts": { "dev": "vite", + "build:prod": "NODE_ENV=production vite build", + "preview:prod": "NODE_ENV=production vite preview", + "deploy": "pnpm build:prod && pnpm preview:prod", "dev:hash": "vite --open ./index.hash.html", "build": "vite build", "lint:fix": "eslint --fix", @@ -39,6 +42,7 @@ "eslint": "^9.16.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", + "gh-pages": "^6.3.0", "globals": "^15.13.0", "husky": "^9.1.7", "jsdom": "^25.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3e31a5eae..8df157d94 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,6 +42,9 @@ importers: eslint-plugin-prettier: specifier: ^5.2.1 version: 5.5.1(eslint-config-prettier@9.1.0(eslint@9.30.1))(eslint@9.30.1)(prettier@3.6.2) + gh-pages: + specifier: ^6.3.0 + version: 6.3.0 globals: specifier: ^15.13.0 version: 15.15.0 @@ -425,6 +428,18 @@ packages: '@napi-rs/wasm-runtime@0.2.11': resolution: {integrity: sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==} + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + '@open-draft/deferred-promise@2.2.0': resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} @@ -773,6 +788,10 @@ packages: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -780,6 +799,9 @@ packages: ast-v8-to-istanbul@0.3.3: resolution: {integrity: sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==} + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -862,6 +884,9 @@ packages: resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} engines: {node: '>=18'} + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -915,6 +940,10 @@ packages: resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} engines: {node: '>=8'} + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} @@ -928,6 +957,9 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + email-addresses@5.0.0: + resolution: {integrity: sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==} + emoji-regex@10.4.0: resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} @@ -973,6 +1005,10 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -1059,12 +1095,19 @@ packages: fast-diff@1.3.0: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fdir@6.4.6: resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} peerDependencies: @@ -1080,10 +1123,26 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + filename-reserved-regex@2.0.0: + resolution: {integrity: sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==} + engines: {node: '>=4'} + + filenamify@4.3.0: + resolution: {integrity: sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==} + engines: {node: '>=8'} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + find-cache-dir@3.3.2: + resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} + engines: {node: '>=8'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -1103,6 +1162,10 @@ packages: resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==} engines: {node: '>= 6'} + fs-extra@11.3.0: + resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} + engines: {node: '>=14.14'} + fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1136,6 +1199,15 @@ packages: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} + gh-pages@6.3.0: + resolution: {integrity: sha512-Ot5lU6jK0Eb+sszG8pciXdjMXdBJ5wODvgjR+imihTqsUWF2K6dJ9HST55lgqcs8wWcw6o6wAsUzfcYRhJPXbA==} + engines: {node: '>=10'} + hasBin: true + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -1152,10 +1224,17 @@ packages: resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} engines: {node: '>=18'} + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graphql@16.11.0: resolution: {integrity: sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} @@ -1307,6 +1386,9 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -1391,6 +1473,10 @@ packages: resolution: {integrity: sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==} engines: {node: '>=18.0.0'} + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -1421,6 +1507,10 @@ packages: magicast@0.3.5: resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -1432,6 +1522,10 @@ packages: merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -1518,14 +1612,26 @@ packages: outvariant@1.4.3: resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -1555,6 +1661,10 @@ packages: path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} @@ -1581,6 +1691,10 @@ packages: engines: {node: '>=0.10'} hasBin: true + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + playwright-core@1.53.2: resolution: {integrity: sha512-ox/OytMy+2w1jcYEYlOo1Hhp8hZkLCximMTUTMBXjGUA1KoFfiSZ+DU+3a739jsPY0yoKH2TFy9S2fsJas8yAw==} engines: {node: '>=18'} @@ -1622,6 +1736,9 @@ packages: querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} @@ -1644,6 +1761,10 @@ packages: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} @@ -1702,6 +1823,9 @@ packages: rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -1709,6 +1833,10 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + semver@7.7.2: resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} engines: {node: '>=10'} @@ -1733,6 +1861,10 @@ packages: resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} engines: {node: '>=18'} + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + slice-ansi@5.0.0: resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} engines: {node: '>=12'} @@ -1797,6 +1929,10 @@ packages: strip-literal@3.0.0: resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} + strip-outer@1.0.1: + resolution: {integrity: sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==} + engines: {node: '>=0.10.0'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -1865,6 +2001,10 @@ packages: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} + trim-repeated@1.0.0: + resolution: {integrity: sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==} + engines: {node: '>=0.10.0'} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -1884,6 +2024,10 @@ packages: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -2343,6 +2487,18 @@ snapshots: '@tybys/wasm-util': 0.9.0 optional: true + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + '@open-draft/deferred-promise@2.2.0': {} '@open-draft/logger@0.3.0': @@ -2645,6 +2801,8 @@ snapshots: aria-query@5.3.2: {} + array-union@2.1.0: {} + assertion-error@2.0.1: {} ast-v8-to-istanbul@0.3.3: @@ -2653,6 +2811,8 @@ snapshots: estree-walker: 3.0.3 js-tokens: 9.0.1 + async@3.2.6: {} + asynckit@0.4.0: {} balanced-match@1.0.2: {} @@ -2732,6 +2892,8 @@ snapshots: commander@13.1.0: {} + commondir@1.0.1: {} + concat-map@0.0.1: {} cookie@0.7.2: {} @@ -2770,6 +2932,10 @@ snapshots: detect-libc@2.0.4: {} + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + dom-accessibility-api@0.5.16: {} dom-accessibility-api@0.6.3: {} @@ -2782,6 +2948,8 @@ snapshots: eastasianwidth@0.2.0: {} + email-addresses@5.0.0: {} + emoji-regex@10.4.0: {} emoji-regex@8.0.0: {} @@ -2839,6 +3007,8 @@ snapshots: escalade@3.2.0: {} + escape-string-regexp@1.0.5: {} + escape-string-regexp@4.0.0: {} eslint-config-prettier@9.1.0(eslint@9.30.1): @@ -2945,10 +3115,22 @@ snapshots: fast-diff@1.3.0: {} + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + fdir@6.4.6(picomatch@4.0.2): optionalDependencies: picomatch: 4.0.2 @@ -2959,10 +3141,29 @@ snapshots: dependencies: flat-cache: 4.0.1 + filename-reserved-regex@2.0.0: {} + + filenamify@4.3.0: + dependencies: + filename-reserved-regex: 2.0.0 + strip-outer: 1.0.1 + trim-repeated: 1.0.0 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 + find-cache-dir@3.3.2: + dependencies: + commondir: 1.0.1 + make-dir: 3.1.0 + pkg-dir: 4.2.0 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -2988,6 +3189,12 @@ snapshots: hasown: 2.0.2 mime-types: 2.1.35 + fs-extra@11.3.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + fsevents@2.3.2: optional: true @@ -3020,6 +3227,20 @@ snapshots: get-stream@8.0.1: {} + gh-pages@6.3.0: + dependencies: + async: 3.2.6 + commander: 13.1.0 + email-addresses: 5.0.0 + filenamify: 4.3.0 + find-cache-dir: 3.3.2 + fs-extra: 11.3.0 + globby: 11.1.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 @@ -3037,8 +3258,19 @@ snapshots: globals@15.15.0: {} + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + gopd@1.2.0: {} + graceful-fs@4.2.11: {} + graphql@16.11.0: {} has-flag@4.0.0: {} @@ -3187,6 +3419,12 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -3267,6 +3505,10 @@ snapshots: rfdc: 1.4.1 wrap-ansi: 9.0.0 + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -3299,6 +3541,10 @@ snapshots: '@babel/types': 7.28.0 source-map-js: 1.2.1 + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + make-dir@4.0.0: dependencies: semver: 7.7.2 @@ -3307,6 +3553,8 @@ snapshots: merge-stream@2.0.0: {} + merge2@1.4.1: {} + micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -3392,14 +3640,24 @@ snapshots: outvariant@1.4.3: {} + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + p-locate@5.0.0: dependencies: p-limit: 3.1.0 + p-try@2.2.0: {} + package-json-from-dist@1.0.1: {} parent-module@1.0.1: @@ -3423,6 +3681,8 @@ snapshots: path-to-regexp@6.3.0: {} + path-type@4.0.0: {} + pathe@1.1.2: {} pathe@2.0.3: {} @@ -3437,6 +3697,10 @@ snapshots: pidtree@0.6.0: {} + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + playwright-core@1.53.2: {} playwright@1.53.2: @@ -3473,6 +3737,8 @@ snapshots: querystringify@2.2.0: {} + queue-microtask@1.2.3: {} + react-is@17.0.2: {} redent@3.0.0: @@ -3491,6 +3757,8 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 + reusify@1.1.0: {} + rfdc@1.4.1: {} rolldown-vite@7.0.5(esbuild@0.25.5)(yaml@2.8.0): @@ -3557,12 +3825,18 @@ snapshots: rrweb-cssom@0.8.0: {} + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + safer-buffer@2.1.2: {} saxes@6.0.0: dependencies: xmlchars: 2.2.0 + semver@6.3.1: {} + semver@7.7.2: {} shebang-command@2.0.0: @@ -3581,6 +3855,8 @@ snapshots: mrmime: 2.0.1 totalist: 3.0.1 + slash@3.0.0: {} + slice-ansi@5.0.0: dependencies: ansi-styles: 6.2.1 @@ -3641,6 +3917,10 @@ snapshots: dependencies: js-tokens: 9.0.1 + strip-outer@1.0.1: + dependencies: + escape-string-regexp: 1.0.5 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -3701,6 +3981,10 @@ snapshots: dependencies: punycode: 2.3.1 + trim-repeated@1.0.0: + dependencies: + escape-string-regexp: 1.0.5 + tslib@2.8.1: optional: true @@ -3714,6 +3998,8 @@ snapshots: universalify@0.2.0: {} + universalify@2.0.1: {} + uri-js@4.4.1: dependencies: punycode: 2.3.1 diff --git a/vite.config.js b/vite.config.js index 2eef1c44e..ca3d048d6 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,6 +1,6 @@ import { defineConfig } from "vitest/config"; - export default defineConfig({ + base: "/", test: { globals: true, environment: "jsdom", From ecb8c6aadd762dfeb2ea9a3e57bf71df30f53640 Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Sat, 12 Jul 2025 11:00:37 +0900 Subject: [PATCH 19/22] =?UTF-8?q?feat:=20=EB=B0=B0=ED=8F=AC=ED=87=B4?= =?UTF-8?q?=EB=A7=88=EC=A1=B0=20=EC=9E=90=EB=A3=8C=EC=B0=B8=EA=B3=A0=20?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 59 +++++++++++++-------- public/404.html | 122 +++++++++++++++++++++++++++++++++++++++++++ src/mocks/browser.js | 12 +++++ src/routes/router.js | 14 +++-- src/setupTests.js | 2 +- vite.config.js | 13 ++++- 6 files changed, 195 insertions(+), 27 deletions(-) create mode 100644 public/404.html diff --git a/index.html b/index.html index d43ffde2d..26473f628 100644 --- a/index.html +++ b/index.html @@ -1,26 +1,41 @@ - - - - 상품 쇼핑몰 - - - + + + + - - -
- - + })(window.location); + + + +
+ + diff --git a/public/404.html b/public/404.html new file mode 100644 index 000000000..0f143d481 --- /dev/null +++ b/public/404.html @@ -0,0 +1,122 @@ + + + + + + 상품 쇼핑몰 + + + + + + +
+ + + + + diff --git a/src/mocks/browser.js b/src/mocks/browser.js index be3dedca2..d0e926e35 100644 --- a/src/mocks/browser.js +++ b/src/mocks/browser.js @@ -1,5 +1,17 @@ +const basePath = import.meta.env.PROD ? "/front_6th_chapter1-1" : ""; + import { setupWorker } from "msw/browser"; import { handlers } from "./handlers"; // MSW 워커 설정 export const worker = setupWorker(...handlers); +export const workerOptions = import.meta.env.PROD + ? { + serviceWorker: { + url: `${basePath}/mockServiceWorker.js`, + }, + onUnhandledRequest: "bypass", + } + : { + onUnhandledRequest: "bypass", + }; diff --git a/src/routes/router.js b/src/routes/router.js index 7f7be84ea..81f792bed 100644 --- a/src/routes/router.js +++ b/src/routes/router.js @@ -2,6 +2,14 @@ import { cartPage } from "../pages/cartPage"; import { notFoundPage } from "../pages/notFoundPage"; import { productDetailPage } from "../pages/productDetailPage"; import { productPage } from "../pages/productPage"; +const BASE_PATH = import.meta.env.PROD ? "/front_6th_chapter1-1" : ""; +// const getAppPath = (fullPath = window.location.pathname) => { +// return fullPath.startsWith(BASE_PATH) ? fullPath.slice(BASE_PATH.length) || "/" : fullPath; +// }; + +const getFullPath = (appPath) => { + return BASE_PATH + appPath; +}; export const ROUTES = { MAIN: "/", PRODUCT: "/product", @@ -18,14 +26,14 @@ const URL_MAP = { }; export async function navigate(pathname, replace = false) { - history[replace ? "replaceState" : "pushState"](null, "", pathname); + history[replace ? "replaceState" : "pushState"](null, "", getFullPath(pathname)); await render(); } export async function render() { const root = document.querySelector("#root"); - const { pathname } = location; - + let { pathname } = location; + pathname = getFullPath(pathname); // 경로만 사용하도록 쿼리스트링 제거 const cleanPath = pathname.split("?")[0]; const pageFn = URL_MAP[cleanPath] || notFoundPage; diff --git a/src/setupTests.js b/src/setupTests.js index d72b8905a..de556279a 100644 --- a/src/setupTests.js +++ b/src/setupTests.js @@ -1,5 +1,5 @@ -import "@testing-library/jest-dom"; import { configure } from "@testing-library/dom"; +import "@testing-library/jest-dom"; import { afterAll, beforeAll } from "vitest"; import { server } from "./__tests__/mockServerHandler.js"; diff --git a/vite.config.js b/vite.config.js index ca3d048d6..7cc0073db 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,6 +1,17 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ - base: "/", + base: process.env.NODE_ENV === "production" ? "/front_6th_chapter1-1/" : "/", + + build: { + outDir: "dist", + assetsDir: "assets", + sourcemap: false, + rollupOptions: { + output: { + manualChunks: undefined, + }, + }, + }, test: { globals: true, environment: "jsdom", From 83ffe2f1b3a335072d92ef3d990193d33e8a7263 Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Sat, 12 Jul 2025 11:05:07 +0900 Subject: [PATCH 20/22] =?UTF-8?q?feat:=20=EC=9E=90=EB=8F=99=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/static.yml | 43 +++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index cf592d6d1..9451f409a 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -1,35 +1,38 @@ -# deploy 를 해주는 workflow -name: Deploy +name: Deploy to GitHub Pages + on: push: - branches: - - main - - release - - develop - + branches: [main] jobs: deploy: - timeout-minutes: 60 runs-on: ubuntu-latest + steps: - - uses: actions/checkout@v3 - - uses: pnpm/action-setup@v4 + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 with: - version: latest - - uses: actions/setup-node@v4 + node-version: "18" + + - name: Setup pnpm + uses: pnpm/action-setup@v2 with: - node-version: 22 - cache: 'pnpm' + version: 8 + - name: Install dependencies - run: | - pnpm install - pnpm run build - - # 빌드를 한 다음에, 빌드한 결과물에 대해 배포하기 + run: pnpm install + + - name: Build + run: pnpm build + env: + NODE_ENV: production + - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v3 if: github.ref == 'refs/heads/main' with: - github_token: ${{ secrets.MY_TOKEN }} + github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./dist From 5ef0a3c1853fd63257440682074c3d014c70737c Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Sat, 12 Jul 2025 11:21:34 +0900 Subject: [PATCH 21/22] =?UTF-8?q?fix:=20=EB=B0=B0=ED=8F=AC=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.js | 6 +----- src/routes/router.js | 14 +++++++------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/main.js b/src/main.js index 9174c78e5..185691781 100644 --- a/src/main.js +++ b/src/main.js @@ -1,10 +1,6 @@ import { render } from "./routes/router.js"; const enableMocking = () => - import("./mocks/browser.js").then(({ worker }) => - worker.start({ - onUnhandledRequest: "bypass", - }), - ); + import("./mocks/browser.js").then(({ worker, workerOptions }) => worker.start(workerOptions)); function main() { render(); diff --git a/src/routes/router.js b/src/routes/router.js index 81f792bed..53ed5a4fd 100644 --- a/src/routes/router.js +++ b/src/routes/router.js @@ -11,11 +11,11 @@ const getFullPath = (appPath) => { return BASE_PATH + appPath; }; export const ROUTES = { - MAIN: "/", - PRODUCT: "/product", - PRODUCT_DETAIL: "/product/detail", - CART: "/cart", - ERROR: "/error", + MAIN: `${BASE_PATH}/`, + PRODUCT: `${BASE_PATH}/product`, + PRODUCT_DETAIL: `${BASE_PATH}/product/detail`, + CART: `${BASE_PATH}/cart`, + ERROR: `${BASE_PATH}/error`, }; const URL_MAP = { [ROUTES.MAIN]: productPage, @@ -32,10 +32,10 @@ export async function navigate(pathname, replace = false) { export async function render() { const root = document.querySelector("#root"); - let { pathname } = location; - pathname = getFullPath(pathname); + const { pathname } = location; // 경로만 사용하도록 쿼리스트링 제거 const cleanPath = pathname.split("?")[0]; + console.log(cleanPath); const pageFn = URL_MAP[cleanPath] || notFoundPage; // 페이지를 그냥 async로 만들어서 사용 const content = await pageFn(); From 7026bda25b6ca66eb2781a0e58af04dc75484a2f Mon Sep 17 00:00:00 2001 From: JangRuBin2 Date: Sat, 12 Jul 2025 11:25:28 +0900 Subject: [PATCH 22/22] =?UTF-8?q?fix:=20=EB=B0=B0=ED=8F=AC=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/router.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/routes/router.js b/src/routes/router.js index 53ed5a4fd..bcb6b79fc 100644 --- a/src/routes/router.js +++ b/src/routes/router.js @@ -2,14 +2,13 @@ import { cartPage } from "../pages/cartPage"; import { notFoundPage } from "../pages/notFoundPage"; import { productDetailPage } from "../pages/productDetailPage"; import { productPage } from "../pages/productPage"; + const BASE_PATH = import.meta.env.PROD ? "/front_6th_chapter1-1" : ""; -// const getAppPath = (fullPath = window.location.pathname) => { -// return fullPath.startsWith(BASE_PATH) ? fullPath.slice(BASE_PATH.length) || "/" : fullPath; -// }; const getFullPath = (appPath) => { return BASE_PATH + appPath; }; + export const ROUTES = { MAIN: `${BASE_PATH}/`, PRODUCT: `${BASE_PATH}/product`, @@ -17,6 +16,7 @@ export const ROUTES = { CART: `${BASE_PATH}/cart`, ERROR: `${BASE_PATH}/error`, }; + const URL_MAP = { [ROUTES.MAIN]: productPage, [ROUTES.PRODUCT]: productPage,