diff --git a/packages/cli/package.json b/packages/cli/package.json index 8acd7c30d..f3e66953d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -76,6 +76,7 @@ "devDependencies": { "@silverhand/eslint-config": "4.0.1", "@silverhand/ts-config": "4.0.0", + "@withtyped/server": "^0.12.9", "@types/inquirer": "^9.0.0", "@types/jest": "^29.4.0", "@types/node": "^18.11.18", diff --git a/packages/cli/src/connector/factories.ts b/packages/cli/src/connector/factories.ts index 1c55b9b0e..23e81168e 100644 --- a/packages/cli/src/connector/factories.ts +++ b/packages/cli/src/connector/factories.ts @@ -1,3 +1,4 @@ +import type { BaseRoutes, Router } from '@withtyped/server'; import chalk from 'chalk'; import { consoleLog } from '../utils.js'; @@ -7,8 +8,8 @@ import { loadConnector } from './loader.js'; import type { ConnectorFactory, ConnectorPackage } from './types.js'; import { parseMetadata, validateConnectorModule } from './utils.js'; -// eslint-disable-next-line @silverhand/fp/no-let -let cachedConnectorFactories: ConnectorFactory[] | undefined; +// eslint-disable-next-line @silverhand/fp/no-let, @typescript-eslint/no-explicit-any +let cachedConnectorFactories: Array>> | undefined; export const loadConnectorFactories = async ( connectorPackages: ConnectorPackage[], @@ -46,7 +47,9 @@ export const loadConnectorFactories = async ( // eslint-disable-next-line @silverhand/fp/no-mutation cachedConnectorFactories = connectorFactories.filter( - (connectorFactory): connectorFactory is ConnectorFactory => connectorFactory !== undefined + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (connectorFactory): connectorFactory is ConnectorFactory> => + connectorFactory !== undefined ); return cachedConnectorFactories; diff --git a/packages/cli/src/connector/types.ts b/packages/cli/src/connector/types.ts index 5da7bdb0c..3184a2762 100644 --- a/packages/cli/src/connector/types.ts +++ b/packages/cli/src/connector/types.ts @@ -1,13 +1,15 @@ import type { AllConnector, CreateConnector } from '@logto/connector-kit'; +import type { BaseRoutes, Router } from '@withtyped/server'; /** * Dynamic loaded connector type. */ -export type ConnectorFactory = Pick< - T, - 'type' | 'metadata' -> & { - createConnector: CreateConnector; +export type ConnectorFactory< + // eslint-disable-next-line @typescript-eslint/no-explicit-any + T extends Router, + U extends AllConnector = AllConnector, +> = Pick & { + createConnector: CreateConnector; path: string; }; diff --git a/packages/cli/src/connector/utils.ts b/packages/cli/src/connector/utils.ts index ebc9a58ff..c0f2c4ca6 100644 --- a/packages/cli/src/connector/utils.ts +++ b/packages/cli/src/connector/utils.ts @@ -10,6 +10,7 @@ import type { GetCloudServiceClient, } from '@logto/connector-kit'; import { ConnectorError, ConnectorErrorCodes, ConnectorType } from '@logto/connector-kit'; +import type { BaseRoutes, Router } from '@withtyped/server'; import { consoleLog } from '../utils.js'; @@ -90,11 +91,15 @@ export const parseMetadata = async ( }; }; -export const buildRawConnector = async ( - connectorFactory: ConnectorFactory, +export const buildRawConnector = async < + // eslint-disable-next-line @typescript-eslint/no-explicit-any + T extends Router, + U extends AllConnector = AllConnector, +>( + connectorFactory: ConnectorFactory, getConnectorConfig?: GetConnectorConfig, - getCloudServiceClient?: GetCloudServiceClient -): Promise<{ rawConnector: T; rawMetadata: ConnectorMetadata }> => { + getCloudServiceClient?: GetCloudServiceClient +): Promise<{ rawConnector: U; rawMetadata: ConnectorMetadata }> => { const { createConnector, path: packagePath } = connectorFactory; const rawConnector = await createConnector({ getConfig: getConnectorConfig ?? notImplemented, diff --git a/packages/connectors/connector-logto-email/package.json b/packages/connectors/connector-logto-email/package.json index 98e4b37df..904aeb18d 100644 --- a/packages/connectors/connector-logto-email/package.json +++ b/packages/connectors/connector-logto-email/package.json @@ -47,5 +47,8 @@ "prettier": "@silverhand/eslint-config/.prettierrc", "publishConfig": { "access": "public" + }, + "devDependencies": { + "@logto/cloud": "0.2.5-d434baa" } } diff --git a/packages/connectors/connector-logto-email/src/index.ts b/packages/connectors/connector-logto-email/src/index.ts index b045d345f..1a7451bd6 100644 --- a/packages/connectors/connector-logto-email/src/index.ts +++ b/packages/connectors/connector-logto-email/src/index.ts @@ -1,6 +1,7 @@ import { assert, conditional } from '@silverhand/essentials'; import { HTTPError } from 'got'; +import type router from '@logto/cloud/routes'; import type { CreateConnector, EmailConnector, @@ -20,7 +21,10 @@ import { defaultMetadata, emailEndpoint, usageEndpoint } from './constant.js'; import { logtoEmailConfigGuard } from './types.js'; const sendMessage = - (getConfig: GetConnectorConfig, getClient?: GetCloudServiceClient): SendMessageFunction => + ( + getConfig: GetConnectorConfig, + getClient?: GetCloudServiceClient + ): SendMessageFunction => async (data, inputConfig) => { const config = inputConfig ?? (await getConfig(defaultMetadata.id)); validateConfig(config, logtoEmailConfigGuard); @@ -47,7 +51,10 @@ const sendMessage = }; const getUsage = - (getConfig: GetConnectorConfig, getClient?: GetCloudServiceClient): GetUsageFunction => + ( + getConfig: GetConnectorConfig, + getClient?: GetCloudServiceClient + ): GetUsageFunction => async (startFrom?: Date) => { const config = await getConfig(defaultMetadata.id); validateConfig(config, logtoEmailConfigGuard); @@ -61,7 +68,7 @@ const getUsage = return count; }; -const createLogtoEmailConnector: CreateConnector = async ({ +const createLogtoEmailConnector: CreateConnector = async ({ getConfig, getCloudServiceClient: getClient, }) => { diff --git a/packages/core/src/__mocks__/connector.ts b/packages/core/src/__mocks__/connector.ts index 20a409307..e8cdabdce 100644 --- a/packages/core/src/__mocks__/connector.ts +++ b/packages/core/src/__mocks__/connector.ts @@ -1,4 +1,5 @@ import type { ConnectorFactory } from '@logto/cli/lib/connector/index.js'; +import type router from '@logto/cloud/routes'; import { ConnectorPlatform, DemoConnector } from '@logto/connector-kit'; import type { Connector } from '@logto/schemas'; import { ConnectorType } from '@logto/schemas'; @@ -54,7 +55,7 @@ export const mockLogtoConnector = { configGuard: any(), }; -export const mockConnectorFactory: ConnectorFactory = { +export const mockConnectorFactory: ConnectorFactory = { metadata: mockMetadata, type: ConnectorType.Social, path: 'random_path', diff --git a/packages/core/src/routes/connector/config-testing.test.ts b/packages/core/src/routes/connector/config-testing.test.ts index 24d444775..64b5fb4dd 100644 --- a/packages/core/src/routes/connector/config-testing.test.ts +++ b/packages/core/src/routes/connector/config-testing.test.ts @@ -1,4 +1,5 @@ import type { ConnectorFactory } from '@logto/cli/lib/connector/index.js'; +import type router from '@logto/cloud/routes'; import { VerificationCodeType } from '@logto/connector-kit'; import type { EmailConnector, SmsConnector } from '@logto/connector-kit'; import { ConnectorType } from '@logto/schemas'; @@ -66,7 +67,7 @@ describe('connector services route', () => { it('should get SMS connector and send test message', async () => { const sendMessage = jest.fn(); - const mockedSmsConnectorFactory: ConnectorFactory = { + const mockedSmsConnectorFactory: ConnectorFactory = { ...mockConnectorFactory, metadata: mockMetadata, type: ConnectorType.Sms, @@ -93,7 +94,7 @@ describe('connector services route', () => { it('should get email connector and send test message', async () => { const sendMessage = jest.fn(); - const mockedEmailConnectorFactory: ConnectorFactory = { + const mockedEmailConnectorFactory: ConnectorFactory = { ...mockConnectorFactory, metadata: mockMetadata, type: ConnectorType.Email, diff --git a/packages/core/src/routes/connector/config-testing.ts b/packages/core/src/routes/connector/config-testing.ts index 622adf8ae..3a6ab4709 100644 --- a/packages/core/src/routes/connector/config-testing.ts +++ b/packages/core/src/routes/connector/config-testing.ts @@ -1,5 +1,6 @@ import { buildRawConnector, notImplemented } from '@logto/cli/lib/connector/index.js'; import type { ConnectorFactory } from '@logto/cli/lib/connector/index.js'; +import type CloudRouter from '@logto/cloud/routes'; import { type SmsConnector, type EmailConnector, @@ -47,7 +48,11 @@ export default function connectorConfigTestingRoutes( const connectorFactories = await loadConnectorFactories(); const connectorFactory = connectorFactories .filter( - (factory): factory is ConnectorFactory | ConnectorFactory => + ( + factory + ): factory is + | ConnectorFactory + | ConnectorFactory => factory.type === ConnectorType.Email || factory.type === ConnectorType.Sms ) .find(({ metadata: { id } }) => id === factoryId && !demoConnectorIds.includes(id)); @@ -66,7 +71,7 @@ export default function connectorConfigTestingRoutes( const { rawConnector: { sendMessage }, - } = await buildRawConnector( + } = await buildRawConnector( connectorFactory, notImplemented, conditional(ServiceConnector.Email === connectorFactory.metadata.id && getClient) diff --git a/packages/core/src/routes/connector/index.ts b/packages/core/src/routes/connector/index.ts index 3474c493c..b6c9b66a3 100644 --- a/packages/core/src/routes/connector/index.ts +++ b/packages/core/src/routes/connector/index.ts @@ -1,4 +1,5 @@ import { type ConnectorFactory, buildRawConnector } from '@logto/cli/lib/connector/index.js'; +import type router from '@logto/cloud/routes'; import { demoConnectorIds, validateConfig } from '@logto/connector-kit'; import { Connectors, ConnectorType, connectorResponseGuard, type JsonObject } from '@logto/schemas'; import { generateStandardShortId } from '@logto/shared'; @@ -20,7 +21,10 @@ import connectorAuthorizationUriRoutes from './authorization-uri.js'; import connectorConfigTestingRoutes from './config-testing.js'; import connectorFactoryRoutes from './factory.js'; -const guardConnectorsQuota = async (factory: ConnectorFactory, quota: QuotaLibrary) => { +const guardConnectorsQuota = async ( + factory: ConnectorFactory, + quota: QuotaLibrary +) => { if (factory.metadata.isStandard) { await quota.guardKey('standardConnectorsLimit'); } diff --git a/packages/core/src/utils/connectors/index.ts b/packages/core/src/utils/connectors/index.ts index 1cc9f23a6..30d0e4070 100644 --- a/packages/core/src/utils/connectors/index.ts +++ b/packages/core/src/utils/connectors/index.ts @@ -6,6 +6,7 @@ import type { ConnectorFactory } from '@logto/cli/lib/connector/index.js'; import { loadConnectorFactories as _loadConnectorFactories } from '@logto/cli/lib/connector/index.js'; import { connectorDirectory } from '@logto/cli/lib/constants.js'; import { getConnectorPackagesFromDirectory } from '@logto/cli/lib/utils.js'; +import type router from '@logto/cloud/routes'; import { demoConnectorIds, ConnectorType, @@ -59,7 +60,7 @@ export const transpileLogtoConnector = async ( export const transpileConnectorFactory = ({ metadata, type, -}: ConnectorFactory): ConnectorFactoryResponse => { +}: ConnectorFactory): ConnectorFactoryResponse => { return { type, ...metadata, @@ -67,7 +68,9 @@ export const transpileConnectorFactory = ({ }; }; -const checkDuplicateConnectorFactoriesId = (connectorFactories: ConnectorFactory[]) => { +const checkDuplicateConnectorFactoriesId = ( + connectorFactories: Array> +) => { const connectorFactoryIds = connectorFactories.map(({ metadata }) => metadata.id); const deduplicatedConnectorFactoryIds = deduplicate(connectorFactoryIds); diff --git a/packages/toolkit/connector-kit/package.json b/packages/toolkit/connector-kit/package.json index a73384842..c870fb7c8 100644 --- a/packages/toolkit/connector-kit/package.json +++ b/packages/toolkit/connector-kit/package.json @@ -38,13 +38,13 @@ "dependencies": { "@logto/language-kit": "workspace:^1.0.0", "@silverhand/essentials": "^2.8.4", - "@withtyped/client": "^0.7.22" + "@withtyped/client": "^0.7.22", + "@withtyped/server":"^0.12.9" }, "optionalDependencies": { "zod": "^3.20.2" }, "devDependencies": { - "@logto/cloud": "0.2.5-a3e852f", "@jest/types": "^29.0.3", "@silverhand/eslint-config": "4.0.1", "@silverhand/ts-config": "4.0.0", diff --git a/packages/toolkit/connector-kit/src/types.ts b/packages/toolkit/connector-kit/src/types.ts index 69ce63512..524ef61e4 100644 --- a/packages/toolkit/connector-kit/src/types.ts +++ b/packages/toolkit/connector-kit/src/types.ts @@ -1,7 +1,7 @@ -import type router from '@logto/cloud/routes'; import type { LanguageTag } from '@logto/language-kit'; import { isLanguageTag } from '@logto/language-kit'; import type Client from '@withtyped/client'; +import type { BaseRoutes, Router } from '@withtyped/server'; import type { ZodType } from 'zod'; import { z } from 'zod'; @@ -199,14 +199,21 @@ export type BaseConnector = { configGuard: ZodType; }; -export type CreateConnector = (options: { +export type CreateConnector< + T extends AllConnector, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + U extends Router = Router, +> = (options: { getConfig: GetConnectorConfig; - getCloudServiceClient?: GetCloudServiceClient; + getCloudServiceClient?: GetCloudServiceClient; }) => Promise; export type GetConnectorConfig = (id: string) => Promise; -export type GetCloudServiceClient = () => Promise>; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type GetCloudServiceClient> = () => Promise< + Client +>; export type AllConnector = SmsConnector | EmailConnector | SocialConnector; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f17093b25..dfb3cc9d4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -218,6 +218,9 @@ importers: '@types/yargs': specifier: ^17.0.13 version: 17.0.13 + '@withtyped/server': + specifier: ^0.12.9 + version: 0.12.9(zod@3.20.2) eslint: specifier: ^8.44.0 version: 8.44.0 @@ -1318,6 +1321,9 @@ importers: '@jest/types': specifier: ^29.5.0 version: 29.5.0 + '@logto/cloud': + specifier: 0.2.5-d434baa + version: 0.2.5-d434baa(zod@3.20.2) '@rollup/plugin-commonjs': specifier: ^25.0.0 version: 25.0.0(rollup@3.8.0) @@ -3977,6 +3983,9 @@ importers: '@withtyped/client': specifier: ^0.7.22 version: 0.7.22(zod@3.20.2) + '@withtyped/server': + specifier: ^0.12.9 + version: 0.12.9(zod@3.20.2) optionalDependencies: zod: specifier: ^3.20.2 @@ -3985,9 +3994,6 @@ importers: '@jest/types': specifier: ^29.0.3 version: 29.1.2 - '@logto/cloud': - specifier: 0.2.5-a3e852f - version: 0.2.5-a3e852f(zod@3.20.2) '@silverhand/eslint-config': specifier: 4.0.1 version: 4.0.1(eslint@8.44.0)(prettier@3.0.0)(typescript@5.0.2) @@ -7341,16 +7347,6 @@ packages: - zod dev: true - /@logto/cloud@0.2.5-a3e852f(zod@3.20.2): - resolution: {integrity: sha512-dIrEUW7gi477HQpNsq/HT1gdvPK2ZmVuV73u2rH9LXGEIFIVGqmmIaaK3IcOPG110jKCBhTzF0+hKsW9Y3Pjmw==} - engines: {node: ^18.12.0} - dependencies: - '@silverhand/essentials': 2.8.4 - '@withtyped/server': 0.12.9(zod@3.20.2) - transitivePeerDependencies: - - zod - dev: true - /@logto/cloud@0.2.5-d434baa(zod@3.20.2): resolution: {integrity: sha512-VmWpqFzpWBrzJPQLvfe0bb7/XjF3lxs/rPbLt3zqBjGPtDXM9FAUn1m/gPbTwzXi5PxGOjlbD7jTl+3+1u4/NQ==} engines: {node: ^18.12.0}