Better mock oauth server (#416)

This commit is contained in:
Zai Shi 2025-02-05 21:10:41 +01:00 committed by GitHub
parent 8bd73f0d99
commit 84ba75895e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 448 additions and 49 deletions

View File

@ -10,8 +10,11 @@
"lint": "eslint ."
},
"dependencies": {
"@types/oidc-provider": "^8.5.1",
"@stackframe/stack-shared": "workspace:*",
"@types/express": "^5.0.0",
"@types/oidc-provider": "^8.5.1",
"body-parser": "^1.20.3",
"express": "^4.21.2",
"oidc-provider": "^8.5.1"
},
"devDependencies": {

View File

@ -1,41 +1,235 @@
import Provider, { Configuration } from 'oidc-provider';
import { strict as assert } from 'assert';
import express from 'express';
import Provider, { errors } from 'oidc-provider';
// terminate this Node.js process if parent terminates
process.on('disconnect', function() {
console.log('parent exited; exiting too');
process.exit();
});
const { SessionNotFound } = errors;
const port = Number.parseInt(process.env.PORT || "8114");
const mockedProviders = [
"github",
"facebook",
"google",
"microsoft",
"spotify",
"discord",
"gitlab",
"bitbucket",
"x",
const port = process.env.PORT || 8114;
const providerIds = [
'github',
'facebook',
'google',
'microsoft',
'spotify',
'discord',
'gitlab',
'bitbucket',
'x',
];
const clients = providerIds.map((id) => ({
client_id: id,
client_secret: 'MOCK-SERVER-SECRET',
redirect_uris: [`http://localhost:8102/api/v1/auth/oauth/callback/${id}`],
}));
const configuration: Configuration = {
clients: mockedProviders.map((providerId) => ({
client_id: providerId,
client_secret: 'MOCK-SERVER-SECRET',
redirect_uris: [
`http://localhost:8102/api/v1/auth/oauth/callback/${providerId}`,
],
})),
ttl: {
// we make sessions short so it asks us for our login again after a minute, instead of automatically logging us in with the already-logged-in session
Session: 60,
},
const configuration = {
clients,
ttl: { Session: 60 },
findAccount: async (ctx: any, sub: string) => ({
accountId: sub,
async claims() {
return { sub, email: sub };
},
}),
};
const oidc = new Provider(`http://localhost:${port}`, configuration);
const app = express();
oidc.listen(port, () => {
console.log(`oidc-provider listening on port ${port}, check http://localhost:${port}/.well-known/openid-configuration`);
app.use(express.urlencoded({ extended: false }));
const renderLoginView = ({ uid, debugInfo }: { uid: string, debugInfo: unknown }): string => {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sign-in</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
body {
background-color: #f8f9fa;
}
.card {
background-color: #fff;
border-radius: 0.5rem;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
</style>
</head>
<body class="min-h-screen flex items-center justify-center p-4">
<div class="card w-full max-w-md p-8">
<h1 class="text-2xl font-bold mb-6 text-center">Mock OAuth Sign-in</h1>
<form method="post" action="/interaction/${uid}/login" class="space-y-4">
<div>
<label for="login" class="block text-gray-700">Email</label>
<input id="login" type="email" name="login" required
class="mt-1 block w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring focus:border-blue-300" />
</div>
<button type="submit"
class="w-full bg-black hover:bg-gray-800 text-white font-semibold py-2 px-4 rounded">
Sign in
</button>
</form>
<!-- Container for displaying stored account emails -->
<div id="stored-accounts" class="mt-4"></div>
<details class="mt-6 bg-gray-50 rounded p-2">
<summary class="cursor-pointer text-sm text-gray-600">Debug</summary>
<pre class="mt-1 text-xs text-gray-500 overflow-x-auto">${JSON.stringify(debugInfo, null, 2)}</pre>
</details>
<script>
document.addEventListener("DOMContentLoaded", () => {
const storedAccountsContainer = document.getElementById('stored-accounts');
const emailInput = document.getElementById('login');
if (!storedAccountsContainer || !emailInput) return;
// Retrieve stored accounts from localStorage or initialize as an empty array
let storedAccounts = JSON.parse(localStorage.getItem('previousAccounts') || '[]');
// Get the form element to submit later
const form = document.querySelector('form');
if (!form) return;
// Render the list of stored accounts and add direct submission on click.
const renderStoredAccounts = () => {
if (storedAccounts.length > 0) {
let listHtml = '<h2 class="text-lg font-medium text-gray-700 mb-2">Previously Used Accounts</h2>';
listHtml += '<div class="grid gap-2">';
storedAccounts.forEach((account) => {
listHtml += \`
<div class="p-3 bg-white border border-gray-200 rounded-lg shadow-sm hover:bg-gray-50 transition-shadow cursor-pointer" data-email="\${account}">
<div class="flex items-center">
<div class="h-8 w-8 rounded-full bg-gray-100 flex items-center justify-center mr-3">
<span class="text-gray-600 font-medium">\${account.charAt(0).toUpperCase()}</span>
</div>
<span class="text-gray-700">\${account}</span>
</div>
</div>
\`;
});
listHtml += '</div>';
storedAccountsContainer.innerHTML = listHtml;
// Add click event listeners that set the email and submit the form directly.
storedAccountsContainer.querySelectorAll('[data-email]').forEach(card => {
card.addEventListener('click', () => {
const selectedEmail = card.getAttribute('data-email') || '';
emailInput.value = selectedEmail;
form.submit();
});
});
} else {
storedAccountsContainer.innerHTML = '';
}
};
renderStoredAccounts();
// On form submission, store the email if it's not already stored.
form.addEventListener('submit', () => {
const email = emailInput.value.trim();
if (email && !storedAccounts.includes(email)) {
storedAccounts.push(email);
localStorage.setItem('previousAccounts', JSON.stringify(storedAccounts));
}
});
});
</script>
</div>
</body>
</html>
`;
};
const setNoCache = (req: express.Request, res: express.Response, next: express.NextFunction) => {
res.set('cache-control', 'no-store');
next();
};
app.get('/interaction/:uid', setNoCache, async (req: express.Request, res: express.Response, next: express.NextFunction) => {
try {
const { uid, prompt, params, session, grantId } = await oidc.interactionDetails(req, res);
const debugInfo = { params, prompt, session };
if (prompt.name === 'login') {
res.send(renderLoginView({
uid,
debugInfo,
}));
} else if (prompt.name === 'consent') {
// Automatically approve consent without showing an approval page.
if (!session) throw new Error('No session found');
const accountId = session.accountId;
const { details } = prompt;
let grant = grantId
? await oidc.Grant.find(grantId)
: new oidc.Grant({ accountId, clientId: params.client_id as string });
if (!grant) {
throw new Error('Failed to create or find grant');
}
if (Array.isArray(details.missingOIDCScope)) {
grant.addOIDCScope(details.missingOIDCScope.join(' '));
}
if (Array.isArray(details.missingOIDCClaims)) {
grant.addOIDCClaims(details.missingOIDCClaims);
}
if (details.missingResourceScopes && typeof details.missingResourceScopes === 'object') {
for (const [indicator, scopes] of Object.entries(details.missingResourceScopes)) {
if (Array.isArray(scopes)) {
grant.addResourceScope(indicator, scopes.join(' '));
}
}
}
const newGrantId = await grant.save();
const consent: { grantId?: string } = {};
if (!grantId) consent.grantId = newGrantId;
const result = { consent };
await oidc.interactionFinished(req, res, result, { mergeWithLastSubmission: true });
} else {
res.send('Unknown prompt');
}
} catch (err) {
next(err);
}
});
app.post('/interaction/:uid/login', setNoCache, async (req, res, next) => {
try {
const { prompt } = await oidc.interactionDetails(req, res);
assert.strictEqual(prompt.name, 'login', 'Expected login prompt');
const result = { login: { accountId: req.body.login, remember: false } };
await oidc.interactionFinished(req, res, result, { mergeWithLastSubmission: false });
} catch (err) {
next(err);
}
});
// The POST consent route has been removed as consent is now auto-approved.
app.get('/interaction/:uid/abort', setNoCache, async (req, res, next) => {
try {
const result = {
error: 'access_denied',
error_description: 'End-User aborted interaction',
};
await oidc.interactionFinished(req, res, result, { mergeWithLastSubmission: false });
} catch (err) {
next(err);
}
});
app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
if (err instanceof SessionNotFound) {
res.status(410).send('Session not found or expired');
} else {
next(err);
}
});
app.use(oidc.callback());
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});

View File

@ -419,9 +419,18 @@ importers:
'@stackframe/stack-shared':
specifier: workspace:*
version: link:../../packages/stack-shared
'@types/express':
specifier: ^5.0.0
version: 5.0.0
'@types/oidc-provider':
specifier: ^8.5.1
version: 8.5.1
body-parser:
specifier: ^1.20.3
version: 1.20.3
express:
specifier: ^4.21.2
version: 4.21.2
oidc-provider:
specifier: ^8.5.1
version: 8.5.1
@ -5330,11 +5339,11 @@ packages:
'@types/estree@1.0.6':
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
'@types/express-serve-static-core@4.19.5':
resolution: {integrity: sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==}
'@types/express-serve-static-core@5.0.6':
resolution: {integrity: sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==}
'@types/express@4.17.21':
resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==}
'@types/express@5.0.0':
resolution: {integrity: sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==}
'@types/geojson@7946.0.15':
resolution: {integrity: sha512-9oSxFzDCT2Rj6DfcHF8G++jxBKS7mBqXl5xrRW+Kbvjry6Uduya2iiwqHPhVXpasAVMBYKkEPGgKhd3+/HZ6xA==}
@ -5833,6 +5842,9 @@ packages:
resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==}
engines: {node: '>= 0.4'}
array-flatten@1.1.1:
resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
array-includes@3.1.8:
resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==}
engines: {node: '>= 0.4'}
@ -6013,6 +6025,10 @@ packages:
bn.js@4.12.0:
resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==}
body-parser@1.20.3:
resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
boolbase@1.0.0:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
@ -6442,6 +6458,9 @@ packages:
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
cookie-signature@1.0.6:
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
cookie@0.4.2:
resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==}
engines: {node: '>= 0.6'}
@ -6450,6 +6469,10 @@ packages:
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
engines: {node: '>= 0.6'}
cookie@0.7.1:
resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==}
engines: {node: '>= 0.6'}
cookies@0.9.1:
resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==}
engines: {node: '>= 0.8'}
@ -6848,6 +6871,10 @@ packages:
resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
engines: {node: '>= 0.8'}
encodeurl@2.0.0:
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
engines: {node: '>= 0.8'}
encoding-sniffer@0.2.0:
resolution: {integrity: sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==}
@ -7209,6 +7236,10 @@ packages:
resolution: {integrity: sha512-tCsc7WXTjrTx4ZjYLplcqrI3o4mYJ+Z6YspeuGL8tbt/hHoMchwBwtKfwM09svEY86iRapY93vUqQttcNuIO5Q==}
engines: {node: '>=6.0.0'}
etag@1.8.1:
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
engines: {node: '>= 0.6'}
eventemitter3@4.0.7:
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
@ -7232,6 +7263,10 @@ packages:
resolution: {integrity: sha512-6CX17Cu+rC2Fi2CyZ4CkgVG3hLl6BFsdAxfXiZkmDFIDY4mRx2y2spdeH6dqPHI9rP+AsHEfGeKz84Uuw7+Pmg==}
engines: {node: ^v12.20.0 || >=v14.13.0}
express@4.21.2:
resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==}
engines: {node: '>= 0.10.0'}
extend@3.0.2:
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
@ -7291,6 +7326,10 @@ packages:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
finalhandler@1.3.1:
resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==}
engines: {node: '>= 0.8'}
find-root@1.1.0:
resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==}
@ -7354,6 +7393,10 @@ packages:
resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==}
engines: {node: '>= 6'}
forwarded@0.2.0:
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
engines: {node: '>= 0.6'}
fraction.js@4.3.7:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
@ -7833,6 +7876,10 @@ packages:
resolution: {integrity: sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
ipaddr.js@1.9.1:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
ipaddr.js@2.2.0:
resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==}
engines: {node: '>= 10'}
@ -8440,6 +8487,9 @@ packages:
resolution: {integrity: sha512-GWHvA5QOcS412WCo8vwKDlTelGLsCGBVevQB5Kva961rmNfun0PCbv5+xta2kUMFJyR8/oWnn7ddeKdosbAPbA==}
engines: {node: '>=10'}
merge-descriptors@1.0.3:
resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==}
merge-stream@2.0.0:
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
@ -8583,6 +8633,11 @@ packages:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
mime@1.6.0:
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
engines: {node: '>=4'}
hasBin: true
mimic-fn@2.1.0:
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
engines: {node: '>=6'}
@ -8942,9 +8997,6 @@ packages:
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
engines: {node: '>= 6'}
object-inspect@1.13.1:
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
object-inspect@1.13.2:
resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==}
engines: {node: '>= 0.4'}
@ -9144,6 +9196,9 @@ packages:
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
engines: {node: '>=16 || 14 >=14.18'}
path-to-regexp@0.1.12:
resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==}
path-to-regexp@3.3.0:
resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==}
@ -9398,6 +9453,10 @@ packages:
resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==}
engines: {node: '>=12.0.0'}
proxy-addr@2.0.7:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'}
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
@ -9426,6 +9485,10 @@ packages:
engines: {node: '>=10.13.0'}
hasBin: true
qs@6.13.0:
resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
engines: {node: '>=0.6'}
querystringify@2.2.0:
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
@ -9460,6 +9523,10 @@ packages:
resolution: {integrity: sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==}
engines: {node: '>= 0.6'}
range-parser@1.2.1:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'}
raw-body@2.5.2:
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
engines: {node: '>= 0.8'}
@ -9904,12 +9971,20 @@ packages:
engines: {node: '>=10'}
hasBin: true
send@0.19.0:
resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==}
engines: {node: '>= 0.8.0'}
serialize-javascript@6.0.2:
resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
serve-handler@6.1.6:
resolution: {integrity: sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==}
serve-static@1.16.2:
resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==}
engines: {node: '>= 0.8.0'}
serve@14.2.4:
resolution: {integrity: sha512-qy1S34PJ/fcY8gjVGszDB3EXiPSk5FKhUa7tQe0UPRddxRidc2V6cNHPNewbE1D7MAkgLuWEt3Vw56vYy73tzQ==}
engines: {node: '>= 14'}
@ -10812,6 +10887,10 @@ packages:
util@0.10.4:
resolution: {integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==}
utils-merge@1.0.1:
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
engines: {node: '>= 0.4.0'}
uuid@9.0.1:
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
hasBin: true
@ -12290,7 +12369,7 @@ snapshots:
'@eslint/eslintrc@1.4.1':
dependencies:
ajv: 6.12.6
debug: 4.3.5
debug: 4.3.7
espree: 9.6.1
globals: 13.24.0
ignore: 5.3.1
@ -12382,7 +12461,7 @@ snapshots:
'@humanwhocodes/config-array@0.11.14':
dependencies:
'@humanwhocodes/object-schema': 2.0.3
debug: 4.3.5
debug: 4.3.7
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@ -15434,7 +15513,7 @@ snapshots:
'@types/cookies@0.9.0':
dependencies:
'@types/connect': 3.4.38
'@types/express': 4.17.21
'@types/express': 5.0.0
'@types/keygrip': 1.0.6
'@types/node': 20.17.6
@ -15492,17 +15571,17 @@ snapshots:
'@types/estree@1.0.6': {}
'@types/express-serve-static-core@4.19.5':
'@types/express-serve-static-core@5.0.6':
dependencies:
'@types/node': 20.17.6
'@types/qs': 6.9.15
'@types/range-parser': 1.2.7
'@types/send': 0.17.4
'@types/express@4.17.21':
'@types/express@5.0.0':
dependencies:
'@types/body-parser': 1.19.5
'@types/express-serve-static-core': 4.19.5
'@types/express-serve-static-core': 5.0.6
'@types/qs': 6.9.15
'@types/serve-static': 1.15.7
@ -16049,6 +16128,8 @@ snapshots:
call-bind: 1.0.7
is-array-buffer: 3.0.4
array-flatten@1.1.1: {}
array-includes@3.1.8:
dependencies:
call-bind: 1.0.7
@ -16288,6 +16369,23 @@ snapshots:
bn.js@4.12.0: {}
body-parser@1.20.3:
dependencies:
bytes: 3.1.2
content-type: 1.0.5
debug: 2.6.9
depd: 2.0.0
destroy: 1.2.0
http-errors: 2.0.0
iconv-lite: 0.4.24
on-finished: 2.4.1
qs: 6.13.0
raw-body: 2.5.2
type-is: 1.6.18
unpipe: 1.0.0
transitivePeerDependencies:
- supports-color
boolbase@1.0.0: {}
boxen@7.0.0:
@ -16747,10 +16845,14 @@ snapshots:
convert-source-map@2.0.0: {}
cookie-signature@1.0.6: {}
cookie@0.4.2: {}
cookie@0.6.0: {}
cookie@0.7.1: {}
cookies@0.9.1:
dependencies:
depd: 2.0.0
@ -17118,6 +17220,8 @@ snapshots:
encodeurl@1.0.2: {}
encodeurl@2.0.0: {}
encoding-sniffer@0.2.0:
dependencies:
iconv-lite: 0.6.3
@ -17830,6 +17934,8 @@ snapshots:
eta@3.4.0: {}
etag@1.8.1: {}
eventemitter3@4.0.7: {}
events@3.3.0: {}
@ -17862,6 +17968,42 @@ snapshots:
export-to-csv@1.4.0: {}
express@4.21.2:
dependencies:
accepts: 1.3.8
array-flatten: 1.1.1
body-parser: 1.20.3
content-disposition: 0.5.4
content-type: 1.0.5
cookie: 0.7.1
cookie-signature: 1.0.6
debug: 2.6.9
depd: 2.0.0
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
finalhandler: 1.3.1
fresh: 0.5.2
http-errors: 2.0.0
merge-descriptors: 1.0.3
methods: 1.1.2
on-finished: 2.4.1
parseurl: 1.3.3
path-to-regexp: 0.1.12
proxy-addr: 2.0.7
qs: 6.13.0
range-parser: 1.2.1
safe-buffer: 5.2.1
send: 0.19.0
serve-static: 1.16.2
setprototypeof: 1.2.0
statuses: 2.0.1
type-is: 1.6.18
utils-merge: 1.0.1
vary: 1.1.2
transitivePeerDependencies:
- supports-color
extend@3.0.2: {}
extendable-error@0.1.7: {}
@ -17912,6 +18054,18 @@ snapshots:
dependencies:
to-regex-range: 5.0.1
finalhandler@1.3.1:
dependencies:
debug: 2.6.9
encodeurl: 2.0.0
escape-html: 1.0.3
on-finished: 2.4.1
parseurl: 1.3.3
statuses: 2.0.1
unpipe: 1.0.0
transitivePeerDependencies:
- supports-color
find-root@1.1.0: {}
find-up@3.0.0:
@ -17968,6 +18122,8 @@ snapshots:
combined-stream: 1.0.8
mime-types: 2.1.35
forwarded@0.2.0: {}
fraction.js@4.3.7: {}
frame-ticker@1.0.3:
@ -18625,6 +18781,8 @@ snapshots:
ip-regex@5.0.0: {}
ipaddr.js@1.9.1: {}
ipaddr.js@2.2.0: {}
is-alphabetical@2.0.1: {}
@ -19336,6 +19494,8 @@ snapshots:
type-fest: 0.13.1
yargs-parser: 18.1.3
merge-descriptors@1.0.3: {}
merge-stream@2.0.0: {}
merge2@1.4.1: {}
@ -19638,6 +19798,8 @@ snapshots:
dependencies:
mime-db: 1.52.0
mime@1.6.0: {}
mimic-fn@2.1.0: {}
mimic-fn@4.0.0: {}
@ -20050,8 +20212,6 @@ snapshots:
object-hash@3.0.0: {}
object-inspect@1.13.1: {}
object-inspect@1.13.2: {}
object-keys@1.1.1: {}
@ -20283,6 +20443,8 @@ snapshots:
lru-cache: 10.2.2
minipass: 7.1.2
path-to-regexp@0.1.12: {}
path-to-regexp@3.3.0: {}
path-to-regexp@6.2.2: {}
@ -20557,6 +20719,11 @@ snapshots:
'@types/node': 20.17.6
long: 5.2.3
proxy-addr@2.0.7:
dependencies:
forwarded: 0.2.0
ipaddr.js: 1.9.1
proxy-from-env@1.1.0: {}
pseudomap@1.0.2: {}
@ -20582,6 +20749,10 @@ snapshots:
pngjs: 5.0.0
yargs: 15.4.1
qs@6.13.0:
dependencies:
side-channel: 1.0.6
querystringify@2.2.0: {}
queue-microtask@1.2.3: {}
@ -20608,6 +20779,8 @@ snapshots:
range-parser@1.2.0: {}
range-parser@1.2.1: {}
raw-body@2.5.2:
dependencies:
bytes: 3.1.2
@ -21210,6 +21383,24 @@ snapshots:
semver@7.6.3: {}
send@0.19.0:
dependencies:
debug: 2.6.9
depd: 2.0.0
destroy: 1.2.0
encodeurl: 1.0.2
escape-html: 1.0.3
etag: 1.8.1
fresh: 0.5.2
http-errors: 2.0.0
mime: 1.6.0
ms: 2.1.3
on-finished: 2.4.1
range-parser: 1.2.1
statuses: 2.0.1
transitivePeerDependencies:
- supports-color
serialize-javascript@6.0.2:
dependencies:
randombytes: 2.1.0
@ -21224,6 +21415,15 @@ snapshots:
path-to-regexp: 3.3.0
range-parser: 1.2.0
serve-static@1.16.2:
dependencies:
encodeurl: 2.0.0
escape-html: 1.0.3
parseurl: 1.3.3
send: 0.19.0
transitivePeerDependencies:
- supports-color
serve@14.2.4:
dependencies:
'@zeit/schemas': 2.36.0
@ -21354,7 +21554,7 @@ snapshots:
call-bind: 1.0.7
es-errors: 1.3.0
get-intrinsic: 1.2.4
object-inspect: 1.13.1
object-inspect: 1.13.2
siginfo@2.0.0: {}
@ -22418,6 +22618,8 @@ snapshots:
dependencies:
inherits: 2.0.3
utils-merge@1.0.1: {}
uuid@9.0.1: {}
uzip@0.20201231.0: {}