diff --git a/go.mod b/go.mod index 3e8dc9be2..439c0f567 100644 --- a/go.mod +++ b/go.mod @@ -21,14 +21,14 @@ require ( github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5 github.com/owenrumney/go-sarif/v2 v2.1.2 github.com/package-url/packageurl-go v0.1.0 - github.com/playwright-community/playwright-go v0.3900.1 + github.com/playwright-community/playwright-go v0.4501.1 github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.11.0 github.com/stretchr/testify v1.9.0 github.com/tektoncd/pipeline v0.57.0 github.com/trivago/tgo v1.0.7 go.mongodb.org/mongo-driver v1.10.0 - golang.org/x/crypto v0.18.0 + golang.org/x/crypto v0.23.0 golang.org/x/oauth2 v0.16.0 google.golang.org/api v0.156.0 google.golang.org/protobuf v1.34.1 @@ -60,12 +60,12 @@ require ( github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect - github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch v5.7.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-jose/go-jose/v3 v3.0.1 // indirect + github.com/go-jose/go-jose/v3 v3.0.3 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/logr v1.4.1 // indirect @@ -139,13 +139,13 @@ require ( go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect - golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/sync v0.6.0 // indirect - golang.org/x/term v0.16.0 // indirect + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/term v0.20.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.16.1 // indirect + golang.org/x/tools v0.21.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/appengine v1.6.8 // indirect @@ -190,8 +190,8 @@ require ( github.com/spf13/pflag v1.0.5 github.com/subosito/gotenv v1.2.0 // indirect go.uber.org/atomic v1.10.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 28c1c56e5..34dc54107 100644 --- a/go.sum +++ b/go.sum @@ -350,12 +350,12 @@ github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1S github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= -github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= -github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -445,8 +445,8 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= -github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= +github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -891,6 +891,8 @@ github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= +github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -1007,8 +1009,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/playwright-community/playwright-go v0.3900.1 h1:8BkmDxVzLTp3USQ50EyXJSXcz0XDMwNP5y29lHIZ9Fc= -github.com/playwright-community/playwright-go v0.3900.1/go.mod h1:mbNzMqt04IVRdhVfXWqmCxd81gCdL3BA5hj6/pVAIqM= +github.com/playwright-community/playwright-go v0.4501.1 h1:kz8SIfR6nEI8blk77nTVD0K5/i37QP5rY/o8a1fG+4c= +github.com/playwright-community/playwright-go v0.4501.1/go.mod h1:bpArn5TqNzmP0jroCgw4poSOG9gSeQg490iLqWAaa7w= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= @@ -1284,8 +1286,9 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1300,8 +1303,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= -golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1335,8 +1338,9 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1396,8 +1400,10 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211013171255-e13a2654a71e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1428,8 +1434,9 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1532,14 +1539,20 @@ golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1550,8 +1563,11 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1632,8 +1648,9 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= -golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/vendor/github.com/danwakefield/fnmatch/LICENSE b/vendor/github.com/danwakefield/fnmatch/LICENSE deleted file mode 100644 index 0dc9851a3..000000000 --- a/vendor/github.com/danwakefield/fnmatch/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) 2016, Daniel Wakefield -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/danwakefield/fnmatch/README.md b/vendor/github.com/danwakefield/fnmatch/README.md deleted file mode 100644 index b8d715662..000000000 --- a/vendor/github.com/danwakefield/fnmatch/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# fnmatch -Updated clone of kballards golang fnmatch gist (https://gist.github.com/kballard/272720) - - diff --git a/vendor/github.com/danwakefield/fnmatch/fnmatch.go b/vendor/github.com/danwakefield/fnmatch/fnmatch.go deleted file mode 100644 index 07ac7b37c..000000000 --- a/vendor/github.com/danwakefield/fnmatch/fnmatch.go +++ /dev/null @@ -1,219 +0,0 @@ -// Provide string-matching based on fnmatch.3 -package fnmatch - -// There are a few issues that I believe to be bugs, but this implementation is -// based as closely as possible on BSD fnmatch. These bugs are present in the -// source of BSD fnmatch, and so are replicated here. The issues are as follows: -// -// * FNM_PERIOD is no longer observed after the first * in a pattern -// This only applies to matches done with FNM_PATHNAME as well -// * FNM_PERIOD doesn't apply to ranges. According to the documentation, -// a period must be matched explicitly, but a range will match it too - -import ( - "unicode" - "unicode/utf8" -) - -const ( - FNM_NOESCAPE = (1 << iota) - FNM_PATHNAME - FNM_PERIOD - - FNM_LEADING_DIR - FNM_CASEFOLD - - FNM_IGNORECASE = FNM_CASEFOLD - FNM_FILE_NAME = FNM_PATHNAME -) - -func unpackRune(str *string) rune { - rune, size := utf8.DecodeRuneInString(*str) - *str = (*str)[size:] - return rune -} - -// Matches the pattern against the string, with the given flags, -// and returns true if the match is successful. -// This function should match fnmatch.3 as closely as possible. -func Match(pattern, s string, flags int) bool { - // The implementation for this function was patterned after the BSD fnmatch.c - // source found at http://src.gnu-darwin.org/src/contrib/csup/fnmatch.c.html - noescape := (flags&FNM_NOESCAPE != 0) - pathname := (flags&FNM_PATHNAME != 0) - period := (flags&FNM_PERIOD != 0) - leadingdir := (flags&FNM_LEADING_DIR != 0) - casefold := (flags&FNM_CASEFOLD != 0) - // the following is some bookkeeping that the original fnmatch.c implementation did not do - // We are forced to do this because we're not keeping indexes into C strings but rather - // processing utf8-encoded strings. Use a custom unpacker to maintain our state for us - sAtStart := true - sLastAtStart := true - sLastSlash := false - sLastUnpacked := rune(0) - unpackS := func() rune { - sLastSlash = (sLastUnpacked == '/') - sLastUnpacked = unpackRune(&s) - sLastAtStart = sAtStart - sAtStart = false - return sLastUnpacked - } - for len(pattern) > 0 { - c := unpackRune(&pattern) - switch c { - case '?': - if len(s) == 0 { - return false - } - sc := unpackS() - if pathname && sc == '/' { - return false - } - if period && sc == '.' && (sLastAtStart || (pathname && sLastSlash)) { - return false - } - case '*': - // collapse multiple *'s - // don't use unpackRune here, the only char we care to detect is ASCII - for len(pattern) > 0 && pattern[0] == '*' { - pattern = pattern[1:] - } - if period && s[0] == '.' && (sAtStart || (pathname && sLastUnpacked == '/')) { - return false - } - // optimize for patterns with * at end or before / - if len(pattern) == 0 { - if pathname { - return leadingdir || (strchr(s, '/') == -1) - } else { - return true - } - return !(pathname && strchr(s, '/') >= 0) - } else if pathname && pattern[0] == '/' { - offset := strchr(s, '/') - if offset == -1 { - return false - } else { - // we already know our pattern and string have a /, skip past it - s = s[offset:] // use unpackS here to maintain our bookkeeping state - unpackS() - pattern = pattern[1:] // we know / is one byte long - break - } - } - // general case, recurse - for test := s; len(test) > 0; unpackRune(&test) { - // I believe the (flags &^ FNM_PERIOD) is a bug when FNM_PATHNAME is specified - // but this follows exactly from how fnmatch.c implements it - if Match(pattern, test, (flags &^ FNM_PERIOD)) { - return true - } else if pathname && test[0] == '/' { - break - } - } - return false - case '[': - if len(s) == 0 { - return false - } - if pathname && s[0] == '/' { - return false - } - sc := unpackS() - if !rangematch(&pattern, sc, flags) { - return false - } - case '\\': - if !noescape { - if len(pattern) > 0 { - c = unpackRune(&pattern) - } - } - fallthrough - default: - if len(s) == 0 { - return false - } - sc := unpackS() - switch { - case sc == c: - case casefold && unicode.ToLower(sc) == unicode.ToLower(c): - default: - return false - } - } - } - return len(s) == 0 || (leadingdir && s[0] == '/') -} - -func rangematch(pattern *string, test rune, flags int) bool { - if len(*pattern) == 0 { - return false - } - casefold := (flags&FNM_CASEFOLD != 0) - noescape := (flags&FNM_NOESCAPE != 0) - if casefold { - test = unicode.ToLower(test) - } - var negate, matched bool - if (*pattern)[0] == '^' || (*pattern)[0] == '!' { - negate = true - (*pattern) = (*pattern)[1:] - } - for !matched && len(*pattern) > 1 && (*pattern)[0] != ']' { - c := unpackRune(pattern) - if !noescape && c == '\\' { - if len(*pattern) > 1 { - c = unpackRune(pattern) - } else { - return false - } - } - if casefold { - c = unicode.ToLower(c) - } - if (*pattern)[0] == '-' && len(*pattern) > 1 && (*pattern)[1] != ']' { - unpackRune(pattern) // skip the - - c2 := unpackRune(pattern) - if !noescape && c2 == '\\' { - if len(*pattern) > 0 { - c2 = unpackRune(pattern) - } else { - return false - } - } - if casefold { - c2 = unicode.ToLower(c2) - } - // this really should be more intelligent, but it looks like - // fnmatch.c does simple int comparisons, therefore we will as well - if c <= test && test <= c2 { - matched = true - } - } else if c == test { - matched = true - } - } - // skip past the rest of the pattern - ok := false - for !ok && len(*pattern) > 0 { - c := unpackRune(pattern) - if c == '\\' && len(*pattern) > 0 { - unpackRune(pattern) - } else if c == ']' { - ok = true - } - } - return ok && matched != negate -} - -// define strchr because strings.Index() seems a bit overkill -// returns the index of c in s, or -1 if there is no match -func strchr(s string, c rune) int { - for i, sc := range s { - if sc == c { - return i - } - } - return -1 -} diff --git a/vendor/github.com/danwakefield/fnmatch/.gitignore b/vendor/github.com/deckarep/golang-set/v2/.gitignore similarity index 94% rename from vendor/github.com/danwakefield/fnmatch/.gitignore rename to vendor/github.com/deckarep/golang-set/v2/.gitignore index daf913b1b..4eb156d14 100644 --- a/vendor/github.com/danwakefield/fnmatch/.gitignore +++ b/vendor/github.com/deckarep/golang-set/v2/.gitignore @@ -20,5 +20,4 @@ _cgo_export.* _testmain.go *.exe -*.test -*.prof +.idea \ No newline at end of file diff --git a/vendor/github.com/deckarep/golang-set/v2/LICENSE b/vendor/github.com/deckarep/golang-set/v2/LICENSE new file mode 100644 index 000000000..efd4827e2 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/LICENSE @@ -0,0 +1,22 @@ +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/deckarep/golang-set/v2/README.md b/vendor/github.com/deckarep/golang-set/v2/README.md new file mode 100644 index 000000000..921f0cec0 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/README.md @@ -0,0 +1,181 @@ +![example workflow](https://github.com/deckarep/golang-set/actions/workflows/ci.yml/badge.svg) +[![Go Report Card](https://goreportcard.com/badge/github.com/deckarep/golang-set/v2)](https://goreportcard.com/report/github.com/deckarep/golang-set/v2) +[![GoDoc](https://godoc.org/github.com/deckarep/golang-set/v2?status.svg)](http://godoc.org/github.com/deckarep/golang-set/v2) + +# golang-set + +The missing `generic` set collection for the Go language. Until Go has sets built-in...use this. + +## Update 3/5/2023 +* Packaged version: `2.2.0` release includes a refactor to minimize pointer indirection, better method documentation standards and a few constructor convenience methods to increase ergonomics when appending items `Append` or creating a new set from an exist `Map`. +* supports `new generic` syntax +* Go `1.18.0` or higher +* Workflow tested on Go `1.20` + +![With Generics](new_improved.jpeg) + +Coming from Python one of the things I miss is the superbly wonderful set collection. This is my attempt to mimic the primary features of the set collection from Python. +You can of course argue that there is no need for a set in Go, otherwise the creators would have added one to the standard library. To those I say simply ignore this repository and carry-on and to the rest that find this useful please contribute in helping me make it better by contributing with suggestions or PRs. + +## Install + +Use `go get` to install this package. + +```shell +go get github.com/deckarep/golang-set/v2 +``` + +## Features + +* *NEW* [Generics](https://go.dev/doc/tutorial/generics) based implementation (requires [Go 1.18](https://go.dev/blog/go1.18beta1) or higher) +* One common *interface* to both implementations + * a **non threadsafe** implementation favoring *performance* + * a **threadsafe** implementation favoring *concurrent* use +* Feature complete set implementation modeled after [Python's set implementation](https://docs.python.org/3/library/stdtypes.html#set). +* Exhaustive unit-test and benchmark suite + +## Trusted by + +This package is trusted by many companies and thousands of open-source packages. Here are just a few sample users of this package. + +* Notable projects/companies using this package + * Ethereum + * Docker + * 1Password + * Hashicorp + +## Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=deckarep/golang-set&type=Date)](https://star-history.com/#deckarep/golang-set&Date) + + +## Usage + +The code below demonstrates how a Set collection can better manage data and actually minimize boilerplate and needless loops in code. This package now fully supports *generic* syntax so you are now able to instantiate a collection for any [comparable](https://flaviocopes.com/golang-comparing-values/) type object. + +What is considered comparable in Go? +* `Booleans`, `integers`, `strings`, `floats` or basically primitive types. +* `Pointers` +* `Arrays` +* `Structs` if *all of their fields* are also comparable independently + +Using this library is as simple as creating either a threadsafe or non-threadsafe set and providing a `comparable` type for instantiation of the collection. + +```go +// Syntax example, doesn't compile. +mySet := mapset.NewSet[T]() // where T is some concrete comparable type. + +// Therefore this code creates an int set +mySet := mapset.NewSet[int]() + +// Or perhaps you want a string set +mySet := mapset.NewSet[string]() + +type myStruct struct { + name string + age uint8 +} + +// Alternatively a set of structs +mySet := mapset.NewSet[myStruct]() + +// Lastly a set that can hold anything using the any or empty interface keyword: interface{}. This is effectively removes type safety. +mySet := mapset.NewSet[any]() +``` + +## Comprehensive Example + +```go +package main + +import ( + "fmt" + mapset "github.com/deckarep/golang-set/v2" +) + +func main() { + // Create a string-based set of required classes. + required := mapset.NewSet[string]() + required.Add("cooking") + required.Add("english") + required.Add("math") + required.Add("biology") + + // Create a string-based set of science classes. + sciences := mapset.NewSet[string]() + sciences.Add("biology") + sciences.Add("chemistry") + + // Create a string-based set of electives. + electives := mapset.NewSet[string]() + electives.Add("welding") + electives.Add("music") + electives.Add("automotive") + + // Create a string-based set of bonus programming classes. + bonus := mapset.NewSet[string]() + bonus.Add("beginner go") + bonus.Add("python for dummies") +} +``` + +Create a set of all unique classes. +Sets will *automatically* deduplicate the same data. + +```go + all := required + .Union(sciences) + .Union(electives) + .Union(bonus) + + fmt.Println(all) +``` + +Output: +```sh +Set{cooking, english, math, chemistry, welding, biology, music, automotive, beginner go, python for dummies} +``` + +Is cooking considered a science class? +```go +result := sciences.Contains("cooking") +fmt.Println(result) +``` + +Output: +```false +false +``` + +Show me all classes that are not science classes, since I don't enjoy science. +```go +notScience := all.Difference(sciences) +fmt.Println(notScience) +``` + +```sh +Set{ music, automotive, beginner go, python for dummies, cooking, english, math, welding } +``` + +Which science classes are also required classes? +```go +reqScience := sciences.Intersect(required) +``` + +Output: +```sh +Set{biology} +``` + +How many bonus classes do you offer? +```go +fmt.Println(bonus.Cardinality()) +``` +Output: +```sh +2 +``` + +Thanks for visiting! + +-deckarep diff --git a/vendor/github.com/deckarep/golang-set/v2/iterator.go b/vendor/github.com/deckarep/golang-set/v2/iterator.go new file mode 100644 index 000000000..fc14e7056 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/iterator.go @@ -0,0 +1,58 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package mapset + +// Iterator defines an iterator over a Set, its C channel can be used to range over the Set's +// elements. +type Iterator[T comparable] struct { + C <-chan T + stop chan struct{} +} + +// Stop stops the Iterator, no further elements will be received on C, C will be closed. +func (i *Iterator[T]) Stop() { + // Allows for Stop() to be called multiple times + // (close() panics when called on already closed channel) + defer func() { + recover() + }() + + close(i.stop) + + // Exhaust any remaining elements. + for range i.C { + } +} + +// newIterator returns a new Iterator instance together with its item and stop channels. +func newIterator[T comparable]() (*Iterator[T], chan<- T, <-chan struct{}) { + itemChan := make(chan T) + stopChan := make(chan struct{}) + return &Iterator[T]{ + C: itemChan, + stop: stopChan, + }, itemChan, stopChan +} diff --git a/vendor/github.com/deckarep/golang-set/v2/new_improved.jpeg b/vendor/github.com/deckarep/golang-set/v2/new_improved.jpeg new file mode 100644 index 000000000..429752a07 Binary files /dev/null and b/vendor/github.com/deckarep/golang-set/v2/new_improved.jpeg differ diff --git a/vendor/github.com/deckarep/golang-set/v2/set.go b/vendor/github.com/deckarep/golang-set/v2/set.go new file mode 100644 index 000000000..292089dcf --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/set.go @@ -0,0 +1,255 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Package mapset implements a simple and set collection. +// Items stored within it are unordered and unique. It supports +// typical set operations: membership testing, intersection, union, +// difference, symmetric difference and cloning. +// +// Package mapset provides two implementations of the Set +// interface. The default implementation is safe for concurrent +// access, but a non-thread-safe implementation is also provided for +// programs that can benefit from the slight speed improvement and +// that can enforce mutual exclusion through other means. +package mapset + +// Set is the primary interface provided by the mapset package. It +// represents an unordered set of data and a large number of +// operations that can be applied to that set. +type Set[T comparable] interface { + // Add adds an element to the set. Returns whether + // the item was added. + Add(val T) bool + + // Append multiple elements to the set. Returns + // the number of elements added. + Append(val ...T) int + + // Cardinality returns the number of elements in the set. + Cardinality() int + + // Clear removes all elements from the set, leaving + // the empty set. + Clear() + + // Clone returns a clone of the set using the same + // implementation, duplicating all keys. + Clone() Set[T] + + // Contains returns whether the given items + // are all in the set. + Contains(val ...T) bool + + // ContainsOne returns whether the given item + // is in the set. + // + // Contains may cause the argument to escape to the heap. + // See: https://github.com/deckarep/golang-set/issues/118 + ContainsOne(val T) bool + + // ContainsAny returns whether at least one of the + // given items are in the set. + ContainsAny(val ...T) bool + + // Difference returns the difference between this set + // and other. The returned set will contain + // all elements of this set that are not also + // elements of other. + // + // Note that the argument to Difference + // must be of the same type as the receiver + // of the method. Otherwise, Difference will + // panic. + Difference(other Set[T]) Set[T] + + // Equal determines if two sets are equal to each + // other. If they have the same cardinality + // and contain the same elements, they are + // considered equal. The order in which + // the elements were added is irrelevant. + // + // Note that the argument to Equal must be + // of the same type as the receiver of the + // method. Otherwise, Equal will panic. + Equal(other Set[T]) bool + + // Intersect returns a new set containing only the elements + // that exist only in both sets. + // + // Note that the argument to Intersect + // must be of the same type as the receiver + // of the method. Otherwise, Intersect will + // panic. + Intersect(other Set[T]) Set[T] + + // IsEmpty determines if there are elements in the set. + IsEmpty() bool + + // IsProperSubset determines if every element in this set is in + // the other set but the two sets are not equal. + // + // Note that the argument to IsProperSubset + // must be of the same type as the receiver + // of the method. Otherwise, IsProperSubset + // will panic. + IsProperSubset(other Set[T]) bool + + // IsProperSuperset determines if every element in the other set + // is in this set but the two sets are not + // equal. + // + // Note that the argument to IsSuperset + // must be of the same type as the receiver + // of the method. Otherwise, IsSuperset will + // panic. + IsProperSuperset(other Set[T]) bool + + // IsSubset determines if every element in this set is in + // the other set. + // + // Note that the argument to IsSubset + // must be of the same type as the receiver + // of the method. Otherwise, IsSubset will + // panic. + IsSubset(other Set[T]) bool + + // IsSuperset determines if every element in the other set + // is in this set. + // + // Note that the argument to IsSuperset + // must be of the same type as the receiver + // of the method. Otherwise, IsSuperset will + // panic. + IsSuperset(other Set[T]) bool + + // Each iterates over elements and executes the passed func against each element. + // If passed func returns true, stop iteration at the time. + Each(func(T) bool) + + // Iter returns a channel of elements that you can + // range over. + Iter() <-chan T + + // Iterator returns an Iterator object that you can + // use to range over the set. + Iterator() *Iterator[T] + + // Remove removes a single element from the set. + Remove(i T) + + // RemoveAll removes multiple elements from the set. + RemoveAll(i ...T) + + // String provides a convenient string representation + // of the current state of the set. + String() string + + // SymmetricDifference returns a new set with all elements which are + // in either this set or the other set but not in both. + // + // Note that the argument to SymmetricDifference + // must be of the same type as the receiver + // of the method. Otherwise, SymmetricDifference + // will panic. + SymmetricDifference(other Set[T]) Set[T] + + // Union returns a new set with all elements in both sets. + // + // Note that the argument to Union must be of the + // same type as the receiver of the method. + // Otherwise, Union will panic. + Union(other Set[T]) Set[T] + + // Pop removes and returns an arbitrary item from the set. + Pop() (T, bool) + + // ToSlice returns the members of the set as a slice. + ToSlice() []T + + // MarshalJSON will marshal the set into a JSON-based representation. + MarshalJSON() ([]byte, error) + + // UnmarshalJSON will unmarshal a JSON-based byte slice into a full Set datastructure. + // For this to work, set subtypes must implemented the Marshal/Unmarshal interface. + UnmarshalJSON(b []byte) error +} + +// NewSet creates and returns a new set with the given elements. +// Operations on the resulting set are thread-safe. +func NewSet[T comparable](vals ...T) Set[T] { + s := newThreadSafeSetWithSize[T](len(vals)) + for _, item := range vals { + s.Add(item) + } + return s +} + +// NewSetWithSize creates and returns a reference to an empty set with a specified +// capacity. Operations on the resulting set are thread-safe. +func NewSetWithSize[T comparable](cardinality int) Set[T] { + s := newThreadSafeSetWithSize[T](cardinality) + return s +} + +// NewThreadUnsafeSet creates and returns a new set with the given elements. +// Operations on the resulting set are not thread-safe. +func NewThreadUnsafeSet[T comparable](vals ...T) Set[T] { + s := newThreadUnsafeSetWithSize[T](len(vals)) + for _, item := range vals { + s.Add(item) + } + return s +} + +// NewThreadUnsafeSetWithSize creates and returns a reference to an empty set with +// a specified capacity. Operations on the resulting set are not thread-safe. +func NewThreadUnsafeSetWithSize[T comparable](cardinality int) Set[T] { + s := newThreadUnsafeSetWithSize[T](cardinality) + return s +} + +// NewSetFromMapKeys creates and returns a new set with the given keys of the map. +// Operations on the resulting set are thread-safe. +func NewSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] { + s := NewSetWithSize[T](len(val)) + + for k := range val { + s.Add(k) + } + + return s +} + +// NewThreadUnsafeSetFromMapKeys creates and returns a new set with the given keys of the map. +// Operations on the resulting set are not thread-safe. +func NewThreadUnsafeSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] { + s := NewThreadUnsafeSetWithSize[T](len(val)) + + for k := range val { + s.Add(k) + } + + return s +} diff --git a/vendor/github.com/deckarep/golang-set/v2/sorted.go b/vendor/github.com/deckarep/golang-set/v2/sorted.go new file mode 100644 index 000000000..8ee2e7076 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/sorted.go @@ -0,0 +1,42 @@ +//go:build go1.21 +// +build go1.21 + +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2023 Ralph Caraveo (deckarep@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package mapset + +import ( + "cmp" + "slices" +) + +// Sorted returns a sorted slice of a set of any ordered type in ascending order. +// When sorting floating-point numbers, NaNs are ordered before other values. +func Sorted[E cmp.Ordered](set Set[E]) []E { + s := set.ToSlice() + slices.Sort(s) + return s +} diff --git a/vendor/github.com/deckarep/golang-set/v2/threadsafe.go b/vendor/github.com/deckarep/golang-set/v2/threadsafe.go new file mode 100644 index 000000000..ad7a834b5 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/threadsafe.go @@ -0,0 +1,299 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package mapset + +import "sync" + +type threadSafeSet[T comparable] struct { + sync.RWMutex + uss threadUnsafeSet[T] +} + +func newThreadSafeSet[T comparable]() *threadSafeSet[T] { + return &threadSafeSet[T]{ + uss: newThreadUnsafeSet[T](), + } +} + +func newThreadSafeSetWithSize[T comparable](cardinality int) *threadSafeSet[T] { + return &threadSafeSet[T]{ + uss: newThreadUnsafeSetWithSize[T](cardinality), + } +} + +func (t *threadSafeSet[T]) Add(v T) bool { + t.Lock() + ret := t.uss.Add(v) + t.Unlock() + return ret +} + +func (t *threadSafeSet[T]) Append(v ...T) int { + t.Lock() + ret := t.uss.Append(v...) + t.Unlock() + return ret +} + +func (t *threadSafeSet[T]) Contains(v ...T) bool { + t.RLock() + ret := t.uss.Contains(v...) + t.RUnlock() + + return ret +} + +func (t *threadSafeSet[T]) ContainsOne(v T) bool { + t.RLock() + ret := t.uss.ContainsOne(v) + t.RUnlock() + + return ret +} + +func (t *threadSafeSet[T]) ContainsAny(v ...T) bool { + t.RLock() + ret := t.uss.ContainsAny(v...) + t.RUnlock() + + return ret +} + +func (t *threadSafeSet[T]) IsEmpty() bool { + return t.Cardinality() == 0 +} + +func (t *threadSafeSet[T]) IsSubset(other Set[T]) bool { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + ret := t.uss.IsSubset(o.uss) + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) IsProperSubset(other Set[T]) bool { + o := other.(*threadSafeSet[T]) + + t.RLock() + defer t.RUnlock() + o.RLock() + defer o.RUnlock() + + return t.uss.IsProperSubset(o.uss) +} + +func (t *threadSafeSet[T]) IsSuperset(other Set[T]) bool { + return other.IsSubset(t) +} + +func (t *threadSafeSet[T]) IsProperSuperset(other Set[T]) bool { + return other.IsProperSubset(t) +} + +func (t *threadSafeSet[T]) Union(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeUnion := t.uss.Union(o.uss).(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeUnion} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Intersect(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeIntersection := t.uss.Intersect(o.uss).(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeIntersection} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Difference(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeDifference := t.uss.Difference(o.uss).(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeDifference} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) SymmetricDifference(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeDifference := t.uss.SymmetricDifference(o.uss).(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeDifference} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Clear() { + t.Lock() + t.uss.Clear() + t.Unlock() +} + +func (t *threadSafeSet[T]) Remove(v T) { + t.Lock() + delete(t.uss, v) + t.Unlock() +} + +func (t *threadSafeSet[T]) RemoveAll(i ...T) { + t.Lock() + t.uss.RemoveAll(i...) + t.Unlock() +} + +func (t *threadSafeSet[T]) Cardinality() int { + t.RLock() + defer t.RUnlock() + return len(t.uss) +} + +func (t *threadSafeSet[T]) Each(cb func(T) bool) { + t.RLock() + for elem := range t.uss { + if cb(elem) { + break + } + } + t.RUnlock() +} + +func (t *threadSafeSet[T]) Iter() <-chan T { + ch := make(chan T) + go func() { + t.RLock() + + for elem := range t.uss { + ch <- elem + } + close(ch) + t.RUnlock() + }() + + return ch +} + +func (t *threadSafeSet[T]) Iterator() *Iterator[T] { + iterator, ch, stopCh := newIterator[T]() + + go func() { + t.RLock() + L: + for elem := range t.uss { + select { + case <-stopCh: + break L + case ch <- elem: + } + } + close(ch) + t.RUnlock() + }() + + return iterator +} + +func (t *threadSafeSet[T]) Equal(other Set[T]) bool { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + ret := t.uss.Equal(o.uss) + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Clone() Set[T] { + t.RLock() + + unsafeClone := t.uss.Clone().(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeClone} + t.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) String() string { + t.RLock() + ret := t.uss.String() + t.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Pop() (T, bool) { + t.Lock() + defer t.Unlock() + return t.uss.Pop() +} + +func (t *threadSafeSet[T]) ToSlice() []T { + keys := make([]T, 0, t.Cardinality()) + t.RLock() + for elem := range t.uss { + keys = append(keys, elem) + } + t.RUnlock() + return keys +} + +func (t *threadSafeSet[T]) MarshalJSON() ([]byte, error) { + t.RLock() + b, err := t.uss.MarshalJSON() + t.RUnlock() + + return b, err +} + +func (t *threadSafeSet[T]) UnmarshalJSON(p []byte) error { + t.RLock() + err := t.uss.UnmarshalJSON(p) + t.RUnlock() + + return err +} diff --git a/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go b/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go new file mode 100644 index 000000000..8b17b0176 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go @@ -0,0 +1,330 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package mapset + +import ( + "encoding/json" + "fmt" + "strings" +) + +type threadUnsafeSet[T comparable] map[T]struct{} + +// Assert concrete type:threadUnsafeSet adheres to Set interface. +var _ Set[string] = (threadUnsafeSet[string])(nil) + +func newThreadUnsafeSet[T comparable]() threadUnsafeSet[T] { + return make(threadUnsafeSet[T]) +} + +func newThreadUnsafeSetWithSize[T comparable](cardinality int) threadUnsafeSet[T] { + return make(threadUnsafeSet[T], cardinality) +} + +func (s threadUnsafeSet[T]) Add(v T) bool { + prevLen := len(s) + s[v] = struct{}{} + return prevLen != len(s) +} + +func (s threadUnsafeSet[T]) Append(v ...T) int { + prevLen := len(s) + for _, val := range v { + (s)[val] = struct{}{} + } + return len(s) - prevLen +} + +// private version of Add which doesn't return a value +func (s threadUnsafeSet[T]) add(v T) { + s[v] = struct{}{} +} + +func (s threadUnsafeSet[T]) Cardinality() int { + return len(s) +} + +func (s threadUnsafeSet[T]) Clear() { + // Constructions like this are optimised by compiler, and replaced by + // mapclear() function, defined in + // https://github.com/golang/go/blob/29bbca5c2c1ad41b2a9747890d183b6dd3a4ace4/src/runtime/map.go#L993) + for key := range s { + delete(s, key) + } +} + +func (s threadUnsafeSet[T]) Clone() Set[T] { + clonedSet := newThreadUnsafeSetWithSize[T](s.Cardinality()) + for elem := range s { + clonedSet.add(elem) + } + return clonedSet +} + +func (s threadUnsafeSet[T]) Contains(v ...T) bool { + for _, val := range v { + if _, ok := s[val]; !ok { + return false + } + } + return true +} + +func (s threadUnsafeSet[T]) ContainsOne(v T) bool { + _, ok := s[v] + return ok +} + +func (s threadUnsafeSet[T]) ContainsAny(v ...T) bool { + for _, val := range v { + if _, ok := s[val]; ok { + return true + } + } + return false +} + +// private version of Contains for a single element v +func (s threadUnsafeSet[T]) contains(v T) (ok bool) { + _, ok = s[v] + return ok +} + +func (s threadUnsafeSet[T]) Difference(other Set[T]) Set[T] { + o := other.(threadUnsafeSet[T]) + + diff := newThreadUnsafeSet[T]() + for elem := range s { + if !o.contains(elem) { + diff.add(elem) + } + } + return diff +} + +func (s threadUnsafeSet[T]) Each(cb func(T) bool) { + for elem := range s { + if cb(elem) { + break + } + } +} + +func (s threadUnsafeSet[T]) Equal(other Set[T]) bool { + o := other.(threadUnsafeSet[T]) + + if s.Cardinality() != other.Cardinality() { + return false + } + for elem := range s { + if !o.contains(elem) { + return false + } + } + return true +} + +func (s threadUnsafeSet[T]) Intersect(other Set[T]) Set[T] { + o := other.(threadUnsafeSet[T]) + + intersection := newThreadUnsafeSet[T]() + // loop over smaller set + if s.Cardinality() < other.Cardinality() { + for elem := range s { + if o.contains(elem) { + intersection.add(elem) + } + } + } else { + for elem := range o { + if s.contains(elem) { + intersection.add(elem) + } + } + } + return intersection +} + +func (s threadUnsafeSet[T]) IsEmpty() bool { + return s.Cardinality() == 0 +} + +func (s threadUnsafeSet[T]) IsProperSubset(other Set[T]) bool { + return s.Cardinality() < other.Cardinality() && s.IsSubset(other) +} + +func (s threadUnsafeSet[T]) IsProperSuperset(other Set[T]) bool { + return s.Cardinality() > other.Cardinality() && s.IsSuperset(other) +} + +func (s threadUnsafeSet[T]) IsSubset(other Set[T]) bool { + o := other.(threadUnsafeSet[T]) + if s.Cardinality() > other.Cardinality() { + return false + } + for elem := range s { + if !o.contains(elem) { + return false + } + } + return true +} + +func (s threadUnsafeSet[T]) IsSuperset(other Set[T]) bool { + return other.IsSubset(s) +} + +func (s threadUnsafeSet[T]) Iter() <-chan T { + ch := make(chan T) + go func() { + for elem := range s { + ch <- elem + } + close(ch) + }() + + return ch +} + +func (s threadUnsafeSet[T]) Iterator() *Iterator[T] { + iterator, ch, stopCh := newIterator[T]() + + go func() { + L: + for elem := range s { + select { + case <-stopCh: + break L + case ch <- elem: + } + } + close(ch) + }() + + return iterator +} + +// Pop returns a popped item in case set is not empty, or nil-value of T +// if set is already empty +func (s threadUnsafeSet[T]) Pop() (v T, ok bool) { + for item := range s { + delete(s, item) + return item, true + } + return v, false +} + +func (s threadUnsafeSet[T]) Remove(v T) { + delete(s, v) +} + +func (s threadUnsafeSet[T]) RemoveAll(i ...T) { + for _, elem := range i { + delete(s, elem) + } +} + +func (s threadUnsafeSet[T]) String() string { + items := make([]string, 0, len(s)) + + for elem := range s { + items = append(items, fmt.Sprintf("%v", elem)) + } + return fmt.Sprintf("Set{%s}", strings.Join(items, ", ")) +} + +func (s threadUnsafeSet[T]) SymmetricDifference(other Set[T]) Set[T] { + o := other.(threadUnsafeSet[T]) + + sd := newThreadUnsafeSet[T]() + for elem := range s { + if !o.contains(elem) { + sd.add(elem) + } + } + for elem := range o { + if !s.contains(elem) { + sd.add(elem) + } + } + return sd +} + +func (s threadUnsafeSet[T]) ToSlice() []T { + keys := make([]T, 0, s.Cardinality()) + for elem := range s { + keys = append(keys, elem) + } + + return keys +} + +func (s threadUnsafeSet[T]) Union(other Set[T]) Set[T] { + o := other.(threadUnsafeSet[T]) + + n := s.Cardinality() + if o.Cardinality() > n { + n = o.Cardinality() + } + unionedSet := make(threadUnsafeSet[T], n) + + for elem := range s { + unionedSet.add(elem) + } + for elem := range o { + unionedSet.add(elem) + } + return unionedSet +} + +// MarshalJSON creates a JSON array from the set, it marshals all elements +func (s threadUnsafeSet[T]) MarshalJSON() ([]byte, error) { + items := make([]string, 0, s.Cardinality()) + + for elem := range s { + b, err := json.Marshal(elem) + if err != nil { + return nil, err + } + + items = append(items, string(b)) + } + + return []byte(fmt.Sprintf("[%s]", strings.Join(items, ","))), nil +} + +// UnmarshalJSON recreates a set from a JSON array, it only decodes +// primitive types. Numbers are decoded as json.Number. +func (s threadUnsafeSet[T]) UnmarshalJSON(b []byte) error { + var i []T + err := json.Unmarshal(b, &i) + if err != nil { + return err + } + s.Append(i...) + + return nil +} diff --git a/vendor/github.com/go-jose/go-jose/v3/json/decode.go b/vendor/github.com/go-jose/go-jose/v3/json/decode.go index 4dbc4146c..50634dd84 100644 --- a/vendor/github.com/go-jose/go-jose/v3/json/decode.go +++ b/vendor/github.com/go-jose/go-jose/v3/json/decode.go @@ -75,14 +75,13 @@ import ( // // The JSON null value unmarshals into an interface, map, pointer, or slice // by setting that Go value to nil. Because null is often used in JSON to mean -// ``not present,'' unmarshaling a JSON null into any other Go type has no effect +// “not present,” unmarshaling a JSON null into any other Go type has no effect // on the value and produces no error. // // When unmarshaling quoted strings, invalid UTF-8 or // invalid UTF-16 surrogate pairs are not treated as an error. // Instead, they are replaced by the Unicode replacement // character U+FFFD. -// func Unmarshal(data []byte, v interface{}) error { // Check for well-formedness. // Avoids filling out half a data structure diff --git a/vendor/github.com/go-jose/go-jose/v3/json/encode.go b/vendor/github.com/go-jose/go-jose/v3/json/encode.go index ea0a13619..98de68ce1 100644 --- a/vendor/github.com/go-jose/go-jose/v3/json/encode.go +++ b/vendor/github.com/go-jose/go-jose/v3/json/encode.go @@ -58,6 +58,7 @@ import ( // becomes a member of the object unless // - the field's tag is "-", or // - the field is empty and its tag specifies the "omitempty" option. +// // The empty values are false, 0, any // nil pointer or interface value, and any array, slice, map, or string of // length zero. The object's default key string is the struct field name @@ -65,28 +66,28 @@ import ( // the struct field's tag value is the key name, followed by an optional comma // and options. Examples: // -// // Field is ignored by this package. -// Field int `json:"-"` +// // Field is ignored by this package. +// Field int `json:"-"` // -// // Field appears in JSON as key "myName". -// Field int `json:"myName"` +// // Field appears in JSON as key "myName". +// Field int `json:"myName"` // -// // Field appears in JSON as key "myName" and -// // the field is omitted from the object if its value is empty, -// // as defined above. -// Field int `json:"myName,omitempty"` +// // Field appears in JSON as key "myName" and +// // the field is omitted from the object if its value is empty, +// // as defined above. +// Field int `json:"myName,omitempty"` // -// // Field appears in JSON as key "Field" (the default), but -// // the field is skipped if empty. -// // Note the leading comma. -// Field int `json:",omitempty"` +// // Field appears in JSON as key "Field" (the default), but +// // the field is skipped if empty. +// // Note the leading comma. +// Field int `json:",omitempty"` // // The "string" option signals that a field is stored as JSON inside a // JSON-encoded string. It applies only to fields of string, floating point, // integer, or boolean types. This extra level of encoding is sometimes used // when communicating with JavaScript programs: // -// Int64String int64 `json:",string"` +// Int64String int64 `json:",string"` // // The key name will be used if it's a non-empty string consisting of // only Unicode letters, digits, dollar signs, percent signs, hyphens, @@ -133,7 +134,6 @@ import ( // JSON cannot represent cyclic data structures and Marshal does not // handle them. Passing cyclic structures to Marshal will result in // an infinite recursion. -// func Marshal(v interface{}) ([]byte, error) { e := &encodeState{} err := e.marshal(v) diff --git a/vendor/github.com/go-jose/go-jose/v3/json/stream.go b/vendor/github.com/go-jose/go-jose/v3/json/stream.go index 9b2b926b0..f03b171e6 100644 --- a/vendor/github.com/go-jose/go-jose/v3/json/stream.go +++ b/vendor/github.com/go-jose/go-jose/v3/json/stream.go @@ -240,7 +240,6 @@ var _ Unmarshaler = (*RawMessage)(nil) // Number, for JSON numbers // string, for JSON string literals // nil, for JSON null -// type Token interface{} const ( diff --git a/vendor/github.com/playwright-community/playwright-go/.gitignore b/vendor/github.com/playwright-community/playwright-go/.gitignore index fa6b0aa17..83e23552e 100644 --- a/vendor/github.com/playwright-community/playwright-go/.gitignore +++ b/vendor/github.com/playwright-community/playwright-go/.gitignore @@ -29,4 +29,6 @@ covprofile api.json _site/ -.jekyll-cache/ \ No newline at end of file +.jekyll-cache/ + +.vscode/settings.json \ No newline at end of file diff --git a/vendor/github.com/playwright-community/playwright-go/.golangci.yaml b/vendor/github.com/playwright-community/playwright-go/.golangci.yaml new file mode 100644 index 000000000..1557a3f3a --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/.golangci.yaml @@ -0,0 +1,6 @@ +--- +linters: + enable-all: false + disable-all: false + enable: + - gofumpt \ No newline at end of file diff --git a/vendor/github.com/playwright-community/playwright-go/CONTRIBUTING.md b/vendor/github.com/playwright-community/playwright-go/CONTRIBUTING.md index 7b00cd505..3b11995ec 100644 --- a/vendor/github.com/playwright-community/playwright-go/CONTRIBUTING.md +++ b/vendor/github.com/playwright-community/playwright-go/CONTRIBUTING.md @@ -1,5 +1,8 @@ # Contributing +## Code style +The Go code is linted with [golangci-lint](https://golangci-lint.run/) and formatted with [gofumpt](https://github.com/mvdan/gofumpt). Please configure your editor to run the tools while developing and make sure to run the tools before committing any code. + ## Tests ### Test coverage diff --git a/vendor/github.com/playwright-community/playwright-go/Dockerfile.example b/vendor/github.com/playwright-community/playwright-go/Dockerfile.example new file mode 100644 index 000000000..6597cc5c5 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/Dockerfile.example @@ -0,0 +1,25 @@ +# Stage 1: Modules caching +FROM golang:1.21 as modules +COPY go.mod go.sum /modules/ +WORKDIR /modules +RUN go mod download + +# Stage 2: Build +FROM golang:1.21 as builder +COPY --from=modules /go/pkg /go/pkg +COPY . /workdir +WORKDIR /workdir +# Install playwright cli with right version for later use +RUN PWGO_VER=$(grep -oE "playwright-go v\S+" /workdir/go.mod | sed 's/playwright-go //g') \ + && go install github.com/playwright-community/playwright-go/cmd/playwright@${PWGO_VER} +# Build your app +RUN GOOS=linux GOARCH=amd64 go build -o /bin/myapp + +# Stage 3: Final +FROM ubuntu:jammy +COPY --from=builder /go/bin/playwright /bin/myapp / +RUN apt-get update && apt-get install -y ca-certificates tzdata \ + # Install dependencies and all browsers (or specify one) + && /playwright install --with-deps \ + && rm -rf /var/lib/apt/lists/* +CMD ["/myapp"] \ No newline at end of file diff --git a/vendor/github.com/playwright-community/playwright-go/README.md b/vendor/github.com/playwright-community/playwright-go/README.md index 09dc5c011..99f4aaf0a 100644 --- a/vendor/github.com/playwright-community/playwright-go/README.md +++ b/vendor/github.com/playwright-community/playwright-go/README.md @@ -5,7 +5,7 @@ [![PkgGoDev](https://pkg.go.dev/badge/github.com/playwright-community/playwright-go)](https://pkg.go.dev/github.com/playwright-community/playwright-go) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](http://opensource.org/licenses/MIT) [![Go Report Card](https://goreportcard.com/badge/github.com/playwright-community/playwright-go)](https://goreportcard.com/report/github.com/playwright-community/playwright-go) ![Build Status](https://github.com/playwright-community/playwright-go/workflows/Go/badge.svg) -[![Join Slack](https://img.shields.io/badge/join-slack-infomational)](https://aka.ms/playwright-slack) [![Coverage Status](https://coveralls.io/repos/github/playwright-community/playwright-go/badge.svg?branch=main)](https://coveralls.io/github/playwright-community/playwright-go?branch=main) [![Chromium version](https://img.shields.io/badge/chromium-119.0.6045.9-blue.svg?logo=google-chrome)](https://www.chromium.org/Home) [![Firefox version](https://img.shields.io/badge/firefox-118.0.1-blue.svg?logo=mozilla-firefox)](https://www.mozilla.org/en-US/firefox/new/) [![WebKit version](https://img.shields.io/badge/webkit-17.4-blue.svg?logo=safari)](https://webkit.org/) +[![Join Slack](https://img.shields.io/badge/join-slack-infomational)](https://aka.ms/playwright-slack) [![Coverage Status](https://coveralls.io/repos/github/playwright-community/playwright-go/badge.svg?branch=main)](https://coveralls.io/github/playwright-community/playwright-go?branch=main) [![Chromium version](https://img.shields.io/badge/chromium-127.0.6533.17-blue.svg?logo=google-chrome)](https://www.chromium.org/Home) [![Firefox version](https://img.shields.io/badge/firefox-127.0-blue.svg?logo=mozilla-firefox)](https://www.mozilla.org/en-US/firefox/new/) [![WebKit version](https://img.shields.io/badge/webkit-17.4-blue.svg?logo=safari)](https://webkit.org/) [API reference](https://playwright.dev/docs/api/class-playwright) | [Example recipes](https://github.com/playwright-community/playwright-go/tree/main/examples) @@ -13,9 +13,9 @@ Playwright is a Go library to automate [Chromium](https://www.chromium.org/Home) | | Linux | macOS | Windows | | :--- | :---: | :---: | :---: | -| Chromium 119.0.6045.9 | ✅ | ✅ | ✅ | +| Chromium 127.0.6533.17 | ✅ | ✅ | ✅ | | WebKit 17.4 | ✅ | ✅ | ✅ | -| Firefox 118.0.1 | ✅ | ✅ | ✅ | +| Firefox 127.0 | ✅ | ✅ | ✅ | Headless execution is supported for all the browsers on all platforms. @@ -103,8 +103,12 @@ func main() { } ``` +## Docker +Refer to the [Dockerfile.example](./Dockerfile.example) to build your own docker image. + ## More examples +* Refer to [helper_test.go](./tests/helper_test.go) for End-To-End testing * [Downloading files](./examples/download/main.go) * [End-To-End testing a website](./examples/end-to-end-testing/main.go) * [Executing JavaScript in the browser](./examples/javascript/main.go) diff --git a/vendor/github.com/playwright-community/playwright-go/artifact.go b/vendor/github.com/playwright-community/playwright-go/artifact.go index 35e910d83..c76b8927d 100644 --- a/vendor/github.com/playwright-community/playwright-go/artifact.go +++ b/vendor/github.com/playwright-community/playwright-go/artifact.go @@ -18,9 +18,6 @@ func (a *artifactImpl) PathAfterFinished() (string, error) { return "", errors.New("Path is not available when connecting remotely. Use SaveAs() to save a local copy") } path, err := a.channel.Send("pathAfterFinished") - if path == nil { - return "", err - } return path.(string), err } @@ -40,11 +37,11 @@ func (a *artifactImpl) SaveAs(path string) error { } func (a *artifactImpl) Failure() error { - failure, err := a.channel.Send("failure") - if failure == nil { + reason, err := a.channel.Send("failure") + if reason == nil { return err } - return fmt.Errorf("%v", failure) + return fmt.Errorf("%w: %v", ErrPlaywright, reason) } func (a *artifactImpl) Delete() error { @@ -62,10 +59,7 @@ func (a *artifactImpl) ReadIntoBuffer() ([]byte, error) { if err != nil { return nil, err } - stream := fromNullableChannel(streamChannel) - if stream == nil { - return nil, nil - } + stream := fromChannel(streamChannel) return stream.(*streamImpl).ReadAll() } diff --git a/vendor/github.com/playwright-community/playwright-go/assertions.go b/vendor/github.com/playwright-community/playwright-go/assertions.go index 43f35b4a9..c7e97f5f7 100644 --- a/vendor/github.com/playwright-community/playwright-go/assertions.go +++ b/vendor/github.com/playwright-community/playwright-go/assertions.go @@ -1,6 +1,7 @@ package playwright import ( + "errors" "fmt" "reflect" "regexp" @@ -103,7 +104,7 @@ func toExpectedTextValues( matchSubstring bool, normalizeWhiteSpace bool, ignoreCase *bool, -) []expectedTextValue { +) ([]expectedTextValue, error) { var out []expectedTextValue for _, item := range items { switch item := item.(type) { @@ -123,9 +124,11 @@ func toExpectedTextValues( NormalizeWhiteSpace: Bool(normalizeWhiteSpace), IgnoreCase: ignoreCase, }) + default: + return nil, errors.New("value must be a string or regexp") } } - return out + return out, nil } func convertToInterfaceList(v interface{}) []interface{} { diff --git a/vendor/github.com/playwright-community/playwright-go/binding_call.go b/vendor/github.com/playwright-community/playwright-go/binding_call.go index 80a13153e..a018557bf 100644 --- a/vendor/github.com/playwright-community/playwright-go/binding_call.go +++ b/vendor/github.com/playwright-community/playwright-go/binding_call.go @@ -1,7 +1,10 @@ package playwright import ( - "log" + "fmt" + "strings" + + "github.com/go-stack/stack" ) type BindingCall interface { @@ -31,7 +34,7 @@ func (b *bindingCallImpl) Call(f BindingCallFunction) { if _, err := b.channel.Send("reject", map[string]interface{}{ "error": serializeError(r.(error)), }); err != nil { - log.Printf("could not reject BindingCall: %v", err) + logger.Printf("could not reject BindingCall: %v\n", err) } } }() @@ -57,7 +60,23 @@ func (b *bindingCallImpl) Call(f BindingCallFunction) { "result": serializeArgument(result), }) if err != nil { - log.Printf("could not resolve BindingCall: %v", err) + logger.Printf("could not resolve BindingCall: %v\n", err) + } +} + +func serializeError(err error) map[string]interface{} { + st := stack.Trace().TrimRuntime() + if len(st) == 0 { // https://github.com/go-stack/stack/issues/27 + st = stack.Trace() + } + return map[string]interface{}{ + "error": &Error{ + Name: "Playwright for Go Error", + Message: err.Error(), + Stack: strings.ReplaceAll(strings.TrimFunc(fmt.Sprintf("%+v", st), func(r rune) bool { + return r == '[' || r == ']' + }), " ", "\n"), + }, } } diff --git a/vendor/github.com/playwright-community/playwright-go/browser.go b/vendor/github.com/playwright-community/playwright-go/browser.go index 6f14ca2d7..cca7abbd6 100644 --- a/vendor/github.com/playwright-community/playwright-go/browser.go +++ b/vendor/github.com/playwright-community/playwright-go/browser.go @@ -2,6 +2,7 @@ package playwright import ( "encoding/json" + "errors" "fmt" "os" "path/filepath" @@ -10,11 +11,11 @@ import ( type browserImpl struct { channelOwner isConnected bool - isClosedOrClosing bool shouldCloseConnectionOnClose bool contexts []BrowserContext browserType BrowserType chromiumTracingPath *string + closeReason *string } func (b *browserImpl) BrowserType() BrowserType { @@ -78,7 +79,7 @@ func (b *browserImpl) NewContext(options ...BrowserNewContextOptions) (BrowserCo } channel, err := b.channel.Send("newContext", options, overrides) if err != nil { - return nil, fmt.Errorf("could not send message: %w", err) + return nil, err } context := fromChannel(channel).(*browserContextImpl) context.browser = b @@ -107,7 +108,7 @@ func (b *browserImpl) NewPage(options ...BrowserNewPageOptions) (Page, error) { func (b *browserImpl) NewBrowserCDPSession() (CDPSession, error) { channel, err := b.channel.Send("newBrowserCDPSession") if err != nil { - return nil, fmt.Errorf("could not send message: %w", err) + return nil, err } cdpSession := fromChannel(channel).(*cdpSessionImpl) @@ -121,19 +122,22 @@ func (b *browserImpl) Contexts() []BrowserContext { return b.contexts } -func (b *browserImpl) Close() error { - if b.isClosedOrClosing { - return nil - } - b.Lock() - b.isClosedOrClosing = true - b.Unlock() - _, err := b.channel.Send("close") - if err != nil && !isSafeCloseError(err) { - return fmt.Errorf("close browser failed: %w", err) +func (b *browserImpl) Close(options ...BrowserCloseOptions) (err error) { + if len(options) == 1 { + b.closeReason = options[0].Reason } + if b.shouldCloseConnectionOnClose { - return b.connection.Stop(errMsgBrowserClosed) + err = b.connection.Stop() + } else if b.closeReason != nil { + _, err = b.channel.Send("close", map[string]interface{}{ + "reason": b.closeReason, + }) + } else { + _, err = b.channel.Send("close") + } + if err != nil && !errors.Is(err, ErrTargetClosed) { + return fmt.Errorf("close browser failed: %w", err) } return nil } @@ -175,11 +179,11 @@ func (b *browserImpl) StopTracing() ([]byte, error) { return binary, err } if b.chromiumTracingPath != nil { - err := os.MkdirAll(filepath.Dir(*b.chromiumTracingPath), 0777) + err := os.MkdirAll(filepath.Dir(*b.chromiumTracingPath), 0o777) if err != nil { return binary, err } - err = os.WriteFile(*b.chromiumTracingPath, binary, 0644) + err = os.WriteFile(*b.chromiumTracingPath, binary, 0o644) if err != nil { return binary, err } @@ -189,7 +193,6 @@ func (b *browserImpl) StopTracing() ([]byte, error) { func (b *browserImpl) onClose() { b.Lock() - b.isClosedOrClosing = true if b.isConnected { b.isConnected = false b.Unlock() diff --git a/vendor/github.com/playwright-community/playwright-go/browser_context.go b/vendor/github.com/playwright-community/playwright-go/browser_context.go index 19c29c4d6..2733bad52 100644 --- a/vendor/github.com/playwright-community/playwright-go/browser_context.go +++ b/vendor/github.com/playwright-community/playwright-go/browser_context.go @@ -4,38 +4,57 @@ import ( "encoding/json" "errors" "fmt" - "log" "os" + "regexp" "strings" + "sync" + + "github.com/playwright-community/playwright-go/internal/safe" + "golang.org/x/exp/slices" ) type browserContextImpl struct { channelOwner - timeoutSettings *timeoutSettings - isClosedOrClosing bool - options *BrowserNewContextOptions - pages []Page - routes []*routeHandlerEntry - ownedPage Page - browser *browserImpl - serviceWorkers []Worker - backgroundPages []Page - bindings map[string]BindingCallFunction - tracing *tracingImpl - request *apiRequestContextImpl - harRecorders map[string]harRecordingMetadata - closed chan struct{} + timeoutSettings *timeoutSettings + closeWasCalled bool + options *BrowserNewContextOptions + pages []Page + routes []*routeHandlerEntry + ownedPage Page + browser *browserImpl + serviceWorkers []Worker + backgroundPages []Page + bindings *safe.SyncMap[string, BindingCallFunction] + tracing *tracingImpl + request *apiRequestContextImpl + harRecorders map[string]harRecordingMetadata + closed chan struct{} + closeReason *string + harRouters []*harRouter + clock Clock +} + +func (b *browserContextImpl) Clock() Clock { + return b.clock } func (b *browserContextImpl) SetDefaultNavigationTimeout(timeout float64) { - b.timeoutSettings.SetDefaultNavigationTimeout(&timeout) + b.setDefaultNavigationTimeoutImpl(&timeout) +} + +func (b *browserContextImpl) setDefaultNavigationTimeoutImpl(timeout *float64) { + b.timeoutSettings.SetDefaultNavigationTimeout(timeout) b.channel.SendNoReply("setDefaultNavigationTimeoutNoReply", map[string]interface{}{ "timeout": timeout, }) } func (b *browserContextImpl) SetDefaultTimeout(timeout float64) { - b.timeoutSettings.SetDefaultTimeout(&timeout) + b.setDefaultTimeoutImpl(&timeout) +} + +func (b *browserContextImpl) setDefaultTimeoutImpl(timeout *float64) { + b.timeoutSettings.SetDefaultTimeout(timeout) b.channel.SendNoReply("setDefaultTimeoutNoReply", map[string]interface{}{ "timeout": timeout, }) @@ -50,6 +69,7 @@ func (b *browserContextImpl) Pages() []Page { func (b *browserContextImpl) Browser() Browser { return b.browser } + func (b *browserContextImpl) Tracing() Tracing { return b.tracing } @@ -67,7 +87,7 @@ func (b *browserContextImpl) NewCDPSession(page interface{}) (CDPSession, error) channel, err := b.channel.Send("newCDPSession", params) if err != nil { - return nil, fmt.Errorf("could not send message: %w", err) + return nil, err } cdpSession := fromChannel(channel).(*cdpSessionImpl) @@ -81,7 +101,7 @@ func (b *browserContextImpl) NewPage() (Page, error) { } channel, err := b.channel.Send("newPage") if err != nil { - return nil, fmt.Errorf("could not send message: %w", err) + return nil, err } return fromChannel(channel).(*pageImpl), nil } @@ -91,7 +111,7 @@ func (b *browserContextImpl) Cookies(urls ...string) ([]Cookie, error) { "urls": urls, }) if err != nil { - return nil, fmt.Errorf("could not send message: %w", err) + return nil, err } cookies := make([]Cookie, len(result.([]interface{}))) for i, item := range result.([]interface{}) { @@ -109,8 +129,53 @@ func (b *browserContextImpl) AddCookies(cookies []OptionalCookie) error { return err } -func (b *browserContextImpl) ClearCookies() error { - _, err := b.channel.Send("clearCookies") +func (b *browserContextImpl) ClearCookies(options ...BrowserContextClearCookiesOptions) error { + params := map[string]interface{}{} + if len(options) == 1 { + if options[0].Domain != nil { + switch t := options[0].Domain.(type) { + case string: + params["domain"] = t + case *string: + params["domain"] = t + case *regexp.Regexp: + pattern, flag := convertRegexp(t) + params["domainRegexSource"] = pattern + params["domainRegexFlags"] = flag + default: + return errors.New("invalid type for domain, expected string or *regexp.Regexp") + } + } + if options[0].Name != nil { + switch t := options[0].Name.(type) { + case string: + params["name"] = t + case *string: + params["name"] = t + case *regexp.Regexp: + pattern, flag := convertRegexp(t) + params["nameRegexSource"] = pattern + params["nameRegexFlags"] = flag + default: + return errors.New("invalid type for name, expected string or *regexp.Regexp") + } + } + if options[0].Path != nil { + switch t := options[0].Path.(type) { + case string: + params["path"] = t + case *string: + params["path"] = t + case *regexp.Regexp: + pattern, flag := convertRegexp(t) + params["pathRegexSource"] = pattern + params["pathRegexFlags"] = flag + default: + return errors.New("invalid type for path, expected string or *regexp.Regexp") + } + } + } + _, err := b.channel.Send("clearCookies", params) return err } @@ -176,18 +241,21 @@ func (b *browserContextImpl) ExposeBinding(name string, binding BindingCallFunct needsHandle = handle[0] } for _, page := range b.Pages() { - if _, ok := page.(*pageImpl).bindings[name]; ok { + if _, ok := page.(*pageImpl).bindings.Load(name); ok { return fmt.Errorf("Function '%s' has been already registered in one of the pages", name) } } - if _, ok := b.bindings[name]; ok { + if _, ok := b.bindings.Load(name); ok { return fmt.Errorf("Function '%s' has been already registered", name) } - b.bindings[name] = binding _, err := b.channel.Send("exposeBinding", map[string]interface{}{ "name": name, "needsHandle": needsHandle, }) + if err != nil { + return err + } + b.bindings.Store(name, binding) return err } @@ -198,21 +266,56 @@ func (b *browserContextImpl) ExposeFunction(name string, binding ExposedFunction } func (b *browserContextImpl) Route(url interface{}, handler routeHandler, times ...int) error { - b.routes = append(b.routes, newRouteHandlerEntry(newURLMatcher(url, b.options.BaseURL), handler, times...)) + b.Lock() + defer b.Unlock() + b.routes = slices.Insert(b.routes, 0, newRouteHandlerEntry(newURLMatcher(url, b.options.BaseURL), handler, times...)) return b.updateInterceptionPatterns() } func (b *browserContextImpl) Unroute(url interface{}, handlers ...routeHandler) error { + removed, remaining, err := unroute(b.routes, url, handlers...) + if err != nil { + return err + } + return b.unrouteInternal(removed, remaining, UnrouteBehaviorDefault) +} + +func (b *browserContextImpl) unrouteInternal(removed []*routeHandlerEntry, remaining []*routeHandlerEntry, behavior *UnrouteBehavior) error { b.Lock() defer b.Unlock() - - routes, err := unroute(b.routes, url, handlers...) - if err != nil { + b.routes = remaining + if err := b.updateInterceptionPatterns(); err != nil { return err } - b.routes = routes + if behavior == nil || behavior == UnrouteBehaviorDefault { + return nil + } + wg := &sync.WaitGroup{} + for _, entry := range removed { + wg.Add(1) + go func(entry *routeHandlerEntry) { + defer wg.Done() + entry.Stop(string(*behavior)) + }(entry) + } + wg.Wait() + return nil +} - return b.updateInterceptionPatterns() +func (b *browserContextImpl) UnrouteAll(options ...BrowserContextUnrouteAllOptions) error { + var behavior *UnrouteBehavior + if len(options) == 1 { + behavior = options[0].Behavior + } + defer b.disposeHarRouters() + return b.unrouteInternal(b.routes, []*routeHandlerEntry{}, behavior) +} + +func (b *browserContextImpl) disposeHarRouters() { + for _, router := range b.harRouters { + router.dispose() + } + b.harRouters = make([]*harRouter, 0) } func (b *browserContextImpl) Request() APIRequestContext { @@ -243,6 +346,7 @@ func (b *browserContextImpl) RouteFromHAR(har string, options ...BrowserContextR notFound = HarNotFoundAbort } router := newHarRouter(b.connection.localUtils, har, *notFound, opt.URL) + b.harRouters = append(b.harRouters, router) return router.addContextRoute(b) } @@ -260,7 +364,7 @@ func (b *browserContextImpl) waiterForEvent(event string, options ...BrowserCont predicate = options[0].Predicate } waiter := newWaiter().WithTimeout(timeout) - waiter.RejectOnEvent(b, "close", errors.New("context closed")) + waiter.RejectOnEvent(b, "close", ErrTargetClosed) return waiter.WaitForEvent(b, event, predicate) } @@ -305,13 +409,24 @@ func (b *browserContextImpl) ExpectPage(cb func() error, options ...BrowserConte return ret.(Page), nil } -func (b *browserContextImpl) Close() error { - if b.isClosedOrClosing { +func (b *browserContextImpl) Close(options ...BrowserContextCloseOptions) error { + if b.closeWasCalled { return nil } - b.Lock() - b.isClosedOrClosing = true - b.Unlock() + if len(options) == 1 { + b.closeReason = options[0].Reason + } + b.closeWasCalled = true + + _, err := b.channel.connection.WrapAPICall(func() (interface{}, error) { + return nil, b.request.Dispose(APIRequestContextDisposeOptions{ + Reason: b.closeReason, + }) + }, true) + if err != nil { + return err + } + innerClose := func() (interface{}, error) { for harId, harMetaData := range b.harRecorders { overrides := map[string]interface{}{} @@ -346,12 +461,17 @@ func (b *browserContextImpl) Close() error { return nil, nil } - _, err := b.channel.connection.WrapAPICall(innerClose, true) + _, err = b.channel.connection.WrapAPICall(innerClose, true) if err != nil { return err } - _, err = b.channel.Send("close") + _, err = b.channel.Send("close", map[string]interface{}{ + "reason": b.closeReason, + }) + if err != nil { + return err + } <-b.closed return err } @@ -417,11 +537,11 @@ func (b *browserContextImpl) StorageState(paths ...string) (*StorageState, error } func (b *browserContextImpl) onBinding(binding *bindingCallImpl) { - function := b.bindings[binding.initializer["name"].(string)] - if function == nil { + function, ok := b.bindings.Load(binding.initializer["name"].(string)) + if !ok || function == nil { return } - go binding.Call(function) + binding.Call(function) } func (b *browserContextImpl) onClose() { @@ -436,6 +556,7 @@ func (b *browserContextImpl) onClose() { b.browser.contexts = contexts b.browser.Unlock() } + b.disposeHarRouters() b.Emit("close", b) } @@ -453,37 +574,55 @@ func (b *browserContextImpl) onPage(page Page) { func (b *browserContextImpl) onRoute(route *routeImpl) { go func() { b.Lock() - defer b.Unlock() route.context = b + page := route.Request().(*requestImpl).safePage() routes := make([]*routeHandlerEntry, len(b.routes)) copy(routes, b.routes) + b.Unlock() - url := route.Request().URL() - for i, handlerEntry := range routes { - if !handlerEntry.Matches(url) { - continue - } - if handlerEntry.WillExceed() { - b.routes = append(b.routes[:i], b.routes[i+1:]...) - } - handled := handlerEntry.Handle(route) + checkInterceptionIfNeeded := func() { + b.Lock() + defer b.Unlock() if len(b.routes) == 0 { _, err := b.connection.WrapAPICall(func() (interface{}, error) { err := b.updateInterceptionPatterns() return nil, err }, true) if err != nil { - log.Printf("could not update interception patterns: %v", err) + logger.Printf("could not update interception patterns: %v\n", err) } } + } + + url := route.Request().URL() + for _, handlerEntry := range routes { + // If the page or the context was closed we stall all requests right away. + if (page != nil && page.closeWasCalled) || b.closeWasCalled { + return + } + if !handlerEntry.Matches(url) { + continue + } + if !slices.ContainsFunc(b.routes, func(entry *routeHandlerEntry) bool { + return entry == handlerEntry + }) { + continue + } + if handlerEntry.WillExceed() { + b.routes = slices.DeleteFunc(b.routes, func(rhe *routeHandlerEntry) bool { + return rhe == handlerEntry + }) + } + handled := handlerEntry.Handle(route) + checkInterceptionIfNeeded() yes := <-handled if yes { return } } - if err := route.internalContinue(true); err != nil { - log.Printf("could not continue request: %v", err) - } + // If the page is closed or unrouteAll() was called without waiting and interception disabled, + // the method will throw an error - silence it. + _ = route.internalContinue(true) }() } @@ -547,6 +686,10 @@ func (b *browserContextImpl) ServiceWorkers() []Worker { return b.serviceWorkers } +func (b *browserContextImpl) OnBackgroundPage(fn func(Page)) { + b.On("backgroundpage", fn) +} + func (b *browserContextImpl) OnClose(fn func(BrowserContext)) { b.On("close", fn) } @@ -583,15 +726,28 @@ func (b *browserContextImpl) OnWebError(fn func(WebError)) { b.On("weberror", fn) } +func (b *browserContextImpl) effectiveCloseReason() *string { + b.Lock() + defer b.Unlock() + if b.closeReason != nil { + return b.closeReason + } + if b.browser != nil { + return b.browser.closeReason + } + return nil +} + func newBrowserContext(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *browserContextImpl { bt := &browserContextImpl{ timeoutSettings: newTimeoutSettings(nil), pages: make([]Page, 0), backgroundPages: make([]Page, 0), routes: make([]*routeHandlerEntry, 0), - bindings: make(map[string]BindingCallFunction), + bindings: safe.NewSyncMap[string, BindingCallFunction](), harRecorders: make(map[string]harRecordingMetadata), closed: make(chan struct{}, 1), + harRouters: make([]*harRouter, 0), } bt.createChannelOwner(bt, parent, objectType, guid, initializer) if parent.objectType == "Browser" { @@ -600,8 +756,9 @@ func newBrowserContext(parent *channelOwner, objectType string, guid string, ini } bt.tracing = fromChannel(initializer["tracing"]).(*tracingImpl) bt.request = fromChannel(initializer["requestContext"]).(*apiRequestContextImpl) + bt.clock = newClock(bt) bt.channel.On("bindingCall", func(params map[string]interface{}) { - bt.onBinding(fromChannel(params["binding"]).(*bindingCallImpl)) + go bt.onBinding(fromChannel(params["binding"]).(*bindingCallImpl)) }) bt.channel.On("close", bt.onClose) @@ -647,8 +804,9 @@ func newBrowserContext(parent *channelOwner, objectType string, guid string, ini }) bt.channel.On( "pageError", func(ev map[string]interface{}) { - err := &Error{} - remapMapToStruct(ev["error"].(map[string]interface{})["error"], err) + pwErr := &Error{} + remapMapToStruct(ev["error"].(map[string]interface{})["error"], pwErr) + err := parseError(*pwErr) page := fromNullableChannel(ev["page"]) if page != nil { bt.Emit("weberror", newWebError(page.(*pageImpl), err)) diff --git a/vendor/github.com/playwright-community/playwright-go/browser_type.go b/vendor/github.com/playwright-community/playwright-go/browser_type.go index 23c87dc3d..e1202caaa 100644 --- a/vendor/github.com/playwright-community/playwright-go/browser_type.go +++ b/vendor/github.com/playwright-community/playwright-go/browser_type.go @@ -25,7 +25,7 @@ func (b *browserTypeImpl) Launch(options ...BrowserTypeLaunchOptions) (Browser, } channel, err := b.channel.Send("launch", options, overrides) if err != nil { - return nil, fmt.Errorf("could not send message: %w", err) + return nil, err } browser := fromChannel(channel).(*browserImpl) b.didLaunchBrowser(browser) @@ -81,12 +81,13 @@ func (b *browserTypeImpl) LaunchPersistentContext(userDataDir string, options .. } channel, err := b.channel.Send("launchPersistentContext", options, overrides) if err != nil { - return nil, fmt.Errorf("could not send message: %w", err) + return nil, err } context := fromChannel(channel).(*browserContextImpl) b.didCreateContext(context, option, tracesDir) return context, nil } + func (b *browserTypeImpl) Connect(wsEndpoint string, options ...BrowserTypeConnectOptions) (Browser, error) { overrides := map[string]interface{}{ "wsEndpoint": wsEndpoint, @@ -97,9 +98,15 @@ func (b *browserTypeImpl) Connect(wsEndpoint string, options ...BrowserTypeConne return nil, err } jsonPipe := fromChannel(pipe.(map[string]interface{})["pipe"]).(*jsonPipe) - connection := newConnection(jsonPipe.Close, localUtils) - connection.isRemote = true - var browser *browserImpl + connection := newConnection(jsonPipe, localUtils) + + playwright, err := connection.Start() + if err != nil { + return nil, err + } + playwright.setSelectors(b.playwright.Selectors) + browser := fromChannel(playwright.initializer["preLaunchedBrowser"]).(*browserImpl) + browser.shouldCloseConnectionOnClose = true pipeClosed := func() { for _, context := range browser.Contexts() { pages := context.Pages() @@ -109,21 +116,10 @@ func (b *browserTypeImpl) Connect(wsEndpoint string, options ...BrowserTypeConne context.(*browserContextImpl).onClose() } browser.onClose() - connection.cleanup(errMsgBrowserClosed) + connection.cleanup() } jsonPipe.On("closed", pipeClosed) - connection.onmessage = func(message map[string]interface{}) error { - if err := jsonPipe.Send(message); err != nil { - pipeClosed() - return err - } - return nil - } - jsonPipe.On("message", connection.Dispatch) - playwright := connection.Start() - playwright.setSelectors(b.playwright.Selectors) - browser = fromChannel(playwright.initializer["preLaunchedBrowser"]).(*browserImpl) - browser.shouldCloseConnectionOnClose = true + b.didLaunchBrowser(browser) return browser, nil } diff --git a/vendor/github.com/playwright-community/playwright-go/channel.go b/vendor/github.com/playwright-community/playwright-go/channel.go index 0499368f2..81ec21600 100644 --- a/vendor/github.com/playwright-community/playwright-go/channel.go +++ b/vendor/github.com/playwright-community/playwright-go/channel.go @@ -1,10 +1,5 @@ package playwright -import ( - "log" - "reflect" -) - type channel struct { eventEmitter guid string @@ -41,14 +36,11 @@ func (c *channel) innerSend(method string, returnAsDict bool, options ...interfa if returnAsDict { return result, nil } - if reflect.TypeOf(result).Kind() == reflect.Map { - mapV := result.(map[string]interface{}) - if len(mapV) == 0 { - return nil, nil - } + if mapV, ok := result.(map[string]interface{}); ok && len(mapV) <= 1 { for key := range mapV { return mapV[key], nil } + return nil, nil } return result, nil } @@ -59,7 +51,7 @@ func (c *channel) SendNoReply(method string, options ...interface{}) { return c.connection.sendMessageToServer(c.owner, method, params, true) }, false) if err != nil { - log.Printf("SendNoReply failed: %v", err) + logger.Printf("SendNoReply failed: %v\n", err) } } @@ -70,6 +62,5 @@ func newChannel(owner *channelOwner, object interface{}) *channel { owner: owner, object: object, } - channel.initEventEmitter() return channel } diff --git a/vendor/github.com/playwright-community/playwright-go/channel_owner.go b/vendor/github.com/playwright-community/playwright-go/channel_owner.go index 6c6114990..007be70e5 100644 --- a/vendor/github.com/playwright-community/playwright-go/channel_owner.go +++ b/vendor/github.com/playwright-community/playwright-go/channel_owner.go @@ -23,7 +23,7 @@ func (c *channelOwner) dispose(reason ...string) { if c.parent != nil { delete(c.parent.objects, c.guid) } - delete(c.connection.objects, c.guid) + c.connection.objects.Delete(c.guid) if len(reason) > 0 { c.wasCollected = reason[0] == "gc" } @@ -89,11 +89,10 @@ func (c *channelOwner) createChannelOwner(self interface{}, parent *channelOwner c.parent.objects[guid] = c } if c.connection != nil { - c.connection.objects[guid] = c + c.connection.objects.Store(guid, c) } c.channel = newChannel(c, self) c.eventToSubscriptionMapping = map[string]string{} - c.initEventEmitter() } type rootChannelOwner struct { diff --git a/vendor/github.com/playwright-community/playwright-go/clock.go b/vendor/github.com/playwright-community/playwright-go/clock.go new file mode 100644 index 000000000..8bab03740 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/clock.go @@ -0,0 +1,111 @@ +package playwright + +import ( + "errors" + "time" +) + +type clockImpl struct { + browserCtx *browserContextImpl +} + +func newClock(bCtx *browserContextImpl) Clock { + return &clockImpl{ + browserCtx: bCtx, + } +} + +func (c *clockImpl) FastForward(ticks interface{}) error { + params, err := parseTicks(ticks) + if err != nil { + return err + } + + _, err = c.browserCtx.channel.Send("clockFastForward", params) + return err +} + +func (c *clockImpl) Install(options ...ClockInstallOptions) (err error) { + params := map[string]any{} + if len(options) == 1 { + if options[0].Time != nil { + params, err = parseTime(options[0].Time) + if err != nil { + return err + } + } + } + + _, err = c.browserCtx.channel.Send("clockInstall", params) + + return err +} + +func (c *clockImpl) PauseAt(time interface{}) error { + params, err := parseTime(time) + if err != nil { + return err + } + + _, err = c.browserCtx.channel.Send("clockPauseAt", params) + return err +} + +func (c *clockImpl) Resume() error { + _, err := c.browserCtx.channel.Send("clockResume") + return err +} + +func (c *clockImpl) RunFor(ticks interface{}) error { + params, err := parseTicks(ticks) + if err != nil { + return err + } + + _, err = c.browserCtx.channel.Send("clockRunFor", params) + return err +} + +func (c *clockImpl) SetFixedTime(time interface{}) error { + params, err := parseTime(time) + if err != nil { + return err + } + + _, err = c.browserCtx.channel.Send("clockSetFixedTime", params) + return err +} + +func (c *clockImpl) SetSystemTime(time interface{}) error { + params, err := parseTime(time) + if err != nil { + return err + } + + _, err = c.browserCtx.channel.Send("clockSetSystemTime", params) + return err +} + +func parseTime(t interface{}) (map[string]any, error) { + switch v := t.(type) { + case int, int64: + return map[string]any{"timeNumber": v}, nil + case string: + return map[string]any{"timeString": v}, nil + case time.Time: + return map[string]any{"timeNumber": v.UnixMilli()}, nil + default: + return nil, errors.New("time should be one of: int, int64, string, time.Time") + } +} + +func parseTicks(ticks interface{}) (map[string]any, error) { + switch v := ticks.(type) { + case int, int64: + return map[string]any{"ticksNumber": v}, nil + case string: + return map[string]any{"ticksString": v}, nil + default: + return nil, errors.New("ticks should be one of: int, int64, string") + } +} diff --git a/vendor/github.com/playwright-community/playwright-go/connection.go b/vendor/github.com/playwright-community/playwright-go/connection.go index 19a43da59..654096736 100644 --- a/vendor/github.com/playwright-community/playwright-go/connection.go +++ b/vendor/github.com/playwright-community/playwright-go/connection.go @@ -3,7 +3,6 @@ package playwright import ( "errors" "fmt" - "log" "reflect" "regexp" "strconv" @@ -13,6 +12,7 @@ import ( "time" "github.com/go-stack/stack" + "github.com/playwright-community/playwright-go/internal/safe" ) var ( @@ -26,82 +26,93 @@ type result struct { } type connection struct { + transport transport apiZone sync.Map - objects map[string]*channelOwner - lastID int - lastIDLock sync.Mutex + objects *safe.SyncMap[string, *channelOwner] + lastID atomic.Uint32 rootObject *rootChannelOwner - callbacks sync.Map + callbacks *safe.SyncMap[uint32, *protocolCallback] afterClose func() onClose func() error - onmessage func(map[string]interface{}) error isRemote bool localUtils *localUtilsImpl tracingCount atomic.Int32 abort chan struct{} - closedError atomic.Value + abortOnce sync.Once + closedError *safeValue[error] } -func (c *connection) Start() *Playwright { - playwright := make(chan *Playwright, 1) +func (c *connection) Start() (*Playwright, error) { go func() { - pw, err := c.rootObject.initialize() - if err != nil { - log.Fatal(err) - return + for { + msg, err := c.transport.Poll() + if err != nil { + _ = c.transport.Close() + c.cleanup(err) + return + } + c.Dispatch(msg) } - playwright <- pw }() - return <-playwright + + c.onClose = func() error { + if err := c.transport.Close(); err != nil { + return err + } + return nil + } + + return c.rootObject.initialize() } -func (c *connection) Stop(errMsg ...string) error { - err := c.onClose() - if err != nil { +func (c *connection) Stop() error { + if err := c.onClose(); err != nil { return err } - c.cleanup(errMsg...) + c.cleanup() return nil } -func (c *connection) cleanup(errMsg ...string) { - if len(errMsg) == 0 { - c.closedError.Store(errors.New("connection closed")) +func (c *connection) cleanup(cause ...error) { + if len(cause) > 0 { + c.closedError.Set(fmt.Errorf("%w: %w", ErrTargetClosed, cause[0])) } else { - c.closedError.Store(errors.New(errMsg[0])) + c.closedError.Set(ErrTargetClosed) } if c.afterClose != nil { c.afterClose() } - select { - case <-c.abort: - default: - close(c.abort) - } + c.abortOnce.Do(func() { + select { + case <-c.abort: + default: + close(c.abort) + } + }) } func (c *connection) Dispatch(msg *message) { - if c.closedError.Load() != nil { + if c.closedError.Get() != nil { return } method := msg.Method if msg.ID != 0 { - cb, _ := c.callbacks.LoadAndDelete(msg.ID) - if cb.(*protocolCallback).noReply { + cb, _ := c.callbacks.LoadAndDelete(uint32(msg.ID)) + if cb.noReply { return } if msg.Error != nil { - cb.(*protocolCallback).SetResult(result{ + cb.SetResult(result{ Error: parseError(msg.Error.Error), }) } else { - cb.(*protocolCallback).SetResult(result{ + cb.SetResult(result{ Data: c.replaceGuidsWithChannels(msg.Result), }) } return } - object := c.objects[msg.GUID] + object, _ := c.objects.Load(msg.GUID) if method == "__create__" { c.createRemoteObject( object, msg.Params["type"].(string), msg.Params["guid"].(string), msg.Params["initializer"], @@ -112,7 +123,7 @@ func (c *connection) Dispatch(msg *message) { return } if method == "__adopt__" { - child, ok := c.objects[msg.Params["guid"].(string)] + child, ok := c.objects.Load(msg.Params["guid"].(string)) if !ok { return } @@ -195,7 +206,7 @@ func (c *connection) replaceGuidsWithChannels(payload interface{}) interface{} { if v.Kind() == reflect.Map { mapV := payload.(map[string]interface{}) if guid, hasGUID := mapV["guid"]; hasGUID { - if channelOwner, ok := c.objects[guid.(string)]; ok { + if channelOwner, ok := c.objects.Load(guid.(string)); ok { return channelOwner.channel } } @@ -208,17 +219,14 @@ func (c *connection) replaceGuidsWithChannels(payload interface{}) interface{} { } func (c *connection) sendMessageToServer(object *channelOwner, method string, params interface{}, noReply bool) (*protocolCallback, error) { - if e := c.closedError.Load(); e != nil { - return nil, e.(error) + if err := c.closedError.Get(); err != nil { + return nil, err } if object.wasCollected { return nil, errors.New("The object has been collected to prevent unbounded heap growth.") } - c.lastIDLock.Lock() - c.lastID++ - id := c.lastID - c.lastIDLock.Unlock() + id := c.lastID.Add(1) cb, _ := c.callbacks.LoadOrStore(id, newProtocolCallback(noReply, c.abort)) var ( metadata = make(map[string]interface{}, 0) @@ -231,7 +239,7 @@ func (c *connection) sendMessageToServer(object *channelOwner, method string, pa } stack = append(stack, apiZone.(parsedStackTrace).frames...) } - metadata["wallTime"] = time.Now().Nanosecond() + metadata["wallTime"] = time.Now().UnixMilli() message := map[string]interface{}{ "id": id, "guid": object.guid, @@ -243,11 +251,11 @@ func (c *connection) sendMessageToServer(object *channelOwner, method string, pa c.LocalUtils().AddStackToTracingNoReply(id, stack) } - if err := c.onmessage(message); err != nil { + if err := c.transport.Send(message); err != nil { return nil, fmt.Errorf("could not send message: %w", err) } - return cb.(*protocolCallback), nil + return cb, nil } func (c *connection) setInTracing(isTracing bool) { @@ -317,15 +325,18 @@ func serializeCallLocation(caller stack.Call) map[string]interface{} { } } -func newConnection(onClose func() error, localUtils ...*localUtilsImpl) *connection { +func newConnection(transport transport, localUtils ...*localUtilsImpl) *connection { connection := &connection{ - abort: make(chan struct{}, 1), - objects: make(map[string]*channelOwner), - onClose: onClose, - isRemote: false, + abort: make(chan struct{}, 1), + callbacks: safe.NewSyncMap[uint32, *protocolCallback](), + objects: safe.NewSyncMap[string, *channelOwner](), + transport: transport, + isRemote: false, + closedError: &safeValue[error]{}, } if len(localUtils) > 0 { connection.localUtils = localUtils[0] + connection.isRemote = true } connection.rootObject = newRootChannelOwner(connection) return connection @@ -343,7 +354,7 @@ func fromNullableChannel(v interface{}) interface{} { } type protocolCallback struct { - Callback chan result + callback chan result noReply bool abort <-chan struct{} } @@ -354,8 +365,12 @@ func (pc *protocolCallback) SetResult(r result) { } select { case <-pc.abort: + select { + case pc.callback <- r: + default: + } return - case pc.Callback <- r: + case pc.callback <- r: } } @@ -364,10 +379,15 @@ func (pc *protocolCallback) GetResult() (interface{}, error) { return nil, nil } select { - case result := <-pc.Callback: + case result := <-pc.callback: return result.Data, result.Error case <-pc.abort: - return nil, errors.New("Connection closed") + select { + case result := <-pc.callback: + return result.Data, result.Error + default: + return nil, errors.New("Connection closed") + } } } @@ -379,7 +399,7 @@ func newProtocolCallback(noReply bool, abort <-chan struct{}) *protocolCallback } } return &protocolCallback{ - Callback: make(chan result), + callback: make(chan result, 1), abort: abort, } } diff --git a/vendor/github.com/playwright-community/playwright-go/element_handle.go b/vendor/github.com/playwright-community/playwright-go/element_handle.go index 51a77105c..a44ae89ff 100644 --- a/vendor/github.com/playwright-community/playwright-go/element_handle.go +++ b/vendor/github.com/playwright-community/playwright-go/element_handle.go @@ -2,6 +2,7 @@ package playwright import ( "encoding/base64" + "errors" "fmt" "os" ) @@ -166,10 +167,20 @@ func (e *elementHandleImpl) ScrollIntoViewIfNeeded(options ...ElementHandleScrol return err } -func (e *elementHandleImpl) SetInputFiles(files []InputFile, options ...ElementHandleSetInputFilesOptions) error { - _, err := e.channel.Send("setInputFiles", map[string]interface{}{ - "files": normalizeFilePayloads(files), - }, options) +func (e *elementHandleImpl) SetInputFiles(files interface{}, options ...ElementHandleSetInputFilesOptions) error { + frame, err := e.OwnerFrame() + if err != nil { + return err + } + if frame == nil { + return errors.New("Cannot set input files to detached element") + } + + params, err := convertInputFiles(files, frame.(*frameImpl).page.browserContext) + if err != nil { + return err + } + _, err = e.channel.Send("setInputFiles", params, options) return err } @@ -250,14 +261,14 @@ func (e *elementHandleImpl) Screenshot(options ...ElementHandleScreenshotOptions } data, err := e.channel.Send("screenshot", options, overrides) if err != nil { - return nil, fmt.Errorf("could not send message :%w", err) + return nil, err } image, err := base64.StdEncoding.DecodeString(data.(string)) if err != nil { return nil, fmt.Errorf("could not decode base64 :%w", err) } if path != nil { - if err := os.WriteFile(*path, image, 0644); err != nil { + if err := os.WriteFile(*path, image, 0o644); err != nil { return nil, err } } @@ -376,18 +387,6 @@ func newElementHandle(parent *channelOwner, objectType string, guid string, init return bt } -func normalizeFilePayloads(files []InputFile) []map[string]string { - out := make([]map[string]string, 0) - for _, file := range files { - out = append(out, map[string]string{ - "name": file.Name, - "mimeType": file.MimeType, - "buffer": base64.StdEncoding.EncodeToString(file.Buffer), - }) - } - return out -} - func transformToStringList(in interface{}) []string { s := in.([]interface{}) diff --git a/vendor/github.com/playwright-community/playwright-go/errors.go b/vendor/github.com/playwright-community/playwright-go/errors.go index e822cbd10..36f7396bb 100644 --- a/vendor/github.com/playwright-community/playwright-go/errors.go +++ b/vendor/github.com/playwright-community/playwright-go/errors.go @@ -1,6 +1,20 @@ package playwright -import "strings" +import ( + "errors" + "fmt" +) + +var ( + // ErrPlaywright wraps all Playwright errors. + // - Use errors.Is to check if the error is a Playwright error. + // - Use errors.As to cast an error to [Error] if you want to access "Stack". + ErrPlaywright = errors.New("playwright") + // ErrTargetClosed usually wraps a reason. + ErrTargetClosed = errors.New("target closed") + // ErrTimeout wraps timeout errors. It can be either Playwright TimeoutError or client timeout. + ErrTimeout = errors.New("timeout") +) // Error represents a Playwright error type Error struct { @@ -27,27 +41,18 @@ func (e *Error) Is(target error) bool { return e.Message == err.Message } -// TimeoutError represents a Playwright TimeoutError -var TimeoutError = &Error{ - Name: "TimeoutError", -} - func parseError(err Error) error { - return &Error{ - Name: err.Name, - Message: err.Message, - Stack: err.Stack, + if err.Name == "TimeoutError" { + return fmt.Errorf("%w: %w: %w", ErrPlaywright, ErrTimeout, &err) + } else if err.Name == "TargetClosedError" { + return fmt.Errorf("%w: %w: %w", ErrPlaywright, ErrTargetClosed, &err) } + return fmt.Errorf("%w: %w", ErrPlaywright, &err) } -const ( - errMsgBrowserClosed = "Browser has been closed" - errMsgBrowserOrContextClosed = "Target page, context or browser has been closed" -) - -func isSafeCloseError(err error) bool { - if err == nil { - return false +func targetClosedError(reason *string) error { + if reason == nil { + return ErrTargetClosed } - return strings.HasSuffix(err.Error(), errMsgBrowserClosed) || strings.HasSuffix(err.Error(), errMsgBrowserOrContextClosed) + return fmt.Errorf("%w: %s", ErrTargetClosed, *reason) } diff --git a/vendor/github.com/playwright-community/playwright-go/event_emitter.go b/vendor/github.com/playwright-community/playwright-go/event_emitter.go index 0c2a9f8fb..3bfd9c2f8 100644 --- a/vendor/github.com/playwright-community/playwright-go/event_emitter.go +++ b/vendor/github.com/playwright-community/playwright-go/event_emitter.go @@ -4,6 +4,8 @@ import ( "math" "reflect" "sync" + + "golang.org/x/exp/slices" ) type EventEmitter interface { @@ -15,45 +17,32 @@ type EventEmitter interface { } type ( - eventRegister struct { - once []interface{} - on []interface{} - } eventEmitter struct { eventsMutex sync.Mutex events map[string]*eventRegister + hasInit bool } -) - -func (e *eventEmitter) Emit(name string, payload ...interface{}) (handled bool) { - e.eventsMutex.Lock() - defer e.eventsMutex.Unlock() - if _, ok := e.events[name]; !ok { - return + eventRegister struct { + sync.Mutex + listeners []listener } - - if len(e.events[name].once) > 0 || len(e.events[name].on) > 0 { - handled = true + listener struct { + handler interface{} + once bool } +) - payloadV := make([]reflect.Value, 0) - - for _, p := range payload { - payloadV = append(payloadV, reflect.ValueOf(p)) - } +func (e *eventEmitter) Emit(name string, payload ...interface{}) (hasListener bool) { + e.eventsMutex.Lock() + e.init() - callHandlers := func(handlers []interface{}) { - for _, handler := range handlers { - handlerV := reflect.ValueOf(handler) - handlerV.Call(payloadV[:int(math.Min(float64(handlerV.Type().NumIn()), float64(len(payloadV))))]) - } + evt, ok := e.events[name] + if !ok { + e.eventsMutex.Unlock() + return } - - callHandlers(e.events[name].on) - callHandlers(e.events[name].once) - - e.events[name].once = make([]interface{}, 0) - return + e.eventsMutex.Unlock() + return evt.callHandlers(payload...) > 0 } func (e *eventEmitter) Once(name string, handler interface{}) { @@ -67,60 +56,97 @@ func (e *eventEmitter) On(name string, handler interface{}) { func (e *eventEmitter) RemoveListener(name string, handler interface{}) { e.eventsMutex.Lock() defer e.eventsMutex.Unlock() - if _, ok := e.events[name]; !ok { - return - } - handlerPtr := reflect.ValueOf(handler).Pointer() - - onHandlers := []interface{}{} - for idx := range e.events[name].on { - eventPtr := reflect.ValueOf(e.events[name].on[idx]).Pointer() - if eventPtr != handlerPtr { - onHandlers = append(onHandlers, e.events[name].on[idx]) - } - } - e.events[name].on = onHandlers + e.init() - onceHandlers := []interface{}{} - for idx := range e.events[name].once { - eventPtr := reflect.ValueOf(e.events[name].once[idx]).Pointer() - if eventPtr != handlerPtr { - onceHandlers = append(onceHandlers, e.events[name].once[idx]) - } + if evt, ok := e.events[name]; ok { + evt.Lock() + defer evt.Unlock() + evt.removeHandler(handler) } - - e.events[name].once = onceHandlers } // ListenerCount count the listeners by name, count all if name is empty func (e *eventEmitter) ListenerCount(name string) int { - count := 0 e.eventsMutex.Lock() - for key := range e.events { - if name == "" || name == key { - count += len(e.events[key].on) + len(e.events[key].once) + defer e.eventsMutex.Unlock() + e.init() + + if name != "" { + evt, ok := e.events[name] + if !ok { + return 0 } + return evt.count() } - e.eventsMutex.Unlock() + + count := 0 + for key := range e.events { + count += e.events[key].count() + } + return count } func (e *eventEmitter) addEvent(name string, handler interface{}, once bool) { e.eventsMutex.Lock() + defer e.eventsMutex.Unlock() + e.init() + if _, ok := e.events[name]; !ok { e.events[name] = &eventRegister{ - on: make([]interface{}, 0), - once: make([]interface{}, 0), + listeners: make([]listener, 0), } } - if once { - e.events[name].once = append(e.events[name].once, handler) - } else { - e.events[name].on = append(e.events[name].on, handler) + e.events[name].addHandler(handler, once) +} + +func (e *eventEmitter) init() { + if !e.hasInit { + e.events = make(map[string]*eventRegister, 0) + e.hasInit = true } - e.eventsMutex.Unlock() } -func (e *eventEmitter) initEventEmitter() { - e.events = make(map[string]*eventRegister) +func (er *eventRegister) addHandler(handler interface{}, once bool) { + er.Lock() + defer er.Unlock() + er.listeners = append(er.listeners, listener{handler: handler, once: once}) +} + +func (er *eventRegister) count() int { + er.Lock() + defer er.Unlock() + return len(er.listeners) +} + +func (e *eventRegister) removeHandler(handler interface{}) { + handlerPtr := reflect.ValueOf(handler).Pointer() + + e.listeners = slices.DeleteFunc(e.listeners, func(l listener) bool { + return reflect.ValueOf(l.handler).Pointer() == handlerPtr + }) +} + +func (er *eventRegister) callHandlers(payloads ...interface{}) int { + payloadV := make([]reflect.Value, 0) + + for _, p := range payloads { + payloadV = append(payloadV, reflect.ValueOf(p)) + } + + handle := func(l listener) { + handlerV := reflect.ValueOf(l.handler) + handlerV.Call(payloadV[:int(math.Min(float64(handlerV.Type().NumIn()), float64(len(payloadV))))]) + } + + er.Lock() + defer er.Unlock() + count := len(er.listeners) + for _, l := range er.listeners { + if l.once { + defer er.removeHandler(l.handler) + } + handle(l) + } + return count } diff --git a/vendor/github.com/playwright-community/playwright-go/fetch.go b/vendor/github.com/playwright-community/playwright-go/fetch.go index aff312778..c88e8b8c0 100644 --- a/vendor/github.com/playwright-community/playwright-go/fetch.go +++ b/vendor/github.com/playwright-community/playwright-go/fetch.go @@ -37,7 +37,7 @@ func (r *apiRequestImpl) NewContext(options ...APIRequestNewContextOptions) (API channel, err := r.channel.Send("newRequest", options, overrides) if err != nil { - return nil, fmt.Errorf("could not send message: %w", err) + return nil, err } return fromChannel(channel).(*apiRequestContextImpl), nil } @@ -48,11 +48,20 @@ func newApiRequestImpl(pw *Playwright) *apiRequestImpl { type apiRequestContextImpl struct { channelOwner - tracing *tracingImpl + tracing *tracingImpl + closeReason *string } -func (r *apiRequestContextImpl) Dispose() error { - _, err := r.channel.Send("dispose") +func (r *apiRequestContextImpl) Dispose(options ...APIRequestContextDisposeOptions) error { + if len(options) == 1 { + r.closeReason = options[0].Reason + } + _, err := r.channel.Send("dispose", map[string]interface{}{ + "reason": r.closeReason, + }) + if errors.Is(err, ErrTargetClosed) { + return nil + } return err } @@ -82,6 +91,9 @@ func (r *apiRequestContextImpl) Fetch(urlOrRequest interface{}, options ...APIRe } func (r *apiRequestContextImpl) innerFetch(url string, request Request, options ...APIRequestContextFetchOptions) (APIResponse, error) { + if r.closeReason != nil { + return nil, fmt.Errorf("%w: %s", ErrTargetClosed, *r.closeReason) + } overrides := map[string]interface{}{} if url != "" { overrides["url"] = url @@ -117,7 +129,15 @@ func (r *apiRequestContextImpl) innerFetch(url string, request Request, options case string: headersArray, ok := overrides["headers"].([]map[string]string) if ok && isJsonContentType(headersArray) { - overrides["jsonData"] = v + if json.Valid([]byte(v)) { + overrides["jsonData"] = v + } else { + data, err := json.Marshal(v) + if err != nil { + return nil, fmt.Errorf("could not marshal data: %w", err) + } + overrides["jsonData"] = string(data) + } } else { overrides["postData"] = base64.StdEncoding.EncodeToString([]byte(v)) } @@ -298,7 +318,7 @@ func (r *apiResponseImpl) Body() ([]byte, error) { }, }) if err != nil { - if isSafeCloseError(err) { + if errors.Is(err, ErrTargetClosed) { return nil, errors.New("response has been disposed") } return nil, err @@ -404,7 +424,6 @@ func isJsonContentType(headers []map[string]string) bool { } } } - } return false } diff --git a/vendor/github.com/playwright-community/playwright-go/file_chooser.go b/vendor/github.com/playwright-community/playwright-go/file_chooser.go index 390ffd572..119e88588 100644 --- a/vendor/github.com/playwright-community/playwright-go/file_chooser.go +++ b/vendor/github.com/playwright-community/playwright-go/file_chooser.go @@ -28,7 +28,7 @@ type InputFile struct { Buffer []byte `json:"buffer"` } -func (f *fileChooserImpl) SetFiles(files []InputFile, options ...FileChooserSetFilesOptions) error { +func (f *fileChooserImpl) SetFiles(files interface{}, options ...FileChooserSetFilesOptions) error { if len(options) == 1 { return f.elementHandle.SetInputFiles(files, ElementHandleSetInputFilesOptions(options[0])) } diff --git a/vendor/github.com/playwright-community/playwright-go/frame.go b/vendor/github.com/playwright-community/playwright-go/frame.go index cd8e508d2..f8ff1ebb8 100644 --- a/vendor/github.com/playwright-community/playwright-go/frame.go +++ b/vendor/github.com/playwright-community/playwright-go/frame.go @@ -6,6 +6,8 @@ import ( "os" "sync" "time" + + mapset "github.com/deckarep/golang-set/v2" ) type frameImpl struct { @@ -17,15 +19,16 @@ type frameImpl struct { url string parentFrame Frame childFrames []Frame - loadStates *safeStringSet + loadStates mapset.Set[string] } func newFrame(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *frameImpl { - var loadStates *safeStringSet + var loadStates mapset.Set[string] + if ls, ok := initializer["loadStates"].([]string); ok { - loadStates = newSafeStringSet(ls) + loadStates = mapset.NewSet[string](ls...) } else { - loadStates = newSafeStringSet([]string{}) + loadStates = mapset.NewSet[string]() } bt := &frameImpl{ name: initializer["name"].(string), @@ -78,7 +81,7 @@ func (f *frameImpl) Goto(url string, options ...FrameGotoOptions) (Response, err "url": url, }, options) if err != nil { - return nil, err + return nil, fmt.Errorf("Frame.Goto %s: %w", url, err) } channelOwner := fromNullableChannel(channel) if channelOwner == nil { @@ -136,7 +139,7 @@ func (f *frameImpl) WaitForLoadState(options ...FrameWaitForLoadStateOptions) er } func (f *frameImpl) waitForLoadStateImpl(state string, timeout *float64, cb func() error) error { - if f.loadStates.Has(state) { + if f.loadStates.ContainsOne(state) { return nil } waiter, err := f.setNavigationWaiter(timeout) @@ -206,8 +209,11 @@ func (f *frameImpl) ExpectNavigation(cb func() error, options ...FrameExpectNavi } predicate := func(events ...interface{}) bool { ev := events[0].(map[string]interface{}) - if ev["error"] != nil { - print("error") + err, ok := ev["error"] + if ok { + // Any failed navigation results in a rejection. + logger.Printf("navigated to %s error: %v", ev["url"].(string), err) + return true } return matcher == nil || matcher.Matches(ev["url"].(string)) } @@ -246,7 +252,7 @@ func (f *frameImpl) setNavigationWaiter(timeout *float64) (*waiter, error) { } else { waiter.WithTimeout(f.page.timeoutSettings.NavigationTimeout()) } - waiter.RejectOnEvent(f.page, "close", fmt.Errorf("Navigation failed because page was closed!")) + waiter.RejectOnEvent(f.page, "close", f.page.closeErrorWithReason()) waiter.RejectOnEvent(f.page, "crash", fmt.Errorf("Navigation failed because page crashed!")) waiter.RejectOnEvent(f.page, "framedetached", fmt.Errorf("Navigating frame was detached!"), func(payload interface{}) bool { frame, ok := payload.(*frameImpl) @@ -452,11 +458,13 @@ func (f *frameImpl) Hover(selector string, options ...FrameHoverOptions) error { return err } -func (f *frameImpl) SetInputFiles(selector string, files []InputFile, options ...FrameSetInputFilesOptions) error { - _, err := f.channel.Send("setInputFiles", map[string]interface{}{ - "selector": selector, - "files": normalizeFilePayloads(files), - }, options) +func (f *frameImpl) SetInputFiles(selector string, files interface{}, options ...FrameSetInputFilesOptions) error { + params, err := convertInputFiles(files, f.page.browserContext) + if err != nil { + return err + } + params.Selector = &selector + _, err = f.channel.Send("setInputFiles", params, options) return err } diff --git a/vendor/github.com/playwright-community/playwright-go/frame_locator.go b/vendor/github.com/playwright-community/playwright-go/frame_locator.go index c5a883723..3bb579489 100644 --- a/vendor/github.com/playwright-community/playwright-go/frame_locator.go +++ b/vendor/github.com/playwright-community/playwright-go/frame_locator.go @@ -120,3 +120,7 @@ func (fl *frameLocatorImpl) Locator(selectorOrLocator interface{}, options ...Fr func (fl *frameLocatorImpl) Nth(index int) FrameLocator { return newFrameLocator(fl.frame, fl.frameSelector+" >> nth="+strconv.Itoa(index)) } + +func (fl *frameLocatorImpl) Owner() Locator { + return newLocator(fl.frame, fl.frameSelector) +} diff --git a/vendor/github.com/playwright-community/playwright-go/generated-enums.go b/vendor/github.com/playwright-community/playwright-go/generated-enums.go index 71fd048eb..0ad547947 100644 --- a/vendor/github.com/playwright-community/playwright-go/generated-enums.go +++ b/vendor/github.com/playwright-community/playwright-go/generated-enums.go @@ -235,6 +235,19 @@ var ( RouteFromHarUpdateContentPolicyAttach = getRouteFromHarUpdateContentPolicy("attach") ) +func getUnrouteBehavior(in string) *UnrouteBehavior { + v := UnrouteBehavior(in) + return &v +} + +type UnrouteBehavior string + +var ( + UnrouteBehaviorWait *UnrouteBehavior = getUnrouteBehavior("wait") + UnrouteBehaviorIgnoreErrors = getUnrouteBehavior("ignoreErrors") + UnrouteBehaviorDefault = getUnrouteBehavior("default") +) + func getMouseButton(in string) *MouseButton { v := MouseButton(in) return &v @@ -256,10 +269,11 @@ func getKeyboardModifier(in string) *KeyboardModifier { type KeyboardModifier string var ( - KeyboardModifierAlt *KeyboardModifier = getKeyboardModifier("Alt") - KeyboardModifierControl = getKeyboardModifier("Control") - KeyboardModifierMeta = getKeyboardModifier("Meta") - KeyboardModifierShift = getKeyboardModifier("Shift") + KeyboardModifierAlt *KeyboardModifier = getKeyboardModifier("Alt") + KeyboardModifierControl = getKeyboardModifier("Control") + KeyboardModifierControlOrMeta = getKeyboardModifier("ControlOrMeta") + KeyboardModifierMeta = getKeyboardModifier("Meta") + KeyboardModifierShift = getKeyboardModifier("Shift") ) func getScreenshotAnimations(in string) *ScreenshotAnimations { @@ -363,3 +377,15 @@ var ( MediaPrint = getMedia("print") MediaNoOverride = getMedia("no-override") ) + +func getHttpCredentialsSend(in string) *HttpCredentialsSend { + v := HttpCredentialsSend(in) + return &v +} + +type HttpCredentialsSend string + +var ( + HttpCredentialsSendUnauthorized *HttpCredentialsSend = getHttpCredentialsSend("unauthorized") + HttpCredentialsSendAlways = getHttpCredentialsSend("always") +) diff --git a/vendor/github.com/playwright-community/playwright-go/generated-interfaces.go b/vendor/github.com/playwright-community/playwright-go/generated-interfaces.go index 5ec04d416..5a78cdaef 100644 --- a/vendor/github.com/playwright-community/playwright-go/generated-interfaces.go +++ b/vendor/github.com/playwright-community/playwright-go/generated-interfaces.go @@ -32,13 +32,12 @@ type APIRequestContext interface { Delete(url string, options ...APIRequestContextDeleteOptions) (APIResponse, error) // All responses returned by [APIRequestContext.Get] and similar methods are stored in the memory, so that you can - // later call [APIResponse.Body]. This method discards all stored responses, and makes [APIResponse.Body] throw - // "Response disposed" error. - Dispose() error + // later call [APIResponse.Body].This method discards all its resources, calling any method on disposed + // [APIRequestContext] will throw an exception. + Dispose(options ...APIRequestContextDisposeOptions) error // Sends HTTP(S) request and returns its response. The method will populate request cookies from the context and - // update context cookies from the response. The method will automatically follow redirects. JSON objects can be - // passed directly to the request. + // update context cookies from the response. The method will automatically follow redirects. // // urlOrRequest: Target URL or Request to get all parameters from. Fetch(urlOrRequest interface{}, options ...APIRequestContextFetchOptions) (APIResponse, error) @@ -139,7 +138,7 @@ type APIResponseAssertions interface { ToBeOK() error } -// A Browser is created via [BrowserType.Launch]. An example of using a [Browser] to create a [Page]: +// A Browser is created via [BrowserType.Launch]. An example of using a [Browser] to create a [Page]: type Browser interface { EventEmitter // Emitted when Browser gets disconnected from the browser application. This might happen because of one of the @@ -158,7 +157,7 @@ type Browser interface { // **NOTE** This is similar to force quitting the browser. Therefore, you should call [BrowserContext.Close] on any // [BrowserContext]'s you explicitly created earlier with [Browser.NewContext] **before** calling [Browser.Close]. // The [Browser] object itself is considered to be disposed and cannot be used anymore. - Close() error + Close(options ...BrowserCloseOptions) error // Returns an array of all open browser contexts. In a newly created browser, this will return zero browser contexts. Contexts() []BrowserContext @@ -210,21 +209,28 @@ type Browser interface { Version() string } -// BrowserContexts provide a way to operate multiple independent browser sessions. +// BrowserContexts provide a way to operate multiple independent browser sessions. +// // If a page opens another page, e.g. with a `window.open` call, the popup will belong to the parent page's browser // context. // Playwright allows creating "incognito" browser contexts with [Browser.NewContext] method. "Incognito" browser // contexts don't write any browsing data to disk. type BrowserContext interface { EventEmitter + // **NOTE** Only works with Chromium browser's persistent context. + // Emitted when new background page is created in the context. + OnBackgroundPage(fn func(Page)) + + // Playwright has ability to mock clock and passage of time. + Clock() Clock + // Emitted when Browser context gets closed. This might happen because of one of the following: // - Browser context is closed. // - Browser application is closed or crashed. // - The [Browser.Close] method was called. OnClose(fn func(BrowserContext)) - // Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also - // emitted if the page throws an error or a warning. + // Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. // The arguments passed into `console.log` and the page are available on the [ConsoleMessage] event handler argument. OnConsole(fn func(ConsoleMessage)) @@ -240,7 +246,9 @@ type BrowserContext interface { // will also fire for popup pages. See also [Page.OnPopup] to receive events about popups relevant to a specific page. // The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a // popup with `window.open('http://example.com')`, this event will fire when the network request to - // "http://example.com" is done and its response has started loading in the popup. + // "http://example.com" is done and its response has started loading in the popup. If you would like to route/listen + // to this network request, use [BrowserContext.Route] and [BrowserContext.OnRequest] respectively instead of similar + // methods on the [Page]. // **NOTE** Use [Page.WaitForLoadState] to wait until the page gets to a particular state (you should not need it in // most cases). OnPage(fn func(Page)) @@ -295,15 +303,15 @@ type BrowserContext interface { // Returns the browser instance of the context. If it was launched as a persistent context null gets returned. Browser() Browser - // Clears context cookies. - ClearCookies() error + // Removes cookies from context. Accepts optional filter. + ClearCookies(options ...BrowserContextClearCookiesOptions) error // Clears all permission overrides for the browser context. ClearPermissions() error // Closes the browser context. All the pages that belong to the browser context will be closed. // **NOTE** The default browser context cannot be closed. - Close() error + Close(options ...BrowserContextCloseOptions) error // If no URLs are specified, this method returns all cookies. If URLs are specified, only cookies that affect those // URLs are returned. @@ -333,21 +341,22 @@ type BrowserContext interface { // specified. // // permissions: A permission or an array of permissions to grant. Permissions can be one of the following values: - // - `'geolocation'` - // - `'midi'` - // - `'midi-sysex'` (system-exclusive midi) - // - `'notifications'` - // - `'camera'` - // - `'microphone'` - // - `'background-sync'` - // - `'ambient-light-sensor'` // - `'accelerometer'` - // - `'gyroscope'` - // - `'magnetometer'` // - `'accessibility-events'` + // - `'ambient-light-sensor'` + // - `'background-sync'` + // - `'camera'` // - `'clipboard-read'` // - `'clipboard-write'` + // - `'geolocation'` + // - `'gyroscope'` + // - `'magnetometer'` + // - `'microphone'` + // - `'midi-sysex'` (system-exclusive midi) + // - `'midi'` + // - `'notifications'` // - `'payment-handler'` + // - `'storage-access'` GrantPermissions(permissions []string, options ...BrowserContextGrantPermissionsOptions) error // **NOTE** CDP sessions are only supported on Chromium-based browsers. @@ -437,6 +446,9 @@ type BrowserContext interface { Tracing() Tracing + // Removes all routes created with [BrowserContext.Route] and [BrowserContext.RouteFromHAR]. + UnrouteAll(options ...BrowserContextUnrouteAllOptions) error + // Removes a route created with [BrowserContext.Route]. When “handler” is not specified, removes all routes for the // “url”. // @@ -514,14 +526,15 @@ type BrowserType interface { Name() string } -// The `CDPSession` instances are used to talk raw Chrome Devtools Protocol: -// - protocol methods can be called with `session.send` method. -// - protocol events can be subscribed to with `session.on` method. +// The `CDPSession` instances are used to talk raw Chrome Devtools Protocol: +// - protocol methods can be called with `session.send` method. +// - protocol events can be subscribed to with `session.on` method. +// // Useful links: -// - Documentation on DevTools Protocol can be found here: -// [DevTools Protocol Viewer]. -// - Getting Started with DevTools Protocol: -// https://github.com/aslushnikov/getting-started-with-cdp/blob/master/README.md +// - Documentation on DevTools Protocol can be found here: +// [DevTools Protocol Viewer]. +// - Getting Started with DevTools Protocol: +// https://github.com/aslushnikov/getting-started-with-cdp/blob/master/README.md // // [DevTools Protocol Viewer]: https://chromedevtools.github.io/devtools-protocol/ type CDPSession interface { @@ -536,7 +549,61 @@ type CDPSession interface { Send(method string, params map[string]interface{}) (interface{}, error) } -// [ConsoleMessage] objects are dispatched by page via the [Page.OnConsole] event. For each console messages logged in +// Accurately simulating time-dependent behavior is essential for verifying the correctness of applications. Learn +// more about [clock emulation]. +// Note that clock is installed for the entire [BrowserContext], so the time in all the pages and iframes is +// controlled by the same clock. +// +// [clock emulation]: https://playwright.dev/docs/clock +type Clock interface { + // Advance the clock by jumping forward in time. Only fires due timers at most once. This is equivalent to user + // closing the laptop lid for a while and reopening it later, after given time. + // + // ticks: Time may be the number of milliseconds to advance the clock by or a human-readable string. Valid string formats are + // "08" for eight seconds, "01:00" for one minute and "02:34:10" for two hours, 34 minutes and ten seconds. + FastForward(ticks interface{}) error + + // Install fake implementations for the following time-related functions: + // - `Date` + // - `setTimeout` + // - `clearTimeout` + // - `setInterval` + // - `clearInterval` + // - `requestAnimationFrame` + // - `cancelAnimationFrame` + // - `requestIdleCallback` + // - `cancelIdleCallback` + // - `performance` + // Fake timers are used to manually control the flow of time in tests. They allow you to advance time, fire timers, + // and control the behavior of time-dependent functions. See [Clock.RunFor] and [Clock.FastForward] for more + // information. + Install(options ...ClockInstallOptions) error + + // Advance the clock, firing all the time-related callbacks. + // + // ticks: Time may be the number of milliseconds to advance the clock by or a human-readable string. Valid string formats are + // "08" for eight seconds, "01:00" for one minute and "02:34:10" for two hours, 34 minutes and ten seconds. + RunFor(ticks interface{}) error + + // Advance the clock by jumping forward in time and pause the time. Once this method is called, no timers are fired + // unless [Clock.RunFor], [Clock.FastForward], [Clock.PauseAt] or [Clock.Resume] is called. + // Only fires due timers at most once. This is equivalent to user closing the laptop lid for a while and reopening it + // at the specified time and pausing. + PauseAt(time interface{}) error + + // Resumes timers. Once this method is called, time resumes flowing, timers are fired as usual. + Resume() error + + // Makes `Date.now` and `new Date()` return fixed fake time at all times, keeps all the timers running. + // + // time: Time to be set. + SetFixedTime(time interface{}) error + + // Sets current system time but does not trigger any timers. + SetSystemTime(time interface{}) error +} + +// [ConsoleMessage] objects are dispatched by page via the [Page.OnConsole] event. For each console message logged in // the page there will be corresponding event in the Playwright context. type ConsoleMessage interface { // List of arguments passed to a `console` function call. See also [Page.OnConsole]. @@ -604,8 +671,8 @@ type Download interface { // Get the page that the download belongs to. Page() Page - // Returns path to the downloaded file in case of successful download. The method will wait for the download to finish - // if necessary. The method throws when connected remotely. + // Returns path to the downloaded file for a successful download, or throws for a failed/canceled download. The method + // will wait for the download to finish if necessary. The method throws when connected remotely. // Note that the download's file name is a random GUID, use [Download.SuggestedFilename] to get suggested file name. Path() (string, error) @@ -630,7 +697,8 @@ type Download interface { String() string } -// ElementHandle represents an in-page DOM element. ElementHandles can be created with the [Page.QuerySelector] +// ElementHandle represents an in-page DOM element. ElementHandles can be created with the [Page.QuerySelector] +// // method. // **NOTE** The use of ElementHandle is discouraged, use [Locator] objects and web-first assertions instead. // ElementHandle prevents DOM element from garbage collection unless the handle is disposed with [JSHandle.Dispose]. @@ -671,7 +739,10 @@ type ElementHandle interface { // When all steps combined have not finished during the specified “timeout”, this method throws a [TimeoutError]. // Passing zero timeout disables this. // + // Deprecated: Use locator-based [Locator.Check] instead. Read more about [locators]. + // // [actionability]: https://playwright.dev/docs/actionability + // [locators]: https://playwright.dev/docs/locators Check(options ...ElementHandleCheckOptions) error // This method clicks the element by performing the following steps: @@ -683,7 +754,10 @@ type ElementHandle interface { // When all steps combined have not finished during the specified “timeout”, this method throws a [TimeoutError]. // Passing zero timeout disables this. // + // Deprecated: Use locator-based [Locator.Click] instead. Read more about [locators]. + // // [actionability]: https://playwright.dev/docs/actionability + // [locators]: https://playwright.dev/docs/locators Click(options ...ElementHandleClickOptions) error // Returns the content frame for element handles referencing iframe nodes, or `null` otherwise @@ -700,24 +774,33 @@ type ElementHandle interface { // Passing zero timeout disables this. // **NOTE** `elementHandle.dblclick()` dispatches two `click` events and a single `dblclick` event. // + // Deprecated: Use locator-based [Locator.Dblclick] instead. Read more about [locators]. + // // [actionability]: https://playwright.dev/docs/actionability + // [locators]: https://playwright.dev/docs/locators Dblclick(options ...ElementHandleDblclickOptions) error // The snippet below dispatches the `click` event on the element. Regardless of the visibility state of the element, // `click` is dispatched. This is equivalent to calling // [element.Click()]. // + // Deprecated: Use locator-based [Locator.DispatchEvent] instead. Read more about [locators]. + // // 1. typ: DOM event type: `"click"`, `"dragstart"`, etc. // 2. eventInit: Optional event-specific initialization properties. // // [element.Click()]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click + // [DeviceMotionEvent]: https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/DeviceMotionEvent + // [DeviceOrientationEvent]: https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/DeviceOrientationEvent // [DragEvent]: https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent + // [Event]: https://developer.mozilla.org/en-US/docs/Web/API/Event/Event // [FocusEvent]: https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/FocusEvent // [KeyboardEvent]: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/KeyboardEvent // [MouseEvent]: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent // [PointerEvent]: https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/PointerEvent // [TouchEvent]: https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/TouchEvent - // [Event]: https://developer.mozilla.org/en-US/docs/Web/API/Event/Event + // [WheelEvent]: https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/WheelEvent + // [locators]: https://playwright.dev/docs/locators DispatchEvent(typ string, eventInit ...interface{}) error // Returns the return value of “expression”. @@ -726,6 +809,8 @@ type ElementHandle interface { // If “expression” returns a [Promise], then [ElementHandle.EvalOnSelector] would wait for the promise to resolve and // return its value. // + // Deprecated: This method does not wait for the element to pass actionability checks and therefore can lead to the flaky tests. Use [Locator.Evaluate], other [Locator] helper methods or web-first assertions instead. + // // 1. selector: A selector to query for. // 2. expression: JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the // function is automatically invoked. @@ -738,6 +823,8 @@ type ElementHandle interface { // If “expression” returns a [Promise], then [ElementHandle.EvalOnSelectorAll] would wait for the promise to resolve // and return its value. // + // Deprecated: In most cases, [Locator.EvaluateAll], other [Locator] helper methods and web-first assertions do a better job. + // // 1. selector: A selector to query for. // 2. expression: JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the // function is automatically invoked. @@ -752,20 +839,30 @@ type ElementHandle interface { // instead. // To send fine-grained keyboard events, use [Locator.PressSequentially]. // + // Deprecated: Use locator-based [Locator.Fill] instead. Read more about [locators]. + // // value: Value to set for the ``, `