From 4b7c0e767cae101511abd86038044861271fe1bc Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Wed, 1 Nov 2023 12:26:55 -0400 Subject: [PATCH 01/11] Bump dependencies --- package-lock.json | 709 ++++++++++++++++++++++++++++++++++++++-------- package.json | 2 +- 2 files changed, 586 insertions(+), 125 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6f0ec9a10..3ec6cda91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -87,7 +87,7 @@ "identity-obj-proxy": "^3.0.0", "jsdom": "^22.1.0", "lint-staged": "^14.0.1", - "msw": "^1.3.2", + "msw": "^2.0.2", "patch-package": "^8.0.0", "postcss": "^8.2.9", "postcss-import": "^15.1.0", @@ -575,6 +575,33 @@ "node": ">=6.9.0" } }, + "node_modules/@bundled-es-modules/cookie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.0.tgz", + "integrity": "sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==", + "dev": true, + "dependencies": { + "cookie": "^0.5.0" + } + }, + "node_modules/@bundled-es-modules/js-levenshtein": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/js-levenshtein/-/js-levenshtein-2.0.1.tgz", + "integrity": "sha512-DERMS3yfbAljKsQc0U2wcqGKUWpdFjwqWuoMugEJlqBnKO180/n+4SR/J8MRDt1AN48X1ovgoD9KrdVXcaa3Rg==", + "dev": true, + "dependencies": { + "js-levenshtein": "^1.1.6" + } + }, + "node_modules/@bundled-es-modules/statuses": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", + "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", + "dev": true, + "dependencies": { + "statuses": "^2.0.1" + } + }, "node_modules/@csstools/selector-specificity": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", @@ -1553,12 +1580,178 @@ "react-dom": ">=16.14.0" } }, + "node_modules/@ladle/react/node_modules/@mswjs/cookies": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-0.2.2.tgz", + "integrity": "sha512-mlN83YSrcFgk7Dm1Mys40DLssI1KdJji2CMKN8eOlBqsTADYzj2+jWzsANsUTFbxDMWPD5e9bfA1RGqBpS3O1g==", + "dev": true, + "dependencies": { + "@types/set-cookie-parser": "^2.4.0", + "set-cookie-parser": "^2.4.6" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ladle/react/node_modules/@mswjs/interceptors": { + "version": "0.17.10", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.17.10.tgz", + "integrity": "sha512-N8x7eSLGcmUFNWZRxT1vsHvypzIRgQYdG0rJey/rZCy6zT/30qDt8Joj7FxzGNLSwXbeZqJOMqDurp7ra4hgbw==", + "dev": true, + "dependencies": { + "@open-draft/until": "^1.0.3", + "@types/debug": "^4.1.7", + "@xmldom/xmldom": "^0.8.3", + "debug": "^4.3.3", + "headers-polyfill": "3.2.5", + "outvariant": "^1.2.1", + "strict-event-emitter": "^0.2.4", + "web-encoding": "^1.1.5" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ladle/react/node_modules/@mswjs/interceptors/node_modules/strict-event-emitter": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.2.8.tgz", + "integrity": "sha512-KDf/ujU8Zud3YaLtMCcTI4xkZlZVIYxTLr+XIULexP+77EEVWixeXroLUXQXiVtH4XH2W7jr/3PT1v3zBuvc3A==", + "dev": true, + "dependencies": { + "events": "^3.3.0" + } + }, + "node_modules/@ladle/react/node_modules/@open-draft/until": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-1.0.3.tgz", + "integrity": "sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==", + "dev": true + }, "node_modules/@ladle/react/node_modules/@types/unist": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.0.tgz", "integrity": "sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w==", "dev": true }, + "node_modules/@ladle/react/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@ladle/react/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@ladle/react/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@ladle/react/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@ladle/react/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@ladle/react/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@ladle/react/node_modules/msw": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/msw/-/msw-1.3.2.tgz", + "integrity": "sha512-wKLhFPR+NitYTkQl5047pia0reNGgf0P6a1eTnA5aNlripmiz0sabMvvHcicE8kQ3/gZcI0YiPFWmYfowfm3lA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@mswjs/cookies": "^0.2.2", + "@mswjs/interceptors": "^0.17.10", + "@open-draft/until": "^1.0.3", + "@types/cookie": "^0.4.1", + "@types/js-levenshtein": "^1.1.1", + "chalk": "^4.1.1", + "chokidar": "^3.4.2", + "cookie": "^0.4.2", + "graphql": "^16.8.1", + "headers-polyfill": "3.2.5", + "inquirer": "^8.2.0", + "is-node-process": "^1.2.0", + "js-levenshtein": "^1.1.6", + "node-fetch": "^2.6.7", + "outvariant": "^1.4.0", + "path-to-regexp": "^6.2.0", + "strict-event-emitter": "^0.4.3", + "type-fest": "^2.19.0", + "yargs": "^17.3.1" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.4.x <= 5.2.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@ladle/react/node_modules/path-to-regexp": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", + "dev": true + }, "node_modules/@ladle/react/node_modules/source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -1568,6 +1761,36 @@ "node": ">= 8" } }, + "node_modules/@ladle/react/node_modules/strict-event-emitter": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.4.6.tgz", + "integrity": "sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==", + "dev": true + }, + "node_modules/@ladle/react/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@ladle/react/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@ladle/react/node_modules/unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", @@ -1685,14 +1908,10 @@ } }, "node_modules/@mswjs/cookies": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-0.2.2.tgz", - "integrity": "sha512-mlN83YSrcFgk7Dm1Mys40DLssI1KdJji2CMKN8eOlBqsTADYzj2+jWzsANsUTFbxDMWPD5e9bfA1RGqBpS3O1g==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-1.0.0.tgz", + "integrity": "sha512-TdXoBdI+h/EDTsVLCX/34s4+9U0sWi92qFnIGUEikpMCSKLhBeujovyYVSoORNbYgsBH5ga7/tfxyWcEZAxiYA==", "dev": true, - "dependencies": { - "@types/set-cookie-parser": "^2.4.0", - "set-cookie-parser": "^2.4.6" - }, "engines": { "node": ">=14" } @@ -1711,31 +1930,20 @@ } }, "node_modules/@mswjs/interceptors": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.17.10.tgz", - "integrity": "sha512-N8x7eSLGcmUFNWZRxT1vsHvypzIRgQYdG0rJey/rZCy6zT/30qDt8Joj7FxzGNLSwXbeZqJOMqDurp7ra4hgbw==", + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.25.7.tgz", + "integrity": "sha512-U7iFYs/qU/5jfz1VDpoYz3xqX9nzhsBXw7q923dv6GiGTy+m2ZLhD33L80R/shHOW/YWjeH6k16GbIHGw+bAng==", "dev": true, "dependencies": { - "@open-draft/until": "^1.0.3", - "@types/debug": "^4.1.7", - "@xmldom/xmldom": "^0.8.3", - "debug": "^4.3.3", - "headers-polyfill": "3.2.5", + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", "outvariant": "^1.2.1", - "strict-event-emitter": "^0.2.4", - "web-encoding": "^1.1.5" + "strict-event-emitter": "^0.5.1" }, "engines": { - "node": ">=14" - } - }, - "node_modules/@mswjs/interceptors/node_modules/strict-event-emitter": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.2.8.tgz", - "integrity": "sha512-KDf/ujU8Zud3YaLtMCcTI4xkZlZVIYxTLr+XIULexP+77EEVWixeXroLUXQXiVtH4XH2W7jr/3PT1v3zBuvc3A==", - "dev": true, - "dependencies": { - "events": "^3.3.0" + "node": ">=18" } }, "node_modules/@nodelib/fs.scandir": { @@ -1773,10 +1981,26 @@ "node": ">= 8" } }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, "node_modules/@open-draft/until": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-1.0.3.tgz", - "integrity": "sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", "dev": true }, "node_modules/@oxide/design-system": { @@ -5908,9 +6132,9 @@ "dev": true }, "node_modules/@types/set-cookie-parser": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.3.tgz", - "integrity": "sha512-7QhnH7bi+6KAhBB+Auejz1uV9DHiopZqu7LfR/5gZZTkejJV5nYeZZpgfFoE0N8aDsXuiYpfKyfyMatCwQhyTQ==", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.5.tgz", + "integrity": "sha512-ZPmztaAQ4rbnW/WTUnT1dwSENQo4bjGqxCSeyK+gZxmd+zJl/QAeF6dpEXcS5UEJX22HwiggFSaY8nE1nRmkbg==", "dev": true, "dependencies": { "@types/node": "*" @@ -5924,6 +6148,12 @@ "optional": true, "peer": true }, + "node_modules/@types/statuses": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.3.tgz", + "integrity": "sha512-NwCYScf83RIwCyi5/9cXocrJB//xrqMh5PMw3mYTSFGaI3DuVjBLfO/PCk7QVAC3Da8b9NjxNmTO9Aj9T3rl/Q==", + "dev": true + }, "node_modules/@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", @@ -10170,6 +10400,19 @@ "node": ">= 6" } }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "dev": true, + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -14926,29 +15169,33 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/msw": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/msw/-/msw-1.3.2.tgz", - "integrity": "sha512-wKLhFPR+NitYTkQl5047pia0reNGgf0P6a1eTnA5aNlripmiz0sabMvvHcicE8kQ3/gZcI0YiPFWmYfowfm3lA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.0.2.tgz", + "integrity": "sha512-loyQnNUDY1x05R/t2naVdtNhP+tfyf+ckEwtvRUuoK9JnDeoh3/ZN3Fu2ZtvO/iJ3IwwuLizWwWaxBxS3sDQUw==", "dev": true, "hasInstallScript": true, "dependencies": { - "@mswjs/cookies": "^0.2.2", - "@mswjs/interceptors": "^0.17.10", - "@open-draft/until": "^1.0.3", + "@bundled-es-modules/cookie": "^2.0.0", + "@bundled-es-modules/js-levenshtein": "^2.0.1", + "@bundled-es-modules/statuses": "^1.0.1", + "@mswjs/cookies": "^1.0.0", + "@mswjs/interceptors": "^0.25.1", + "@open-draft/until": "^2.1.0", "@types/cookie": "^0.4.1", "@types/js-levenshtein": "^1.1.1", - "chalk": "^4.1.1", + "@types/statuses": "^2.0.1", + "chalk": "^4.1.2", "chokidar": "^3.4.2", - "cookie": "^0.4.2", + "formdata-node": "4.4.1", "graphql": "^16.8.1", - "headers-polyfill": "3.2.5", + "headers-polyfill": "^4.0.1", "inquirer": "^8.2.0", "is-node-process": "^1.2.0", "js-levenshtein": "^1.1.6", "node-fetch": "^2.6.7", "outvariant": "^1.4.0", "path-to-regexp": "^6.2.0", - "strict-event-emitter": "^0.4.3", + "strict-event-emitter": "^0.5.0", "type-fest": "^2.19.0", "yargs": "^17.3.1" }, @@ -14956,14 +15203,14 @@ "msw": "cli/index.js" }, "engines": { - "node": ">=14" + "node": ">=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mswjs" }, "peerDependencies": { - "typescript": ">= 4.4.x <= 5.2.x" + "typescript": ">= 4.7.x <= 5.2.x" }, "peerDependenciesMeta": { "typescript": { @@ -14987,9 +15234,9 @@ } }, "node_modules/msw/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { "ansi-styles": "^4.1.0", @@ -15020,15 +15267,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/msw/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/msw/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -15038,18 +15276,18 @@ "node": ">=8" } }, + "node_modules/msw/node_modules/headers-polyfill": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.2.tgz", + "integrity": "sha512-EWGTfnTqAO2L/j5HZgoM/3z82L7necsJ0pO9Tp0X1wil3PDLrkypTBRgVO2ExehEEvUycejZD3FuRaXpZZc3kw==", + "dev": true + }, "node_modules/msw/node_modules/path-to-regexp": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", "dev": true }, - "node_modules/msw/node_modules/strict-event-emitter": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.4.6.tgz", - "integrity": "sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==", - "dev": true - }, "node_modules/msw/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -15132,6 +15370,25 @@ "tslib": "^2.0.3" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -19908,6 +20165,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -20695,6 +20961,33 @@ "to-fast-properties": "^2.0.0" } }, + "@bundled-es-modules/cookie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.0.tgz", + "integrity": "sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==", + "dev": true, + "requires": { + "cookie": "^0.5.0" + } + }, + "@bundled-es-modules/js-levenshtein": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/js-levenshtein/-/js-levenshtein-2.0.1.tgz", + "integrity": "sha512-DERMS3yfbAljKsQc0U2wcqGKUWpdFjwqWuoMugEJlqBnKO180/n+4SR/J8MRDt1AN48X1ovgoD9KrdVXcaa3Rg==", + "dev": true, + "requires": { + "js-levenshtein": "^1.1.6" + } + }, + "@bundled-es-modules/statuses": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", + "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", + "dev": true, + "requires": { + "statuses": "^2.0.1" + } + }, "@csstools/selector-specificity": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", @@ -21344,18 +21637,161 @@ "vite-tsconfig-paths": "^4.2.1" }, "dependencies": { + "@mswjs/cookies": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-0.2.2.tgz", + "integrity": "sha512-mlN83YSrcFgk7Dm1Mys40DLssI1KdJji2CMKN8eOlBqsTADYzj2+jWzsANsUTFbxDMWPD5e9bfA1RGqBpS3O1g==", + "dev": true, + "requires": { + "@types/set-cookie-parser": "^2.4.0", + "set-cookie-parser": "^2.4.6" + } + }, + "@mswjs/interceptors": { + "version": "0.17.10", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.17.10.tgz", + "integrity": "sha512-N8x7eSLGcmUFNWZRxT1vsHvypzIRgQYdG0rJey/rZCy6zT/30qDt8Joj7FxzGNLSwXbeZqJOMqDurp7ra4hgbw==", + "dev": true, + "requires": { + "@open-draft/until": "^1.0.3", + "@types/debug": "^4.1.7", + "@xmldom/xmldom": "^0.8.3", + "debug": "^4.3.3", + "headers-polyfill": "3.2.5", + "outvariant": "^1.2.1", + "strict-event-emitter": "^0.2.4", + "web-encoding": "^1.1.5" + }, + "dependencies": { + "strict-event-emitter": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.2.8.tgz", + "integrity": "sha512-KDf/ujU8Zud3YaLtMCcTI4xkZlZVIYxTLr+XIULexP+77EEVWixeXroLUXQXiVtH4XH2W7jr/3PT1v3zBuvc3A==", + "dev": true, + "requires": { + "events": "^3.3.0" + } + } + } + }, + "@open-draft/until": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-1.0.3.tgz", + "integrity": "sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==", + "dev": true + }, "@types/unist": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.0.tgz", "integrity": "sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w==", "dev": true }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "msw": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/msw/-/msw-1.3.2.tgz", + "integrity": "sha512-wKLhFPR+NitYTkQl5047pia0reNGgf0P6a1eTnA5aNlripmiz0sabMvvHcicE8kQ3/gZcI0YiPFWmYfowfm3lA==", + "dev": true, + "requires": { + "@mswjs/cookies": "^0.2.2", + "@mswjs/interceptors": "^0.17.10", + "@open-draft/until": "^1.0.3", + "@types/cookie": "^0.4.1", + "@types/js-levenshtein": "^1.1.1", + "chalk": "^4.1.1", + "chokidar": "^3.4.2", + "cookie": "^0.4.2", + "graphql": "^16.8.1", + "headers-polyfill": "3.2.5", + "inquirer": "^8.2.0", + "is-node-process": "^1.2.0", + "js-levenshtein": "^1.1.6", + "node-fetch": "^2.6.7", + "outvariant": "^1.4.0", + "path-to-regexp": "^6.2.0", + "strict-event-emitter": "^0.4.3", + "type-fest": "^2.19.0", + "yargs": "^17.3.1" + } + }, + "path-to-regexp": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", + "dev": true + }, "source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true }, + "strict-event-emitter": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.4.6.tgz", + "integrity": "sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true + }, "unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", @@ -21453,14 +21889,10 @@ } }, "@mswjs/cookies": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-0.2.2.tgz", - "integrity": "sha512-mlN83YSrcFgk7Dm1Mys40DLssI1KdJji2CMKN8eOlBqsTADYzj2+jWzsANsUTFbxDMWPD5e9bfA1RGqBpS3O1g==", - "dev": true, - "requires": { - "@types/set-cookie-parser": "^2.4.0", - "set-cookie-parser": "^2.4.6" - } + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-1.0.0.tgz", + "integrity": "sha512-TdXoBdI+h/EDTsVLCX/34s4+9U0sWi92qFnIGUEikpMCSKLhBeujovyYVSoORNbYgsBH5ga7/tfxyWcEZAxiYA==", + "dev": true }, "@mswjs/http-middleware": { "version": "0.8.0", @@ -21472,30 +21904,17 @@ } }, "@mswjs/interceptors": { - "version": "0.17.10", - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.17.10.tgz", - "integrity": "sha512-N8x7eSLGcmUFNWZRxT1vsHvypzIRgQYdG0rJey/rZCy6zT/30qDt8Joj7FxzGNLSwXbeZqJOMqDurp7ra4hgbw==", + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.25.7.tgz", + "integrity": "sha512-U7iFYs/qU/5jfz1VDpoYz3xqX9nzhsBXw7q923dv6GiGTy+m2ZLhD33L80R/shHOW/YWjeH6k16GbIHGw+bAng==", "dev": true, "requires": { - "@open-draft/until": "^1.0.3", - "@types/debug": "^4.1.7", - "@xmldom/xmldom": "^0.8.3", - "debug": "^4.3.3", - "headers-polyfill": "3.2.5", + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", "outvariant": "^1.2.1", - "strict-event-emitter": "^0.2.4", - "web-encoding": "^1.1.5" - }, - "dependencies": { - "strict-event-emitter": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.2.8.tgz", - "integrity": "sha512-KDf/ujU8Zud3YaLtMCcTI4xkZlZVIYxTLr+XIULexP+77EEVWixeXroLUXQXiVtH4XH2W7jr/3PT1v3zBuvc3A==", - "dev": true, - "requires": { - "events": "^3.3.0" - } - } + "strict-event-emitter": "^0.5.1" } }, "@nodelib/fs.scandir": { @@ -21524,10 +21943,26 @@ "fastq": "^1.6.0" } }, + "@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true + }, + "@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "requires": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, "@open-draft/until": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-1.0.3.tgz", - "integrity": "sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", "dev": true }, "@oxide/design-system": { @@ -24771,9 +25206,9 @@ "dev": true }, "@types/set-cookie-parser": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.3.tgz", - "integrity": "sha512-7QhnH7bi+6KAhBB+Auejz1uV9DHiopZqu7LfR/5gZZTkejJV5nYeZZpgfFoE0N8aDsXuiYpfKyfyMatCwQhyTQ==", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.5.tgz", + "integrity": "sha512-ZPmztaAQ4rbnW/WTUnT1dwSENQo4bjGqxCSeyK+gZxmd+zJl/QAeF6dpEXcS5UEJX22HwiggFSaY8nE1nRmkbg==", "dev": true, "requires": { "@types/node": "*" @@ -24787,6 +25222,12 @@ "optional": true, "peer": true }, + "@types/statuses": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.3.tgz", + "integrity": "sha512-NwCYScf83RIwCyi5/9cXocrJB//xrqMh5PMw3mYTSFGaI3DuVjBLfO/PCk7QVAC3Da8b9NjxNmTO9Aj9T3rl/Q==", + "dev": true + }, "@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", @@ -27804,6 +28245,16 @@ "mime-types": "^2.1.12" } }, + "formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "dev": true, + "requires": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + } + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -31220,28 +31671,32 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "msw": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/msw/-/msw-1.3.2.tgz", - "integrity": "sha512-wKLhFPR+NitYTkQl5047pia0reNGgf0P6a1eTnA5aNlripmiz0sabMvvHcicE8kQ3/gZcI0YiPFWmYfowfm3lA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.0.2.tgz", + "integrity": "sha512-loyQnNUDY1x05R/t2naVdtNhP+tfyf+ckEwtvRUuoK9JnDeoh3/ZN3Fu2ZtvO/iJ3IwwuLizWwWaxBxS3sDQUw==", "dev": true, "requires": { - "@mswjs/cookies": "^0.2.2", - "@mswjs/interceptors": "^0.17.10", - "@open-draft/until": "^1.0.3", + "@bundled-es-modules/cookie": "^2.0.0", + "@bundled-es-modules/js-levenshtein": "^2.0.1", + "@bundled-es-modules/statuses": "^1.0.1", + "@mswjs/cookies": "^1.0.0", + "@mswjs/interceptors": "^0.25.1", + "@open-draft/until": "^2.1.0", "@types/cookie": "^0.4.1", "@types/js-levenshtein": "^1.1.1", - "chalk": "^4.1.1", + "@types/statuses": "^2.0.1", + "chalk": "^4.1.2", "chokidar": "^3.4.2", - "cookie": "^0.4.2", + "formdata-node": "4.4.1", "graphql": "^16.8.1", - "headers-polyfill": "3.2.5", + "headers-polyfill": "^4.0.1", "inquirer": "^8.2.0", "is-node-process": "^1.2.0", "js-levenshtein": "^1.1.6", "node-fetch": "^2.6.7", "outvariant": "^1.4.0", "path-to-regexp": "^6.2.0", - "strict-event-emitter": "^0.4.3", + "strict-event-emitter": "^0.5.0", "type-fest": "^2.19.0", "yargs": "^17.3.1" }, @@ -31256,9 +31711,9 @@ } }, "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -31280,30 +31735,24 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "headers-polyfill": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.2.tgz", + "integrity": "sha512-EWGTfnTqAO2L/j5HZgoM/3z82L7necsJ0pO9Tp0X1wil3PDLrkypTBRgVO2ExehEEvUycejZD3FuRaXpZZc3kw==", + "dev": true + }, "path-to-regexp": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", "dev": true }, - "strict-event-emitter": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.4.6.tgz", - "integrity": "sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==", - "dev": true - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -31364,6 +31813,12 @@ "tslib": "^2.0.3" } }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true + }, "node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -34617,6 +35072,12 @@ "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", "dev": true }, + "web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "dev": true + }, "webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", diff --git a/package.json b/package.json index deba5abd1..e36cda8fe 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "identity-obj-proxy": "^3.0.0", "jsdom": "^22.1.0", "lint-staged": "^14.0.1", - "msw": "^1.3.2", + "msw": "^2.0.2", "patch-package": "^8.0.0", "postcss": "^8.2.9", "postcss-import": "^15.1.0", From d9a94c66a0019da5da60b5137af98d7f5e4fc846 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Wed, 1 Nov 2023 19:14:33 -0400 Subject: [PATCH 02/11] Update msw dependency --- mockServiceWorker.js | 175 ++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 83 insertions(+), 94 deletions(-) diff --git a/mockServiceWorker.js b/mockServiceWorker.js index 51d85eeeb..6498967c0 100644 --- a/mockServiceWorker.js +++ b/mockServiceWorker.js @@ -2,13 +2,14 @@ /* tslint:disable */ /** - * Mock Service Worker (1.3.2). + * Mock Service Worker (2.0.2). * @see https://github.com/mswjs/msw * - Please do NOT modify this file. * - Please do NOT serve this file on production. */ -const INTEGRITY_CHECKSUM = '3d6b9f06410d179a7f7404d4bf4c3c70' +const INTEGRITY_CHECKSUM = '0877fcdc026242810f5bfde0d7178db4' +const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() self.addEventListener('install', function () { @@ -86,12 +87,6 @@ self.addEventListener('message', async function (event) { self.addEventListener('fetch', function (event) { const { request } = event - const accept = request.headers.get('accept') || '' - - // Bypass server-sent events. - if (accept.includes('text/event-stream')) { - return - } // Bypass navigation requests. if (request.mode === 'navigate') { @@ -112,29 +107,8 @@ self.addEventListener('fetch', function (event) { } // Generate unique request ID. - const requestId = Math.random().toString(16).slice(2) - - event.respondWith( - handleRequest(event, requestId).catch((error) => { - if (error.name === 'NetworkError') { - console.warn( - '[MSW] Successfully emulated a network error for the "%s %s" request.', - request.method, - request.url, - ) - return - } - - // At this point, any exception indicates an issue with the original request/response. - console.error( - `\ -[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`, - request.method, - request.url, - `${error.name}: ${error.message}`, - ) - }), - ) + const requestId = crypto.randomUUID() + event.respondWith(handleRequest(event, requestId)) }) async function handleRequest(event, requestId) { @@ -146,21 +120,29 @@ async function handleRequest(event, requestId) { // this message will pend indefinitely. if (client && activeClientIds.has(client.id)) { ;(async function () { - const clonedResponse = response.clone() - sendToClient(client, { - type: 'RESPONSE', - payload: { - requestId, - type: clonedResponse.type, - ok: clonedResponse.ok, - status: clonedResponse.status, - statusText: clonedResponse.statusText, - body: - clonedResponse.body === null ? null : await clonedResponse.text(), - headers: Object.fromEntries(clonedResponse.headers.entries()), - redirected: clonedResponse.redirected, + const responseClone = response.clone() + // When performing original requests, response body will + // always be a ReadableStream, even for 204 responses. + // But when creating a new Response instance on the client, + // the body for a 204 response must be null. + const responseBody = response.status === 204 ? null : responseClone.body + + sendToClient( + client, + { + type: 'RESPONSE', + payload: { + requestId, + isMockedResponse: IS_MOCKED_RESPONSE in response, + type: responseClone.type, + status: responseClone.status, + statusText: responseClone.statusText, + body: responseBody, + headers: Object.fromEntries(responseClone.headers.entries()), + }, }, - }) + [responseBody], + ) })() } @@ -196,20 +178,20 @@ async function resolveMainClient(event) { async function getResponse(event, client, requestId) { const { request } = event - const clonedRequest = request.clone() + + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const requestClone = request.clone() function passthrough() { - // Clone the request because it might've been already used - // (i.e. its body has been read and sent to the client). - const headers = Object.fromEntries(clonedRequest.headers.entries()) + const headers = Object.fromEntries(requestClone.headers.entries()) - // Remove MSW-specific request headers so the bypassed requests - // comply with the server's CORS preflight check. - // Operate with the headers as an object because request "Headers" - // are immutable. - delete headers['x-msw-bypass'] + // Remove internal MSW request header so the passthrough request + // complies with any potential CORS preflight checks on the server. + // Some servers forbid unknown request headers. + delete headers['x-msw-intention'] - return fetch(clonedRequest, { headers }) + return fetch(requestClone, { headers }) } // Bypass mocking when the client is not active. @@ -227,31 +209,36 @@ async function getResponse(event, client, requestId) { // Bypass requests with the explicit bypass header. // Such requests can be issued by "ctx.fetch()". - if (request.headers.get('x-msw-bypass') === 'true') { + const mswIntention = request.headers.get('x-msw-intention') + if (['bypass', 'passthrough'].includes(mswIntention)) { return passthrough() } // Notify the client that a request has been intercepted. - const clientMessage = await sendToClient(client, { - type: 'REQUEST', - payload: { - id: requestId, - url: request.url, - method: request.method, - headers: Object.fromEntries(request.headers.entries()), - cache: request.cache, - mode: request.mode, - credentials: request.credentials, - destination: request.destination, - integrity: request.integrity, - redirect: request.redirect, - referrer: request.referrer, - referrerPolicy: request.referrerPolicy, - body: await request.text(), - bodyUsed: request.bodyUsed, - keepalive: request.keepalive, + const requestBuffer = await request.arrayBuffer() + const clientMessage = await sendToClient( + client, + { + type: 'REQUEST', + payload: { + id: requestId, + url: request.url, + mode: request.mode, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: requestBuffer, + keepalive: request.keepalive, + }, }, - }) + [requestBuffer], + ) switch (clientMessage.type) { case 'MOCK_RESPONSE': { @@ -261,21 +248,12 @@ async function getResponse(event, client, requestId) { case 'MOCK_NOT_FOUND': { return passthrough() } - - case 'NETWORK_ERROR': { - const { name, message } = clientMessage.data - const networkError = new Error(message) - networkError.name = name - - // Rejecting a "respondWith" promise emulates a network error. - throw networkError - } } return passthrough() } -function sendToClient(client, message) { +function sendToClient(client, message, transferrables = []) { return new Promise((resolve, reject) => { const channel = new MessageChannel() @@ -287,17 +265,28 @@ function sendToClient(client, message) { resolve(event.data) } - client.postMessage(message, [channel.port2]) + client.postMessage( + message, + [channel.port2].concat(transferrables.filter(Boolean)), + ) }) } -function sleep(timeMs) { - return new Promise((resolve) => { - setTimeout(resolve, timeMs) +async function respondWithMock(response) { + // Setting response status code to 0 is a no-op. + // However, when responding with a "Response.error()", the produced Response + // instance will have status code set to 0. Since it's not possible to create + // a Response instance with status code 0, handle that use-case separately. + if (response.status === 0) { + return Response.error() + } + + const mockedResponse = new Response(response.body, response) + + Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { + value: true, + enumerable: true, }) -} -async function respondWithMock(response) { - await sleep(response.delay) - return new Response(response.body, response) + return mockedResponse } diff --git a/package.json b/package.json index e36cda8fe..4ca09e3e3 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,7 @@ "not IE 11" ], "msw": { - "workerDirectory": "." + "workerDirectory": "" }, "lint-staged": { "*.{js,ts,tsx,json}": "eslint --cache --fix" From 00bbfe72ed67c3bd87174a7785b480e667146e70 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Wed, 1 Nov 2023 19:15:31 -0400 Subject: [PATCH 03/11] Fix MSW related type errors in generation --- app/msw-mock-api.ts | 16 +- app/test/unit/server.ts | 18 +- libs/api-mocks/index.ts | 2 +- libs/api-mocks/msw/db.ts | 6 +- libs/api-mocks/msw/handlers.ts | 115 +-- libs/api-mocks/msw/util.ts | 26 +- libs/api/__generated__/Api.ts | 1 + libs/api/__generated__/http-client.ts | 1 + libs/api/__generated__/msw-handlers.ts | 1189 ++++++++++++++---------- libs/api/__generated__/validate.ts | 1 + 10 files changed, 785 insertions(+), 590 deletions(-) diff --git a/app/msw-mock-api.ts b/app/msw-mock-api.ts index 989a197b8..b06c19b98 100644 --- a/app/msw-mock-api.ts +++ b/app/msw-mock-api.ts @@ -57,16 +57,22 @@ const sleep = async (ms: number) => new Promise((res) => setTimeout(res, ms)) export async function startMockAPI() { // dynamic imports to make extremely sure none of this code ends up in the prod bundle const { handlers } = await import('@oxide/api-mocks') - const { setupWorker, rest, compose } = await import('msw') + const { http, HttpResponse } = await import('msw') + const { setupWorker } = await import('msw/browser') // defined in here because it depends on the dynamic import - const interceptAll = rest.all('/v1/*', async (_req, res, ctx) => { + const interceptAll = http.all('/v1/*', async () => { // random delay on all requests to simulate a real API await sleep(randInt(200, 400)) if (shouldFail(chaos)) { // special header lets client indicate chaos failures so we don't get confused - return res(compose(ctx.status(randomStatus()), ctx.set('X-Chaos', ''))) + return new HttpResponse(null, { + status: randomStatus(), + headers: { + 'X-Chaos': '', + }, + }) } // don't return anything means fall through to the real handlers }) @@ -77,7 +83,7 @@ export async function startMockAPI() { // custom handler only to make logging less noisy. unhandled requests still // pass through to the server onUnhandledRequest(req) { - const path = req.url.pathname + const path = new URL(req.url).pathname const ignore = [ path.includes('libs/ui/assets'), // assets obviously loaded from file system path.startsWith('/forms/'), // lazy loaded forms @@ -86,7 +92,7 @@ export async function startMockAPI() { // message format copied from MSW source console.warn(`[MSW] Warning: captured an API request without a matching request handler: - • ${req.method} ${req.url.pathname} + • ${req.method} ${path} If you want to intercept this unhandled request, create a request handler for it.`) } diff --git a/app/test/unit/server.ts b/app/test/unit/server.ts index 497fb0e0a..d5dc4caeb 100644 --- a/app/test/unit/server.ts +++ b/app/test/unit/server.ts @@ -5,7 +5,7 @@ * * Copyright Oxide Computer Company */ -import { rest } from 'msw' +import { http, HttpResponse } from 'msw' import { setupServer } from 'msw/node' import { handlers } from '@oxide/api-mocks' @@ -20,18 +20,20 @@ export const server = setupServer( // Override request handlers in order to test special cases export function overrideOnce( - method: keyof typeof rest, + method: keyof typeof http, path: string, status: number, body: string | Record ) { server.use( - rest[method](path, (_req, res, ctx) => - // https://mswjs.io/docs/api/response/once - res.once( - ctx.status(status), - typeof body === 'string' ? ctx.text(body) : ctx.json(body) - ) + http[method]( + path, + () => + // https://mswjs.io/docs/api/response/once + typeof body === 'string' + ? new HttpResponse(body, { status }) + : HttpResponse.json(body, { status }), + { once: true } ) ) } diff --git a/libs/api-mocks/index.ts b/libs/api-mocks/index.ts index 7a5b5fc16..d0ac67102 100644 --- a/libs/api-mocks/index.ts +++ b/libs/api-mocks/index.ts @@ -24,5 +24,5 @@ export * from './user' export * from './vpc' export { handlers } from './msw/handlers' -export { json, MSW_USER_COOKIE } from './msw/util' +export { MSW_USER_COOKIE } from './msw/util' export { resetDb } from './msw/db' diff --git a/libs/api-mocks/msw/db.ts b/libs/api-mocks/msw/db.ts index 818e2b839..41f8787c1 100644 --- a/libs/api-mocks/msw/db.ts +++ b/libs/api-mocks/msw/db.ts @@ -7,6 +7,7 @@ */ // note that isUuid checks for any kind of UUID. strictly speaking, we should // only be checking for v4 +import { HttpResponse } from 'msw' import { validate as isUuid } from 'uuid' import type { ApiTypes as Api, PathParams as PP } from '@oxide/api' @@ -14,11 +15,12 @@ import * as mock from '@oxide/api-mocks' import { user1 } from '@oxide/api-mocks' import type { Json } from '../json-type' -import { json } from './util' const notFoundBody = { error_code: 'ObjectNotFound' } as const export type NotFound = typeof notFoundBody -export const notFoundErr = json({ error_code: 'ObjectNotFound' } as const, { status: 404 }) +export const notFoundErr = HttpResponse.json({ error_code: 'ObjectNotFound' } as const, { + status: 404, +}) export const lookupById = (table: T[], id: string) => { const item = table.find((i) => i.id === id) diff --git a/libs/api-mocks/msw/handlers.ts b/libs/api-mocks/msw/handlers.ts index 59c764920..b47ec0813 100644 --- a/libs/api-mocks/msw/handlers.ts +++ b/libs/api-mocks/msw/handlers.ts @@ -5,6 +5,7 @@ * * Copyright Oxide Computer Company */ +import { delay } from 'msw' import { v4 as uuid } from 'uuid' import { @@ -17,7 +18,7 @@ import { type ApiTypes as Api, type SamlIdentityProvider, } from '@oxide/api' -import { json, makeHandlers, type Json } from '@oxide/gen/msw-handlers' +import { HttpResponse, makeHandlers, type Json } from '@oxide/gen/msw-handlers' import { GiB, pick, sortBy } from '@oxide/util' import { genCumulativeI64Data } from '../metrics' @@ -63,7 +64,7 @@ export const handlers = makeHandlers({ } db.projects.push(newProject) - return json(newProject, { status: 201 }) + return HttpResponse.json(newProject, { status: 201 }) }, projectView: ({ path }) => { if (path.project.endsWith('error-503')) { @@ -126,7 +127,7 @@ export const handlers = makeHandlers({ } db.disks.push(newDisk) - return json(newDisk, { status: 201 }) + return HttpResponse.json(newDisk, { status: 201 }) }, diskView: ({ path, query }) => lookup.disk({ ...path, ...query }), diskDelete({ path, query }) { @@ -253,13 +254,13 @@ export const handlers = makeHandlers({ ...getTimestamps(), } db.images.push(newImage) - return json(newImage, { status: 201 }) + return HttpResponse.json(newImage, { status: 201 }) }, imageView: ({ path, query }) => lookup.image({ ...path, ...query }), - imageDelete({ path, query, req }) { + imageDelete({ path, query, cookies }) { // if it's a silo image, you need silo write to delete it if (!query.project) { - requireRole(req, 'silo', defaultSilo.id, 'collaborator') + requireRole(cookies, 'silo', defaultSilo.id, 'collaborator') } const image = lookup.image({ ...path, ...query }) @@ -272,7 +273,7 @@ export const handlers = makeHandlers({ delete image.project_id - return json(image, { status: 202 }) + return HttpResponse.json(image, { status: 202 }) }, imageDemote({ path, query }) { const image = lookup.image({ ...path, ...query }) @@ -280,7 +281,7 @@ export const handlers = makeHandlers({ image.project_id = project.id - return json(image, { status: 202 }) + return HttpResponse.json(image, { status: 202 }) }, instanceList({ query }) { const project = lookup.project(query) @@ -407,7 +408,7 @@ export const handlers = makeHandlers({ time_run_state_updated: new Date().toISOString(), } db.instances.push(newInstance) - return json(newInstance, { status: 201 }) + return HttpResponse.json(newInstance, { status: 201 }) }, instanceView: ({ path, query }) => lookup.instance({ ...path, ...query }), instanceDelete({ path, query }) { @@ -530,23 +531,23 @@ export const handlers = makeHandlers({ instance.run_state = 'running' }, 3000) - return json(instance, { status: 202 }) + return HttpResponse.json(instance, { status: 202 }) }, - instanceSerialConsole(_params) { - // TODO: Add support for params - return json(serial, { delay: 3000 }) + async instanceSerialConsole(_params) { + await delay(3000) + return HttpResponse.json(serial) }, instanceStart({ path, query }) { const instance = lookup.instance({ ...path, ...query }) instance.run_state = 'running' - return json(instance, { status: 202 }) + return HttpResponse.json(instance, { status: 202 }) }, instanceStop({ path, query }) { const instance = lookup.instance({ ...path, ...query }) instance.run_state = 'stopped' - return json(instance, { status: 202 }) + return HttpResponse.json(instance, { status: 202 }) }, projectPolicyView({ path }) { const project = lookup.project(path) @@ -601,7 +602,7 @@ export const handlers = makeHandlers({ } db.snapshots.push(newSnapshot) - return json(newSnapshot, { status: 201 }) + return HttpResponse.json(newSnapshot, { status: 201 }) }, snapshotView: ({ path, query }) => lookup.snapshot({ ...path, ...query }), snapshotDelete({ path, query }) { @@ -643,7 +644,7 @@ export const handlers = makeHandlers({ } db.vpcSubnets.push(newSubnet) - return json(newVpc, { status: 201 }) + return HttpResponse.json(newVpc, { status: 201 }) }, vpcView: ({ path, query }) => lookup.vpc({ ...path, ...query }), vpcUpdate({ body, path, query }) { @@ -720,7 +721,7 @@ export const handlers = makeHandlers({ ...getTimestamps(), } db.vpcRouters.push(newRouter) - return json(newRouter, { status: 201 }) + return HttpResponse.json(newRouter, { status: 201 }) }, vpcRouterView: ({ path, query }) => lookup.vpcRouter({ ...path, ...query }), vpcRouterUpdate({ body, path, query }) { @@ -760,7 +761,7 @@ export const handlers = makeHandlers({ ...body, ...getTimestamps(), } - return json(newRoute, { status: 201 }) + return HttpResponse.json(newRoute, { status: 201 }) }, vpcRouterRouteView: ({ path, query }) => lookup.vpcRouterRoute({ ...path, ...query }), vpcRouterRouteUpdate({ body, path, query }) { @@ -805,7 +806,7 @@ export const handlers = makeHandlers({ ...getTimestamps(), } db.vpcSubnets.push(newSubnet) - return json(newSubnet, { status: 201 }) + return HttpResponse.json(newSubnet, { status: 201 }) }, vpcSubnetView: ({ path, query }) => lookup.vpcSubnet({ ...path, ...query }), vpcSubnetUpdate({ body, path, query }) { @@ -831,14 +832,14 @@ export const handlers = makeHandlers({ const nics = db.networkInterfaces.filter((n) => n.subnet_id === subnet.id) return paginated(query, nics) }, - sledPhysicalDiskList({ path, query, req }) { - requireFleetViewer(req) + sledPhysicalDiskList({ path, query, cookies }) { + requireFleetViewer(cookies) const sled = lookup.sled(path) const disks = db.physicalDisks.filter((n) => n.sled_id === sled.id) return paginated(query, disks) }, - physicalDiskList({ query, req }) { - requireFleetViewer(req) + physicalDiskList({ query, cookies }) { + requireFleetViewer(cookies) return paginated(query, db.physicalDisks) }, policyView() { @@ -866,27 +867,27 @@ export const handlers = makeHandlers({ return body }, - rackList: ({ query, req }) => { - requireFleetViewer(req) + rackList: ({ query, cookies }) => { + requireFleetViewer(cookies) return paginated(query, db.racks) }, - currentUserView({ req }) { - return { ...currentUser(req), silo_name: defaultSilo.name } + currentUserView({ cookies }) { + return { ...currentUser(cookies), silo_name: defaultSilo.name } }, - currentUserGroups({ req }) { - const user = currentUser(req) + currentUserGroups({ cookies }) { + const user = currentUser(cookies) const memberships = db.groupMemberships.filter((gm) => gm.userId === user.id) const groupIds = new Set(memberships.map((gm) => gm.groupId)) const groups = db.userGroups.filter((g) => groupIds.has(g.id)) return { items: groups } }, - currentUserSshKeyList({ query, req }) { - const user = currentUser(req) + currentUserSshKeyList({ query, cookies }) { + const user = currentUser(cookies) const keys = db.sshKeys.filter((k) => k.silo_user_id === user.id) return paginated(query, keys) }, - currentUserSshKeyCreate({ body, req }) { - const user = currentUser(req) + currentUserSshKeyCreate({ body, cookies }) { + const user = currentUser(cookies) errIfExists(db.sshKeys, { silo_user_id: user.id, name: body.name }) const newSshKey: Json = { @@ -896,7 +897,7 @@ export const handlers = makeHandlers({ ...getTimestamps(), } db.sshKeys.push(newSshKey) - return json(newSshKey, { status: 201 }) + return HttpResponse.json(newSshKey, { status: 201 }) }, currentUserSshKeyView: ({ path }) => lookup.sshKey(path), currentUserSshKeyDelete({ path }) { @@ -904,16 +905,16 @@ export const handlers = makeHandlers({ db.sshKeys = db.sshKeys.filter((i) => i.id !== sshKey.id) return 204 }, - sledView({ path, req }) { - requireFleetViewer(req) + sledView({ path, cookies }) { + requireFleetViewer(cookies) return lookup.sled(path) }, - sledList({ query, req }) { - requireFleetViewer(req) + sledList({ query, cookies }) { + requireFleetViewer(cookies) return paginated(query, db.sleds) }, - sledInstanceList({ query, path, req }) { - requireFleetViewer(req) + sledInstanceList({ query, path, cookies }) { + requireFleetViewer(cookies) const sled = lookupById(db.sleds, path.sledId) return paginated( query, @@ -929,12 +930,12 @@ export const handlers = makeHandlers({ }) ) }, - siloList({ query, req }) { - requireFleetViewer(req) + siloList({ query, cookies }) { + requireFleetViewer(cookies) return paginated(query, db.silos) }, - siloCreate({ body, req }) { - requireFleetViewer(req) + siloCreate({ body, cookies }) { + requireFleetViewer(cookies) errIfExists(db.silos, { name: body.name }) const newSilo: Json = { id: uuid(), @@ -943,27 +944,27 @@ export const handlers = makeHandlers({ mapped_fleet_roles: body.mapped_fleet_roles || {}, } db.silos.push(newSilo) - return json(newSilo, { status: 201 }) + return HttpResponse.json(newSilo, { status: 201 }) }, - siloView({ path, req }) { - requireFleetViewer(req) + siloView({ path, cookies }) { + requireFleetViewer(cookies) return lookup.silo(path) }, - siloDelete({ path, req }) { - requireFleetViewer(req) + siloDelete({ path, cookies }) { + requireFleetViewer(cookies) const silo = lookup.silo(path) db.silos = db.silos.filter((i) => i.id !== silo.id) return 204 }, - siloIdentityProviderList({ query, req }) { - requireFleetViewer(req) + siloIdentityProviderList({ query, cookies }) { + requireFleetViewer(cookies) const silo = lookup.silo(query) const idps = db.identityProviders.filter(({ siloId }) => siloId === silo.id).map(toIdp) return { items: idps } }, - samlIdentityProviderCreate({ query, body, req }) { - requireFleetViewer(req) + samlIdentityProviderCreate({ query, body, cookies }) { + requireFleetViewer(cookies) const silo = lookup.silo(query) // this is a bit silly, but errIfExists doesn't handle nested keys like @@ -1019,8 +1020,8 @@ export const handlers = makeHandlers({ return paginated(query, db.users) }, - systemPolicyView({ req }) { - requireFleetViewer(req) + systemPolicyView({ cookies }) { + requireFleetViewer(cookies) const role_assignments = db.roleAssignments .filter((r) => r.resource_type === 'fleet' && r.resource_id === FLEET_ID) @@ -1029,7 +1030,7 @@ export const handlers = makeHandlers({ return { role_assignments } }, systemMetric(params) { - requireFleetViewer(params.req) + requireFleetViewer(params.cookies) return handleMetrics(params) }, siloMetric: handleMetrics, diff --git a/libs/api-mocks/msw/util.ts b/libs/api-mocks/msw/util.ts index 16d628a7c..44ff2d2cd 100644 --- a/libs/api-mocks/msw/util.ts +++ b/libs/api-mocks/msw/util.ts @@ -6,7 +6,6 @@ * Copyright Oxide Computer Company */ import { differenceInSeconds, subHours } from 'date-fns' -import type { RestRequest } from 'msw' import { FLEET_ID, @@ -20,15 +19,13 @@ import { type SystemMetricQueryParams, type User, } from '@oxide/api' -import { json, type Json } from '@oxide/gen/msw-handlers' +import { HttpResponse, type Json } from '@oxide/gen/msw-handlers' import { GiB, isTruthy, TiB } from '@oxide/util' import type { DbRoleAssignmentResourceType } from '..' import { genI64Data } from '../metrics' import { db } from './db' -export { json } from '@oxide/gen/msw-handlers' - interface PaginateOptions { limit?: number pageToken?: string @@ -88,10 +85,13 @@ export function getTimestamps() { return { time_created: now, time_modified: now } } -export const unavailableErr = json({ error_code: 'ServiceUnavailable' }, { status: 503 }) +export const unavailableErr = HttpResponse.json( + { error_code: 'ServiceUnavailable' }, + { status: 503 } +) export const NotImplemented = () => { - throw json({ error_code: 'NotImplemented' }, { status: 501 }) + throw HttpResponse.json({ error_code: 'NotImplemented' }, { status: 501 }) } export const errIfExists = >( @@ -105,7 +105,7 @@ export const errIfExists = >( ) ) { const name = 'name' in match ? match.name : 'id' in match ? match.id : '' - throw json( + throw HttpResponse.json( { error_code: 'ObjectAlreadyExists', message: `already exists: ${resourceLabel} "${name}"`, @@ -292,8 +292,8 @@ export const MSW_USER_COOKIE = 'msw-user' * If cookie is empty or name is not found, return the first user in the list, * who has admin on everything. */ -export function currentUser(req: RestRequest): Json { - const name = req.cookies[MSW_USER_COOKIE] +export function currentUser(cookies: Record): Json { + const name = cookies[MSW_USER_COOKIE] return db.users.find((u) => u.display_name === name) ?? db.users[0] } @@ -347,8 +347,8 @@ export function userHasRole( * fleet roles for the user as well as for the user's groups. Do nothing if yes, * throw 403 if no. */ -export function requireFleetViewer(req: RestRequest) { - requireRole(req, 'fleet', FLEET_ID, 'viewer') +export function requireFleetViewer(cookies: Record) { + requireRole(cookies, 'fleet', FLEET_ID, 'viewer') } /** @@ -357,12 +357,12 @@ export function requireFleetViewer(req: RestRequest) { * if no. */ export function requireRole( - req: RestRequest, + cookies: Record, resourceType: DbRoleAssignmentResourceType, resourceId: string, role: RoleKey ) { - const user = currentUser(req) + const user = currentUser(cookies) // should it 404? I think the API is a mix if (!userHasRole(user, resourceType, resourceId, role)) throw 403 } diff --git a/libs/api/__generated__/Api.ts b/libs/api/__generated__/Api.ts index 9e42d4fb9..cda8c7178 100644 --- a/libs/api/__generated__/Api.ts +++ b/libs/api/__generated__/Api.ts @@ -7,6 +7,7 @@ * * Copyright Oxide Computer Company */ + import { HttpClient, toQueryString, type FetchParams } from './http-client' export type { ApiConfig, ApiResult, ErrorBody, ErrorResult } from './http-client' diff --git a/libs/api/__generated__/http-client.ts b/libs/api/__generated__/http-client.ts index ecc1002b4..1f4c338a3 100644 --- a/libs/api/__generated__/http-client.ts +++ b/libs/api/__generated__/http-client.ts @@ -5,6 +5,7 @@ * * Copyright Oxide Computer Company */ + import { camelToSnake, isNotNull, processResponseBody, snakeify } from './util' /** Success responses from the API */ diff --git a/libs/api/__generated__/msw-handlers.ts b/libs/api/__generated__/msw-handlers.ts index 9e2260ef2..0f017b05f 100644 --- a/libs/api/__generated__/msw-handlers.ts +++ b/libs/api/__generated__/msw-handlers.ts @@ -7,40 +7,26 @@ * * Copyright Oxide Computer Company */ + import { - compose, - context, - ResponseComposition, - ResponseTransformer, - rest, - RestContext, - RestHandler, - RestRequest, + DefaultBodyType, + delay as doDelay, + http, + HttpHandler, + HttpResponse, + PathParams, + StrictResponse, } from 'msw' -import type { SnakeCasedPropertiesDeep as Snakify } from 'type-fest' +import type { Promisable, SnakeCasedPropertiesDeep as Snakify } from 'type-fest' import { z, ZodSchema } from 'zod' import type * as Api from './Api' import { snakeify } from './util' import * as schema from './validate' -type HandlerResult = Json | ResponseTransformer> +type HandlerResult = Json | StrictResponse> type StatusCode = number -/** - * Custom transformer: convenience function for setting response `status` and/or - * `delay`. - * - * @see https://mswjs.io/docs/basics/response-transformer#custom-transformer - */ -export function json( - body: B, - options: { status?: number; delay?: number } = {} -): ResponseTransformer { - const { status = 200, delay = 0 } = options - return compose(context.status(status), context.json(body), context.delay(delay)) -} - // these are used for turning our nice JS-ified API types back into the original // API JSON types (snake cased and dates as strings) for use in our mock API @@ -58,851 +44,1036 @@ type StringifyDates = T extends Date */ export type Json = Snakify> +// Shortcut to reduce number of imports required in consumers +export { HttpResponse } + export interface MSWHandlers { /** `POST /device/auth` */ - deviceAuthRequest: (params: { req: RestRequest }) => StatusCode + deviceAuthRequest: (params: { + req: Request + cookies: Record + }) => Promisable /** `POST /device/confirm` */ deviceAuthConfirm: (params: { body: Json - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `POST /device/token` */ - deviceAccessToken: (params: { req: RestRequest }) => StatusCode + deviceAccessToken: (params: { + req: Request + cookies: Record + }) => Promisable /** `POST /login/:siloName/saml/:providerName` */ - loginSaml: (params: { path: Api.LoginSamlPathParams; req: RestRequest }) => StatusCode + loginSaml: (params: { + path: Api.LoginSamlPathParams + req: Request + cookies: Record + }) => Promisable /** `GET /v1/certificates` */ certificateList: (params: { query: Api.CertificateListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/certificates` */ certificateCreate: (params: { body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/certificates/:certificate` */ certificateView: (params: { path: Api.CertificateViewPathParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/certificates/:certificate` */ certificateDelete: (params: { path: Api.CertificateDeletePathParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/disks` */ diskList: (params: { query: Api.DiskListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/disks` */ diskCreate: (params: { query: Api.DiskCreateQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/disks/:disk` */ diskView: (params: { path: Api.DiskViewPathParams query: Api.DiskViewQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/disks/:disk` */ diskDelete: (params: { path: Api.DiskDeletePathParams query: Api.DiskDeleteQueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `POST /v1/disks/:disk/bulk-write` */ diskBulkWriteImport: (params: { path: Api.DiskBulkWriteImportPathParams query: Api.DiskBulkWriteImportQueryParams body: Json - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `POST /v1/disks/:disk/bulk-write-start` */ diskBulkWriteImportStart: (params: { path: Api.DiskBulkWriteImportStartPathParams query: Api.DiskBulkWriteImportStartQueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `POST /v1/disks/:disk/bulk-write-stop` */ diskBulkWriteImportStop: (params: { path: Api.DiskBulkWriteImportStopPathParams query: Api.DiskBulkWriteImportStopQueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `POST /v1/disks/:disk/finalize` */ diskFinalizeImport: (params: { path: Api.DiskFinalizeImportPathParams query: Api.DiskFinalizeImportQueryParams body: Json - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `POST /v1/disks/:disk/import` */ diskImportBlocksFromUrl: (params: { path: Api.DiskImportBlocksFromUrlPathParams query: Api.DiskImportBlocksFromUrlQueryParams body: Json - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/disks/:disk/metrics/:metric` */ diskMetricsList: (params: { path: Api.DiskMetricsListPathParams query: Api.DiskMetricsListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/groups` */ groupList: (params: { query: Api.GroupListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/groups/:groupId` */ groupView: (params: { path: Api.GroupViewPathParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/images` */ imageList: (params: { query: Api.ImageListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/images` */ imageCreate: (params: { query: Api.ImageCreateQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/images/:image` */ imageView: (params: { path: Api.ImageViewPathParams query: Api.ImageViewQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/images/:image` */ imageDelete: (params: { path: Api.ImageDeletePathParams query: Api.ImageDeleteQueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `POST /v1/images/:image/demote` */ imageDemote: (params: { path: Api.ImageDemotePathParams query: Api.ImageDemoteQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/images/:image/promote` */ imagePromote: (params: { path: Api.ImagePromotePathParams query: Api.ImagePromoteQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/instances` */ instanceList: (params: { query: Api.InstanceListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/instances` */ instanceCreate: (params: { query: Api.InstanceCreateQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/instances/:instance` */ instanceView: (params: { path: Api.InstanceViewPathParams query: Api.InstanceViewQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/instances/:instance` */ instanceDelete: (params: { path: Api.InstanceDeletePathParams query: Api.InstanceDeleteQueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/instances/:instance/disks` */ instanceDiskList: (params: { path: Api.InstanceDiskListPathParams query: Api.InstanceDiskListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/instances/:instance/disks/attach` */ instanceDiskAttach: (params: { path: Api.InstanceDiskAttachPathParams query: Api.InstanceDiskAttachQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/instances/:instance/disks/detach` */ instanceDiskDetach: (params: { path: Api.InstanceDiskDetachPathParams query: Api.InstanceDiskDetachQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/instances/:instance/external-ips` */ instanceExternalIpList: (params: { path: Api.InstanceExternalIpListPathParams query: Api.InstanceExternalIpListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/instances/:instance/migrate` */ instanceMigrate: (params: { path: Api.InstanceMigratePathParams query: Api.InstanceMigrateQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/instances/:instance/reboot` */ instanceReboot: (params: { path: Api.InstanceRebootPathParams query: Api.InstanceRebootQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/instances/:instance/serial-console` */ instanceSerialConsole: (params: { path: Api.InstanceSerialConsolePathParams query: Api.InstanceSerialConsoleQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/instances/:instance/serial-console/stream` */ instanceSerialConsoleStream: (params: { path: Api.InstanceSerialConsoleStreamPathParams query: Api.InstanceSerialConsoleStreamQueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `POST /v1/instances/:instance/start` */ instanceStart: (params: { path: Api.InstanceStartPathParams query: Api.InstanceStartQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/instances/:instance/stop` */ instanceStop: (params: { path: Api.InstanceStopPathParams query: Api.InstanceStopQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/ip-pools` */ projectIpPoolList: (params: { query: Api.ProjectIpPoolListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/ip-pools/:pool` */ projectIpPoolView: (params: { path: Api.ProjectIpPoolViewPathParams query: Api.ProjectIpPoolViewQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/login/:siloName/local` */ loginLocal: (params: { path: Api.LoginLocalPathParams body: Json - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `POST /v1/logout` */ - logout: (params: { req: RestRequest }) => StatusCode + logout: (params: { + req: Request + cookies: Record + }) => Promisable /** `GET /v1/me` */ - currentUserView: (params: { req: RestRequest }) => HandlerResult + currentUserView: (params: { + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/me/groups` */ currentUserGroups: (params: { query: Api.CurrentUserGroupsQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/me/ssh-keys` */ currentUserSshKeyList: (params: { query: Api.CurrentUserSshKeyListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/me/ssh-keys` */ currentUserSshKeyCreate: (params: { body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/me/ssh-keys/:sshKey` */ currentUserSshKeyView: (params: { path: Api.CurrentUserSshKeyViewPathParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/me/ssh-keys/:sshKey` */ currentUserSshKeyDelete: (params: { path: Api.CurrentUserSshKeyDeletePathParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/metrics/:metricName` */ siloMetric: (params: { path: Api.SiloMetricPathParams query: Api.SiloMetricQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/network-interfaces` */ instanceNetworkInterfaceList: (params: { query: Api.InstanceNetworkInterfaceListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/network-interfaces` */ instanceNetworkInterfaceCreate: (params: { query: Api.InstanceNetworkInterfaceCreateQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/network-interfaces/:interface` */ instanceNetworkInterfaceView: (params: { path: Api.InstanceNetworkInterfaceViewPathParams query: Api.InstanceNetworkInterfaceViewQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `PUT /v1/network-interfaces/:interface` */ instanceNetworkInterfaceUpdate: (params: { path: Api.InstanceNetworkInterfaceUpdatePathParams query: Api.InstanceNetworkInterfaceUpdateQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/network-interfaces/:interface` */ instanceNetworkInterfaceDelete: (params: { path: Api.InstanceNetworkInterfaceDeletePathParams query: Api.InstanceNetworkInterfaceDeleteQueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/ping` */ - ping: (params: { req: RestRequest }) => HandlerResult + ping: (params: { + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/policy` */ - policyView: (params: { req: RestRequest }) => HandlerResult + policyView: (params: { + req: Request + cookies: Record + }) => Promisable> /** `PUT /v1/policy` */ policyUpdate: (params: { body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/projects` */ projectList: (params: { query: Api.ProjectListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/projects` */ projectCreate: (params: { body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/projects/:project` */ projectView: (params: { path: Api.ProjectViewPathParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `PUT /v1/projects/:project` */ projectUpdate: (params: { path: Api.ProjectUpdatePathParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/projects/:project` */ projectDelete: (params: { path: Api.ProjectDeletePathParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/projects/:project/policy` */ projectPolicyView: (params: { path: Api.ProjectPolicyViewPathParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `PUT /v1/projects/:project/policy` */ projectPolicyUpdate: (params: { path: Api.ProjectPolicyUpdatePathParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/snapshots` */ snapshotList: (params: { query: Api.SnapshotListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/snapshots` */ snapshotCreate: (params: { query: Api.SnapshotCreateQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/snapshots/:snapshot` */ snapshotView: (params: { path: Api.SnapshotViewPathParams query: Api.SnapshotViewQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/snapshots/:snapshot` */ snapshotDelete: (params: { path: Api.SnapshotDeletePathParams query: Api.SnapshotDeleteQueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/system/hardware/disks` */ physicalDiskList: (params: { query: Api.PhysicalDiskListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/hardware/racks` */ rackList: (params: { query: Api.RackListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/hardware/racks/:rackId` */ rackView: (params: { path: Api.RackViewPathParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/hardware/sleds` */ sledList: (params: { query: Api.SledListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/hardware/sleds/:sledId` */ sledView: (params: { path: Api.SledViewPathParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/hardware/sleds/:sledId/disks` */ sledPhysicalDiskList: (params: { path: Api.SledPhysicalDiskListPathParams query: Api.SledPhysicalDiskListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/hardware/sleds/:sledId/instances` */ sledInstanceList: (params: { path: Api.SledInstanceListPathParams query: Api.SledInstanceListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/hardware/switch-port` */ networkingSwitchPortList: (params: { query: Api.NetworkingSwitchPortListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/system/hardware/switch-port/:port/settings` */ networkingSwitchPortApplySettings: (params: { path: Api.NetworkingSwitchPortApplySettingsPathParams query: Api.NetworkingSwitchPortApplySettingsQueryParams body: Json - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `DELETE /v1/system/hardware/switch-port/:port/settings` */ networkingSwitchPortClearSettings: (params: { path: Api.NetworkingSwitchPortClearSettingsPathParams query: Api.NetworkingSwitchPortClearSettingsQueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/system/hardware/switches` */ switchList: (params: { query: Api.SwitchListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/hardware/switches/:switchId` */ switchView: (params: { path: Api.SwitchViewPathParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/identity-providers` */ siloIdentityProviderList: (params: { query: Api.SiloIdentityProviderListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/system/identity-providers/local/users` */ localIdpUserCreate: (params: { query: Api.LocalIdpUserCreateQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/system/identity-providers/local/users/:userId` */ localIdpUserDelete: (params: { path: Api.LocalIdpUserDeletePathParams query: Api.LocalIdpUserDeleteQueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `POST /v1/system/identity-providers/local/users/:userId/set-password` */ localIdpUserSetPassword: (params: { path: Api.LocalIdpUserSetPasswordPathParams query: Api.LocalIdpUserSetPasswordQueryParams body: Json - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `POST /v1/system/identity-providers/saml` */ samlIdentityProviderCreate: (params: { query: Api.SamlIdentityProviderCreateQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/identity-providers/saml/:provider` */ samlIdentityProviderView: (params: { path: Api.SamlIdentityProviderViewPathParams query: Api.SamlIdentityProviderViewQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/ip-pools` */ ipPoolList: (params: { query: Api.IpPoolListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/system/ip-pools` */ ipPoolCreate: (params: { body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/ip-pools/:pool` */ ipPoolView: (params: { path: Api.IpPoolViewPathParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `PUT /v1/system/ip-pools/:pool` */ ipPoolUpdate: (params: { path: Api.IpPoolUpdatePathParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/system/ip-pools/:pool` */ ipPoolDelete: (params: { path: Api.IpPoolDeletePathParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/system/ip-pools/:pool/ranges` */ ipPoolRangeList: (params: { path: Api.IpPoolRangeListPathParams query: Api.IpPoolRangeListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/system/ip-pools/:pool/ranges/add` */ ipPoolRangeAdd: (params: { path: Api.IpPoolRangeAddPathParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/system/ip-pools/:pool/ranges/remove` */ ipPoolRangeRemove: (params: { path: Api.IpPoolRangeRemovePathParams body: Json - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/system/ip-pools-service` */ - ipPoolServiceView: (params: { req: RestRequest }) => HandlerResult + ipPoolServiceView: (params: { + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/ip-pools-service/ranges` */ ipPoolServiceRangeList: (params: { query: Api.IpPoolServiceRangeListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/system/ip-pools-service/ranges/add` */ ipPoolServiceRangeAdd: (params: { body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/system/ip-pools-service/ranges/remove` */ ipPoolServiceRangeRemove: (params: { body: Json - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/system/metrics/:metricName` */ systemMetric: (params: { path: Api.SystemMetricPathParams query: Api.SystemMetricQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/networking/address-lot` */ networkingAddressLotList: (params: { query: Api.NetworkingAddressLotListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/system/networking/address-lot` */ networkingAddressLotCreate: (params: { body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/system/networking/address-lot/:addressLot` */ networkingAddressLotDelete: (params: { path: Api.NetworkingAddressLotDeletePathParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/system/networking/address-lot/:addressLot/blocks` */ networkingAddressLotBlockList: (params: { path: Api.NetworkingAddressLotBlockListPathParams query: Api.NetworkingAddressLotBlockListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/networking/bgp` */ networkingBgpConfigList: (params: { query: Api.NetworkingBgpConfigListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/system/networking/bgp` */ networkingBgpConfigCreate: (params: { body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/system/networking/bgp` */ networkingBgpConfigDelete: (params: { query: Api.NetworkingBgpConfigDeleteQueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/system/networking/bgp-announce` */ networkingBgpAnnounceSetList: (params: { query: Api.NetworkingBgpAnnounceSetListQueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `POST /v1/system/networking/bgp-announce` */ networkingBgpAnnounceSetCreate: (params: { body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/system/networking/bgp-announce` */ networkingBgpAnnounceSetDelete: (params: { query: Api.NetworkingBgpAnnounceSetDeleteQueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/system/networking/bgp-routes-ipv4` */ networkingBgpImportedRoutesIpv4: (params: { query: Api.NetworkingBgpImportedRoutesIpv4QueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/system/networking/bgp-status` */ - networkingBgpStatus: (params: { req: RestRequest }) => StatusCode + networkingBgpStatus: (params: { + req: Request + cookies: Record + }) => Promisable /** `GET /v1/system/networking/loopback-address` */ networkingLoopbackAddressList: (params: { query: Api.NetworkingLoopbackAddressListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/system/networking/loopback-address` */ networkingLoopbackAddressCreate: (params: { body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/system/networking/loopback-address/:rackId/:switchLocation/:address/:subnetMask` */ networkingLoopbackAddressDelete: (params: { path: Api.NetworkingLoopbackAddressDeletePathParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/system/networking/switch-port-settings` */ networkingSwitchPortSettingsList: (params: { query: Api.NetworkingSwitchPortSettingsListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/system/networking/switch-port-settings` */ networkingSwitchPortSettingsCreate: (params: { body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/system/networking/switch-port-settings` */ networkingSwitchPortSettingsDelete: (params: { query: Api.NetworkingSwitchPortSettingsDeleteQueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/system/networking/switch-port-settings/:port` */ networkingSwitchPortSettingsView: (params: { path: Api.NetworkingSwitchPortSettingsViewPathParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/policy` */ - systemPolicyView: (params: { req: RestRequest }) => HandlerResult + systemPolicyView: (params: { + req: Request + cookies: Record + }) => Promisable> /** `PUT /v1/system/policy` */ systemPolicyUpdate: (params: { body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/roles` */ roleList: (params: { query: Api.RoleListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/roles/:roleName` */ roleView: (params: { path: Api.RoleViewPathParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/silos` */ siloList: (params: { query: Api.SiloListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/system/silos` */ siloCreate: (params: { body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/silos/:silo` */ siloView: (params: { path: Api.SiloViewPathParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/system/silos/:silo` */ - siloDelete: (params: { path: Api.SiloDeletePathParams; req: RestRequest }) => StatusCode + siloDelete: (params: { + path: Api.SiloDeletePathParams + req: Request + cookies: Record + }) => Promisable /** `GET /v1/system/silos/:silo/policy` */ siloPolicyView: (params: { path: Api.SiloPolicyViewPathParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `PUT /v1/system/silos/:silo/policy` */ siloPolicyUpdate: (params: { path: Api.SiloPolicyUpdatePathParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/users` */ siloUserList: (params: { query: Api.SiloUserListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/users/:userId` */ siloUserView: (params: { path: Api.SiloUserViewPathParams query: Api.SiloUserViewQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/users-builtin` */ userBuiltinList: (params: { query: Api.UserBuiltinListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/system/users-builtin/:user` */ userBuiltinView: (params: { path: Api.UserBuiltinViewPathParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/users` */ userList: (params: { query: Api.UserListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/vpc-firewall-rules` */ vpcFirewallRulesView: (params: { query: Api.VpcFirewallRulesViewQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `PUT /v1/vpc-firewall-rules` */ vpcFirewallRulesUpdate: (params: { query: Api.VpcFirewallRulesUpdateQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/vpc-router-routes` */ vpcRouterRouteList: (params: { query: Api.VpcRouterRouteListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/vpc-router-routes` */ vpcRouterRouteCreate: (params: { query: Api.VpcRouterRouteCreateQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/vpc-router-routes/:route` */ vpcRouterRouteView: (params: { path: Api.VpcRouterRouteViewPathParams query: Api.VpcRouterRouteViewQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `PUT /v1/vpc-router-routes/:route` */ vpcRouterRouteUpdate: (params: { path: Api.VpcRouterRouteUpdatePathParams query: Api.VpcRouterRouteUpdateQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/vpc-router-routes/:route` */ vpcRouterRouteDelete: (params: { path: Api.VpcRouterRouteDeletePathParams query: Api.VpcRouterRouteDeleteQueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/vpc-routers` */ vpcRouterList: (params: { query: Api.VpcRouterListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/vpc-routers` */ vpcRouterCreate: (params: { query: Api.VpcRouterCreateQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/vpc-routers/:router` */ vpcRouterView: (params: { path: Api.VpcRouterViewPathParams query: Api.VpcRouterViewQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `PUT /v1/vpc-routers/:router` */ vpcRouterUpdate: (params: { path: Api.VpcRouterUpdatePathParams query: Api.VpcRouterUpdateQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/vpc-routers/:router` */ vpcRouterDelete: (params: { path: Api.VpcRouterDeletePathParams query: Api.VpcRouterDeleteQueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/vpc-subnets` */ vpcSubnetList: (params: { query: Api.VpcSubnetListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/vpc-subnets` */ vpcSubnetCreate: (params: { query: Api.VpcSubnetCreateQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/vpc-subnets/:subnet` */ vpcSubnetView: (params: { path: Api.VpcSubnetViewPathParams query: Api.VpcSubnetViewQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `PUT /v1/vpc-subnets/:subnet` */ vpcSubnetUpdate: (params: { path: Api.VpcSubnetUpdatePathParams query: Api.VpcSubnetUpdateQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/vpc-subnets/:subnet` */ vpcSubnetDelete: (params: { path: Api.VpcSubnetDeletePathParams query: Api.VpcSubnetDeleteQueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable /** `GET /v1/vpc-subnets/:subnet/network-interfaces` */ vpcSubnetListNetworkInterfaces: (params: { path: Api.VpcSubnetListNetworkInterfacesPathParams query: Api.VpcSubnetListNetworkInterfacesQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/vpcs` */ vpcList: (params: { query: Api.VpcListQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `POST /v1/vpcs` */ vpcCreate: (params: { query: Api.VpcCreateQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `GET /v1/vpcs/:vpc` */ vpcView: (params: { path: Api.VpcViewPathParams query: Api.VpcViewQueryParams - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `PUT /v1/vpcs/:vpc` */ vpcUpdate: (params: { path: Api.VpcUpdatePathParams query: Api.VpcUpdateQueryParams body: Json - req: RestRequest - }) => HandlerResult + req: Request + cookies: Record + }) => Promisable> /** `DELETE /v1/vpcs/:vpc` */ vpcDelete: (params: { path: Api.VpcDeletePathParams query: Api.VpcDeleteQueryParams - req: RestRequest - }) => StatusCode + req: Request + cookies: Record + }) => Promisable } function validateBody(schema: S, body: unknown) { @@ -910,10 +1081,14 @@ function validateBody(schema: S, body: unknown) { if (result.success) { return { body: result.data as Json> } } - return { bodyErr: json(result.error.issues, { status: 400 }) } + return { bodyErr: HttpResponse.json(result.error.issues, { status: 400 }) } } -function validateParams(schema: S, req: RestRequest) { - const rawParams = new URLSearchParams(req.url.search) +function validateParams( + schema: S, + req: Request, + pathParams: PathParams +) { + const rawParams = new URLSearchParams(new URL(req.url).search) const params: [string, unknown][] = [] // Ensure numeric params like `limit` are parsed as numbers @@ -922,7 +1097,7 @@ function validateParams(schema: S, req: RestRequest) { } const result = schema.safeParse({ - path: req.params, + path: pathParams, query: Object.fromEntries(params), }) @@ -934,7 +1109,7 @@ function validateParams(schema: S, req: RestRequest) { // exist if there's no valid name const { issues } = result.error const status = issues.some((e) => e.path[0] === 'path') ? 404 : 400 - return { paramsErr: json(issues, { status }) } + return { paramsErr: HttpResponse.json(issues, { status }) } } const handler = @@ -943,18 +1118,24 @@ const handler = paramSchema: ZodSchema | null, bodySchema: ZodSchema | null ) => - async (req: RestRequest, res: ResponseComposition, ctx: RestContext) => { + async ({ + request: req, + params: pathParams, + }: { + request: Request + params: PathParams + }) => { const { params, paramsErr } = paramSchema - ? validateParams(paramSchema, req) + ? validateParams(paramSchema, req, pathParams) : { params: {}, paramsErr: undefined } - if (paramsErr) return res(paramsErr) + if (paramsErr) return HttpResponse.json(paramsErr, { status: 400 }) const { path, query } = params const { body, bodyErr } = bodySchema ? validateBody(bodySchema, await req.json()) : { body: undefined, bodyErr: undefined } - if (bodyErr) return res(bodyErr) + if (bodyErr) return HttpResponse.json(bodyErr, { status: 400 }) try { // TypeScript can't narrow the handler down because there's not an explicit relationship between the schema @@ -962,66 +1143,66 @@ const handler = // relevant schema is required if and only if the handler has a type that matches the inferred schema const result = await (handler as any).apply(null, [{ path, query, body, req }]) if (typeof result === 'number') { - return res(ctx.status(result)) + return new HttpResponse(null, { status: result }) } if (typeof result === 'function') { - return res(result as ResponseTransformer) + return result() } - return res(json(result)) + return HttpResponse.json(result) } catch (thrown) { if (typeof thrown === 'number') { - return res(ctx.status(thrown)) + return new HttpResponse(null, { status: thrown }) } if (typeof thrown === 'function') { - return res(thrown as ResponseTransformer) + return thrown() } if (typeof thrown === 'string') { - return res(json({ message: thrown }, { status: 400 })) + return HttpResponse.json({ message: thrown }, { status: 400 }) } console.error('Unexpected mock error', thrown) - return res(json({ message: 'Unknown Server Error' }, { status: 500 })) + return HttpResponse.json({ message: 'Unknown Server Error' }, { status: 500 }) } } -export function makeHandlers(handlers: MSWHandlers): RestHandler[] { +export function makeHandlers(handlers: MSWHandlers): HttpHandler[] { return [ - rest.post('/device/auth', handler(handlers['deviceAuthRequest'], null, null)), - rest.post( + http.post('/device/auth', handler(handlers['deviceAuthRequest'], null, null)), + http.post( '/device/confirm', handler(handlers['deviceAuthConfirm'], null, schema.DeviceAuthVerify) ), - rest.post('/device/token', handler(handlers['deviceAccessToken'], null, null)), - rest.post( + http.post('/device/token', handler(handlers['deviceAccessToken'], null, null)), + http.post( '/login/:siloName/saml/:providerName', handler(handlers['loginSaml'], schema.LoginSamlParams, null) ), - rest.get( + http.get( '/v1/certificates', handler(handlers['certificateList'], schema.CertificateListParams, null) ), - rest.post( + http.post( '/v1/certificates', handler(handlers['certificateCreate'], null, schema.CertificateCreate) ), - rest.get( + http.get( '/v1/certificates/:certificate', handler(handlers['certificateView'], schema.CertificateViewParams, null) ), - rest.delete( + http.delete( '/v1/certificates/:certificate', handler(handlers['certificateDelete'], schema.CertificateDeleteParams, null) ), - rest.get('/v1/disks', handler(handlers['diskList'], schema.DiskListParams, null)), - rest.post( + http.get('/v1/disks', handler(handlers['diskList'], schema.DiskListParams, null)), + http.post( '/v1/disks', handler(handlers['diskCreate'], schema.DiskCreateParams, schema.DiskCreate) ), - rest.get('/v1/disks/:disk', handler(handlers['diskView'], schema.DiskViewParams, null)), - rest.delete( + http.get('/v1/disks/:disk', handler(handlers['diskView'], schema.DiskViewParams, null)), + http.delete( '/v1/disks/:disk', handler(handlers['diskDelete'], schema.DiskDeleteParams, null) ), - rest.post( + http.post( '/v1/disks/:disk/bulk-write', handler( handlers['diskBulkWriteImport'], @@ -1029,7 +1210,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.ImportBlocksBulkWrite ) ), - rest.post( + http.post( '/v1/disks/:disk/bulk-write-start', handler( handlers['diskBulkWriteImportStart'], @@ -1037,7 +1218,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.post( + http.post( '/v1/disks/:disk/bulk-write-stop', handler( handlers['diskBulkWriteImportStop'], @@ -1045,7 +1226,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.post( + http.post( '/v1/disks/:disk/finalize', handler( handlers['diskFinalizeImport'], @@ -1053,7 +1234,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.FinalizeDisk ) ), - rest.post( + http.post( '/v1/disks/:disk/import', handler( handlers['diskImportBlocksFromUrl'], @@ -1061,41 +1242,41 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.ImportBlocksFromUrl ) ), - rest.get( + http.get( '/v1/disks/:disk/metrics/:metric', handler(handlers['diskMetricsList'], schema.DiskMetricsListParams, null) ), - rest.get('/v1/groups', handler(handlers['groupList'], schema.GroupListParams, null)), - rest.get( + http.get('/v1/groups', handler(handlers['groupList'], schema.GroupListParams, null)), + http.get( '/v1/groups/:groupId', handler(handlers['groupView'], schema.GroupViewParams, null) ), - rest.get('/v1/images', handler(handlers['imageList'], schema.ImageListParams, null)), - rest.post( + http.get('/v1/images', handler(handlers['imageList'], schema.ImageListParams, null)), + http.post( '/v1/images', handler(handlers['imageCreate'], schema.ImageCreateParams, schema.ImageCreate) ), - rest.get( + http.get( '/v1/images/:image', handler(handlers['imageView'], schema.ImageViewParams, null) ), - rest.delete( + http.delete( '/v1/images/:image', handler(handlers['imageDelete'], schema.ImageDeleteParams, null) ), - rest.post( + http.post( '/v1/images/:image/demote', handler(handlers['imageDemote'], schema.ImageDemoteParams, null) ), - rest.post( + http.post( '/v1/images/:image/promote', handler(handlers['imagePromote'], schema.ImagePromoteParams, null) ), - rest.get( + http.get( '/v1/instances', handler(handlers['instanceList'], schema.InstanceListParams, null) ), - rest.post( + http.post( '/v1/instances', handler( handlers['instanceCreate'], @@ -1103,19 +1284,19 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.InstanceCreate ) ), - rest.get( + http.get( '/v1/instances/:instance', handler(handlers['instanceView'], schema.InstanceViewParams, null) ), - rest.delete( + http.delete( '/v1/instances/:instance', handler(handlers['instanceDelete'], schema.InstanceDeleteParams, null) ), - rest.get( + http.get( '/v1/instances/:instance/disks', handler(handlers['instanceDiskList'], schema.InstanceDiskListParams, null) ), - rest.post( + http.post( '/v1/instances/:instance/disks/attach', handler( handlers['instanceDiskAttach'], @@ -1123,7 +1304,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.DiskPath ) ), - rest.post( + http.post( '/v1/instances/:instance/disks/detach', handler( handlers['instanceDiskDetach'], @@ -1131,11 +1312,11 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.DiskPath ) ), - rest.get( + http.get( '/v1/instances/:instance/external-ips', handler(handlers['instanceExternalIpList'], schema.InstanceExternalIpListParams, null) ), - rest.post( + http.post( '/v1/instances/:instance/migrate', handler( handlers['instanceMigrate'], @@ -1143,15 +1324,15 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.InstanceMigrate ) ), - rest.post( + http.post( '/v1/instances/:instance/reboot', handler(handlers['instanceReboot'], schema.InstanceRebootParams, null) ), - rest.get( + http.get( '/v1/instances/:instance/serial-console', handler(handlers['instanceSerialConsole'], schema.InstanceSerialConsoleParams, null) ), - rest.get( + http.get( '/v1/instances/:instance/serial-console/stream', handler( handlers['instanceSerialConsoleStream'], @@ -1159,23 +1340,23 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.post( + http.post( '/v1/instances/:instance/start', handler(handlers['instanceStart'], schema.InstanceStartParams, null) ), - rest.post( + http.post( '/v1/instances/:instance/stop', handler(handlers['instanceStop'], schema.InstanceStopParams, null) ), - rest.get( + http.get( '/v1/ip-pools', handler(handlers['projectIpPoolList'], schema.ProjectIpPoolListParams, null) ), - rest.get( + http.get( '/v1/ip-pools/:pool', handler(handlers['projectIpPoolView'], schema.ProjectIpPoolViewParams, null) ), - rest.post( + http.post( '/v1/login/:siloName/local', handler( handlers['loginLocal'], @@ -1183,25 +1364,25 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.UsernamePasswordCredentials ) ), - rest.post('/v1/logout', handler(handlers['logout'], null, null)), - rest.get('/v1/me', handler(handlers['currentUserView'], null, null)), - rest.get( + http.post('/v1/logout', handler(handlers['logout'], null, null)), + http.get('/v1/me', handler(handlers['currentUserView'], null, null)), + http.get( '/v1/me/groups', handler(handlers['currentUserGroups'], schema.CurrentUserGroupsParams, null) ), - rest.get( + http.get( '/v1/me/ssh-keys', handler(handlers['currentUserSshKeyList'], schema.CurrentUserSshKeyListParams, null) ), - rest.post( + http.post( '/v1/me/ssh-keys', handler(handlers['currentUserSshKeyCreate'], null, schema.SshKeyCreate) ), - rest.get( + http.get( '/v1/me/ssh-keys/:sshKey', handler(handlers['currentUserSshKeyView'], schema.CurrentUserSshKeyViewParams, null) ), - rest.delete( + http.delete( '/v1/me/ssh-keys/:sshKey', handler( handlers['currentUserSshKeyDelete'], @@ -1209,11 +1390,11 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.get( + http.get( '/v1/metrics/:metricName', handler(handlers['siloMetric'], schema.SiloMetricParams, null) ), - rest.get( + http.get( '/v1/network-interfaces', handler( handlers['instanceNetworkInterfaceList'], @@ -1221,7 +1402,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.post( + http.post( '/v1/network-interfaces', handler( handlers['instanceNetworkInterfaceCreate'], @@ -1229,7 +1410,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.InstanceNetworkInterfaceCreate ) ), - rest.get( + http.get( '/v1/network-interfaces/:interface', handler( handlers['instanceNetworkInterfaceView'], @@ -1237,7 +1418,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.put( + http.put( '/v1/network-interfaces/:interface', handler( handlers['instanceNetworkInterfaceUpdate'], @@ -1245,7 +1426,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.InstanceNetworkInterfaceUpdate ) ), - rest.delete( + http.delete( '/v1/network-interfaces/:interface', handler( handlers['instanceNetworkInterfaceDelete'], @@ -1253,34 +1434,34 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.get('/v1/ping', handler(handlers['ping'], null, null)), - rest.get('/v1/policy', handler(handlers['policyView'], null, null)), - rest.put('/v1/policy', handler(handlers['policyUpdate'], null, schema.SiloRolePolicy)), - rest.get( + http.get('/v1/ping', handler(handlers['ping'], null, null)), + http.get('/v1/policy', handler(handlers['policyView'], null, null)), + http.put('/v1/policy', handler(handlers['policyUpdate'], null, schema.SiloRolePolicy)), + http.get( '/v1/projects', handler(handlers['projectList'], schema.ProjectListParams, null) ), - rest.post( + http.post( '/v1/projects', handler(handlers['projectCreate'], null, schema.ProjectCreate) ), - rest.get( + http.get( '/v1/projects/:project', handler(handlers['projectView'], schema.ProjectViewParams, null) ), - rest.put( + http.put( '/v1/projects/:project', handler(handlers['projectUpdate'], schema.ProjectUpdateParams, schema.ProjectUpdate) ), - rest.delete( + http.delete( '/v1/projects/:project', handler(handlers['projectDelete'], schema.ProjectDeleteParams, null) ), - rest.get( + http.get( '/v1/projects/:project/policy', handler(handlers['projectPolicyView'], schema.ProjectPolicyViewParams, null) ), - rest.put( + http.put( '/v1/projects/:project/policy', handler( handlers['projectPolicyUpdate'], @@ -1288,11 +1469,11 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.ProjectRolePolicy ) ), - rest.get( + http.get( '/v1/snapshots', handler(handlers['snapshotList'], schema.SnapshotListParams, null) ), - rest.post( + http.post( '/v1/snapshots', handler( handlers['snapshotCreate'], @@ -1300,43 +1481,43 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.SnapshotCreate ) ), - rest.get( + http.get( '/v1/snapshots/:snapshot', handler(handlers['snapshotView'], schema.SnapshotViewParams, null) ), - rest.delete( + http.delete( '/v1/snapshots/:snapshot', handler(handlers['snapshotDelete'], schema.SnapshotDeleteParams, null) ), - rest.get( + http.get( '/v1/system/hardware/disks', handler(handlers['physicalDiskList'], schema.PhysicalDiskListParams, null) ), - rest.get( + http.get( '/v1/system/hardware/racks', handler(handlers['rackList'], schema.RackListParams, null) ), - rest.get( + http.get( '/v1/system/hardware/racks/:rackId', handler(handlers['rackView'], schema.RackViewParams, null) ), - rest.get( + http.get( '/v1/system/hardware/sleds', handler(handlers['sledList'], schema.SledListParams, null) ), - rest.get( + http.get( '/v1/system/hardware/sleds/:sledId', handler(handlers['sledView'], schema.SledViewParams, null) ), - rest.get( + http.get( '/v1/system/hardware/sleds/:sledId/disks', handler(handlers['sledPhysicalDiskList'], schema.SledPhysicalDiskListParams, null) ), - rest.get( + http.get( '/v1/system/hardware/sleds/:sledId/instances', handler(handlers['sledInstanceList'], schema.SledInstanceListParams, null) ), - rest.get( + http.get( '/v1/system/hardware/switch-port', handler( handlers['networkingSwitchPortList'], @@ -1344,7 +1525,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.post( + http.post( '/v1/system/hardware/switch-port/:port/settings', handler( handlers['networkingSwitchPortApplySettings'], @@ -1352,7 +1533,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.SwitchPortApplySettings ) ), - rest.delete( + http.delete( '/v1/system/hardware/switch-port/:port/settings', handler( handlers['networkingSwitchPortClearSettings'], @@ -1360,15 +1541,15 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.get( + http.get( '/v1/system/hardware/switches', handler(handlers['switchList'], schema.SwitchListParams, null) ), - rest.get( + http.get( '/v1/system/hardware/switches/:switchId', handler(handlers['switchView'], schema.SwitchViewParams, null) ), - rest.get( + http.get( '/v1/system/identity-providers', handler( handlers['siloIdentityProviderList'], @@ -1376,7 +1557,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.post( + http.post( '/v1/system/identity-providers/local/users', handler( handlers['localIdpUserCreate'], @@ -1384,11 +1565,11 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.UserCreate ) ), - rest.delete( + http.delete( '/v1/system/identity-providers/local/users/:userId', handler(handlers['localIdpUserDelete'], schema.LocalIdpUserDeleteParams, null) ), - rest.post( + http.post( '/v1/system/identity-providers/local/users/:userId/set-password', handler( handlers['localIdpUserSetPassword'], @@ -1396,7 +1577,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.UserPassword ) ), - rest.post( + http.post( '/v1/system/identity-providers/saml', handler( handlers['samlIdentityProviderCreate'], @@ -1404,7 +1585,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.SamlIdentityProviderCreate ) ), - rest.get( + http.get( '/v1/system/identity-providers/saml/:provider', handler( handlers['samlIdentityProviderView'], @@ -1412,59 +1593,59 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.get( + http.get( '/v1/system/ip-pools', handler(handlers['ipPoolList'], schema.IpPoolListParams, null) ), - rest.post( + http.post( '/v1/system/ip-pools', handler(handlers['ipPoolCreate'], null, schema.IpPoolCreate) ), - rest.get( + http.get( '/v1/system/ip-pools/:pool', handler(handlers['ipPoolView'], schema.IpPoolViewParams, null) ), - rest.put( + http.put( '/v1/system/ip-pools/:pool', handler(handlers['ipPoolUpdate'], schema.IpPoolUpdateParams, schema.IpPoolUpdate) ), - rest.delete( + http.delete( '/v1/system/ip-pools/:pool', handler(handlers['ipPoolDelete'], schema.IpPoolDeleteParams, null) ), - rest.get( + http.get( '/v1/system/ip-pools/:pool/ranges', handler(handlers['ipPoolRangeList'], schema.IpPoolRangeListParams, null) ), - rest.post( + http.post( '/v1/system/ip-pools/:pool/ranges/add', handler(handlers['ipPoolRangeAdd'], schema.IpPoolRangeAddParams, schema.IpRange) ), - rest.post( + http.post( '/v1/system/ip-pools/:pool/ranges/remove', handler(handlers['ipPoolRangeRemove'], schema.IpPoolRangeRemoveParams, schema.IpRange) ), - rest.get( + http.get( '/v1/system/ip-pools-service', handler(handlers['ipPoolServiceView'], null, null) ), - rest.get( + http.get( '/v1/system/ip-pools-service/ranges', handler(handlers['ipPoolServiceRangeList'], schema.IpPoolServiceRangeListParams, null) ), - rest.post( + http.post( '/v1/system/ip-pools-service/ranges/add', handler(handlers['ipPoolServiceRangeAdd'], null, schema.IpRange) ), - rest.post( + http.post( '/v1/system/ip-pools-service/ranges/remove', handler(handlers['ipPoolServiceRangeRemove'], null, schema.IpRange) ), - rest.get( + http.get( '/v1/system/metrics/:metricName', handler(handlers['systemMetric'], schema.SystemMetricParams, null) ), - rest.get( + http.get( '/v1/system/networking/address-lot', handler( handlers['networkingAddressLotList'], @@ -1472,11 +1653,11 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.post( + http.post( '/v1/system/networking/address-lot', handler(handlers['networkingAddressLotCreate'], null, schema.AddressLotCreate) ), - rest.delete( + http.delete( '/v1/system/networking/address-lot/:addressLot', handler( handlers['networkingAddressLotDelete'], @@ -1484,7 +1665,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.get( + http.get( '/v1/system/networking/address-lot/:addressLot/blocks', handler( handlers['networkingAddressLotBlockList'], @@ -1492,7 +1673,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.get( + http.get( '/v1/system/networking/bgp', handler( handlers['networkingBgpConfigList'], @@ -1500,11 +1681,11 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.post( + http.post( '/v1/system/networking/bgp', handler(handlers['networkingBgpConfigCreate'], null, schema.BgpConfigCreate) ), - rest.delete( + http.delete( '/v1/system/networking/bgp', handler( handlers['networkingBgpConfigDelete'], @@ -1512,7 +1693,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.get( + http.get( '/v1/system/networking/bgp-announce', handler( handlers['networkingBgpAnnounceSetList'], @@ -1520,11 +1701,11 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.post( + http.post( '/v1/system/networking/bgp-announce', handler(handlers['networkingBgpAnnounceSetCreate'], null, schema.BgpAnnounceSetCreate) ), - rest.delete( + http.delete( '/v1/system/networking/bgp-announce', handler( handlers['networkingBgpAnnounceSetDelete'], @@ -1532,7 +1713,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.get( + http.get( '/v1/system/networking/bgp-routes-ipv4', handler( handlers['networkingBgpImportedRoutesIpv4'], @@ -1540,11 +1721,11 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.get( + http.get( '/v1/system/networking/bgp-status', handler(handlers['networkingBgpStatus'], null, null) ), - rest.get( + http.get( '/v1/system/networking/loopback-address', handler( handlers['networkingLoopbackAddressList'], @@ -1552,7 +1733,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.post( + http.post( '/v1/system/networking/loopback-address', handler( handlers['networkingLoopbackAddressCreate'], @@ -1560,7 +1741,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.LoopbackAddressCreate ) ), - rest.delete( + http.delete( '/v1/system/networking/loopback-address/:rackId/:switchLocation/:address/:subnetMask', handler( handlers['networkingLoopbackAddressDelete'], @@ -1568,7 +1749,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.get( + http.get( '/v1/system/networking/switch-port-settings', handler( handlers['networkingSwitchPortSettingsList'], @@ -1576,7 +1757,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.post( + http.post( '/v1/system/networking/switch-port-settings', handler( handlers['networkingSwitchPortSettingsCreate'], @@ -1584,7 +1765,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.SwitchPortSettingsCreate ) ), - rest.delete( + http.delete( '/v1/system/networking/switch-port-settings', handler( handlers['networkingSwitchPortSettingsDelete'], @@ -1592,7 +1773,7 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.get( + http.get( '/v1/system/networking/switch-port-settings/:port', handler( handlers['networkingSwitchPortSettingsView'], @@ -1600,37 +1781,37 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.get('/v1/system/policy', handler(handlers['systemPolicyView'], null, null)), - rest.put( + http.get('/v1/system/policy', handler(handlers['systemPolicyView'], null, null)), + http.put( '/v1/system/policy', handler(handlers['systemPolicyUpdate'], null, schema.FleetRolePolicy) ), - rest.get( + http.get( '/v1/system/roles', handler(handlers['roleList'], schema.RoleListParams, null) ), - rest.get( + http.get( '/v1/system/roles/:roleName', handler(handlers['roleView'], schema.RoleViewParams, null) ), - rest.get( + http.get( '/v1/system/silos', handler(handlers['siloList'], schema.SiloListParams, null) ), - rest.post('/v1/system/silos', handler(handlers['siloCreate'], null, schema.SiloCreate)), - rest.get( + http.post('/v1/system/silos', handler(handlers['siloCreate'], null, schema.SiloCreate)), + http.get( '/v1/system/silos/:silo', handler(handlers['siloView'], schema.SiloViewParams, null) ), - rest.delete( + http.delete( '/v1/system/silos/:silo', handler(handlers['siloDelete'], schema.SiloDeleteParams, null) ), - rest.get( + http.get( '/v1/system/silos/:silo/policy', handler(handlers['siloPolicyView'], schema.SiloPolicyViewParams, null) ), - rest.put( + http.put( '/v1/system/silos/:silo/policy', handler( handlers['siloPolicyUpdate'], @@ -1638,28 +1819,28 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.SiloRolePolicy ) ), - rest.get( + http.get( '/v1/system/users', handler(handlers['siloUserList'], schema.SiloUserListParams, null) ), - rest.get( + http.get( '/v1/system/users/:userId', handler(handlers['siloUserView'], schema.SiloUserViewParams, null) ), - rest.get( + http.get( '/v1/system/users-builtin', handler(handlers['userBuiltinList'], schema.UserBuiltinListParams, null) ), - rest.get( + http.get( '/v1/system/users-builtin/:user', handler(handlers['userBuiltinView'], schema.UserBuiltinViewParams, null) ), - rest.get('/v1/users', handler(handlers['userList'], schema.UserListParams, null)), - rest.get( + http.get('/v1/users', handler(handlers['userList'], schema.UserListParams, null)), + http.get( '/v1/vpc-firewall-rules', handler(handlers['vpcFirewallRulesView'], schema.VpcFirewallRulesViewParams, null) ), - rest.put( + http.put( '/v1/vpc-firewall-rules', handler( handlers['vpcFirewallRulesUpdate'], @@ -1667,11 +1848,11 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.VpcFirewallRuleUpdateParams ) ), - rest.get( + http.get( '/v1/vpc-router-routes', handler(handlers['vpcRouterRouteList'], schema.VpcRouterRouteListParams, null) ), - rest.post( + http.post( '/v1/vpc-router-routes', handler( handlers['vpcRouterRouteCreate'], @@ -1679,11 +1860,11 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.RouterRouteCreate ) ), - rest.get( + http.get( '/v1/vpc-router-routes/:route', handler(handlers['vpcRouterRouteView'], schema.VpcRouterRouteViewParams, null) ), - rest.put( + http.put( '/v1/vpc-router-routes/:route', handler( handlers['vpcRouterRouteUpdate'], @@ -1691,15 +1872,15 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.RouterRouteUpdate ) ), - rest.delete( + http.delete( '/v1/vpc-router-routes/:route', handler(handlers['vpcRouterRouteDelete'], schema.VpcRouterRouteDeleteParams, null) ), - rest.get( + http.get( '/v1/vpc-routers', handler(handlers['vpcRouterList'], schema.VpcRouterListParams, null) ), - rest.post( + http.post( '/v1/vpc-routers', handler( handlers['vpcRouterCreate'], @@ -1707,11 +1888,11 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.VpcRouterCreate ) ), - rest.get( + http.get( '/v1/vpc-routers/:router', handler(handlers['vpcRouterView'], schema.VpcRouterViewParams, null) ), - rest.put( + http.put( '/v1/vpc-routers/:router', handler( handlers['vpcRouterUpdate'], @@ -1719,15 +1900,15 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.VpcRouterUpdate ) ), - rest.delete( + http.delete( '/v1/vpc-routers/:router', handler(handlers['vpcRouterDelete'], schema.VpcRouterDeleteParams, null) ), - rest.get( + http.get( '/v1/vpc-subnets', handler(handlers['vpcSubnetList'], schema.VpcSubnetListParams, null) ), - rest.post( + http.post( '/v1/vpc-subnets', handler( handlers['vpcSubnetCreate'], @@ -1735,11 +1916,11 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.VpcSubnetCreate ) ), - rest.get( + http.get( '/v1/vpc-subnets/:subnet', handler(handlers['vpcSubnetView'], schema.VpcSubnetViewParams, null) ), - rest.put( + http.put( '/v1/vpc-subnets/:subnet', handler( handlers['vpcSubnetUpdate'], @@ -1747,11 +1928,11 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { schema.VpcSubnetUpdate ) ), - rest.delete( + http.delete( '/v1/vpc-subnets/:subnet', handler(handlers['vpcSubnetDelete'], schema.VpcSubnetDeleteParams, null) ), - rest.get( + http.get( '/v1/vpc-subnets/:subnet/network-interfaces', handler( handlers['vpcSubnetListNetworkInterfaces'], @@ -1759,17 +1940,17 @@ export function makeHandlers(handlers: MSWHandlers): RestHandler[] { null ) ), - rest.get('/v1/vpcs', handler(handlers['vpcList'], schema.VpcListParams, null)), - rest.post( + http.get('/v1/vpcs', handler(handlers['vpcList'], schema.VpcListParams, null)), + http.post( '/v1/vpcs', handler(handlers['vpcCreate'], schema.VpcCreateParams, schema.VpcCreate) ), - rest.get('/v1/vpcs/:vpc', handler(handlers['vpcView'], schema.VpcViewParams, null)), - rest.put( + http.get('/v1/vpcs/:vpc', handler(handlers['vpcView'], schema.VpcViewParams, null)), + http.put( '/v1/vpcs/:vpc', handler(handlers['vpcUpdate'], schema.VpcUpdateParams, schema.VpcUpdate) ), - rest.delete( + http.delete( '/v1/vpcs/:vpc', handler(handlers['vpcDelete'], schema.VpcDeleteParams, null) ), diff --git a/libs/api/__generated__/validate.ts b/libs/api/__generated__/validate.ts index 6bb4188d8..3ca5da16d 100644 --- a/libs/api/__generated__/validate.ts +++ b/libs/api/__generated__/validate.ts @@ -7,6 +7,7 @@ * * Copyright Oxide Computer Company */ + import { z, ZodType } from 'zod' import { processResponseBody, uniqueItems } from './util' From f81a36dbe6b27e4c002e4e43f2635bb1ac535688 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Thu, 2 Nov 2023 09:06:05 -0400 Subject: [PATCH 04/11] Pull cookies from correct place --- libs/api/__generated__/msw-handlers.ts | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/libs/api/__generated__/msw-handlers.ts b/libs/api/__generated__/msw-handlers.ts index 0f017b05f..78f0c15d4 100644 --- a/libs/api/__generated__/msw-handlers.ts +++ b/libs/api/__generated__/msw-handlers.ts @@ -8,15 +8,7 @@ * Copyright Oxide Computer Company */ -import { - DefaultBodyType, - delay as doDelay, - http, - HttpHandler, - HttpResponse, - PathParams, - StrictResponse, -} from 'msw' +import { http, HttpHandler, HttpResponse, PathParams, StrictResponse } from 'msw' import type { Promisable, SnakeCasedPropertiesDeep as Snakify } from 'type-fest' import { z, ZodSchema } from 'zod' @@ -1121,9 +1113,11 @@ const handler = async ({ request: req, params: pathParams, + cookies }: { request: Request - params: PathParams + params: PathParams, + cookies: Record }) => { const { params, paramsErr } = paramSchema ? validateParams(paramSchema, req, pathParams) @@ -1141,7 +1135,9 @@ const handler = // TypeScript can't narrow the handler down because there's not an explicit relationship between the schema // being present and the shape of the handler API. The type of this function could be resolved such that the // relevant schema is required if and only if the handler has a type that matches the inferred schema - const result = await (handler as any).apply(null, [{ path, query, body, req }]) + const result = await (handler as any).apply(null, [ + { path, query, body, req, cookies }, + ]) if (typeof result === 'number') { return new HttpResponse(null, { status: result }) } From 53c91526a2da1599856f1c913772e0d1cb2fc8a8 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 2 Nov 2023 13:07:27 +0000 Subject: [PATCH 05/11] Bot commit: format with prettier --- libs/api/__generated__/msw-handlers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/api/__generated__/msw-handlers.ts b/libs/api/__generated__/msw-handlers.ts index 78f0c15d4..98143b719 100644 --- a/libs/api/__generated__/msw-handlers.ts +++ b/libs/api/__generated__/msw-handlers.ts @@ -1113,10 +1113,10 @@ const handler = async ({ request: req, params: pathParams, - cookies + cookies, }: { request: Request - params: PathParams, + params: PathParams cookies: Record }) => { const { params, paramsErr } = paramSchema From 4c96e558b54c0ff3c2e178d147fd4c1d6c95cd4a Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Thu, 2 Nov 2023 09:52:49 -0400 Subject: [PATCH 06/11] Reduce diff w/ json export from generated code --- libs/api-mocks/index.ts | 2 +- libs/api-mocks/msw/db.ts | 6 ++--- libs/api-mocks/msw/handlers.ts | 36 +++++++++++++------------- libs/api-mocks/msw/util.ts | 13 +++++----- libs/api/__generated__/msw-handlers.ts | 17 ++++++------ 5 files changed, 36 insertions(+), 38 deletions(-) diff --git a/libs/api-mocks/index.ts b/libs/api-mocks/index.ts index d0ac67102..7a5b5fc16 100644 --- a/libs/api-mocks/index.ts +++ b/libs/api-mocks/index.ts @@ -24,5 +24,5 @@ export * from './user' export * from './vpc' export { handlers } from './msw/handlers' -export { MSW_USER_COOKIE } from './msw/util' +export { json, MSW_USER_COOKIE } from './msw/util' export { resetDb } from './msw/db' diff --git a/libs/api-mocks/msw/db.ts b/libs/api-mocks/msw/db.ts index 41f8787c1..818e2b839 100644 --- a/libs/api-mocks/msw/db.ts +++ b/libs/api-mocks/msw/db.ts @@ -7,7 +7,6 @@ */ // note that isUuid checks for any kind of UUID. strictly speaking, we should // only be checking for v4 -import { HttpResponse } from 'msw' import { validate as isUuid } from 'uuid' import type { ApiTypes as Api, PathParams as PP } from '@oxide/api' @@ -15,12 +14,11 @@ import * as mock from '@oxide/api-mocks' import { user1 } from '@oxide/api-mocks' import type { Json } from '../json-type' +import { json } from './util' const notFoundBody = { error_code: 'ObjectNotFound' } as const export type NotFound = typeof notFoundBody -export const notFoundErr = HttpResponse.json({ error_code: 'ObjectNotFound' } as const, { - status: 404, -}) +export const notFoundErr = json({ error_code: 'ObjectNotFound' } as const, { status: 404 }) export const lookupById = (table: T[], id: string) => { const item = table.find((i) => i.id === id) diff --git a/libs/api-mocks/msw/handlers.ts b/libs/api-mocks/msw/handlers.ts index b47ec0813..fafbe6827 100644 --- a/libs/api-mocks/msw/handlers.ts +++ b/libs/api-mocks/msw/handlers.ts @@ -18,7 +18,7 @@ import { type ApiTypes as Api, type SamlIdentityProvider, } from '@oxide/api' -import { HttpResponse, makeHandlers, type Json } from '@oxide/gen/msw-handlers' +import { json, makeHandlers, type Json } from '@oxide/gen/msw-handlers' import { GiB, pick, sortBy } from '@oxide/util' import { genCumulativeI64Data } from '../metrics' @@ -64,7 +64,7 @@ export const handlers = makeHandlers({ } db.projects.push(newProject) - return HttpResponse.json(newProject, { status: 201 }) + return json(newProject, { status: 201 }) }, projectView: ({ path }) => { if (path.project.endsWith('error-503')) { @@ -127,7 +127,7 @@ export const handlers = makeHandlers({ } db.disks.push(newDisk) - return HttpResponse.json(newDisk, { status: 201 }) + return json(newDisk, { status: 201 }) }, diskView: ({ path, query }) => lookup.disk({ ...path, ...query }), diskDelete({ path, query }) { @@ -254,7 +254,7 @@ export const handlers = makeHandlers({ ...getTimestamps(), } db.images.push(newImage) - return HttpResponse.json(newImage, { status: 201 }) + return json(newImage, { status: 201 }) }, imageView: ({ path, query }) => lookup.image({ ...path, ...query }), imageDelete({ path, query, cookies }) { @@ -273,7 +273,7 @@ export const handlers = makeHandlers({ delete image.project_id - return HttpResponse.json(image, { status: 202 }) + return json(image, { status: 202 }) }, imageDemote({ path, query }) { const image = lookup.image({ ...path, ...query }) @@ -281,7 +281,7 @@ export const handlers = makeHandlers({ image.project_id = project.id - return HttpResponse.json(image, { status: 202 }) + return json(image, { status: 202 }) }, instanceList({ query }) { const project = lookup.project(query) @@ -408,7 +408,7 @@ export const handlers = makeHandlers({ time_run_state_updated: new Date().toISOString(), } db.instances.push(newInstance) - return HttpResponse.json(newInstance, { status: 201 }) + return json(newInstance, { status: 201 }) }, instanceView: ({ path, query }) => lookup.instance({ ...path, ...query }), instanceDelete({ path, query }) { @@ -531,23 +531,23 @@ export const handlers = makeHandlers({ instance.run_state = 'running' }, 3000) - return HttpResponse.json(instance, { status: 202 }) + return json(instance, { status: 202 }) }, async instanceSerialConsole(_params) { await delay(3000) - return HttpResponse.json(serial) + return json(serial) }, instanceStart({ path, query }) { const instance = lookup.instance({ ...path, ...query }) instance.run_state = 'running' - return HttpResponse.json(instance, { status: 202 }) + return json(instance, { status: 202 }) }, instanceStop({ path, query }) { const instance = lookup.instance({ ...path, ...query }) instance.run_state = 'stopped' - return HttpResponse.json(instance, { status: 202 }) + return json(instance, { status: 202 }) }, projectPolicyView({ path }) { const project = lookup.project(path) @@ -602,7 +602,7 @@ export const handlers = makeHandlers({ } db.snapshots.push(newSnapshot) - return HttpResponse.json(newSnapshot, { status: 201 }) + return json(newSnapshot, { status: 201 }) }, snapshotView: ({ path, query }) => lookup.snapshot({ ...path, ...query }), snapshotDelete({ path, query }) { @@ -644,7 +644,7 @@ export const handlers = makeHandlers({ } db.vpcSubnets.push(newSubnet) - return HttpResponse.json(newVpc, { status: 201 }) + return json(newVpc, { status: 201 }) }, vpcView: ({ path, query }) => lookup.vpc({ ...path, ...query }), vpcUpdate({ body, path, query }) { @@ -721,7 +721,7 @@ export const handlers = makeHandlers({ ...getTimestamps(), } db.vpcRouters.push(newRouter) - return HttpResponse.json(newRouter, { status: 201 }) + return json(newRouter, { status: 201 }) }, vpcRouterView: ({ path, query }) => lookup.vpcRouter({ ...path, ...query }), vpcRouterUpdate({ body, path, query }) { @@ -761,7 +761,7 @@ export const handlers = makeHandlers({ ...body, ...getTimestamps(), } - return HttpResponse.json(newRoute, { status: 201 }) + return json(newRoute, { status: 201 }) }, vpcRouterRouteView: ({ path, query }) => lookup.vpcRouterRoute({ ...path, ...query }), vpcRouterRouteUpdate({ body, path, query }) { @@ -806,7 +806,7 @@ export const handlers = makeHandlers({ ...getTimestamps(), } db.vpcSubnets.push(newSubnet) - return HttpResponse.json(newSubnet, { status: 201 }) + return json(newSubnet, { status: 201 }) }, vpcSubnetView: ({ path, query }) => lookup.vpcSubnet({ ...path, ...query }), vpcSubnetUpdate({ body, path, query }) { @@ -897,7 +897,7 @@ export const handlers = makeHandlers({ ...getTimestamps(), } db.sshKeys.push(newSshKey) - return HttpResponse.json(newSshKey, { status: 201 }) + return json(newSshKey, { status: 201 }) }, currentUserSshKeyView: ({ path }) => lookup.sshKey(path), currentUserSshKeyDelete({ path }) { @@ -944,7 +944,7 @@ export const handlers = makeHandlers({ mapped_fleet_roles: body.mapped_fleet_roles || {}, } db.silos.push(newSilo) - return HttpResponse.json(newSilo, { status: 201 }) + return json(newSilo, { status: 201 }) }, siloView({ path, cookies }) { requireFleetViewer(cookies) diff --git a/libs/api-mocks/msw/util.ts b/libs/api-mocks/msw/util.ts index 44ff2d2cd..7edfcd5ff 100644 --- a/libs/api-mocks/msw/util.ts +++ b/libs/api-mocks/msw/util.ts @@ -19,13 +19,15 @@ import { type SystemMetricQueryParams, type User, } from '@oxide/api' -import { HttpResponse, type Json } from '@oxide/gen/msw-handlers' +import { json, type Json } from '@oxide/gen/msw-handlers' import { GiB, isTruthy, TiB } from '@oxide/util' import type { DbRoleAssignmentResourceType } from '..' import { genI64Data } from '../metrics' import { db } from './db' +export { json } from '@oxide/gen/msw-handlers' + interface PaginateOptions { limit?: number pageToken?: string @@ -85,13 +87,10 @@ export function getTimestamps() { return { time_created: now, time_modified: now } } -export const unavailableErr = HttpResponse.json( - { error_code: 'ServiceUnavailable' }, - { status: 503 } -) +export const unavailableErr = json({ error_code: 'ServiceUnavailable' }, { status: 503 }) export const NotImplemented = () => { - throw HttpResponse.json({ error_code: 'NotImplemented' }, { status: 501 }) + throw json({ error_code: 'NotImplemented' }, { status: 501 }) } export const errIfExists = >( @@ -105,7 +104,7 @@ export const errIfExists = >( ) ) { const name = 'name' in match ? match.name : 'id' in match ? match.id : '' - throw HttpResponse.json( + throw json( { error_code: 'ObjectAlreadyExists', message: `already exists: ${resourceLabel} "${name}"`, diff --git a/libs/api/__generated__/msw-handlers.ts b/libs/api/__generated__/msw-handlers.ts index 98143b719..bd87239d8 100644 --- a/libs/api/__generated__/msw-handlers.ts +++ b/libs/api/__generated__/msw-handlers.ts @@ -35,6 +35,7 @@ type StringifyDates = T extends Date * purpose JSON type! */ export type Json = Snakify> +export const json = HttpResponse.json // Shortcut to reduce number of imports required in consumers export { HttpResponse } @@ -1073,7 +1074,7 @@ function validateBody(schema: S, body: unknown) { if (result.success) { return { body: result.data as Json> } } - return { bodyErr: HttpResponse.json(result.error.issues, { status: 400 }) } + return { bodyErr: json(result.error.issues, { status: 400 }) } } function validateParams( schema: S, @@ -1101,7 +1102,7 @@ function validateParams( // exist if there's no valid name const { issues } = result.error const status = issues.some((e) => e.path[0] === 'path') ? 404 : 400 - return { paramsErr: HttpResponse.json(issues, { status }) } + return { paramsErr: json(issues, { status }) } } const handler = @@ -1117,19 +1118,19 @@ const handler = }: { request: Request params: PathParams - cookies: Record + cookies: Record }) => { const { params, paramsErr } = paramSchema ? validateParams(paramSchema, req, pathParams) : { params: {}, paramsErr: undefined } - if (paramsErr) return HttpResponse.json(paramsErr, { status: 400 }) + if (paramsErr) return json(paramsErr, { status: 400 }) const { path, query } = params const { body, bodyErr } = bodySchema ? validateBody(bodySchema, await req.json()) : { body: undefined, bodyErr: undefined } - if (bodyErr) return HttpResponse.json(bodyErr, { status: 400 }) + if (bodyErr) return json(bodyErr, { status: 400 }) try { // TypeScript can't narrow the handler down because there's not an explicit relationship between the schema @@ -1144,7 +1145,7 @@ const handler = if (typeof result === 'function') { return result() } - return HttpResponse.json(result) + return json(result) } catch (thrown) { if (typeof thrown === 'number') { return new HttpResponse(null, { status: thrown }) @@ -1153,10 +1154,10 @@ const handler = return thrown() } if (typeof thrown === 'string') { - return HttpResponse.json({ message: thrown }, { status: 400 }) + return json({ message: thrown }, { status: 400 }) } console.error('Unexpected mock error', thrown) - return HttpResponse.json({ message: 'Unknown Server Error' }, { status: 500 }) + return json({ message: 'Unknown Server Error' }, { status: 500 }) } } From 39be68a7b75d23d5a7cb721dcad5933090384a29 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Thu, 2 Nov 2023 11:12:15 -0400 Subject: [PATCH 07/11] Don't cache response object --- libs/api-mocks/msw/util.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/api-mocks/msw/util.ts b/libs/api-mocks/msw/util.ts index 7edfcd5ff..b729cd2b2 100644 --- a/libs/api-mocks/msw/util.ts +++ b/libs/api-mocks/msw/util.ts @@ -87,7 +87,8 @@ export function getTimestamps() { return { time_created: now, time_modified: now } } -export const unavailableErr = json({ error_code: 'ServiceUnavailable' }, { status: 503 }) +export const unavailableErr = () => + json({ error_code: 'ServiceUnavailable' }, { status: 503 }) export const NotImplemented = () => { throw json({ error_code: 'NotImplemented' }, { status: 501 }) From 8c601275b77a2dcb4091dcbecaf6138427a330d2 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Thu, 2 Nov 2023 12:29:00 -0400 Subject: [PATCH 08/11] Wrap response returns in functions --- libs/api-mocks/msw/db.ts | 3 ++- libs/api-mocks/msw/util.ts | 4 +--- libs/api/__generated__/msw-handlers.ts | 6 ++++++ libs/api/__tests__/hooks.spec.tsx | 7 ++++--- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/libs/api-mocks/msw/db.ts b/libs/api-mocks/msw/db.ts index 818e2b839..afbe2eec9 100644 --- a/libs/api-mocks/msw/db.ts +++ b/libs/api-mocks/msw/db.ts @@ -18,7 +18,8 @@ import { json } from './util' const notFoundBody = { error_code: 'ObjectNotFound' } as const export type NotFound = typeof notFoundBody -export const notFoundErr = json({ error_code: 'ObjectNotFound' } as const, { status: 404 }) +export const notFoundErr = () => + json({ error_code: 'ObjectNotFound' } as const, { status: 404 }) export const lookupById = (table: T[], id: string) => { const item = table.find((i) => i.id === id) diff --git a/libs/api-mocks/msw/util.ts b/libs/api-mocks/msw/util.ts index b729cd2b2..f1f56842e 100644 --- a/libs/api-mocks/msw/util.ts +++ b/libs/api-mocks/msw/util.ts @@ -90,9 +90,7 @@ export function getTimestamps() { export const unavailableErr = () => json({ error_code: 'ServiceUnavailable' }, { status: 503 }) -export const NotImplemented = () => { - throw json({ error_code: 'NotImplemented' }, { status: 501 }) -} +export const NotImplemented = () => json({ error_code: 'NotImplemented' }, { status: 501 }) export const errIfExists = >( collection: T[], diff --git a/libs/api/__generated__/msw-handlers.ts b/libs/api/__generated__/msw-handlers.ts index bd87239d8..69b01aa46 100644 --- a/libs/api/__generated__/msw-handlers.ts +++ b/libs/api/__generated__/msw-handlers.ts @@ -1145,6 +1145,9 @@ const handler = if (typeof result === 'function') { return result() } + if (result instanceof Response) { + return result + } return json(result) } catch (thrown) { if (typeof thrown === 'number') { @@ -1156,6 +1159,9 @@ const handler = if (typeof thrown === 'string') { return json({ message: thrown }, { status: 400 }) } + if (thrown instanceof Response) { + return thrown + } console.error('Unexpected mock error', thrown) return json({ message: 'Unknown Server Error' }, { status: 500 }) } diff --git a/libs/api/__tests__/hooks.spec.tsx b/libs/api/__tests__/hooks.spec.tsx index 1aa9e2dde..7bbd3f4e5 100644 --- a/libs/api/__tests__/hooks.spec.tsx +++ b/libs/api/__tests__/hooks.spec.tsx @@ -261,11 +261,12 @@ describe('useApiMutation', () => { const { result } = renderCreateProject() act(() => result.current.mutate(createParams)) - await waitFor(() => - expect(result.current.data).toMatchObject({ + await waitFor(() => { + console.log('result', result.current) + return expect(result.current.data).toMatchObject({ name: createParams.body.name, }) - ) + }) }) // RQ doesn't like a value of undefined for data, so we're using {} for now From 4dd86d3daedc6dcc6c0be71736377e04457e3685 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Thu, 2 Nov 2023 12:33:59 -0400 Subject: [PATCH 09/11] Remove stay console.log --- libs/api/__tests__/hooks.spec.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libs/api/__tests__/hooks.spec.tsx b/libs/api/__tests__/hooks.spec.tsx index 7bbd3f4e5..1aa9e2dde 100644 --- a/libs/api/__tests__/hooks.spec.tsx +++ b/libs/api/__tests__/hooks.spec.tsx @@ -261,12 +261,11 @@ describe('useApiMutation', () => { const { result } = renderCreateProject() act(() => result.current.mutate(createParams)) - await waitFor(() => { - console.log('result', result.current) - return expect(result.current.data).toMatchObject({ + await waitFor(() => + expect(result.current.data).toMatchObject({ name: createParams.body.name, }) - }) + ) }) // RQ doesn't like a value of undefined for data, so we're using {} for now From 1d33318a5d8601bc05b6555a9cbb3f2b2306a107 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Thu, 2 Nov 2023 12:39:55 -0400 Subject: [PATCH 10/11] Fix type error w/ NotImplemented --- libs/api-mocks/msw/util.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libs/api-mocks/msw/util.ts b/libs/api-mocks/msw/util.ts index f1f56842e..4e153262c 100644 --- a/libs/api-mocks/msw/util.ts +++ b/libs/api-mocks/msw/util.ts @@ -90,7 +90,11 @@ export function getTimestamps() { export const unavailableErr = () => json({ error_code: 'ServiceUnavailable' }, { status: 503 }) -export const NotImplemented = () => json({ error_code: 'NotImplemented' }, { status: 501 }) +export const NotImplemented = () => { + // This doesn't just return the response because it broadens the type to be usable + // directly as a handler + throw json({ error_code: 'NotImplemented' }, { status: 501 }) +} export const errIfExists = >( collection: T[], From 7a930f20f0861f3f9abffc633010d1512b211b23 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Fri, 3 Nov 2023 13:48:35 -0400 Subject: [PATCH 11/11] Ensure Response isn't constructed w/ body when body should be null --- mockServiceWorker.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/mockServiceWorker.js b/mockServiceWorker.js index 6498967c0..325dce2dd 100644 --- a/mockServiceWorker.js +++ b/mockServiceWorker.js @@ -125,7 +125,7 @@ async function handleRequest(event, requestId) { // always be a ReadableStream, even for 204 responses. // But when creating a new Response instance on the client, // the body for a 204 response must be null. - const responseBody = response.status === 204 ? null : responseClone.body + const responseBody = getSafeBody(response.status, responseClone.body) sendToClient( client, @@ -281,7 +281,7 @@ async function respondWithMock(response) { return Response.error() } - const mockedResponse = new Response(response.body, response) + const mockedResponse = new Response(getSafeBody(response.status, response.body), response) Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { value: true, @@ -290,3 +290,23 @@ async function respondWithMock(response) { return mockedResponse } + +/** + * Ensures that the response body is only included when the status code doesn't indicate + * the body should be empty + * + * This is a workaround required due to a bug in MSW's implementation. Once fixed this file + * should be regenerated. + * + * @see https://github.com/mswjs/msw/issues/1827 + * + * @param {number} status + * @param {B} body + * @returns {B | null} + */ +function getSafeBody(status, body) { + if (status === 101 || status=== 103 || status === 204 || status === 205 || status === 304) { + return null + } + return body +} \ No newline at end of file