0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-27 21:39:16 -05:00

refactor(connector): refactor connector

This commit is contained in:
Darcy Ye 2022-08-04 17:01:59 +08:00
parent 0acd432a3f
commit 7634c425c8
No known key found for this signature in database
GPG key ID: B46F4C07EDEFC610
24 changed files with 435 additions and 643 deletions

View file

@ -10,10 +10,9 @@
*/
import {
AuthResponseParser,
ConnectorError,
ConnectorErrorCodes,
ConnectorMetadata,
Connector,
GetAuthorizationUri,
GetUserInfo,
SocialConnectorInstance,
@ -22,7 +21,6 @@ import {
import { assert } from '@silverhand/essentials';
import dayjs from 'dayjs';
import got from 'got';
import { z } from 'zod';
import {
alipayEndpoint,
@ -37,7 +35,9 @@ import {
} from './constant';
import {
alipayNativeConfigGuard,
authResponseGuard,
AlipayNativeConfig,
AuthResponse,
accessTokenResponseGuard,
userInfoResponseGuard,
ErrorHandler,
@ -46,25 +46,17 @@ import { signingParameters } from './utils';
export type { AlipayNativeConfig } from './types';
export default class AlipayNativeConnector implements SocialConnectorInstance<AlipayNativeConfig> {
public metadata: ConnectorMetadata = defaultMetadata;
private _connector?: Connector;
public get connector() {
if (!this._connector) {
throw new ConnectorError(ConnectorErrorCodes.General);
}
return this._connector;
}
public set connector(input: Connector) {
this._connector = input;
}
export default class AlipayNativeConnector<T> extends SocialConnectorInstance<
AlipayNativeConfig,
T
> {
private readonly signingParameters = signingParameters;
constructor(public readonly getConfig: GetConnectorConfig) {}
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
this.metadataParser();
}
public validateConfig(config: unknown): asserts config is AlipayNativeConfig {
const result = alipayNativeConfigGuard.safeParse(config);
@ -125,7 +117,7 @@ export default class AlipayNativeConnector implements SocialConnectorInstance<Al
};
public getUserInfo: GetUserInfo = async (data) => {
const { auth_code } = await this.authorizationCallbackHandler(data);
const { auth_code } = await this.authResponseParser(data);
const config = await this.getConfig(this.metadata.id);
this.validateConfig(config);
@ -174,6 +166,18 @@ export default class AlipayNativeConnector implements SocialConnectorInstance<Al
return { id, avatar, name };
};
protected readonly authResponseParser: AuthResponseParser<AuthResponse> = async (
parameterObject: unknown
) => {
const result = authResponseGuard.safeParse(parameterObject);
if (!result.success) {
throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(parameterObject));
}
return result.data;
};
private readonly errorHandler: ErrorHandler = ({ code, msg, sub_code, sub_msg }) => {
if (invalidAccessTokenCode.includes(code)) {
throw new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid, msg);
@ -193,17 +197,5 @@ export default class AlipayNativeConnector implements SocialConnectorInstance<Al
});
}
};
private readonly authorizationCallbackHandler = async (parameterObject: unknown) => {
const dataGuard = z.object({ auth_code: z.string() });
const result = dataGuard.safeParse(parameterObject);
if (!result.success) {
throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(parameterObject));
}
return result.data;
};
}
/* eslint-enable unicorn/text-encoding-identifier-case */

View file

