diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..59d648c36 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,13 @@ +{ + "image": "mcr.microsoft.com/devcontainers/universal:2", + "features": { + "ghcr.io/devcontainers/features/node:1": {} + }, + "updateContentCommand": "npm i -g pnpm && pnpm i && pnpm prepack && pnpm cli connector add --official -p .", + "postStartCommand": "docker run -d -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=p0stgr3s postgres:14-alpine", + "postAttachCommand": "pnpm cli db seed && [[ ! -z $CODESPACES ]] && export ENDPOINT=https://$CODESPACE_NAME-3001.preview.app.github.dev", + "containerEnv": { + "DB_URL": "postgres://postgres:p0stgr3s@localhost:5432/logto", + "TRUST_PROXY_HEADER": "1" + } +} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 44d0150b9..dffec17c5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,7 +27,7 @@ jobs: main-lint: # avoid out of memory issue since macOS has bigger memory # https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources - runs-on: macos-latest + runs-on: ubuntu-latest-4-cores steps: - uses: actions/checkout@v3 diff --git a/.gitignore b/.gitignore index 0a6e52864..4ebbdef2d 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ cache .idea/ *.pem .history +fly.toml # connectors /packages/core/connectors diff --git a/.gitpod.yml b/.gitpod.yml index b2887d25f..06891a0ed 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -19,7 +19,7 @@ tasks: pnpm start:dev env: TRUST_PROXY_HEADER: 1 - DB_URL: postgres://postgres:p0stgr3s@127.0.0.1:5432 + DB_URL: postgres://postgres:p0stgr3s@127.0.0.1:5432/logto ports: - name: Logto diff --git a/.vscode/settings.json b/.vscode/settings.json index c85102924..10453f7bc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -32,6 +32,7 @@ "silverhand", "slonik", "stylelint", - "topbar" + "topbar", + "hasura" ] } diff --git a/packages/console/package.json b/packages/console/package.json index 27b6fbe1b..227b9bbbf 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -22,7 +22,7 @@ "@logto/language-kit": "1.0.0-beta.20", "@logto/phrases": "workspace:^", "@logto/phrases-ui": "workspace:^", - "@logto/react": "1.0.0-beta.10", + "@logto/react": "1.0.0-beta.11", "@logto/schemas": "workspace:^", "@mdx-js/react": "^1.6.22", "@parcel/core": "2.7.0", diff --git a/packages/console/src/hooks/use-api.ts b/packages/console/src/hooks/use-api.ts index a5aabc193..d7f229af4 100644 --- a/packages/console/src/hooks/use-api.ts +++ b/packages/console/src/hooks/use-api.ts @@ -2,7 +2,7 @@ import { useLogto } from '@logto/react'; import type { RequestErrorBody } from '@logto/schemas'; import { managementResource } from '@logto/schemas/lib/seeds'; import ky from 'ky'; -import { useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; @@ -19,31 +19,27 @@ export class RequestError extends Error { } } -const useToastError = () => { - const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - - const toastError = async (response: Response) => { - const fallbackErrorMessage = t('errors.unknown_server_error'); - - try { - const data = await response.json(); - toast.error([data.message, data.details].join('\n') || fallbackErrorMessage); - } catch { - toast.error(fallbackErrorMessage); - } - }; - - return toastError; -}; - type Props = { hideErrorToast?: boolean; }; const useApi = ({ hideErrorToast }: Props = {}) => { const { isAuthenticated, getAccessToken } = useLogto(); - const { i18n } = useTranslation(); - const toastError = useToastError(); + const { t, i18n } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + + const toastError = useCallback( + async (response: Response) => { + const fallbackErrorMessage = t('errors.unknown_server_error'); + + try { + const data = await response.json(); + toast.error([data.message, data.details].join('\n') || fallbackErrorMessage); + } catch { + toast.error(fallbackErrorMessage); + } + }, + [t] + ); const api = useMemo( () => diff --git a/packages/console/src/hooks/use-position.ts b/packages/console/src/hooks/use-position.ts index da6bea222..6a8d88c85 100644 --- a/packages/console/src/hooks/use-position.ts +++ b/packages/console/src/hooks/use-position.ts @@ -196,12 +196,11 @@ export default function usePosition({ const anchorRect = anchorRef.current.getBoundingClientRect(); const overlayRect = overlayRef.current.getBoundingClientRect(); - const { scrollTop, scrollLeft } = document.documentElement; - const verticalTop = anchorRect.y - overlayRect.height + scrollTop - offset.vertical; + const verticalTop = anchorRect.y - overlayRect.height - offset.vertical; const verticalCenter = - anchorRect.y - anchorRect.height / 2 - overlayRect.height / 2 + scrollTop + offset.vertical; - const verticalBottom = anchorRect.y + anchorRect.height + scrollTop + offset.vertical; + anchorRect.y - anchorRect.height / 2 - overlayRect.height / 2 + offset.vertical; + const verticalBottom = anchorRect.y + anchorRect.height + offset.vertical; const verticalPositionMap = { top: verticalTop, @@ -209,11 +208,10 @@ export default function usePosition({ bottom: verticalBottom, }; - const horizontalStart = anchorRect.x + scrollLeft + offset.horizontal; + const horizontalStart = anchorRect.x + offset.horizontal; const horizontalCenter = - anchorRect.x + anchorRect.width / 2 - overlayRect.width / 2 + scrollLeft + offset.horizontal; - const horizontalEnd = - anchorRect.x + anchorRect.width - overlayRect.width + scrollLeft + offset.horizontal; + anchorRect.x + anchorRect.width / 2 - overlayRect.width / 2 + offset.horizontal; + const horizontalEnd = anchorRect.x + anchorRect.width - overlayRect.width + offset.horizontal; const horizontalPositionMap = { start: horizontalStart, diff --git a/packages/core/src/middleware/koa-auth.ts b/packages/core/src/middleware/koa-auth.ts index eda71e3bb..07e3b1778 100644 --- a/packages/core/src/middleware/koa-auth.ts +++ b/packages/core/src/middleware/koa-auth.ts @@ -2,6 +2,7 @@ import type { IncomingHttpHeaders } from 'http'; import { UserRole } from '@logto/schemas'; import { managementResource } from '@logto/schemas/lib/seeds'; +import type { Optional } from '@silverhand/essentials'; import { conditional } from '@silverhand/essentials'; import { jwtVerify } from 'jose'; import type { MiddlewareType, Request } from 'koa'; @@ -49,7 +50,7 @@ type TokenInfo = { // eslint-disable-next-line complexity export const verifyBearerTokenFromRequest = async ( request: Request, - resourceIndicator = managementResource.indicator + resourceIndicator: Optional ): Promise => { const { isProduction, isIntegrationTest, developmentUserId } = envSet.values; const userId = request.headers['development-user-id']?.toString() ?? developmentUserId; @@ -83,7 +84,10 @@ export default function koaAuth, ResponseBodyT> { return async (ctx, next) => { - const { sub, clientId, roleNames } = await verifyBearerTokenFromRequest(ctx.request); + const { sub, clientId, roleNames } = await verifyBearerTokenFromRequest( + ctx.request, + managementResource.indicator + ); if (forRole) { assertThat( diff --git a/packages/core/src/routes/authn.test.ts b/packages/core/src/routes/authn.test.ts new file mode 100644 index 000000000..4a856693c --- /dev/null +++ b/packages/core/src/routes/authn.test.ts @@ -0,0 +1,106 @@ +import RequestError from '@/errors/RequestError'; +import * as functions from '@/middleware/koa-auth'; +import { createRequester } from '@/utils/test-utils'; + +import authnRoutes from './authn'; + +describe('authn route for Hasura', () => { + const request = createRequester({ anonymousRoutes: authnRoutes }); + const mockUserId = 'foo'; + const mockExpectedRole = 'some_role'; + const mockUnauthorizedRole = 'V'; + const keys = Object.freeze({ + expectedRole: 'Expected-Role', + hasuraUserId: 'X-Hasura-User-Id', + hasuraRole: 'X-Hasura-Role', + }); + + describe('with successful verification', () => { + beforeEach(() => { + jest.spyOn(functions, 'verifyBearerTokenFromRequest').mockResolvedValue({ + clientId: 'ok', + sub: mockUserId, + roleNames: [mockExpectedRole], + }); + }); + + it('has expected role', async () => { + const response = await request + .get('/authn/hasura') + .query({ resource: 'https://api.logto.io' }) + .set(keys.expectedRole, mockExpectedRole); + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + [keys.hasuraUserId]: mockUserId, + [keys.hasuraRole]: mockExpectedRole, + }); + }); + + it('throws 401 if no expected role present', async () => { + const response = await request + .get('/authn/hasura') + .query({ resource: 'https://api.logto.io' }) + .set(keys.expectedRole, mockExpectedRole + '1'); + expect(response.status).toEqual(401); + }); + + it('falls back to unauthorized role if no expected role present', async () => { + const response = await request + .get('/authn/hasura') + .query({ resource: 'https://api.logto.io', unauthorizedRole: mockUnauthorizedRole }) + .set(keys.expectedRole, mockExpectedRole + '1'); + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + [keys.hasuraUserId]: mockUserId, + [keys.hasuraRole]: mockUnauthorizedRole, + }); + }); + }); + + describe('with failed verification', () => { + beforeEach(() => { + jest + .spyOn(functions, 'verifyBearerTokenFromRequest') + .mockImplementation(async (_, resource) => { + if (resource) { + throw new RequestError({ code: 'auth.jwt_sub_missing', status: 401 }); + } + + return { clientId: 'not ok', sub: mockUserId }; + }); + }); + + it('throws 401 if no unauthorized role presents', async () => { + const response = await request + .get('/authn/hasura') + .query({ resource: 'https://api.logto.io' }) + .set(keys.expectedRole, mockExpectedRole); + expect(response.status).toEqual(401); + }); + + it('falls back to unauthorized role with user id if no expected resource present', async () => { + const response = await request + .get('/authn/hasura') + .query({ resource: 'https://api.logto.io', unauthorizedRole: mockUnauthorizedRole }) + .set(keys.expectedRole, mockExpectedRole); + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + [keys.hasuraUserId]: mockUserId, + [keys.hasuraRole]: mockUnauthorizedRole, + }); + }); + + it('falls back to unauthorized role if JWT is invalid', async () => { + jest + .spyOn(functions, 'verifyBearerTokenFromRequest') + .mockRejectedValue(new RequestError({ code: 'auth.jwt_sub_missing', status: 401 })); + const response = await request + .get('/authn/hasura') + .query({ resource: 'https://api.logto.io', unauthorizedRole: mockUnauthorizedRole }); + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + [keys.hasuraRole]: mockUnauthorizedRole, + }); + }); + }); +}); diff --git a/packages/core/src/routes/authn.ts b/packages/core/src/routes/authn.ts index d6a99b439..aa523a84a 100644 --- a/packages/core/src/routes/authn.ts +++ b/packages/core/src/routes/authn.ts @@ -16,15 +16,39 @@ export default function authnRoutes(router: T) { router.get( '/authn/hasura', koaGuard({ - query: z.object({ resource: z.string().min(1) }), + query: z.object({ resource: z.string().min(1), unauthorizedRole: z.string().optional() }), status: [200, 401], }), async (ctx, next) => { + const { resource, unauthorizedRole } = ctx.guard.query; const expectedRole = ctx.headers['expected-role']?.toString(); - const { sub, roleNames } = await verifyBearerTokenFromRequest( - ctx.request, - ctx.guard.query.resource - ); + + const verifyToken = async (expectedResource?: string) => { + try { + return await verifyBearerTokenFromRequest(ctx.request, expectedResource); + } catch { + return { + sub: undefined, + roleNames: undefined, + }; + } + }; + + const { sub, roleNames } = await verifyToken(resource); + + if (unauthorizedRole && (!expectedRole || !roleNames?.includes(expectedRole))) { + ctx.body = { + 'X-Hasura-User-Id': + sub ?? + // When the previous token verification throws, the reason could be resource mismatch. + // So we verify the token again with no resource provided. + (await verifyToken().then(({ sub }) => sub)), + 'X-Hasura-Role': unauthorizedRole, + }; + ctx.status = 200; + + return next(); + } if (expectedRole) { assertThat( diff --git a/packages/demo-app/package.json b/packages/demo-app/package.json index 8968ed885..3aa49eb91 100644 --- a/packages/demo-app/package.json +++ b/packages/demo-app/package.json @@ -20,7 +20,7 @@ "@logto/core-kit": "1.0.0-beta.20", "@logto/language-kit": "1.0.0-beta.20", "@logto/phrases": "workspace:^", - "@logto/react": "1.0.0-beta.10", + "@logto/react": "1.0.0-beta.11", "@logto/schemas": "workspace:^", "@parcel/core": "2.7.0", "@parcel/transformer-sass": "2.7.0", diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index 8b20f21de..ae31215c1 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -15,7 +15,7 @@ }, "devDependencies": { "@jest/types": "^29.1.2", - "@logto/node": "1.0.0-beta.10", + "@logto/node": "1.0.0-beta.11", "@logto/schemas": "workspace:^", "@peculiar/webcrypto": "^1.3.3", "@silverhand/eslint-config": "1.3.0", diff --git a/packages/phrases-ui/src/locales/zh-cn.ts b/packages/phrases-ui/src/locales/zh-cn.ts index 3cf0073af..fb9c97a0b 100644 --- a/packages/phrases-ui/src/locales/zh-cn.ts +++ b/packages/phrases-ui/src/locales/zh-cn.ts @@ -30,7 +30,7 @@ const translation = { got_it: '知道了', sign_in_with: '通过 {{name}} 登录', forgot_password: '重置密码', - switch_to: '用{{method}}登录', + switch_to: '切换到{{method}}', }, description: { email: '邮箱', diff --git a/packages/phrases/src/locales/zh-cn/errors.ts b/packages/phrases/src/locales/zh-cn/errors.ts index ab46113a9..3cdf5263d 100644 --- a/packages/phrases/src/locales/zh-cn/errors.ts +++ b/packages/phrases/src/locales/zh-cn/errors.ts @@ -67,7 +67,7 @@ const errors = { connector_id_mismatch: '传入的连接器 ID 与 session 中保存的记录不一致', connector_session_not_found: '无法找到连接器登录信息,请尝试重新登录。', verification_session_not_found: '验证失败,请重新验证。', - verification_expired: '无密码验证已过期。请返回重新验证。', + verification_expired: '当前页面已超时。为确保你的账号安全,请重新验证。', unauthorized: '请先登录', unsupported_prompt_name: '不支持的 prompt name', forgot_password_not_enabled: '忘记密码功能没有开启。', diff --git a/packages/ui/src/components/Button/index.module.scss b/packages/ui/src/components/Button/index.module.scss index 18791f966..ba8784ec7 100644 --- a/packages/ui/src/components/Button/index.module.scss +++ b/packages/ui/src/components/Button/index.module.scss @@ -55,21 +55,6 @@ } } -.outline { - border: _.border(var(--color-brand-default)); - background: transparent; - color: var(--color-type-link); - - &.disabled, - &:disabled { - border-color: var(--color-type-disable); - color: var(--color-type-disable); - } - - &:active { - background: var(--color-overlay-brand-pressed); - } -} :global(body.desktop) { .primary { @@ -91,14 +76,4 @@ background: var(--color-overlay-neutral-hover); } } - - .outline { - &:focus-visible { - outline: 3px solid var(--color-overlay-brand-focused); - } - - &:not(:disabled):not(:active):hover { - background: var(--color-overlay-brand-hover); - } - } } diff --git a/packages/ui/src/components/Button/index.tsx b/packages/ui/src/components/Button/index.tsx index 61768432e..6b2098abb 100644 --- a/packages/ui/src/components/Button/index.tsx +++ b/packages/ui/src/components/Button/index.tsx @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'; import * as styles from './index.module.scss'; -export type ButtonType = 'primary' | 'secondary' | 'outline'; +export type ButtonType = 'primary' | 'secondary'; type BaseProps = Omit, 'type' | 'size' | 'title'> & { htmlType?: 'button' | 'submit' | 'reset'; diff --git a/packages/ui/src/components/ConfirmModal/AcModal.tsx b/packages/ui/src/components/ConfirmModal/AcModal.tsx index 2dc670d56..7b4bb0687 100644 --- a/packages/ui/src/components/ConfirmModal/AcModal.tsx +++ b/packages/ui/src/components/ConfirmModal/AcModal.tsx @@ -37,7 +37,7 @@ const AcModal = ({
{children}
-
diff --git a/packages/ui/src/components/Input/index.module.scss b/packages/ui/src/components/Input/index.module.scss index 44fa9c63e..7e8d1db7f 100644 --- a/packages/ui/src/components/Input/index.module.scss +++ b/packages/ui/src/components/Input/index.module.scss @@ -35,13 +35,6 @@ &::placeholder { color: var(--color-type-secondary); } - - // Overwrite webkit auto-fill style - &:-webkit-autofill { - box-shadow: 0 0 0 30px var(--color-bg-body) inset; - -webkit-text-fill-color: var(--color-type-primary); - transition: none; - } } &:focus-within { diff --git a/packages/ui/src/components/Notification/index.module.scss b/packages/ui/src/components/Notification/index.module.scss index b5322444c..d3a430ddd 100644 --- a/packages/ui/src/components/Notification/index.module.scss +++ b/packages/ui/src/components/Notification/index.module.scss @@ -4,20 +4,30 @@ padding: _.unit(3) _.unit(4); font: var(--font-body-2); color: var(--color-type-primary); - background: var(--color-alert-99); margin: 0 auto _.unit(2); @include _.flex_row; - &:focus-visible { - outline: none; + .icon { + width: 20px; + height: 20px; + margin-right: _.unit(3); } -} -.icon { - color: var(--color-alert-70); - width: 20px; - height: 20px; - margin-right: _.unit(3); + &.alert { + background: var(--color-alert-99); + + .icon { + color: var(--color-alert-70); + } + } + + &.info { + background: var(--color-neutral-variant-80); + + .icon { + color: var(--color-neutral-variant-60); + } + } } .message { @@ -26,8 +36,6 @@ } .link { - text-decoration: underline; - cursor: pointer; max-width: 20%; } @@ -35,10 +43,4 @@ .notification { border-radius: var(--radius); } - - .link { - &:hover { - color: var(--color-brand-default); - } - } } diff --git a/packages/ui/src/components/Notification/index.tsx b/packages/ui/src/components/Notification/index.tsx index 38510e3c6..95d3032fe 100644 --- a/packages/ui/src/components/Notification/index.tsx +++ b/packages/ui/src/components/Notification/index.tsx @@ -1,38 +1,23 @@ import classNames from 'classnames'; -import { useTranslation } from 'react-i18next'; import InfoIcon from '@/assets/icons/info-icon.svg'; -import { onKeyDownHandler } from '@/utils/a11y'; +import TextLink from '../TextLink'; import * as styles from './index.module.scss'; type Props = { className?: string; message: string; onClose: () => void; + type?: 'info' | 'alert'; }; -const Notification = ({ className, message, onClose }: Props) => { - const { t } = useTranslation(); - +const Notification = ({ className, message, onClose, type = 'info' }: Props) => { return ( -
+
{message}
- {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} - - {t('action.got_it')} - +
); }; diff --git a/packages/ui/src/components/TextLink/index.module.scss b/packages/ui/src/components/TextLink/index.module.scss index d8e3746c2..7dfa46fba 100644 --- a/packages/ui/src/components/TextLink/index.module.scss +++ b/packages/ui/src/components/TextLink/index.module.scss @@ -9,6 +9,12 @@ color: var(--color-brand-default); text-decoration: none; font: var(--font-label-2); + border-radius: _.unit(1); + padding: _.unit(1) _.unit(0.5); + + &:active { + background: var(--color-overlay-brand-pressed); + } } &.secondary { @@ -21,10 +27,15 @@ :global(body.desktop) { .link { &.primary:hover { - text-decoration: underline; + background: var(--color-overlay-brand-hover); } - &.secondary:hover { + &.primary:focus-visible { + outline: _.border(var(--color-overlay-brand-focused)); + } + + &.secondary:hover, + &.secondary:active { color: var(--color-brand-default); } } diff --git a/packages/ui/src/scss/_colors.scss b/packages/ui/src/scss/_colors.scss index 3666003dd..a0064313e 100644 --- a/packages/ui/src/scss/_colors.scss +++ b/packages/ui/src/scss/_colors.scss @@ -20,6 +20,7 @@ --color-neutral-95: #eff1f1; --color-neutral-100: #fff; + --color-neutral-variant-60: #928f9a; --color-neutral-variant-80: #e5e1ec; --color-danger-30: #930006; @@ -104,6 +105,7 @@ --color-neutral-99: #191c1d; --color-neutral-100: #000; + --color-neutral-variant-60: #928f9a; --color-neutral-variant-80: #5f5d67; --color-neutral-variant-90: #47464e; diff --git a/packages/ui/src/utils/a11y.ts b/packages/ui/src/utils/a11y.ts index 81452c593..7b0aa0b5f 100644 --- a/packages/ui/src/utils/a11y.ts +++ b/packages/ui/src/utils/a11y.ts @@ -18,6 +18,9 @@ export const onKeyDownHandler = if (typeof callback === 'object') { callback[key]?.(event); - event.preventDefault(); + + if (callback[key]) { + event.preventDefault(); + } } }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 356eedd82..02d300564 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -109,7 +109,7 @@ importers: '@logto/language-kit': 1.0.0-beta.20 '@logto/phrases': workspace:^ '@logto/phrases-ui': workspace:^ - '@logto/react': 1.0.0-beta.10 + '@logto/react': 1.0.0-beta.11 '@logto/schemas': workspace:^ '@mdx-js/react': ^1.6.22 '@parcel/core': 2.7.0 @@ -180,7 +180,7 @@ importers: '@logto/language-kit': 1.0.0-beta.20 '@logto/phrases': link:../phrases '@logto/phrases-ui': link:../phrases-ui - '@logto/react': 1.0.0-beta.10_react@18.2.0 + '@logto/react': 1.0.0-beta.11_react@18.2.0 '@logto/schemas': link:../schemas '@mdx-js/react': 1.6.22_react@18.2.0 '@parcel/core': 2.7.0 @@ -416,7 +416,7 @@ importers: '@logto/core-kit': 1.0.0-beta.20 '@logto/language-kit': 1.0.0-beta.20 '@logto/phrases': workspace:^ - '@logto/react': 1.0.0-beta.10 + '@logto/react': 1.0.0-beta.11 '@logto/schemas': workspace:^ '@parcel/core': 2.7.0 '@parcel/transformer-sass': 2.7.0 @@ -443,7 +443,7 @@ importers: '@logto/core-kit': 1.0.0-beta.20 '@logto/language-kit': 1.0.0-beta.20 '@logto/phrases': link:../phrases - '@logto/react': 1.0.0-beta.10_react@18.2.0 + '@logto/react': 1.0.0-beta.11_react@18.2.0 '@logto/schemas': link:../schemas '@parcel/core': 2.7.0 '@parcel/transformer-sass': 2.7.0_@parcel+core@2.7.0 @@ -470,7 +470,7 @@ importers: packages/integration-tests: specifiers: '@jest/types': ^29.1.2 - '@logto/node': 1.0.0-beta.10 + '@logto/node': 1.0.0-beta.11 '@logto/schemas': workspace:^ '@peculiar/webcrypto': ^1.3.3 '@silverhand/eslint-config': 1.3.0 @@ -495,7 +495,7 @@ importers: typescript: ^4.7.4 devDependencies: '@jest/types': 29.1.2 - '@logto/node': 1.0.0-beta.10 + '@logto/node': 1.0.0-beta.11 '@logto/schemas': link:../schemas '@peculiar/webcrypto': 1.3.3 '@silverhand/eslint-config': 1.3.0_swk2g7ygmfleszo5c33j4vooni @@ -2300,19 +2300,19 @@ packages: dev: true optional: true - /@logto/browser/1.0.0-beta.10: - resolution: {integrity: sha512-ziZv8TTWwzK9PgBtioF9Wplfaj0J/InyxSBmfgFS5PX54GAVfOy3uGBi9hGL6HDRPj3SYs2U1bi21YPlF9z/8w==} + /@logto/browser/1.0.0-beta.11: + resolution: {integrity: sha512-Ofdj5UqLzwoW66XGnff+1U6Lix90qI2Q30+TISJz6soCm97oqJdWpY2SRlMAfnvDobsFPqAxyCAz49Cg16h50Q==} dependencies: - '@logto/client': 1.0.0-beta.10 + '@logto/client': 1.0.0-beta.11 '@silverhand/essentials': 1.3.0 js-base64: 3.7.2 dev: true - /@logto/client/1.0.0-beta.10: - resolution: {integrity: sha512-XHkOJdvxsBix/8cZ3a6Hx3UiiQkaJBDF9D7BGM8lZxPz5nsGq8YkX5ZRvjVCX9Y3fE3nRb5Iv1ccWvV192lUWg==} + /@logto/client/1.0.0-beta.11: + resolution: {integrity: sha512-7Nl+53JPgB0wjMU9zJH6SrOj4OKn0Kl9U1cumt/IHSYuaDyV7TgvR/HaaeSZuZ0JxbMg66Riee87Qx9Tv46k6A==} dependencies: - '@logto/core-kit': 1.0.0-beta.19 - '@logto/js': 1.0.0-beta.10 + '@logto/core-kit': 1.0.0-beta.20 + '@logto/js': 1.0.0-beta.11 '@silverhand/essentials': 1.3.0 camelcase-keys: 7.0.2 jose: 4.6.0 @@ -2340,16 +2340,6 @@ packages: zod: 3.19.1 dev: false - /@logto/core-kit/1.0.0-beta.19: - resolution: {integrity: sha512-cqwfz+Ic/t7mV23QUEXWeRaLTqN71NSv02adaul8VWZQg4IePOlWLLaiTyY6ods88f3ZOarIBlJ5fYexw3J8qg==} - engines: {node: ^16.0.0} - dependencies: - '@logto/language-kit': 1.0.0-beta.20 - color: 4.2.3 - nanoid: 3.3.4 - zod: 3.19.1 - dev: true - /@logto/core-kit/1.0.0-beta.20: resolution: {integrity: sha512-seYvL/aGYRfO4d0FYfKIW/Cu9PnFMRpRM5/oRXwXbcbv+LY1a3TcAX0itrVXeBygIrxiAmWd9DL7CGIWzb48Qg==} engines: {node: ^16.0.0} @@ -2359,10 +2349,10 @@ packages: nanoid: 3.3.4 zod: 3.19.1 - /@logto/js/1.0.0-beta.10: - resolution: {integrity: sha512-mMzverjbeKtGjSb0NmEUHzDBRrXhCPOydCE37yhzL/qORiehyblPqntw3lLrf5oCNUKaxv7PzT5q/Lfbxb3Q8g==} + /@logto/js/1.0.0-beta.11: + resolution: {integrity: sha512-V0cV+T+DFcpqAAIjfdiEuEYG+ePASR7VbPM1zMLv02S/5zz3pStedbLrksZaSVUOmQvsieEF5d5/kZ8ZshQa0A==} dependencies: - '@logto/core-kit': 1.0.0-beta.19 + '@logto/core-kit': 1.0.0-beta.20 '@silverhand/essentials': 1.3.0 camelcase-keys: 7.0.2 jose: 4.6.0 @@ -2375,10 +2365,10 @@ packages: dependencies: zod: 3.19.1 - /@logto/node/1.0.0-beta.10: - resolution: {integrity: sha512-4st77cD1h/bCIrt+BttDHva/I7ibLw0ilvi30uadEwuH0Ih8WFYvlyi2V1wOljd5Ym+fqSMbyKXQQIwba6CenQ==} + /@logto/node/1.0.0-beta.11: + resolution: {integrity: sha512-nsa9RtrzBRmLMJNeNzutJ72bLwZpGUr4lhBAze7YXJi6o9ujvZOLgdvZAZeoSdxxBhTilwbDWNXmj8n3Ndavaw==} dependencies: - '@logto/client': 1.0.0-beta.10 + '@logto/client': 1.0.0-beta.11 '@silverhand/essentials': 1.3.0 js-base64: 3.7.2 node-fetch: 2.6.7 @@ -2386,12 +2376,12 @@ packages: - encoding dev: true - /@logto/react/1.0.0-beta.10_react@18.2.0: - resolution: {integrity: sha512-MutQplD5VkUqYIhnaQgNiRpbQTRK/OS2Ut70j99mVax/frImlIu1m1YeuE8NEqGmB9SBQvJoP5kECxVNf2a4Nw==} + /@logto/react/1.0.0-beta.11_react@18.2.0: + resolution: {integrity: sha512-W9L1QrJml4tHlrUkmdaJRsj4ln+SgwGAeyF+QhmKBq5CoSR+EauE63ySLhPn2KFVrc3m/sioejJIVy+1sUe21w==} peerDependencies: react: '>=16.8.0 || ^18.0.0' dependencies: - '@logto/browser': 1.0.0-beta.10 + '@logto/browser': 1.0.0-beta.11 '@silverhand/essentials': 1.3.0 react: 18.2.0 dev: true