feat: Clerk Middleware (#151)

* initial commit

* wip

* Update README.md

* tests(clerk-auth): Setup testing and added first test

* docs(clerk-auth): Update README.md

* tests(clerk-auth): Added some more tests

* chore(clerk-auth): Added refernece in tests

* ci(clerk-atuh): Add Clerk Auth workflow

* use `env` helper to get enviroment variables

---------

Co-authored-by: Yusuke Wada <yusuke@kamawada.com>
pull/213/head
Vaggelis Yfantis 2023-10-27 00:50:22 -07:00 committed by GitHub
parent 90d3e84831
commit a18da2a545
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 640 additions and 6 deletions

View File

@ -0,0 +1,5 @@
---
'@hono/clerk-auth': major
---
Added Clerk Middleware

View File

@ -0,0 +1,25 @@
name: ci-clerk-auth
on:
push:
branches: [main]
paths:
- 'packages/clerk-auth/**'
pull_request:
branches: ['*']
paths:
- 'packages/clerk-auth/**'
jobs:
ci:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./packages/clerk-auth
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 18.x
- run: yarn install --frozen-lockfile
- run: yarn build
- run: yarn test

View File

@ -15,6 +15,7 @@
"build:sentry": "yarn workspace @hono/sentry build",
"build:firebase-auth": "yarn workspace @hono/firebase-auth build",
"build:trpc-server": "yarn workspace @hono/trpc-server build",
"build:clerk-auth": "yarn workspace @hono/clerk-auth build",
"build:typebox-validator": "yarn workspace @hono/typebox-validator build",
"build:medley-router": "yarn workspace @hono/medley-router build",
"build:valibot-validator": "yarn workspace @hono/valibot-validator build",

View File

View File

@ -0,0 +1,79 @@
# Clerk middleware for Hono
This is a [Clerk](https://clerk.com) third-party middleware for [Hono](https://github.com/honojs/hono).
This middleware can be used to inject the active Clerk session into the request context.
## Installation
```plain
npm i hono @hono/clerk-auth @clerk/backend
```
## Configuration
Before starting using the middleware you must set the following environment variables:
```plain
CLERK_SECRET_KEY=<You-secret-key>
CLERK_PUBLISHABLE_KEY=<Your-publishable-key>
```
## How to Use
```ts
import { clerkMiddleware, getAuth } from '@hono/clerk-auth'
import { Hono } from 'hono'
const app = new Hono()
app.use('*', clerkMiddleware())
app.get('/', (c) => {
const auth = getAuth(c)
if (!auth?.userId) {
return c.json({
message: 'You are not logged in.'
})
}
return c.json({
message: 'You are logged in!',
userId: auth.userId
})
})
export default app
```
## Accessing instance of Backend API client
```ts
import { clerkMiddleware, getAuth } from '@hono/clerk-auth'
import { Hono } from 'hono'
const app = new Hono()
app.use('*', clerkMiddleware())
app.get('/', async (c) => {
const clerk = c.get('clerk')
try {
const user = await clerkClient.users.getUser('user_id_....')
return c.json({
user,
})
} catch (e) {
return c.json({
message: 'User not found.'
}, 404)
}
})
export default app
```
## Author
Vaggelis Yfantis <https://github.com/octoper>

View File

@ -0,0 +1,12 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
displayName: 'hono',
injectGlobals: true,
testMatch: ['**/test/**/*.+(ts|tsx|js)', '**/src/**/(*.)+(spec|test).+(ts|tsx|js)'],
transform: { '^.+\\.m?tsx?$': 'ts-jest' },
testPathIgnorePatterns: ['/node_modules/', '/jest/'],
moduleNameMapper: {
'#crypto': '@clerk/backend/dist/runtime/node/crypto.js',
'#fetch': '@clerk/backend/dist/runtime/node/fetch.js',
},
}

View File

@ -0,0 +1,36 @@
{
"name": "@hono/clerk-auth",
"version": "0.0.0",
"description": "A third-party Clerk auth middleware for Hono",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"test": "jest",
"build": "rimraf dist && tsc",
"prerelease": "yarn build && yarn test",
"release": "yarn publish"
},
"license": "MIT",
"private": false,
"publishConfig": {
"registry": "https://registry.npmjs.org",
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/honojs/middleware.git"
},
"homepage": "https://github.com/honojs/middleware",
"peerDependencies": {
"@clerk/backend": "0.30.*",
"hono": "3.*"
},
"devDependencies": {
"@clerk/backend": "^0.30.1",
"hono": "^3.7.3",
"node-fetch-native": "^1.4.0"
}
}