@ -57,3 +57,7 @@ export const userInfoResponseGuard = z.object({
export type UserInfoResponse = z.infer<typeof userInfoResponseGuard>;
export type ErrorHandler = (response: AlipayUserInfoShareResponseGuard) => void;
export const authResponseGuard = z.object({ auth_code: z.string() });
export type AuthResponse = z.infer<typeof authResponseGuard>;

View file

@ -6,10 +6,9 @@
*/
import {
AuthResponseParser,
ConnectorError,
ConnectorErrorCodes,
ConnectorMetadata,
Connector,
GetAuthorizationUri,
GetUserInfo,
SocialConnectorInstance,
@ -18,7 +17,6 @@ import {
import { assert } from '@silverhand/essentials';
import dayjs from 'dayjs';
import got from 'got';
import { z } from 'zod';
import {
alipayEndpoint,
@ -35,7 +33,9 @@ import {
} from './constant';
import {
alipayConfigGuard,
authResponseGuard,
AlipayConfig,
AuthResponse,
accessTokenResponseGuard,
userInfoResponseGuard,
ErrorHandler,
@ -44,25 +44,14 @@ import { signingParameters } from './utils';
export type { AlipayConfig } from './types';
export default class AlipayConnector implements SocialConnectorInstance<AlipayConfig> {
public metadata: ConnectorMetadata = defaultMetadata;
private _connector?: Connector;
public get connector() {
if (!this._connector) {
throw new ConnectorError(ConnectorErrorCodes.General);
}
return this._connector;
}
public set connector(input: Connector) {
this._connector = input;
}
export default class AlipayConnector<T> extends SocialConnectorInstance<AlipayConfig, T> {
private readonly signingParameters = signingParameters;
constructor(public readonly getConfig: GetConnectorConfig) {}
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
this.metadataParser();
}
public validateConfig(config: unknown): asserts config is AlipayConfig {
const result = alipayConfigGuard.safeParse(config);
@ -131,7 +120,7 @@ export default class AlipayConnector implements SocialConnectorInstance<AlipayCo
};
public getUserInfo: GetUserInfo = async (data) => {
const { auth_code } = await this.authorizationCallbackHandler(data);
const { auth_code } = await this.authResponseParser(data);
const config = await this.getConfig(this.metadata.id);
this.validateConfig(config);
@ -183,6 +172,21 @@ export default class AlipayConnector implements SocialConnectorInstance<AlipayCo
return { id, avatar, name };
};
protected readonly authResponseParser: AuthResponseParser<AuthResponse> = async (
parameterObject: unknown
) => {
const result = authResponseGuard.safeParse(parameterObject);
if (!result.success) {
throw new ConnectorError(
ConnectorErrorCodes.InvalidResponse,
JSON.stringify(parameterObject)
);
}
return result.data;
};
private readonly errorHandler: ErrorHandler = ({ code, msg, sub_code, sub_msg }) => {
if (invalidAccessTokenCode.includes(code)) {
throw new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid, msg);
@ -202,19 +206,4 @@ export default class AlipayConnector implements SocialConnectorInstance<AlipayCo
});
}
};
private readonly authorizationCallbackHandler = async (parameterObject: unknown) => {
const dataGuard = z.object({ auth_code: z.string() });
const result = dataGuard.safeParse(parameterObject);
if (!result.success) {
throw new ConnectorError(
ConnectorErrorCodes.InvalidResponse,
JSON.stringify(parameterObject)
);
}
return result.data;
};
}

View file

@ -58,3 +58,7 @@ export const userInfoResponseGuard = z.object({
export type UserInfoResponse = z.infer<typeof userInfoResponseGuard>;
export type ErrorHandler = (response: AlipayUserInfoShareResponse) => void;
export const authResponseGuard = z.object({ auth_code: z.string() });
export type AuthResponse = z.infer<typeof authResponseGuard>;

View file

@ -1,13 +1,9 @@
import {
ConnectorError,
ConnectorErrorCodes,
ConnectorMetadata,
Connector,
EmailSendMessageFunction,
EmailSendTestMessageFunction,
EmailSendMessageByFunction,
EmailConnectorInstance,
GetConnectorConfig,
EmailMessageTypes,
} from '@logto/connector-types';
import { assert } from '@silverhand/essentials';
import { HTTPError } from 'got';
@ -21,24 +17,13 @@ import {
sendMailErrorResponseGuard,
} from './types';
export default class AliyunDmConnector implements EmailConnectorInstance<AliyunDmConfig> {
public metadata: ConnectorMetadata = defaultMetadata;
private _connector?: Connector;
public get connector() {
if (!this._connector) {
throw new ConnectorError(ConnectorErrorCodes.General);
}
return this._connector;
export default class AliyunDmConnector<T> extends EmailConnectorInstance<AliyunDmConfig, T> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
this.metadataParser();
}
public set connector(input: Connector) {
this._connector = input;
}
constructor(public readonly getConfig: GetConnectorConfig) {}
public validateConfig(config: unknown): asserts config is AliyunDmConfig {
const result = aliyunDmConfigGuard.safeParse(config);
@ -47,25 +32,11 @@ export default class AliyunDmConnector implements EmailConnectorInstance<AliyunD
}
}
public sendMessage: EmailSendMessageFunction = async (address, type, data) => {
const emailConfig = await this.getConfig(this.metadata.id);
this.validateConfig(emailConfig);
return this.sendMessageBy(emailConfig, address, type, data);
};
public sendTestMessage: EmailSendTestMessageFunction = async (config, address, type, data) => {
this.validateConfig(config);
return this.sendMessageBy(config, address, type, data);
};
private readonly sendMessageBy = async (
config: AliyunDmConfig,
address: string,
type: keyof EmailMessageTypes,
data: EmailMessageTypes[typeof type]
public readonly sendMessageBy: EmailSendMessageByFunction<AliyunDmConfig> = async (
config,
address,
type,
data
) => {
const { accessKeyId, accessKeySecret, accountName, fromAlias, templates } = config;
const template = templates.find((template) => template.usageType === type);

View file

@ -1,13 +1,9 @@
import {
ConnectorError,
ConnectorErrorCodes,
ConnectorMetadata,
Connector,
SmsSendMessageFunction,
SmsSendTestMessageFunction,
SmsSendMessageByFunction,
SmsConnectorInstance,
GetConnectorConfig,
SmsMessageTypes,
} from '@logto/connector-types';
import { assert } from '@silverhand/essentials';
import { HTTPError } from 'got';
@ -16,24 +12,13 @@ import { defaultMetadata } from './constant';
import { sendSms } from './single-send-text';
import { aliyunSmsConfigGuard, AliyunSmsConfig, sendSmsResponseGuard } from './types';
export default class AliyunSmsConnector implements SmsConnectorInstance<AliyunSmsConfig> {
public metadata: ConnectorMetadata = defaultMetadata;
private _connector?: Connector;
public get connector() {
if (!this._connector) {
throw new ConnectorError(ConnectorErrorCodes.General);
}
return this._connector;
export default class AliyunSmsConnector<T> extends SmsConnectorInstance<AliyunSmsConfig, T> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
this.metadataParser();
}
public set connector(input: Connector) {
this._connector = input;
}
constructor(public readonly getConfig: GetConnectorConfig) {}
public validateConfig(config: unknown): asserts config is AliyunSmsConfig {
const result = aliyunSmsConfigGuard.safeParse(config);
@ -42,25 +27,11 @@ export default class AliyunSmsConnector implements SmsConnectorInstance<AliyunSm
}
}
public sendMessage: SmsSendMessageFunction = async (phone, type, data) => {
const smsConfig = await this.getConfig(this.metadata.id);
this.validateConfig(smsConfig);
return this.sendMessageBy(smsConfig, phone, type, data);
};
public sendTestMessage: SmsSendTestMessageFunction = async (config, phone, type, data) => {
this.validateConfig(config);
return this.sendMessageBy(config, phone, type, data);
};
private readonly sendMessageBy = async (
config: AliyunSmsConfig,
phone: string,
type: keyof SmsMessageTypes,
data: SmsMessageTypes[typeof type]
public readonly sendMessageBy: SmsSendMessageByFunction<AliyunSmsConfig> = async (
config,
phone,
type,
data
) => {
const { accessKeyId, accessKeySecret, signName, templates } = config;
const template = templates.find(({ usageType }) => usageType === type);

View file

@ -1,37 +1,25 @@
import {
ConnectorMetadata,
AuthResponseParser,
GetAuthorizationUri,
GetUserInfo,
ConnectorError,
ConnectorErrorCodes,
Connector,
SocialConnectorInstance,
GetConnectorConfig,
} from '@logto/connector-types';
import { createRemoteJWKSet, jwtVerify } from 'jose';
import { scope, defaultMetadata, jwksUri, issuer, authorizationEndpoint } from './constant';
import { appleConfigGuard, AppleConfig, dataGuard } from './types';
import { appleConfigGuard, authResponseGuard, AppleConfig, AuthResponse } from './types';
// TO-DO: support nonce validation
export default class AppleConnector implements SocialConnectorInstance<AppleConfig> {
public metadata: ConnectorMetadata = defaultMetadata;
private _connector?: Connector;
public get connector() {
if (!this._connector) {
throw new ConnectorError(ConnectorErrorCodes.General);
}
return this._connector;
export default class AppleConnector<T> extends SocialConnectorInstance<AppleConfig, T> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
this.metadataParser();
}
public set connector(input: Connector) {
this._connector = input;
}
constructor(public readonly getConfig: GetConnectorConfig) {}
public validateConfig(config: unknown): asserts config is AppleConfig {
const result = appleConfigGuard.safeParse(config);
@ -59,7 +47,7 @@ export default class AppleConnector implements SocialConnectorInstance<AppleConf
};
public getUserInfo: GetUserInfo = async (data) => {
const { id_token: idToken } = await this.authorizationCallbackHandler(data);
const { id_token: idToken } = await this.authResponseParser(data);
if (!idToken) {
throw new ConnectorError(ConnectorErrorCodes.SocialIdTokenInvalid);
@ -89,8 +77,10 @@ export default class AppleConnector implements SocialConnectorInstance<AppleConf
}
};
private readonly authorizationCallbackHandler = async (parameterObject: unknown) => {
const result = dataGuard.safeParse(parameterObject);
protected readonly authResponseParser: AuthResponseParser<AuthResponse> = async (
parameterObject: unknown
) => {
const result = authResponseGuard.safeParse(parameterObject);
if (!result.success) {
throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(parameterObject));

View file

@ -7,6 +7,8 @@ export const appleConfigGuard = z.object({
export type AppleConfig = z.infer<typeof appleConfigGuard>;
// https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/configuring_your_webpage_for_sign_in_with_apple#3331292
export const dataGuard = z.object({
export const authResponseGuard = z.object({
id_token: z.string(),
});
export type AuthResponse = z.infer<typeof authResponseGuard>;

View file

@ -11,16 +11,15 @@ import {
ConnectorErrorCodes,
GetAuthorizationUri,
GetUserInfo,
ConnectorMetadata,
Connector,
SocialConnectorInstance,
GetConnectorConfig,
codeWithRedirectDataGuard,
CodeWithRedirectData,
GetConnectorConfig,
} from '@logto/connector-types';
import { assert, conditional } from '@silverhand/essentials';
import got, { HTTPError } from 'got';
import { scopes, defaultMetadata, defaultTimeout, graphAPIEndpoint } from './constant';
import { scopes, defaultTimeout, graphAPIEndpoint, defaultMetadata } from './constant';
import {
azureADConfigGuard,
AzureADConfig,
@ -28,31 +27,19 @@ import {
userInfoResponseGuard,
} from './types';
export default class AzureADConnector implements SocialConnectorInstance<AzureADConfig> {
public metadata: ConnectorMetadata = defaultMetadata;
export default class AzureADConnector<T> extends SocialConnectorInstance<AzureADConfig, T> {
public clientApplication!: ConfidentialClientApplication;
public authCodeUrlParams!: AuthorizationUrlRequest;
cryptoProvider = new CryptoProvider();
private readonly authCodeRequest!: AuthorizationCodeRequest;
private _connector?: Connector;
public get connector() {
if (!this._connector) {
throw new ConnectorError(ConnectorErrorCodes.General);
}
return this._connector;
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
this.metadataParser();
}
public set connector(input: Connector) {
this._connector = input;
}
constructor(public readonly getConfig: GetConnectorConfig) {}
public validateConfig(config: unknown): asserts config is AzureADConfig {
const result = azureADConfigGuard.safeParse(config);
@ -114,7 +101,7 @@ export default class AzureADConnector implements SocialConnectorInstance<AzureAD
};
public getUserInfo: GetUserInfo = async (data) => {
const { code, redirectUri } = await this.authorizationCallbackHandler(data);
const { code, redirectUri } = await this.authResponseParser(data);
const { accessToken } = await this.getAccessToken(code, redirectUri);
const config = await this.getConfig(this.metadata.id);
@ -157,7 +144,7 @@ export default class AzureADConnector implements SocialConnectorInstance<AzureAD
}
};
private readonly authorizationCallbackHandler = async (parameterObject: unknown) => {
public authResponseParser = async (parameterObject: unknown): Promise<CodeWithRedirectData> => {
const result = codeWithRedirectDataGuard.safeParse(parameterObject);
if (!result.success) {

View file

@ -4,15 +4,15 @@
*/
import {
AuthResponseParser,
ConnectorError,
ConnectorErrorCodes,
ConnectorMetadata,
Connector,
GetAuthorizationUri,
GetUserInfo,
SocialConnectorInstance,
GetConnectorConfig,
codeWithRedirectDataGuard,
CodeWithRedirectData,
} from '@logto/connector-types';
import { assert } from '@silverhand/essentials';
import got, { HTTPError } from 'got';
@ -33,24 +33,13 @@ import {
userInfoResponseGuard,
} from './types';
export default class FacebookConnector implements SocialConnectorInstance<FacebookConfig> {
public metadata: ConnectorMetadata = defaultMetadata;
private _connector?: Connector;
public get connector() {
if (!this._connector) {
throw new ConnectorError(ConnectorErrorCodes.General);
}
return this._connector;
export default class FacebookConnector<T> extends SocialConnectorInstance<FacebookConfig, T> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
this.metadataParser();
}
public set connector(input: Connector) {
this._connector = input;
}
constructor(public readonly getConfig: GetConnectorConfig) {}
public validateConfig(config: unknown): asserts config is FacebookConfig {
const result = facebookConfigGuard.safeParse(config);
@ -106,7 +95,7 @@ export default class FacebookConnector implements SocialConnectorInstance<Facebo
};
public getUserInfo: GetUserInfo = async (data) => {
const { code, redirectUri } = await this.authorizationCallbackHandler(data);
const { code, redirectUri } = await this.authResponseParser(data);
const { accessToken } = await this.getAccessToken(code, redirectUri);
try {
@ -149,7 +138,9 @@ export default class FacebookConnector implements SocialConnectorInstance<Facebo
}
};
private readonly authorizationCallbackHandler = async (parameterObject: unknown) => {
public readonly authResponseParser: AuthResponseParser<CodeWithRedirectData> = async (
parameterObject: unknown
) => {
const result = codeWithRedirectDataGuard.safeParse(parameterObject);
if (result.success) {

View file

@ -1,13 +1,13 @@
import {
ConnectorMetadata,
AuthResponseParser,
GetAuthorizationUri,
GetUserInfo,
ConnectorError,
ConnectorErrorCodes,
Connector,
SocialConnectorInstance,
GetConnectorConfig,
codeDataGuard,
CodeData,
} from '@logto/connector-types';
import { assert, conditional } from '@silverhand/essentials';
import got, { HTTPError } from 'got';
@ -29,24 +29,13 @@ import {
userInfoResponseGuard,
} from './types';
export default class GithubConnector implements SocialConnectorInstance<GithubConfig> {
public metadata: ConnectorMetadata = defaultMetadata;
private _connector?: Connector;
public get connector() {
if (!this._connector) {
throw new ConnectorError(ConnectorErrorCodes.General);
}
return this._connector;
export default class GithubConnector<T> extends SocialConnectorInstance<GithubConfig, T> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
this.metadataParser();
}
public set connector(input: Connector) {
this._connector = input;
}
constructor(public readonly getConfig: GetConnectorConfig) {}
public validateConfig(config: unknown): asserts config is GithubConfig {
const result = githubConfigGuard.safeParse(config);
@ -101,7 +90,7 @@ export default class GithubConnector implements SocialConnectorInstance<GithubCo
};
public getUserInfo: GetUserInfo = async (data) => {
const { code } = await this.authorizationCallbackHandler(data);
const { code } = await this.authResponseParser(data);
const { accessToken } = await this.getAccessToken(code);
try {
@ -141,7 +130,9 @@ export default class GithubConnector implements SocialConnectorInstance<GithubCo
}
};
private readonly authorizationCallbackHandler = async (parameterObject: unknown) => {
public readonly authResponseParser: AuthResponseParser<CodeData> = async (
parameterObject: unknown
) => {
const result = codeDataGuard.safeParse(parameterObject);
if (result.success) {

View file

@ -3,15 +3,15 @@
* https://developers.google.com/identity/protocols/oauth2/openid-connect
*/
import {
AuthResponseParser,
ConnectorError,
ConnectorErrorCodes,
GetAuthorizationUri,
GetUserInfo,
ConnectorMetadata,
Connector,
SocialConnectorInstance,
GetConnectorConfig,
codeWithRedirectDataGuard,
CodeWithRedirectData,
} from '@logto/connector-types';
import { conditional, assert } from '@silverhand/essentials';
import got, { HTTPError } from 'got';
@ -31,24 +31,13 @@ import {
userInfoResponseGuard,
} from './types';
export default class GoogleConnector implements SocialConnectorInstance<GoogleConfig> {
public metadata: ConnectorMetadata = defaultMetadata;
private _connector?: Connector;
public get connector() {
if (!this._connector) {
throw new ConnectorError(ConnectorErrorCodes.General);
}
return this._connector;
export default class GoogleConnector<T> extends SocialConnectorInstance<GoogleConfig, T> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
this.metadataParser();
}
public set connector(input: Connector) {
this._connector = input;
}
constructor(public readonly getConfig: GetConnectorConfig) {}
public validateConfig(config: unknown): asserts config is GoogleConfig {
const result = googleConfigGuard.safeParse(config);
@ -107,7 +96,7 @@ export default class GoogleConnector implements SocialConnectorInstance<GoogleCo
};
public getUserInfo: GetUserInfo = async (data) => {
const { code, redirectUri } = await this.authorizationCallbackHandler(data);
const { code, redirectUri } = await this.authResponseParser(data);
const { accessToken } = await this.getAccessToken(code, redirectUri);
try {
@ -137,7 +126,9 @@ export default class GoogleConnector implements SocialConnectorInstance<GoogleCo
}
};
private readonly authorizationCallbackHandler = async (parameterObject: unknown) => {
public readonly authResponseParser: AuthResponseParser<CodeWithRedirectData> = async (
parameterObject: unknown
) => {
const result = codeWithRedirectDataGuard.safeParse(parameterObject);
if (!result.success) {

View file

@ -4,37 +4,22 @@ import path from 'path';
import {
ConnectorError,
ConnectorErrorCodes,
ConnectorMetadata,
Connector,
EmailSendMessageFunction,
EmailSendTestMessageFunction,
EmailSendMessageByFunction,
EmailConnectorInstance,
GetConnectorConfig,
EmailMessageTypes,
} from '@logto/connector-types';
import { assert } from '@silverhand/essentials';
import { defaultMetadata } from './constant';
import { mockMailConfigGuard, MockMailConfig } from './types';
export default class MockMailConnector implements EmailConnectorInstance<MockMailConfig> {
public metadata: ConnectorMetadata = defaultMetadata;
private _connector?: Connector;
public get connector() {
if (!this._connector) {
throw new ConnectorError(ConnectorErrorCodes.General);
}
return this._connector;
export default class MockMailConnector<T> extends EmailConnectorInstance<MockMailConfig, T> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
this.metadataParser();
}
public set connector(input: Connector) {
this._connector = input;
}
constructor(public readonly getConfig: GetConnectorConfig) {}
public validateConfig(config: unknown): asserts config is MockMailConfig {
const result = mockMailConfigGuard.safeParse(config);
@ -43,25 +28,11 @@ export default class MockMailConnector implements EmailConnectorInstance<MockMai
}
}
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);
};
private readonly sendMessageBy = async (
config: MockMailConfig,
address: string,
type: keyof EmailMessageTypes,
data: EmailMessageTypes[typeof type]
public readonly sendMessageBy: EmailSendMessageByFunction<MockMailConfig> = async (
config,
address,
type,
data
) => {
const { templates } = config;
const template = templates.find((template) => template.usageType === type);

View file

@ -4,37 +4,22 @@ import path from 'path';
import {
ConnectorError,
ConnectorErrorCodes,
ConnectorMetadata,
Connector,
SmsSendMessageFunction,
SmsSendTestMessageFunction,
SmsSendMessageByFunction,
SmsConnectorInstance,
GetConnectorConfig,
SmsMessageTypes,
} from '@logto/connector-types';
import { assert } from '@silverhand/essentials';
import { defaultMetadata } from './constant';
import { mockSmsConfigGuard, MockSmsConfig } from './types';
export default class MockSmsConnector implements SmsConnectorInstance<MockSmsConfig> {
public metadata: ConnectorMetadata = defaultMetadata;
private _connector?: Connector;
public get connector() {
if (!this._connector) {
throw new ConnectorError(ConnectorErrorCodes.General);
}
return this._connector;
export default class MockSmsConnector<T> extends SmsConnectorInstance<MockSmsConfig, T> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
this.metadataParser();
}
public set connector(input: Connector) {
this._connector = input;
}
constructor(public readonly getConfig: GetConnectorConfig) {}
public validateConfig(config: unknown): asserts config is MockSmsConfig {
const result = mockSmsConfigGuard.safeParse(config);
@ -43,25 +28,11 @@ export default class MockSmsConnector implements SmsConnectorInstance<MockSmsCon
}
}
public sendMessage: SmsSendMessageFunction = async (phone, type, data) => {
const config = await this.getConfig(this.metadata.id);
this.validateConfig(config);
return this.sendMessageBy(config, phone, type, data);
};
public sendTestMessage: SmsSendTestMessageFunction = async (config, phone, type, data) => {
this.validateConfig(config);
return this.sendMessageBy(config, phone, type, data);
};
private readonly sendMessageBy = async (
config: MockSmsConfig,
phone: string,
type: keyof SmsMessageTypes,
data: SmsMessageTypes[typeof type]
public readonly sendMessageBy: SmsSendMessageByFunction<MockSmsConfig> = async (
config,
phone,
type,
data
) => {
const { templates } = config;
const template = templates.find((template) => template.usageType === type);

View file

@ -5,8 +5,6 @@ import {
ConnectorErrorCodes,
GetAuthorizationUri,
GetUserInfo,
ConnectorMetadata,
Connector,
SocialConnectorInstance,
GetConnectorConfig,
} from '@logto/connector-types';
@ -15,24 +13,13 @@ import { z } from 'zod';
import { defaultMetadata } from './constant';
import { mockSocialConfigGuard, MockSocialConfig } from './types';
export default class MockSocialConnector implements SocialConnectorInstance<MockSocialConfig> {
public metadata: ConnectorMetadata = defaultMetadata;
private _connector?: Connector;
public get connector() {
if (!this._connector) {
throw new ConnectorError(ConnectorErrorCodes.General);
}
return this._connector;
export default class MockSocialConnector<T> extends SocialConnectorInstance<MockSocialConfig, T> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
this.metadataParser();
}
public set connector(input: Connector) {
this._connector = input;
}
constructor(public readonly getConfig: GetConnectorConfig) {}
public validateConfig(config: unknown): asserts config is MockSocialConfig {
const result = mockSocialConfigGuard.safeParse(config);

View file

@ -1,13 +1,9 @@
import {
ConnectorError,
ConnectorErrorCodes,
ConnectorMetadata,
Connector,
EmailSendMessageFunction,
EmailSendTestMessageFunction,
EmailSendMessageByFunction,
EmailConnectorInstance,
GetConnectorConfig,
EmailMessageTypes,
} from '@logto/connector-types';
import { assert } from '@silverhand/essentials';
import got, { HTTPError } from 'got';
@ -22,24 +18,16 @@ import {
PublicParameters,
} from './types';
export default class SendGridMailConnector implements EmailConnectorInstance<SendGridMailConfig> {
public metadata: ConnectorMetadata = defaultMetadata;
private _connector?: Connector;
public get connector() {
if (!this._connector) {
throw new ConnectorError(ConnectorErrorCodes.General);
}
return this._connector;
export default class SendGridMailConnector<T> extends EmailConnectorInstance<
SendGridMailConfig,
T
> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
this.metadataParser();
}
public set connector(input: Connector) {
this._connector = input;
}
constructor(public readonly getConfig: GetConnectorConfig) {}
public validateConfig(config: unknown): asserts config is SendGridMailConfig {
const result = sendGridMailConfigGuard.safeParse(config);
@ -48,25 +36,11 @@ export default class SendGridMailConnector implements EmailConnectorInstance<Sen
}
}
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);
};
private readonly sendMessageBy = async (
config: SendGridMailConfig,
address: string,
type: keyof EmailMessageTypes,
data: EmailMessageTypes[typeof type]
public readonly sendMessageBy: EmailSendMessageByFunction<SendGridMailConfig> = async (
config,
address,
type,
data
) => {
const { apiKey, fromEmail, fromName, templates } = config;
const template = templates.find((template) => template.usageType === type);

View file

@ -1,13 +1,9 @@
import {
ConnectorError,
ConnectorErrorCodes,
ConnectorMetadata,
Connector,
EmailSendMessageFunction,
EmailSendTestMessageFunction,
EmailSendMessageByFunction,
EmailConnectorInstance,
GetConnectorConfig,
EmailMessageTypes,
} from '@logto/connector-types';
import { assert } from '@silverhand/essentials';
import nodemailer from 'nodemailer';
@ -16,24 +12,13 @@ import SMTPTransport from 'nodemailer/lib/smtp-transport';
import { defaultMetadata } from './constant';
import { ContextType, smtpConfigGuard, SmtpConfig } from './types';
export default class SmtpConnector implements EmailConnectorInstance<SmtpConfig> {
public metadata: ConnectorMetadata = defaultMetadata;
private _connector?: Connector;
public get connector() {
if (!this._connector) {
throw new ConnectorError(ConnectorErrorCodes.General);
}
return this._connector;
export default class SmtpConnector<T> extends EmailConnectorInstance<SmtpConfig, T> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
this.metadataParser();
}
public set connector(input: Connector) {
this._connector = input;
}
constructor(public readonly getConfig: GetConnectorConfig) {}
public validateConfig(config: unknown): asserts config is SmtpConfig {
const result = smtpConfigGuard.safeParse(config);
@ -42,25 +27,11 @@ export default class SmtpConnector implements EmailConnectorInstance<SmtpConfig>
}
}
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);
};
private readonly sendMessageBy = async (
config: SmtpConfig,
address: string,
type: keyof EmailMessageTypes,
data: EmailMessageTypes[typeof type]
public readonly sendMessageBy: EmailSendMessageByFunction<SmtpConfig> = async (
config,
address,
type,
data
) => {
const { host, port, username, password, fromEmail, replyTo, templates } = config;
const template = templates.find((template) => template.usageType === type);

View file

@ -1,13 +1,9 @@
import {
ConnectorError,
ConnectorErrorCodes,
ConnectorMetadata,
Connector,
SmsSendMessageFunction,
SmsSendTestMessageFunction,
SmsSendMessageByFunction,
SmsConnectorInstance,
GetConnectorConfig,
SmsMessageTypes,
} from '@logto/connector-types';
import { assert } from '@silverhand/essentials';
import got, { HTTPError } from 'got';
@ -15,24 +11,13 @@ import got, { HTTPError } from 'got';
import { defaultMetadata, endpoint } from './constant';
import { twilioSmsConfigGuard, TwilioSmsConfig, PublicParameters } from './types';
export default class TwilioSmsConnector implements SmsConnectorInstance<TwilioSmsConfig> {
public metadata: ConnectorMetadata = defaultMetadata;
private _connector?: Connector;
public get connector() {
if (!this._connector) {
throw new ConnectorError(ConnectorErrorCodes.General);
}
return this._connector;
export default class TwilioSmsConnector<T> extends SmsConnectorInstance<TwilioSmsConfig, T> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
this.metadataParser();
}
public set connector(input: Connector) {
this._connector = input;
}
constructor(public readonly getConfig: GetConnectorConfig) {}
public validateConfig(config: unknown): asserts config is TwilioSmsConfig {
const result = twilioSmsConfigGuard.safeParse(config);
@ -41,25 +26,11 @@ export default class TwilioSmsConnector implements SmsConnectorInstance<TwilioSm
}
}
public sendMessage: SmsSendMessageFunction = async (phone, type, data) => {
const config = await this.getConfig(this.metadata.id);
this.validateConfig(config);
return this.sendMessageBy(config, phone, type, data);
};
public sendTestMessage: SmsSendTestMessageFunction = async (config, phone, type, data) => {
this.validateConfig(config);
return this.sendMessageBy(config, phone, type, data);
};
private readonly sendMessageBy = async (
config: TwilioSmsConfig,
phone: string,
type: keyof SmsMessageTypes,
data: SmsMessageTypes[typeof type]
public readonly sendMessageBy: SmsSendMessageByFunction<TwilioSmsConfig> = async (
config,
phone,
type,
data
) => {
const { accountSID, authToken, fromMessagingServiceSID, templates } = config;
const template = templates.find((template) => template.usageType === type);

View file

@ -1,26 +1,9 @@
// FIXME: @Darcy
/* eslint-disable @typescript-eslint/consistent-type-definitions */
import type { Language } from '@logto/phrases';
import { Nullable } from '@silverhand/essentials';
import { z } from 'zod';
/**
* Connector is auto-generated in @logto/schemas according to sql file.
* As @logto/schemas depends on this repo (@logto/connector-types), we manually define Connector type again as a temporary solution.
*/
export const arbitraryObjectGuard = z.union([z.object({}).catchall(z.unknown()), z.object({})]);
export type ArbitraryObject = z.infer<typeof arbitraryObjectGuard>;
export type Connector = {
id: string;
enabled: boolean;
config: ArbitraryObject;
createdAt: number;
};
export enum ConnectorType {
Email = 'Email',
SMS = 'SMS',
@ -103,6 +86,13 @@ export type EmailSendTestMessageFunction<T = unknown> = (
payload: EmailMessageTypes[typeof type]
) => Promise<T>;
export type EmailSendMessageByFunction<T> = (
config: T,
address: string,
type: keyof EmailMessageTypes,
payload: EmailMessageTypes[typeof type]
) => Promise<unknown>;
export type SmsSendMessageFunction<T = unknown> = (
phone: string,
type: keyof SmsMessageTypes,
@ -116,45 +106,14 @@ export type SmsSendTestMessageFunction<T = unknown> = (
payload: SmsMessageTypes[typeof type]
) => Promise<T>;
export interface BaseConnector<T = unknown> {
metadata: ConnectorMetadata;
getConfig: GetConnectorConfig;
validateConfig: ValidateConfig<T>;
}
export type SmsSendMessageByFunction<T> = (
config: T,
phone: string,
type: keyof SmsMessageTypes,
payload: SmsMessageTypes[typeof type]
) => Promise<unknown>;
export interface SmsConnector<T = unknown> extends BaseConnector<T> {
sendMessage: SmsSendMessageFunction;
sendTestMessage?: SmsSendTestMessageFunction;
}
export interface SmsConnectorInstance<T = unknown> extends SmsConnector<T> {
connector: Connector;
}
export interface EmailConnector<T = unknown> extends BaseConnector<T> {
sendMessage: EmailSendMessageFunction;
sendTestMessage?: EmailSendTestMessageFunction;
}
export interface EmailConnectorInstance<T = unknown> extends EmailConnector<T> {
connector: Connector;
}
export interface SocialConnector<T = unknown> extends BaseConnector<T> {
getAuthorizationUri: GetAuthorizationUri;
getUserInfo: GetUserInfo;
}
export interface SocialConnectorInstance<T = unknown> extends SocialConnector<T> {
connector: Connector;
}
export type ConnectorInstance =
| SmsConnectorInstance
| EmailConnectorInstance
| SocialConnectorInstance;
export type ValidateConfig<T = unknown> = (config: unknown) => asserts config is T;
export type ValidateConfig<T> = (config: unknown) => asserts config is T;
export type GetAuthorizationUri = (payload: {
state: string;
@ -167,6 +126,8 @@ export type GetUserInfo = (
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(),
});

View file

@ -1,6 +1,7 @@
{
"extends": "@silverhand/ts-config/tsconfig.base",
"compilerOptions": {
"types": ["node"],
"outDir": "lib",
"baseUrl": ".",
"paths": {

View file

@ -4,15 +4,15 @@
*/
import {
ConnectorMetadata,
GetAuthorizationUri,
GetUserInfo,
ConnectorError,
ConnectorErrorCodes,
Connector,
SocialConnectorInstance,
GetConnectorConfig,
AuthResponseParser,
codeDataGuard,
CodeData,
} from '@logto/connector-types';
import { assert } from '@silverhand/essentials';
import got, { HTTPError } from 'got';
@ -35,24 +35,16 @@ import {
WechatNativeConfig,
} from './types';
export default class WechatNativeConnector implements SocialConnectorInstance<WechatNativeConfig> {
public metadata: ConnectorMetadata = defaultMetadata;
private _connector?: Connector;
public get connector() {
if (!this._connector) {
throw new ConnectorError(ConnectorErrorCodes.General);
}
return this._connector;
export default class WechatNativeConnector<T> extends SocialConnectorInstance<
WechatNativeConfig,
T
> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
this.metadataParser();
}
public set connector(input: Connector) {
this._connector = input;
}
constructor(public readonly getConfig: GetConnectorConfig) {}
public validateConfig(config: unknown): asserts config is WechatNativeConfig {
const result = wechatNativeConfigGuard.safeParse(config);
@ -108,7 +100,7 @@ export default class WechatNativeConnector implements SocialConnectorInstance<We
};
public getUserInfo: GetUserInfo = async (data) => {
const { code } = await this.authorizationCallbackHandler(data);
const { code } = await this.authResponseParser(data);
const { accessToken, openid } = await this.getAccessToken(code);
try {
@ -136,6 +128,16 @@ export default class WechatNativeConnector implements SocialConnectorInstance<We
}
};
protected authResponseParser: AuthResponseParser<CodeData> = async (parameterObject: unknown) => {
const result = codeDataGuard.safeParse(parameterObject);
if (!result.success) {
throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(parameterObject));
}
return result.data;
};
// See https://developers.weixin.qq.com/doc/oplatform/Return_codes/Return_code_descriptions_new.html
private readonly getAccessTokenErrorHandler: GetAccessTokenErrorHandler = (accessToken) => {
const { errcode, errmsg } = accessToken;
@ -176,14 +178,4 @@ export default class WechatNativeConnector implements SocialConnectorInstance<We
throw error;
};
private readonly authorizationCallbackHandler = async (parameterObject: unknown) => {
const result = codeDataGuard.safeParse(parameterObject);
if (!result.success) {
throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(parameterObject));
}
return result.data;
};
}

View file

@ -4,15 +4,15 @@
*/
import {
ConnectorMetadata,
AuthResponseParser,
GetAuthorizationUri,
GetUserInfo,
ConnectorError,
ConnectorErrorCodes,
Connector,
SocialConnectorInstance,
GetConnectorConfig,
codeDataGuard,
CodeData,
} from '@logto/connector-types';
import { assert } from '@silverhand/essentials';
import got, { HTTPError } from 'got';
@ -36,24 +36,13 @@ import {
WechatConfig,
} from './types';
export default class WechatConnector implements SocialConnectorInstance<WechatConfig> {
public metadata: ConnectorMetadata = defaultMetadata;
private _connector?: Connector;
public get connector() {
if (!this._connector) {
throw new ConnectorError(ConnectorErrorCodes.General);
}
return this._connector;
export default class WechatConnector<T> extends SocialConnectorInstance<WechatConfig, T> {
constructor(getConnectorConfig: GetConnectorConfig) {
super(getConnectorConfig);
this.metadata = defaultMetadata;
this.metadataParser();
}
public set connector(input: Connector) {
this._connector = input;
}
constructor(public readonly getConfig: GetConnectorConfig) {}
public validateConfig(config: unknown): asserts config is WechatConfig {
const result = wechatConfigGuard.safeParse(config);
@ -110,7 +99,7 @@ export default class WechatConnector implements SocialConnectorInstance<WechatCo
};
public getUserInfo: GetUserInfo = async (data) => {
const { code } = await this.authorizationCallbackHandler(data);
const { code } = await this.authResponseParser(data);
const { accessToken, openid } = await this.getAccessToken(code);
try {
@ -138,6 +127,16 @@ export default class WechatConnector implements SocialConnectorInstance<WechatCo
}
};
protected authResponseParser: AuthResponseParser<CodeData> = async (parameterObject: unknown) => {
const result = codeDataGuard.safeParse(parameterObject);
if (!result.success) {
throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(parameterObject));
}
return result.data;
};
// See https://developers.weixin.qq.com/doc/oplatform/Return_codes/Return_code_descriptions_new.html
private readonly getAccessTokenErrorHandler: GetAccessTokenErrorHandler = (accessToken) => {
const { errcode, errmsg } = accessToken;
@ -178,14 +177,4 @@ export default class WechatConnector implements SocialConnectorInstance<WechatCo
throw error;
};
private readonly authorizationCallbackHandler = async (parameterObject: unknown) => {
const result = codeDataGuard.safeParse(parameterObject);
if (!result.success) {
throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(parameterObject));
}
return result.data;
};
}

View file

@ -1,8 +1,5 @@
import { existsSync, readFileSync } from 'fs';
import path from 'path';
import { ConnectorInstance, SocialConnectorInstance } from '@logto/connector-types';
import resolvePackagePath from 'resolve-package-path';
import { Connector } from '@logto/schemas';
import envSet from '@/env-set';
import RequestError from '@/errors/RequestError';
@ -32,55 +29,7 @@ const loadConnectors = async () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { default: Builder } = await import(packageName);
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment
const instance: ConnectorInstance = new Builder(getConnectorConfig);
// 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'
);
}
const instance: ConnectorInstance = new Builder<Connector>(getConnectorConfig);
return instance;
})
@ -127,13 +76,13 @@ export const getConnectorInstanceById = async (id: string): Promise<ConnectorIns
const isSocialConnectorInstance = (
connector: ConnectorInstance
): connector is SocialConnectorInstance => {
): connector is InstanceType<typeof SocialConnectorInstance> => {
return connector.metadata.type === ConnectorType.Social;
};
export const getSocialConnectorInstanceById = async (
id: string
): Promise<SocialConnectorInstance> => {
): Promise<InstanceType<typeof SocialConnectorInstance>> => {
const connector = await getConnectorInstanceById(id);
if (!isSocialConnectorInstance(connector)) {

176
pnpm-lock.yaml generated
View file

@ -660,10 +660,18 @@ importers:
'@shopify/jest-koa-mocks': ^5.0.0
'@silverhand/eslint-config': 1.0.0-rc.2
'@silverhand/essentials': ^1.1.6
<<<<<<< HEAD
'@silverhand/ts-config': 1.0.0-rc.2
'@types/jest': ^28.1.6
eslint: ^8.21.0
jest: ^28.1.3
=======
'@silverhand/ts-config': ^0.17.0
'@types/jest': ^27.4.1
'@types/node': ^16.3.1
eslint: ^8.19.0
jest: ^27.5.1
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
lint-staged: ^13.0.0
prettier: ^2.7.1
typescript: ^4.7.4
@ -676,10 +684,18 @@ importers:
'@shopify/jest-koa-mocks': 5.0.0
'@silverhand/eslint-config': 1.0.0-rc.2_swk2g7ygmfleszo5c33j4vooni
'@silverhand/essentials': 1.1.7
<<<<<<< HEAD
'@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
=======
'@silverhand/ts-config': 0.17.0_typescript@4.6.3
'@types/jest': 27.4.1
'@types/node': 16.11.12
eslint: 8.19.0
jest: 27.5.1
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
lint-staged: 13.0.0
prettier: 2.7.1
typescript: 4.7.4
@ -2065,8 +2081,13 @@ packages:
resolution: {integrity: sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
dependencies:
<<<<<<< HEAD
'@jest/types': 28.1.3
'@types/node': 17.0.23
=======
'@jest/types': 27.5.1
'@types/node': 18.6.3
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
chalk: 4.1.2
jest-message-util: 28.1.3
jest-util: 28.1.3
@ -2081,12 +2102,21 @@ packages:
node-notifier:
optional: true
dependencies:
<<<<<<< HEAD
'@jest/console': 28.1.3
'@jest/reporters': 28.1.3
'@jest/test-result': 28.1.3
'@jest/transform': 28.1.3
'@jest/types': 28.1.3
'@types/node': 17.0.23
=======
'@jest/console': 27.5.1
'@jest/reporters': 27.5.1
'@jest/test-result': 27.5.1
'@jest/transform': 27.5.1
'@jest/types': 27.5.1
'@types/node': 18.6.3
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
ansi-escapes: 4.3.2
chalk: 4.1.2
ci-info: 3.3.2
@ -2123,12 +2153,21 @@ packages:
node-notifier:
optional: true
dependencies:
<<<<<<< HEAD
'@jest/console': 28.1.3
'@jest/reporters': 28.1.3
'@jest/test-result': 28.1.3
'@jest/transform': 28.1.3
'@jest/types': 28.1.3
'@types/node': 17.0.23
=======
'@jest/console': 27.5.1
'@jest/reporters': 27.5.1
'@jest/test-result': 27.5.1
'@jest/transform': 27.5.1
'@jest/types': 27.5.1
'@types/node': 18.6.3
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
ansi-escapes: 4.3.2
chalk: 4.1.2
ci-info: 3.3.2
@ -2161,10 +2200,18 @@ packages:
resolution: {integrity: sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
dependencies:
<<<<<<< HEAD
'@jest/fake-timers': 28.1.3
'@jest/types': 28.1.3
'@types/node': 17.0.23
jest-mock: 28.1.3
=======
'@jest/fake-timers': 27.5.1
'@jest/types': 27.5.1
'@types/node': 18.6.3
jest-mock: 27.5.1
dev: true
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
/@jest/expect-utils/28.1.3:
resolution: {integrity: sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==}
@ -2185,12 +2232,22 @@ packages:
resolution: {integrity: sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
dependencies:
<<<<<<< HEAD
'@jest/types': 28.1.3
'@sinonjs/fake-timers': 9.1.2
'@types/node': 17.0.23
jest-message-util: 28.1.3
jest-mock: 28.1.3
jest-util: 28.1.3
=======
'@jest/types': 27.5.1
'@sinonjs/fake-timers': 8.1.0
'@types/node': 18.6.3
jest-message-util: 27.5.1
jest-mock: 27.5.1
jest-util: 27.5.1
dev: true
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
/@jest/globals/28.1.3:
resolution: {integrity: sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==}
@ -2212,12 +2269,20 @@ packages:
optional: true
dependencies:
'@bcoe/v8-coverage': 0.2.3
<<<<<<< HEAD
'@jest/console': 28.1.3
'@jest/test-result': 28.1.3
'@jest/transform': 28.1.3
'@jest/types': 28.1.3
'@jridgewell/trace-mapping': 0.3.14
'@types/node': 17.0.23
=======
'@jest/console': 27.5.1
'@jest/test-result': 27.5.1
'@jest/transform': 27.5.1
'@jest/types': 27.5.1
'@types/node': 18.6.3
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
chalk: 4.1.2
collect-v8-coverage: 1.0.1
exit: 0.1.2
@ -2300,8 +2365,13 @@ packages:
'@jest/schemas': 28.1.3
'@types/istanbul-lib-coverage': 2.0.3
'@types/istanbul-reports': 3.0.1
<<<<<<< HEAD
'@types/node': 17.0.23
'@types/yargs': 17.0.10
=======
'@types/node': 18.6.3
'@types/yargs': 16.0.4
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
chalk: 4.1.2
/@jridgewell/resolve-uri/3.0.5:
@ -4941,7 +5011,12 @@ packages:
/@types/graceful-fs/4.1.5:
resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==}
dependencies:
<<<<<<< HEAD
'@types/node': 17.0.23
=======
'@types/node': 18.6.3
dev: true
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
/@types/hast/2.3.4:
resolution: {integrity: sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==}
@ -5125,6 +5200,9 @@ packages:
/@types/node/17.0.23:
resolution: {integrity: sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==}
/@types/node/18.6.3:
resolution: {integrity: sha512-6qKpDtoaYLM+5+AFChLhHermMQxc3TOEFIDzrZLPRGHPrLEwqFkkT5Kx3ju05g6X7uDPazz3jHbKPX0KzCjntg==}
/@types/nodemailer/6.4.4:
resolution: {integrity: sha512-Ksw4t7iliXeYGvIQcSIgWQ5BLuC/mljIEbjf615svhZL10PE9t+ei8O9gDaD3FPCasUJn9KTLwz2JFJyiiyuqw==}
dependencies:
@ -9363,11 +9441,18 @@ packages:
resolution: {integrity: sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
dependencies:
<<<<<<< HEAD
'@jest/environment': 28.1.3
'@jest/expect': 28.1.3
'@jest/test-result': 28.1.3
'@jest/types': 28.1.3
'@types/node': 17.0.23
=======
'@jest/environment': 27.5.1
'@jest/test-result': 27.5.1
'@jest/types': 27.5.1
'@types/node': 18.6.3
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
chalk: 4.1.2
co: 4.6.0
dedent: 0.7.0
@ -9666,6 +9751,7 @@ packages:
resolution: {integrity: sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
dependencies:
<<<<<<< HEAD
chalk: 4.1.2
diff-sequences: 28.1.1
jest-get-type: 28.0.2
@ -9699,6 +9785,15 @@ packages:
jest-mock: 28.1.3
jest-util: 28.1.3
jsdom: 19.0.0
=======
'@jest/environment': 27.5.1
'@jest/fake-timers': 27.5.1
'@jest/types': 27.5.1
'@types/node': 18.6.3
jest-mock: 27.5.1
jest-util: 27.5.1
jsdom: 16.7.0
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
transitivePeerDependencies:
- bufferutil
- canvas
@ -9710,12 +9805,22 @@ packages:
resolution: {integrity: sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
dependencies:
<<<<<<< HEAD
'@jest/environment': 28.1.3
'@jest/fake-timers': 28.1.3
'@jest/types': 28.1.3
'@types/node': 17.0.23
jest-mock: 28.1.3
jest-util: 28.1.3
=======
'@jest/environment': 27.5.1
'@jest/fake-timers': 27.5.1
'@jest/types': 27.5.1
'@types/node': 18.6.3
jest-mock: 27.5.1
jest-util: 27.5.1
dev: true
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
/jest-get-type/28.0.2:
resolution: {integrity: sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==}
@ -9727,7 +9832,7 @@ packages:
dependencies:
'@jest/types': 28.1.3
'@types/graceful-fs': 4.1.5
'@types/node': 17.0.23
'@types/node': 18.6.3
anymatch: 3.1.2
fb-watchman: 2.0.1
graceful-fs: 4.2.9
@ -9738,6 +9843,35 @@ packages:
walker: 1.0.8
optionalDependencies:
fsevents: 2.3.2
<<<<<<< HEAD
=======
dev: true
/jest-jasmine2/27.5.1:
resolution: {integrity: sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
dependencies:
'@jest/environment': 27.5.1
'@jest/source-map': 27.5.1
'@jest/test-result': 27.5.1
'@jest/types': 27.5.1
'@types/node': 18.6.3
chalk: 4.1.2
co: 4.6.0
expect: 27.5.1
is-generator-fn: 2.1.0
jest-each: 27.5.1
jest-matcher-utils: 27.5.1
jest-message-util: 27.5.1
jest-runtime: 27.5.1
jest-snapshot: 27.5.1
jest-util: 27.5.1
pretty-format: 27.5.1
throat: 6.0.1
transitivePeerDependencies:
- supports-color
dev: true
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
/jest-leak-detector/28.1.3:
resolution: {integrity: sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==}
@ -9776,8 +9910,14 @@ packages:
resolution: {integrity: sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
dependencies:
<<<<<<< HEAD
'@jest/types': 28.1.3
'@types/node': 17.0.23
=======
'@jest/types': 27.5.1
'@types/node': 18.6.3
dev: true
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
/jest-pnp-resolver/1.2.2_jest-resolve@28.1.3:
resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==}
@ -9821,12 +9961,21 @@ packages:
resolution: {integrity: sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
dependencies:
<<<<<<< HEAD
'@jest/console': 28.1.3
'@jest/environment': 28.1.3
'@jest/test-result': 28.1.3
'@jest/transform': 28.1.3
'@jest/types': 28.1.3
'@types/node': 17.0.23
=======
'@jest/console': 27.5.1
'@jest/environment': 27.5.1
'@jest/test-result': 27.5.1
'@jest/transform': 27.5.1
'@jest/types': 27.5.1
'@types/node': 18.6.3
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
chalk: 4.1.2
emittery: 0.10.2
graceful-fs: 4.2.9
@ -9873,6 +10022,18 @@ packages:
strip-bom: 4.0.0
transitivePeerDependencies:
- supports-color
<<<<<<< HEAD
=======
dev: true
/jest-serializer/27.5.1:
resolution: {integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
dependencies:
'@types/node': 18.6.3
graceful-fs: 4.2.9
dev: true
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
/jest-snapshot/28.1.3:
resolution: {integrity: sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==}
@ -9921,8 +10082,13 @@ packages:
resolution: {integrity: sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
dependencies:
<<<<<<< HEAD
'@jest/types': 28.1.3
'@types/node': 17.0.23
=======
'@jest/types': 27.5.1
'@types/node': 18.6.3
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
chalk: 4.1.2
ci-info: 3.3.2
graceful-fs: 4.2.9
@ -9943,9 +10109,15 @@ packages:
resolution: {integrity: sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
dependencies:
<<<<<<< HEAD
'@jest/test-result': 28.1.3
'@jest/types': 28.1.3
'@types/node': 17.0.23
=======
'@jest/test-result': 27.5.1
'@jest/types': 27.5.1
'@types/node': 18.6.3
>>>>>>> f04cfcf3 (refactor(connector): refactor connector)
ansi-escapes: 4.3.2
chalk: 4.1.2
emittery: 0.10.2
@ -9956,7 +10128,7 @@ packages:
resolution: {integrity: sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
dependencies:
'@types/node': 17.0.23
'@types/node': 18.6.3
merge-stream: 2.0.0
supports-color: 8.1.1