mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
feat(console,phrases): integrate monaco editor (#5460)
* feat(console,phrases): integrate monaco editor integrate monaco editor * chore: update pnpm lock file update pnpm lock file * fix(console): remove unused styles remove unused styles * feat(console): update font styles update font styles
This commit is contained in:
parent
e1def81ed5
commit
2f72f8ffd7
28 changed files with 574 additions and 36 deletions
|
@ -36,6 +36,7 @@
|
|||
"@logto/schemas": "workspace:^1.13.1",
|
||||
"@logto/shared": "workspace:^3.1.0",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@parcel/compressor-brotli": "2.9.3",
|
||||
"@parcel/compressor-gzip": "2.9.3",
|
||||
"@parcel/core": "2.9.3",
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import { type EditorProps } from '@monaco-editor/react';
|
||||
|
||||
import type { IStandaloneThemeData } from './type';
|
||||
|
||||
// Logto dark theme extends vs-dark theme
|
||||
export const logtoDarkTheme: IStandaloneThemeData = {
|
||||
base: 'vs-dark',
|
||||
inherit: true,
|
||||
rules: [],
|
||||
colors: {
|
||||
'editor.background': '#181133', // :token/code/code-bg
|
||||
},
|
||||
};
|
||||
|
||||
// @see {@link https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IStandaloneEditorConstructionOptions.html}
|
||||
export const defaultOptions: EditorProps['options'] = {
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
wordWrap: 'on',
|
||||
renderLineHighlight: 'none',
|
||||
fontFamily: 'Roboto Mono, monospace',
|
||||
fontSize: 14,
|
||||
};
|
|
@ -0,0 +1,58 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
@use '@/scss/code-editor' as codeEditor;
|
||||
|
||||
.codeEditor {
|
||||
@include codeEditor.color;
|
||||
@include codeEditor.font;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 8px;
|
||||
background-color: var(--color-code-bg);
|
||||
|
||||
|
||||
header {
|
||||
padding: _.unit(4);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.tabList {
|
||||
display: flex;
|
||||
gap: _.unit(2);
|
||||
|
||||
.tab {
|
||||
padding: _.unit(1.5) _.unit(3);
|
||||
font: var(--font-label-2);
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
color: var(--color-code-grey);
|
||||
cursor: pointer;
|
||||
|
||||
&.active,
|
||||
&:hover {
|
||||
color: var(--color-code-white);
|
||||
background-color: var(--color-code-tab-active-bg);
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font: var(--font-label-2);
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
color: var(--color-code-white);
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: _.unit(2);
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.editorContainer {
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
padding-bottom: _.unit(4);
|
||||
}
|
||||
}
|
126
packages/console/src/pages/JwtClaims/MonacoCodeEditor/index.tsx
Normal file
126
packages/console/src/pages/JwtClaims/MonacoCodeEditor/index.tsx
Normal file
|
@ -0,0 +1,126 @@
|
|||
import { Editor, type BeforeMount, type OnMount, useMonaco } from '@monaco-editor/react';
|
||||
import { type Nullable } from '@silverhand/essentials';
|
||||
import classNames from 'classnames';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Copy from '@/assets/icons/copy.svg';
|
||||
import IconButton from '@/ds-components/IconButton';
|
||||
import { onKeyDownHandler } from '@/utils/a11y';
|
||||
|
||||
import { logtoDarkTheme, defaultOptions } from './config.js';
|
||||
import * as styles from './index.module.scss';
|
||||
import type { IStandaloneCodeEditor, Model } from './type.js';
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
actions?: React.ReactNode;
|
||||
models: Model[];
|
||||
};
|
||||
|
||||
function MonacoCodeEditor({ className, actions, models }: Props) {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const monaco = useMonaco();
|
||||
const editorRef = useRef<Nullable<IStandaloneCodeEditor>>(null);
|
||||
|
||||
const [activeModelName, setActiveModelName] = useState<string>();
|
||||
const activeModel = useMemo(
|
||||
() => models.find((model) => model.name === activeModelName),
|
||||
[activeModelName, models]
|
||||
);
|
||||
|
||||
// Set the first model as the active model
|
||||
useEffect(() => {
|
||||
setActiveModelName(models[0]?.name);
|
||||
}, [models]);
|
||||
|
||||
useEffect(() => {
|
||||
// Add global declarations
|
||||
// monaco will be ready after the editor is mounted, useEffect will be called after the monaco is ready
|
||||
if (!monaco || !activeModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the global declarations for the active model
|
||||
// @see {@link https://microsoft.github.io/monaco-editor/typedoc/interfaces/languages.typescript.LanguageServiceDefaults.html#setExtraLibs}
|
||||
if (activeModel.globalDeclarations) {
|
||||
monaco.languages.typescript.typescriptDefaults.setExtraLibs([
|
||||
{
|
||||
content: activeModel.globalDeclarations,
|
||||
filePath: `file:///global.d.ts`,
|
||||
},
|
||||
]);
|
||||
}
|
||||
}, [activeModel, monaco]);
|
||||
|
||||
const handleCodeCopy = useCallback(async () => {
|
||||
const editor = editorRef.current;
|
||||
|
||||
if (editor) {
|
||||
const code = editor.getValue();
|
||||
await navigator.clipboard.writeText(code);
|
||||
toast.success(t('general.copied'));
|
||||
}
|
||||
}, [t]);
|
||||
|
||||
const handleEditorWillMount = useCallback<BeforeMount>((monaco) => {
|
||||
// Register the new logto theme
|
||||
monaco.editor.defineTheme('logto-dark', logtoDarkTheme);
|
||||
}, []);
|
||||
|
||||
const handleEditorDidMount = useCallback<OnMount>((editor) => {
|
||||
// eslint-disable-next-line @silverhand/fp/no-mutation
|
||||
editorRef.current = editor;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={classNames(className, styles.codeEditor)}>
|
||||
<header>
|
||||
{models.length > 1 ? (
|
||||
<div className={styles.tabList}>
|
||||
{models.map(({ name }) => (
|
||||
<div
|
||||
key={name}
|
||||
className={classNames(styles.tab, name === activeModelName && styles.active)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => {
|
||||
setActiveModelName(name);
|
||||
}}
|
||||
onKeyDown={onKeyDownHandler(() => {
|
||||
setActiveModelName(name);
|
||||
})}
|
||||
>
|
||||
{name}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.title}>{activeModel?.title}</div>
|
||||
)}
|
||||
<div className={styles.actions}>
|
||||
{actions}
|
||||
<IconButton size="small" onClick={handleCodeCopy}>
|
||||
<Copy />
|
||||
</IconButton>
|
||||
</div>
|
||||
</header>
|
||||
<div className={styles.editorContainer}>
|
||||
<Editor
|
||||
height="100%"
|
||||
language="typescript"
|
||||
// TODO: need to check on the usage of value and defaultValue
|
||||
defaultValue={activeModel?.defaultValue}
|
||||
path={activeModel?.name}
|
||||
theme="logto-dark"
|
||||
options={defaultOptions}
|
||||
beforeMount={handleEditorWillMount}
|
||||
onMount={handleEditorDidMount}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MonacoCodeEditor;
|
|
@ -0,0 +1,15 @@
|
|||
import { type Monaco, type OnMount } from '@monaco-editor/react';
|
||||
|
||||
export type IStandaloneThemeData = Parameters<Monaco['editor']['defineTheme']>[1];
|
||||
|
||||
export type IStandaloneCodeEditor = Parameters<OnMount>[0];
|
||||
|
||||
export type Model = {
|
||||
/** Used as the unique key for the monaco editor model @see {@link https://github.com/suren-atoyan/monaco-react?tab=readme-ov-file#multi-model-editor} */
|
||||
name: string;
|
||||
/** The title of the model */
|
||||
title: string;
|
||||
defaultValue: string;
|
||||
language: string;
|
||||
globalDeclarations?: string;
|
||||
};
|
118
packages/console/src/pages/JwtClaims/config.ts
Normal file
118
packages/console/src/pages/JwtClaims/config.ts
Normal file
|
@ -0,0 +1,118 @@
|
|||
import type { Model } from './MonacoCodeEditor/type.js';
|
||||
|
||||
const userJwtGlobalDeclarations = `
|
||||
declare global {
|
||||
export interface CustomJwtClaims extends Record<string, any> {}
|
||||
|
||||
/** The user info associated with the token.
|
||||
*
|
||||
* @param {string} id - The user id
|
||||
* @param {string} [primaryEmail] - The user email
|
||||
* @param {string} [primaryPhone] - The user phone
|
||||
* @param {string} [username] - The user username
|
||||
* @param {string} [name] - The user name
|
||||
* @param {string} [avatar] - The user avatar
|
||||
*
|
||||
*/
|
||||
export type User = {
|
||||
id: string;
|
||||
primaryEmail?: string;
|
||||
primaryPhone?: string;
|
||||
username?: string;
|
||||
name?: string;
|
||||
avatar?: string;
|
||||
}
|
||||
|
||||
/** Logto internal data that can be used to pass additional information
|
||||
* @param {User} user - The user info associated with the token.
|
||||
*/
|
||||
export type Data = {
|
||||
user: User;
|
||||
}
|
||||
|
||||
export interface Exports {
|
||||
/**
|
||||
* This function is called to get custom claims for the JWT token.
|
||||
*
|
||||
* @param {string} token -The JWT token.
|
||||
* @param {Data} data - Logto internal data that can be used to pass additional information
|
||||
* @param {User} data.user - The user info associated with the token.
|
||||
*
|
||||
* @returns The custom claims.
|
||||
*/
|
||||
getCustomJwtClaims: (token: string, data: Data) => Promise<CustomJwtClaims>;
|
||||
}
|
||||
|
||||
const exports: Exports;
|
||||
}
|
||||
|
||||
export { exports as default };
|
||||
`;
|
||||
|
||||
const machineToMachineJwtGlobalDeclarations = `
|
||||
declare global {
|
||||
export interface CustomJwtClaims extends Record<string, any> {}
|
||||
|
||||
export interface Exports {
|
||||
/**
|
||||
* This function is called to get custom claims for the JWT token.
|
||||
*
|
||||
* @param {string} token -The JWT token.
|
||||
*
|
||||
* @returns The custom claims.
|
||||
*/
|
||||
getCustomJwtClaims: (token: string) => Promise<CustomJwtClaims>;
|
||||
}
|
||||
|
||||
const exports: Exports;
|
||||
}
|
||||
|
||||
export { exports as default };
|
||||
`;
|
||||
|
||||
const defaultUserJwtClaimsCode = `/**
|
||||
* This function is called to get custom claims for the JWT token.
|
||||
*
|
||||
* @param {string} token -The JWT token.
|
||||
* @param {Data} data - Logto internal data that can be used to pass additional information
|
||||
* @param {User} data.user - The user info associated with the token.
|
||||
*
|
||||
* @returns The custom claims.
|
||||
*/
|
||||
|
||||
exports.getCustomJwtClaims = async (token, data) => {
|
||||
return {};
|
||||
}`;
|
||||
|
||||
const defaultMachineToMachineJwtClaimsCode = `/**
|
||||
* This function is called to get custom claims for the JWT token.
|
||||
*
|
||||
* @param {string} token -The JWT token.
|
||||
*
|
||||
* @returns The custom claims.
|
||||
*/
|
||||
|
||||
exports.getCustomJwtClaims = async (token) => {
|
||||
return {};
|
||||
}`;
|
||||
|
||||
export const userJwtFile: Model = {
|
||||
name: 'user-jwt.ts',
|
||||
title: 'TypeScript',
|
||||
language: 'typescript',
|
||||
defaultValue: defaultUserJwtClaimsCode,
|
||||
globalDeclarations: userJwtGlobalDeclarations,
|
||||
};
|
||||
|
||||
export const machineToMachineJwtFile: Model = {
|
||||
name: 'machine-to-machine-jwt.ts',
|
||||
title: 'TypeScript',
|
||||
language: 'typescript',
|
||||
defaultValue: defaultMachineToMachineJwtClaimsCode,
|
||||
globalDeclarations: machineToMachineJwtGlobalDeclarations,
|
||||
};
|
||||
|
||||
export enum JwtTokenType {
|
||||
UserAccessToken = 'user-access-token',
|
||||
MachineToMachineAccessToken = 'm2m-access-token',
|
||||
}
|
|
@ -5,8 +5,39 @@
|
|||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.cardTitle {
|
||||
.header {
|
||||
flex-shrink: 0;
|
||||
margin-bottom: _.unit(4);
|
||||
}
|
||||
|
||||
.tabNav {
|
||||
margin-bottom: _.unit(4);
|
||||
}
|
||||
}
|
||||
|
||||
.tabContent {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: _.unit(2);
|
||||
flex-grow: 1;
|
||||
|
||||
> * {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.codePanel {
|
||||
margin-bottom: _.unit(6);
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.cardTitle {
|
||||
font: var(--font-title-2);
|
||||
margin-bottom: _.unit(3);
|
||||
}
|
||||
|
||||
.flexGrow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,44 +1,73 @@
|
|||
import { withAppInsights } from '@logto/app-insights/react/AppInsightsReact';
|
||||
import classNames from 'classnames';
|
||||
import type { TFuncKey } from 'i18next';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Card from '@/ds-components/Card';
|
||||
import CardTitle from '@/ds-components/CardTitle';
|
||||
import TabNav, { TabNavItem } from '@/ds-components/TabNav';
|
||||
|
||||
import MonacoCodeEditor from './MonacoCodeEditor';
|
||||
import { type Model } from './MonacoCodeEditor/type';
|
||||
import { userJwtFile, machineToMachineJwtFile, JwtTokenType } from './config';
|
||||
import * as styles from './index.module.scss';
|
||||
import { JwtTokenType } from './type';
|
||||
|
||||
export * from './type';
|
||||
export { JwtTokenType } from './config';
|
||||
|
||||
type Props = {
|
||||
tab: JwtTokenType;
|
||||
};
|
||||
|
||||
const phrases = Object.freeze({
|
||||
tab: {
|
||||
[JwtTokenType.UserAccessToken]: 'user_jwt_tab',
|
||||
[JwtTokenType.MachineToMachineAccessToken]: 'machine_to_machine_jwt_tab',
|
||||
},
|
||||
token: {
|
||||
[JwtTokenType.UserAccessToken]: 'user_jwt',
|
||||
[JwtTokenType.MachineToMachineAccessToken]: 'machine_to_machine_jwt',
|
||||
},
|
||||
} satisfies Record<
|
||||
string,
|
||||
Record<JwtTokenType, TFuncKey<'translation', 'admin_console.jwt_claims'>>
|
||||
>);
|
||||
|
||||
const getPath = (tab: JwtTokenType) => `/jwt-claims/${tab}`;
|
||||
|
||||
function JwtClaims({ tab }: Props) {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
// TODO: API integration, read/write the custom claims code value
|
||||
const activeModel = useMemo<Model>(() => {
|
||||
return tab === JwtTokenType.UserAccessToken ? userJwtFile : machineToMachineJwtFile;
|
||||
}, [tab]);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<CardTitle
|
||||
title="jwt_claims.title"
|
||||
subtitle="jwt_claims.description"
|
||||
className={styles.cardTitle}
|
||||
className={styles.header}
|
||||
/>
|
||||
<TabNav>
|
||||
<TabNavItem
|
||||
href={getPath(JwtTokenType.UserAccessToken)}
|
||||
isActive={tab === JwtTokenType.UserAccessToken}
|
||||
>
|
||||
{t('jwt_claims.user_jwt_tab')}
|
||||
</TabNavItem>
|
||||
<TabNavItem
|
||||
href={getPath(JwtTokenType.MachineToMachineAccessToken)}
|
||||
isActive={tab === JwtTokenType.MachineToMachineAccessToken}
|
||||
>
|
||||
{t('jwt_claims.machine_to_machine_jwt_tab')}
|
||||
<TabNav className={styles.tabNav}>
|
||||
{Object.values(JwtTokenType).map((tokenType) => (
|
||||
<TabNavItem key={tokenType} href={getPath(tokenType)} isActive={tokenType === tab}>
|
||||
{t(`jwt_claims.${phrases.tab[tokenType]}`)}
|
||||
</TabNavItem>
|
||||
))}
|
||||
</TabNav>
|
||||
<form className={classNames(styles.tabContent)}>
|
||||
<Card className={styles.codePanel}>
|
||||
<div className={styles.cardTitle}>
|
||||
{t('jwt_claims.code_editor_title', {
|
||||
token: t(`jwt_claims.${phrases.token[tab]}`),
|
||||
})}
|
||||
</div>
|
||||
<MonacoCodeEditor className={styles.flexGrow} models={[activeModel]} />
|
||||
</Card>
|
||||
<div>Form Panel</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
export enum JwtTokenType {
|
||||
UserAccessToken = 'user-access-token',
|
||||
MachineToMachineAccessToken = 'm2m-access-token',
|
||||
}
|
10
packages/console/src/scss/_code-editor.scss
Normal file
10
packages/console/src/scss/_code-editor.scss
Normal file
|
@ -0,0 +1,10 @@
|
|||
@mixin color {
|
||||
--color-code-bg: #181133;
|
||||
--color-code-white: var(--color-base);
|
||||
--color-code-grey: #adaab4;
|
||||
--color-code-tab-active-bg: rgba(255, 255, 255, 24%);
|
||||
}
|
||||
|
||||
@mixin font {
|
||||
--font-code: 500 14px/20px 'Roboto Mono', monospace;
|
||||
}
|
|
@ -9,6 +9,7 @@ body {
|
|||
color: var(--color-text);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: auto;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
* {
|
||||
|
|
|
@ -39,6 +39,14 @@ export default function koaSecurityHeaders<StateT, ContextT, ResponseBodyT>(
|
|||
const developmentOrigins = conditionalArray(!isProduction && 'ws:');
|
||||
const appInsightsOrigins = ['https://*.applicationinsights.azure.com'];
|
||||
|
||||
// We use react-monaco-editor for code editing in the admin console. It loads the monaco editor asynchronously from a CDN.
|
||||
// Allow the CDN src in the CSP.
|
||||
// Allow blob: for monaco editor to load worker scripts
|
||||
const monacoEditorCDNSource = [
|
||||
'https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/vs/',
|
||||
'blob:',
|
||||
];
|
||||
|
||||
/**
|
||||
* Default Applied rules:
|
||||
* - crossOriginOpenerPolicy: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#cross-origin-opener-policy-coop
|
||||
|
@ -107,6 +115,7 @@ export default function koaSecurityHeaders<StateT, ContextT, ResponseBodyT>(
|
|||
scriptSrc: [
|
||||
"'self'",
|
||||
...conditionalArray(!isProduction && ["'unsafe-eval'", "'unsafe-inline'"]),
|
||||
...monacoEditorCDNSource,
|
||||
],
|
||||
connectSrc: [
|
||||
"'self'",
|
||||
|
|
|
@ -7,7 +7,13 @@ const jwt_claims = {
|
|||
/** UNTRANSLATED */
|
||||
user_jwt_tab: 'User JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT',
|
||||
machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
user_jwt: 'user JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt: 'machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
code_editor_title: 'Customize the {{token}} claims',
|
||||
};
|
||||
|
||||
export default Object.freeze(jwt_claims);
|
||||
|
|
|
@ -3,7 +3,10 @@ const jwt_claims = {
|
|||
description:
|
||||
'Set up custom JWT claims to include in the access token. These claims can be used to pass additional information to your application.',
|
||||
user_jwt_tab: 'User JWT',
|
||||
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT',
|
||||
machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
|
||||
user_jwt: 'user JWT',
|
||||
machine_to_machine_jwt: 'machine-to-machine JWT',
|
||||
code_editor_title: 'Customize the {{token}} claims',
|
||||
};
|
||||
|
||||
export default Object.freeze(jwt_claims);
|
||||
|
|
|
@ -7,7 +7,13 @@ const jwt_claims = {
|
|||
/** UNTRANSLATED */
|
||||
user_jwt_tab: 'User JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT',
|
||||
machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
user_jwt: 'user JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt: 'machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
code_editor_title: 'Customize the {{token}} claims',
|
||||
};
|
||||
|
||||
export default Object.freeze(jwt_claims);
|
||||
|
|
|
@ -7,7 +7,13 @@ const jwt_claims = {
|
|||
/** UNTRANSLATED */
|
||||
user_jwt_tab: 'User JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT',
|
||||
machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
user_jwt: 'user JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt: 'machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
code_editor_title: 'Customize the {{token}} claims',
|
||||
};
|
||||
|
||||
export default Object.freeze(jwt_claims);
|
||||
|
|
|
@ -7,7 +7,13 @@ const jwt_claims = {
|
|||
/** UNTRANSLATED */
|
||||
user_jwt_tab: 'User JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT',
|
||||
machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
user_jwt: 'user JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt: 'machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
code_editor_title: 'Customize the {{token}} claims',
|
||||
};
|
||||
|
||||
export default Object.freeze(jwt_claims);
|
||||
|
|
|
@ -7,7 +7,13 @@ const jwt_claims = {
|
|||
/** UNTRANSLATED */
|
||||
user_jwt_tab: 'User JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT',
|
||||
machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
user_jwt: 'user JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt: 'machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
code_editor_title: 'Customize the {{token}} claims',
|
||||
};
|
||||
|
||||
export default Object.freeze(jwt_claims);
|
||||
|
|
|
@ -7,7 +7,13 @@ const jwt_claims = {
|
|||
/** UNTRANSLATED */
|
||||
user_jwt_tab: 'User JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT',
|
||||
machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
user_jwt: 'user JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt: 'machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
code_editor_title: 'Customize the {{token}} claims',
|
||||
};
|
||||
|
||||
export default Object.freeze(jwt_claims);
|
||||
|
|
|
@ -7,7 +7,13 @@ const jwt_claims = {
|
|||
/** UNTRANSLATED */
|
||||
user_jwt_tab: 'User JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT',
|
||||
machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
user_jwt: 'user JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt: 'machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
code_editor_title: 'Customize the {{token}} claims',
|
||||
};
|
||||
|
||||
export default Object.freeze(jwt_claims);
|
||||
|
|
|
@ -7,7 +7,13 @@ const jwt_claims = {
|
|||
/** UNTRANSLATED */
|
||||
user_jwt_tab: 'User JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT',
|
||||
machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
user_jwt: 'user JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt: 'machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
code_editor_title: 'Customize the {{token}} claims',
|
||||
};
|
||||
|
||||
export default Object.freeze(jwt_claims);
|
||||
|
|
|
@ -7,7 +7,13 @@ const jwt_claims = {
|
|||
/** UNTRANSLATED */
|
||||
user_jwt_tab: 'User JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT',
|
||||
machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
user_jwt: 'user JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt: 'machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
code_editor_title: 'Customize the {{token}} claims',
|
||||
};
|
||||
|
||||
export default Object.freeze(jwt_claims);
|
||||
|
|
|
@ -7,7 +7,13 @@ const jwt_claims = {
|
|||
/** UNTRANSLATED */
|
||||
user_jwt_tab: 'User JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT',
|
||||
machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
user_jwt: 'user JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt: 'machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
code_editor_title: 'Customize the {{token}} claims',
|
||||
};
|
||||
|
||||
export default Object.freeze(jwt_claims);
|
||||
|
|
|
@ -7,7 +7,13 @@ const jwt_claims = {
|
|||
/** UNTRANSLATED */
|
||||
user_jwt_tab: 'User JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT',
|
||||
machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
user_jwt: 'user JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt: 'machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
code_editor_title: 'Customize the {{token}} claims',
|
||||
};
|
||||
|
||||
export default Object.freeze(jwt_claims);
|
||||
|
|
|
@ -7,7 +7,13 @@ const jwt_claims = {
|
|||
/** UNTRANSLATED */
|
||||
user_jwt_tab: 'User JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT',
|
||||
machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
user_jwt: 'user JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt: 'machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
code_editor_title: 'Customize the {{token}} claims',
|
||||
};
|
||||
|
||||
export default Object.freeze(jwt_claims);
|
||||
|
|
|
@ -7,7 +7,13 @@ const jwt_claims = {
|
|||
/** UNTRANSLATED */
|
||||
user_jwt_tab: 'User JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT',
|
||||
machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
user_jwt: 'user JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt: 'machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
code_editor_title: 'Customize the {{token}} claims',
|
||||
};
|
||||
|
||||
export default Object.freeze(jwt_claims);
|
||||
|
|
|
@ -7,7 +7,13 @@ const jwt_claims = {
|
|||
/** UNTRANSLATED */
|
||||
user_jwt_tab: 'User JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt_tab: 'Machine-to-Machine JWT',
|
||||
machine_to_machine_jwt_tab: 'Machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
user_jwt: 'user JWT',
|
||||
/** UNTRANSLATED */
|
||||
machine_to_machine_jwt: 'machine-to-machine JWT',
|
||||
/** UNTRANSLATED */
|
||||
code_editor_title: 'Customize the {{token}} claims',
|
||||
};
|
||||
|
||||
export default Object.freeze(jwt_claims);
|
||||
|
|
|
@ -2948,6 +2948,9 @@ importers:
|
|||
'@mdx-js/react':
|
||||
specifier: ^1.6.22
|
||||
version: 1.6.22(react@18.2.0)
|
||||
'@monaco-editor/react':
|
||||
specifier: ^4.6.0
|
||||
version: 4.6.0(monaco-editor@0.46.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@parcel/compressor-brotli':
|
||||
specifier: 2.9.3
|
||||
version: 2.9.3(@parcel/core@2.9.3)
|
||||
|
@ -7933,6 +7936,28 @@ packages:
|
|||
json5: 2.2.1
|
||||
dev: true
|
||||
|
||||
/@monaco-editor/loader@1.4.0(monaco-editor@0.46.0):
|
||||
resolution: {integrity: sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==}
|
||||
peerDependencies:
|
||||
monaco-editor: '>= 0.21.0 < 1'
|
||||
dependencies:
|
||||
monaco-editor: 0.46.0
|
||||
state-local: 1.0.7
|
||||
dev: true
|
||||
|
||||
/@monaco-editor/react@4.6.0(monaco-editor@0.46.0)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==}
|
||||
peerDependencies:
|
||||
monaco-editor: '>= 0.25.0 < 1'
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
'@monaco-editor/loader': 1.4.0(monaco-editor@0.46.0)
|
||||
monaco-editor: 0.46.0
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: true
|
||||
|
||||
/@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2:
|
||||
resolution: {integrity: sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==}
|
||||
cpu: [arm64]
|
||||
|
@ -17200,6 +17225,10 @@ packages:
|
|||
resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==}
|
||||
dev: false
|
||||
|
||||
/monaco-editor@0.46.0:
|
||||
resolution: {integrity: sha512-ADwtLIIww+9FKybWscd7OCfm9odsFYHImBRI1v9AviGce55QY8raT+9ihH8jX/E/e6QVSGM+pKj4jSUSRmALNQ==}
|
||||
dev: true
|
||||
|
||||
/ms@2.1.2:
|
||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||
|
||||
|
@ -20081,6 +20110,10 @@ packages:
|
|||
escape-string-regexp: 2.0.0
|
||||
dev: true
|
||||
|
||||
/state-local@1.0.7:
|
||||
resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==}
|
||||
dev: true
|
||||
|
||||
/state-toggle@1.0.3:
|
||||
resolution: {integrity: sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==}
|
||||
dev: true
|
||||
|
|
Loading…
Reference in a new issue