View File

@ -0,0 +1,89 @@
import type { ClerkOptions } from '@clerk/backend'
import { Clerk, createIsomorphicRequest, constants } from '@clerk/backend'
import type { Context, MiddlewareHandler } from 'hono'
import { env } from 'hono/adapter'
type ClerkAuth = Awaited<ReturnType<ReturnType<typeof Clerk>['authenticateRequest']>>['toAuth']
declare module 'hono' {
interface ContextVariableMap {
clerk: ReturnType<typeof Clerk>
clerkAuth: ReturnType<ClerkAuth>
}
}
export const getAuth = (c: Context) => {
return c.get('clerkAuth')
}
type ClerkEnv = {
CLERK_SECRET_KEY: string
CLERK_PUBLISHABLE_KEY: string
CLERK_API_URL: string
CLERK_API_VERSION: string
CLERK_FRONTEND_API: string
}
export const clerkMiddleware = (options?: ClerkOptions): MiddlewareHandler => {
return async (c, next) => {
const clerkEnv = env<ClerkEnv>(c)
const { secretKey, publishableKey, apiUrl, apiVersion, ...rest } = options || {
secretKey: clerkEnv.CLERK_SECRET_KEY || '',
publishableKey: clerkEnv.CLERK_PUBLISHABLE_KEY || '',
apiUrl: clerkEnv.CLERK_API_URL || 'https://api.clerk.dev',
apiVersion: clerkEnv.CLERK_API_VERSION || 'v1',
}
const frontendApi = clerkEnv.CLERK_FRONTEND_API || ''
if (!secretKey) {
throw new Error('Missing Clerk Secret key')
}
if (!publishableKey) {
throw new Error('Missing Clerk Publishable key')
}
const clerkClient = Clerk({
...rest,
apiUrl,
apiVersion,
secretKey,
publishableKey,
})
const requestState = await clerkClient.authenticateRequest({
...rest,
secretKey,
publishableKey,
request: createIsomorphicRequest((Request) => {
return new Request(c.req.url, {
method: c.req.method,
headers: c.req.raw.headers,
})
}),
})
// Interstitial cases
if (requestState.isUnknown) {
c.header(constants.Headers.AuthReason, requestState.reason)
c.header(constants.Headers.AuthMessage, requestState.message)
return c.body(null, 401)
}
if (requestState.isInterstitial) {
const interstitialHtmlPage = clerkClient.localInterstitial({
publishableKey,
frontendApi,
})
c.header(constants.Headers.AuthReason, requestState.reason)
c.header(constants.Headers.AuthMessage, requestState.message)
return c.html(interstitialHtmlPage, 401)
}
c.set('clerkAuth', requestState.toAuth())
c.set('clerk', clerkClient)
await next()
}
}

View File

