0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

refactor(console): add get tenant add-on-skus endpoint (#6886)

* refactor(console): add get tenant add-on-skus endpoint

dynamicly display  token usage tooltips content based on the token add-on SKU details

* fix: fix email connector ts error

fix email connector ts error
This commit is contained in:
simeng-li 2024-12-16 13:55:41 +08:00 committed by GitHub
parent 8fc5e250e6
commit a6aa235289
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 118 additions and 62 deletions

View file

@ -52,7 +52,7 @@
"access": "public" "access": "public"
}, },
"devDependencies": { "devDependencies": {
"@logto/cloud": "0.2.5-5e334eb", "@logto/cloud": "0.2.5-aac51e9",
"@silverhand/eslint-config": "6.0.1", "@silverhand/eslint-config": "6.0.1",
"@silverhand/ts-config": "6.0.0", "@silverhand/ts-config": "6.0.0",
"@types/node": "^20.11.20", "@types/node": "^20.11.20",

View file

@ -40,8 +40,6 @@ const sendMessage =
body: { body: {
data: { data: {
to, to,
// TODO @wangsijie: fix this circular dependency, the connector-kit type change should be released first
// @ts-expect-error circular dependency
type, type,
payload: { payload: {
...payload, ...payload,

View file

@ -27,7 +27,7 @@
"devDependencies": { "devDependencies": {
"@fontsource/roboto-mono": "^5.0.0", "@fontsource/roboto-mono": "^5.0.0",
"@jest/types": "^29.5.0", "@jest/types": "^29.5.0",
"@logto/cloud": "0.2.5-5e334eb", "@logto/cloud": "0.2.5-aac51e9",
"@logto/connector-kit": "workspace:^4.1.0", "@logto/connector-kit": "workspace:^4.1.0",
"@logto/core-kit": "workspace:^2.5.0", "@logto/core-kit": "workspace:^2.5.0",
"@logto/language-kit": "workspace:^1.1.0", "@logto/language-kit": "workspace:^1.1.0",

View file

@ -11,6 +11,10 @@ export type LogtoSkuResponse = GetArrayElementType<GuardedResponse<GetRoutes['/a
export type Subscription = GuardedResponse<GetRoutes['/api/tenants/:tenantId/subscription']>; export type Subscription = GuardedResponse<GetRoutes['/api/tenants/:tenantId/subscription']>;
export type TenantUsageAddOnSkus = GuardedResponse<
GetRoutes['/api/tenants/:tenantId/subscription/add-on-skus']
>;
/* ===== Use `New` in the naming to avoid confusion with legacy types ===== */ /* ===== Use `New` in the naming to avoid confusion with legacy types ===== */
export type NewSubscriptionUsageResponse = GuardedResponse< export type NewSubscriptionUsageResponse = GuardedResponse<
GetRoutes['/api/tenants/:tenantId/subscription-usage'] GetRoutes['/api/tenants/:tenantId/subscription-usage']

View file

@ -5,6 +5,7 @@ import { useContext } from 'react';
import { Trans, useTranslation } from 'react-i18next'; import { Trans, useTranslation } from 'react-i18next';
import Tip from '@/assets/icons/tip.svg?react'; import Tip from '@/assets/icons/tip.svg?react';
import { type LogtoSkuResponse } from '@/cloud/types/router';
import { addOnPricingExplanationLink } from '@/consts/external-links'; import { addOnPricingExplanationLink } from '@/consts/external-links';
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
import DynamicT from '@/ds-components/DynamicT'; import DynamicT from '@/ds-components/DynamicT';
@ -60,6 +61,30 @@ const formatNumberTypedUsageDescription = ({
return `${formatNumber(usage)} / ${unlimitedString}`; return `${formatNumber(usage)} / ${unlimitedString}`;
}; };
/**
* The price unit returned from DB is cent, so we need to divide it by 100 to get the dollar price.
*/
const formatDecimalPrice = (price: number): string => {
return (price / 100).toFixed(2);
};
// Manually format the quota display for add-on usages
const formatAddOnQuota = (quota?: LogtoSkuResponse['quota']) => {
if (!quota) {
return;
}
return {
...quota,
...conditional(quota.tokenLimit && { tokenLimit: formatQuotaNumber(quota.tokenLimit) }),
};
};
/**
* @param unitPrice Hardcoded add-on unit price. Only used for the tooltip.
* @param usageAddOnSku The add-on SKU object. Only used for the tooltip.
* If provided, use the unit price and count from the SKU object first. Otherwise, fallback to the hardcoded unit price.
*/
export type Props = { export type Props = {
readonly usage: number | boolean; readonly usage: number | boolean;
readonly quota?: Nullable<number> | boolean; readonly quota?: Nullable<number> | boolean;
@ -70,6 +95,7 @@ export type Props = {
readonly unitPrice: number; readonly unitPrice: number;
readonly className?: string; readonly className?: string;
readonly isQuotaNoticeHidden?: boolean; readonly isQuotaNoticeHidden?: boolean;
readonly usageAddOnSku?: LogtoSkuResponse;
}; };
function PlanUsageCard({ function PlanUsageCard({
@ -82,6 +108,7 @@ function PlanUsageCard({
tooltipKey, tooltipKey,
className, className,
isQuotaNoticeHidden, isQuotaNoticeHidden,
usageAddOnSku,
}: Props) { }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const { const {
@ -109,12 +136,15 @@ function PlanUsageCard({
}} }}
> >
{t(tooltipKey, { {t(tooltipKey, {
price: unitPrice, price: usageAddOnSku?.unitPrice
? formatDecimalPrice(usageAddOnSku.unitPrice)
: unitPrice,
...conditional( ...conditional(
typeof basicQuota === 'number' && { typeof basicQuota === 'number' && {
basicQuota: formatQuotaNumber(basicQuota), basicQuota: formatQuotaNumber(basicQuota),
} }
), ),
...conditional(usageAddOnSku && formatAddOnQuota(usageAddOnSku.quota)),
})} })}
</Trans> </Trans>
} }

View file

@ -8,6 +8,7 @@ import {
type NewSubscriptionPeriodicUsage, type NewSubscriptionPeriodicUsage,
type NewSubscriptionCountBasedUsage, type NewSubscriptionCountBasedUsage,
type NewSubscriptionQuota, type NewSubscriptionQuota,
type TenantUsageAddOnSkus,
} from '@/cloud/types/router'; } from '@/cloud/types/router';
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
import DynamicT from '@/ds-components/DynamicT'; import DynamicT from '@/ds-components/DynamicT';
@ -26,6 +27,7 @@ import {
type Props = { type Props = {
readonly periodicUsage: NewSubscriptionPeriodicUsage | undefined; readonly periodicUsage: NewSubscriptionPeriodicUsage | undefined;
readonly usageAddOnSkus?: TenantUsageAddOnSkus;
}; };
const getUsageByKey = ( const getUsageByKey = (
@ -57,7 +59,7 @@ const getUsageByKey = (
return countBasedUsage[key]; return countBasedUsage[key];
}; };
function PlanUsage({ periodicUsage }: Props) { function PlanUsage({ periodicUsage, usageAddOnSkus }: Props) {
const { const {
currentSubscriptionQuota, currentSubscriptionQuota,
currentSubscriptionBasicQuota, currentSubscriptionBasicQuota,
@ -87,6 +89,8 @@ function PlanUsage({ periodicUsage }: Props) {
usageKey: 'subscription.usage.usage_description_with_limited_quota', usageKey: 'subscription.usage.usage_description_with_limited_quota',
titleKey: `subscription.usage.${titleKeyMap[key]}`, titleKey: `subscription.usage.${titleKeyMap[key]}`,
unitPrice: usageKeyPriceMap[key], unitPrice: usageKeyPriceMap[key],
// Only support tokenLimit for now
usageAddOnSku: cond(key === 'tokenLimit' && usageAddOnSkus?.[key]),
...cond( ...cond(
// We only show the usage card for MAU and token for Free plan // We only show the usage card for MAU and token for Free plan
(key === 'tokenLimit' || key === 'mauLimit' || isPaidTenant) && { (key === 'tokenLimit' || key === 'mauLimit' || isPaidTenant) && {

View file

@ -64,7 +64,6 @@ const useSubscribe = () => {
const { redirectUri, sessionId } = await cloudApi.post('/api/checkout-session', { const { redirectUri, sessionId } = await cloudApi.post('/api/checkout-session', {
body: { body: {
skuId, skuId,
planId,
successCallbackUrl, successCallbackUrl,
tenantId, tenantId,
tenantName: tenantData?.name, tenantName: tenantData?.name,

View file

@ -1,6 +1,6 @@
import { useContext, useMemo } from 'react'; import { useContext, useMemo } from 'react';
import { type NewSubscriptionPeriodicUsage } from '@/cloud/types/router'; import { type TenantUsageAddOnSkus, type NewSubscriptionPeriodicUsage } from '@/cloud/types/router';
import BillInfo from '@/components/BillInfo'; import BillInfo from '@/components/BillInfo';
import FormCard from '@/components/FormCard'; import FormCard from '@/components/FormCard';
import PlanDescription from '@/components/PlanDescription'; import PlanDescription from '@/components/PlanDescription';
@ -17,9 +17,10 @@ import styles from './index.module.scss';
type Props = { type Props = {
readonly periodicUsage?: NewSubscriptionPeriodicUsage; readonly periodicUsage?: NewSubscriptionPeriodicUsage;
readonly usageAddOnSkus?: TenantUsageAddOnSkus;
}; };
function CurrentPlan({ periodicUsage }: Props) { function CurrentPlan({ periodicUsage, usageAddOnSkus }: Props) {
const { const {
currentSku: { unitPrice }, currentSku: { unitPrice },
currentSubscription: { upcomingInvoice, isEnterprisePlan, planId }, currentSubscription: { upcomingInvoice, isEnterprisePlan, planId },
@ -49,7 +50,7 @@ function CurrentPlan({ periodicUsage }: Props) {
</div> </div>
</div> </div>
<FormField title="subscription.plan_usage"> <FormField title="subscription.plan_usage">
<PlanUsage periodicUsage={periodicUsage} /> <PlanUsage periodicUsage={periodicUsage} usageAddOnSkus={usageAddOnSkus} />
</FormField> </FormField>
<FormField title="subscription.next_bill"> <FormField title="subscription.next_bill">
<BillInfo cost={upcomingCost} isManagePaymentVisible={Boolean(upcomingCost)} /> <BillInfo cost={upcomingCost} isManagePaymentVisible={Boolean(upcomingCost)} />

View file

@ -1,9 +1,11 @@
import { type ResponseError } from '@withtyped/client';
import { useContext, useEffect } from 'react'; import { useContext, useEffect } from 'react';
import useSWR from 'swr'; import useSWR from 'swr';
import { useCloudApi } from '@/cloud/hooks/use-cloud-api'; import { useCloudApi } from '@/cloud/hooks/use-cloud-api';
import { type TenantUsageAddOnSkus, type NewSubscriptionPeriodicUsage } from '@/cloud/types/router';
import PageMeta from '@/components/PageMeta'; import PageMeta from '@/components/PageMeta';
import { isCloud } from '@/consts/env'; import { isCloud, isDevFeaturesEnabled } from '@/consts/env';
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
import { TenantsContext } from '@/contexts/TenantsProvider'; import { TenantsContext } from '@/contexts/TenantsProvider';
import { pickupFeaturedLogtoSkus } from '@/utils/subscription'; import { pickupFeaturedLogtoSkus } from '@/utils/subscription';
@ -23,14 +25,27 @@ function Subscription() {
const reservedSkus = pickupFeaturedLogtoSkus(logtoSkus); const reservedSkus = pickupFeaturedLogtoSkus(logtoSkus);
const { data: periodicUsage, isLoading } = useSWR( const { data: periodicUsage, error: periodicUsageError } = useSWR<
isCloud && `/api/tenants/${currentTenantId}/subscription/periodic-usage`, NewSubscriptionPeriodicUsage,
async () => ResponseError
cloudApi.get(`/api/tenants/:tenantId/subscription/periodic-usage`, { >(isCloud && `/api/tenants/${currentTenantId}/subscription/periodic-usage`, async () =>
params: { tenantId: currentTenantId }, cloudApi.get(`/api/tenants/:tenantId/subscription/periodic-usage`, {
}) params: { tenantId: currentTenantId },
})
); );
const { data: usageAddOnSkus, error: usageAddOnSkusError } = useSWR<
TenantUsageAddOnSkus,
ResponseError
>(isCloud && isDevFeaturesEnabled && `/api/tenants/${currentTenantId}/add-on-skus`, async () =>
cloudApi.get(`/api/tenants/:tenantId/subscription/add-on-skus`, {
params: { tenantId: currentTenantId },
})
);
const isLoading =
(!periodicUsage && !periodicUsageError) || (!usageAddOnSkus && !usageAddOnSkusError);
useEffect(() => { useEffect(() => {
if (isCloud) { if (isCloud) {
onCurrentSubscriptionUpdated(); onCurrentSubscriptionUpdated();
@ -55,7 +70,7 @@ function Subscription() {
return ( return (
<div className={styles.container}> <div className={styles.container}>
<PageMeta titleKey={['tenants.tabs.subscription', 'tenants.title']} /> <PageMeta titleKey={['tenants.tabs.subscription', 'tenants.title']} />
<CurrentPlan periodicUsage={periodicUsage} /> <CurrentPlan periodicUsage={periodicUsage} usageAddOnSkus={usageAddOnSkus} />
<PlanComparisonTable /> <PlanComparisonTable />
<SwitchPlanActionBar <SwitchPlanActionBar
currentSkuId={currentSku.id} currentSkuId={currentSku.id}

View file

@ -99,7 +99,7 @@
"zod": "^3.23.8" "zod": "^3.23.8"
}, },
"devDependencies": { "devDependencies": {
"@logto/cloud": "0.2.5-5e334eb", "@logto/cloud": "0.2.5-aac51e9",
"@silverhand/eslint-config": "6.0.1", "@silverhand/eslint-config": "6.0.1",
"@silverhand/ts-config": "6.0.0", "@silverhand/ts-config": "6.0.0",
"@types/adm-zip": "^0.5.5", "@types/adm-zip": "^0.5.5",

View file

@ -47,7 +47,8 @@ const usage = {
tokens: { tokens: {
title: 'الرموز', title: 'الرموز',
description: '{{usage}}', description: '{{usage}}',
tooltip: 'ميزة إضافية بسعر قدره ${{price, number}} لكل مليون رمز. الرموز الأولى مليون مشمولة.', tooltip:
'ميزة إضافية بسعر ${{price, number}} لكل {{tokenLimit}} توكن. أول {{basicQuota}} توكن مشمولة.',
}, },
hooks: { hooks: {
title: 'الخطافات', title: 'الخطافات',

View file

@ -48,7 +48,7 @@ const usage = {
title: 'Tokens', title: 'Tokens',
description: '{{usage}}', description: '{{usage}}',
tooltip: tooltip:
'Zusatzfeature zu einem Preis von ${{price, number}} pro Million Tokens. Die ersten 1 Million Tokens sind inklusive.', 'Zusatzfeature zu einem Preis von ${{price, number}} pro {{tokenLimit}} Tokens. Die ersten {{basicQuota}} Tokens sind inklusive.',
}, },
hooks: { hooks: {
title: 'Hooks', title: 'Hooks',

View file

@ -61,9 +61,9 @@ const usage = {
tokens: { tokens: {
title: 'Tokens', title: 'Tokens',
tooltip: tooltip:
'Add-on feature priced at ${{price, number}} per million tokens. The first 1 million tokens is included.', 'Add-on feature priced at ${{price, number}} per {{tokenLimit}} tokens. The first {{basicQuota}} tokens is included.',
tooltip_for_enterprise: tooltip_for_enterprise:
'The first {{basicQuota}} tokens is included and free to use in your contract-based plan. If you need more, ${{price, number}} per million tokens per month.', 'The first {{basicQuota}} tokens is included and free to use in your contract-based plan. If you need more, ${{price, number}} per {{tokenLimit}} tokens per month.',
}, },
hooks: { hooks: {
title: 'Hooks', title: 'Hooks',

View file

@ -48,7 +48,7 @@ const usage = {
title: 'Tokens', title: 'Tokens',
description: '{{usage}}', description: '{{usage}}',
tooltip: tooltip:
'Función adicional con un precio de ${{price, number}} por millón de tokens. El primer millón de tokens está incluido.', 'Función adicional con un precio de ${{price, number}} por {{tokenLimit}} de tokens. El primer {{basicQuota}} de tokens está incluido.',
}, },
hooks: { hooks: {
title: 'Ganchos', title: 'Ganchos',

View file

@ -49,7 +49,7 @@ const usage = {
title: 'Jetons', title: 'Jetons',
description: '{{usage}}', description: '{{usage}}',
tooltip: tooltip:
'Fonctionnalité en supplément au prix de ${{price, number}} par million de jetons. Le premier million de jetons est inclus.', 'Fonctionnalité en supplément au prix de ${{price, number}} par {{tokenLimit}} de jetons. Le premier {{basicQuota}} de jetons est inclus.',
}, },
hooks: { hooks: {
title: 'Hooks', title: 'Hooks',

View file

@ -49,7 +49,7 @@ const usage = {
title: 'Token', title: 'Token',
description: '{{usage}}', description: '{{usage}}',
tooltip: tooltip:
'Funzionalità aggiuntiva con un prezzo di ${{price, number}} per milione di token. Il primo milione di token è incluso.', 'Funzionalità aggiuntiva con un prezzo di ${{price, number}} per {{tokenLimit}} di token. Il primo {{basicQuota}} di token è incluso.',
}, },
hooks: { hooks: {
title: 'Hook', title: 'Hook',

View file

@ -48,7 +48,7 @@ const usage = {
title: 'トークン', title: 'トークン',
description: '{{usage}}', description: '{{usage}}',
tooltip: tooltip:
'追加機能として、100 万トークンにつき {{price, number}} ドルの料金です。最初の 100 万トークンは含まれています。', '追加機能として、{{tokenLimit}} トークンにつき {{price, number}} ドルの料金です。最初の {{basicQuota}} トークンは含まれています。',
}, },
hooks: { hooks: {
title: 'フック', title: 'フック',

View file

@ -53,7 +53,8 @@ const usage = {
hooks: { hooks: {
title: '훅', title: '훅',
description: '{{usage}} <span>(처음 10개는 무료)</span>', description: '{{usage}} <span>(처음 10개는 무료)</span>',
tooltip: '훅 하나당 ${{price, number}} 의 추가 기능입니다. 처음 10개의 훅이 포함되어 있습니다.', tooltip:
'추가 기능은 ${{price, number}}에 {{tokenLimit}} 토큰당 가격이 책정됩니다. 처음 {{basicQuota}} 토큰이 포함되어 있습니다.',
}, },
pricing: { pricing: {
add_on_changes_in_current_cycle_notice: add_on_changes_in_current_cycle_notice:

View file

@ -48,7 +48,7 @@ const usage = {
title: 'Tokeny', title: 'Tokeny',
description: '{{usage}}', description: '{{usage}}',
tooltip: tooltip:
'Funkcja dodatkowa w cenie ${{price, number}} za milion tokenów. Pierwszy 1 milion tokenów jest wliczony.', 'Funkcja dodatkowa w cenie ${{price, number}} za {{tokenLimit}} tokenów. Pierwszy {{basicQuota}} tokenów jest wliczony.',
}, },
hooks: { hooks: {
title: 'Haki', title: 'Haki',

View file

@ -48,7 +48,7 @@ const usage = {
title: 'Tokens', title: 'Tokens',
description: '{{usage}}', description: '{{usage}}',
tooltip: tooltip:
'Recurso adicional com preço de ${{price, number}} por milhão de tokens. O primeiro 1 milhão de tokens está incluído.', 'Recurso adicional com preço de ${{price, number}} por {{tokenLimit}} de tokens. O primeiro {{basicQuota}} de tokens está incluído.',
}, },
hooks: { hooks: {
title: 'Hooks', title: 'Hooks',

View file

@ -48,7 +48,7 @@ const usage = {
title: 'Tokens', title: 'Tokens',
description: '{{usage}}', description: '{{usage}}',
tooltip: tooltip:
'Funcionalidade adicional com um preço de ${{price, number}} por milhão de tokens. O primeiro milhão de tokens está incluído.', 'Funcionalidade adicional com um preço de ${{price, number}} por {{tokenLimit}} de tokens. O primeiro {{basicQuota}} de tokens está incluído.',
}, },
hooks: { hooks: {
title: 'Hooks', title: 'Hooks',

View file

@ -48,7 +48,7 @@ const usage = {
title: 'Токены', title: 'Токены',
description: '{{usage}}', description: '{{usage}}',
tooltip: tooltip:
'Дополнительная функция с ценой ${{price, number}} за миллион токенов. Первые 1 миллион токенов включены.', 'Дополнительная функция с ценой ${{price, number}} за {{tokenLimit}} токенов. Первые 1 {{basicQuota}} токенов включены.',
}, },
hooks: { hooks: {
title: 'Хуки', title: 'Хуки',

View file

@ -48,7 +48,7 @@ const usage = {
title: 'Tokenler', title: 'Tokenler',
description: '{{usage}}', description: '{{usage}}',
tooltip: tooltip:
'Milyon token başına {{price, number}} $ ücretle ek özellik. İlk 1 milyon token dahildir.', '{{tokenLimit}} token başına {{price, number}} $ ücretle ek özellik. İlk 1 {{basicQuota}} token dahildir.',
}, },
hooks: { hooks: {
title: 'Hooklar', title: 'Hooklar',

View file

@ -42,7 +42,8 @@ const usage = {
tokens: { tokens: {
title: '令牌', title: '令牌',
description: '{{usage}}', description: '{{usage}}',
tooltip: '附加功能,每百万个令牌价格为 ${{price, number}} 。首百万个令牌包含在内。', tooltip:
'附加功能,每 {{tokenLimit}} 个令牌价格为 ${{price, number}} 。首 {{basicQuota}} 个令牌包含在内。',
}, },
hooks: { hooks: {
title: '钩子', title: '钩子',

View file

@ -42,7 +42,8 @@ const usage = {
tokens: { tokens: {
title: '令牌', title: '令牌',
description: '{{usage}}', description: '{{usage}}',
tooltip: '附加功能,每百萬次令牌 ${{price, number}}。首百萬次令牌已包含在內。', tooltip:
'附加功能,每 {{tokenLimit}} 次令牌 ${{price, number}}。首 {{basicQuota}} 次令牌已包含在內。',
}, },
hooks: { hooks: {
title: '鉤子', title: '鉤子',

View file

@ -42,7 +42,8 @@ const usage = {
tokens: { tokens: {
title: '令牌', title: '令牌',
description: '{{usage}}', description: '{{usage}}',
tooltip: '附加功能,每百萬令牌 ${{price, number}}。前 1 百萬令牌包含在內。', tooltip:
'附加功能,每 {{tokenLimit}} 令牌 ${{price, number}}。前 {{basicQuota}} 令牌包含在內。',
}, },
hooks: { hooks: {
title: '鉤子', title: '鉤子',

View file

@ -1394,8 +1394,8 @@ importers:
version: 3.23.8 version: 3.23.8
devDependencies: devDependencies:
'@logto/cloud': '@logto/cloud':
specifier: 0.2.5-5e334eb specifier: 0.2.5-aac51e9
version: 0.2.5-5e334eb(zod@3.23.8) version: 0.2.5-aac51e9(zod@3.23.8)
'@silverhand/eslint-config': '@silverhand/eslint-config':
specifier: 6.0.1 specifier: 6.0.1
version: 6.0.1(eslint@8.57.0)(prettier@3.0.0)(typescript@5.5.3) version: 6.0.1(eslint@8.57.0)(prettier@3.0.0)(typescript@5.5.3)
@ -2748,8 +2748,8 @@ importers:
specifier: ^29.5.0 specifier: ^29.5.0
version: 29.5.0 version: 29.5.0
'@logto/cloud': '@logto/cloud':
specifier: 0.2.5-5e334eb specifier: 0.2.5-aac51e9
version: 0.2.5-5e334eb(zod@3.23.8) version: 0.2.5-aac51e9(zod@3.23.8)
'@logto/connector-kit': '@logto/connector-kit':
specifier: workspace:^4.1.0 specifier: workspace:^4.1.0
version: link:../toolkit/connector-kit version: link:../toolkit/connector-kit
@ -3244,8 +3244,8 @@ importers:
version: 3.23.8 version: 3.23.8
devDependencies: devDependencies:
'@logto/cloud': '@logto/cloud':
specifier: 0.2.5-5e334eb specifier: 0.2.5-aac51e9
version: 0.2.5-5e334eb(zod@3.23.8) version: 0.2.5-aac51e9(zod@3.23.8)
'@silverhand/eslint-config': '@silverhand/eslint-config':
specifier: 6.0.1 specifier: 6.0.1
version: 6.0.1(eslint@8.57.0)(prettier@3.0.0)(typescript@5.5.3) version: 6.0.1(eslint@8.57.0)(prettier@3.0.0)(typescript@5.5.3)
@ -4747,7 +4747,7 @@ packages:
'@azure/core-http@3.0.4': '@azure/core-http@3.0.4':
resolution: {integrity: sha512-Fok9VVhMdxAFOtqiiAtg74fL0UJkt0z3D+ouUUxcRLzZNBioPRAMJFVxiWoJljYpXsRi4GDQHzQHDc9AiYaIUQ==} resolution: {integrity: sha512-Fok9VVhMdxAFOtqiiAtg74fL0UJkt0z3D+ouUUxcRLzZNBioPRAMJFVxiWoJljYpXsRi4GDQHzQHDc9AiYaIUQ==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
deprecated: This package is no longer supported. Please migrate to use @azure/core-rest-pipeline deprecated: deprecating as we migrated to core v2
'@azure/core-lro@2.5.1': '@azure/core-lro@2.5.1':
resolution: {integrity: sha512-JHQy/bA3NOz2WuzOi5zEk6n/TJdAropupxUT521JIJvW7EXV2YN2SFYZrf/2RHeD28QAClGdynYadZsbmP+nyQ==} resolution: {integrity: sha512-JHQy/bA3NOz2WuzOi5zEk6n/TJdAropupxUT521JIJvW7EXV2YN2SFYZrf/2RHeD28QAClGdynYadZsbmP+nyQ==}
@ -6092,8 +6092,8 @@ packages:
'@logto/client@2.7.2': '@logto/client@2.7.2':
resolution: {integrity: sha512-jsmuDl9QpXfR3uLEMPE67tvYoL5XcjJi+4yGqucYPjd4GH6SUHp3N9skk8C/OyygnKDPLY+ttwD0LaIbpGvn+Q==} resolution: {integrity: sha512-jsmuDl9QpXfR3uLEMPE67tvYoL5XcjJi+4yGqucYPjd4GH6SUHp3N9skk8C/OyygnKDPLY+ttwD0LaIbpGvn+Q==}
'@logto/cloud@0.2.5-5e334eb': '@logto/cloud@0.2.5-aac51e9':
resolution: {integrity: sha512-HqcxjQV5F87Q5dM8sQY+F06YrjZofFl2Hb3VJh6c6Q5fHIPZ6WWJDAVVbGZU8GK7LO2f8gpLWWD+p7DIyLzDoA==} resolution: {integrity: sha512-p/9u33xBFIuh6NRFO5D/G2rsvmZJAdnjjB6n8RxlMp34XHBuRRmiuB/KadKFQmUkPVJhDOo5hs9dgdd28ClQIA==}
engines: {node: ^20.9.0} engines: {node: ^20.9.0}
'@logto/js@4.1.4': '@logto/js@4.1.4':
@ -8308,7 +8308,7 @@ packages:
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
concat-map@0.0.1: concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
confusing-browser-globals@1.0.11: confusing-browser-globals@1.0.11:
resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==}
@ -8950,7 +8950,7 @@ packages:
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
ee-first@1.1.1: ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=}
electron-to-chromium@1.5.0: electron-to-chromium@1.5.0:
resolution: {integrity: sha512-Vb3xHHYnLseK8vlMJQKJYXJ++t4u1/qJ3vykuVrVjvdiOEhYyT1AuP4x03G8EnPmYvYOhe9T+dADTmthjRQMkA==} resolution: {integrity: sha512-Vb3xHHYnLseK8vlMJQKJYXJ++t4u1/qJ3vykuVrVjvdiOEhYyT1AuP4x03G8EnPmYvYOhe9T+dADTmthjRQMkA==}
@ -10019,7 +10019,7 @@ packages:
engines: {node: '>=16.17.0'} engines: {node: '>=16.17.0'}
humanize-number@0.0.2: humanize-number@0.0.2:
resolution: {integrity: sha512-un3ZAcNQGI7RzaWGZzQDH47HETM4Wrj6z6E4TId8Yeq9w5ZKUVB1nrT2jwFheTUjEmqcgTjXDc959jum+ai1kQ==} resolution: {integrity: sha1-EcCvakcWQ2M1iFiASPF5lUFInBg=}
husky@9.0.7: husky@9.0.7:
resolution: {integrity: sha512-vWdusw+y12DUEeoZqW1kplOFqk3tedGV8qlga8/SF6a3lOiWLqGZZQvfWvY0fQYdfiRi/u1DFNpudTSV9l1aCg==} resolution: {integrity: sha512-vWdusw+y12DUEeoZqW1kplOFqk3tedGV8qlga8/SF6a3lOiWLqGZZQvfWvY0fQYdfiRi/u1DFNpudTSV9l1aCg==}
@ -11272,7 +11272,7 @@ packages:
resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
media-typer@0.3.0: media-typer@0.3.0:
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} resolution: {integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
meow@10.1.5: meow@10.1.5:
@ -13335,7 +13335,7 @@ packages:
resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==}
stubs@3.0.0: stubs@3.0.0:
resolution: {integrity: sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==} resolution: {integrity: sha1-6NK6H6nJBXAwPAMLaQD31fiavls=}
style-search@0.1.0: style-search@0.1.0:
resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==} resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==}
@ -13993,7 +13993,7 @@ packages:
optional: true optional: true
void-elements@3.1.0: void-elements@3.1.0:
resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} resolution: {integrity: sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
w3c-xmlserializer@3.0.0: w3c-xmlserializer@3.0.0:
@ -16382,11 +16382,11 @@ snapshots:
'@logto/client@2.7.2': '@logto/client@2.7.2':
dependencies: dependencies:
'@logto/js': 4.1.4 '@logto/js': 4.1.4
'@silverhand/essentials': 2.9.2 '@silverhand/essentials': 2.9.1
camelcase-keys: 7.0.2 camelcase-keys: 7.0.2
jose: 5.9.6 jose: 5.9.6
'@logto/cloud@0.2.5-5e334eb(zod@3.23.8)': '@logto/cloud@0.2.5-aac51e9(zod@3.23.8)':
dependencies: dependencies:
'@silverhand/essentials': 2.9.2 '@silverhand/essentials': 2.9.2
'@withtyped/server': 0.14.0(zod@3.23.8) '@withtyped/server': 0.14.0(zod@3.23.8)
@ -16898,10 +16898,10 @@ snapshots:
eslint-config-prettier: 9.1.0(eslint@8.57.0) eslint-config-prettier: 9.1.0(eslint@8.57.0)
eslint-config-xo: 0.44.0(eslint@8.57.0) eslint-config-xo: 0.44.0(eslint@8.57.0)
eslint-config-xo-typescript: 4.0.0(@typescript-eslint/eslint-plugin@7.7.0(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3))(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3) eslint-config-xo-typescript: 4.0.0(@typescript-eslint/eslint-plugin@7.7.0(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3))(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3)
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0) eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0)
eslint-plugin-consistent-default-export-name: 0.0.15 eslint-plugin-consistent-default-export-name: 0.0.15
eslint-plugin-eslint-comments: 3.2.0(eslint@8.57.0) eslint-plugin-eslint-comments: 3.2.0(eslint@8.57.0)
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0)
eslint-plugin-n: 17.2.1(eslint@8.57.0) eslint-plugin-n: 17.2.1(eslint@8.57.0)
eslint-plugin-no-use-extend-native: 0.5.0 eslint-plugin-no-use-extend-native: 0.5.0
eslint-plugin-prettier: 5.1.3(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.0.0) eslint-plugin-prettier: 5.1.3(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.0.0)
@ -20350,13 +20350,13 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0): eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0):
dependencies: dependencies:
debug: 4.3.5 debug: 4.3.5
enhanced-resolve: 5.16.0 enhanced-resolve: 5.16.0
eslint: 8.57.0 eslint: 8.57.0
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0)
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0)
fast-glob: 3.3.2 fast-glob: 3.3.2
get-tsconfig: 4.7.3 get-tsconfig: 4.7.3
is-core-module: 2.13.1 is-core-module: 2.13.1
@ -20367,14 +20367,14 @@ snapshots:
- eslint-import-resolver-webpack - eslint-import-resolver-webpack
- supports-color - supports-color
eslint-module-utils@2.8.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): eslint-module-utils@2.8.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0):
dependencies: dependencies:
debug: 3.2.7 debug: 3.2.7
optionalDependencies: optionalDependencies:
'@typescript-eslint/parser': 7.7.0(eslint@8.57.0)(typescript@5.5.3) '@typescript-eslint/parser': 7.7.0(eslint@8.57.0)(typescript@5.5.3)
eslint: 8.57.0 eslint: 8.57.0
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0) eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -20396,7 +20396,7 @@ snapshots:
eslint: 8.57.0 eslint: 8.57.0
ignore: 5.3.1 ignore: 5.3.1
eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0):
dependencies: dependencies:
array-includes: 3.1.8 array-includes: 3.1.8
array.prototype.findlastindex: 1.2.5 array.prototype.findlastindex: 1.2.5
@ -20406,7 +20406,7 @@ snapshots:
doctrine: 2.1.0 doctrine: 2.1.0
eslint: 8.57.0 eslint: 8.57.0
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0)
hasown: 2.0.2 hasown: 2.0.2
is-core-module: 2.13.1 is-core-module: 2.13.1
is-glob: 4.0.3 is-glob: 4.0.3