0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-31 22:51:25 -05:00

refactor(connector): update per discussion

This commit is contained in:
Darcy Ye 2022-08-11 16:21:20 +08:00
parent ff89c0b3a5
commit d9560ae44e
No known key found for this signature in database
GPG key ID: B46F4C07EDEFC610
99 changed files with 604 additions and 724 deletions

View file

@ -24,7 +24,7 @@
"prepack": "pnpm build"
},
"dependencies": {
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/shared": "^1.0.0-beta.4",
"@silverhand/essentials": "^1.1.0",
"@silverhand/jest-config": "1.0.0-rc.3",

View file

@ -1,4 +1,4 @@
import { ConnectorType, ConnectorMetadata, ConnectorPlatform } from '@logto/connector-types';
import { ConnectorType, ConnectorMetadata, ConnectorPlatform } from '@logto/connector-schemas';
export const authorizationEndpoint = 'alipay://'; // This is used to arouse the native Alipay App
export const alipayEndpoint = 'https://openapi.alipay.com/gateway.do';

View file

@ -3,7 +3,7 @@ import {
ConnectorErrorCodes,
GetConnectorConfig,
ValidateConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import nock from 'nock';
import AlipayNativeConnector from '.';

View file

@ -9,15 +9,15 @@
* https://opendocs.alipay.com/open/204/105296/
*/
import { SocialConnector } from '@logto/connector-base-classes';
import {
SocialConnector,
AuthResponseParser,
ConnectorError,
ConnectorErrorCodes,
GetAuthorizationUri,
GetUserInfo,
GetConnectorConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import { assert } from '@silverhand/essentials';
import dayjs from 'dayjs';
import got from 'got';
@ -45,6 +45,7 @@ import {
import { signingParameters } from './utils';
export type { AlipayNativeConfig } from './types';
export { defaultMetadata } from './constant';
export default class AlipayNativeConnector extends SocialConnector<AlipayNativeConfig> {
private readonly signingParameters = signingParameters;
@ -52,8 +53,6 @@ export default class AlipayNativeConnector extends SocialConnector<AlipayNativeC
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
// eslint-disable-next-line unicorn/prefer-module
this.metadataParser(__dirname);
}
public validateConfig(config: unknown): asserts config is AlipayNativeConfig {

View file

@ -24,7 +24,7 @@
"prepack": "pnpm build"
},
"dependencies": {
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/shared": "^1.0.0-beta.4",
"@silverhand/essentials": "^1.1.0",
"@silverhand/jest-config": "1.0.0-rc.3",

View file

@ -1,6 +1,6 @@
// FIXME: @Darcy
/* eslint-disable unicorn/text-encoding-identifier-case */
import { ConnectorType, ConnectorMetadata, ConnectorPlatform } from '@logto/connector-types';
import { ConnectorType, ConnectorMetadata, ConnectorPlatform } from '@logto/connector-schemas';
export const authorizationEndpoint = 'https://openauth.alipay.com/oauth2/publicAppAuthorize.htm';
export const alipayEndpoint = 'https://openapi.alipay.com/gateway.do';

View file

@ -3,7 +3,7 @@ import {
ConnectorErrorCodes,
GetConnectorConfig,
ValidateConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import nock from 'nock';
import AlipayConnector from '.';

View file

@ -4,15 +4,15 @@
* https://opendocs.alipay.com/open/263/105808
* https://opendocs.alipay.com/open/01emu5
*/
import { SocialConnector } from '@logto/connector-base-classes';
import {
SocialConnector,
AuthResponseParser,
ConnectorError,
ConnectorErrorCodes,
GetAuthorizationUri,
GetUserInfo,
GetConnectorConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import { assert } from '@silverhand/essentials';
import dayjs from 'dayjs';
import got from 'got';
@ -42,6 +42,7 @@ import {
import { signingParameters } from './utils';
export type { AlipayConfig } from './types';
export { defaultMetadata } from './constant';
export default class AlipayConnector extends SocialConnector<AlipayConfig> {
private readonly signingParameters = signingParameters;
@ -49,8 +50,6 @@ export default class AlipayConnector extends SocialConnector<AlipayConfig> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
// eslint-disable-next-line unicorn/prefer-module
this.metadataParser(__dirname);
}
public validateConfig(config: unknown): asserts config is AlipayConfig {

View file

@ -24,7 +24,7 @@
"prepack": "pnpm build"
},
"dependencies": {
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/shared": "^1.0.0-beta.4",
"@silverhand/essentials": "^1.1.0",
"@silverhand/jest-config": "1.0.0-rc.3",

View file

@ -1,4 +1,4 @@
import { ConnectorType, ConnectorMetadata } from '@logto/connector-types';
import { ConnectorType, ConnectorMetadata } from '@logto/connector-schemas';
export const endpoint = 'https://dm.aliyuncs.com/';

View file

@ -1,4 +1,4 @@
import { GetConnectorConfig, ValidateConfig } from '@logto/connector-types';
import { GetConnectorConfig, ValidateConfig } from '@logto/connector-schemas';
import AliyunDmConnector from '.';
import { mockedConfig } from './mock';

View file

@ -1,10 +1,10 @@
import { EmailConnector } from '@logto/connector-base-classes';
import {
EmailConnector,
ConnectorError,
ConnectorErrorCodes,
EmailSendMessageByFunction,
GetConnectorConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import { assert } from '@silverhand/essentials';
import { HTTPError } from 'got';
@ -17,12 +17,12 @@ import {
sendMailErrorResponseGuard,
} from './types';
export { defaultMetadata } from './constant';
export default class AliyunDmConnector extends EmailConnector<AliyunDmConfig> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
// eslint-disable-next-line unicorn/prefer-module
this.metadataParser(__dirname);
}
public validateConfig(config: unknown): asserts config is AliyunDmConfig {

View file

@ -24,7 +24,7 @@
"prepack": "pnpm build"
},
"dependencies": {
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/schemas": "^1.0.0-beta.4",
"@logto/shared": "^1.0.0-beta.4",
"@silverhand/essentials": "^1.1.0",

View file

@ -1,4 +1,4 @@
import { ConnectorMetadata, ConnectorType } from '@logto/connector-types';
import { ConnectorMetadata, ConnectorType } from '@logto/connector-schemas';
export const endpoint = 'https://dysmsapi.aliyuncs.com/';

View file

@ -1,4 +1,4 @@
import { GetConnectorConfig, ValidateConfig } from '@logto/connector-types';
import { GetConnectorConfig, ValidateConfig } from '@logto/connector-schemas';
import AliyunSmsConnector from '.';
import { mockedConnectorConfig, mockedValidConnectorConfig, phoneTest, codeTest } from './mock';

View file

@ -1,10 +1,10 @@
import { SmsConnector } from '@logto/connector-base-classes';
import {
SmsConnector,
ConnectorError,
ConnectorErrorCodes,
SmsSendMessageByFunction,
GetConnectorConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import { assert } from '@silverhand/essentials';
import { HTTPError } from 'got';
@ -12,12 +12,12 @@ import { defaultMetadata } from './constant';
import { sendSms } from './single-send-text';
import { aliyunSmsConfigGuard, AliyunSmsConfig, sendSmsResponseGuard } from './types';
export { defaultMetadata } from './constant';
export default class AliyunSmsConnector extends SmsConnector<AliyunSmsConfig> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
// eslint-disable-next-line unicorn/prefer-module
this.metadataParser(__dirname);
}
public validateConfig(config: unknown): asserts config is AliyunSmsConfig {

View file

@ -25,7 +25,7 @@
"prepack": "pnpm build"
},
"dependencies": {
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/schemas": "^1.0.0-beta.4",
"@logto/shared": "^1.0.0-beta.4",
"@silverhand/essentials": "^1.1.0",

View file

@ -1,4 +1,4 @@
import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-types';
import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-schemas';
// https://appleid.apple.com/.well-known/openid-configuration
export const issuer = 'https://appleid.apple.com';

View file

@ -3,7 +3,7 @@ import {
ConnectorErrorCodes,
GetConnectorConfig,
ValidateConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import { jwtVerify } from 'jose';
import AppleConnector from '.';

View file

@ -1,24 +1,24 @@
import { SocialConnector } from '@logto/connector-base-classes';
import {
SocialConnector,
AuthResponseParser,
GetAuthorizationUri,
GetUserInfo,
ConnectorError,
ConnectorErrorCodes,
GetConnectorConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import { createRemoteJWKSet, jwtVerify } from 'jose';
import { scope, defaultMetadata, jwksUri, issuer, authorizationEndpoint } from './constant';
import { appleConfigGuard, authResponseGuard, AppleConfig, AuthResponse } from './types';
export { defaultMetadata } from './constant';
// TO-DO: support nonce validation
export default class AppleConnector extends SocialConnector<AppleConfig> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
// eslint-disable-next-line unicorn/prefer-module
this.metadataParser(__dirname);
}
public validateConfig(config: unknown): asserts config is AppleConfig {

View file

@ -25,7 +25,7 @@
},
"dependencies": {
"@azure/msal-node": "^1.12.0",
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/schemas": "^1.0.0-beta.4",
"@logto/shared": "^1.0.0-beta.4",
"@silverhand/essentials": "^1.1.0",

View file

@ -1,4 +1,4 @@
import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-types';
import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-schemas';
export const graphAPIEndpoint = 'https://graph.microsoft.com/v1.0/me';
export const scopes = ['User.Read'];

View file

@ -1,4 +1,4 @@
import { GetConnectorConfig } from '@logto/connector-types';
import { GetConnectorConfig } from '@logto/connector-schemas';
import AzureADConnector from '.';

View file

@ -6,8 +6,8 @@ import {
AuthorizationUrlRequest,
CryptoProvider,
} from '@azure/msal-node';
import { SocialConnector } from '@logto/connector-base-classes';
import {
SocialConnector,
ConnectorError,
ConnectorErrorCodes,
GetAuthorizationUri,
@ -15,7 +15,7 @@ import {
codeWithRedirectDataGuard,
CodeWithRedirectData,
GetConnectorConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import { assert, conditional } from '@silverhand/essentials';
import got, { HTTPError } from 'got';
@ -27,6 +27,8 @@ import {
userInfoResponseGuard,
} from './types';
export { defaultMetadata } from './constant';
export default class AzureADConnector extends SocialConnector<AzureADConfig> {
public clientApplication!: ConfidentialClientApplication;
public authCodeUrlParams!: AuthorizationUrlRequest;
@ -37,8 +39,6 @@ export default class AzureADConnector extends SocialConnector<AzureADConfig> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
// eslint-disable-next-line unicorn/prefer-module
this.metadataParser(__dirname);
}
public validateConfig(config: unknown): asserts config is AzureADConfig {

View file

@ -1,38 +0,0 @@
{
"name": "@logto/connector-base-classes",
"version": "1.0.0-beta.3",
"main": "lib/index.js",
"author": "Silverhand Inc. <contact@silverhand.io>",
"license": "MPL-2.0",
"private": true,
"files": [
"lib"
],
"scripts": {
"precommit": "lint-staged",
"build": "rm -rf lib/ && tsc --p tsconfig.build.json",
"lint": "eslint --ext .ts src",
"lint:report": "pnpm lint --format json --output-file report.json",
"prepack": "pnpm build"
},
"engines": {
"node": "^16.0.0"
},
"dependencies": {
"@logto/connector-types": "^1.0.0-beta.3"
},
"devDependencies": {
"@silverhand/eslint-config": "1.0.0-rc.2",
"@silverhand/essentials": "^1.1.6",
"@silverhand/ts-config": "1.0.0-rc.2",
"@types/node": "^16.3.1",
"eslint": "^8.21.0",
"lint-staged": "^13.0.0",
"prettier": "^2.7.1",
"typescript": "^4.7.4"
},
"eslintConfig": {
"extends": "@silverhand"
},
"prettier": "@silverhand/eslint-config/.prettierrc"
}

View file

@ -1,107 +0,0 @@
import { existsSync, readFileSync } from 'fs';
import path from 'path';
import {
ConnectorMetadata,
GetConnectorConfig,
EmailSendMessageFunction,
EmailSendTestMessageFunction,
EmailSendMessageByFunction,
SmsSendMessageFunction,
SmsSendTestMessageFunction,
SmsSendMessageByFunction,
GetAuthorizationUri,
GetUserInfo,
AuthResponseParser,
} from '@logto/connector-types';
export class BaseConnector<T> {
public metadata!: ConnectorMetadata;
public getConfig: GetConnectorConfig;
constructor(getConnectorConfig: GetConnectorConfig) {
this.getConfig = getConnectorConfig;
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
public validateConfig(config: unknown): asserts config is T {}
// eslint-disable-next-line complexity
protected metadataParser = (currentPath: string) => {
if (
!this.metadata.logo.startsWith('http') &&
existsSync(path.join(currentPath, '..', this.metadata.logo))
) {
const data = readFileSync(path.join(currentPath, '..', this.metadata.logo));
this.metadata.logo = `data:image/svg+xml;base64,${data.toString('base64')}`;
}
if (
this.metadata.logoDark &&
!this.metadata.logoDark.startsWith('http') &&
existsSync(path.join(currentPath, '..', this.metadata.logoDark))
) {
const data = readFileSync(path.join(currentPath, '..', this.metadata.logoDark));
this.metadata.logoDark = `data:image/svg+xml;base64,${data.toString('base64')}`;
}
if (this.metadata.readme && existsSync(path.join(currentPath, '..', this.metadata.readme))) {
this.metadata.readme = readFileSync(
path.join(currentPath, '..', this.metadata.readme),
'utf8'
);
}
if (
this.metadata.configTemplate &&
existsSync(path.join(currentPath, '..', this.metadata.configTemplate))
) {
this.metadata.configTemplate = readFileSync(
path.join(currentPath, '..', this.metadata.configTemplate),
'utf8'
);
}
};
}
export class SmsConnector<T> extends BaseConnector<T> {
protected readonly sendMessageBy!: EmailSendMessageByFunction<T>;
public sendMessage: EmailSendMessageFunction = async (address, type, data) => {
const config = await this.getConfig(this.metadata.id);
this.validateConfig(config);
return this.sendMessageBy(config, address, type, data);
};
public sendTestMessage?: EmailSendTestMessageFunction = async (config, address, type, data) => {
this.validateConfig(config);
return this.sendMessageBy(config, address, type, data);
};
}
export class EmailConnector<T> extends BaseConnector<T> {
protected readonly sendMessageBy!: SmsSendMessageByFunction<T>;
public sendMessage: SmsSendMessageFunction = async (address, type, data) => {
const config = await this.getConfig(this.metadata.id);
this.validateConfig(config);
return this.sendMessageBy(config, address, type, data);
};
public sendTestMessage?: SmsSendTestMessageFunction = async (config, address, type, data) => {
this.validateConfig(config);
return this.sendMessageBy(config, address, type, data);
};
}
export class SocialConnector<T> extends BaseConnector<T> {
public getAuthorizationUri!: GetAuthorizationUri;
public getUserInfo!: GetUserInfo;
protected authResponseParser!: AuthResponseParser;
}

View file

@ -24,7 +24,7 @@
"prepack": "pnpm build"
},
"dependencies": {
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/schemas": "^1.0.0-beta.4",
"@logto/shared": "^1.0.0-beta.4",
"@silverhand/essentials": "^1.1.0",

View file

@ -1,4 +1,4 @@
import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-types';
import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-schemas';
/**
* Note: If you do not include a version number we will default to the oldest available version, so it's recommended to include the version number in your requests.

View file

@ -3,7 +3,7 @@ import {
ConnectorErrorCodes,
GetConnectorConfig,
ValidateConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import nock from 'nock';
import FacebookConnector from '.';

View file

@ -3,8 +3,8 @@
* https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow
*/
import { SocialConnector } from '@logto/connector-base-classes';
import {
SocialConnector,
AuthResponseParser,
ConnectorError,
ConnectorErrorCodes,
@ -13,7 +13,7 @@ import {
GetConnectorConfig,
codeWithRedirectDataGuard,
CodeWithRedirectData,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import { assert } from '@silverhand/essentials';
import got, { HTTPError } from 'got';
@ -33,12 +33,12 @@ import {
userInfoResponseGuard,
} from './types';
export { defaultMetadata } from './constant';
export default class FacebookConnector extends SocialConnector<FacebookConfig> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
// eslint-disable-next-line unicorn/prefer-module
this.metadataParser(__dirname);
}
public validateConfig(config: unknown): asserts config is FacebookConfig {

View file

@ -25,7 +25,7 @@
"prepack": "pnpm build"
},
"dependencies": {
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/schemas": "^1.0.0-beta.4",
"@logto/shared": "^1.0.0-beta.4",
"@silverhand/essentials": "^1.1.0",

View file

@ -1,4 +1,4 @@
import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-types';
import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-schemas';
export const authorizationEndpoint = 'https://github.com/login/oauth/authorize';
export const scope = 'read:user';

View file

@ -3,7 +3,7 @@ import {
ConnectorErrorCodes,
GetConnectorConfig,
ValidateConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import nock from 'nock';
import * as qs from 'query-string';

View file

@ -1,5 +1,5 @@
import { SocialConnector } from '@logto/connector-base-classes';
import {
SocialConnector,
AuthResponseParser,
GetAuthorizationUri,
GetUserInfo,
@ -8,7 +8,7 @@ import {
GetConnectorConfig,
codeDataGuard,
CodeData,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import { assert, conditional } from '@silverhand/essentials';
import got, { HTTPError } from 'got';
import * as qs from 'query-string';
@ -29,12 +29,12 @@ import {
userInfoResponseGuard,
} from './types';
export { defaultMetadata } from './constant';
export default class GithubConnector extends SocialConnector<GithubConfig> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
// eslint-disable-next-line unicorn/prefer-module
this.metadataParser(__dirname);
}
public validateConfig(config: unknown): asserts config is GithubConfig {

View file

@ -24,7 +24,7 @@
"prepack": "pnpm build"
},
"dependencies": {
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/schemas": "^1.0.0-beta.4",
"@logto/shared": "^1.0.0-beta.4",
"@silverhand/essentials": "^1.1.0",

View file

@ -1,4 +1,4 @@
import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-types';
import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-schemas';
export const authorizationEndpoint = 'https://accounts.google.com/o/oauth2/v2/auth';
export const accessTokenEndpoint = 'https://oauth2.googleapis.com/token';

View file

@ -3,7 +3,7 @@ import {
ConnectorErrorCodes,
GetConnectorConfig,
ValidateConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import nock from 'nock';
import GoogleConnector from '.';

View file

@ -2,8 +2,8 @@
* The Implementation of OpenID Connect of Google Identity Platform.
* https://developers.google.com/identity/protocols/oauth2/openid-connect
*/
import { SocialConnector } from '@logto/connector-base-classes';
import {
SocialConnector,
AuthResponseParser,
ConnectorError,
ConnectorErrorCodes,
@ -12,7 +12,7 @@ import {
GetConnectorConfig,
codeWithRedirectDataGuard,
CodeWithRedirectData,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import { conditional, assert } from '@silverhand/essentials';
import got, { HTTPError } from 'got';
@ -31,12 +31,12 @@ import {
userInfoResponseGuard,
} from './types';
export { defaultMetadata } from './constant';
export default class GoogleConnector extends SocialConnector<GoogleConfig> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
// eslint-disable-next-line unicorn/prefer-module
this.metadataParser(__dirname);
}
public validateConfig(config: unknown): asserts config is GoogleConfig {

View file

@ -22,7 +22,7 @@
"prepack": "pnpm build"
},
"dependencies": {
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/shared": "^1.0.0-beta.4",
"@silverhand/essentials": "^1.1.6",
"zod": "^3.14.3"

View file

@ -1,4 +1,4 @@
import { ConnectorType, ConnectorMetadata } from '@logto/connector-types';
import { ConnectorType, ConnectorMetadata } from '@logto/connector-schemas';
export const defaultMetadata: ConnectorMetadata = {
id: 'mock-email-service',

View file

@ -1,24 +1,24 @@
import fs from 'fs/promises';
import path from 'path';
import { EmailConnector } from '@logto/connector-base-classes';
import {
EmailConnector,
ConnectorError,
ConnectorErrorCodes,
EmailSendMessageByFunction,
GetConnectorConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import { assert } from '@silverhand/essentials';
import { defaultMetadata } from './constant';
import { mockMailConfigGuard, MockMailConfig } from './types';
export { defaultMetadata } from './constant';
export default class MockMailConnector extends EmailConnector<MockMailConfig> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
// eslint-disable-next-line unicorn/prefer-module
this.metadataParser(__dirname);
}
public validateConfig(config: unknown): asserts config is MockMailConfig {

View file

@ -22,7 +22,7 @@
"prepack": "pnpm build"
},
"dependencies": {
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/shared": "^1.0.0-beta.4",
"@silverhand/essentials": "^1.1.6",
"zod": "^3.14.3"

View file

@ -1,4 +1,4 @@
import { ConnectorType, ConnectorMetadata } from '@logto/connector-types';
import { ConnectorType, ConnectorMetadata } from '@logto/connector-schemas';
export const defaultMetadata: ConnectorMetadata = {
id: 'mock-short-message-service',

View file

@ -1,24 +1,24 @@
import fs from 'fs/promises';
import path from 'path';
import { SmsConnector } from '@logto/connector-base-classes';
import {
SmsConnector,
ConnectorError,
ConnectorErrorCodes,
SmsSendMessageByFunction,
GetConnectorConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import { assert } from '@silverhand/essentials';
import { defaultMetadata } from './constant';
import { mockSmsConfigGuard, MockSmsConfig } from './types';
export { defaultMetadata } from './constant';
export default class MockSmsConnector extends SmsConnector<MockSmsConfig> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
// eslint-disable-next-line unicorn/prefer-module
this.metadataParser(__dirname);
}
public validateConfig(config: unknown): asserts config is MockSmsConfig {

View file

@ -22,7 +22,7 @@
"prepack": "pnpm build"
},
"dependencies": {
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/schemas": "^1.0.0-beta.4",
"@logto/shared": "^1.0.0-beta.4",
"@silverhand/essentials": "^1.1.0",

View file

@ -1,4 +1,4 @@
import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-types';
import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-schemas';
export const defaultMetadata: ConnectorMetadata = {
id: 'mock-social-connector',

View file

@ -1,24 +1,24 @@
import { randomUUID } from 'crypto';
import { SocialConnector } from '@logto/connector-base-classes';
import {
SocialConnector,
ConnectorError,
ConnectorErrorCodes,
GetAuthorizationUri,
GetUserInfo,
GetConnectorConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import { z } from 'zod';
import { defaultMetadata } from './constant';
import { mockSocialConfigGuard, MockSocialConfig } from './types';
export { defaultMetadata } from './constant';
export default class MockSocialConnector extends SocialConnector<MockSocialConfig> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
// eslint-disable-next-line unicorn/prefer-module
this.metadataParser(__dirname);
}
public validateConfig(config: unknown): asserts config is MockSocialConfig {

View file

@ -1,5 +1,5 @@
{
"name": "@logto/connector-types",
"name": "@logto/connector-schemas",
"version": "1.0.0-beta.4",
"main": "lib/index.js",
"author": "Silverhand Inc. <contact@silverhand.io>",
@ -29,6 +29,7 @@
"@silverhand/essentials": "^1.1.6",
"@silverhand/ts-config": "1.0.0-rc.2",
"@types/jest": "^28.1.6",
"@types/node": "^16.3.1",
"eslint": "^8.21.0",
"jest": "^28.1.3",
"lint-staged": "^13.0.0",

View file

@ -1,63 +1,4 @@
// FIXME: @Darcy
/* eslint-disable @typescript-eslint/consistent-type-definitions */
import type { Language } from '@logto/phrases';
import { Nullable } from '@silverhand/essentials';
import { z } from 'zod';
export enum ConnectorType {
Email = 'Email',
SMS = 'SMS',
Social = 'Social',
}
export enum ConnectorPlatform {
Native = 'Native',
Universal = 'Universal',
Web = 'Web',
}
type i18nPhrases = { [Language.English]: string } & {
[key in Exclude<Language, Language.English>]?: string;
};
export interface ConnectorMetadata {
id: string;
target: string;
type: ConnectorType;
platform: Nullable<ConnectorPlatform>;
name: i18nPhrases;
logo: string;
logoDark: Nullable<string>;
description: i18nPhrases;
readme: string;
configTemplate: string;
}
export enum ConnectorErrorCodes {
General,
InsufficientRequestParameters,
InvalidConfig,
InvalidResponse,
TemplateNotFound,
NotImplemented,
SocialAuthCodeInvalid,
SocialAccessTokenInvalid,
SocialIdTokenInvalid,
AuthorizationFailed,
}
export class ConnectorError extends Error {
public code: ConnectorErrorCodes;
public data: unknown;
constructor(code: ConnectorErrorCodes, data?: unknown) {
const message = typeof data === 'string' ? data : 'Connector error occurred.';
super(message);
this.code = code;
this.data = typeof data === 'string' ? { message: data } : data;
}
}
import { ConnectorMetadata } from './types';
export type EmailMessageTypes = {
SignIn: {
@ -129,16 +70,55 @@ export type GetConnectorConfig = (id: string) => Promise<unknown>;
export type AuthResponseParser<T = Record<string, unknown>> = (response: unknown) => Promise<T>;
export const codeDataGuard = z.object({
code: z.string(),
});
abstract class BaseConnector<T> {
public getConfig: GetConnectorConfig;
public metadata!: ConnectorMetadata;
export type CodeData = z.infer<typeof codeDataGuard>;
constructor(getConnectorConfig: GetConnectorConfig) {
this.getConfig = getConnectorConfig;
}
export const codeWithRedirectDataGuard = z.object({
code: z.string(),
redirectUri: z.string(),
});
public abstract validateConfig(config: unknown): asserts config is T;
}
export type CodeWithRedirectData = z.infer<typeof codeWithRedirectDataGuard>;
/* eslint-enable @typescript-eslint/consistent-type-definitions */
export abstract class SmsConnector<T> extends BaseConnector<T> {
protected abstract readonly sendMessageBy: EmailSendMessageByFunction<T>;
public sendMessage: EmailSendMessageFunction = async (address, type, data) => {
const config = await this.getConfig(this.metadata.id);
this.validateConfig(config);
return this.sendMessageBy(config, address, type, data);
};
public sendTestMessage?: EmailSendTestMessageFunction = async (config, address, type, data) => {
this.validateConfig(config);
return this.sendMessageBy(config, address, type, data);
};
}
export abstract class EmailConnector<T> extends BaseConnector<T> {
protected abstract readonly sendMessageBy: SmsSendMessageByFunction<T>;
public sendMessage: SmsSendMessageFunction = async (address, type, data) => {
const config = await this.getConfig(this.metadata.id);
this.validateConfig(config);
return this.sendMessageBy(config, address, type, data);
};
public sendTestMessage?: SmsSendTestMessageFunction = async (config, address, type, data) => {
this.validateConfig(config);
return this.sendMessageBy(config, address, type, data);
};
}
export abstract class SocialConnector<T> extends BaseConnector<T> {
public abstract getAuthorizationUri: GetAuthorizationUri;
public abstract getUserInfo: GetUserInfo;
protected authResponseParser?: AuthResponseParser;
}

View file

@ -0,0 +1,13 @@
import { ConnectorErrorCodes } from './types';
export class ConnectorError extends Error {
public code: ConnectorErrorCodes;
public data: unknown;
constructor(code: ConnectorErrorCodes, data?: unknown) {
const message = typeof data === 'string' ? data : 'Connector error occurred.';
super(message);
this.code = code;
this.data = typeof data === 'string' ? { message: data } : data;
}
}

View file

@ -0,0 +1,3 @@
export * from './types';
export * from './error';
export * from './class';

View file

@ -0,0 +1,62 @@
// FIXME: @Darcy
/* eslint-disable @typescript-eslint/consistent-type-definitions */
import type { Language } from '@logto/phrases';
import { Nullable } from '@silverhand/essentials';
import { z } from 'zod';
export enum ConnectorType {
Email = 'Email',
SMS = 'SMS',
Social = 'Social',
}
export enum ConnectorPlatform {
Native = 'Native',
Universal = 'Universal',
Web = 'Web',
}
export enum ConnectorErrorCodes {
General,
InsufficientRequestParameters,
InvalidConfig,
InvalidResponse,
TemplateNotFound,
NotImplemented,
SocialAuthCodeInvalid,
SocialAccessTokenInvalid,
SocialIdTokenInvalid,
AuthorizationFailed,
}
type i18nPhrases = { [Language.English]: string } & {
[key in Exclude<Language, Language.English>]?: string;
};
export interface ConnectorMetadata {
id: string;
target: string;
type: ConnectorType;
platform: Nullable<ConnectorPlatform>;
name: i18nPhrases;
logo: string;
logoDark: Nullable<string>;
description: i18nPhrases;
readme: string;
configTemplate: string;
}
export const codeDataGuard = z.object({
code: z.string(),
});
export type CodeData = z.infer<typeof codeDataGuard>;
export const codeWithRedirectDataGuard = z.object({
code: z.string(),
redirectUri: z.string(),
});
export type CodeWithRedirectData = z.infer<typeof codeWithRedirectDataGuard>;
/* eslint-enable @typescript-eslint/consistent-type-definitions */

View file

@ -24,7 +24,7 @@
"prepack": "pnpm build"
},
"dependencies": {
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/shared": "^1.0.0-beta.4",
"@silverhand/essentials": "^1.1.6",
"@silverhand/jest-config": "1.0.0-rc.3",

View file

@ -1,4 +1,4 @@
import { ConnectorType, ConnectorMetadata } from '@logto/connector-types';
import { ConnectorType, ConnectorMetadata } from '@logto/connector-schemas';
export const endpoint = 'https://api.sendgrid.com/v3/mail/send';

View file

@ -1,4 +1,4 @@
import { GetConnectorConfig, ValidateConfig } from '@logto/connector-types';
import { GetConnectorConfig, ValidateConfig } from '@logto/connector-schemas';
import SendGridMailConnector from '.';
import { mockedConfig } from './mock';

View file

@ -1,10 +1,10 @@
import { EmailConnector } from '@logto/connector-base-classes';
import {
EmailConnector,
ConnectorError,
ConnectorErrorCodes,
EmailSendMessageByFunction,
GetConnectorConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import { assert } from '@silverhand/essentials';
import got, { HTTPError } from 'got';
@ -18,12 +18,12 @@ import {
PublicParameters,
} from './types';
export { defaultMetadata } from './constant';
export default class SendGridMailConnector extends EmailConnector<SendGridMailConfig> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
// eslint-disable-next-line unicorn/prefer-module
this.metadataParser(__dirname);
}
public validateConfig(config: unknown): asserts config is SendGridMailConfig {

View file

@ -24,7 +24,7 @@
"prepack": "pnpm build"
},
"dependencies": {
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/shared": "^1.0.0-beta.4",
"@silverhand/essentials": "^1.1.6",
"@silverhand/jest-config": "1.0.0-rc.3",

View file

@ -1,4 +1,4 @@
import { ConnectorType, ConnectorMetadata } from '@logto/connector-types';
import { ConnectorType, ConnectorMetadata } from '@logto/connector-schemas';
export const defaultMetadata: ConnectorMetadata = {
id: 'simple-mail-transfer-protocol',

View file

@ -1,4 +1,4 @@
import { GetConnectorConfig, ValidateConfig } from '@logto/connector-types';
import { GetConnectorConfig, ValidateConfig } from '@logto/connector-schemas';
import SmtpConnector from '.';
import { SmtpConfig } from './types';

View file

@ -1,10 +1,10 @@
import { EmailConnector } from '@logto/connector-base-classes';
import {
EmailConnector,
ConnectorError,
ConnectorErrorCodes,
EmailSendMessageByFunction,
GetConnectorConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import { assert } from '@silverhand/essentials';
import nodemailer from 'nodemailer';
import SMTPTransport from 'nodemailer/lib/smtp-transport';
@ -12,12 +12,12 @@ import SMTPTransport from 'nodemailer/lib/smtp-transport';
import { defaultMetadata } from './constant';
import { ContextType, smtpConfigGuard, SmtpConfig } from './types';
export { defaultMetadata } from './constant';
export default class SmtpConnector extends EmailConnector<SmtpConfig> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
// eslint-disable-next-line unicorn/prefer-module
this.metadataParser(__dirname);
}
public validateConfig(config: unknown): asserts config is SmtpConfig {

View file

@ -24,7 +24,7 @@
"prepack": "pnpm build"
},
"dependencies": {
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/shared": "^1.0.0-beta.4",
"@silverhand/essentials": "^1.1.6",
"@silverhand/jest-config": "1.0.0-rc.3",

View file

@ -1,4 +1,4 @@
import { ConnectorType, ConnectorMetadata } from '@logto/connector-types';
import { ConnectorType, ConnectorMetadata } from '@logto/connector-schemas';
export const endpoint = 'https://api.twilio.com/2010-04-01/Accounts/{{accountSID}}/Messages.json';

View file

@ -1,4 +1,4 @@
import { GetConnectorConfig, ValidateConfig } from '@logto/connector-types';
import { GetConnectorConfig, ValidateConfig } from '@logto/connector-schemas';
import TwilioSmsConnector from '.';
import { mockedConfig } from './mock';

View file

@ -1,22 +1,22 @@
import { SmsConnector } from '@logto/connector-base-classes';
import {
SmsConnector,
ConnectorError,
ConnectorErrorCodes,
SmsSendMessageByFunction,
GetConnectorConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import { assert } from '@silverhand/essentials';
import got, { HTTPError } from 'got';
import { defaultMetadata, endpoint } from './constant';
import { twilioSmsConfigGuard, TwilioSmsConfig, PublicParameters } from './types';
export { defaultMetadata } from './constant';
export default class TwilioSmsConnector extends SmsConnector<TwilioSmsConfig> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
// eslint-disable-next-line unicorn/prefer-module
this.metadataParser(__dirname);
}
public validateConfig(config: unknown): asserts config is TwilioSmsConfig {

View file

@ -1,5 +0,0 @@
{
"extends": "./tsconfig",
"include": ["src"],
"exclude": ["src/**/*.test.ts"]
}

View file

@ -1,11 +0,0 @@
{
"extends": "@silverhand/ts-config/tsconfig.base",
"compilerOptions": {
"outDir": "lib",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src"]
}

View file

@ -24,7 +24,7 @@
"prepack": "pnpm build"
},
"dependencies": {
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/schemas": "^1.0.0-beta.4",
"@logto/shared": "^1.0.0-beta.4",
"@silverhand/essentials": "^1.1.0",

View file

@ -1,4 +1,4 @@
import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-types';
import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-schemas';
export const authorizationEndpoint = 'wechat://'; // This is used to arouse the native WeChat App
export const accessTokenEndpoint = 'https://api.weixin.qq.com/sns/oauth2/access_token';

View file

@ -3,7 +3,7 @@ import {
ConnectorErrorCodes,
GetConnectorConfig,
ValidateConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import nock from 'nock';
import WechatNativeConnector from '.';

View file

@ -3,8 +3,8 @@
* https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
*/
import { SocialConnector } from '@logto/connector-base-classes';
import {
SocialConnector,
GetAuthorizationUri,
GetUserInfo,
ConnectorError,
@ -13,7 +13,7 @@ import {
AuthResponseParser,
codeDataGuard,
CodeData,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import { assert } from '@silverhand/essentials';
import got, { HTTPError } from 'got';
@ -35,12 +35,12 @@ import {
WechatNativeConfig,
} from './types';
export { defaultMetadata } from './constant';
export default class WechatNativeConnector extends SocialConnector<WechatNativeConfig> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
// eslint-disable-next-line unicorn/prefer-module
this.metadataParser(__dirname);
}
public validateConfig(config: unknown): asserts config is WechatNativeConfig {

View file

@ -24,7 +24,7 @@
"prepack": "pnpm build"
},
"dependencies": {
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/schemas": "^1.0.0-beta.4",
"@logto/shared": "^1.0.0-beta.4",
"@silverhand/essentials": "^1.1.0",

View file

@ -1,4 +1,4 @@
import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-types';
import { ConnectorMetadata, ConnectorType, ConnectorPlatform } from '@logto/connector-schemas';
export const authorizationEndpoint = 'https://open.weixin.qq.com/connect/qrconnect';
export const accessTokenEndpoint = 'https://api.weixin.qq.com/sns/oauth2/access_token';

View file

@ -3,7 +3,7 @@ import {
ConnectorErrorCodes,
GetConnectorConfig,
ValidateConfig,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import nock from 'nock';
import WechatConnector from '.';

View file

@ -3,8 +3,8 @@
* https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
*/
import { SocialConnector } from '@logto/connector-base-classes';
import {
SocialConnector,
AuthResponseParser,
GetAuthorizationUri,
GetUserInfo,
@ -13,7 +13,7 @@ import {
GetConnectorConfig,
codeDataGuard,
CodeData,
} from '@logto/connector-types';
} from '@logto/connector-schemas';
import { assert } from '@silverhand/essentials';
import got, { HTTPError } from 'got';
@ -36,12 +36,12 @@ import {
WechatConfig,
} from './types';
export { defaultMetadata } from './constant';
export default class WechatConnector extends SocialConnector<WechatConfig> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
// eslint-disable-next-line unicorn/prefer-module
this.metadataParser(__dirname);
}
public validateConfig(config: unknown): asserts config is WechatConfig {

View file

@ -28,10 +28,10 @@
"@logto/connector-facebook": "^1.0.0-beta.4",
"@logto/connector-github": "^1.0.0-beta.4",
"@logto/connector-google": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/connector-sendgrid-email": "^1.0.0-beta.4",
"@logto/connector-smtp": "^1.0.0-beta.4",
"@logto/connector-twilio-sms": "^1.0.0-beta.4",
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-wechat-native": "^1.0.0-beta.4",
"@logto/connector-wechat-web": "^1.0.0-beta.4",
"@logto/phrases": "^1.0.0-beta.4",

View file

@ -1,4 +1,4 @@
import { ConnectorPlatform } from '@logto/connector-types';
import { ConnectorPlatform } from '@logto/connector-schemas';
import { Connector, ConnectorMetadata, ConnectorType } from '@logto/schemas';
export const mockMetadata: ConnectorMetadata = {
@ -148,35 +148,35 @@ export const mockConnectorList: Connector[] = [
export const mockConnectorInstanceList: Array<{
connector: Connector;
instance: { metadata: ConnectorMetadata };
metadata: ConnectorMetadata;
}> = [
{
connector: mockConnector0,
instance: { metadata: { ...mockMetadata0, type: ConnectorType.Social } },
metadata: { ...mockMetadata0, type: ConnectorType.Social },
},
{
connector: mockConnector1,
instance: { metadata: mockMetadata1 },
metadata: mockMetadata1,
},
{
connector: mockConnector2,
instance: { metadata: mockMetadata2 },
metadata: mockMetadata2,
},
{
connector: mockConnector3,
instance: { metadata: mockMetadata3 },
metadata: mockMetadata3,
},
{
connector: mockConnector4,
instance: { metadata: { ...mockMetadata4, type: ConnectorType.Email, platform: null } },
metadata: { ...mockMetadata4, type: ConnectorType.Email, platform: null },
},
{
connector: mockConnector5,
instance: { metadata: { ...mockMetadata5, type: ConnectorType.SMS, platform: null } },
metadata: { ...mockMetadata5, type: ConnectorType.SMS, platform: null },
},
{
connector: mockConnector6,
instance: { metadata: { ...mockMetadata6, type: ConnectorType.Email, platform: null } },
metadata: { ...mockMetadata6, type: ConnectorType.Email, platform: null },
},
];
@ -185,14 +185,12 @@ export const mockAliyunDmConnectorInstance = {
...mockConnector,
id: 'aliyun-dm',
},
instance: {
metadata: {
...mockMetadata,
id: 'aliyun-dm',
target: 'aliyun-dm',
type: ConnectorType.Email,
platform: null,
},
metadata: {
...mockMetadata,
id: 'aliyun-dm',
target: 'aliyun-dm',
type: ConnectorType.Email,
platform: null,
},
};
@ -201,14 +199,12 @@ export const mockAliyunSmsConnectorInstance = {
...mockConnector,
id: 'aliyun-sms',
},
instance: {
metadata: {
...mockMetadata,
id: 'aliyun-sms',
target: 'aliyun-sms',
type: ConnectorType.SMS,
platform: null,
},
metadata: {
...mockMetadata,
id: 'aliyun-sms',
target: 'aliyun-sms',
type: ConnectorType.SMS,
platform: null,
},
};
@ -217,14 +213,12 @@ export const mockFacebookConnectorInstance = {
...mockConnector,
id: 'facebook',
},
instance: {
metadata: {
...mockMetadata,
id: 'facebook',
target: 'facebook',
type: ConnectorType.Social,
platform: ConnectorPlatform.Web,
},
metadata: {
...mockMetadata,
id: 'facebook',
target: 'facebook',
type: ConnectorType.Social,
platform: ConnectorPlatform.Web,
},
};
@ -233,14 +227,12 @@ export const mockGithubConnectorInstance = {
...mockConnector,
id: 'github',
},
instance: {
metadata: {
...mockMetadata,
id: 'github',
target: 'github',
type: ConnectorType.Social,
platform: ConnectorPlatform.Web,
},
metadata: {
...mockMetadata,
id: 'github',
target: 'github',
type: ConnectorType.Social,
platform: ConnectorPlatform.Web,
},
};
@ -249,14 +241,12 @@ export const mockWechatConnectorInstance = {
...mockConnector,
id: 'wechat-web',
},
instance: {
metadata: {
...mockMetadata,
id: 'wechat-web',
target: 'wechat',
type: ConnectorType.Social,
platform: ConnectorPlatform.Web,
},
metadata: {
...mockMetadata,
id: 'wechat-web',
target: 'wechat',
type: ConnectorType.Social,
platform: ConnectorPlatform.Web,
},
};
@ -265,14 +255,12 @@ export const mockWechatNativeConnectorInstance = {
...mockConnector,
id: 'wechat-native',
},
instance: {
metadata: {
...mockMetadata,
id: 'wechat-native',
target: 'wechat',
type: ConnectorType.Social,
platform: ConnectorPlatform.Native,
},
metadata: {
...mockMetadata,
id: 'wechat-native',
target: 'wechat',
type: ConnectorType.Social,
platform: ConnectorPlatform.Native,
},
};
@ -282,14 +270,12 @@ export const mockGoogleConnectorInstance = {
id: 'google',
enabled: false,
},
instance: {
metadata: {
...mockMetadata,
id: 'google',
target: 'google',
type: ConnectorType.Social,
platform: ConnectorPlatform.Web,
},
metadata: {
...mockMetadata,
id: 'google',
target: 'google',
type: ConnectorType.Social,
platform: ConnectorPlatform.Web,
},
};

View file

@ -1,4 +1,4 @@
import { ConnectorPlatform } from '@logto/connector-types';
import { ConnectorPlatform } from '@logto/connector-schemas';
import { Connector } from '@logto/schemas';
import {

View file

@ -1,16 +1,37 @@
import { existsSync, readFileSync } from 'fs';
import path from 'path';
import { ConnectorType, GetConnectorConfig } from '@logto/connector-schemas';
import { Connector } from '@logto/schemas';
import resolvePackagePath from 'resolve-package-path';
import { ConnectorInstance, SocialConnectorInstance } from '@/connectors/types';
import RequestError from '@/errors/RequestError';
import { findAllConnectors, insertConnector } from '@/queries/connector';
import { defaultConnectorPackages } from './consts';
import { ConnectorType, ConnectorInstance, Instance, SocialConnectorInstance } from './types';
import { connectorPackages } from './consts';
import { getConnectorConfig } from './utilities';
// eslint-disable-next-line @silverhand/fp/no-let
let cachedConnectors: Instance[] | undefined;
let cachedConnectorInstances: ConnectorInstance[] | undefined;
const loadConnectors = async () => {
if (cachedConnectors) {
return cachedConnectors;
export const getConnectorInstances = async (): Promise<ConnectorInstance[]> => {
const connectors = await findAllConnectors();
const connectorMap = new Map(connectors.map((connector) => [connector.id, connector]));
if (cachedConnectorInstances) {
return cachedConnectorInstances.map((connectorInstance) => {
const { id } = connectorInstance.metadata;
const connector = connectorMap.get(id);
if (!connector) {
throw new RequestError({ code: 'entity.not_found', id, status: 404 });
}
// eslint-disable-next-line @silverhand/fp/no-mutation
connectorInstance.connector = connector;
return connectorInstance;
});
}
const {
@ -20,36 +41,95 @@ const loadConnectors = async () => {
const connectorPackages = [...defaultConnectorPackages, ...additionalConnectorPackages];
// eslint-disable-next-line @silverhand/fp/no-mutation
cachedConnectors = await Promise.all(
cachedConnectorInstances = await Promise.all(
connectorPackages.map(async (packageName) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { default: Builder } = await import(packageName);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
const instance: Instance = new Builder(getConnectorConfig);
const { default: Builder, defaultMetadata: metadata } = await import(packageName);
class InstanceBuilder extends Builder {
private _connector: Connector;
public get connector() {
return this._connector;
}
public set connector(input: Connector) {
this._connector = input;
}
constructor(getConnectorConfig: GetConnectorConfig, connectorFromDatabase: Connector) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
super(getConnectorConfig);
this._connector = connectorFromDatabase;
}
}
const connector = connectorMap.get(metadata.id);
if (!connector) {
throw new RequestError({ code: 'entity.not_found', id: metadata.id, status: 404 });
}
// TODO: can address type of Builder when dynamic import, temporarily use `as` to unblock
// eslint-disable-next-line no-restricted-syntax
const instance = new InstanceBuilder(
getConnectorConfig,
connector
) as unknown as ConnectorInstance;
// eslint-disable-next-line unicorn/prefer-module
const packagePath = resolvePackagePath(packageName, __dirname);
// For relative path logo url, try to read local asset.
if (
packagePath &&
!instance.metadata.logo.startsWith('http') &&
existsSync(path.join(packagePath, '..', instance.metadata.logo))
) {
const data = readFileSync(path.join(packagePath, '..', instance.metadata.logo));
// eslint-disable-next-line @silverhand/fp/no-mutation
instance.metadata.logo = `data:image/svg+xml;base64,${data.toString('base64')}`;
}
if (
packagePath &&
instance.metadata.logoDark &&
!instance.metadata.logoDark.startsWith('http') &&
existsSync(path.join(packagePath, '..', instance.metadata.logoDark))
) {
const data = readFileSync(path.join(packagePath, '..', instance.metadata.logoDark));
// eslint-disable-next-line @silverhand/fp/no-mutation
instance.metadata.logoDark = `data:image/svg+xml;base64,${data.toString('base64')}`;
}
if (
packagePath &&
instance.metadata.readme &&
existsSync(path.join(packagePath, '..', instance.metadata.readme))
) {
// eslint-disable-next-line @silverhand/fp/no-mutation
instance.metadata.readme = readFileSync(
path.join(packagePath, '..', instance.metadata.readme),
'utf8'
);
}
if (
packagePath &&
instance.metadata.configTemplate &&
existsSync(path.join(packagePath, '..', instance.metadata.configTemplate))
) {
// eslint-disable-next-line @silverhand/fp/no-mutation
instance.metadata.configTemplate = readFileSync(
path.join(packagePath, '..', instance.metadata.configTemplate),
'utf8'
);
}
return instance;
})
);
return cachedConnectors;
};
export const getConnectorInstances = async (): Promise<ConnectorInstance[]> => {
const connectors = await findAllConnectors();
const connectorMap = new Map(connectors.map((connector) => [connector.id, connector]));
const allInstances = await loadConnectors();
return allInstances.map((instance) => {
const { id } = instance.metadata;
const connector = connectorMap.get(id);
if (!connector) {
throw new RequestError({ code: 'entity.not_found', id, status: 404 });
}
return { instance, connector };
});
return cachedConnectorInstances;
};
export const getConnectorInstanceById = async (id: string): Promise<ConnectorInstance> => {
@ -70,7 +150,7 @@ export const getConnectorInstanceById = async (id: string): Promise<ConnectorIns
const isSocialConnectorInstance = (
connector: ConnectorInstance
): connector is SocialConnectorInstance => {
return connector.instance.metadata.type === ConnectorType.Social;
return connector.metadata.type === ConnectorType.Social;
};
export const getSocialConnectorInstanceById = async (
@ -92,8 +172,8 @@ export const getSocialConnectorInstanceById = async (
export const initConnectors = async () => {
const connectors = await findAllConnectors();
const existingConnectors = new Map(connectors.map((connector) => [connector.id, connector]));
const allConnectors = await loadConnectors();
const newConnectors = allConnectors.filter(({ metadata: { id } }) => {
const allConnectorInstances = await getConnectorInstances();
const newConnectors = allConnectorInstances.filter(({ metadata: { id } }) => {
const connector = existingConnectors.get(id);
if (!connector) {

View file

@ -1,4 +1,4 @@
import { SmsConnector, EmailConnector, SocialConnector } from '@logto/connector-base-classes';
import { SmsConnector, EmailConnector, SocialConnector } from '@logto/connector-schemas';
import { PasscodeType, Connector } from '@logto/schemas';
import { z } from 'zod';
@ -17,28 +17,17 @@ export const socialUserInfoGuard = z.object({
export type SocialUserInfo = z.infer<typeof socialUserInfoGuard>;
export type Instance =
export type SmsConnectorInstance = InstanceType<typeof SmsConnector> & { connector: Connector };
export type EmailConnectorInstance = InstanceType<typeof EmailConnector> & { connector: Connector };
export type SocialConnectorInstance = InstanceType<typeof SocialConnector> & {
connector: Connector;
};
type ArbitraryConnector =
| InstanceType<typeof SmsConnector>
| InstanceType<typeof EmailConnector>
| InstanceType<typeof SocialConnector>;
export type SmsConnectorInstance = {
instance: InstanceType<typeof SmsConnector>;
connector: Connector;
};
export type EmailConnectorInstance = {
instance: InstanceType<typeof EmailConnector>;
connector: Connector;
};
export type SocialConnectorInstance = {
instance: InstanceType<typeof SocialConnector>;
connector: Connector;
};
export type ConnectorInstance =
| SmsConnectorInstance
| EmailConnectorInstance
| SocialConnectorInstance
| { instance: Instance; connector: Connector };
export type ConnectorInstance = ArbitraryConnector & { connector: Connector };

View file

@ -1,4 +1,4 @@
import { ConnectorType, ValidateConfig, GetConnectorConfig } from '@logto/connector-types';
import { ConnectorType, ValidateConfig, GetConnectorConfig } from '@logto/connector-schemas';
import { Passcode, PasscodeType, Connector } from '@logto/schemas';
import { mockConnector, mockMetadata } from '@/__mocks__';
@ -27,13 +27,11 @@ jest.mock('@/connectors');
type ConnectorInstance = {
connector: Connector;
instance: {
metadata: ConnectorMetadata;
validateConfig?: ValidateConfig<unknown>;
getConfig?: GetConnectorConfig;
sendMessage?: unknown;
sendTestMessage?: unknown;
};
metadata: ConnectorMetadata;
validateConfig?: ValidateConfig<unknown>;
getConfig?: GetConnectorConfig;
sendMessage?: unknown;
sendTestMessage?: unknown;
};
const mockedFindUnconsumedPasscodesByJtiAndType =
@ -149,16 +147,14 @@ describe('sendPasscode', () => {
...mockConnector,
id: 'id1',
},
instance: {
metadata: {
...mockMetadata,
type: ConnectorType.Email,
platform: null,
},
sendMessage,
validateConfig,
getConfig,
metadata: {
...mockMetadata,
type: ConnectorType.Email,
platform: null,
},
sendMessage,
validateConfig,
getConfig,
},
];
getConnectorInstancesPlaceHolder.mockResolvedValueOnce(mockConnectorInstances);
@ -191,31 +187,27 @@ describe('sendPasscode', () => {
...mockConnector,
id: 'id0',
},
instance: {
metadata: {
...mockMetadata,
type: ConnectorType.SMS,
platform: null,
},
sendMessage,
validateConfig,
getConfig,
metadata: {
...mockMetadata,
type: ConnectorType.SMS,
platform: null,
},
sendMessage,
validateConfig,
getConfig,
},
{
connector: {
...mockConnector,
id: 'id1',
},
instance: {
metadata: {
...mockMetadata,
type: ConnectorType.Email,
platform: null,
},
validateConfig,
getConfig,
metadata: {
...mockMetadata,
type: ConnectorType.Email,
platform: null,
},
validateConfig,
getConfig,
},
];
getConnectorInstancesPlaceHolder.mockResolvedValueOnce(mockConnectorInstances);

View file

@ -2,7 +2,7 @@ import { Passcode, PasscodeType } from '@logto/schemas';
import { customAlphabet, nanoid } from 'nanoid';
import { getConnectorInstances } from '@/connectors';
import { ConnectorType, EmailConnectorInstance, SmsConnectorInstance } from '@/connectors/types';
import { EmailConnectorInstance, SmsConnectorInstance, ConnectorType } from '@/connectors/types';
import RequestError from '@/errors/RequestError';
import {
consumePasscode,
@ -49,11 +49,11 @@ export const sendPasscode = async (passcode: Passcode) => {
const emailConnectorInstance = connectorInstances.find(
(connector): connector is EmailConnectorInstance =>
connector.connector.enabled && connector.instance.metadata.type === ConnectorType.Email
connector.connector.enabled && connector.metadata.type === ConnectorType.Email
);
const smsConnectorInstance = connectorInstances.find(
(connector): connector is SmsConnectorInstance =>
connector.connector.enabled && connector.instance.metadata.type === ConnectorType.SMS
connector.connector.enabled && connector.metadata.type === ConnectorType.SMS
);
const connectorInstance = passcode.email ? emailConnectorInstance : smsConnectorInstance;
@ -66,10 +66,7 @@ export const sendPasscode = async (passcode: Passcode) => {
})
);
const {
connector,
instance: { metadata, sendMessage },
} = connectorInstance;
const { connector, metadata, sendMessage } = connectorInstance;
const response = await sendMessage(emailOrPhone, passcode.type, {
code: passcode.code,

View file

@ -41,7 +41,7 @@ export const validateSignInMethods = (
if (isEnabled(signInMethods.email)) {
assertThat(
enabledConnectorInstances.some((item) => item.instance.metadata.type === ConnectorType.Email),
enabledConnectorInstances.some((item) => item.metadata.type === ConnectorType.Email),
new RequestError({
code: 'sign_in_experiences.enabled_connector_not_found',
type: ConnectorType.Email,
@ -51,7 +51,7 @@ export const validateSignInMethods = (
if (isEnabled(signInMethods.sms)) {
assertThat(
enabledConnectorInstances.some((item) => item.instance.metadata.type === ConnectorType.SMS),
enabledConnectorInstances.some((item) => item.metadata.type === ConnectorType.SMS),
new RequestError({
code: 'sign_in_experiences.enabled_connector_not_found',
type: ConnectorType.SMS,
@ -61,9 +61,7 @@ export const validateSignInMethods = (
if (isEnabled(signInMethods.social)) {
assertThat(
enabledConnectorInstances.some(
(item) => item.instance.metadata.type === ConnectorType.Social
),
enabledConnectorInstances.some((item) => item.metadata.type === ConnectorType.Social),
new RequestError({
code: 'sign_in_experiences.enabled_connector_not_found',
type: ConnectorType.Social,

View file

@ -41,7 +41,7 @@ export const getUserInfoByAuthCode = async (
): Promise<SocialUserInfo> => {
const connector = await getConnector(connectorId);
return connector.instance.getUserInfo(data);
return connector.getUserInfo(data);
};
export const getUserInfoFromInteractionResult = async (

View file

@ -1,4 +1,4 @@
import { ConnectorError, ConnectorErrorCodes } from '@logto/connector-types';
import { ConnectorError, ConnectorErrorCodes } from '@logto/connector-schemas';
import RequestError from '@/errors/RequestError';
import { createContextWithRouteParameters } from '@/utils/test-utils';

View file

@ -1,4 +1,4 @@
import { ConnectorError, ConnectorErrorCodes } from '@logto/connector-types';
import { ConnectorError, ConnectorErrorCodes } from '@logto/connector-schemas';
import { conditional } from '@silverhand/essentials';
import { Middleware } from 'koa';
import { z } from 'zod';

View file

@ -1,4 +1,4 @@
import { GetConnectorConfig, ValidateConfig } from '@logto/connector-types';
import { GetConnectorConfig, ValidateConfig } from '@logto/connector-schemas';
import { Connector, ConnectorType } from '@logto/schemas';
import { mockConnectorInstanceList, mockMetadata, mockConnector } from '@/__mocks__';
@ -11,13 +11,11 @@ import connectorRoutes from './connector';
type ConnectorInstance = {
connector: Connector;
instance: {
metadata: ConnectorMetadata;
validateConfig?: ValidateConfig<unknown>;
getConfig?: GetConnectorConfig;
sendMessage?: unknown;
sendTestMessage?: unknown;
};
metadata: ConnectorMetadata;
validateConfig?: ValidateConfig<unknown>;
getConfig?: GetConnectorConfig;
sendMessage?: unknown;
sendTestMessage?: unknown;
};
const getConnectorInstancesPlaceHolder = jest.fn() as jest.MockedFunction<
@ -59,7 +57,7 @@ describe('connector route', () => {
it('throws if more than one SMS connector is enabled', async () => {
getConnectorInstancesPlaceHolder.mockResolvedValueOnce(
mockConnectorInstanceList.filter(
(connectorInstance) => connectorInstance.instance.metadata.type !== ConnectorType.Email
(connectorInstance) => connectorInstance.metadata.type !== ConnectorType.Email
)
);
const response = await connectorRequest.get('/connectors').send({});
@ -69,7 +67,7 @@ describe('connector route', () => {
it('shows all connectors', async () => {
getConnectorInstancesPlaceHolder.mockResolvedValueOnce(
mockConnectorInstanceList.filter(
(connectorInstance) => connectorInstance.instance.metadata.type === ConnectorType.Social
(connectorInstance) => connectorInstance.metadata.type === ConnectorType.Social
)
);
const response = await connectorRequest.get('/connectors').send({});
@ -114,13 +112,11 @@ describe('connector route', () => {
};
const mockedSmsConnectorInstance: ConnectorInstance = {
connector: mockConnector,
instance: {
metadata: mockedMetadata,
validateConfig: jest.fn(),
getConfig: jest.fn(),
sendMessage: jest.fn(),
sendTestMessage: mockSendTestMessage,
},
metadata: mockedMetadata,
validateConfig: jest.fn(),
getConfig: jest.fn(),
sendMessage: jest.fn(),
sendTestMessage: mockSendTestMessage,
};
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([mockedSmsConnectorInstance]);
const response = await connectorRequest
@ -137,13 +133,11 @@ describe('connector route', () => {
const sendTestMessage = jest.fn();
const mockedEmailConnector: ConnectorInstance = {
connector: mockConnector,
instance: {
metadata: mockMetadata,
validateConfig: jest.fn(),
getConfig: jest.fn(),
sendMessage: jest.fn(),
sendTestMessage,
},
metadata: mockMetadata,
validateConfig: jest.fn(),
getConfig: jest.fn(),
sendMessage: jest.fn(),
sendTestMessage,
};
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([mockedEmailConnector]);
const response = await connectorRequest

View file

@ -15,10 +15,7 @@ import assertThat from '@/utils/assert-that';
import { AuthedRouter } from './types';
const transpileConnectorInstance = ({
connector,
instance: { metadata },
}: ConnectorInstance): ConnectorDto => ({
const transpileConnectorInstance = ({ connector, metadata }: ConnectorInstance): ConnectorDto => ({
...connector,
...metadata,
});
@ -38,26 +35,20 @@ export default function connectorRoutes<T extends AuthedRouter>(router: T) {
assertThat(
connectorInstances.filter(
(connector) =>
connector.connector.enabled && connector.instance.metadata.type === ConnectorType.Email
connector.connector.enabled && connector.metadata.type === ConnectorType.Email
).length <= 1,
'connector.more_than_one_email'
);
assertThat(
connectorInstances.filter(
(connector) =>
connector.connector.enabled && connector.instance.metadata.type === ConnectorType.SMS
connector.connector.enabled && connector.metadata.type === ConnectorType.SMS
).length <= 1,
'connector.more_than_one_sms'
);
const filteredInstances = filterTarget
? connectorInstances.filter(
({
instance: {
metadata: { target },
},
}) => target === filterTarget
)
? connectorInstances.filter(({ metadata: { target } }) => target === filterTarget)
: connectorInstances;
ctx.body = filteredInstances.map((connectorInstance) =>
@ -97,7 +88,8 @@ export default function connectorRoutes<T extends AuthedRouter>(router: T) {
const connectorInstance = await getConnectorInstanceById(id);
const {
connector: { config },
instance: { validateConfig, metadata },
validateConfig,
metadata,
} = connectorInstance;
/**
@ -121,7 +113,7 @@ export default function connectorRoutes<T extends AuthedRouter>(router: T) {
connectors
.filter(
(connector) =>
connector.instance.metadata.type === metadata.type && connector.connector.enabled
connector.metadata.type === metadata.type && connector.connector.enabled
)
.map(async ({ connector: { id } }) =>
updateConnector({ set: { enabled: false }, where: { id }, jsonbMode: 'merge' })
@ -152,9 +144,7 @@ export default function connectorRoutes<T extends AuthedRouter>(router: T) {
body,
} = ctx.guard;
const {
instance: { metadata, validateConfig },
} = await getConnectorInstanceById(id);
const { metadata, validateConfig } = await getConnectorInstanceById(id);
/**
* Assertion functions always need explicit annotations.
@ -197,13 +187,11 @@ export default function connectorRoutes<T extends AuthedRouter>(router: T) {
const connector: SmsConnectorInstance | EmailConnectorInstance | undefined = phone
? connectorInstances.find(
(connector): connector is SmsConnectorInstance =>
connector.instance.metadata.id === id &&
connector.instance.metadata.type === ConnectorType.SMS
connector.metadata.id === id && connector.metadata.type === ConnectorType.SMS
)
: connectorInstances.find(
(connector): connector is EmailConnectorInstance =>
connector.instance.metadata.id === id &&
connector.instance.metadata.type === ConnectorType.Email
connector.metadata.id === id && connector.metadata.type === ConnectorType.Email
);
assertThat(
@ -214,9 +202,7 @@ export default function connectorRoutes<T extends AuthedRouter>(router: T) {
})
);
const {
instance: { sendTestMessage },
} = connector;
const { sendTestMessage } = connector;
assertThat(
sendTestMessage,
new RequestError({

View file

@ -1,4 +1,4 @@
import { ConnectorError, ConnectorErrorCodes, ValidateConfig } from '@logto/connector-types';
import { ConnectorError, ConnectorErrorCodes, ValidateConfig } from '@logto/connector-schemas';
import { Connector, ConnectorType } from '@logto/schemas';
import { mockConnectorInstanceList, mockMetadata, mockConnector } from '@/__mocks__';
@ -12,11 +12,9 @@ import connectorRoutes from './connector';
type ConnectorInstance = {
connector: Connector;
instance: {
metadata: ConnectorMetadata;
validateConfig?: ValidateConfig<unknown>;
sendMessage?: unknown;
};
metadata: ConnectorMetadata;
validateConfig?: ValidateConfig<unknown>;
sendMessage?: unknown;
};
const getConnectorInstancesPlaceHolder = jest.fn() as jest.MockedFunction<
@ -36,15 +34,13 @@ const getConnectorInstanceByIdPlaceHolder = jest.fn(async (connectorId: string)
})
);
const { instance, connector } = connectorInstance;
const { metadata, connector } = connectorInstance;
return {
connector,
instance: {
...instance,
validateConfig: validateConfigPlaceHolder,
sendMessage: sendMessagePlaceHolder,
},
metadata,
validateConfig: validateConfigPlaceHolder,
sendMessage: sendMessagePlaceHolder,
};
});
const validateConfigPlaceHolder = jest.fn() as jest.MockedFunction<ValidateConfig<unknown>>;
@ -87,7 +83,7 @@ describe('connector PATCH routes', () => {
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([
{
connector: mockConnector,
instance: { metadata: { ...mockMetadata, type: ConnectorType.Social } },
metadata: { ...mockMetadata, type: ConnectorType.Social },
},
]);
const response = await connectorRequest
@ -113,7 +109,7 @@ describe('connector PATCH routes', () => {
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([
{
connector: mockConnector,
instance: { metadata: mockMetadata },
metadata: mockMetadata,
},
]);
const response = await connectorRequest
@ -126,7 +122,7 @@ describe('connector PATCH routes', () => {
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([
{
connector: mockConnector,
instance: { metadata: mockMetadata },
metadata: mockMetadata,
},
]);
const response = await connectorRequest
@ -158,7 +154,9 @@ describe('connector PATCH routes', () => {
};
getConnectorInstanceByIdPlaceHolder.mockResolvedValueOnce({
connector: mockedConnector,
instance: { metadata: mockedMetadata, validateConfig: jest.fn(), sendMessage: jest.fn() },
metadata: mockedMetadata,
validateConfig: jest.fn(),
sendMessage: jest.fn(),
});
const response = await connectorRequest
.patch('/connectors/id1/enabled')
@ -200,11 +198,9 @@ describe('connector PATCH routes', () => {
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([
{
connector: mockConnector,
instance: {
metadata: {
...mockMetadata,
type: ConnectorType.SMS,
},
metadata: {
...mockMetadata,
type: ConnectorType.SMS,
},
},
]);
@ -218,7 +214,7 @@ describe('connector PATCH routes', () => {
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([
{
connector: mockConnector,
instance: { metadata: mockMetadata },
metadata: mockMetadata,
},
]);
const response = await connectorRequest
@ -262,7 +258,7 @@ describe('connector PATCH routes', () => {
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([
{
connector: mockConnector,
instance: { metadata: mockMetadata },
metadata: mockMetadata,
},
]);
const response = await connectorRequest
@ -275,7 +271,7 @@ describe('connector PATCH routes', () => {
getConnectorInstancesPlaceHolder.mockResolvedValueOnce([
{
connector: mockConnector,
instance: { metadata: mockMetadata },
metadata: mockMetadata,
},
]);
const response = await connectorRequest

View file

@ -61,13 +61,13 @@ const getConnectorInstanceByIdHelper = jest.fn(async (connectorId: string) => {
type: connectorId.startsWith('social') ? ConnectorType.Social : ConnectorType.SMS,
};
return { connector, instance: { metadata, getAuthorizationUri: jest.fn(async () => '') } };
return { connector, metadata, getAuthorizationUri: jest.fn(async () => '') };
});
jest.mock('@/connectors', () => ({
getSocialConnectorInstanceById: async (connectorId: string) => {
const connectorInstance = await getConnectorInstanceByIdHelper(connectorId);
if (connectorInstance.instance.metadata.type !== ConnectorType.Social) {
if (connectorInstance.metadata.type !== ConnectorType.Social) {
throw new RequestError({
code: 'entity.not_found',
status: 404,
@ -170,7 +170,7 @@ describe('sessionSocialRoutes', () => {
it('throw error when auth code is wrong', async () => {
(getConnectorInstanceById as jest.Mock).mockResolvedValueOnce({
instance: { metadata: { target: connectorTarget } },
metadata: { target: connectorTarget },
});
const response = await sessionRequest.post('/session/sign-in/social/auth').send({
connectorId: 'connectorId',
@ -183,7 +183,7 @@ describe('sessionSocialRoutes', () => {
it('throw error when code is provided but connector can not be found', async () => {
(getConnectorInstanceById as jest.Mock).mockResolvedValueOnce({
instance: { metadata: { target: connectorTarget } },
metadata: { target: connectorTarget },
});
const response = await sessionRequest.post('/session/sign-in/social/auth').send({
connectorId: '_connectorId',
@ -196,7 +196,7 @@ describe('sessionSocialRoutes', () => {
it('get and add user info with auth code, as well as assign result and redirect', async () => {
(getConnectorInstanceById as jest.Mock).mockResolvedValueOnce({
instance: { metadata: { target: connectorTarget } },
metadata: { target: connectorTarget },
});
const response = await sessionRequest.post('/session/sign-in/social/auth').send({
connectorId: 'connectorId',
@ -224,7 +224,7 @@ describe('sessionSocialRoutes', () => {
it('throw error when identity exists', async () => {
const wrongConnectorTarget = 'wrongConnectorTarget';
(getConnectorInstanceById as jest.Mock).mockResolvedValueOnce({
instance: { metadata: { target: wrongConnectorTarget } },
metadata: { target: wrongConnectorTarget },
});
const response = await sessionRequest.post('/session/sign-in/social/auth').send({
connectorId: '_connectorId_',
@ -250,7 +250,7 @@ describe('sessionSocialRoutes', () => {
beforeEach(() => {
const mockGetConnectorInstanceById = getConnectorInstanceById as jest.Mock;
mockGetConnectorInstanceById.mockResolvedValueOnce({
instance: { metadata: { target: 'connectorTarget' } },
metadata: { target: 'connectorTarget' },
});
});
afterEach(() => {
@ -312,7 +312,7 @@ describe('sessionSocialRoutes', () => {
beforeEach(() => {
const mockGetConnectorInstanceById = getConnectorInstanceById as jest.Mock;
mockGetConnectorInstanceById.mockResolvedValueOnce({
instance: { metadata: { target: 'connectorTarget' } },
metadata: { target: 'connectorTarget' },
});
});
afterEach(() => {
@ -378,7 +378,7 @@ describe('sessionSocialRoutes', () => {
beforeEach(() => {
const mockGetConnectorInstanceById = getConnectorInstanceById as jest.Mock;
mockGetConnectorInstanceById.mockResolvedValueOnce({
instance: { metadata: { target: 'connectorTarget' } },
metadata: { target: 'connectorTarget' },
});
});
afterEach(() => {

View file

@ -45,7 +45,7 @@ export default function sessionSocialRoutes<T extends AnonymousRouter>(
assertThat(state && redirectUri, 'session.insufficient_info');
const connector = await getSocialConnectorInstanceById(connectorId);
assertThat(connector.connector.enabled, 'connector.not_enabled');
const redirectTo = await connector.instance.getAuthorizationUri({ state, redirectUri });
const redirectTo = await connector.getAuthorizationUri({ state, redirectUri });
ctx.body = { redirectTo };
return next();
@ -67,9 +67,7 @@ export default function sessionSocialRoutes<T extends AnonymousRouter>(
const type = 'SignInSocial';
ctx.log(type, { connectorId, data });
const {
instance: {
metadata: { target },
},
metadata: { target },
} = await getConnectorInstanceById(connectorId);
const userInfo = await getUserInfoByAuthCode(connectorId, data);
@ -120,9 +118,7 @@ export default function sessionSocialRoutes<T extends AnonymousRouter>(
const type = 'SignInSocialBind';
ctx.log(type, { connectorId });
const {
instance: {
metadata: { target },
},
metadata: { target },
} = await getConnectorInstanceById(connectorId);
const userInfo = await getUserInfoFromInteractionResult(connectorId, result);
@ -162,9 +158,7 @@ export default function sessionSocialRoutes<T extends AnonymousRouter>(
const type = 'RegisterSocial';
ctx.log(type, { connectorId });
const {
instance: {
metadata: { target },
},
metadata: { target },
} = await getConnectorInstanceById(connectorId);
const userInfo = await getUserInfoFromInteractionResult(connectorId, result);
@ -209,9 +203,7 @@ export default function sessionSocialRoutes<T extends AnonymousRouter>(
const type = 'RegisterSocialBind';
ctx.log(type, { connectorId, userId });
const {
instance: {
metadata: { target },
},
metadata: { target },
} = await getConnectorInstanceById(connectorId);
const userInfo = await getUserInfoFromInteractionResult(connectorId, result);

View file

@ -52,8 +52,7 @@ export default function signInExperiencesRoutes<T extends AuthedRouter>(router:
const filteredSocialSignInConnectorTargets = socialSignInConnectorTargets?.filter((target) =>
enabledConnectorInstances.some(
(connector) =>
connector.instance.metadata.target === target &&
connector.instance.metadata.type === ConnectorType.Social
connector.metadata.target === target && connector.metadata.type === ConnectorType.Social
)
);

View file

@ -32,7 +32,7 @@ jest.mock('@/connectors', () => ({
getSocialConnectorInstanceById: async (connectorId: string) => {
const connectorInstance = await getConnectorInstanceById(connectorId);
if (connectorInstance.instance.metadata.type !== ConnectorType.Social) {
if (connectorInstance.metadata.type !== ConnectorType.Social) {
throw new RequestError({
code: 'entity.not_found',
status: 404,
@ -92,19 +92,19 @@ describe('GET /.well-known/sign-in-exp', () => {
...mockSignInExperience,
socialConnectors: [
{
...mockGithubConnectorInstance.instance.metadata,
...mockGithubConnectorInstance.metadata,
id: mockGithubConnectorInstance.connector.id,
},
{
...mockFacebookConnectorInstance.instance.metadata,
...mockFacebookConnectorInstance.metadata,
id: mockFacebookConnectorInstance.connector.id,
},
{
...mockWechatConnectorInstance.instance.metadata,
...mockWechatConnectorInstance.metadata,
id: mockWechatConnectorInstance.connector.id,
},
{
...mockWechatNativeConnectorInstance.instance.metadata,
...mockWechatNativeConnectorInstance.metadata,
id: mockWechatNativeConnectorInstance.connector.id,
},
],

View file

@ -1,4 +1,4 @@
import { ConnectorMetadata } from '@logto/connector-types';
import { ConnectorMetadata } from '@logto/connector-schemas';
import { SignInMode } from '@logto/schemas';
import {
adminConsoleApplicationId,
@ -55,17 +55,13 @@ export default function wellKnownRoutes<T extends AnonymousRouter>(router: T, pr
Array<ConnectorMetadata & { id: string }>
>((previous, connectorTarget) => {
const connectors = connectorInstances.filter(
({
instance: {
metadata: { target },
},
connector: { enabled },
}) => target === connectorTarget && enabled
({ metadata: { target }, connector: { enabled } }) =>
target === connectorTarget && enabled
);
return [
...previous,
...connectors.map(({ instance: { metadata }, connector: { id } }) => ({
...connectors.map(({ metadata, connector: { id } }) => ({
...metadata,
id,
})),

View file

@ -47,7 +47,7 @@
},
"prettier": "@silverhand/eslint-config/.prettierrc",
"dependencies": {
"@logto/connector-types": "^1.0.0-beta.4",
"@logto/connector-schemas": "^1.0.0-beta.4",
"@logto/phrases": "^1.0.0-beta.4",
"@logto/phrases-ui": "^1.0.0-beta.4",
"@logto/shared": "^1.0.0-beta.4",

View file

@ -1,8 +1,8 @@
import { ConnectorMetadata } from '@logto/connector-types';
import { ConnectorMetadata } from '@logto/connector-schemas';
import { Connector } from '../db-entries';
export type { ConnectorMetadata } from '@logto/connector-types';
export { ConnectorType, ConnectorPlatform } from '@logto/connector-types';
export type { ConnectorMetadata } from '@logto/connector-schemas';
export { ConnectorType, ConnectorPlatform } from '@logto/connector-schemas';
export type ConnectorDto = Connector & ConnectorMetadata;

163
pnpm-lock.yaml generated
View file

@ -21,7 +21,7 @@ importers:
packages/connector-alipay-native:
specifiers:
'@jest/types': ^28.1.3
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/shared': ^1.0.0-beta.4
'@shopify/jest-koa-mocks': ^5.0.0
'@silverhand/eslint-config': 1.0.0-rc.2
@ -47,7 +47,7 @@ importers:
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/connector-types': link:../connector-types
'@logto/connector-schemas': link:../connector-schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.1.7
'@silverhand/jest-config': 1.0.0-rc.3_bi2kohzqnxavgozw3csgny5hju
@ -78,7 +78,7 @@ importers:
packages/connector-alipay-web:
specifiers:
'@jest/types': ^28.1.3
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/shared': ^1.0.0-beta.4
'@shopify/jest-koa-mocks': ^5.0.0
'@silverhand/eslint-config': 1.0.0-rc.2
@ -104,7 +104,7 @@ importers:
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/connector-types': link:../connector-types
'@logto/connector-schemas': link:../connector-schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.1.7
'@silverhand/jest-config': 1.0.0-rc.3_bi2kohzqnxavgozw3csgny5hju
@ -135,7 +135,7 @@ importers:
packages/connector-aliyun-dm:
specifiers:
'@jest/types': ^28.1.3
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/shared': ^1.0.0-beta.4
'@silverhand/eslint-config': 1.0.0-rc.2
'@silverhand/essentials': ^1.1.0
@ -152,7 +152,7 @@ importers:
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/connector-types': link:../connector-types
'@logto/connector-schemas': link:../connector-schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.1.7
'@silverhand/jest-config': 1.0.0-rc.3_bi2kohzqnxavgozw3csgny5hju
@ -174,7 +174,7 @@ importers:
packages/connector-aliyun-sms:
specifiers:
'@jest/types': ^28.1.3
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/schemas': ^1.0.0-beta.4
'@logto/shared': ^1.0.0-beta.4
'@silverhand/eslint-config': 1.0.0-rc.2
@ -192,7 +192,7 @@ importers:
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/connector-types': link:../connector-types
'@logto/connector-schemas': link:../connector-schemas
'@logto/schemas': link:../schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.1.7
@ -215,7 +215,7 @@ importers:
packages/connector-apple:
specifiers:
'@jest/types': ^28.1.3
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/schemas': ^1.0.0-beta.4
'@logto/shared': ^1.0.0-beta.4
'@silverhand/eslint-config': 1.0.0-rc.2
@ -236,7 +236,7 @@ importers:
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/connector-types': link:../connector-types
'@logto/connector-schemas': link:../connector-schemas
'@logto/schemas': link:../schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.1.7
@ -263,7 +263,7 @@ importers:
specifiers:
'@azure/msal-node': ^1.12.0
'@jest/types': ^28.1.3
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/schemas': ^1.0.0-beta.4
'@logto/shared': ^1.0.0-beta.4
'@silverhand/eslint-config': 1.0.0-rc.2
@ -285,7 +285,7 @@ importers:
zod: ^3.14.3
dependencies:
'@azure/msal-node': 1.12.0
'@logto/connector-types': link:../connector-types
'@logto/connector-schemas': link:../connector-schemas
'@logto/schemas': link:../schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.1.7
@ -308,33 +308,10 @@ importers:
tsc-watch: 5.0.3_typescript@4.7.4
typescript: 4.7.4
packages/connector-base-classes:
specifiers:
'@logto/connector-types': ^1.0.0-beta.3
'@silverhand/eslint-config': 1.0.0-rc.2
'@silverhand/essentials': ^1.1.6
'@silverhand/ts-config': 1.0.0-rc.2
'@types/node': ^16.3.1
eslint: ^8.21.0
lint-staged: ^13.0.0
prettier: ^2.7.1
typescript: ^4.7.4
dependencies:
'@logto/connector-types': link:../connector-types
devDependencies:
'@silverhand/eslint-config': 1.0.0-rc.2_swk2g7ygmfleszo5c33j4vooni
'@silverhand/essentials': 1.1.7
'@silverhand/ts-config': 1.0.0-rc.2_typescript@4.7.4
'@types/node': 16.11.12
eslint: 8.21.0
lint-staged: 13.0.0
prettier: 2.7.1
typescript: 4.7.4
packages/connector-facebook:
specifiers:
'@jest/types': ^28.1.3
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/schemas': ^1.0.0-beta.4
'@logto/shared': ^1.0.0-beta.4
'@silverhand/eslint-config': 1.0.0-rc.2
@ -354,7 +331,7 @@ importers:
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/connector-types': link:../connector-types
'@logto/connector-schemas': link:../connector-schemas
'@logto/schemas': link:../schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.1.7
@ -379,7 +356,7 @@ importers:
packages/connector-github:
specifiers:
'@jest/types': ^28.1.3
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/schemas': ^1.0.0-beta.4
'@logto/shared': ^1.0.0-beta.4
'@silverhand/eslint-config': 1.0.0-rc.2
@ -400,7 +377,7 @@ importers:
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/connector-types': link:../connector-types
'@logto/connector-schemas': link:../connector-schemas
'@logto/schemas': link:../schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.1.7
@ -426,7 +403,7 @@ importers:
packages/connector-google:
specifiers:
'@jest/types': ^28.1.3
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/schemas': ^1.0.0-beta.4
'@logto/shared': ^1.0.0-beta.4
'@silverhand/eslint-config': 1.0.0-rc.2
@ -446,7 +423,7 @@ importers:
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/connector-types': link:../connector-types
'@logto/connector-schemas': link:../connector-schemas
'@logto/schemas': link:../schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.1.7
@ -470,7 +447,7 @@ importers:
packages/connector-mock-email:
specifiers:
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/shared': ^1.0.0-beta.4
'@silverhand/eslint-config': 1.0.0-rc.2
'@silverhand/essentials': ^1.1.6
@ -483,7 +460,7 @@ importers:
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/connector-types': link:../connector-types
'@logto/connector-schemas': link:../connector-schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.1.7
zod: 3.14.3
@ -499,7 +476,7 @@ importers:
packages/connector-mock-sms:
specifiers:
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/shared': ^1.0.0-beta.4
'@silverhand/eslint-config': 1.0.0-rc.2
'@silverhand/essentials': ^1.1.6
@ -512,7 +489,7 @@ importers:
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/connector-types': link:../connector-types
'@logto/connector-schemas': link:../connector-schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.1.7
zod: 3.14.3
@ -528,7 +505,7 @@ importers:
packages/connector-mock-social:
specifiers:
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/schemas': ^1.0.0-beta.4
'@logto/shared': ^1.0.0-beta.4
'@silverhand/eslint-config': 1.0.0-rc.2
@ -542,7 +519,7 @@ importers:
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/connector-types': link:../connector-types
'@logto/connector-schemas': link:../connector-schemas
'@logto/schemas': link:../schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.1.7
@ -557,10 +534,43 @@ importers:
tsc-watch: 5.0.3_typescript@4.7.4
typescript: 4.7.4
packages/connector-schemas:
specifiers:
'@jest/types': ^28.1.3
'@logto/phrases': ^1.0.0-beta.4
'@shopify/jest-koa-mocks': ^5.0.0
'@silverhand/eslint-config': 1.0.0-rc.2
'@silverhand/essentials': ^1.1.6
'@silverhand/ts-config': 1.0.0-rc.2
'@types/jest': ^28.1.6
'@types/node': ^16.3.1
eslint: ^8.21.0
jest: ^28.1.3
lint-staged: ^13.0.0
prettier: ^2.7.1
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/phrases': link:../phrases
zod: 3.14.3
devDependencies:
'@jest/types': 28.1.3
'@shopify/jest-koa-mocks': 5.0.0
'@silverhand/eslint-config': 1.0.0-rc.2_swk2g7ygmfleszo5c33j4vooni
'@silverhand/essentials': 1.1.7
'@silverhand/ts-config': 1.0.0-rc.2_typescript@4.7.4
'@types/jest': 28.1.6
'@types/node': 16.11.12
eslint: 8.21.0
jest: 28.1.3_@types+node@16.11.12
lint-staged: 13.0.0
prettier: 2.7.1
typescript: 4.7.4
packages/connector-sendgrid-mail:
specifiers:
'@jest/types': ^28.1.3
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/shared': ^1.0.0-beta.4
'@silverhand/eslint-config': 1.0.0-rc.2
'@silverhand/essentials': ^1.1.6
@ -577,7 +587,7 @@ importers:
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/connector-types': link:../connector-types
'@logto/connector-schemas': link:../connector-schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.1.7
'@silverhand/jest-config': 1.0.0-rc.3_bi2kohzqnxavgozw3csgny5hju
@ -599,7 +609,7 @@ importers:
packages/connector-smtp:
specifiers:
'@jest/types': ^28.1.3
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/shared': ^1.0.0-beta.4
'@silverhand/eslint-config': 1.0.0-rc.2
'@silverhand/essentials': ^1.1.6
@ -617,7 +627,7 @@ importers:
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/connector-types': link:../connector-types
'@logto/connector-schemas': link:../connector-schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.1.7
'@silverhand/jest-config': 1.0.0-rc.3_bi2kohzqnxavgozw3csgny5hju
@ -640,7 +650,7 @@ importers:
packages/connector-twilio-sms:
specifiers:
'@jest/types': ^28.1.3
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/shared': ^1.0.0-beta.4
'@silverhand/eslint-config': 1.0.0-rc.2
'@silverhand/essentials': ^1.1.6
@ -657,7 +667,7 @@ importers:
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/connector-types': link:../connector-types
'@logto/connector-schemas': link:../connector-schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.1.7
'@silverhand/jest-config': 1.0.0-rc.3_bi2kohzqnxavgozw3csgny5hju
@ -676,41 +686,10 @@ importers:
tsc-watch: 5.0.3_typescript@4.7.4
typescript: 4.7.4
packages/connector-types:
specifiers:
'@jest/types': ^28.1.3
'@logto/phrases': ^1.0.0-beta.4
'@shopify/jest-koa-mocks': ^5.0.0
'@silverhand/eslint-config': 1.0.0-rc.2
'@silverhand/essentials': ^1.1.6
'@silverhand/ts-config': 1.0.0-rc.2
'@types/jest': ^28.1.6
eslint: ^8.21.0
jest: ^28.1.3
lint-staged: ^13.0.0
prettier: ^2.7.1
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/phrases': link:../phrases
zod: 3.14.3
devDependencies:
'@jest/types': 28.1.3
'@shopify/jest-koa-mocks': 5.0.0
'@silverhand/eslint-config': 1.0.0-rc.2_swk2g7ygmfleszo5c33j4vooni
'@silverhand/essentials': 1.1.7
'@silverhand/ts-config': 1.0.0-rc.2_typescript@4.7.4
'@types/jest': 28.1.6
eslint: 8.21.0
jest: 28.1.3
lint-staged: 13.0.0
prettier: 2.7.1
typescript: 4.7.4
packages/connector-wechat-native:
specifiers:
'@jest/types': ^28.1.3
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/schemas': ^1.0.0-beta.4
'@logto/shared': ^1.0.0-beta.4
'@silverhand/eslint-config': 1.0.0-rc.2
@ -730,7 +709,7 @@ importers:
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/connector-types': link:../connector-types
'@logto/connector-schemas': link:../connector-schemas
'@logto/schemas': link:../schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.1.7
@ -755,7 +734,7 @@ importers:
packages/connector-wechat-web:
specifiers:
'@jest/types': ^28.1.3
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/schemas': ^1.0.0-beta.4
'@logto/shared': ^1.0.0-beta.4
'@silverhand/eslint-config': 1.0.0-rc.2
@ -775,7 +754,7 @@ importers:
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/connector-types': link:../connector-types
'@logto/connector-schemas': link:../connector-schemas
'@logto/schemas': link:../schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.1.7
@ -938,10 +917,10 @@ importers:
'@logto/connector-facebook': ^1.0.0-beta.4
'@logto/connector-github': ^1.0.0-beta.4
'@logto/connector-google': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/connector-sendgrid-email': ^1.0.0-beta.4
'@logto/connector-smtp': ^1.0.0-beta.4
'@logto/connector-twilio-sms': ^1.0.0-beta.4
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-wechat-native': ^1.0.0-beta.4
'@logto/connector-wechat-web': ^1.0.0-beta.4
'@logto/phrases': ^1.0.0-beta.4
@ -1023,10 +1002,10 @@ importers:
'@logto/connector-facebook': link:../connector-facebook
'@logto/connector-github': link:../connector-github
'@logto/connector-google': link:../connector-google
'@logto/connector-schemas': link:../connector-schemas
'@logto/connector-sendgrid-email': link:../connector-sendgrid-mail
'@logto/connector-smtp': link:../connector-smtp
'@logto/connector-twilio-sms': link:../connector-twilio-sms
'@logto/connector-types': link:../connector-types
'@logto/connector-wechat-native': link:../connector-wechat-native
'@logto/connector-wechat-web': link:../connector-wechat-web
'@logto/phrases': link:../phrases
@ -1242,7 +1221,7 @@ importers:
packages/schemas:
specifiers:
'@logto/connector-types': ^1.0.0-beta.4
'@logto/connector-schemas': ^1.0.0-beta.4
'@logto/phrases': ^1.0.0-beta.4
'@logto/phrases-ui': ^1.0.0-beta.4
'@logto/shared': ^1.0.0-beta.4
@ -1265,7 +1244,7 @@ importers:
typescript: ^4.7.4
zod: ^3.14.3
dependencies:
'@logto/connector-types': link:../connector-types
'@logto/connector-schemas': link:../connector-schemas
'@logto/phrases': link:../phrases
'@logto/phrases-ui': link:../phrases-ui
'@logto/shared': link:../shared