From 3e5ffc499116546b876d76064e55ac029f6fa2cb Mon Sep 17 00:00:00 2001 From: Xiao Yijun Date: Mon, 6 May 2024 09:54:48 +0800 Subject: [PATCH] feat(connector): add hugging face connector (#5797) --- .changeset/nasty-dots-lie.md | 5 + .vscode/settings.json | 7 +- .../connector-huggingface/README.md | 64 +++ .../connectors/connector-huggingface/logo.svg | 19 + .../connector-huggingface/package.json | 74 +++ .../connector-huggingface/src/constant.ts | 33 ++ .../connector-huggingface/src/index.test.ts | 133 +++++ .../connector-huggingface/src/index.ts | 154 ++++++ .../connector-huggingface/src/types.ts | 36 ++ pnpm-lock.yaml | 475 +++++++++++++----- 10 files changed, 884 insertions(+), 116 deletions(-) create mode 100644 .changeset/nasty-dots-lie.md create mode 100644 packages/connectors/connector-huggingface/README.md create mode 100644 packages/connectors/connector-huggingface/logo.svg create mode 100644 packages/connectors/connector-huggingface/package.json create mode 100644 packages/connectors/connector-huggingface/src/constant.ts create mode 100644 packages/connectors/connector-huggingface/src/index.test.ts create mode 100644 packages/connectors/connector-huggingface/src/index.ts create mode 100644 packages/connectors/connector-huggingface/src/types.ts diff --git a/.changeset/nasty-dots-lie.md b/.changeset/nasty-dots-lie.md new file mode 100644 index 000000000..dc7d517c3 --- /dev/null +++ b/.changeset/nasty-dots-lie.md @@ -0,0 +1,5 @@ +--- +"@logto/connector-huggingface": minor +--- + +add Hugging Face social connector diff --git a/.vscode/settings.json b/.vscode/settings.json index d69e1c720..b83dc8dfa 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -39,20 +39,21 @@ "CIAM", "codecov", "hasura", + "huggingface", "Logto", + "mailgun", "oidc", "passcode", "passcodes", "Passwordless", "pnpm", + "sendgrid", "silverhand", "slonik", "stylelint", "timestamptz", "topbar", - "withtyped", - "sendgrid", - "mailgun", "upsell", + "withtyped" ] } diff --git a/packages/connectors/connector-huggingface/README.md b/packages/connectors/connector-huggingface/README.md new file mode 100644 index 000000000..4a0b20234 --- /dev/null +++ b/packages/connectors/connector-huggingface/README.md @@ -0,0 +1,64 @@ +# Hugging Face connector + +The official Logto connector for Hugging Face social sign-in. + +**Table of contents** + +- [Hugging Face connector](#hugging-face-connector) + - [Get started](#get-started) + - [Sign in with Hugging Face account](#sign-in-with-hugging-face-account) + - [Create an OAuth app in the Hugging Face](#create-an-oauth-app-in-the-hugging-face) + - [Managing Hugging Face OAuth apps](#managing-hugging-face-oauth-apps) + - [Configure your connector](#configure-your-connector) + - [Config types](#config-types) + - [Test Hugging Face connector](#test-hugging-face-connector) + - [Reference](#reference) + + +## Get started + +The Hugging Face connector enables end-users to sign in to your application using their own Hugging Face accounts via Hugging Face OAuth / OpenID connect flow. + +## Sign in with Hugging Face account + +Go to the [Hugging Face website](https://huggingface.co/) and sign in with your Hugging Face account. You may register a new account if you don't have one. + +## Create an OAuth app in the Hugging Face + +Follow the [Creating an oauth app](https://huggingface.co/docs/hub/en/oauth#creating-an-oauth-app) guide, and register a new application. + +In the creation process, you will need to provide the following information: + +- **Application Name**: The name of your application. +- **Homepage URL**: The URL of your application's homepage or landing page. +- **Logo URL**: The URL of your application's logo. +- **Scopes**: The scopes allowed for the OAuth app. For Hugging Face connector, usually use `profile` to get the user's profile information and `email` to get the user's email address. Ensure these scopes are allowed in your Hugging Face OAuth app if you want to use them. +- **Redirect URI**: The URL to redirect the user to after they have authenticated. You can find the redirect URI in the Logto Admin Console when you're creating a Hugging Face connector or in the created Hugging Face connector details page. + +## Managing Hugging Face OAuth apps + +Go to the [Connected Applications](https://huggingface.co/settings/connected-applications) page, you can add, edit or delete existing OAuth apps. +You can also find `Client ID` and generate `App secrets` in corresponding OAuth app settings pages. + +## Configure your connector + +Fill out the `clientId` and `clientSecret` field with _Client ID_ and _App Secret_ you've got from OAuth app detail pages mentioned in the previous section. + +`scope` is a space-delimited list of [Hugging Face supported scopes](https://huggingface.co/docs/hub/en/oauth#currently-supported-scopes). If not provided, scope defaults to be `profile`. For Hugging Face connector, the scope you may want to use is `profile` and `email`. `profile` scope is required to get the user's profile information, and `email` scope is required to get the user's email address. Ensure you have allowed these scopes in your Hugging Face OAuth app (configured in [Create an OAuth app in the Hugging Face](#create-an-oauth-app-in-the-hugging-face) section). + +### Config types + +| Name | Type | +|--------------|--------| +| clientId | string | +| clientSecret | string | +| scope | string | + + +## Test Hugging Face connector + +That's it. The Hugging Face connector should be available now. Don't forget to [Enable connector in sign-in experience](https://docs.logto.io/docs/recipes/configure-connectors/social-connector/enable-social-sign-in/). + +## Reference + +- [Hugging Face - Sign in with Hugging Face](https://huggingface.co/docs/hub/en/oauth#sign-in-with-hugging-face) diff --git a/packages/connectors/connector-huggingface/logo.svg b/packages/connectors/connector-huggingface/logo.svg new file mode 100644 index 000000000..954043a4e --- /dev/null +++ b/packages/connectors/connector-huggingface/logo.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/packages/connectors/connector-huggingface/package.json b/packages/connectors/connector-huggingface/package.json new file mode 100644 index 000000000..10841e884 --- /dev/null +++ b/packages/connectors/connector-huggingface/package.json @@ -0,0 +1,74 @@ +{ + "name": "@logto/connector-huggingface", + "version": "0.0.0", + "description": "Hugging Face connector implementation.", + "author": "Silverhand Inc. ", + "dependencies": { + "@logto/connector-kit": "workspace:^3.0.0", + "@logto/connector-oauth": "workspace:^1.2.0", + "@silverhand/essentials": "^2.9.0", + "ky": "^1.2.3", + "zod": "^3.22.4" + }, + "main": "./lib/index.js", + "module": "./lib/index.js", + "exports": "./lib/index.js", + "license": "MPL-2.0", + "type": "module", + "files": [ + "lib", + "docs", + "logo.svg", + "logo-dark.svg" + ], + "scripts": { + "precommit": "lint-staged", + "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap", + "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", + "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental", + "lint": "eslint --ext .ts src", + "lint:report": "pnpm lint --format json --output-file report.json", + "test": "vitest src", + "test:ci": "pnpm run test --silent --coverage", + "prepublishOnly": "pnpm build" + }, + "engines": { + "node": "^20.9.0" + }, + "eslintConfig": { + "extends": "@silverhand", + "settings": { + "import/core-modules": [ + "@silverhand/essentials", + "got", + "nock", + "snakecase-keys", + "zod" + ] + } + }, + "prettier": "@silverhand/eslint-config/.prettierrc", + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-typescript": "^11.1.6", + "@silverhand/eslint-config": "6.0.1", + "@silverhand/ts-config": "6.0.0", + "@types/node": "^20.11.20", + "@types/supertest": "^6.0.2", + "@vitest/coverage-v8": "^1.4.0", + "eslint": "^8.56.0", + "lint-staged": "^15.0.2", + "nock": "14.0.0-beta.6", + "prettier": "^3.0.0", + "rollup": "^4.12.0", + "rollup-plugin-output-size": "^1.3.0", + "supertest": "^7.0.0", + "typescript": "^5.3.3", + "vitest": "^1.4.0" + } +} diff --git a/packages/connectors/connector-huggingface/src/constant.ts b/packages/connectors/connector-huggingface/src/constant.ts new file mode 100644 index 000000000..0d33191a6 --- /dev/null +++ b/packages/connectors/connector-huggingface/src/constant.ts @@ -0,0 +1,33 @@ +import type { ConnectorMetadata } from '@logto/connector-kit'; +import { ConnectorPlatform } from '@logto/connector-kit'; +import { clientIdFormItem, clientSecretFormItem, scopeFormItem } from '@logto/connector-oauth'; + +export const authorizationEndpoint = 'https://huggingface.co/oauth/authorize'; +export const tokenEndpoint = 'https://huggingface.co/oauth/token'; +export const userInfoEndpoint = 'https://huggingface.co/oauth/userinfo'; + +export const defaultMetadata: ConnectorMetadata = { + id: 'huggingface-universal', + target: 'huggingface', + platform: ConnectorPlatform.Universal, + name: { + en: 'Hugging Face', + }, + logo: './logo.svg', + logoDark: null, + description: { + en: 'Hugging Face is a machine learning (ML) and data science platform and community that helps users build, deploy and train machine learning models.', + }, + readme: './README.md', + formItems: [ + clientIdFormItem, + clientSecretFormItem, + { + ...scopeFormItem, + description: + "`profile` is required to get user's profile information, `email` is required to get user's email address. These scopes can be used individually or in combination; if no scopes are specified, `profile` will be used by default.", + }, + ], +}; + +export const defaultTimeout = 5000; diff --git a/packages/connectors/connector-huggingface/src/index.test.ts b/packages/connectors/connector-huggingface/src/index.test.ts new file mode 100644 index 000000000..e0b79e490 --- /dev/null +++ b/packages/connectors/connector-huggingface/src/index.test.ts @@ -0,0 +1,133 @@ +import nock from 'nock'; + +import { ConnectorError, ConnectorErrorCodes } from '@logto/connector-kit'; + +import { authorizationEndpoint, tokenEndpoint, userInfoEndpoint } from './constant.js'; +import createConnector from './index.js'; + +const getConfig = vi.fn().mockResolvedValue({ + clientId: '', + clientSecret: '', + scope: 'profile email', +}); + +const getSessionMock = vi.fn().mockResolvedValue({ redirectUri: 'http://localhost:3000/callback' }); + +describe('Hugging Face connector', () => { + beforeEach(() => { + nock(tokenEndpoint).post('').reply(200, { + access_token: 'access_token', + scope: 'scope', + token_type: 'token_type', + }); + }); + + afterEach(() => { + nock.cleanAll(); + vi.clearAllMocks(); + }); + + it('should get a valid uri by redirectUri and state', async () => { + const connector = await createConnector({ getConfig }); + const authorizationUri = await connector.getAuthorizationUri( + { + state: 'some_state', + redirectUri: 'http://localhost:3000/callback', + connectorId: 'some_connector_id', + connectorFactoryId: 'some_connector_factory_id', + jti: 'some_jti', + headers: {}, + }, + vi.fn() + ); + + expect(authorizationUri).toEqual( + `${authorizationEndpoint}?${new URLSearchParams({ + response_type: 'code', + client_id: '', + scope: 'profile email', + redirect_uri: 'http://localhost:3000/callback', + state: 'some_state', + }).toString()}` + ); + }); + + it('should get valid SocialUserInfo', async () => { + nock(userInfoEndpoint).get('').reply(200, { + sub: 'id', + name: 'name', + email: 'email', + picture: 'picture', + }); + + const connector = await createConnector({ getConfig }); + const socialUserInfo = await connector.getUserInfo({ code: 'code' }, getSessionMock); + + expect(socialUserInfo).toStrictEqual({ + id: 'id', + avatar: 'picture', + name: 'name', + email: 'email', + rawData: { + sub: 'id', + name: 'name', + email: 'email', + picture: 'picture', + }, + }); + }); + + it('throws AuthorizationFailed error if authentication failed', async () => { + const connector = await createConnector({ getConfig }); + await expect( + connector.getUserInfo({ error: 'some error' }, getSessionMock) + ).rejects.toStrictEqual( + new ConnectorError(ConnectorErrorCodes.AuthorizationFailed, { error: 'some error' }) + ); + }); + + it('throws InvalidResponse error if token response is invalid', async () => { + // Clear token response mock + nock.cleanAll(); + + nock(tokenEndpoint).post('').reply(200, { + invalid_filed: true, + }); + + const connector = await createConnector({ getConfig }); + await expect(connector.getUserInfo({ code: 'code' }, getSessionMock)).rejects.toSatisfy( + (connectorError) => + (connectorError as ConnectorError).code === ConnectorErrorCodes.InvalidResponse + ); + }); + + it('throws InvalidResponse error if userinfo response is invalid', async () => { + nock(userInfoEndpoint).get('').reply(200, { + id: 'id', + }); + + const connector = await createConnector({ getConfig }); + await expect(connector.getUserInfo({ code: 'code' }, getSessionMock)).rejects.toSatisfy( + (connectorError) => + (connectorError as ConnectorError).code === ConnectorErrorCodes.InvalidResponse + ); + }); + + it('throws SocialAccessTokenInvalid error if user info responded with 401', async () => { + nock(userInfoEndpoint).get('').reply(401); + + const connector = await createConnector({ getConfig }); + await expect(connector.getUserInfo({ code: 'code' }, getSessionMock)).rejects.toStrictEqual( + new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid) + ); + }); + + it('throws General error if user info responded with a non-401 error', async () => { + nock(userInfoEndpoint).get('').reply(422); + + const connector = await createConnector({ getConfig }); + await expect(connector.getUserInfo({ code: 'code' }, getSessionMock)).rejects.toStrictEqual( + new ConnectorError(ConnectorErrorCodes.General) + ); + }); +}); diff --git a/packages/connectors/connector-huggingface/src/index.ts b/packages/connectors/connector-huggingface/src/index.ts new file mode 100644 index 000000000..398b166f7 --- /dev/null +++ b/packages/connectors/connector-huggingface/src/index.ts @@ -0,0 +1,154 @@ +import { conditional, assert } from '@silverhand/essentials'; + +import type { + GetAuthorizationUri, + GetUserInfo, + SocialConnector, + CreateConnector, + GetConnectorConfig, +} from '@logto/connector-kit'; +import { + ConnectorError, + ConnectorErrorCodes, + validateConfig, + ConnectorType, + parseJson, +} from '@logto/connector-kit'; +import { + constructAuthorizationUri, + oauth2AuthResponseGuard, + requestTokenEndpoint, + TokenEndpointAuthMethod, +} from '@logto/connector-oauth'; +import ky, { HTTPError } from 'ky'; + +import { + authorizationEndpoint, + userInfoEndpoint, + defaultMetadata, + defaultTimeout, + tokenEndpoint, +} from './constant.js'; +import { + accessTokenResponseGuard, + huggingfaceConnectorConfigGuard, + userInfoResponseGuard, +} from './types.js'; + +const getAuthorizationUri = + (getConfig: GetConnectorConfig): GetAuthorizationUri => + async ({ state, redirectUri }, setSession) => { + const config = await getConfig(defaultMetadata.id); + validateConfig(config, huggingfaceConnectorConfigGuard); + + const { clientId, scope } = config; + + await setSession({ redirectUri }); + + return constructAuthorizationUri(authorizationEndpoint, { + responseType: 'code', + clientId, + scope: scope ?? 'profile', // Defaults to 'profile' if not provided + redirectUri, + state, + }); + }; + +const getUserInfo = + (getConfig: GetConnectorConfig): GetUserInfo => + async (data, getSession) => { + const authResponseResult = oauth2AuthResponseGuard.safeParse(data); + + if (!authResponseResult.success) { + throw new ConnectorError(ConnectorErrorCodes.AuthorizationFailed, data); + } + + const { code } = authResponseResult.data; + + const config = await getConfig(defaultMetadata.id); + validateConfig(config, huggingfaceConnectorConfigGuard); + + const { clientId, clientSecret } = config; + const { redirectUri } = await getSession(); + + if (!redirectUri) { + throw new ConnectorError(ConnectorErrorCodes.General, { + message: 'Cannot find `redirectUri` from connector session.', + }); + } + + const tokenResponse = await requestTokenEndpoint({ + tokenEndpoint, + tokenEndpointAuthOptions: { + method: TokenEndpointAuthMethod.ClientSecretBasic, + }, + tokenRequestBody: { + grantType: 'authorization_code', + code, + redirectUri, + clientId, + clientSecret, + }, + }); + + const parsedTokenResponse = accessTokenResponseGuard.safeParse(await tokenResponse.json()); + + if (!parsedTokenResponse.success) { + throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, parsedTokenResponse.error); + } + + const { access_token: accessToken, token_type: tokenType } = parsedTokenResponse.data; + + assert(accessToken, new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid)); + + try { + const userInfoResponse = await ky.get(userInfoEndpoint, { + headers: { + authorization: `${tokenType} ${accessToken}`, + }, + timeout: defaultTimeout, + }); + + const rawData = parseJson(await userInfoResponse.text()); + + const parsedUserInfoResponse = userInfoResponseGuard.safeParse(rawData); + + if (!parsedUserInfoResponse.success) { + throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, parsedUserInfoResponse.error); + } + + const { sub, picture, email, name } = parsedUserInfoResponse.data; + + return { + id: sub, + avatar: conditional(picture), + email: conditional(email), + name: conditional(name), + rawData, + }; + } catch (error: unknown) { + if (error instanceof HTTPError) { + const { response } = error; + + if (response.status === 401) { + throw new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid); + } + + throw new ConnectorError(ConnectorErrorCodes.General, await response.text()); + } + + throw error; + } + }; + +const createHuggingfaceConnector: CreateConnector = async ({ getConfig }) => { + return { + metadata: defaultMetadata, + type: ConnectorType.Social, + configGuard: huggingfaceConnectorConfigGuard, + getAuthorizationUri: getAuthorizationUri(getConfig), + getUserInfo: getUserInfo(getConfig), + }; +}; + +export default createHuggingfaceConnector; diff --git a/packages/connectors/connector-huggingface/src/types.ts b/packages/connectors/connector-huggingface/src/types.ts new file mode 100644 index 000000000..3a6a06644 --- /dev/null +++ b/packages/connectors/connector-huggingface/src/types.ts @@ -0,0 +1,36 @@ +import { z } from 'zod'; + +import { oauth2ConfigGuard } from '@logto/connector-oauth'; + +export const huggingfaceConnectorConfigGuard = oauth2ConfigGuard.pick({ + clientId: true, + clientSecret: true, + scope: true, +}); + +export type HuggingfaceConnectorConfigGuard = z.infer; + +export const accessTokenResponseGuard = z.object({ + access_token: z.string(), + scope: z.string(), + token_type: z.string(), +}); + +export type AccessTokenResponse = z.infer; + +export const userInfoResponseGuard = z.object({ + sub: z.string(), + name: z.string().optional().nullable(), + picture: z.string().optional().nullable(), + email: z.string().optional().nullable(), +}); + +export type UserInfoResponse = z.infer; + +export const authorizationCallbackErrorGuard = z.object({ + error: z.string(), + error_description: z.string(), + error_uri: z.string(), +}); + +export const authResponseGuard = z.object({ code: z.string() }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b8e4f967a..206291d5b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1122,6 +1122,79 @@ importers: specifier: ^1.4.0 version: 1.4.0(@types/node@20.11.20) + packages/connectors/connector-huggingface: + dependencies: + '@logto/connector-kit': + specifier: workspace:^3.0.0 + version: link:../../toolkit/connector-kit + '@logto/connector-oauth': + specifier: workspace:^1.2.0 + version: link:../connector-oauth2 + '@silverhand/essentials': + specifier: ^2.9.0 + version: 2.9.0 + ky: + specifier: ^1.2.3 + version: 1.2.3 + zod: + specifier: ^3.22.4 + version: 3.22.4 + devDependencies: + '@rollup/plugin-commonjs': + specifier: ^25.0.7 + version: 25.0.7(rollup@4.14.3) + '@rollup/plugin-json': + specifier: ^6.1.0 + version: 6.1.0(rollup@4.14.3) + '@rollup/plugin-node-resolve': + specifier: ^15.2.3 + version: 15.2.3(rollup@4.14.3) + '@rollup/plugin-typescript': + specifier: ^11.1.6 + version: 11.1.6(rollup@4.14.3)(typescript@5.3.3) + '@silverhand/eslint-config': + specifier: 6.0.1 + version: 6.0.1(eslint@8.57.0)(prettier@3.0.0)(typescript@5.3.3) + '@silverhand/ts-config': + specifier: 6.0.0 + version: 6.0.0(typescript@5.3.3) + '@types/node': + specifier: ^20.11.20 + version: 20.12.7 + '@types/supertest': + specifier: ^6.0.2 + version: 6.0.2 + '@vitest/coverage-v8': + specifier: ^1.4.0 + version: 1.4.0(vitest@1.4.0) + eslint: + specifier: ^8.56.0 + version: 8.57.0 + lint-staged: + specifier: ^15.0.2 + version: 15.0.2 + nock: + specifier: 14.0.0-beta.6 + version: 14.0.0-beta.6 + prettier: + specifier: ^3.0.0 + version: 3.0.0 + rollup: + specifier: ^4.12.0 + version: 4.14.3 + rollup-plugin-output-size: + specifier: ^1.3.0 + version: 1.3.0(rollup@4.14.3) + supertest: + specifier: ^7.0.0 + version: 7.0.0 + typescript: + specifier: ^5.3.3 + version: 5.3.3 + vitest: + specifier: ^1.4.0 + version: 1.4.0(@types/node@20.12.7) + packages/connectors/connector-kakao: dependencies: '@logto/connector-kit': @@ -4967,7 +5040,7 @@ packages: gensync: 1.0.0-beta.2 json5: 2.2.3 lodash: 4.17.21 - resolve: 1.22.2 + resolve: 1.22.8 semver: 5.7.2 source-map: 0.5.7 transitivePeerDependencies: @@ -5160,14 +5233,6 @@ packages: picocolors: 1.0.0 dev: true - /@babel/parser@7.20.3: - resolution: {integrity: sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.24.0 - dev: true - /@babel/parser@7.24.0: resolution: {integrity: sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==} engines: {node: '>=6.0.0'} @@ -5384,7 +5449,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.22.5 - '@babel/parser': 7.24.0 + '@babel/parser': 7.24.4 '@babel/types': 7.24.0 dev: true @@ -6244,7 +6309,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.11.20 + '@types/node': 20.12.7 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -6256,7 +6321,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.11.20 + '@types/node': 20.12.7 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -6277,14 +6342,14 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.20 + '@types/node': 20.12.7 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.8.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.11.20)(ts-node@10.9.2) + jest-config: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -6319,7 +6384,7 @@ packages: dependencies: '@jest/fake-timers': 29.5.0 '@jest/types': 29.6.3 - '@types/node': 20.11.20 + '@types/node': 20.12.7 jest-mock: 29.5.0 dev: true @@ -6329,7 +6394,7 @@ packages: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.20 + '@types/node': 20.12.7 jest-mock: 29.7.0 dev: true @@ -6356,7 +6421,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.11.20 + '@types/node': 20.12.7 jest-message-util: 29.5.0 jest-mock: 29.5.0 jest-util: 29.5.0 @@ -6368,7 +6433,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.11.20 + '@types/node': 20.12.7 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -6401,7 +6466,7 @@ packages: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.18 - '@types/node': 20.11.20 + '@types/node': 20.12.7 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -6448,7 +6513,7 @@ packages: resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jridgewell/trace-mapping': 0.3.18 + '@jridgewell/trace-mapping': 0.3.25 callsites: 3.1.0 graceful-fs: 4.2.11 dev: true @@ -6522,7 +6587,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 20.11.20 + '@types/node': 20.12.7 '@types/yargs': 16.0.4 chalk: 4.1.2 dev: true @@ -6534,7 +6599,7 @@ packages: '@jest/schemas': 29.0.0 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 20.11.20 + '@types/node': 20.12.7 '@types/yargs': 17.0.13 chalk: 4.1.2 dev: true @@ -6546,7 +6611,7 @@ packages: '@jest/schemas': 29.4.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 20.11.20 + '@types/node': 20.12.7 '@types/yargs': 17.0.13 chalk: 4.1.2 dev: true @@ -6558,7 +6623,7 @@ packages: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 20.11.20 + '@types/node': 20.12.7 '@types/yargs': 17.0.13 chalk: 4.1.2 dev: true @@ -7959,6 +8024,24 @@ packages: rollup: 4.12.0 dev: true + /@rollup/plugin-commonjs@25.0.7(rollup@4.14.3): + resolution: {integrity: sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.68.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.14.3) + commondir: 1.0.1 + estree-walker: 2.0.2 + glob: 8.1.0 + is-reference: 1.2.1 + magic-string: 0.30.7 + rollup: 4.14.3 + dev: true + /@rollup/plugin-json@6.1.0(rollup@4.12.0): resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} engines: {node: '>=14.0.0'} @@ -7972,6 +8055,19 @@ packages: rollup: 4.12.0 dev: true + /@rollup/plugin-json@6.1.0(rollup@4.14.3): + resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.14.3) + rollup: 4.14.3 + dev: true + /@rollup/plugin-node-resolve@15.2.3(rollup@4.12.0): resolution: {integrity: sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==} engines: {node: '>=14.0.0'} @@ -7990,6 +8086,24 @@ packages: rollup: 4.12.0 dev: true + /@rollup/plugin-node-resolve@15.2.3(rollup@4.14.3): + resolution: {integrity: sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.14.3) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-builtin-module: 3.2.1 + is-module: 1.0.0 + resolve: 1.22.2 + rollup: 4.14.3 + dev: true + /@rollup/plugin-typescript@11.1.6(rollup@4.12.0)(typescript@5.3.3): resolution: {integrity: sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==} engines: {node: '>=14.0.0'} @@ -8004,11 +8118,30 @@ packages: optional: true dependencies: '@rollup/pluginutils': 5.1.0(rollup@4.12.0) - resolve: 1.22.2 + resolve: 1.22.8 rollup: 4.12.0 typescript: 5.3.3 dev: true + /@rollup/plugin-typescript@11.1.6(rollup@4.14.3)(typescript@5.3.3): + resolution: {integrity: sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.14.0||^3.0.0||^4.0.0 + tslib: '*' + typescript: '>=3.7.0' + peerDependenciesMeta: + rollup: + optional: true + tslib: + optional: true + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.14.3) + resolve: 1.22.8 + rollup: 4.14.3 + typescript: 5.3.3 + dev: true + /@rollup/pluginutils@5.1.0(rollup@4.12.0): resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} engines: {node: '>=14.0.0'} @@ -8024,6 +8157,21 @@ packages: rollup: 4.12.0 dev: true + /@rollup/pluginutils@5.1.0(rollup@4.14.3): + resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@types/estree': 1.0.5 + estree-walker: 2.0.2 + picomatch: 2.3.1 + rollup: 4.14.3 + dev: true + /@rollup/rollup-android-arm-eabi@4.12.0: resolution: {integrity: sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==} cpu: [arm] @@ -9293,7 +9441,7 @@ packages: /@types/accepts@1.3.5: resolution: {integrity: sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==} dependencies: - '@types/node': 20.11.20 + '@types/node': 20.12.7 /@types/aria-query@5.0.1: resolution: {integrity: sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==} @@ -9302,8 +9450,8 @@ packages: /@types/babel__core@7.1.19: resolution: {integrity: sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==} dependencies: - '@babel/parser': 7.20.3 - '@babel/types': 7.20.2 + '@babel/parser': 7.24.4 + '@babel/types': 7.24.0 '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 '@types/babel__traverse': 7.18.2 @@ -9312,27 +9460,27 @@ packages: /@types/babel__generator@7.6.4: resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} dependencies: - '@babel/types': 7.20.2 + '@babel/types': 7.24.0 dev: true /@types/babel__template@7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: - '@babel/parser': 7.20.3 - '@babel/types': 7.20.2 + '@babel/parser': 7.24.4 + '@babel/types': 7.24.0 dev: true /@types/babel__traverse@7.18.2: resolution: {integrity: sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg==} dependencies: - '@babel/types': 7.20.2 + '@babel/types': 7.24.0 dev: true /@types/body-parser@1.19.2: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: '@types/connect': 3.4.35 - '@types/node': 20.11.20 + '@types/node': 20.12.7 /@types/caseless@0.12.5: resolution: {integrity: sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==} @@ -9364,7 +9512,7 @@ packages: /@types/connect@3.4.35: resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} dependencies: - '@types/node': 20.11.20 + '@types/node': 20.12.7 /@types/content-disposition@0.5.4: resolution: {integrity: sha512-0mPF08jn9zYI0n0Q/Pnz7C4kThdSt+6LD4amsrYDDpgBfrVWa3TcCOxKX1zkGgYniGagRv8heN2cbh+CAn+uuQ==} @@ -9372,7 +9520,7 @@ packages: /@types/conventional-commits-parser@5.0.0: resolution: {integrity: sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==} dependencies: - '@types/node': 20.11.20 + '@types/node': 20.12.7 dev: true /@types/cookiejar@2.1.5: @@ -9385,7 +9533,7 @@ packages: '@types/connect': 3.4.35 '@types/express': 4.17.13 '@types/keygrip': 1.0.2 - '@types/node': 20.11.20 + '@types/node': 20.12.7 /@types/debug@4.1.7: resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} @@ -9400,13 +9548,13 @@ packages: /@types/etag@1.8.1: resolution: {integrity: sha512-bsKkeSqN7HYyYntFRAmzcwx/dKW4Wa+KVMTInANlI72PWLQmOpZu96j0OqHZGArW4VQwCmJPteQlXaUDeOB0WQ==} dependencies: - '@types/node': 20.11.20 + '@types/node': 20.12.7 dev: true /@types/express-serve-static-core@4.17.26: resolution: {integrity: sha512-zeu3tpouA043RHxW0gzRxwCHchMgftE8GArRsvYT0ByDMbn19olQHx5jLue0LxWY6iYtXb7rXmuVtSkhy9YZvQ==} dependencies: - '@types/node': 20.11.20 + '@types/node': 20.12.7 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 @@ -9495,7 +9643,7 @@ packages: /@types/jsdom@20.0.0: resolution: {integrity: sha512-YfAchFs0yM1QPDrLm2VHe+WHGtqms3NXnXAMolrgrVP6fgBHHXy1ozAbo/dFtPNtZC/m66bPiCTWYmqp1F14gA==} dependencies: - '@types/node': 20.11.20 + '@types/node': 20.12.7 '@types/tough-cookie': 4.0.2 parse5: 7.1.1 dev: true @@ -9520,7 +9668,7 @@ packages: resolution: {integrity: sha512-nJSII/tOSvYCwk3yDEBJLHd8ctkt5CQFZ0j8ZBnHZ2x0hg24z9H1i38lWXA/5z0Ix0uitMW1jov+kVbQI1aNPQ==} dependencies: '@types/koa': 2.13.4 - '@types/node': 20.11.20 + '@types/node': 20.12.7 dev: true /@types/koa-logger@3.1.2: @@ -9551,7 +9699,7 @@ packages: '@types/http-errors': 1.8.2 '@types/keygrip': 1.0.2 '@types/koa-compose': 3.2.5 - '@types/node': 20.11.20 + '@types/node': 20.12.7 /@types/koa@2.15.0: resolution: {integrity: sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g==} @@ -9630,6 +9778,7 @@ packages: resolution: {integrity: sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==} dependencies: undici-types: 5.26.5 + dev: true /@types/node@20.12.7: resolution: {integrity: sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==} @@ -9639,7 +9788,7 @@ packages: /@types/nodemailer@6.4.7: resolution: {integrity: sha512-f5qCBGAn/f0qtRcd4SEn88c8Fp3Swct1731X4ryPKqS61/A3LmmzN8zaEz7hneJvpjFbUUgY7lru/B/7ODTazg==} dependencies: - '@types/node': 20.11.20 + '@types/node': 20.12.7 dev: true /@types/normalize-package-data@2.4.1: @@ -9664,14 +9813,14 @@ packages: /@types/pg@8.11.2: resolution: {integrity: sha512-G2Mjygf2jFMU/9hCaTYxJrwdObdcnuQde1gndooZSOHsNSaCehAuwc7EIuSA34Do8Jx2yZ19KtvW8P0j4EuUXw==} dependencies: - '@types/node': 20.11.20 + '@types/node': 20.12.7 pg-protocol: 1.6.0 pg-types: 4.0.2 /@types/pg@8.6.6: resolution: {integrity: sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==} dependencies: - '@types/node': 20.11.20 + '@types/node': 20.12.7 pg-protocol: 1.6.0 pg-types: 2.2.0 dev: true @@ -9687,7 +9836,7 @@ packages: /@types/qrcode@1.5.2: resolution: {integrity: sha512-W4KDz75m7rJjFbyCctzCtRzZUj+PrUHV+YjqDp50sSRezTbrtEAIq2iTzC6lISARl3qw+8IlcCyljdcVJE0Wug==} dependencies: - '@types/node': 20.11.20 + '@types/node': 20.12.7 dev: true /@types/qs@6.9.7: @@ -9793,7 +9942,7 @@ packages: resolution: {integrity: sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==} dependencies: '@types/mime': 1.3.2 - '@types/node': 20.11.20 + '@types/node': 20.12.7 /@types/shimmer@1.0.2: resolution: {integrity: sha512-dKkr1bTxbEsFlh2ARpKzcaAmsYixqt9UyCdoEZk8rHyE4iQYcDCyvSjDSf7JUWJHlJiTtbIoQjxKh6ViywqDAg==} @@ -9838,7 +9987,7 @@ packages: /@types/through@0.0.30: resolution: {integrity: sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==} dependencies: - '@types/node': 20.11.20 + '@types/node': 20.12.7 dev: true /@types/tough-cookie@4.0.2: @@ -10037,7 +10186,7 @@ packages: strip-literal: 2.0.0 test-exclude: 6.0.0 v8-to-istanbul: 9.2.0 - vitest: 1.4.0(@types/node@20.10.4) + vitest: 1.4.0(@types/node@20.12.7) transitivePeerDependencies: - supports-color dev: true @@ -10645,7 +10794,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/template': 7.18.10 - '@babel/types': 7.20.2 + '@babel/types': 7.24.0 '@types/babel__core': 7.1.19 '@types/babel__traverse': 7.18.2 dev: true @@ -11781,6 +11930,17 @@ packages: /dayjs@1.11.6: resolution: {integrity: sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ==} + /debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: true + /debug@3.2.7(supports-color@5.5.0): resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -12545,7 +12705,7 @@ packages: /eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} dependencies: - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7 is-core-module: 2.13.1 resolve: 1.22.8 transitivePeerDependencies: @@ -12597,7 +12757,7 @@ packages: optional: true dependencies: '@typescript-eslint/parser': 7.7.0(eslint@8.57.0)(typescript@5.3.3) - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0) @@ -12651,7 +12811,7 @@ packages: array.prototype.findlastindex: 1.2.5 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7 doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 @@ -13420,6 +13580,7 @@ packages: /function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true /function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -13882,6 +14043,7 @@ packages: engines: {node: '>= 0.4.0'} dependencies: function-bind: 1.1.1 + dev: true /hash-wasm@4.9.0: resolution: {integrity: sha512-7SW7ejyfnRxuOc7ptQHSf4LDoZaWOivfzqw+5rpcQku0nHfmicPKE51ra9BiRLAmT8+gGLestr1XroUkqdjL6w==} @@ -14517,12 +14679,12 @@ packages: resolution: {integrity: sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==} dependencies: has: 1.0.3 + dev: true /is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: hasown: 2.0.2 - dev: true /is-data-view@1.0.1: resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} @@ -14831,7 +14993,7 @@ packages: engines: {node: '>=8'} dependencies: '@babel/core': 7.24.4 - '@babel/parser': 7.24.0 + '@babel/parser': 7.24.4 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 @@ -14844,7 +15006,7 @@ packages: engines: {node: '>=10'} dependencies: '@babel/core': 7.24.4 - '@babel/parser': 7.24.0 + '@babel/parser': 7.24.4 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 7.6.0 @@ -14927,7 +15089,7 @@ packages: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.20 + '@types/node': 20.12.7 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.1 @@ -15072,47 +15234,6 @@ packages: - supports-color dev: true - /jest-config@29.7.0(@types/node@20.11.20)(ts-node@10.9.2): - resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - dependencies: - '@babel/core': 7.24.4 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.11.20 - babel-jest: 29.7.0(@babel/core@7.24.4) - chalk: 4.1.2 - ci-info: 3.8.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.5 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - ts-node: 10.9.2(@swc/core@1.3.52)(@types/node@20.12.7)(typescript@5.3.3) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - dev: true - /jest-config@29.7.0(@types/node@20.12.7)(ts-node@10.9.2): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -15211,7 +15332,7 @@ packages: '@jest/fake-timers': 29.5.0 '@jest/types': 29.6.3 '@types/jsdom': 20.0.0 - '@types/node': 20.11.20 + '@types/node': 20.12.7 jest-mock: 29.5.0 jest-util: 29.5.0 jsdom: 20.0.2 @@ -15228,7 +15349,7 @@ packages: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.20 + '@types/node': 20.12.7 jest-mock: 29.7.0 jest-util: 29.7.0 dev: true @@ -15259,7 +15380,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.5 - '@types/node': 20.11.20 + '@types/node': 20.12.7 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.10 @@ -15278,7 +15399,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.5 - '@types/node': 20.11.20 + '@types/node': 20.12.7 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -15348,7 +15469,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.11.20 + '@types/node': 20.12.7 jest-util: 29.5.0 dev: true @@ -15357,7 +15478,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.11.20 + '@types/node': 20.12.7 jest-util: 29.7.0 dev: true @@ -15418,7 +15539,7 @@ packages: jest-pnp-resolver: 1.2.2(jest-resolve@29.7.0) jest-util: 29.7.0 jest-validate: 29.7.0 - resolve: 1.22.2 + resolve: 1.22.8 resolve.exports: 2.0.1 slash: 3.0.0 dev: true @@ -15432,7 +15553,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.20 + '@types/node': 20.12.7 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -15463,7 +15584,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.20 + '@types/node': 20.12.7 chalk: 4.1.2 cjs-module-lexer: 1.2.2 collect-v8-coverage: 1.0.1 @@ -15529,7 +15650,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.11.20 + '@types/node': 20.12.7 chalk: 4.1.2 ci-info: 3.8.0 graceful-fs: 4.2.11 @@ -15541,7 +15662,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.11.20 + '@types/node': 20.12.7 chalk: 4.1.2 ci-info: 3.8.0 graceful-fs: 4.2.11 @@ -15566,7 +15687,7 @@ packages: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.20 + '@types/node': 20.12.7 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -15588,7 +15709,7 @@ packages: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 20.11.20 + '@types/node': 20.12.7 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -17436,7 +17557,7 @@ packages: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: hosted-git-info: 2.8.9 - resolve: 1.22.2 + resolve: 1.22.8 semver: 5.7.2 validate-npm-package-license: 3.0.4 dev: true @@ -19357,7 +19478,7 @@ packages: dependencies: debug: 4.3.4 module-details-from-path: 1.0.3 - resolve: 1.22.2 + resolve: 1.22.8 transitivePeerDependencies: - supports-color dev: false @@ -19421,6 +19542,7 @@ packages: is-core-module: 2.12.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + dev: true /resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} @@ -19429,7 +19551,6 @@ packages: is-core-module: 2.13.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: true /resolve@2.0.0-next.5: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} @@ -19528,6 +19649,21 @@ packages: rollup: 4.12.0 dev: true + /rollup-plugin-output-size@1.3.0(rollup@4.14.3): + resolution: {integrity: sha512-OrTfhj8mxFAO4Yp7OEWwI388uhsiBNDCpfD6tOmcqPfNs5+CWZPDtVmTRZrmXMWfv8skWCOm5ToO4UPy7eRqYg==} + engines: {node: '>=14.16.0'} + peerDependencies: + rollup: ^2.0.0 || ^3.0.0 || ^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + colorette: 2.0.20 + gzip-size: 7.0.0 + pretty-bytes: 6.1.1 + rollup: 4.14.3 + dev: true + /rollup@4.12.0: resolution: {integrity: sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -21258,6 +21394,27 @@ packages: - terser dev: true + /vite-node@1.4.0(@types/node@20.12.7): + resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + pathe: 1.1.2 + picocolors: 1.0.0 + vite: 5.2.9(@types/node@20.12.7) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vite@5.2.9(@types/node@20.10.4): resolution: {integrity: sha512-uOQWfuZBlc6Y3W/DTuQ1Sr+oIXWvqljLvS881SVmAj00d5RdgShLcuXWxseWPd4HXwiYBFW/vXHfKFeqj9uQnw==} engines: {node: ^18.0.0 || >=20.0.0} @@ -21330,6 +21487,42 @@ packages: fsevents: 2.3.3 dev: true + /vite@5.2.9(@types/node@20.12.7): + resolution: {integrity: sha512-uOQWfuZBlc6Y3W/DTuQ1Sr+oIXWvqljLvS881SVmAj00d5RdgShLcuXWxseWPd4HXwiYBFW/vXHfKFeqj9uQnw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.12.7 + esbuild: 0.20.2 + postcss: 8.4.38 + rollup: 4.14.3 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /vitest@1.4.0(@types/node@20.10.4): resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==} engines: {node: ^18.0.0 || >=20.0.0} @@ -21442,6 +21635,62 @@ packages: - terser dev: true + /vitest@1.4.0(@types/node@20.12.7): + resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 1.4.0 + '@vitest/ui': 1.4.0 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@types/node': 20.12.7 + '@vitest/expect': 1.4.0 + '@vitest/runner': 1.4.0 + '@vitest/snapshot': 1.4.0 + '@vitest/spy': 1.4.0 + '@vitest/utils': 1.4.0 + acorn-walk: 8.3.2 + chai: 4.4.1 + debug: 4.3.4 + execa: 8.0.1 + local-pkg: 0.5.0 + magic-string: 0.30.7 + pathe: 1.1.2 + picocolors: 1.0.0 + std-env: 3.7.0 + strip-literal: 2.0.0 + tinybench: 2.6.0 + tinypool: 0.8.2 + vite: 5.2.9(@types/node@20.12.7) + vite-node: 1.4.0(@types/node@20.12.7) + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /void-elements@3.1.0: resolution: {integrity: sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=} engines: {node: '>=0.10.0'}