mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-20 20:29:06 +00:00
Merge pull request #176 from Monadical-SAS/feat-user-auth-fief-api-test
Implement Auth - Front-end
This commit is contained in:
@@ -79,7 +79,7 @@ This data is then returned from the `useWebRTC` hook and can be used in your com
|
||||
To generate the TypeScript files from the openapi.json file, make sure the python server is running, then run:
|
||||
|
||||
```
|
||||
openapi-generator-cli generate -i http://localhost:1250/openapi.json -g typescript-fetch -o app/api
|
||||
yarn openapi
|
||||
```
|
||||
|
||||
You may need to run `yarn global add @openapitools/openapi-generator-cli` first. You also need a Java runtime installed on your machine.
|
||||
|
||||
11
www/app/(auth)/fiefWrapper.tsx
Normal file
11
www/app/(auth)/fiefWrapper.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { FiefAuthProvider } from "@fief/fief/nextjs/react";
|
||||
|
||||
export default function FiefWrapper({ children }) {
|
||||
return (
|
||||
<FiefAuthProvider currentUserPath="/api/current-user">
|
||||
{children}
|
||||
</FiefAuthProvider>
|
||||
);
|
||||
}
|
||||
43
www/app/(auth)/userInfo.tsx
Normal file
43
www/app/(auth)/userInfo.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
"use client";
|
||||
import {
|
||||
useFiefIsAuthenticated,
|
||||
useFiefUserinfo,
|
||||
} from "@fief/fief/nextjs/react";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
|
||||
export default function UserInfo() {
|
||||
const isAuthenticated = useFiefIsAuthenticated();
|
||||
const userinfo = useFiefUserinfo();
|
||||
|
||||
return (
|
||||
<header className="bg-black w-full border-b border-gray-700 flex justify-between items-center py-2 mb-3">
|
||||
{/* Logo on the left */}
|
||||
<Link href="/">
|
||||
<Image
|
||||
src="/reach.png"
|
||||
width={16}
|
||||
height={16}
|
||||
className="h-6 w-auto ml-2"
|
||||
alt="Reflector"
|
||||
/>
|
||||
</Link>
|
||||
|
||||
{/* Text link on the right */}
|
||||
{!isAuthenticated && (
|
||||
<span className="text-white hover:underline font-thin px-2">
|
||||
<Link href="/login">Log in or create account</Link>
|
||||
</span>
|
||||
)}
|
||||
{isAuthenticated && (
|
||||
<span className="text-white font-thin px-2">
|
||||
{userinfo?.email} (
|
||||
<span className="hover:underline">
|
||||
<Link href="/logout">Log out</Link>
|
||||
</span>
|
||||
)
|
||||
</span>
|
||||
)}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
@@ -9,6 +9,7 @@ models/PageGetTranscript.ts
|
||||
models/RtcOffer.ts
|
||||
models/TranscriptTopic.ts
|
||||
models/UpdateTranscript.ts
|
||||
models/UserInfo.ts
|
||||
models/ValidationError.ts
|
||||
models/index.ts
|
||||
runtime.ts
|
||||
|
||||
@@ -55,6 +55,10 @@ export interface V1TranscriptGetAudioRequest {
|
||||
transcriptId: any;
|
||||
}
|
||||
|
||||
export interface V1TranscriptGetAudioMp3Request {
|
||||
transcriptId: any;
|
||||
}
|
||||
|
||||
export interface V1TranscriptGetTopicsRequest {
|
||||
transcriptId: any;
|
||||
}
|
||||
@@ -159,6 +163,14 @@ export class DefaultApi extends runtime.BaseAPI {
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.accessToken) {
|
||||
// oauth required
|
||||
headerParameters["Authorization"] = await this.configuration.accessToken(
|
||||
"OAuth2AuthorizationCodeBearer",
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
const response = await this.request(
|
||||
{
|
||||
path: `/v1/transcripts/{transcript_id}`.replace(
|
||||
@@ -212,6 +224,14 @@ export class DefaultApi extends runtime.BaseAPI {
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.accessToken) {
|
||||
// oauth required
|
||||
headerParameters["Authorization"] = await this.configuration.accessToken(
|
||||
"OAuth2AuthorizationCodeBearer",
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
const response = await this.request(
|
||||
{
|
||||
path: `/v1/transcripts/{transcript_id}`.replace(
|
||||
@@ -299,6 +319,61 @@ export class DefaultApi extends runtime.BaseAPI {
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transcript Get Audio Mp3
|
||||
*/
|
||||
async v1TranscriptGetAudioMp3Raw(
|
||||
requestParameters: V1TranscriptGetAudioMp3Request,
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<runtime.ApiResponse<any>> {
|
||||
if (
|
||||
requestParameters.transcriptId === null ||
|
||||
requestParameters.transcriptId === undefined
|
||||
) {
|
||||
throw new runtime.RequiredError(
|
||||
"transcriptId",
|
||||
"Required parameter requestParameters.transcriptId was null or undefined when calling v1TranscriptGetAudioMp3.",
|
||||
);
|
||||
}
|
||||
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
const response = await this.request(
|
||||
{
|
||||
path: `/v1/transcripts/{transcript_id}/audio/mp3`.replace(
|
||||
`{${"transcript_id"}}`,
|
||||
encodeURIComponent(String(requestParameters.transcriptId)),
|
||||
),
|
||||
method: "GET",
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
},
|
||||
initOverrides,
|
||||
);
|
||||
|
||||
if (this.isJsonMime(response.headers.get("content-type"))) {
|
||||
return new runtime.JSONApiResponse<any>(response);
|
||||
} else {
|
||||
return new runtime.TextApiResponse(response) as any;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transcript Get Audio Mp3
|
||||
*/
|
||||
async v1TranscriptGetAudioMp3(
|
||||
requestParameters: V1TranscriptGetAudioMp3Request,
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<any> {
|
||||
const response = await this.v1TranscriptGetAudioMp3Raw(
|
||||
requestParameters,
|
||||
initOverrides,
|
||||
);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transcript Get Topics
|
||||
*/
|
||||
@@ -510,6 +585,14 @@ export class DefaultApi extends runtime.BaseAPI {
|
||||
|
||||
headerParameters["Content-Type"] = "application/json";
|
||||
|
||||
if (this.configuration && this.configuration.accessToken) {
|
||||
// oauth required
|
||||
headerParameters["Authorization"] = await this.configuration.accessToken(
|
||||
"OAuth2AuthorizationCodeBearer",
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
const response = await this.request(
|
||||
{
|
||||
path: `/v1/transcripts/{transcript_id}`.replace(
|
||||
@@ -566,6 +649,14 @@ export class DefaultApi extends runtime.BaseAPI {
|
||||
|
||||
headerParameters["Content-Type"] = "application/json";
|
||||
|
||||
if (this.configuration && this.configuration.accessToken) {
|
||||
// oauth required
|
||||
headerParameters["Authorization"] = await this.configuration.accessToken(
|
||||
"OAuth2AuthorizationCodeBearer",
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
const response = await this.request(
|
||||
{
|
||||
path: `/v1/transcripts`,
|
||||
@@ -615,6 +706,14 @@ export class DefaultApi extends runtime.BaseAPI {
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.accessToken) {
|
||||
// oauth required
|
||||
headerParameters["Authorization"] = await this.configuration.accessToken(
|
||||
"OAuth2AuthorizationCodeBearer",
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
const response = await this.request(
|
||||
{
|
||||
path: `/v1/transcripts`,
|
||||
@@ -643,4 +742,49 @@ export class DefaultApi extends runtime.BaseAPI {
|
||||
);
|
||||
return await response.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* User Me
|
||||
*/
|
||||
async v1UserMeRaw(
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<runtime.ApiResponse<any>> {
|
||||
const queryParameters: any = {};
|
||||
|
||||
const headerParameters: runtime.HTTPHeaders = {};
|
||||
|
||||
if (this.configuration && this.configuration.accessToken) {
|
||||
// oauth required
|
||||
headerParameters["Authorization"] = await this.configuration.accessToken(
|
||||
"OAuth2AuthorizationCodeBearer",
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
const response = await this.request(
|
||||
{
|
||||
path: `/v1/me`,
|
||||
method: "GET",
|
||||
headers: headerParameters,
|
||||
query: queryParameters,
|
||||
},
|
||||
initOverrides,
|
||||
);
|
||||
|
||||
if (this.isJsonMime(response.headers.get("content-type"))) {
|
||||
return new runtime.JSONApiResponse<any>(response);
|
||||
} else {
|
||||
return new runtime.TextApiResponse(response) as any;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User Me
|
||||
*/
|
||||
async v1UserMe(
|
||||
initOverrides?: RequestInit | runtime.InitOverrideFunction,
|
||||
): Promise<any> {
|
||||
const response = await this.v1UserMeRaw(initOverrides);
|
||||
return await response.value();
|
||||
}
|
||||
}
|
||||
|
||||
84
www/app/api/models/UserInfo.ts
Normal file
84
www/app/api/models/UserInfo.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* FastAPI
|
||||
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
import { exists, mapValues } from "../runtime";
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface UserInfo
|
||||
*/
|
||||
export interface UserInfo {
|
||||
/**
|
||||
*
|
||||
* @type {any}
|
||||
* @memberof UserInfo
|
||||
*/
|
||||
sub: any | null;
|
||||
/**
|
||||
*
|
||||
* @type {any}
|
||||
* @memberof UserInfo
|
||||
*/
|
||||
email: any | null;
|
||||
/**
|
||||
*
|
||||
* @type {any}
|
||||
* @memberof UserInfo
|
||||
*/
|
||||
emailVerified: any | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given object implements the UserInfo interface.
|
||||
*/
|
||||
export function instanceOfUserInfo(value: object): boolean {
|
||||
let isInstance = true;
|
||||
isInstance = isInstance && "sub" in value;
|
||||
isInstance = isInstance && "email" in value;
|
||||
isInstance = isInstance && "emailVerified" in value;
|
||||
|
||||
return isInstance;
|
||||
}
|
||||
|
||||
export function UserInfoFromJSON(json: any): UserInfo {
|
||||
return UserInfoFromJSONTyped(json, false);
|
||||
}
|
||||
|
||||
export function UserInfoFromJSONTyped(
|
||||
json: any,
|
||||
ignoreDiscriminator: boolean,
|
||||
): UserInfo {
|
||||
if (json === undefined || json === null) {
|
||||
return json;
|
||||
}
|
||||
return {
|
||||
sub: json["sub"],
|
||||
email: json["email"],
|
||||
emailVerified: json["email_verified"],
|
||||
};
|
||||
}
|
||||
|
||||
export function UserInfoToJSON(value?: UserInfo | null): any {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
sub: value.sub,
|
||||
email: value.email,
|
||||
email_verified: value.emailVerified,
|
||||
};
|
||||
}
|
||||
@@ -8,4 +8,5 @@ export * from "./PageGetTranscript";
|
||||
export * from "./RtcOffer";
|
||||
export * from "./TranscriptTopic";
|
||||
export * from "./UpdateTranscript";
|
||||
export * from "./UserInfo";
|
||||
export * from "./ValidationError";
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import "./styles/globals.scss";
|
||||
import { Roboto } from "next/font/google";
|
||||
import { Metadata } from "next";
|
||||
import FiefWrapper from "./(auth)/fiefWrapper";
|
||||
import UserInfo from "./(auth)/userInfo";
|
||||
|
||||
const roboto = Roboto({ subsets: ["latin"], weight: "400" });
|
||||
|
||||
@@ -52,7 +54,21 @@ export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={roboto.className + " flex flex-col min-h-screen"}>
|
||||
{children}
|
||||
<FiefWrapper>
|
||||
<div id="container">
|
||||
<div className="flex flex-col items-center h-[100svh] bg-gradient-to-r from-[#8ec5fc30] to-[#e0c3fc42]">
|
||||
<UserInfo />
|
||||
|
||||
<div className="h-[13svh] flex flex-col justify-center items-center">
|
||||
<h1 className="text-5xl font-bold text-blue-500">Reflector</h1>
|
||||
<p className="text-gray-500">
|
||||
Capture The Signal, Not The Noise
|
||||
</p>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</FiefWrapper>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
47
www/app/lib/fief.ts
Normal file
47
www/app/lib/fief.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
"use client";
|
||||
import { Fief, FiefUserInfo } from "@fief/fief";
|
||||
import { FiefAuth, IUserInfoCache } from "@fief/fief/nextjs";
|
||||
|
||||
export const SESSION_COOKIE_NAME = "reflector-auth";
|
||||
|
||||
export const fiefClient = new Fief({
|
||||
baseURL: process.env.FIEF_URL ?? "",
|
||||
clientId: process.env.FIEF_CLIENT_ID ?? "",
|
||||
clientSecret: process.env.FIEF_CLIENT_SECRET ?? "",
|
||||
});
|
||||
|
||||
class MemoryUserInfoCache implements IUserInfoCache {
|
||||
private storage: Record<string, any>;
|
||||
|
||||
constructor() {
|
||||
this.storage = {};
|
||||
}
|
||||
|
||||
async get(id: string): Promise<FiefUserInfo | null> {
|
||||
const userinfo = this.storage[id];
|
||||
if (userinfo) {
|
||||
return userinfo;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async set(id: string, userinfo: FiefUserInfo): Promise<void> {
|
||||
this.storage[id] = userinfo;
|
||||
}
|
||||
|
||||
async remove(id: string): Promise<void> {
|
||||
this.storage[id] = undefined;
|
||||
}
|
||||
|
||||
async clear(): Promise<void> {
|
||||
this.storage = {};
|
||||
}
|
||||
}
|
||||
|
||||
export const fiefAuth = new FiefAuth({
|
||||
client: fiefClient,
|
||||
sessionCookieName: SESSION_COOKIE_NAME,
|
||||
redirectURI: "http://localhost:3000/auth-callback",
|
||||
logoutRedirectURI: "http://localhost:3000",
|
||||
userInfoCache: new MemoryUserInfoCache(),
|
||||
});
|
||||
18
www/app/lib/getApi.ts
Normal file
18
www/app/lib/getApi.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Configuration } from "../api/runtime";
|
||||
import { DefaultApi } from "../api/apis/DefaultApi";
|
||||
|
||||
import { useFiefAccessTokenInfo } from "@fief/fief/nextjs/react";
|
||||
|
||||
export default function getApi(): DefaultApi {
|
||||
const accessTokenInfo = useFiefAccessTokenInfo();
|
||||
|
||||
const apiConfiguration = new Configuration({
|
||||
basePath: process.env.NEXT_PUBLIC_API_URL,
|
||||
accessToken: accessTokenInfo
|
||||
? "Bearer " + accessTokenInfo.access_token
|
||||
: undefined,
|
||||
});
|
||||
const api = new DefaultApi(apiConfiguration);
|
||||
|
||||
return api;
|
||||
}
|
||||
21
www/app/lib/user.ts
Normal file
21
www/app/lib/user.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export async function getCurrentUser(): Promise<any> {
|
||||
try {
|
||||
const response = await fetch("/api/current-user");
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Ensure the data structure is as expected
|
||||
if (data.userinfo && data.access_token_info) {
|
||||
return data;
|
||||
} else {
|
||||
throw new Error("Unexpected data structure");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching the user data:", error);
|
||||
throw error; // or you can return an appropriate fallback or error indicator
|
||||
}
|
||||
}
|
||||
@@ -26,12 +26,7 @@ const App = () => {
|
||||
const webSockets = useWebSockets(transcript.response?.id);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center h-[100svh] bg-gradient-to-r from-[#8ec5fc30] to-[#e0c3fc42]">
|
||||
<div className="h-[13svh] flex flex-col justify-center items-center">
|
||||
<h1 className="text-5xl font-bold text-blue-500">Reflector</h1>
|
||||
<p className="text-gray-500">Capture The Signal, Not The Noise</p>
|
||||
</div>
|
||||
|
||||
<>
|
||||
<Recorder
|
||||
setStream={setStream}
|
||||
onStop={() => {
|
||||
@@ -48,7 +43,7 @@ const App = () => {
|
||||
topics={webSockets.topics}
|
||||
disconnected={disconnected}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
|
||||
import { DefaultApi, V1TranscriptsCreateRequest } from "../api/apis/DefaultApi";
|
||||
import { Configuration } from "../api/runtime";
|
||||
import { GetTranscript } from "../api";
|
||||
import getApi from "../lib/getApi";
|
||||
|
||||
type UseTranscript = {
|
||||
response: GetTranscript | null;
|
||||
@@ -15,10 +16,7 @@ const useTranscript = (): UseTranscript => {
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const apiConfiguration = new Configuration({
|
||||
basePath: process.env.NEXT_PUBLIC_API_URL,
|
||||
});
|
||||
const api = new DefaultApi(apiConfiguration);
|
||||
const api = getApi();
|
||||
|
||||
const createTranscript = () => {
|
||||
setLoading(true);
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
V1TranscriptRecordWebrtcRequest,
|
||||
} from "../api/apis/DefaultApi";
|
||||
import { Configuration } from "../api/runtime";
|
||||
import getApi from "../lib/getApi";
|
||||
|
||||
const useWebRTC = (
|
||||
stream: MediaStream | null,
|
||||
@@ -17,10 +18,7 @@ const useWebRTC = (
|
||||
return;
|
||||
}
|
||||
|
||||
const apiConfiguration = new Configuration({
|
||||
basePath: process.env.NEXT_PUBLIC_API_URL,
|
||||
});
|
||||
const api = new DefaultApi(apiConfiguration);
|
||||
const api = getApi();
|
||||
|
||||
let p: Peer = new Peer({ initiator: true, stream: stream });
|
||||
|
||||
|
||||
20
www/middleware.ts
Normal file
20
www/middleware.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { NextRequest } from "next/server";
|
||||
|
||||
import { fiefAuth } from "./app/lib/fief";
|
||||
|
||||
const authMiddleware = fiefAuth.middleware([
|
||||
{
|
||||
matcher: "/private",
|
||||
parameters: {},
|
||||
},
|
||||
{
|
||||
matcher: "/castles/:path*",
|
||||
parameters: {
|
||||
permissions: ["castles:read"],
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
export async function middleware(request: NextRequest) {
|
||||
return authMiddleware(request);
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
"openapi": "openapi-generator-cli generate -i http://localhost:1250/openapi.json -g typescript-fetch -o app/api && yarn format"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fief/fief": "^0.13.5",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
|
||||
3
www/pages/api/current-user.ts
Normal file
3
www/pages/api/current-user.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { fiefAuth } from "../../app/lib/fief";
|
||||
|
||||
export default fiefAuth.currentUser();
|
||||
7
www/pages/forbidden.tsx
Normal file
7
www/pages/forbidden.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { NextPage } from "next";
|
||||
|
||||
const Forbidden: NextPage = () => {
|
||||
return <h2>Sorry, you are not authorized to access this page.</h2>;
|
||||
};
|
||||
|
||||
export default Forbidden;
|
||||
@@ -14,6 +14,16 @@
|
||||
dependencies:
|
||||
regenerator-runtime "^0.14.0"
|
||||
|
||||
"@fief/fief@^0.13.5":
|
||||
version "0.13.5"
|
||||
resolved "https://registry.yarnpkg.com/@fief/fief/-/fief-0.13.5.tgz#01ac833ddff0b84f2e1737cc168721568b0e7a39"
|
||||
integrity sha512-st4+/Rc1rw18B6RtqLmC6t9k0pnYLG7AqMe0JYjKYcamCNnABwl198ZJt6HShtaZKI5maHsh99UoCA3MqpbWsQ==
|
||||
dependencies:
|
||||
encoding "^0.1.13"
|
||||
jose "^4.6.0"
|
||||
node-fetch "^2.6.7"
|
||||
path-to-regexp "^6.2.1"
|
||||
|
||||
"@fortawesome/fontawesome-common-types@6.4.0":
|
||||
version "6.4.0"
|
||||
resolved "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz"
|
||||
@@ -868,6 +878,13 @@ emoji-regex@^8.0.0:
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
|
||||
|
||||
encoding@^0.1.13:
|
||||
version "0.1.13"
|
||||
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
|
||||
integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==
|
||||
dependencies:
|
||||
iconv-lite "^0.6.2"
|
||||
|
||||
err-code@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmjs.org/err-code/-/err-code-3.0.1.tgz"
|
||||
@@ -1108,6 +1125,13 @@ iconv-lite@^0.4.24:
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3"
|
||||
|
||||
iconv-lite@^0.6.2:
|
||||
version "0.6.3"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
|
||||
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3.0.0"
|
||||
|
||||
ieee754@^1.1.13, ieee754@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz"
|
||||
@@ -1247,6 +1271,11 @@ jiti@^1.18.2:
|
||||
resolved "https://registry.npmjs.org/jiti/-/jiti-1.19.1.tgz"
|
||||
integrity sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==
|
||||
|
||||
jose@^4.6.0:
|
||||
version "4.14.4"
|
||||
resolved "https://registry.yarnpkg.com/jose/-/jose-4.14.4.tgz#59e09204e2670c3164ee24cbfe7115c6f8bff9ca"
|
||||
integrity sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
|
||||
@@ -1529,6 +1558,11 @@ path-to-regexp@3.2.0:
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-3.2.0.tgz#fa7877ecbc495c601907562222453c43cc204a5f"
|
||||
integrity sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==
|
||||
|
||||
path-to-regexp@^6.2.1:
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5"
|
||||
integrity sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==
|
||||
|
||||
picocolors@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz"
|
||||
@@ -1786,7 +1820,7 @@ safe-buffer@^5.1.0, safe-buffer@~5.2.0:
|
||||
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
||||
"safer-buffer@>= 2.1.2 < 3":
|
||||
"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
Reference in New Issue
Block a user