mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
test(integration): username and password flow (#1111)
This commit is contained in:
parent
9fa5d8ca46
commit
8d27adce39
10 changed files with 368 additions and 9 deletions
|
@ -1,7 +1,8 @@
|
||||||
import { merge, Config } from '@silverhand/jest-config';
|
import { merge, Config } from '@silverhand/jest-config';
|
||||||
|
|
||||||
const config: Config.InitialOptions = merge({
|
const config: Config.InitialOptions = merge({
|
||||||
testEnvironment: 'node',
|
testEnvironment: 'jsdom',
|
||||||
|
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
|
||||||
});
|
});
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|
14
packages/integration-tests/jest.setup.js
Normal file
14
packages/integration-tests/jest.setup.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// Need to disable following rules to mock text-decode/text-encoder and crypto for jsdom
|
||||||
|
// https://github.com/jsdom/jsdom/issues/1612
|
||||||
|
import { Crypto } from '@peculiar/webcrypto';
|
||||||
|
import { TextDecoder, TextEncoder } from 'text-encoder';
|
||||||
|
|
||||||
|
// eslint-disable-next-line unicorn/prefer-module
|
||||||
|
const fetch = require('node-fetch');
|
||||||
|
|
||||||
|
/* eslint-disable @silverhand/fp/no-mutation */
|
||||||
|
global.crypto = new Crypto();
|
||||||
|
global.fetch = fetch;
|
||||||
|
global.TextDecoder = TextDecoder;
|
||||||
|
global.TextEncoder = TextEncoder;
|
||||||
|
/* eslint-enable @silverhand/fp/no-mutation */
|
|
@ -12,6 +12,9 @@
|
||||||
"start": "jest"
|
"start": "jest"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@jest/types": "^27.5.1",
|
||||||
|
"@logto/js": "^0.1.16",
|
||||||
|
"@peculiar/webcrypto": "^1.3.3",
|
||||||
"@silverhand/eslint-config": "^0.14.0",
|
"@silverhand/eslint-config": "^0.14.0",
|
||||||
"@silverhand/essentials": "^1.1.7",
|
"@silverhand/essentials": "^1.1.7",
|
||||||
"@silverhand/jest-config": "^0.14.0",
|
"@silverhand/jest-config": "^0.14.0",
|
||||||
|
@ -21,7 +24,9 @@
|
||||||
"eslint": "^8.10.0",
|
"eslint": "^8.10.0",
|
||||||
"got": "^11.8.2",
|
"got": "^11.8.2",
|
||||||
"jest": "^27.5.1",
|
"jest": "^27.5.1",
|
||||||
|
"node-fetch": "^2.6.7",
|
||||||
"prettier": "^2.3.2",
|
"prettier": "^2.3.2",
|
||||||
|
"text-encoder": "^0.0.4",
|
||||||
"ts-node": "^10.0.0",
|
"ts-node": "^10.0.0",
|
||||||
"typescript": "^4.6.4"
|
"typescript": "^4.6.4"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { getEnv } from '@silverhand/essentials';
|
|
||||||
import got from 'got';
|
import got from 'got';
|
||||||
|
|
||||||
export default got.extend({ prefixUrl: new URL('/api', getEnv('LOGTO_URL')) });
|
import { logtoUrl } from '@/constants';
|
||||||
|
|
||||||
|
export default got.extend({ prefixUrl: new URL('/api', logtoUrl) });
|
||||||
|
|
9
packages/integration-tests/src/constants.ts
Normal file
9
packages/integration-tests/src/constants.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { getEnv } from '@silverhand/essentials';
|
||||||
|
|
||||||
|
export const logtoUrl = getEnv('LOGTO_URL');
|
||||||
|
|
||||||
|
export const adminConsoleApplicationId = 'admin_console';
|
||||||
|
|
||||||
|
export const discoveryUrl = `${logtoUrl}/oidc/.well-known/openid-configuration`;
|
||||||
|
|
||||||
|
export const redirectUri = `${logtoUrl}/console/callback`;
|
111
packages/integration-tests/src/logto-context.ts
Normal file
111
packages/integration-tests/src/logto-context.ts
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
import { generateCodeVerifier, generateState, generateCodeChallenge } from '@logto/js';
|
||||||
|
|
||||||
|
import { generatePassword, generateUsername } from './utils';
|
||||||
|
|
||||||
|
type Account = {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ContextData = {
|
||||||
|
account: Account;
|
||||||
|
codeVerifier: string;
|
||||||
|
codeChallenge: string;
|
||||||
|
state: string;
|
||||||
|
authorizationEndpoint: string;
|
||||||
|
tokenEndpoint: string;
|
||||||
|
authorizationCode: string;
|
||||||
|
interactionCookie: string;
|
||||||
|
nextRedirectTo: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ContextDataKey = keyof ContextData;
|
||||||
|
|
||||||
|
type ContextStore = {
|
||||||
|
getData: <T extends ContextDataKey>(key: T) => ContextData[T];
|
||||||
|
setData: <T extends ContextDataKey>(key: T, value: ContextData[T]) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createContextStore = (): ContextStore => {
|
||||||
|
const data: ContextData = {
|
||||||
|
account: { username: '', password: '' },
|
||||||
|
codeVerifier: '',
|
||||||
|
codeChallenge: '',
|
||||||
|
state: '',
|
||||||
|
interactionCookie: '',
|
||||||
|
authorizationCode: '',
|
||||||
|
authorizationEndpoint: '',
|
||||||
|
tokenEndpoint: '',
|
||||||
|
nextRedirectTo: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
getData: <T extends ContextDataKey>(key: T) => data[key],
|
||||||
|
setData: <T extends ContextDataKey>(key: T, value: ContextData[T]) => {
|
||||||
|
// eslint-disable-next-line @silverhand/fp/no-mutation
|
||||||
|
data[key] = value;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export class LogtoContext {
|
||||||
|
private readonly contextData: ContextStore = createContextStore();
|
||||||
|
|
||||||
|
public async init() {
|
||||||
|
const account = {
|
||||||
|
username: generatePassword(),
|
||||||
|
password: generateUsername(),
|
||||||
|
};
|
||||||
|
const codeVerifier = generateCodeVerifier();
|
||||||
|
const codeChallenge = await generateCodeChallenge(codeVerifier);
|
||||||
|
|
||||||
|
this.setData('account', account);
|
||||||
|
this.setData('codeVerifier', codeVerifier);
|
||||||
|
this.setData('codeChallenge', codeChallenge);
|
||||||
|
this.setData('state', generateState());
|
||||||
|
}
|
||||||
|
|
||||||
|
public get account(): Account {
|
||||||
|
return this.getData('account');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get codeVerifier(): string {
|
||||||
|
return this.getData('codeVerifier');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get codeChallenge(): string {
|
||||||
|
return this.getData('codeChallenge');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get state(): string {
|
||||||
|
return this.getData('state');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get authorizationCode(): string {
|
||||||
|
return this.getData('authorizationCode');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get interactionCookie(): string {
|
||||||
|
return this.getData('interactionCookie');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get authorizationEndpoint(): string {
|
||||||
|
return this.getData('authorizationEndpoint');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get tokenEndpoint(): string {
|
||||||
|
return this.getData('tokenEndpoint');
|
||||||
|
}
|
||||||
|
|
||||||
|
public get nextRedirectTo(): string {
|
||||||
|
return this.getData('nextRedirectTo');
|
||||||
|
}
|
||||||
|
|
||||||
|
public setData<T extends ContextDataKey>(key: T, value: ContextData[T]): void {
|
||||||
|
this.contextData.setData(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getData<T extends ContextDataKey>(key: T): ContextData[T] {
|
||||||
|
return this.contextData.getData(key);
|
||||||
|
}
|
||||||
|
}
|
10
packages/integration-tests/src/utils.ts
Normal file
10
packages/integration-tests/src/utils.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { Response } from 'got/dist/source';
|
||||||
|
|
||||||
|
export const extractCookie = (response: Response) => {
|
||||||
|
const { headers } = response;
|
||||||
|
|
||||||
|
return headers['set-cookie']?.join('; ') ?? '';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const generateUsername = () => `usr-${crypto.randomUUID()}`;
|
||||||
|
export const generatePassword = () => `pwd-${crypto.randomUUID()}`;
|
183
packages/integration-tests/tests/username-password-flow.test.ts
Normal file
183
packages/integration-tests/tests/username-password-flow.test.ts
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
import {
|
||||||
|
createRequester,
|
||||||
|
fetchOidcConfig,
|
||||||
|
fetchTokenByAuthorizationCode,
|
||||||
|
generateSignInUri,
|
||||||
|
verifyAndParseCodeFromCallbackUri,
|
||||||
|
} from '@logto/js';
|
||||||
|
import got from 'got/dist/source';
|
||||||
|
|
||||||
|
import api from '@/api';
|
||||||
|
|
||||||
|
import { adminConsoleApplicationId, discoveryUrl, logtoUrl, redirectUri } from '../src/constants';
|
||||||
|
import { LogtoContext } from '../src/logto-context';
|
||||||
|
import { extractCookie } from '../src/utils';
|
||||||
|
|
||||||
|
describe('username and password flow', () => {
|
||||||
|
const logtoContext = new LogtoContext();
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await logtoContext.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch OIDC configuration', async () => {
|
||||||
|
const oidcConfig = await fetchOidcConfig(discoveryUrl, createRequester());
|
||||||
|
const { authorizationEndpoint, tokenEndpoint } = oidcConfig;
|
||||||
|
expect(authorizationEndpoint).toBeTruthy();
|
||||||
|
expect(tokenEndpoint).toBeTruthy();
|
||||||
|
|
||||||
|
logtoContext.setData('authorizationEndpoint', authorizationEndpoint);
|
||||||
|
logtoContext.setData('tokenEndpoint', tokenEndpoint);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should visit authorization endpoint and get interaction cookie', async () => {
|
||||||
|
const signInUri = generateSignInUri({
|
||||||
|
authorizationEndpoint: logtoContext.authorizationEndpoint,
|
||||||
|
clientId: adminConsoleApplicationId,
|
||||||
|
redirectUri,
|
||||||
|
codeChallenge: logtoContext.codeChallenge,
|
||||||
|
state: logtoContext.state,
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await got(signInUri, {
|
||||||
|
followRedirect: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Note: this will redirect to the ui sign in page
|
||||||
|
expect(response.statusCode).toBe(303);
|
||||||
|
expect(response.headers.location).toBe('/sign-in');
|
||||||
|
|
||||||
|
const cookie = extractCookie(response);
|
||||||
|
expect(cookie).toBeTruthy();
|
||||||
|
|
||||||
|
logtoContext.setData('interactionCookie', cookie);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should register with username and password and redirect to oidc/auth endpoint to start an auth process', async () => {
|
||||||
|
type RegisterResponse = {
|
||||||
|
redirectTo: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const registerResponse = await api
|
||||||
|
.post('session/register/username-password', {
|
||||||
|
headers: {
|
||||||
|
cookie: logtoContext.interactionCookie,
|
||||||
|
},
|
||||||
|
json: logtoContext.account,
|
||||||
|
})
|
||||||
|
.json<RegisterResponse>();
|
||||||
|
|
||||||
|
const { redirectTo: invokeAuthUrl } = registerResponse;
|
||||||
|
|
||||||
|
expect(invokeAuthUrl.startsWith(`${logtoUrl}/oidc/auth`)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sign in with username and password and redirect to oidc/auth endpoint to start an auth process', async () => {
|
||||||
|
type SignInResponse = {
|
||||||
|
redirectTo: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const signInResponse = await api
|
||||||
|
.post('session/sign-in/username-password', {
|
||||||
|
headers: {
|
||||||
|
cookie: logtoContext.interactionCookie,
|
||||||
|
},
|
||||||
|
json: logtoContext.account,
|
||||||
|
followRedirect: false,
|
||||||
|
})
|
||||||
|
.json<SignInResponse>();
|
||||||
|
|
||||||
|
const { redirectTo: invokeAuthUrl } = signInResponse;
|
||||||
|
|
||||||
|
expect(invokeAuthUrl.startsWith(`${logtoUrl}/oidc/auth`)).toBeTruthy();
|
||||||
|
|
||||||
|
logtoContext.setData('nextRedirectTo', invokeAuthUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invoke the auth process and redirect to the consent page with session cookie', async () => {
|
||||||
|
const invokeAuthUrl = logtoContext.nextRedirectTo;
|
||||||
|
const invokeAuthResponse = await got.get(invokeAuthUrl, {
|
||||||
|
headers: {
|
||||||
|
cookie: logtoContext.interactionCookie,
|
||||||
|
},
|
||||||
|
followRedirect: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Note: Redirect to consent page
|
||||||
|
expect(invokeAuthResponse).toHaveProperty('statusCode', 303);
|
||||||
|
expect(invokeAuthResponse.headers.location).toBe('/sign-in/consent');
|
||||||
|
|
||||||
|
const cookie = extractCookie(invokeAuthResponse);
|
||||||
|
expect(cookie).toBeTruthy();
|
||||||
|
expect(cookie.includes('_session.sig')).toBeTruthy();
|
||||||
|
|
||||||
|
logtoContext.setData('interactionCookie', cookie);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should redirect to oidc/auth endpoint to complete the auth process after consent', async () => {
|
||||||
|
type ConsentResponse = {
|
||||||
|
redirectTo: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const consentResponse = await api
|
||||||
|
.post('session/consent', {
|
||||||
|
headers: {
|
||||||
|
cookie: logtoContext.interactionCookie,
|
||||||
|
},
|
||||||
|
followRedirect: false,
|
||||||
|
})
|
||||||
|
.json<ConsentResponse>();
|
||||||
|
|
||||||
|
const { redirectTo: completeAuthUrl } = consentResponse;
|
||||||
|
|
||||||
|
expect(completeAuthUrl.startsWith(`${logtoUrl}/oidc/auth`)).toBeTruthy();
|
||||||
|
|
||||||
|
logtoContext.setData('nextRedirectTo', completeAuthUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get the authorization code from the callback uri when the auth process is completed', async () => {
|
||||||
|
const completeAuthUrl = logtoContext.nextRedirectTo;
|
||||||
|
const authCodeResponse = await got.get(completeAuthUrl, {
|
||||||
|
headers: {
|
||||||
|
cookie: logtoContext.interactionCookie,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(authCodeResponse).toHaveProperty('statusCode', 200);
|
||||||
|
const callbackUri = authCodeResponse.redirectUrls[0];
|
||||||
|
expect(callbackUri).toBeTruthy();
|
||||||
|
|
||||||
|
if (!callbackUri) {
|
||||||
|
throw new Error('No redirect uri');
|
||||||
|
}
|
||||||
|
|
||||||
|
const authorizationCode = verifyAndParseCodeFromCallbackUri(
|
||||||
|
callbackUri,
|
||||||
|
redirectUri,
|
||||||
|
logtoContext.state
|
||||||
|
);
|
||||||
|
expect(authorizationCode).toBeTruthy();
|
||||||
|
|
||||||
|
logtoContext.setData('authorizationCode', authorizationCode);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch token by authorization code', async () => {
|
||||||
|
const token = await fetchTokenByAuthorizationCode(
|
||||||
|
{
|
||||||
|
clientId: adminConsoleApplicationId,
|
||||||
|
tokenEndpoint: logtoContext.tokenEndpoint,
|
||||||
|
redirectUri,
|
||||||
|
codeVerifier: logtoContext.codeVerifier,
|
||||||
|
code: logtoContext.authorizationCode,
|
||||||
|
},
|
||||||
|
createRequester()
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(token).toHaveProperty('accessToken');
|
||||||
|
expect(token).toHaveProperty('expiresIn');
|
||||||
|
expect(token).toHaveProperty('idToken');
|
||||||
|
expect(token).toHaveProperty('refreshToken');
|
||||||
|
expect(token).toHaveProperty('scope');
|
||||||
|
expect(token).toHaveProperty('tokenType');
|
||||||
|
});
|
||||||
|
});
|
|
@ -11,5 +11,5 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["tests", "src", "jest.*.ts"]
|
"include": ["tests", "src", "jest.*.ts", "jest.setup.js"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -962,6 +962,9 @@ importers:
|
||||||
|
|
||||||
packages/integration-tests:
|
packages/integration-tests:
|
||||||
specifiers:
|
specifiers:
|
||||||
|
'@jest/types': ^27.5.1
|
||||||
|
'@logto/js': ^0.1.16
|
||||||
|
'@peculiar/webcrypto': ^1.3.3
|
||||||
'@silverhand/eslint-config': ^0.14.0
|
'@silverhand/eslint-config': ^0.14.0
|
||||||
'@silverhand/essentials': ^1.1.7
|
'@silverhand/essentials': ^1.1.7
|
||||||
'@silverhand/jest-config': ^0.14.0
|
'@silverhand/jest-config': ^0.14.0
|
||||||
|
@ -971,10 +974,15 @@ importers:
|
||||||
eslint: ^8.10.0
|
eslint: ^8.10.0
|
||||||
got: ^11.8.2
|
got: ^11.8.2
|
||||||
jest: ^27.5.1
|
jest: ^27.5.1
|
||||||
|
node-fetch: ^2.6.7
|
||||||
prettier: ^2.3.2
|
prettier: ^2.3.2
|
||||||
|
text-encoder: ^0.0.4
|
||||||
ts-node: ^10.0.0
|
ts-node: ^10.0.0
|
||||||
typescript: ^4.6.4
|
typescript: ^4.6.4
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@jest/types': 27.5.1
|
||||||
|
'@logto/js': 0.1.16
|
||||||
|
'@peculiar/webcrypto': 1.3.3
|
||||||
'@silverhand/eslint-config': 0.14.0_rqoong6vegs374egqglqjbgiwm
|
'@silverhand/eslint-config': 0.14.0_rqoong6vegs374egqglqjbgiwm
|
||||||
'@silverhand/essentials': 1.1.7
|
'@silverhand/essentials': 1.1.7
|
||||||
'@silverhand/jest-config': 0.14.0_53ggqi2i4rbcfjtktmjua6zili
|
'@silverhand/jest-config': 0.14.0_53ggqi2i4rbcfjtktmjua6zili
|
||||||
|
@ -984,7 +992,9 @@ importers:
|
||||||
eslint: 8.10.0
|
eslint: 8.10.0
|
||||||
got: 11.8.3
|
got: 11.8.3
|
||||||
jest: 27.5.1_ts-node@10.7.0
|
jest: 27.5.1_ts-node@10.7.0
|
||||||
|
node-fetch: 2.6.7
|
||||||
prettier: 2.5.1
|
prettier: 2.5.1
|
||||||
|
text-encoder: 0.0.4
|
||||||
ts-node: 10.7.0_drbbnc2wk7uwp4gsdsdvgzqgya
|
ts-node: 10.7.0_drbbnc2wk7uwp4gsdsdvgzqgya
|
||||||
typescript: 4.6.4
|
typescript: 4.6.4
|
||||||
|
|
||||||
|
@ -2811,6 +2821,17 @@ packages:
|
||||||
superstruct: 0.15.4
|
superstruct: 0.15.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@logto/js/0.1.16:
|
||||||
|
resolution: {integrity: sha512-SwOmfQn/QJ6OTchElYSP5hKoXKm9sVOWmcgwjlblPUutWAMyR2Eo4wMBiiwjHQSNoXvYmK06cOXZp9BK4iYsrw==}
|
||||||
|
dependencies:
|
||||||
|
'@silverhand/essentials': 1.1.7
|
||||||
|
camelcase-keys: 7.0.2
|
||||||
|
jose: 4.6.0
|
||||||
|
js-base64: 3.7.2
|
||||||
|
lodash.get: 4.4.2
|
||||||
|
superstruct: 0.15.4
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@logto/react/0.1.15_react@17.0.2:
|
/@logto/react/0.1.15_react@17.0.2:
|
||||||
resolution: {integrity: sha512-GCbVRooMdCOBWJLvfDpChkKYt96Hr/Ki03xjO/gDb5KKQ3zrK9fSvN36vtUidRpddbWUsJJCK40J+FzCwUsHGg==}
|
resolution: {integrity: sha512-GCbVRooMdCOBWJLvfDpChkKYt96Hr/Ki03xjO/gDb5KKQ3zrK9fSvN36vtUidRpddbWUsJJCK40J+FzCwUsHGg==}
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
@ -7546,7 +7567,7 @@ packages:
|
||||||
eslint-import-resolver-webpack:
|
eslint-import-resolver-webpack:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/parser': 5.14.0_pzezdwkd5bvjkx2hshexc25sxq
|
'@typescript-eslint/parser': 5.14.0_fo4uz55zgcu432252zy2gvpvcu
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
eslint-import-resolver-node: 0.3.6
|
eslint-import-resolver-node: 0.3.6
|
||||||
eslint-import-resolver-typescript: 2.5.0_rnagsyfcubvpoxo2ynj23pim7u
|
eslint-import-resolver-typescript: 2.5.0_rnagsyfcubvpoxo2ynj23pim7u
|
||||||
|
@ -7597,7 +7618,7 @@ packages:
|
||||||
'@typescript-eslint/parser':
|
'@typescript-eslint/parser':
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/parser': 5.14.0_pzezdwkd5bvjkx2hshexc25sxq
|
'@typescript-eslint/parser': 5.14.0_fo4uz55zgcu432252zy2gvpvcu
|
||||||
array-includes: 3.1.4
|
array-includes: 3.1.4
|
||||||
array.prototype.flat: 1.2.5
|
array.prototype.flat: 1.2.5
|
||||||
debug: 2.6.9
|
debug: 2.6.9
|
||||||
|
@ -14682,6 +14703,10 @@ packages:
|
||||||
glob: 7.2.0
|
glob: 7.2.0
|
||||||
minimatch: 3.1.2
|
minimatch: 3.1.2
|
||||||
|
|
||||||
|
/text-encoder/0.0.4:
|
||||||
|
resolution: {integrity: sha512-12gllbNnC0Zdh9r+LCpEwpUdvncaE9hfUmCVm2ryCH1LEVUZbS6NdRq8omEgJI0zKgaGFTjwQVHbglGDCIbmNA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/text-extensions/1.9.0:
|
/text-extensions/1.9.0:
|
||||||
resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==}
|
resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==}
|
||||||
engines: {node: '>=0.10'}
|
engines: {node: '>=0.10'}
|
||||||
|
@ -14752,7 +14777,7 @@ packages:
|
||||||
universalify: 0.1.2
|
universalify: 0.1.2
|
||||||
|
|
||||||
/tr46/0.0.3:
|
/tr46/0.0.3:
|
||||||
resolution: {integrity: sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=}
|
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||||
|
|
||||||
/tr46/1.0.1:
|
/tr46/1.0.1:
|
||||||
resolution: {integrity: sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=}
|
resolution: {integrity: sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=}
|
||||||
|
@ -15558,7 +15583,7 @@ packages:
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/webidl-conversions/3.0.1:
|
/webidl-conversions/3.0.1:
|
||||||
resolution: {integrity: sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=}
|
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||||
|
|
||||||
/webidl-conversions/4.0.2:
|
/webidl-conversions/4.0.2:
|
||||||
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
|
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
|
||||||
|
@ -15581,7 +15606,7 @@ packages:
|
||||||
resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==}
|
resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==}
|
||||||
|
|
||||||
/whatwg-url/5.0.0:
|
/whatwg-url/5.0.0:
|
||||||
resolution: {integrity: sha1-lmRU6HZUYuN2RNNib2dCzotwll0=}
|
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
tr46: 0.0.3
|
tr46: 0.0.3
|
||||||
webidl-conversions: 3.0.1
|
webidl-conversions: 3.0.1
|
||||||
|
|
Loading…
Reference in a new issue