@ -0,0 +1,217 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Hono } from 'hono'
import { clerkMiddleware, getAuth } from '../src'
const EnvVariables = {
CLERK_SECRET_KEY: 'TEST_API_KEY',
CLERK_PUBLISHABLE_KEY: 'TEST_API_KEY',
}
const authenticateRequestMock = jest.fn()
const localInterstitialMock = jest.fn()
jest.mock('@clerk/backend', () => {
return {
...jest.requireActual('@clerk/backend'),
Clerk: () => {
return {
authenticateRequest: (...args: any) => authenticateRequestMock(...args),
localInterstitial: (...args: any) => localInterstitialMock(...args),
}
},
}
})
// Test are based on Clerk's test suite for Fastify plugin - https://github.com/clerkinc/javascript/blob/main/packages/fastify/src/withClerkMiddleware.test.ts
describe('clerkMiddleware()', () => {
beforeEach(() => {
process.env.CLERK_SECRET_KEY = EnvVariables.CLERK_SECRET_KEY
process.env.CLERK_PUBLISHABLE_KEY = EnvVariables.CLERK_PUBLISHABLE_KEY
jest.clearAllMocks()
jest.restoreAllMocks()
})
afterEach(() => {
jest.clearAllMocks()
})
test('handles signin with Authorization Bearer', async () => {
authenticateRequestMock.mockResolvedValue({
isUnknown: false,
isInterstitial: false,
isSignedIn: true,
toAuth: () => 'mockedAuth',
})
const app = new Hono()
app.use('*', clerkMiddleware())
app.get('/', (ctx) => {
const auth = getAuth(ctx)
return ctx.json({ auth })
})
const req = new Request('http://localhost/', {
headers: {
Authorization: 'Bearer deadbeef',
Origin: 'http://origin.com',
Host: 'host.com',
'X-Forwarded-Port': '1234',
'X-Forwarded-Host': 'forwarded-host.com',
Referer: 'referer.com',
'User-Agent':
'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36',
},
})
const response = await app.request(req)
expect(response.status).toEqual(200)
expect(await response.json()).toEqual({ auth: 'mockedAuth' })
expect(authenticateRequestMock).toBeCalledWith(
expect.objectContaining({
secretKey: EnvVariables.CLERK_SECRET_KEY,
publishableKey: EnvVariables.CLERK_PUBLISHABLE_KEY,
request: expect.any(Request),
})
)
})
test('handles signin with cookie', async () => {
authenticateRequestMock.mockResolvedValue({
isUnknown: false,
isInterstitial: false,
isSignedIn: true,
toAuth: () => 'mockedAuth',
})
const app = new Hono()
app.use('*', clerkMiddleware())
app.get('/', (ctx) => {
const auth = getAuth(ctx)
return ctx.json({ auth })
})
const req = new Request('http://localhost/', {
headers: {
cookie: '_gcl_au=value1; ko_id=value2; __session=deadbeef; __client_uat=1675692233',
Origin: 'http://origin.com',
Host: 'host.com',
'X-Forwarded-Port': '1234',
'X-Forwarded-Host': 'forwarded-host.com',
Referer: 'referer.com',
'User-Agent':
'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36',
},
})
const response = await app.request(req)
expect(response.status).toEqual(200)
expect(await response.json()).toEqual({ auth: 'mockedAuth' })
expect(authenticateRequestMock).toBeCalledWith(
expect.objectContaining({
secretKey: EnvVariables.CLERK_SECRET_KEY,
publishableKey: EnvVariables.CLERK_PUBLISHABLE_KEY,
request: expect.any(Request),
})
)
})
test('handles unknown case by terminating the request with empty response and 401 http code', async () => {
authenticateRequestMock.mockResolvedValue({
isUnknown: true,
isInterstitial: false,
isSignedIn: false,
reason: 'auth-reason',
message: 'auth-message',
toAuth: () => 'mockedAuth',
})
const app = new Hono()
app.use('*', clerkMiddleware())
app.get('/', (ctx) => {
const auth = getAuth(ctx)
return ctx.json({ auth })
})
const req = new Request('http://localhost/', {
headers: {
cookie: '_gcl_au=value1; ko_id=value2; __session=deadbeef; __client_uat=1675692233',
},
})
const response = await app.request(req)
expect(response.status).toEqual(401)
expect(response.headers.get('x-clerk-auth-reason')).toEqual('auth-reason')
expect(response.headers.get('x-clerk-auth-message')).toEqual('auth-message')
expect(await response.text()).toEqual('')
})
test('handles interstitial case by terminating the request with interstitial html page and 401 http code', async () => {
authenticateRequestMock.mockResolvedValue({
isUnknown: false,
isInterstitial: true,
isSignedIn: false,
reason: 'auth-reason',
message: 'auth-message',
toAuth: () => 'mockedAuth',
})
localInterstitialMock.mockReturnValue('<html><body>Interstitial</body></html>')
const app = new Hono()
app.use('*', clerkMiddleware())
app.get('/', (ctx) => {
const auth = getAuth(ctx)
return ctx.json({ auth })
})
const req = new Request('http://localhost/', {
headers: {
cookie: '_gcl_au=value1; ko_id=value2; __session=deadbeef; __client_uat=1675692233',
},
})
const response = await app.request(req)
expect(response.status).toEqual(401)
expect(response.headers.get('content-type')).toMatch('text/html')
expect(response.headers.get('x-clerk-auth-reason')).toEqual('auth-reason')
expect(response.headers.get('x-clerk-auth-message')).toEqual('auth-message')
expect(await response.text()).toEqual('<html><body>Interstitial</body></html>')
})
test('handles signout case by populating the req.auth', async () => {
authenticateRequestMock.mockResolvedValue({
isUnknown: false,
isInterstitial: false,
isSignedIn: false,
toAuth: () => 'mockedAuth',
})
const app = new Hono()
app.use('*', clerkMiddleware())
app.get('/', (ctx) => {
const auth = getAuth(ctx)
return ctx.json({ auth })
})
const req = new Request('http://localhost/', {
headers: {
Authorization: 'Bearer deadbeef',
},
})
const response = await app.request(req)
expect(response.status).toEqual(200)
expect(await response.json()).toEqual({ auth: 'mockedAuth' })
expect(authenticateRequestMock).toBeCalledWith(
expect.objectContaining({
secretKey: EnvVariables.CLERK_SECRET_KEY,
publishableKey: EnvVariables.CLERK_PUBLISHABLE_KEY,
request: expect.any(Request),
})
)
})
})

