diff --git a/package.json b/package.json index a7ff379..5113687 100644 --- a/package.json +++ b/package.json @@ -6,14 +6,17 @@ "deploy": "wrangler deploy", "dev": "wrangler dev", "start": "wrangler dev", - "test": "vitest", + "test": "vitest --run", "cf-typegen": "wrangler types", "lint": "biome check", "format": "biome format --write" }, "devDependencies": { + "@aws-sdk/client-s3": "^3.965.0", + "@aws-sdk/s3-request-presigner": "^3.965.0", "@biomejs/biome": "^2.3.11", "@cloudflare/vitest-pool-workers": "^0.8.19", + "aws4fetch": "^1.0.20", "typescript": "^5.5.2", "vitest": "~3.2.0", "wrangler": "^4.57.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 410bf0b..f30a3aa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,12 +8,21 @@ importers: .: devDependencies: + '@aws-sdk/client-s3': + specifier: ^3.965.0 + version: 3.965.0 + '@aws-sdk/s3-request-presigner': + specifier: ^3.965.0 + version: 3.965.0 '@biomejs/biome': specifier: ^2.3.11 version: 2.3.11 '@cloudflare/vitest-pool-workers': specifier: ^0.8.19 version: 0.8.71(@vitest/runner@3.2.4)(@vitest/snapshot@3.2.4)(vitest@3.2.4) + aws4fetch: + specifier: ^1.0.20 + version: 1.0.20 typescript: specifier: ^5.5.2 version: 5.9.3 @@ -26,6 +35,177 @@ importers: packages: + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/crc32c@5.2.0': + resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} + + '@aws-crypto/sha1-browser@5.2.0': + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-s3@3.965.0': + resolution: {integrity: sha512-BTeaaU1iK0BfatTCrtYjNkIHCoZH256qOI18l9bK4z6mVOgpHkYN4RvOu+NnKgyX58n+HWfOuhtKUD4OE33Vdw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/client-sso@3.965.0': + resolution: {integrity: sha512-iv2tr+n4aZ+nPUFFvG00hISPuEd4DU+1/Q8rPAYKXsM+vEPJ2nAnP5duUOa2fbOLIUCRxX3dcQaQaghVHDHzQw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/core@3.965.0': + resolution: {integrity: sha512-aq9BhQxdHit8UUJ9C0im9TtuKeK0pT6NXmNJxMTCFeStI7GG7ImIsSislg3BZTIifVg1P6VLdzMyz9de85iutQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/crc64-nvme@3.965.0': + resolution: {integrity: sha512-9FbIyJ/Zz1AdEIrb0+Pn7wRi+F/0Y566ooepg0hDyHUzRV3ZXKjOlu3wJH3YwTz2UkdwQmldfUos2yDJps7RyA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-env@3.965.0': + resolution: {integrity: sha512-mdGnaIjMxTIjsb70dEj3VsWPWpoq1V5MWzBSfJq2H8zgMBXjn6d5/qHP8HMf53l9PrsgqzMpXGv3Av549A2x1g==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-http@3.965.0': + resolution: {integrity: sha512-YuGQel9EgA/z25oeLM+GYYQS750+8AESvr7ZEmVnRPL0sg+K3DmGqdv+9gFjFd0UkLjTlC/jtbP2cuY6UcPiHQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-ini@3.965.0': + resolution: {integrity: sha512-xRo72Prer5s0xYVSCxCymVIRSqrVlevK5cmU0GWq9yJtaBNpnx02jwdJg80t/Ni7pgbkQyFWRMcq38c1tc6M/w==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-login@3.965.0': + resolution: {integrity: sha512-43/H8Qku8LHyugbhLo8kjD+eauhybCeVkmrnvWl8bXNHJP7xi1jCdtBQJKKJqiIHZws4MOEwkji8kFdAVRCe6g==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-node@3.965.0': + resolution: {integrity: sha512-cRxmMHF+Zh2lkkkEVduKl+8OQdtg/DhYA69+/7SPSQURlgyjFQGlRQ58B7q8abuNlrGT3sV+UzeOylZpJbV61Q==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-process@3.965.0': + resolution: {integrity: sha512-gmkPmdiR0yxnTzLPDb7rwrDhGuCUjtgnj8qWP+m0gSz/W43rR4jRPVEf6DUX2iC+ImQhxo3NFhuB3V42Kzo3TQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-sso@3.965.0': + resolution: {integrity: sha512-N01AYvtCqG3Wo/s/LvYt19ity18/FqggiXT+elAs3X9Om/Wfx+hw9G+i7jaDmy+/xewmv8AdQ2SK5Q30dXw/Fw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.965.0': + resolution: {integrity: sha512-T4gMZ2JzXnfxe1oTD+EDGLSxFfk1+WkLZdiHXEMZp8bFI1swP/3YyDFXI+Ib9Uq1JhnAmrCXtOnkicKEhDkdhQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-bucket-endpoint@3.965.0': + resolution: {integrity: sha512-gbdv3Dl8l8xmg4oH60fXvfDyTxfx28w5/Hxdymx3vurM07tAyd4qld8zEXejnSpraTo45QcHRtk5auELIMfeag==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-expect-continue@3.965.0': + resolution: {integrity: sha512-UBxVytsmhEmFwkBnt+aV0eAJ7uc+ouNokCqMBrQ7Oc5A77qhlcHfOgXIKz2SxqsiYTsDq+a0lWFM/XpyRWraqA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-flexible-checksums@3.965.0': + resolution: {integrity: sha512-5rzEW08trcpHMe6jkQyYc4PL1KG/H7BbnySFSzhih+r/gktQEiE36sb1BNf7av9I0Vk2Ccmt7wocB5PIT7GDkQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-host-header@3.965.0': + resolution: {integrity: sha512-SfpSYqoPOAmdb3DBsnNsZ0vix+1VAtkUkzXM79JL3R5IfacpyKE2zytOgVAQx/FjhhlpSTwuXd+LRhUEVb3MaA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-location-constraint@3.965.0': + resolution: {integrity: sha512-07T1rwAarQs33mVg5U28AsSdLB5JUXu9yBTBmspFGajKVsEahIyntf53j9mAXF1N2KR0bNdP0J4A0kst4t43UQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-logger@3.965.0': + resolution: {integrity: sha512-gjUvJRZT1bUABKewnvkj51LAynFrfz2h5DYAg5/2F4Utx6UOGByTSr9Rq8JCLbURvvzAbCtcMkkIJRxw+8Zuzw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.965.0': + resolution: {integrity: sha512-6dvD+18Ni14KCRu+tfEoNxq1sIGVp9tvoZDZ7aMvpnA7mDXuRLrOjRQ/TAZqXwr9ENKVGyxcPl0cRK8jk1YWjA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.965.0': + resolution: {integrity: sha512-dXEgnojaaVRl+OlOx35mg3rYEbfffIN4X6tLmIfDnaKz0hMaDMvsE9jJXb/vBvokbdO1sVB27/2FEM4ttLSLnw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-ssec@3.965.0': + resolution: {integrity: sha512-dke++CTw26y+a2D1DdVuZ4+2TkgItdx6TeuE0zOl4lsqXGvTBUG4eaIZalt7ZOAW5ys2pbDOk1bPuh4opoD3pQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-user-agent@3.965.0': + resolution: {integrity: sha512-RBEYVGgu/WeAt+H/qLrGc+t8LqAUkbyvh3wBfTiuAD+uBcWsKnvnB1iSBX75FearC0fmoxzXRUc0PMxMdqpjJQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/nested-clients@3.965.0': + resolution: {integrity: sha512-muNVUjUEU+/KLFrLzQ8PMXyw4+a/MP6t4GIvwLtyx/kH0rpSy5s0YmqacMXheuIe6F/5QT8uksXGNAQenitkGQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/region-config-resolver@3.965.0': + resolution: {integrity: sha512-RoMhu9ly2B0coxn8ctXosPP2WmDD0MkQlZGLjoYHQUOCBmty5qmCxOqBmBDa6wbWbB8xKtMQ/4VXloQOgzjHXg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/s3-request-presigner@3.965.0': + resolution: {integrity: sha512-aQ9vvXjeoQsAaRHS18l8doY+E/6mmNMSDMU6eJsSUDgvgGRMHhsKjiVh7DJGbZRRogdrES4KAfx6raIB4kBz5Q==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.965.0': + resolution: {integrity: sha512-hgbAThbsUrWtNpFBQxzXevIfd5Qgr4TLbXY1AIbmpSX9fPVC114pdieRMpopJ0fYaJ7v5/blTiS6wzVdXleZ/w==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/token-providers@3.965.0': + resolution: {integrity: sha512-aR0qxg0b8flkXJVE+CM1gzo7uJ57md50z2eyCwofC0QIz5Y0P7/7vvb9/dmUQt6eT9XRN5iRcUqq2IVxVDvJOw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/types@3.965.0': + resolution: {integrity: sha512-jvodoJdMavvg8faN7co58vVJRO5MVep4JFPRzUNCzpJ98BDqWDk/ad045aMJcmxkLzYLS2UAnUmqjJ/tUPNlzQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/util-arn-parser@3.965.0': + resolution: {integrity: sha512-bNGKr5Tct28jGLkL8xIkGu7swpDgBpkTVbGaofhzr/X80iclbOv656RGxhMpDvmc4S9UuQnqLRXyceNFNF2V7Q==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/util-endpoints@3.965.0': + resolution: {integrity: sha512-WqSCB0XIsGUwZWvrYkuoofi2vzoVHqyeJ2kN+WyoOsxPLTiQSBIoqm/01R/qJvoxwK/gOOF7su9i84Vw2NQQpQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/util-format-url@3.965.0': + resolution: {integrity: sha512-KiplV4xYGXdNCcz5eRP8WfAejT5EkE2gQxC4IY6WsuxYprzQKsnGaAzEQ+giR5GgQLIRBkPaWT0xHEYkMiCQ1Q==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/util-locate-window@3.965.0': + resolution: {integrity: sha512-9LJFand4bIoOjOF4x3wx0UZYiFZRo4oUauxQSiEX2dVg+5qeBOJSjp2SeWykIE6+6frCZ5wvWm2fGLK8D32aJw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/util-user-agent-browser@3.965.0': + resolution: {integrity: sha512-Xiza/zMntQGpkd2dETQeAK8So1pg5+STTzpcdGWxj5q0jGO5ayjqT/q1Q7BrsX5KIr6PvRkl9/V7lLCv04wGjQ==} + + '@aws-sdk/util-user-agent-node@3.965.0': + resolution: {integrity: sha512-kokIHUfNT3/P55E4fUJJrFHuuA9BbjFKUIxoLrd3UaRfdafT0ScRfg2eaZie6arf60EuhlUIZH0yALxttMEjxQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/xml-builder@3.965.0': + resolution: {integrity: sha512-Tcod25/BTupraQwtb+Q+GX8bmEZfxIFjjJ/AvkhUZsZlkPeVluzq1uu3Oeqf145DCdMjzLIN6vab5MrykbDP+g==} + engines: {node: '>=18.0.0'} + + '@aws/lambda-invoke-store@0.2.2': + resolution: {integrity: sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==} + engines: {node: '>=18.0.0'} + '@biomejs/biome@2.3.11': resolution: {integrity: sha512-/zt+6qazBWguPG6+eWmiELqO+9jRsMZ/DBU3lfuU2ngtIQYzymocHhKiZRyrbra4aCOoyTg/BmY+6WH5mv9xmQ==} engines: {node: '>=14.21.3'} @@ -894,6 +1074,222 @@ packages: resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} engines: {node: '>=18'} + '@smithy/abort-controller@4.2.7': + resolution: {integrity: sha512-rzMY6CaKx2qxrbYbqjXWS0plqEy7LOdKHS0bg4ixJ6aoGDPNUcLWk/FRNuCILh7GKLG9TFUXYYeQQldMBBwuyw==} + engines: {node: '>=18.0.0'} + + '@smithy/chunked-blob-reader-native@4.2.1': + resolution: {integrity: sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==} + engines: {node: '>=18.0.0'} + + '@smithy/chunked-blob-reader@5.2.0': + resolution: {integrity: sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==} + engines: {node: '>=18.0.0'} + + '@smithy/config-resolver@4.4.5': + resolution: {integrity: sha512-HAGoUAFYsUkoSckuKbCPayECeMim8pOu+yLy1zOxt1sifzEbrsRpYa+mKcMdiHKMeiqOibyPG0sFJnmaV/OGEg==} + engines: {node: '>=18.0.0'} + + '@smithy/core@3.20.1': + resolution: {integrity: sha512-wOboSEdQ85dbKAJ0zL+wQ6b0HTSBRhtGa0PYKysQXkRg+vK0tdCRRVruiFM2QMprkOQwSYOnwF4og96PAaEGag==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.2.7': + resolution: {integrity: sha512-CmduWdCiILCRNbQWFR0OcZlUPVtyE49Sr8yYL0rZQ4D/wKxiNzBNS/YHemvnbkIWj623fplgkexUd/c9CAKdoA==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-codec@4.2.7': + resolution: {integrity: sha512-DrpkEoM3j9cBBWhufqBwnbbn+3nf1N9FP6xuVJ+e220jbactKuQgaZwjwP5CP1t+O94brm2JgVMD2atMGX3xIQ==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-browser@4.2.7': + resolution: {integrity: sha512-ujzPk8seYoDBmABDE5YqlhQZAXLOrtxtJLrbhHMKjBoG5b4dK4i6/mEU+6/7yXIAkqOO8sJ6YxZl+h0QQ1IJ7g==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-config-resolver@4.3.7': + resolution: {integrity: sha512-x7BtAiIPSaNaWuzm24Q/mtSkv+BrISO/fmheiJ39PKRNH3RmH2Hph/bUKSOBOBC9unqfIYDhKTHwpyZycLGPVQ==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-node@4.2.7': + resolution: {integrity: sha512-roySCtHC5+pQq5lK4be1fZ/WR6s/AxnPaLfCODIPArtN2du8s5Ot4mKVK3pPtijL/L654ws592JHJ1PbZFF6+A==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-universal@4.2.7': + resolution: {integrity: sha512-QVD+g3+icFkThoy4r8wVFZMsIP08taHVKjE6Jpmz8h5CgX/kk6pTODq5cht0OMtcapUx+xrPzUTQdA+TmO0m1g==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.3.8': + resolution: {integrity: sha512-h/Fi+o7mti4n8wx1SR6UHWLaakwHRx29sizvp8OOm7iqwKGFneT06GCSFhml6Bha5BT6ot5pj3CYZnCHhGC2Rg==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-blob-browser@4.2.8': + resolution: {integrity: sha512-07InZontqsM1ggTCPSRgI7d8DirqRrnpL7nIACT4PW0AWrgDiHhjGZzbAE5UtRSiU0NISGUYe7/rri9ZeWyDpw==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-node@4.2.7': + resolution: {integrity: sha512-PU/JWLTBCV1c8FtB8tEFnY4eV1tSfBc7bDBADHfn1K+uRbPgSJ9jnJp0hyjiFN2PMdPzxsf1Fdu0eo9fJ760Xw==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-stream-node@4.2.7': + resolution: {integrity: sha512-ZQVoAwNYnFMIbd4DUc517HuwNelJUY6YOzwqrbcAgCnVn+79/OK7UjwA93SPpdTOpKDVkLIzavWm/Ck7SmnDPQ==} + engines: {node: '>=18.0.0'} + + '@smithy/invalid-dependency@4.2.7': + resolution: {integrity: sha512-ncvgCr9a15nPlkhIUx3CU4d7E7WEuVJOV7fS7nnK2hLtPK9tYRBkMHQbhXU1VvvKeBm/O0x26OEoBq+ngFpOEQ==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@4.2.0': + resolution: {integrity: sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==} + engines: {node: '>=18.0.0'} + + '@smithy/md5-js@4.2.7': + resolution: {integrity: sha512-Wv6JcUxtOLTnxvNjDnAiATUsk8gvA6EeS8zzHig07dotpByYsLot+m0AaQEniUBjx97AC41MQR4hW0baraD1Xw==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-content-length@4.2.7': + resolution: {integrity: sha512-GszfBfCcvt7kIbJ41LuNa5f0wvQCHhnGx/aDaZJCCT05Ld6x6U2s0xsc/0mBFONBZjQJp2U/0uSJ178OXOwbhg==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-endpoint@4.4.2': + resolution: {integrity: sha512-mqpAdux0BNmZu/SqkFhQEnod4fX23xxTvU2LUpmKp0JpSI+kPYCiHJMmzREr8yxbNxKL2/DU1UZm9i++ayU+2g==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-retry@4.4.18': + resolution: {integrity: sha512-E5hulijA59nBk/zvcwVMaS7FG7Y4l6hWA9vrW018r+8kiZef4/ETQaPI4oY+3zsy9f6KqDv3c4VKtO4DwwgpCg==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-serde@4.2.8': + resolution: {integrity: sha512-8rDGYen5m5+NV9eHv9ry0sqm2gI6W7mc1VSFMtn6Igo25S507/HaOX9LTHAS2/J32VXD0xSzrY0H5FJtOMS4/w==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-stack@4.2.7': + resolution: {integrity: sha512-bsOT0rJ+HHlZd9crHoS37mt8qRRN/h9jRve1SXUhVbkRzu0QaNYZp1i1jha4n098tsvROjcwfLlfvcFuJSXEsw==} + engines: {node: '>=18.0.0'} + + '@smithy/node-config-provider@4.3.7': + resolution: {integrity: sha512-7r58wq8sdOcrwWe+klL9y3bc4GW1gnlfnFOuL7CXa7UzfhzhxKuzNdtqgzmTV+53lEp9NXh5hY/S4UgjLOzPfw==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.4.7': + resolution: {integrity: sha512-NELpdmBOO6EpZtWgQiHjoShs1kmweaiNuETUpuup+cmm/xJYjT4eUjfhrXRP4jCOaAsS3c3yPsP3B+K+/fyPCQ==} + engines: {node: '>=18.0.0'} + + '@smithy/property-provider@4.2.7': + resolution: {integrity: sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA==} + engines: {node: '>=18.0.0'} + + '@smithy/protocol-http@5.3.7': + resolution: {integrity: sha512-1r07pb994I20dD/c2seaZhoCuNYm0rWrvBxhCQ70brNh11M5Ml2ew6qJVo0lclB3jMIXirD4s2XRXRe7QEi0xA==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-builder@4.2.7': + resolution: {integrity: sha512-eKONSywHZxK4tBxe2lXEysh8wbBdvDWiA+RIuaxZSgCMmA0zMgoDpGLJhnyj+c0leOQprVnXOmcB4m+W9Rw7sg==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-parser@4.2.7': + resolution: {integrity: sha512-3X5ZvzUHmlSTHAXFlswrS6EGt8fMSIxX/c3Rm1Pni3+wYWB6cjGocmRIoqcQF9nU5OgGmL0u7l9m44tSUpfj9w==} + engines: {node: '>=18.0.0'} + + '@smithy/service-error-classification@4.2.7': + resolution: {integrity: sha512-YB7oCbukqEb2Dlh3340/8g8vNGbs/QsNNRms+gv3N2AtZz9/1vSBx6/6tpwQpZMEJFs7Uq8h4mmOn48ZZ72MkA==} + engines: {node: '>=18.0.0'} + + '@smithy/shared-ini-file-loader@4.4.2': + resolution: {integrity: sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.3.7': + resolution: {integrity: sha512-9oNUlqBlFZFOSdxgImA6X5GFuzE7V2H7VG/7E70cdLhidFbdtvxxt81EHgykGK5vq5D3FafH//X+Oy31j3CKOg==} + engines: {node: '>=18.0.0'} + + '@smithy/smithy-client@4.10.3': + resolution: {integrity: sha512-EfECiO/0fAfb590LBnUe7rI5ux7XfquQ8LBzTe7gxw0j9QW/q8UT/EHWHlxV/+jhQ3+Ssga9uUYXCQgImGMbNg==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.11.0': + resolution: {integrity: sha512-mlrmL0DRDVe3mNrjTcVcZEgkFmufITfUAPBEA+AHYiIeYyJebso/He1qLbP3PssRe22KUzLRpQSdBPbXdgZ2VA==} + engines: {node: '>=18.0.0'} + + '@smithy/url-parser@4.2.7': + resolution: {integrity: sha512-/RLtVsRV4uY3qPWhBDsjwahAtt3x2IsMGnP5W1b2VZIe+qgCqkLxI1UOHDZp1Q1QSOrdOR32MF3Ph2JfWT1VHg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-base64@4.3.0': + resolution: {integrity: sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-browser@4.2.0': + resolution: {integrity: sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-node@4.2.1': + resolution: {integrity: sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@4.2.0': + resolution: {integrity: sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==} + engines: {node: '>=18.0.0'} + + '@smithy/util-config-provider@4.2.0': + resolution: {integrity: sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-browser@4.3.17': + resolution: {integrity: sha512-dwN4GmivYF1QphnP3xJESXKtHvkkvKHSZI8GrSKMVoENVSKW2cFPRYC4ZgstYjUHdR3zwaDkIaTDIp26JuY7Cw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-node@4.2.20': + resolution: {integrity: sha512-VD/I4AEhF1lpB3B//pmOIMBNLMrtdMXwy9yCOfa2QkJGDr63vH3RqPbSAKzoGMov3iryCxTXCxSsyGmEB8PDpg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-endpoints@3.2.7': + resolution: {integrity: sha512-s4ILhyAvVqhMDYREeTS68R43B1V5aenV5q/V1QpRQJkCXib5BPRo4s7uNdzGtIKxaPHCfU/8YkvPAEvTpxgspg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-hex-encoding@4.2.0': + resolution: {integrity: sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-middleware@4.2.7': + resolution: {integrity: sha512-i1IkpbOae6NvIKsEeLLM9/2q4X+M90KV3oCFgWQI4q0Qz+yUZvsr+gZPdAEAtFhWQhAHpTsJO8DRJPuwVyln+w==} + engines: {node: '>=18.0.0'} + + '@smithy/util-retry@4.2.7': + resolution: {integrity: sha512-SvDdsQyF5CIASa4EYVT02LukPHVzAgUA4kMAuZ97QJc2BpAqZfA4PINB8/KOoCXEw9tsuv/jQjMeaHFvxdLNGg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-stream@4.5.8': + resolution: {integrity: sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w==} + engines: {node: '>=18.0.0'} + + '@smithy/util-uri-escape@4.2.0': + resolution: {integrity: sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@4.2.0': + resolution: {integrity: sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-waiter@4.2.7': + resolution: {integrity: sha512-vHJFXi9b7kUEpHWUCY3Twl+9NPOZvQ0SAi+Ewtn48mbiJk4JY9MZmKQjGB4SCvVb9WPiSphZJYY6RIbs+grrzw==} + engines: {node: '>=18.0.0'} + + '@smithy/uuid@1.1.0': + resolution: {integrity: sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==} + engines: {node: '>=18.0.0'} + '@speed-highlight/core@1.2.14': resolution: {integrity: sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA==} @@ -948,12 +1344,18 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + aws4fetch@1.0.20: + resolution: {integrity: sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==} + birpc@0.2.14: resolution: {integrity: sha512-37FHE8rqsYM5JEKCnXFyHpBCzvgHEExwVVTq+nUmloInU7l8ezD1TpOhKpS8oe1DTYFqEK27rFZVKG43oTqXRA==} blake3-wasm@2.1.5: resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} + bowser@2.13.1: + resolution: {integrity: sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -1045,6 +1447,10 @@ packages: exsolve@1.0.8: resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + fast-xml-parser@5.2.5: + resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} + hasBin: true + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -1162,6 +1568,9 @@ packages: strip-literal@3.1.0: resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + strnum@2.1.2: + resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==} + supports-color@10.2.2: resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} engines: {node: '>=18'} @@ -1347,6 +1756,509 @@ packages: snapshots: + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.965.0 + tslib: 2.8.1 + + '@aws-crypto/crc32c@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.965.0 + tslib: 2.8.1 + + '@aws-crypto/sha1-browser@5.2.0': + dependencies: + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.965.0 + '@aws-sdk/util-locate-window': 3.965.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.965.0 + '@aws-sdk/util-locate-window': 3.965.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.965.0 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.965.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/client-s3@3.965.0': + dependencies: + '@aws-crypto/sha1-browser': 5.2.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.965.0 + '@aws-sdk/credential-provider-node': 3.965.0 + '@aws-sdk/middleware-bucket-endpoint': 3.965.0 + '@aws-sdk/middleware-expect-continue': 3.965.0 + '@aws-sdk/middleware-flexible-checksums': 3.965.0 + '@aws-sdk/middleware-host-header': 3.965.0 + '@aws-sdk/middleware-location-constraint': 3.965.0 + '@aws-sdk/middleware-logger': 3.965.0 + '@aws-sdk/middleware-recursion-detection': 3.965.0 + '@aws-sdk/middleware-sdk-s3': 3.965.0 + '@aws-sdk/middleware-ssec': 3.965.0 + '@aws-sdk/middleware-user-agent': 3.965.0 + '@aws-sdk/region-config-resolver': 3.965.0 + '@aws-sdk/signature-v4-multi-region': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@aws-sdk/util-endpoints': 3.965.0 + '@aws-sdk/util-user-agent-browser': 3.965.0 + '@aws-sdk/util-user-agent-node': 3.965.0 + '@smithy/config-resolver': 4.4.5 + '@smithy/core': 3.20.1 + '@smithy/eventstream-serde-browser': 4.2.7 + '@smithy/eventstream-serde-config-resolver': 4.3.7 + '@smithy/eventstream-serde-node': 4.2.7 + '@smithy/fetch-http-handler': 5.3.8 + '@smithy/hash-blob-browser': 4.2.8 + '@smithy/hash-node': 4.2.7 + '@smithy/hash-stream-node': 4.2.7 + '@smithy/invalid-dependency': 4.2.7 + '@smithy/md5-js': 4.2.7 + '@smithy/middleware-content-length': 4.2.7 + '@smithy/middleware-endpoint': 4.4.2 + '@smithy/middleware-retry': 4.4.18 + '@smithy/middleware-serde': 4.2.8 + '@smithy/middleware-stack': 4.2.7 + '@smithy/node-config-provider': 4.3.7 + '@smithy/node-http-handler': 4.4.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/smithy-client': 4.10.3 + '@smithy/types': 4.11.0 + '@smithy/url-parser': 4.2.7 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-body-length-node': 4.2.1 + '@smithy/util-defaults-mode-browser': 4.3.17 + '@smithy/util-defaults-mode-node': 4.2.20 + '@smithy/util-endpoints': 3.2.7 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-retry': 4.2.7 + '@smithy/util-stream': 4.5.8 + '@smithy/util-utf8': 4.2.0 + '@smithy/util-waiter': 4.2.7 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sso@3.965.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.965.0 + '@aws-sdk/middleware-host-header': 3.965.0 + '@aws-sdk/middleware-logger': 3.965.0 + '@aws-sdk/middleware-recursion-detection': 3.965.0 + '@aws-sdk/middleware-user-agent': 3.965.0 + '@aws-sdk/region-config-resolver': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@aws-sdk/util-endpoints': 3.965.0 + '@aws-sdk/util-user-agent-browser': 3.965.0 + '@aws-sdk/util-user-agent-node': 3.965.0 + '@smithy/config-resolver': 4.4.5 + '@smithy/core': 3.20.1 + '@smithy/fetch-http-handler': 5.3.8 + '@smithy/hash-node': 4.2.7 + '@smithy/invalid-dependency': 4.2.7 + '@smithy/middleware-content-length': 4.2.7 + '@smithy/middleware-endpoint': 4.4.2 + '@smithy/middleware-retry': 4.4.18 + '@smithy/middleware-serde': 4.2.8 + '@smithy/middleware-stack': 4.2.7 + '@smithy/node-config-provider': 4.3.7 + '@smithy/node-http-handler': 4.4.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/smithy-client': 4.10.3 + '@smithy/types': 4.11.0 + '@smithy/url-parser': 4.2.7 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-body-length-node': 4.2.1 + '@smithy/util-defaults-mode-browser': 4.3.17 + '@smithy/util-defaults-mode-node': 4.2.20 + '@smithy/util-endpoints': 3.2.7 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-retry': 4.2.7 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/core@3.965.0': + dependencies: + '@aws-sdk/types': 3.965.0 + '@aws-sdk/xml-builder': 3.965.0 + '@smithy/core': 3.20.1 + '@smithy/node-config-provider': 4.3.7 + '@smithy/property-provider': 4.2.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/signature-v4': 5.3.7 + '@smithy/smithy-client': 4.10.3 + '@smithy/types': 4.11.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/crc64-nvme@3.965.0': + dependencies: + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.965.0': + dependencies: + '@aws-sdk/core': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@smithy/property-provider': 4.2.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.965.0': + dependencies: + '@aws-sdk/core': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@smithy/fetch-http-handler': 5.3.8 + '@smithy/node-http-handler': 4.4.7 + '@smithy/property-provider': 4.2.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/smithy-client': 4.10.3 + '@smithy/types': 4.11.0 + '@smithy/util-stream': 4.5.8 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.965.0': + dependencies: + '@aws-sdk/core': 3.965.0 + '@aws-sdk/credential-provider-env': 3.965.0 + '@aws-sdk/credential-provider-http': 3.965.0 + '@aws-sdk/credential-provider-login': 3.965.0 + '@aws-sdk/credential-provider-process': 3.965.0 + '@aws-sdk/credential-provider-sso': 3.965.0 + '@aws-sdk/credential-provider-web-identity': 3.965.0 + '@aws-sdk/nested-clients': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@smithy/credential-provider-imds': 4.2.7 + '@smithy/property-provider': 4.2.7 + '@smithy/shared-ini-file-loader': 4.4.2 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-login@3.965.0': + dependencies: + '@aws-sdk/core': 3.965.0 + '@aws-sdk/nested-clients': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@smithy/property-provider': 4.2.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/shared-ini-file-loader': 4.4.2 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-node@3.965.0': + dependencies: + '@aws-sdk/credential-provider-env': 3.965.0 + '@aws-sdk/credential-provider-http': 3.965.0 + '@aws-sdk/credential-provider-ini': 3.965.0 + '@aws-sdk/credential-provider-process': 3.965.0 + '@aws-sdk/credential-provider-sso': 3.965.0 + '@aws-sdk/credential-provider-web-identity': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@smithy/credential-provider-imds': 4.2.7 + '@smithy/property-provider': 4.2.7 + '@smithy/shared-ini-file-loader': 4.4.2 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-process@3.965.0': + dependencies: + '@aws-sdk/core': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@smithy/property-provider': 4.2.7 + '@smithy/shared-ini-file-loader': 4.4.2 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.965.0': + dependencies: + '@aws-sdk/client-sso': 3.965.0 + '@aws-sdk/core': 3.965.0 + '@aws-sdk/token-providers': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@smithy/property-provider': 4.2.7 + '@smithy/shared-ini-file-loader': 4.4.2 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-web-identity@3.965.0': + dependencies: + '@aws-sdk/core': 3.965.0 + '@aws-sdk/nested-clients': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@smithy/property-provider': 4.2.7 + '@smithy/shared-ini-file-loader': 4.4.2 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/middleware-bucket-endpoint@3.965.0': + dependencies: + '@aws-sdk/types': 3.965.0 + '@aws-sdk/util-arn-parser': 3.965.0 + '@smithy/node-config-provider': 4.3.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 + '@smithy/util-config-provider': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-expect-continue@3.965.0': + dependencies: + '@aws-sdk/types': 3.965.0 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-flexible-checksums@3.965.0': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@aws-crypto/crc32c': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/core': 3.965.0 + '@aws-sdk/crc64-nvme': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@smithy/is-array-buffer': 4.2.0 + '@smithy/node-config-provider': 4.3.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-stream': 4.5.8 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-host-header@3.965.0': + dependencies: + '@aws-sdk/types': 3.965.0 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-location-constraint@3.965.0': + dependencies: + '@aws-sdk/types': 3.965.0 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-logger@3.965.0': + dependencies: + '@aws-sdk/types': 3.965.0 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-recursion-detection@3.965.0': + dependencies: + '@aws-sdk/types': 3.965.0 + '@aws/lambda-invoke-store': 0.2.2 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3@3.965.0': + dependencies: + '@aws-sdk/core': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@aws-sdk/util-arn-parser': 3.965.0 + '@smithy/core': 3.20.1 + '@smithy/node-config-provider': 4.3.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/signature-v4': 5.3.7 + '@smithy/smithy-client': 4.10.3 + '@smithy/types': 4.11.0 + '@smithy/util-config-provider': 4.2.0 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-stream': 4.5.8 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-ssec@3.965.0': + dependencies: + '@aws-sdk/types': 3.965.0 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-user-agent@3.965.0': + dependencies: + '@aws-sdk/core': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@aws-sdk/util-endpoints': 3.965.0 + '@smithy/core': 3.20.1 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.965.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.965.0 + '@aws-sdk/middleware-host-header': 3.965.0 + '@aws-sdk/middleware-logger': 3.965.0 + '@aws-sdk/middleware-recursion-detection': 3.965.0 + '@aws-sdk/middleware-user-agent': 3.965.0 + '@aws-sdk/region-config-resolver': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@aws-sdk/util-endpoints': 3.965.0 + '@aws-sdk/util-user-agent-browser': 3.965.0 + '@aws-sdk/util-user-agent-node': 3.965.0 + '@smithy/config-resolver': 4.4.5 + '@smithy/core': 3.20.1 + '@smithy/fetch-http-handler': 5.3.8 + '@smithy/hash-node': 4.2.7 + '@smithy/invalid-dependency': 4.2.7 + '@smithy/middleware-content-length': 4.2.7 + '@smithy/middleware-endpoint': 4.4.2 + '@smithy/middleware-retry': 4.4.18 + '@smithy/middleware-serde': 4.2.8 + '@smithy/middleware-stack': 4.2.7 + '@smithy/node-config-provider': 4.3.7 + '@smithy/node-http-handler': 4.4.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/smithy-client': 4.10.3 + '@smithy/types': 4.11.0 + '@smithy/url-parser': 4.2.7 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-body-length-node': 4.2.1 + '@smithy/util-defaults-mode-browser': 4.3.17 + '@smithy/util-defaults-mode-node': 4.2.20 + '@smithy/util-endpoints': 3.2.7 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-retry': 4.2.7 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/region-config-resolver@3.965.0': + dependencies: + '@aws-sdk/types': 3.965.0 + '@smithy/config-resolver': 4.4.5 + '@smithy/node-config-provider': 4.3.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@aws-sdk/s3-request-presigner@3.965.0': + dependencies: + '@aws-sdk/signature-v4-multi-region': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@aws-sdk/util-format-url': 3.965.0 + '@smithy/middleware-endpoint': 4.4.2 + '@smithy/protocol-http': 5.3.7 + '@smithy/smithy-client': 4.10.3 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.965.0': + dependencies: + '@aws-sdk/middleware-sdk-s3': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@smithy/protocol-http': 5.3.7 + '@smithy/signature-v4': 5.3.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.965.0': + dependencies: + '@aws-sdk/core': 3.965.0 + '@aws-sdk/nested-clients': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@smithy/property-provider': 4.2.7 + '@smithy/shared-ini-file-loader': 4.4.2 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/types@3.965.0': + dependencies: + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@aws-sdk/util-arn-parser@3.965.0': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-endpoints@3.965.0': + dependencies: + '@aws-sdk/types': 3.965.0 + '@smithy/types': 4.11.0 + '@smithy/url-parser': 4.2.7 + '@smithy/util-endpoints': 3.2.7 + tslib: 2.8.1 + + '@aws-sdk/util-format-url@3.965.0': + dependencies: + '@aws-sdk/types': 3.965.0 + '@smithy/querystring-builder': 4.2.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.965.0': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-browser@3.965.0': + dependencies: + '@aws-sdk/types': 3.965.0 + '@smithy/types': 4.11.0 + bowser: 2.13.1 + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-node@3.965.0': + dependencies: + '@aws-sdk/middleware-user-agent': 3.965.0 + '@aws-sdk/types': 3.965.0 + '@smithy/node-config-provider': 4.3.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.965.0': + dependencies: + '@smithy/types': 4.11.0 + fast-xml-parser: 5.2.5 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.2.2': {} + '@biomejs/biome@2.3.11': optionalDependencies: '@biomejs/cli-darwin-arm64': 2.3.11 @@ -1862,6 +2774,344 @@ snapshots: '@sindresorhus/is@7.2.0': {} + '@smithy/abort-controller@4.2.7': + dependencies: + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/chunked-blob-reader-native@4.2.1': + dependencies: + '@smithy/util-base64': 4.3.0 + tslib: 2.8.1 + + '@smithy/chunked-blob-reader@5.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/config-resolver@4.4.5': + dependencies: + '@smithy/node-config-provider': 4.3.7 + '@smithy/types': 4.11.0 + '@smithy/util-config-provider': 4.2.0 + '@smithy/util-endpoints': 3.2.7 + '@smithy/util-middleware': 4.2.7 + tslib: 2.8.1 + + '@smithy/core@3.20.1': + dependencies: + '@smithy/middleware-serde': 4.2.8 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-stream': 4.5.8 + '@smithy/util-utf8': 4.2.0 + '@smithy/uuid': 1.1.0 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.2.7': + dependencies: + '@smithy/node-config-provider': 4.3.7 + '@smithy/property-provider': 4.2.7 + '@smithy/types': 4.11.0 + '@smithy/url-parser': 4.2.7 + tslib: 2.8.1 + + '@smithy/eventstream-codec@4.2.7': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.11.0 + '@smithy/util-hex-encoding': 4.2.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-browser@4.2.7': + dependencies: + '@smithy/eventstream-serde-universal': 4.2.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-config-resolver@4.3.7': + dependencies: + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-node@4.2.7': + dependencies: + '@smithy/eventstream-serde-universal': 4.2.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-universal@4.2.7': + dependencies: + '@smithy/eventstream-codec': 4.2.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.3.8': + dependencies: + '@smithy/protocol-http': 5.3.7 + '@smithy/querystring-builder': 4.2.7 + '@smithy/types': 4.11.0 + '@smithy/util-base64': 4.3.0 + tslib: 2.8.1 + + '@smithy/hash-blob-browser@4.2.8': + dependencies: + '@smithy/chunked-blob-reader': 5.2.0 + '@smithy/chunked-blob-reader-native': 4.2.1 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/hash-node@4.2.7': + dependencies: + '@smithy/types': 4.11.0 + '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/hash-stream-node@4.2.7': + dependencies: + '@smithy/types': 4.11.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/invalid-dependency@4.2.7': + dependencies: + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/is-array-buffer@4.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/md5-js@4.2.7': + dependencies: + '@smithy/types': 4.11.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/middleware-content-length@4.2.7': + dependencies: + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/middleware-endpoint@4.4.2': + dependencies: + '@smithy/core': 3.20.1 + '@smithy/middleware-serde': 4.2.8 + '@smithy/node-config-provider': 4.3.7 + '@smithy/shared-ini-file-loader': 4.4.2 + '@smithy/types': 4.11.0 + '@smithy/url-parser': 4.2.7 + '@smithy/util-middleware': 4.2.7 + tslib: 2.8.1 + + '@smithy/middleware-retry@4.4.18': + dependencies: + '@smithy/node-config-provider': 4.3.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/service-error-classification': 4.2.7 + '@smithy/smithy-client': 4.10.3 + '@smithy/types': 4.11.0 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-retry': 4.2.7 + '@smithy/uuid': 1.1.0 + tslib: 2.8.1 + + '@smithy/middleware-serde@4.2.8': + dependencies: + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/middleware-stack@4.2.7': + dependencies: + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/node-config-provider@4.3.7': + dependencies: + '@smithy/property-provider': 4.2.7 + '@smithy/shared-ini-file-loader': 4.4.2 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.4.7': + dependencies: + '@smithy/abort-controller': 4.2.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/querystring-builder': 4.2.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/property-provider@4.2.7': + dependencies: + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/protocol-http@5.3.7': + dependencies: + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/querystring-builder@4.2.7': + dependencies: + '@smithy/types': 4.11.0 + '@smithy/util-uri-escape': 4.2.0 + tslib: 2.8.1 + + '@smithy/querystring-parser@4.2.7': + dependencies: + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/service-error-classification@4.2.7': + dependencies: + '@smithy/types': 4.11.0 + + '@smithy/shared-ini-file-loader@4.4.2': + dependencies: + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/signature-v4@5.3.7': + dependencies: + '@smithy/is-array-buffer': 4.2.0 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 + '@smithy/util-hex-encoding': 4.2.0 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-uri-escape': 4.2.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/smithy-client@4.10.3': + dependencies: + '@smithy/core': 3.20.1 + '@smithy/middleware-endpoint': 4.4.2 + '@smithy/middleware-stack': 4.2.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 + '@smithy/util-stream': 4.5.8 + tslib: 2.8.1 + + '@smithy/types@4.11.0': + dependencies: + tslib: 2.8.1 + + '@smithy/url-parser@4.2.7': + dependencies: + '@smithy/querystring-parser': 4.2.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/util-base64@4.3.0': + dependencies: + '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/util-body-length-browser@4.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-body-length-node@4.2.1': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-buffer-from@4.2.0': + dependencies: + '@smithy/is-array-buffer': 4.2.0 + tslib: 2.8.1 + + '@smithy/util-config-provider@4.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-defaults-mode-browser@4.3.17': + dependencies: + '@smithy/property-provider': 4.2.7 + '@smithy/smithy-client': 4.10.3 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/util-defaults-mode-node@4.2.20': + dependencies: + '@smithy/config-resolver': 4.4.5 + '@smithy/credential-provider-imds': 4.2.7 + '@smithy/node-config-provider': 4.3.7 + '@smithy/property-provider': 4.2.7 + '@smithy/smithy-client': 4.10.3 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/util-endpoints@3.2.7': + dependencies: + '@smithy/node-config-provider': 4.3.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/util-hex-encoding@4.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-middleware@4.2.7': + dependencies: + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/util-retry@4.2.7': + dependencies: + '@smithy/service-error-classification': 4.2.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/util-stream@4.5.8': + dependencies: + '@smithy/fetch-http-handler': 5.3.8 + '@smithy/node-http-handler': 4.4.7 + '@smithy/types': 4.11.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-hex-encoding': 4.2.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/util-uri-escape@4.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-utf8@4.2.0': + dependencies: + '@smithy/util-buffer-from': 4.2.0 + tslib: 2.8.1 + + '@smithy/util-waiter@4.2.7': + dependencies: + '@smithy/abort-controller': 4.2.7 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@smithy/uuid@1.1.0': + dependencies: + tslib: 2.8.1 + '@speed-highlight/core@1.2.14': {} '@types/chai@5.2.3': @@ -1921,10 +3171,14 @@ snapshots: assertion-error@2.0.1: {} + aws4fetch@1.0.20: {} + birpc@0.2.14: {} blake3-wasm@2.1.5: {} + bowser@2.13.1: {} + cac@6.7.14: {} chai@5.3.3: @@ -2069,6 +3323,10 @@ snapshots: exsolve@1.0.8: {} + fast-xml-parser@5.2.5: + dependencies: + strnum: 2.1.2 + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 @@ -2227,6 +3485,8 @@ snapshots: dependencies: js-tokens: 9.0.1 + strnum@2.1.2: {} + supports-color@10.2.2: {} tinybench@2.9.0: {} @@ -2244,8 +3504,7 @@ snapshots: tinyspy@4.0.4: {} - tslib@2.8.1: - optional: true + tslib@2.8.1: {} typescript@5.9.3: {} diff --git a/src/index.ts b/src/index.ts index 94ebf82..dca941a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,7 +16,7 @@ export default { // ここでバックエンドストレージとの接続処理を実装 // 例: R2, KV, または外部ストレージへのプロキシ - + if (method === "PUT" || method === "POST") { // ストリーミングアップロード処理 // await uploadToBackend(request.body, url.pathname); @@ -27,7 +27,8 @@ export default { // return new Response(stream, { status: 200 }); return new Response(`Verified ${method} for ${url.pathname}`, { status: 200 }); } else if (method === "DELETE") { - return new Response(`Verified ${method} for ${url.pathname}`, { status: 204 }); + // 204 No Content はボディを持てない + return new Response(null, { status: 204 }); } return new Response("Method not allowed", { status: 405 }); @@ -46,12 +47,13 @@ interface Env { } async function verifySignature(request: Request, env: Env): Promise { + const start = performance.now(); const url = new URL(request.url); const headers = request.headers; // Query-based auth (Presigned URL) かどうか判定 const isQueryAuth = url.searchParams.has("X-Amz-Algorithm"); - + let algorithm: string; if (isQueryAuth) { algorithm = url.searchParams.get("X-Amz-Algorithm") ?? ""; @@ -65,12 +67,10 @@ async function verifySignature(request: Request, env: Env): Promise { } // 日時情報の取得 - const datetime = (isQueryAuth - ? url.searchParams.get("X-Amz-Date") - : headers.get("x-amz-date")) ?? ""; - + const datetime = (isQueryAuth ? url.searchParams.get("X-Amz-Date") : headers.get("x-amz-date")) ?? ""; + if (!datetime) return false; - + const date = datetime.substring(0, 8); // Canonical Request の生成 @@ -79,12 +79,7 @@ async function verifySignature(request: Request, env: Env): Promise { // String to Sign の生成 const credentialScope = `${date}/${env.REGION}/s3/aws4_request`; - const stringToSign = [ - "AWS4-HMAC-SHA256", - datetime, - credentialScope, - hashedCanonicalRequest - ].join("\n"); + const stringToSign = ["AWS4-HMAC-SHA256", datetime, credentialScope, hashedCanonicalRequest].join("\n"); // 署名の計算 const signingKey = await getSigningKey(env.SECRET_KEY, date, env.REGION, "s3"); @@ -101,25 +96,45 @@ async function verifySignature(request: Request, env: Env): Promise { expectedSignature = match ? match[1] : ""; } + // デバッグ用 + if (signatureHex !== expectedSignature) { + console.log("Signature mismatch!"); + console.log("Canonical Request:", canonicalRequest); + console.log("String to Sign:", stringToSign); + console.log("Computed:", signatureHex); + console.log("Expected:", expectedSignature); + } + + const end = performance.now(); + const cpuUsed = end - start; + + console.debug(`CPU time used: ${cpuUsed}ms`); + return signatureHex === expectedSignature; } async function createCanonicalRequest(request: Request, isQueryAuth: boolean): Promise { const url = new URL(request.url); - + // HTTPメソッド const method = request.method; - + // Canonical URI (パス部分) const canonicalUri = url.pathname || "/"; - + // Canonical Query String + // AWS署名バージョン4では、パラメータ名の大文字小文字を区別してソート const params = Array.from(url.searchParams.entries()) .filter(([key]) => key !== "X-Amz-Signature") // 署名自体は除外 - .sort(([a], [b]) => a.localeCompare(b)) + .sort(([a], [b]) => { + // バイナリソート(大文字小文字を区別) + if (a < b) return -1; + if (a > b) return 1; + return 0; + }) .map(([key, val]) => `${encodeRFC3986(key)}=${encodeRFC3986(val)}`) .join("&"); - + // Signed Headers の取得 let signedHeadersList: string[]; if (isQueryAuth) { @@ -129,78 +144,52 @@ async function createCanonicalRequest(request: Request, isQueryAuth: boolean): P const match = authHeader.match(/SignedHeaders=([^,\s]+)/); signedHeadersList = match ? match[1].split(";") : ["host"]; } - + // Canonical Headers の生成 const canonicalHeaders = signedHeadersList - .map(h => { + .map((h) => { const headerName = h.toLowerCase(); let headerValue = ""; - + if (headerName === "host") { - // ホストヘッダーはポート番号を除外 (標準ポートの場合) - const host = url.hostname; + // ホストヘッダーをそのまま使用(URLのhostnameを使用) + headerValue = url.hostname; + // 非標準ポートの場合はポート番号を追加 const port = url.port; - if ((url.protocol === "https:" && port === "443") || - (url.protocol === "http:" && port === "80") || - !port) { - headerValue = host; - } else { - headerValue = `${host}:${port}`; + if (port && !((url.protocol === "https:" && port === "443") || (url.protocol === "http:" && port === "80"))) { + headerValue += `:${port}`; } } else { headerValue = request.headers.get(headerName)?.trim() ?? ""; } - + return `${headerName}:${headerValue}\n`; }) .join(""); - + const signedHeaders = signedHeadersList.join(";"); - + // Payload Hash - const payloadHash = request.headers.get("x-amz-content-sha256") ?? - (isQueryAuth ? "UNSIGNED-PAYLOAD" : "UNSIGNED-PAYLOAD"); - - return [ - method, - canonicalUri, - params, - canonicalHeaders, - signedHeaders, - payloadHash - ].join("\n"); + const payloadHash = request.headers.get("x-amz-content-sha256") ?? (isQueryAuth ? "UNSIGNED-PAYLOAD" : "UNSIGNED-PAYLOAD"); + + return [method, canonicalUri, params, canonicalHeaders, signedHeaders, payloadHash].join("\n"); } // RFC3986に準拠したURLエンコーディング function encodeRFC3986(str: string): string { - return encodeURIComponent(str) - .replace(/[!'()*]/g, c => `%${c.charCodeAt(0).toString(16).toUpperCase()}`); + return encodeURIComponent(str).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`); } -async function getSigningKey( - secret: string, - date: string, - region: string, - service: string -): Promise { +async function getSigningKey(secret: string, date: string, region: string, service: string): Promise { const kDate = await hmacSha256("AWS4" + secret, date); const kRegion = await hmacSha256(kDate, region); const kService = await hmacSha256(kRegion, service); return await hmacSha256(kService, "aws4_request"); } -async function hmacSha256( - key: string | ArrayBuffer, - data: string -): Promise { +async function hmacSha256(key: string | ArrayBuffer, data: string): Promise { const keyData = typeof key === "string" ? new TextEncoder().encode(key) : key; - const cryptoKey = await crypto.subtle.importKey( - "raw", - keyData, - { name: "HMAC", hash: "SHA-256" }, - false, - ["sign"] - ); + const cryptoKey = await crypto.subtle.importKey("raw", keyData, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]); return await crypto.subtle.sign("HMAC", cryptoKey, new TextEncoder().encode(data)); } @@ -211,6 +200,6 @@ async function sha256(data: string): Promise { function bufToHex(buf: ArrayBuffer): string { return Array.from(new Uint8Array(buf)) - .map(b => b.toString(16).padStart(2, "0")) + .map((b) => b.toString(16).padStart(2, "0")) .join(""); -} \ No newline at end of file +} diff --git a/test/index.spec.ts b/test/index.spec.ts deleted file mode 100644 index 3abb76b..0000000 --- a/test/index.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { env, createExecutionContext, waitOnExecutionContext, SELF } from "cloudflare:test"; -import { describe, it, expect } from "vitest"; -import worker from "../src/index"; - -// For now, you'll need to do something like this to get a correctly-typed -// `Request` to pass to `worker.fetch()`. -const IncomingRequest = Request; - -describe("Hello World worker", () => { - it("responds with Hello World! (unit style)", async () => { - const request = new IncomingRequest("http://example.com"); - // Create an empty context to pass to `worker.fetch()`. - const ctx = createExecutionContext(); - const response = await worker.fetch(request, env, ctx); - // Wait for all `Promise`s passed to `ctx.waitUntil()` to settle before running test assertions - await waitOnExecutionContext(ctx); - expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`); - }); - - it("responds with Hello World! (integration style)", async () => { - const response = await SELF.fetch("https://example.com"); - expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`); - }); -}); diff --git a/test/s3.test.ts b/test/s3.test.ts new file mode 100644 index 0000000..5bff9dd --- /dev/null +++ b/test/s3.test.ts @@ -0,0 +1,127 @@ +import { describe, it, expect, vi } from "vitest"; +import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3"; +import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; +import { AwsClient } from "aws4fetch"; +import worker from "../src/index"; + +const ENV = { + ACCESS_KEY: "test-access-key", + SECRET_KEY: "test-secret-key", + REGION: "auto", +}; + +const CTX = { + waitUntil: vi.fn(), + passThroughOnException: vi.fn(), +}; + +describe("S3 API Server Authentication", () => { + const endpoint = "https://s3-api.example.com"; + + it("should verify requests from aws4fetch (Header Auth)", async () => { + const aws4 = new AwsClient({ + accessKeyId: ENV.ACCESS_KEY, + secretAccessKey: ENV.SECRET_KEY, + region: ENV.REGION, + service: "s3", + }); + + const requestUrl = `${endpoint}/test-bucket/test-file.txt`; + const signedReq = await aws4.sign(requestUrl, { + method: "PUT", + headers: { + "Content-Type": "text/plain", + "x-amz-content-sha256": "UNSIGNED-PAYLOAD", + }, + body: "hello world", + }); + + const response = await worker.fetch(signedReq, ENV, CTX); + expect(response.status).toBe(200); + expect(await response.text()).toContain("Verified PUT"); + }); + + it("should verify presigned URLs from @aws-sdk/s3-request-presigner", async () => { + const s3 = new S3Client({ + region: ENV.REGION, + credentials: { + accessKeyId: ENV.ACCESS_KEY, + secretAccessKey: ENV.SECRET_KEY, + }, + }); + + const command = new GetObjectCommand({ + Bucket: "my-bucket", + Key: "test.png", + }); + + const url = await getSignedUrl(s3, command, { expiresIn: 3600 }); + + console.log("Presigned URL:", url); + + const parsedUrl = new URL(url); + + const testUrl = new URL(endpoint); + testUrl.hostname = parsedUrl.hostname; + testUrl.pathname = parsedUrl.pathname; + testUrl.search = parsedUrl.search; + + const request = new Request(testUrl.toString(), { method: "GET" }); + const response = await worker.fetch(request, ENV, CTX); + + expect(response.status).toBe(200); + expect(await response.text()).toContain("Verified GET"); + }); + + it("should verify requests from aws4fetch with query parameters", async () => { + const aws4 = new AwsClient({ + accessKeyId: ENV.ACCESS_KEY, + secretAccessKey: ENV.SECRET_KEY, + region: ENV.REGION, + service: "s3", + }); + + const requestUrl = `${endpoint}/test-bucket/aws4-fetch-test`; + const signedReq = await aws4.sign(requestUrl, { + method: "GET", + aws: { signQuery: true }, // クエリパラメータ署名 + }); + + const response = await worker.fetch(signedReq, ENV, CTX); + expect(response.status).toBe(200); + }); + + it("should reject invalid signatures", async () => { + const request = new Request(`${endpoint}/hack`, { + method: "GET", + headers: { + Authorization: "AWS4-HMAC-SHA256 Credential=bad/20260108/auto/s3/aws4_request, SignedHeaders=host, Signature=wrong", + "x-amz-date": "20260108T000000Z", + }, + }); + + const response = await worker.fetch(request, ENV, CTX); + expect(response.status).toBe(403); + }); + + // 5. DELETE メソッドのテスト + it("should verify DELETE requests", async () => { + const aws4 = new AwsClient({ + accessKeyId: ENV.ACCESS_KEY, + secretAccessKey: ENV.SECRET_KEY, + region: ENV.REGION, + service: "s3", + }); + + const requestUrl = `${endpoint}/test-bucket/file-to-delete.txt`; + const signedReq = await aws4.sign(requestUrl, { + method: "DELETE", + headers: { + "x-amz-content-sha256": "UNSIGNED-PAYLOAD", + }, + }); + + const response = await worker.fetch(signedReq, ENV, CTX); + expect(response.status).toBe(204); + }); +}); diff --git a/vitest.config.mts b/vitest.config.mts index d830077..a57ae67 100644 --- a/vitest.config.mts +++ b/vitest.config.mts @@ -1,11 +1,7 @@ -import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config"; +import { configDefaults, defineConfig } from "vitest/config"; -export default defineWorkersConfig({ +export default defineConfig({ test: { - poolOptions: { - workers: { - wrangler: { configPath: "./wrangler.jsonc" }, - }, - }, + exclude: [...configDefaults.exclude, ".pnpm-wrangler/**", ".pnpm-store/**", "node_modules/**"], }, });