diff --git a/.changeset/few-moose-sniff.md b/.changeset/few-moose-sniff.md new file mode 100644 index 000000000..1f7b91aaf --- /dev/null +++ b/.changeset/few-moose-sniff.md @@ -0,0 +1,5 @@ +--- +"@logto/demo-app": minor +--- + +add dev panel diff --git a/packages/console/package.json b/packages/console/package.json index 40f98746c..03a3210bb 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -33,7 +33,7 @@ "@logto/language-kit": "workspace:^1.1.0", "@logto/phrases": "workspace:^1.11.0", "@logto/phrases-experience": "workspace:^1.6.1", - "@logto/react": "^3.0.8", + "@logto/react": "^3.0.11", "@logto/schemas": "workspace:^1.17.0", "@logto/shared": "workspace:^3.1.1", "@mdx-js/mdx": "^3.0.1", diff --git a/packages/demo-app/package.json b/packages/demo-app/package.json index 3f3601d32..ff0551ec4 100644 --- a/packages/demo-app/package.json +++ b/packages/demo-app/package.json @@ -22,7 +22,7 @@ "@logto/core-kit": "workspace:^2.4.0", "@logto/language-kit": "workspace:^1.1.0", "@logto/phrases": "workspace:^1.10.0", - "@logto/react": "^3.0.8", + "@logto/react": "^3.0.11", "@logto/schemas": "workspace:^1.15.0", "@parcel/core": "2.9.3", "@parcel/transformer-sass": "2.9.3", @@ -37,6 +37,7 @@ "eslint": "^8.56.0", "i18next": "^22.4.15", "i18next-browser-languagedetector": "^8.0.0", + "jose": "^5.0.0", "lint-staged": "^15.0.0", "parcel": "2.9.3", "postcss": "^8.4.31", diff --git a/packages/demo-app/src/App.module.scss b/packages/demo-app/src/App.module.scss index fbf059be4..b739ae103 100644 --- a/packages/demo-app/src/App.module.scss +++ b/packages/demo-app/src/App.module.scss @@ -3,40 +3,113 @@ @use '@logto/core-kit/scss/console-themes' as themes; .app { + input { + background-color: var(--color-layer-1); + font: var(--font-body-2); + color: var(--color-text); + padding: 0 _.unit(3); + height: 36px; + border: 1px solid var(--color-border); + outline: 3px solid transparent; + border-radius: 6px; + } + + .button { + display: inline-block; + user-select: none; + border: 1px solid var(--color-border); + background-color: var(--color-layer-1); + border-radius: 8px; + padding: _.unit(3) _.unit(6); + font: var(--font-label-2); + color: var(--color-text); + transition: background ease-in-out 0.2s; + + &:hover { + cursor: pointer; + background: var(--color-hover); + } + + &:focus { + outline: 3px solid var(--color-focused-variant); + } + } + + .card { background: var(--color-layer-1); border-radius: 16px; - position: absolute; - left: 50%; - top: 50%; - width: 640px; - height: 640px; - transform: translate(-50%, -50%); display: flex; flex-direction: column; - align-items: center; - text-align: center; font-size: 14px; line-height: 20px; - img { - margin-top: _.unit(25); - width: 120px; - height: 120px; - } - .title { - margin-top: _.unit(6); color: var(--color-neutral-10); font: var(--font-title-2); } .text { - margin-top: _.unit(1); font: var(--font-body-2); color: var(--color-text-secondary); } + &.congrats { + position: absolute; + left: 50%; + top: 50%; + align-items: center; + text-align: center; + width: 640px; + height: 640px; + transform: translate(-50%, -50%); + + .title { + margin-top: _.unit(6); + } + + .text { + margin-top: _.unit(1); + } + + .button { + margin-top: _.unit(8); + } + } + + &.devPanel { + max-width: 800px; + width: 25vw; + position: fixed; + left: _.unit(2); + top: _.unit(2); + padding: _.unit(4); + gap: _.unit(3); + + .item { + margin: _.unit(2) 0; + display: flex; + flex-direction: column; + gap: _.unit(1); + } + + .button { + align-self: flex-end; + } + + .action { + display: flex; + justify-content: space-between; + align-items: center; + } + } + + img { + margin-top: _.unit(25); + width: 120px; + height: 120px; + } + .infoCard { margin-top: _.unit(4); padding: _.unit(4); @@ -61,25 +134,6 @@ } } - .button { - user-select: none; - margin-top: _.unit(8); - border: 1px solid var(--color-border); - border-radius: 8px; - padding: _.unit(3) _.unit(6); - font: var(--font-label-2); - color: var(--color-text); - transition: background ease-in-out 0.2s; - - &:hover { - cursor: pointer; - background: var(--color-hover); - } - - &:focus { - outline: 3px solid var(--color-focused-variant); - } - } .continue { margin-top: _.unit(12); @@ -121,6 +175,11 @@ } } +.error { + color: var(--color-neutral-10); + margin: _.unit(3); +} + @media (prefers-color-scheme: light) { body { @include themes.light; diff --git a/packages/demo-app/src/App.tsx b/packages/demo-app/src/App.tsx index b5e4b8768..be4102bbc 100644 --- a/packages/demo-app/src/App.tsx +++ b/packages/demo-app/src/App.tsx @@ -1,28 +1,41 @@ -import type { IdTokenClaims } from '@logto/react'; -import { LogtoProvider, useLogto, Prompt, UserScope } from '@logto/react'; +import { type IdTokenClaims, LogtoProvider, useLogto, type Prompt } from '@logto/react'; import { demoAppApplicationId } from '@logto/schemas'; -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import '@/scss/normalized.scss'; + import * as styles from './App.module.scss'; import Callback from './Callback'; +import DevPanel from './DevPanel'; import congratsDark from './assets/congrats-dark.svg'; import congrats from './assets/congrats.svg'; import initI18n from './i18n/init'; +import { getLocalData, setLocalData } from './utils'; void initI18n(); const Main = () => { + const params = new URL(window.location.href).searchParams; const { isAuthenticated, isLoading, getIdTokenClaims, signIn, signOut } = useLogto(); const [user, setUser] = useState>(); const { t } = useTranslation(undefined, { keyPrefix: 'demo_app' }); - const isInCallback = Boolean(new URL(window.location.href).searchParams.get('code')); + const isInCallback = Boolean(params.get('code')); const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches; const [congratsIcon, setCongratsIcon] = useState(isDarkMode ? congratsDark : congrats); + const [showDevPanel, setShowDevPanel] = useState(getLocalData('ui').showDevPanel ?? false); + const error = params.get('error'); + const errorDescription = params.get('error_description'); + + const toggleDevPanel = useCallback(() => { + setShowDevPanel((previous) => { + setLocalData('ui', { showDevPanel: !previous }); + return !previous; + }); + }, []); useEffect(() => { - if (isInCallback || isLoading) { + if (isInCallback || isLoading || error) { return; } @@ -43,7 +56,7 @@ const Main = () => { extraParams: Object.fromEntries(new URLSearchParams(window.location.search).entries()), }); } - }, [getIdTokenClaims, isAuthenticated, isInCallback, isLoading, signIn, user]); + }, [error, getIdTokenClaims, isAuthenticated, isInCallback, isLoading, signIn, user]); useEffect(() => { const onThemeChange = (event: MediaQueryListEvent) => { @@ -64,13 +77,37 @@ const Main = () => { return ; } + if (error) { + return ( +
+
+

+ Error occurred: {error} +
+ {errorDescription} +

+ +
+
+ ); + } + if (!isAuthenticated || !user) { return null; } return (
-
+ {showDevPanel && } +
{congratsIcon && Congrats}
{t('title')}
{t('subtitle')}
@@ -99,19 +136,36 @@ const Main = () => { > {t('sign_out')}
+
{ + if (key === 'Enter' || key === ' ') { + toggleDevPanel(); + } + }} + > + {showDevPanel ? 'Close' : 'Open'} dev panel +
); }; const App = () => { + const config = getLocalData('config'); + return (
diff --git a/packages/demo-app/src/DevPanel.tsx b/packages/demo-app/src/DevPanel.tsx new file mode 100644 index 000000000..3ec27ea1a --- /dev/null +++ b/packages/demo-app/src/DevPanel.tsx @@ -0,0 +1,114 @@ +import { useLogto } from '@logto/react'; +import { decodeJwt } from 'jose'; +import { useCallback, useState, type FormEventHandler } from 'react'; + +import * as styles from './App.module.scss'; +import { getLocalData, setLocalData } from './utils'; + +const safeDecodeJwt = (token: string) => { + try { + return decodeJwt(token); + } catch { + return token; + } +}; + +const DevPanel = () => { + const config = getLocalData('config'); + const [showSaved, setShowSaved] = useState(false); + const { getAccessToken, getIdTokenClaims, fetchUserInfo } = useLogto(); + + const submitConfig: FormEventHandler = useCallback((event) => { + event.preventDefault(); + const formData = new FormData(event.currentTarget); + const data = Object.fromEntries(formData.entries()); + setLocalData('config', data); + setShowSaved(true); + + setTimeout(() => { + setShowSaved(false); + }, 500); + }, []); + + const requestToken: FormEventHandler = useCallback( + async (event) => { + event.preventDefault(); + const formData = new FormData(event.currentTarget); + const data = Object.fromEntries(formData.entries()); + const token = await getAccessToken( + data.resource ? String(data.resource) : undefined, + data.organizationId ? String(data.organizationId) : undefined + ); + console.log(token ? safeDecodeJwt(token) : 'No token'); + }, + [getAccessToken] + ); + + return ( +
+
+
Logto config
+
+
Prompt
+ +
+
+
Scope
+ +
+
+
Resource (space delimited)
+ +
+
+
Sign out to apply changes.
+ +
+
+
+
Refresh token grant
+
+
Resource
+ +
+
+
Organization ID
+ +
+
+
See console for the result.
+ +
+
+
+
User info
+
See console for the result.
+

+ +

+

+ +

+
+
+ ); +}; +export default DevPanel; diff --git a/packages/demo-app/src/utils.ts b/packages/demo-app/src/utils.ts new file mode 100644 index 000000000..23eeb569a --- /dev/null +++ b/packages/demo-app/src/utils.ts @@ -0,0 +1,70 @@ +import { Prompt, UserScope } from '@logto/react'; +import { z } from 'zod'; + +type LocalLogtoConfig = { + prompt?: string; + scope?: string; + resource?: string; +}; + +const localLogtoConfigGuard = z.object({ + prompt: z.string(), + scope: z.string(), + resource: z.string(), +}) satisfies z.ZodType; + +type LocalUiConfig = { + showDevPanel?: boolean; +}; + +const localUiConfigGuard = z.object({ + showDevPanel: z.boolean(), +}) satisfies z.ZodType; + +type Key = 'config' | 'ui'; + +const keyPrefix = 'logto:demo-app:dev:'; + +type KeyToType = { + config: LocalLogtoConfig; + ui: LocalUiConfig; +}; + +const keyToGuard: Readonly<{ + [K in Key]: z.ZodType; +}> = Object.freeze({ + config: localLogtoConfigGuard, + ui: localUiConfigGuard, +}); + +const keyToDefault = Object.freeze({ + config: { + prompt: [Prompt.Login, Prompt.Consent].join(' '), + scope: [UserScope.Organizations, UserScope.OrganizationRoles].join(' '), + }, + ui: {}, +} satisfies Record); + +const safeJsonParse = (value: string): unknown => { + try { + return JSON.parse(value); + } catch { + return null; + } +}; + +const safeZodParse = (guard: z.ZodType, value: unknown) => { + const result = guard.safeParse(value); + return result.success ? result.data : {}; +}; + +export const getLocalData = (key: K): KeyToType[K] => { + const result = keyToGuard[key].safeParse( + safeJsonParse(localStorage.getItem(`${keyPrefix}${key}`) ?? '') + ); + return result.success ? result.data : keyToDefault[key]; +}; + +export const setLocalData = (key: Key, value: unknown) => { + localStorage.setItem(`${keyPrefix}${key}`, JSON.stringify(safeZodParse(keyToGuard[key], value))); +}; diff --git a/packages/phrases/src/locales/de/translation/demo-app.ts b/packages/phrases/src/locales/de/translation/demo-app.ts index 387aa9a6b..c029a2031 100644 --- a/packages/phrases/src/locales/de/translation/demo-app.ts +++ b/packages/phrases/src/locales/de/translation/demo-app.ts @@ -1,6 +1,5 @@ const demo_app = { title: 'Sie haben sich erfolgreich bei der Live-Vorschau angemeldet!', - subtitle: 'Hier sind Ihre Anmeldeinformationen:', username: 'Benutzername: ', user_id: 'Benutzer ID: ', sign_out: 'Abmeldung von der Live-Vorschau', diff --git a/packages/phrases/src/locales/en/translation/demo-app.ts b/packages/phrases/src/locales/en/translation/demo-app.ts index 112b45dbe..f97b4d0b6 100644 --- a/packages/phrases/src/locales/en/translation/demo-app.ts +++ b/packages/phrases/src/locales/en/translation/demo-app.ts @@ -1,6 +1,6 @@ const demo_app = { title: "You've successfully signed in the live preview!", - subtitle: 'Here is your log in information:', + subtitle: 'Here is your user information:', username: 'Username: ', user_id: 'User ID: ', sign_out: 'Sign out the live preview', diff --git a/packages/phrases/src/locales/es/translation/demo-app.ts b/packages/phrases/src/locales/es/translation/demo-app.ts index 6355736c6..007e52626 100644 --- a/packages/phrases/src/locales/es/translation/demo-app.ts +++ b/packages/phrases/src/locales/es/translation/demo-app.ts @@ -1,6 +1,5 @@ const demo_app = { title: '¡Ha iniciado sesión correctamente en la vista previa en vivo!', - subtitle: 'Aquí está su información de inicio de sesión:', username: 'Nombre de usuario: ', user_id: 'ID de usuario: ', sign_out: 'Cerrar sesión en la vista previa en vivo', diff --git a/packages/phrases/src/locales/fr/translation/demo-app.ts b/packages/phrases/src/locales/fr/translation/demo-app.ts index 1116656f4..9513c7c0e 100644 --- a/packages/phrases/src/locales/fr/translation/demo-app.ts +++ b/packages/phrases/src/locales/fr/translation/demo-app.ts @@ -1,6 +1,5 @@ const demo_app = { title: "Vous vous êtes connecté avec succès à l'aperçu en direct !", - subtitle: 'Voici vos informations de connexion :', username: "Nom d'utilisateur :", user_id: "ID de l'utilisateur :", sign_out: "Se déconnecter de l'aperçu en direct", diff --git a/packages/phrases/src/locales/it/translation/demo-app.ts b/packages/phrases/src/locales/it/translation/demo-app.ts index 4007a0985..dd8abf5a1 100644 --- a/packages/phrases/src/locales/it/translation/demo-app.ts +++ b/packages/phrases/src/locales/it/translation/demo-app.ts @@ -1,6 +1,5 @@ const demo_app = { title: "Hai effettuato l'accesso anteprima live con successo!", - subtitle: 'Ecco le tue informazioni di accesso:', username: 'Nome utente: ', user_id: 'ID utente: ', sign_out: "Esci dall'anteprima live", diff --git a/packages/phrases/src/locales/ja/translation/demo-app.ts b/packages/phrases/src/locales/ja/translation/demo-app.ts index 53b233467..6166e0ef4 100644 --- a/packages/phrases/src/locales/ja/translation/demo-app.ts +++ b/packages/phrases/src/locales/ja/translation/demo-app.ts @@ -1,6 +1,5 @@ const demo_app = { title: 'ライブプレビューへのサインインに成功しました!', - subtitle: 'あなたのログイン情報は以下の通りです:', username: 'ユーザー名:', user_id: 'ユーザーID:', sign_out: 'ライブプレビューからサインアウトする', diff --git a/packages/phrases/src/locales/ko/translation/demo-app.ts b/packages/phrases/src/locales/ko/translation/demo-app.ts index 6e18e8dde..2089c0d18 100644 --- a/packages/phrases/src/locales/ko/translation/demo-app.ts +++ b/packages/phrases/src/locales/ko/translation/demo-app.ts @@ -1,6 +1,5 @@ const demo_app = { title: 'Live Preview에 성공적으로 로그인했습니다!', - subtitle: '여기 로그인 정보가 있어요:', username: '사용자 이름: ', user_id: '사용자 ID: ', sign_out: 'Live Preview에서 로그아웃', diff --git a/packages/phrases/src/locales/pl-pl/translation/demo-app.ts b/packages/phrases/src/locales/pl-pl/translation/demo-app.ts index 096780ff6..20c8e8b2e 100644 --- a/packages/phrases/src/locales/pl-pl/translation/demo-app.ts +++ b/packages/phrases/src/locales/pl-pl/translation/demo-app.ts @@ -1,6 +1,5 @@ const demo_app = { title: 'Pomyślnie zalogowałeś się do podglądu na żywo!', - subtitle: 'Oto twoje informacje logowania:', username: 'Nazwa użytkownika: ', user_id: 'ID użytkownika: ', sign_out: 'Wyloguj się z podglądu na żywo', diff --git a/packages/phrases/src/locales/pt-br/translation/demo-app.ts b/packages/phrases/src/locales/pt-br/translation/demo-app.ts index f5fad17b7..511c5109e 100644 --- a/packages/phrases/src/locales/pt-br/translation/demo-app.ts +++ b/packages/phrases/src/locales/pt-br/translation/demo-app.ts @@ -1,6 +1,5 @@ const demo_app = { title: "You've successfully signed in the live preview!", - subtitle: 'Aqui estão suas informações de login:', username: 'Nome de usuário: ', user_id: 'ID do usuário: ', sign_out: 'Sair da Visualização ao Vivo', diff --git a/packages/phrases/src/locales/pt-pt/translation/demo-app.ts b/packages/phrases/src/locales/pt-pt/translation/demo-app.ts index b80249292..013b13329 100644 --- a/packages/phrases/src/locales/pt-pt/translation/demo-app.ts +++ b/packages/phrases/src/locales/pt-pt/translation/demo-app.ts @@ -1,6 +1,5 @@ const demo_app = { title: 'Iniciou sessão com sucesso na pré-visualização ao vivo!', - subtitle: 'Aqui estão as suas informações de login:', username: 'Utilizador: ', user_id: 'ID de utilizador: ', sign_out: 'Terminar sessão na visualização ao vivo', diff --git a/packages/phrases/src/locales/ru/translation/demo-app.ts b/packages/phrases/src/locales/ru/translation/demo-app.ts index b072bc3c9..65bcdec62 100644 --- a/packages/phrases/src/locales/ru/translation/demo-app.ts +++ b/packages/phrases/src/locales/ru/translation/demo-app.ts @@ -1,6 +1,5 @@ const demo_app = { title: 'Вы успешно вошли в живой просмотр!', - subtitle: 'Вот ваши данные для входа:', username: 'Имя пользователя: ', user_id: 'Идентификатор пользователя: ', sign_out: 'Выйти из живого просмотра', diff --git a/packages/phrases/src/locales/tr-tr/translation/demo-app.ts b/packages/phrases/src/locales/tr-tr/translation/demo-app.ts index d22b9aa5f..ec595fd6b 100644 --- a/packages/phrases/src/locales/tr-tr/translation/demo-app.ts +++ b/packages/phrases/src/locales/tr-tr/translation/demo-app.ts @@ -1,6 +1,5 @@ const demo_app = { title: 'Canlı önizlemede başarıyla oturum açtınız!', - subtitle: 'Sisteme giriş bilgileriniz:', username: 'Kullanıcı Adı: ', user_id: 'Kullanıcı Kimliği: ', sign_out: 'Canlı Önizlemeyi Kapat', diff --git a/packages/phrases/src/locales/zh-cn/translation/demo-app.ts b/packages/phrases/src/locales/zh-cn/translation/demo-app.ts index 97c736a97..d95201960 100644 --- a/packages/phrases/src/locales/zh-cn/translation/demo-app.ts +++ b/packages/phrases/src/locales/zh-cn/translation/demo-app.ts @@ -1,6 +1,5 @@ const demo_app = { title: '你已成功登录实时预览!', - subtitle: '以下是本次登录的用户信息:', username: '用户名:', user_id: '用户 ID:', sign_out: '退出实时预览', diff --git a/packages/phrases/src/locales/zh-hk/translation/demo-app.ts b/packages/phrases/src/locales/zh-hk/translation/demo-app.ts index ad8e5f980..23c5e617e 100644 --- a/packages/phrases/src/locales/zh-hk/translation/demo-app.ts +++ b/packages/phrases/src/locales/zh-hk/translation/demo-app.ts @@ -1,6 +1,5 @@ const demo_app = { title: '你已成功登錄實時預覽!', - subtitle: '以下是本次登錄的用戶信息:', username: '用戶名:', user_id: '用戶 ID:', sign_out: '退出實時預覽', diff --git a/packages/phrases/src/locales/zh-tw/translation/demo-app.ts b/packages/phrases/src/locales/zh-tw/translation/demo-app.ts index ad8e5f980..23c5e617e 100644 --- a/packages/phrases/src/locales/zh-tw/translation/demo-app.ts +++ b/packages/phrases/src/locales/zh-tw/translation/demo-app.ts @@ -1,6 +1,5 @@ const demo_app = { title: '你已成功登錄實時預覽!', - subtitle: '以下是本次登錄的用戶信息:', username: '用戶名:', user_id: '用戶 ID:', sign_out: '退出實時預覽', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 22c80cfb9..c0b88df59 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2876,8 +2876,8 @@ importers: specifier: workspace:^1.6.1 version: link:../phrases-experience '@logto/react': - specifier: ^3.0.8 - version: 3.0.8(react@18.2.0) + specifier: ^3.0.11 + version: 3.0.11(react@18.2.0) '@logto/schemas': specifier: workspace:^1.17.0 version: link:../schemas @@ -3471,8 +3471,8 @@ importers: specifier: workspace:^1.10.0 version: link:../phrases '@logto/react': - specifier: ^3.0.8 - version: 3.0.8(react@18.2.0) + specifier: ^3.0.11 + version: 3.0.11(react@18.2.0) '@logto/schemas': specifier: workspace:^1.15.0 version: link:../schemas @@ -3515,6 +3515,9 @@ importers: i18next-browser-languagedetector: specifier: ^8.0.0 version: 8.0.0 + jose: + specifier: ^5.0.0 + version: 5.2.4 lint-staged: specifier: ^15.0.0 version: 15.0.2 @@ -5242,12 +5245,15 @@ packages: resolution: {integrity: sha512-yDWSZMI2Qo/xoYU92tnwSP/gnSvq8+CLK5DqD/4brO42QJa7xjt7eA+HSyuMmSUrKffY2nP3riU81gs+nR8DkA==} engines: {node: ^18.12.0} - '@logto/browser@2.2.10': - resolution: {integrity: sha512-y6NauaxctqpfApccP6uFVmpg/vG1OhsDVLD4Pdpzbmj3whl63Nb17yxSTQHt4eYNKmSZJ2SzudAnMnVEYD91iQ==} + '@logto/browser@2.2.13': + resolution: {integrity: sha512-7fyenm6f2xSzZc8GHFpAL38rxAXf1hQH/ySSow8QHjgypF0pz9zy9bRSG1oVFgsMrFWYf73dPk7V2ACQ5Tujww==} '@logto/client@2.6.6': resolution: {integrity: sha512-QT7jMnzEIWHBNrf9/M8p1OErRBbbNZjoekXGji5aZCyUh975hh8+GEBL21HV71FT3H/5Cq4Gf1GzUbAIW3izMA==} + '@logto/client@2.7.0': + resolution: {integrity: sha512-8mj+757befwQJ4M0h4f3fQwUB1lruDkyLNTyRFOQxFfDMN1QeD70B4nMnz6iTX/Gi09HnIskmj9MHWrbvH6LSQ==} + '@logto/cloud@0.2.5-a7eedce': resolution: {integrity: sha512-FFjkGjqUgn9PCZnSuCODm2FcjqBm4JfPxfHCiXlOkUjeUhTJLrj7C0gjKzSQ/B6IaWri4EXN/meuqi5z/AMIPg==} engines: {node: ^20.9.0} @@ -5255,11 +5261,14 @@ packages: '@logto/js@4.1.1': resolution: {integrity: sha512-+RgthBvDw30UojirtAjZeHNfOwDQVURmpjcIBYTIf6afx5F5jJq8b1D/eaFbrCFrmXmatkT2iN7X8kYHui86WQ==} + '@logto/js@4.1.3': + resolution: {integrity: sha512-TIYrVSyD0c1mEt3fU9NKbWRTblujs3Ct/DYgNzBLzE/tmSVvwM6Z4JxnwZZ3xyfnL8dC4UfSQRlc2gjSMzKUGw==} + '@logto/node@2.4.7': resolution: {integrity: sha512-AlANeqY1NIt93EBcRzrTmyAVHXOHpszTJK+qe1ok50rmZlTmX2p7yQvrg0/Ehwf/+4Rla5vooAR+HIFMaOmPpQ==} - '@logto/react@3.0.8': - resolution: {integrity: sha512-p3pV4rX4g8ZwHQ159mxI+pP3Bwome47dNEmP1hI8/10WqdIPXGYTnfYn5c2l4Y2DyslYyK3ur2Sy4i4K6ept9A==} + '@logto/react@3.0.11': + resolution: {integrity: sha512-JyOOf7zZOEg7fTRldfi9SGwwbuk3qJTlIKld+GQ1yGWIXilI2JyyY2XQraZWpHQLW8KtQFgRWQ/fUKV0bideFQ==} peerDependencies: react: '>=16.8.0' @@ -14951,9 +14960,9 @@ snapshots: '@silverhand/essentials': 2.9.1 tiny-cookie: 2.4.1 - '@logto/browser@2.2.10': + '@logto/browser@2.2.13': dependencies: - '@logto/client': 2.6.6 + '@logto/client': 2.7.0 '@silverhand/essentials': 2.9.1 js-base64: 3.7.5 @@ -14964,6 +14973,13 @@ snapshots: camelcase-keys: 7.0.2 jose: 5.2.4 + '@logto/client@2.7.0': + dependencies: + '@logto/js': 4.1.3 + '@silverhand/essentials': 2.9.1 + camelcase-keys: 7.0.2 + jose: 5.2.4 + '@logto/cloud@0.2.5-a7eedce(zod@3.22.4)': dependencies: '@silverhand/essentials': 2.9.1 @@ -14976,15 +14992,20 @@ snapshots: '@silverhand/essentials': 2.9.1 camelcase-keys: 7.0.2 + '@logto/js@4.1.3': + dependencies: + '@silverhand/essentials': 2.9.1 + camelcase-keys: 7.0.2 + '@logto/node@2.4.7': dependencies: '@logto/client': 2.6.6 '@silverhand/essentials': 2.9.1 js-base64: 3.7.5 - '@logto/react@3.0.8(react@18.2.0)': + '@logto/react@3.0.11(react@18.2.0)': dependencies: - '@logto/browser': 2.2.10 + '@logto/browser': 2.2.13 '@silverhand/essentials': 2.9.1 react: 18.2.0