mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
refactor(console): refactor the code editor type definition (#5516)
* refactor(console): refactor the code editor type definition refactor the code editor type definition * refactor(console): extract type definition gen process extract the type definition gen step to the build time. As typescript is not available at run time. * fix(console): add generate to console build script add generate to console build script * fix(console): add prettier format add prettier format
This commit is contained in:
parent
23bc5cdc8e
commit
08acdf73e3
16 changed files with 251 additions and 62 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -35,3 +35,6 @@ dump.rdb
|
||||||
|
|
||||||
# connectors
|
# connectors
|
||||||
/packages/core/connectors
|
/packages/core/connectors
|
||||||
|
|
||||||
|
# console auto generated files
|
||||||
|
/packages/console/src/consts/jwt-customizer-type-definition.ts
|
||||||
|
|
12
packages/console/generate.sh
Executable file
12
packages/console/generate.sh
Executable file
|
@ -0,0 +1,12 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
rm -rf scripts-js/
|
||||||
|
# build the jwt-customizer-type-definition generate script
|
||||||
|
pnpm exec tsc -p tsconfig.scripts.gen.json
|
||||||
|
# clean up the existing generated jwt-customizer-type-definition file
|
||||||
|
rm -f src/consts/jwt-customizer-type-definition.ts
|
||||||
|
# run script
|
||||||
|
node scripts-js/generate-jwt-customizer-type-definition.js
|
||||||
|
# Clean up
|
||||||
|
rm -rf scripts-js/
|
|
@ -11,11 +11,13 @@
|
||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"prepack": "pnpm generate",
|
||||||
|
"generate": "./generate.sh",
|
||||||
"precommit": "lint-staged",
|
"precommit": "lint-staged",
|
||||||
"start": "parcel src/index.html",
|
"start": "parcel src/index.html",
|
||||||
"dev": "cross-env PORT=5002 parcel src/index.html --public-url ${CONSOLE_PUBLIC_URL:-/console} --no-cache --hmr-port 6002",
|
"dev": "cross-env PORT=5002 parcel src/index.html --public-url ${CONSOLE_PUBLIC_URL:-/console} --no-cache --hmr-port 6002",
|
||||||
"check": "tsc --noEmit",
|
"check": "tsc --noEmit",
|
||||||
"build": "pnpm check && rm -rf dist && parcel build src/index.html --no-autoinstall --no-cache --public-url ${CONSOLE_PUBLIC_URL:-/console}",
|
"build": "pnpm generate && pnpm check && rm -rf dist && parcel build src/index.html --no-autoinstall --no-cache --public-url ${CONSOLE_PUBLIC_URL:-/console}",
|
||||||
"lint": "eslint --ext .ts --ext .tsx src",
|
"lint": "eslint --ext .ts --ext .tsx src",
|
||||||
"lint:report": "pnpm lint --format json --output-file report.json",
|
"lint:report": "pnpm lint --format json --output-file report.json",
|
||||||
"stylelint": "stylelint \"src/**/*.scss\"",
|
"stylelint": "stylelint \"src/**/*.scss\"",
|
||||||
|
@ -121,7 +123,8 @@
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"tslib": "^2.4.1",
|
"tslib": "^2.4.1",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4",
|
||||||
|
"zod-to-ts": "^1.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.9.0"
|
"node": "^20.9.0"
|
||||||
|
@ -141,6 +144,12 @@
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "@silverhand/react",
|
"extends": "@silverhand/react",
|
||||||
|
"parserOptions": {
|
||||||
|
"project": [
|
||||||
|
"./tsconfig.json",
|
||||||
|
"./tsconfig.scripts.gen.json"
|
||||||
|
]
|
||||||
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"react/function-component-definition": [
|
"react/function-component-definition": [
|
||||||
"error",
|
"error",
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import fs from 'node:fs';
|
||||||
|
|
||||||
|
import {
|
||||||
|
jwtCustomizerUserContextGuard,
|
||||||
|
accessTokenPayloadGuard,
|
||||||
|
clientCredentialsPayloadGuard,
|
||||||
|
} from '@logto/schemas';
|
||||||
|
import prettier from 'prettier';
|
||||||
|
import { type ZodTypeAny } from 'zod';
|
||||||
|
import { zodToTs, printNode } from 'zod-to-ts';
|
||||||
|
|
||||||
|
const filePath = 'src/consts/jwt-customizer-type-definition.ts';
|
||||||
|
|
||||||
|
const typeIdentifiers = `export enum JwtCustomizerTypeDefinitionKey {
|
||||||
|
JwtCustomizerUserContext = 'JwtCustomizerUserContext',
|
||||||
|
AccessTokenPayload = 'AccessTokenPayload',
|
||||||
|
ClientCredentialsPayload = 'ClientCredentialsPayload',
|
||||||
|
EnvironmentVariables = 'EnvironmentVariables',
|
||||||
|
};`;
|
||||||
|
|
||||||
|
const inferTsDefinitionFromZod = (zodSchema: ZodTypeAny, identifier: string): string => {
|
||||||
|
const { node } = zodToTs(zodSchema, identifier);
|
||||||
|
const typeDefinition = printNode(node);
|
||||||
|
|
||||||
|
return `type ${identifier} = ${typeDefinition};`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the jwt-customizer-type-definition.ts file
|
||||||
|
const createJwtCustomizerTypeDefinitions = async () => {
|
||||||
|
const jwtCustomizerUserContextTypeDefinition = inferTsDefinitionFromZod(
|
||||||
|
jwtCustomizerUserContextGuard,
|
||||||
|
'JwtCustomizerUserContext'
|
||||||
|
);
|
||||||
|
|
||||||
|
const accessTokenPayloadTypeDefinition = inferTsDefinitionFromZod(
|
||||||
|
accessTokenPayloadGuard,
|
||||||
|
'AccessTokenPayload'
|
||||||
|
);
|
||||||
|
|
||||||
|
const clientCredentialsPayloadTypeDefinition = inferTsDefinitionFromZod(
|
||||||
|
clientCredentialsPayloadGuard,
|
||||||
|
'ClientCredentialsPayload'
|
||||||
|
);
|
||||||
|
|
||||||
|
const fileContent = `/* This file is auto-generated. Do not modify it manually. */
|
||||||
|
${typeIdentifiers}
|
||||||
|
|
||||||
|
export const jwtCustomizerUserContextTypeDefinition = \`${jwtCustomizerUserContextTypeDefinition}\`;
|
||||||
|
|
||||||
|
export const accessTokenPayloadTypeDefinition = \`${accessTokenPayloadTypeDefinition}\`;
|
||||||
|
|
||||||
|
export const clientCredentialsPayloadTypeDefinition = \`${clientCredentialsPayloadTypeDefinition}\`;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const formattedFileContent = await prettier.format(fileContent, {
|
||||||
|
parser: 'typescript',
|
||||||
|
tabWidth: 2,
|
||||||
|
singleQuote: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
fs.writeFileSync(filePath, formattedFileContent);
|
||||||
|
};
|
||||||
|
|
||||||
|
void createJwtCustomizerTypeDefinitions();
|
|
@ -17,7 +17,11 @@ import ScriptSection from './ScriptSection';
|
||||||
import SettingsSection from './SettingsSection';
|
import SettingsSection from './SettingsSection';
|
||||||
import * as styles from './index.module.scss';
|
import * as styles from './index.module.scss';
|
||||||
import { type JwtClaimsFormType } from './type';
|
import { type JwtClaimsFormType } from './type';
|
||||||
import { formatResponseDataToFormData, formatFormDataToRequestData, getApiPath } from './utils';
|
import {
|
||||||
|
formatResponseDataToFormData,
|
||||||
|
formatFormDataToRequestData,
|
||||||
|
getApiPath,
|
||||||
|
} from './utils/format';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
@ -100,7 +104,7 @@ function Main({
|
||||||
onDiscard={reset}
|
onDiscard={reset}
|
||||||
onSubmit={onSubmitHandler}
|
onSubmit={onSubmitHandler}
|
||||||
/>
|
/>
|
||||||
<UnsavedChangesAlertModal hasUnsavedChanges={isDirty && !isSubmitting} />
|
<UnsavedChangesAlertModal hasUnsavedChanges={isDirty && !isSubmitting} onConfirm={reset} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ type Props = {
|
||||||
activeModelName?: string;
|
activeModelName?: string;
|
||||||
setActiveModel?: (name: string) => void;
|
setActiveModel?: (name: string) => void;
|
||||||
value?: string;
|
value?: string;
|
||||||
|
environmentVariablesDefinition?: string;
|
||||||
onChange?: (value: string | undefined) => void;
|
onChange?: (value: string | undefined) => void;
|
||||||
onMountHandler?: (editor: IStandaloneCodeEditor) => void;
|
onMountHandler?: (editor: IStandaloneCodeEditor) => void;
|
||||||
};
|
};
|
||||||
|
@ -37,6 +38,7 @@ type Props = {
|
||||||
* @param {(name: string) => void} prop.setActiveModel - The callback function to set the active model. Used to switch between tabs.
|
* @param {(name: string) => void} prop.setActiveModel - The callback function to set the active model. Used to switch between tabs.
|
||||||
* @param {string} prop.value - The value of the code editor for the current active model.
|
* @param {string} prop.value - The value of the code editor for the current active model.
|
||||||
* @param {(value: string | undefined) => void} prop.onChange - The callback function to handle the value change of the code editor.
|
* @param {(value: string | undefined) => void} prop.onChange - The callback function to handle the value change of the code editor.
|
||||||
|
* @param {string} [prop.environmentVariablesDefinition] - The environment variables type definition for the script section.
|
||||||
*
|
*
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
|
@ -46,6 +48,7 @@ function MonacoCodeEditor({
|
||||||
models,
|
models,
|
||||||
activeModelName,
|
activeModelName,
|
||||||
value,
|
value,
|
||||||
|
environmentVariablesDefinition,
|
||||||
setActiveModel,
|
setActiveModel,
|
||||||
onChange,
|
onChange,
|
||||||
onMountHandler,
|
onMountHandler,
|
||||||
|
@ -71,19 +74,28 @@ function MonacoCodeEditor({
|
||||||
|
|
||||||
// Set the global declarations for the active model
|
// Set the global declarations for the active model
|
||||||
// @see {@link https://microsoft.github.io/monaco-editor/typedoc/interfaces/languages.typescript.LanguageServiceDefaults.html#setExtraLibs}
|
// @see {@link https://microsoft.github.io/monaco-editor/typedoc/interfaces/languages.typescript.LanguageServiceDefaults.html#setExtraLibs}
|
||||||
if (activeModel.globalDeclarations) {
|
if (activeModel.extraLibs) {
|
||||||
monaco.languages.typescript.typescriptDefaults.setExtraLibs([
|
monaco.languages.typescript.typescriptDefaults.setExtraLibs(activeModel.extraLibs);
|
||||||
{
|
|
||||||
content: activeModel.globalDeclarations,
|
|
||||||
filePath: `file:///global.d.ts`,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}, [activeModel, monaco]);
|
|
||||||
|
// Set the environment variables type definition for the active model
|
||||||
|
if (environmentVariablesDefinition) {
|
||||||
|
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||||
|
environmentVariablesDefinition,
|
||||||
|
'environmentVariables.d.ts'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [activeModel, monaco, environmentVariablesDefinition]);
|
||||||
|
|
||||||
const handleEditorWillMount = useCallback<BeforeMount>((monaco) => {
|
const handleEditorWillMount = useCallback<BeforeMount>((monaco) => {
|
||||||
// Register the new logto theme
|
// Register the new logto theme
|
||||||
monaco.editor.defineTheme('logto-dark', logtoDarkTheme);
|
monaco.editor.defineTheme('logto-dark', logtoDarkTheme);
|
||||||
|
|
||||||
|
// Set the typescript compiler options
|
||||||
|
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
||||||
|
allowNonTsExtensions: true,
|
||||||
|
strictNullChecks: true,
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleEditorDidMount = useCallback<OnMount>(
|
const handleEditorDidMount = useCallback<OnMount>(
|
||||||
|
|
|
@ -4,6 +4,11 @@ export type IStandaloneThemeData = Parameters<Monaco['editor']['defineTheme']>[1
|
||||||
|
|
||||||
export type IStandaloneCodeEditor = Parameters<OnMount>[0];
|
export type IStandaloneCodeEditor = Parameters<OnMount>[0];
|
||||||
|
|
||||||
|
type ExtraLibrary = {
|
||||||
|
content: string;
|
||||||
|
filePath: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type ModelSettings = {
|
export type ModelSettings = {
|
||||||
/** 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} */
|
/** 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;
|
name: string;
|
||||||
|
@ -19,7 +24,7 @@ export type ModelSettings = {
|
||||||
* @see {@link https://microsoft.github.io/monaco-editor/typedoc/interfaces/languages.typescript.LanguageServiceDefaults.html#setExtraLibs}
|
* @see {@link https://microsoft.github.io/monaco-editor/typedoc/interfaces/languages.typescript.LanguageServiceDefaults.html#setExtraLibs}
|
||||||
* We use this to load the global type declarations for the active model
|
* We use this to load the global type declarations for the active model
|
||||||
*/
|
*/
|
||||||
globalDeclarations?: string;
|
extraLibs?: ExtraLibrary[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ModelControl = {
|
export type ModelControl = {
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
/* Code Editor for the custom JWT claims script. */
|
/* Code Editor for the custom JWT claims script. */
|
||||||
import { LogtoJwtTokenPath } from '@logto/schemas';
|
import { LogtoJwtTokenPath } from '@logto/schemas';
|
||||||
import { useMemo, useContext, useCallback } from 'react';
|
import { useMemo, useContext, useCallback } from 'react';
|
||||||
import { useFormContext, Controller } from 'react-hook-form';
|
import { useFormContext, Controller, useWatch } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import Card from '@/ds-components/Card';
|
import Card from '@/ds-components/Card';
|
||||||
|
|
||||||
import { CodeEditorLoadingContext } from './CodeEditorLoadingContext';
|
import { CodeEditorLoadingContext } from './CodeEditorLoadingContext';
|
||||||
import MonacoCodeEditor, { type ModelSettings } from './MonacoCodeEditor';
|
import MonacoCodeEditor, { type ModelSettings } from './MonacoCodeEditor';
|
||||||
import { userJwtFile, machineToMachineJwtFile } from './config';
|
|
||||||
import * as styles from './index.module.scss';
|
import * as styles from './index.module.scss';
|
||||||
import { type JwtClaimsFormType } from './type';
|
import { type JwtClaimsFormType } from './type';
|
||||||
|
import { accessTokenJwtCustomizerModel, clientCredentialsModel } from './utils/config';
|
||||||
|
import { buildEnvironmentVariablesTypeDefinition } from './utils/type-definitions';
|
||||||
|
|
||||||
const titlePhrases = Object.freeze({
|
const titlePhrases = Object.freeze({
|
||||||
[LogtoJwtTokenPath.AccessToken]: 'user_jwt',
|
[LogtoJwtTokenPath.AccessToken]: 'user_jwt',
|
||||||
|
@ -21,12 +22,28 @@ function ScriptSection() {
|
||||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||||
|
|
||||||
const { watch, control } = useFormContext<JwtClaimsFormType>();
|
const { watch, control } = useFormContext<JwtClaimsFormType>();
|
||||||
|
|
||||||
const tokenType = watch('tokenType');
|
const tokenType = watch('tokenType');
|
||||||
|
|
||||||
|
// Need to use useWatch hook to subscribe the mutation of the environmentVariables field
|
||||||
|
// Otherwise, the default watch function's return value won't mutate when the environmentVariables field changes
|
||||||
|
const envVariables = useWatch({
|
||||||
|
control,
|
||||||
|
name: 'environmentVariables',
|
||||||
|
});
|
||||||
|
|
||||||
|
const environmentVariablesTypeDefinition = useMemo(
|
||||||
|
() => buildEnvironmentVariablesTypeDefinition(envVariables),
|
||||||
|
[envVariables]
|
||||||
|
);
|
||||||
|
|
||||||
const { setIsMonacoLoaded } = useContext(CodeEditorLoadingContext);
|
const { setIsMonacoLoaded } = useContext(CodeEditorLoadingContext);
|
||||||
|
|
||||||
const activeModel = useMemo<ModelSettings>(
|
const activeModel = useMemo<ModelSettings>(
|
||||||
() => (tokenType === LogtoJwtTokenPath.AccessToken ? userJwtFile : machineToMachineJwtFile),
|
() =>
|
||||||
|
tokenType === LogtoJwtTokenPath.AccessToken
|
||||||
|
? accessTokenJwtCustomizerModel
|
||||||
|
: clientCredentialsModel,
|
||||||
[tokenType]
|
[tokenType]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -54,6 +71,7 @@ function ScriptSection() {
|
||||||
models={[activeModel]}
|
models={[activeModel]}
|
||||||
activeModelName={activeModel.name}
|
activeModelName={activeModel.name}
|
||||||
value={value}
|
value={value}
|
||||||
|
environmentVariablesDefinition={environmentVariablesTypeDefinition}
|
||||||
onChange={(newValue) => {
|
onChange={(newValue) => {
|
||||||
// If the value is the same as the default code and the original form script value is undefined, reset the value to undefined as well
|
// If the value is the same as the default code and the original form script value is undefined, reset the value to undefined as well
|
||||||
if (newValue === activeModel.defaultValue && !defaultValues?.script) {
|
if (newValue === activeModel.defaultValue && !defaultValues?.script) {
|
||||||
|
|
|
@ -7,13 +7,13 @@ import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import Table from '@/ds-components/Table';
|
import Table from '@/ds-components/Table';
|
||||||
|
|
||||||
|
import { type JwtClaimsFormType } from '../type';
|
||||||
import {
|
import {
|
||||||
userDataDescription,
|
userDataDescription,
|
||||||
tokenDataDescription,
|
tokenDataDescription,
|
||||||
fetchExternalDataEditorOptions,
|
fetchExternalDataEditorOptions,
|
||||||
fetchExternalDataCodeExample,
|
fetchExternalDataCodeExample,
|
||||||
} from '../config';
|
} from '../utils/config';
|
||||||
import { type JwtClaimsFormType } from '../type';
|
|
||||||
|
|
||||||
import EnvironmentVariablesField from './EnvironmentVariablesField';
|
import EnvironmentVariablesField from './EnvironmentVariablesField';
|
||||||
import GuideCard, { CardType } from './GuideCard';
|
import GuideCard, { CardType } from './GuideCard';
|
||||||
|
|
|
@ -8,12 +8,12 @@ import Button from '@/ds-components/Button';
|
||||||
import Card from '@/ds-components/Card';
|
import Card from '@/ds-components/Card';
|
||||||
|
|
||||||
import MonacoCodeEditor, { type ModelControl } from '../MonacoCodeEditor/index.js';
|
import MonacoCodeEditor, { type ModelControl } from '../MonacoCodeEditor/index.js';
|
||||||
|
import { type JwtClaimsFormType } from '../type.js';
|
||||||
import {
|
import {
|
||||||
userTokenPayloadTestModel,
|
userTokenPayloadTestModel,
|
||||||
machineToMachineTokenPayloadTestModel,
|
machineToMachineTokenPayloadTestModel,
|
||||||
userTokenContextTestModel,
|
userTokenContextTestModel,
|
||||||
} from '../config.js';
|
} from '../utils/config.js';
|
||||||
import { type JwtClaimsFormType } from '../type.js';
|
|
||||||
|
|
||||||
import TestResult, { type TestResultData } from './TestResult.js';
|
import TestResult, { type TestResultData } from './TestResult.js';
|
||||||
import * as styles from './index.module.scss';
|
import * as styles from './index.module.scss';
|
||||||
|
|
|
@ -11,7 +11,7 @@ import useApi from '@/hooks/use-api';
|
||||||
import useSwrFetcher from '@/hooks/use-swr-fetcher';
|
import useSwrFetcher from '@/hooks/use-swr-fetcher';
|
||||||
import { shouldRetryOnError } from '@/utils/request';
|
import { shouldRetryOnError } from '@/utils/request';
|
||||||
|
|
||||||
import { getApiPath } from './utils';
|
import { getApiPath } from './utils/format';
|
||||||
|
|
||||||
function useJwtCustomizer() {
|
function useJwtCustomizer() {
|
||||||
const fetchApi = useApi({ hideErrorToast: true });
|
const fetchApi = useApi({ hideErrorToast: true });
|
||||||
|
|
|
@ -3,52 +3,40 @@ import { type EditorProps } from '@monaco-editor/react';
|
||||||
import TokenFileIcon from '@/assets/icons/token-file-icon.svg';
|
import TokenFileIcon from '@/assets/icons/token-file-icon.svg';
|
||||||
import UserFileIcon from '@/assets/icons/user-file-icon.svg';
|
import UserFileIcon from '@/assets/icons/user-file-icon.svg';
|
||||||
|
|
||||||
import type { ModelSettings } from './MonacoCodeEditor/type.js';
|
import type { ModelSettings } from '../MonacoCodeEditor/type.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
JwtCustomizerTypeDefinitionKey,
|
||||||
|
buildAccessTokenJwtCustomizerContextTsDefinition,
|
||||||
|
buildClientCredentialsJwtCustomizerContextTsDefinition,
|
||||||
|
} from './type-definitions.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JWT token code editor configuration
|
* JWT token code editor configuration
|
||||||
*/
|
*/
|
||||||
const userJwtGlobalDeclarations = `
|
const accessTokenJwtCustomizerDefinition = `
|
||||||
declare global {
|
declare global {
|
||||||
export interface CustomJwtClaims extends Record<string, any> {}
|
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
|
/** Logto internal data that can be used to pass additional information
|
||||||
* @param {User} user - The user info associated with the token.
|
* @param {${JwtCustomizerTypeDefinitionKey.JwtCustomizerUserContext}} user - The user info associated with the token.
|
||||||
*/
|
*/
|
||||||
export type Data = {
|
export type Data = {
|
||||||
user: User;
|
user: ${JwtCustomizerTypeDefinitionKey.JwtCustomizerUserContext};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Exports {
|
export interface Exports {
|
||||||
/**
|
/**
|
||||||
* This function is called to get custom claims for the JWT token.
|
* This function is called to get custom claims for the JWT token.
|
||||||
*
|
*
|
||||||
* @param {string} token -The JWT token.
|
* @param {${JwtCustomizerTypeDefinitionKey.AccessTokenPayload}} token -The JWT token.
|
||||||
* @param {Data} data - Logto internal data that can be used to pass additional information
|
* @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.
|
* @param {${JwtCustomizerTypeDefinitionKey.JwtCustomizerUserContext}} data.user - The user info associated with the token.
|
||||||
|
* @param {${JwtCustomizerTypeDefinitionKey.EnvironmentVariables}} envVariables - The environment variables.
|
||||||
*
|
*
|
||||||
* @returns The custom claims.
|
* @returns The custom claims.
|
||||||
*/
|
*/
|
||||||
getCustomJwtClaims: (token: string, data: Data) => Promise<CustomJwtClaims>;
|
getCustomJwtClaims: (token: ${JwtCustomizerTypeDefinitionKey.AccessTokenPayload}, data: Data, envVariables: ${JwtCustomizerTypeDefinitionKey.EnvironmentVariables}) => Promise<CustomJwtClaims>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const exports: Exports;
|
const exports: Exports;
|
||||||
|
@ -57,7 +45,7 @@ declare global {
|
||||||
export { exports as default };
|
export { exports as default };
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const machineToMachineJwtGlobalDeclarations = `
|
const clientCredentialsJwtCustomizerDefinition = `
|
||||||
declare global {
|
declare global {
|
||||||
export interface CustomJwtClaims extends Record<string, any> {}
|
export interface CustomJwtClaims extends Record<string, any> {}
|
||||||
|
|
||||||
|
@ -65,11 +53,11 @@ declare global {
|
||||||
/**
|
/**
|
||||||
* This function is called to get custom claims for the JWT token.
|
* This function is called to get custom claims for the JWT token.
|
||||||
*
|
*
|
||||||
* @param {string} token -The JWT token.
|
* @param {${JwtCustomizerTypeDefinitionKey.ClientCredentialsPayload}} token -The JWT token.
|
||||||
*
|
*
|
||||||
* @returns The custom claims.
|
* @returns The custom claims.
|
||||||
*/
|
*/
|
||||||
getCustomJwtClaims: (token: string) => Promise<CustomJwtClaims>;
|
getCustomJwtClaims: (token: ${JwtCustomizerTypeDefinitionKey.ClientCredentialsPayload}, envVariables: ${JwtCustomizerTypeDefinitionKey.EnvironmentVariables}) => Promise<CustomJwtClaims>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const exports: Exports;
|
const exports: Exports;
|
||||||
|
@ -78,12 +66,13 @@ declare global {
|
||||||
export { exports as default };
|
export { exports as default };
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const defaultUserJwtClaimsCode = `/**
|
const defaultAccessTokenJwtCustomizerCode = `/**
|
||||||
* This function is called to get custom claims for the JWT token.
|
* This function is called to get custom claims for the JWT token.
|
||||||
*
|
*
|
||||||
* @param {string} token -The JWT token.
|
* @param {${JwtCustomizerTypeDefinitionKey.AccessTokenPayload}} token -The JWT token.
|
||||||
* @param {Data} data - Logto internal data that can be used to pass additional information
|
* @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.
|
* @param {${JwtCustomizerTypeDefinitionKey.JwtCustomizerUserContext}} data.user - The user info associated with the token.
|
||||||
|
* @param {${JwtCustomizerTypeDefinitionKey.EnvironmentVariables}} [envVariables] - The environment variables.
|
||||||
*
|
*
|
||||||
* @returns The custom claims.
|
* @returns The custom claims.
|
||||||
*/
|
*/
|
||||||
|
@ -92,10 +81,11 @@ exports.getCustomJwtClaims = async (token, data) => {
|
||||||
return {};
|
return {};
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
const defaultMachineToMachineJwtClaimsCode = `/**
|
const defaultClientCredentialsJwtCustomizerCode = `/**
|
||||||
* This function is called to get custom claims for the JWT token.
|
* This function is called to get custom claims for the JWT token.
|
||||||
*
|
*
|
||||||
* @param {string} token -The JWT token.
|
* @param {${JwtCustomizerTypeDefinitionKey.ClientCredentialsPayload}} token -The JWT token.
|
||||||
|
* @param {${JwtCustomizerTypeDefinitionKey.EnvironmentVariables}} [envVariables] - The environment variables.
|
||||||
*
|
*
|
||||||
* @returns The custom claims.
|
* @returns The custom claims.
|
||||||
*/
|
*/
|
||||||
|
@ -104,20 +94,38 @@ exports.getCustomJwtClaims = async (token) => {
|
||||||
return {};
|
return {};
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
export const userJwtFile: ModelSettings = {
|
export const accessTokenJwtCustomizerModel: ModelSettings = {
|
||||||
name: 'user-jwt.ts',
|
name: 'user-jwt.ts',
|
||||||
title: 'TypeScript',
|
title: 'TypeScript',
|
||||||
language: 'typescript',
|
language: 'typescript',
|
||||||
defaultValue: defaultUserJwtClaimsCode,
|
defaultValue: defaultAccessTokenJwtCustomizerCode,
|
||||||
globalDeclarations: userJwtGlobalDeclarations,
|
extraLibs: [
|
||||||
|
{
|
||||||
|
content: accessTokenJwtCustomizerDefinition,
|
||||||
|
filePath: `file:///logto-jwt-customizer.d.ts`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: buildAccessTokenJwtCustomizerContextTsDefinition(),
|
||||||
|
filePath: `file:///logto-jwt-customizer-context.d.ts`,
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const machineToMachineJwtFile: ModelSettings = {
|
export const clientCredentialsModel: ModelSettings = {
|
||||||
name: 'machine-to-machine-jwt.ts',
|
name: 'machine-to-machine-jwt.ts',
|
||||||
title: 'TypeScript',
|
title: 'TypeScript',
|
||||||
language: 'typescript',
|
language: 'typescript',
|
||||||
defaultValue: defaultMachineToMachineJwtClaimsCode,
|
defaultValue: defaultClientCredentialsJwtCustomizerCode,
|
||||||
globalDeclarations: machineToMachineJwtGlobalDeclarations,
|
extraLibs: [
|
||||||
|
{
|
||||||
|
content: clientCredentialsJwtCustomizerDefinition,
|
||||||
|
filePath: `file:///logto-jwt-customizer.d.ts`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: buildClientCredentialsJwtCustomizerContextTsDefinition(),
|
||||||
|
filePath: `file:///logto-jwt-customizer-context.d.ts`,
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -4,7 +4,7 @@ import {
|
||||||
type ClientCredentialsJwtCustomizer,
|
type ClientCredentialsJwtCustomizer,
|
||||||
} from '@logto/schemas';
|
} from '@logto/schemas';
|
||||||
|
|
||||||
import type { JwtClaimsFormType } from './type';
|
import type { JwtClaimsFormType } from '../type';
|
||||||
|
|
||||||
const formatEnvVariablesResponseToFormData = (
|
const formatEnvVariablesResponseToFormData = (
|
||||||
enVariables?: AccessTokenJwtCustomizer['envVars']
|
enVariables?: AccessTokenJwtCustomizer['envVars']
|
|
@ -0,0 +1,34 @@
|
||||||
|
import {
|
||||||
|
JwtCustomizerTypeDefinitionKey,
|
||||||
|
accessTokenPayloadTypeDefinition,
|
||||||
|
jwtCustomizerUserContextTypeDefinition,
|
||||||
|
clientCredentialsPayloadTypeDefinition,
|
||||||
|
} from '@/consts/jwt-customizer-type-definition';
|
||||||
|
|
||||||
|
import { type JwtClaimsFormType } from '../type';
|
||||||
|
|
||||||
|
export { JwtCustomizerTypeDefinitionKey } from '@/consts/jwt-customizer-type-definition';
|
||||||
|
|
||||||
|
export const buildAccessTokenJwtCustomizerContextTsDefinition = () => {
|
||||||
|
return `declare ${jwtCustomizerUserContextTypeDefinition}
|
||||||
|
|
||||||
|
declare ${accessTokenPayloadTypeDefinition}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const buildClientCredentialsJwtCustomizerContextTsDefinition = () =>
|
||||||
|
`declare ${clientCredentialsPayloadTypeDefinition}`;
|
||||||
|
|
||||||
|
export const buildEnvironmentVariablesTypeDefinition = (
|
||||||
|
envVariables?: JwtClaimsFormType['environmentVariables']
|
||||||
|
) => {
|
||||||
|
const typeDefinition = envVariables
|
||||||
|
? `{
|
||||||
|
${envVariables
|
||||||
|
.filter(({ key }) => Boolean(key))
|
||||||
|
.map(({ key }) => `${key}: string`)
|
||||||
|
.join(';\n')}
|
||||||
|
}`
|
||||||
|
: 'undefined';
|
||||||
|
|
||||||
|
return `declare type ${JwtCustomizerTypeDefinitionKey.EnvironmentVariables} = ${typeDefinition}`;
|
||||||
|
};
|
7
packages/console/tsconfig.scripts.gen.json
Normal file
7
packages/console/tsconfig.scripts.gen.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"extends": "@silverhand/ts-config/tsconfig.base",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "scripts-js"
|
||||||
|
},
|
||||||
|
"include": ["scripts"]
|
||||||
|
}
|
|
@ -3200,6 +3200,9 @@ importers:
|
||||||
zod:
|
zod:
|
||||||
specifier: ^3.22.4
|
specifier: ^3.22.4
|
||||||
version: 3.22.4
|
version: 3.22.4
|
||||||
|
zod-to-ts:
|
||||||
|
specifier: ^1.2.0
|
||||||
|
version: 1.2.0(typescript@5.3.3)(zod@3.22.4)
|
||||||
|
|
||||||
packages/core:
|
packages/core:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -21483,6 +21486,16 @@ packages:
|
||||||
resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
|
resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
|
||||||
engines: {node: '>=12.20'}
|
engines: {node: '>=12.20'}
|
||||||
|
|
||||||
|
/zod-to-ts@1.2.0(typescript@5.3.3)(zod@3.22.4):
|
||||||
|
resolution: {integrity: sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==}
|
||||||
|
peerDependencies:
|
||||||
|
typescript: ^4.9.4 || ^5.0.2
|
||||||
|
zod: ^3
|
||||||
|
dependencies:
|
||||||
|
typescript: 5.3.3
|
||||||
|
zod: 3.22.4
|
||||||
|
dev: true
|
||||||
|
|
||||||
/zod@3.22.4:
|
/zod@3.22.4:
|
||||||
resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
|
resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
|
Loading…
Reference in a new issue