View File

@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist",
},
"include": [
"src/**/*.ts"
],
}

172
yarn.lock
View File

@ -560,6 +560,27 @@
human-id "^1.0.2"
prettier "^2.7.1"
"@clerk/backend@^0.30.1":
version "0.30.1"
resolved "https://registry.yarnpkg.com/@clerk/backend/-/backend-0.30.1.tgz#23179c68f0ce8a94d99bc2e8c5000cf896c8e577"
integrity sha512-+769bWE89Ejdy4ddu4wF/yBZypGVA5G/vAerJsXv/sdos3rSFtSghLx5OMdatn/2XqZUnrZoQGLtUz5ZxzDJXg==
dependencies:
"@clerk/types" "^3.53.0"
"@peculiar/webcrypto" "1.4.1"
"@types/node" "16.18.6"
cookie "0.5.0"
deepmerge "4.2.2"
node-fetch-native "1.0.1"
snakecase-keys "5.4.4"
tslib "2.4.1"
"@clerk/types@^3.53.0":
version "3.53.0"
resolved "https://registry.yarnpkg.com/@clerk/types/-/types-3.53.0.tgz#5a7436e0ff89134401edafef7d50557e626a928f"
integrity sha512-V+9BvKAo9f2ShgP+RpQ/MgJTbx4ayk3sYiFbMtLYlmtLCsn0xwglfP7i++7640d6+MAtCnMTAcrTp61IvUxSFw==
dependencies:
csstype "3.1.1"
"@cloudflare/workers-types@^3.14.0", "@cloudflare/workers-types@^3.14.1":
version "3.19.0"
resolved "https://registry.yarnpkg.com/@cloudflare/workers-types/-/workers-types-3.19.0.tgz#f3791b80b23f7cf0072f2e67e98aa37801204b6c"
@ -1621,6 +1642,33 @@
resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz#ba07b864a3c955f061aa30ea3ef7f4ae4449794a"
integrity sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA==
"@peculiar/asn1-schema@^2.3.0", "@peculiar/asn1-schema@^2.3.6":
version "2.3.6"
resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz#3dd3c2ade7f702a9a94dfb395c192f5fa5d6b922"
integrity sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA==
dependencies:
asn1js "^3.0.5"
pvtsutils "^1.3.2"
tslib "^2.4.0"
"@peculiar/json-schema@^1.1.12":
version "1.1.12"
resolved "https://registry.yarnpkg.com/@peculiar/json-schema/-/json-schema-1.1.12.tgz#fe61e85259e3b5ba5ad566cb62ca75b3d3cd5339"
integrity sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==
dependencies:
tslib "^2.0.0"
"@peculiar/webcrypto@1.4.1":
version "1.4.1"
resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.1.tgz#821493bd5ad0f05939bd5f53b28536f68158360a"
integrity sha512-eK4C6WTNYxoI7JOabMoZICiyqRRtJB220bh0Mbj5RwRycleZf9BPyZoxsTvpP0FpmVS2aS13NKOuh5/tN3sIRw==
dependencies:
"@peculiar/asn1-schema" "^2.3.0"
"@peculiar/json-schema" "^1.1.12"
pvtsutils "^1.3.2"
tslib "^2.4.1"
webcrypto-core "^1.7.4"
"@pkgjs/parseargs@^0.11.0":
version "0.11.0"
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
@ -2093,6 +2141,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.1.tgz#e8a83f1aa8b649377bb1fb5d7bac5cb90e784dfe"
integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==
"@types/node@16.18.6":
version "16.18.6"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.6.tgz#87846192fd51b693368fad3e99123169225621d4"
integrity sha512-vmYJF0REqDyyU0gviezF/KHq/fYaUbFhkcNbQCuPGFQj6VTbXuHZoxs/Y7mutWe73C8AC6l9fFu8mSYiBAqkGA==
"@types/node@^12.7.1":
version "12.20.55"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240"
@ -2636,6 +2689,15 @@ asn1@~0.2.3:
dependencies:
safer-buffer "~2.1.0"
asn1js@^3.0.1, asn1js@^3.0.5:
version "3.0.5"
resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.5.tgz#5ea36820443dbefb51cc7f88a2ebb5b462114f38"
integrity sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==
dependencies:
pvtsutils "^1.3.2"
pvutils "^1.1.3"
tslib "^2.4.0"
assert-plus@1.0.0, assert-plus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
@ -3755,6 +3817,11 @@ csso@^5.0.5:
dependencies:
css-tree "~2.2.0"
csstype@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9"
integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==
csstype@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
@ -3908,6 +3975,11 @@ deep-is@^0.1.3, deep-is@~0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
deepmerge@4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
deepmerge@^4.2.2:
version "4.3.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
@ -4117,6 +4189,14 @@ domutils@^3.0.1:
domelementtype "^2.3.0"
domhandler "^5.0.3"
dot-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==
dependencies:
no-case "^3.0.4"
tslib "^2.0.3"
dot-prop@^5.2.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88"
@ -5869,6 +5949,11 @@ hono@^3.5.8:
resolved "https://registry.yarnpkg.com/hono/-/hono-3.5.8.tgz#9bbc412f5a54183cf2a81a36a9b9ea56da10f785"
integrity sha512-ZipTmGfHm43q5QOEBGog2wyejyNUcicjPt0BLDQ8yz9xij/y9RYXRpR1YPxMpQqeyNM7isvpsIAe9Ems51Wq0Q==
hono@^3.7.3:
version "3.7.3"
resolved "https://registry.yarnpkg.com/hono/-/hono-3.7.3.tgz#01fd88360e9a431235110197fbb5a998131e104e"
integrity sha512-BQHdLPXb30hQ9k+04byeSi4QMHk20U1GUq0nT5kGUCGZtxeYhAS7mUJ1wgjn4SCvgiw1rcc6oBOAlwJQ7jQymA==
hono@^3.7.2:
version "3.7.2"
resolved "https://registry.yarnpkg.com/hono/-/hono-3.7.2.tgz#c3839d7ffbb5120850b2b926363d065020f4d18c"
@ -8003,6 +8088,13 @@ loupe@^2.3.1, loupe@^2.3.6:
dependencies:
get-func-name "^2.0.0"
lower-case@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
dependencies:
tslib "^2.0.3"
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
@ -8104,7 +8196,7 @@ map-obj@^1.0.0:
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==
map-obj@^4.0.0:
map-obj@^4.0.0, map-obj@^4.1.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a"
integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==
@ -8945,6 +9037,14 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
no-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==
dependencies:
lower-case "^2.0.2"
tslib "^2.0.3"
node-abi@^3.3.0:
version "3.47.0"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.47.0.tgz#6cbfa2916805ae25c2b7156ca640131632eb05e8"
@ -8964,6 +9064,16 @@ node-emoji@^1.11.0:
dependencies:
lodash "^4.17.21"
node-fetch-native@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.0.1.tgz#1dfe78f57545d07e07016b7df4c0cb9d2ff416c7"
integrity sha512-VzW+TAk2wE4X9maiKMlT+GsPU4OMmR1U9CrHSmd3DFLn2IcZ9VJ6M6BBugGfYUnPCLSYxXdZy17M0BEJyhUTwg==
node-fetch-native@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.4.0.tgz#fbe8ac033cb6aa44bd106b5e4fd2b6277ba70fa1"
integrity sha512-F5kfEj95kX8tkDhUCYdV8dg3/8Olx/94zB8+ZNthFs6Bz31UpUi8Xh40TN3thLwXgrwXry1pEg9lJ++tLWTcqA==
node-fetch@^2.5.0, node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@^2.6.9:
version "2.6.11"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.11.tgz#cde7fc71deef3131ef80a738919f999e6edfff25"
@ -10006,6 +10116,18 @@ pure-rand@^6.0.0:
resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.2.tgz#a9c2ddcae9b68d736a8163036f088a2781c8b306"
integrity sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==
pvtsutils@^1.3.2:
version "1.3.5"
resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.5.tgz#b8705b437b7b134cd7fd858f025a23456f1ce910"
integrity sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==
dependencies:
tslib "^2.6.1"
pvutils@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.3.tgz#f35fc1d27e7cd3dfbd39c0826d173e806a03f5a3"
integrity sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==
qs@6.11.0:
version "6.11.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
@ -10728,6 +10850,23 @@ smartwrap@^2.0.2:
wcwidth "^1.0.1"
yargs "^15.1.0"
snake-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c"
integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==
dependencies:
dot-case "^3.0.4"
tslib "^2.0.3"
snakecase-keys@5.4.4:
version "5.4.4"
resolved "https://registry.yarnpkg.com/snakecase-keys/-/snakecase-keys-5.4.4.tgz#28745b0175863ffc292ad97d96fe4e8e288a87a2"
integrity sha512-YTywJG93yxwHLgrYLZjlC75moVEX04LZM4FHfihjHe1FCXm+QaLOFfSf535aXOAd0ArVQMWUAe8ZPm4VtWyXaA==
dependencies:
map-obj "^4.1.0"
snake-case "^3.0.4"
type-fest "^2.5.2"
socks-proxy-agent@5, socks-proxy-agent@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz#032fb583048a29ebffec2e6a73fca0761f48177e"
@ -11522,21 +11661,26 @@ tsconfig-paths@^3.14.1:
minimist "^1.2.6"
strip-bom "^3.0.0"
tslib@2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e"
integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==
tslib@^1.8.1, tslib@^1.9.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.0, tslib@^2.0.3, tslib@^2.4.0, tslib@^2.4.1, "tslib@^2.4.1 || ^1.9.3", tslib@^2.6.1:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
tslib@^2.0.1, tslib@^2.1.0, tslib@^2.5.0:
version "2.5.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913"
integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==
"tslib@^2.4.1 || ^1.9.3":
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
tsup@^7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/tsup/-/tsup-7.2.0.tgz#bb24c0d5e436477900c712e42adc67200607303c"
@ -11648,6 +11792,11 @@ type-fest@^0.8.1:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
type-fest@^2.5.2:
version "2.19.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b"
integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==
type-fest@^3.0.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.12.0.tgz#4ce26edc1ccc59fc171e495887ef391fe1f5280e"
@ -12148,6 +12297,17 @@ wcwidth@^1.0.1:
dependencies:
defaults "^1.0.3"
webcrypto-core@^1.7.4:
version "1.7.7"
resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.7.tgz#06f24b3498463e570fed64d7cab149e5437b162c"
integrity sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g==
dependencies:
"@peculiar/asn1-schema" "^2.3.6"
"@peculiar/json-schema" "^1.1.12"
asn1js "^3.0.1"
pvtsutils "^1.3.2"
tslib "^2.4.0"
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"