From 2219d880596e4cd39096c207e2537e9bb67deb05 Mon Sep 17 00:00:00 2001 From: Kiril Ivonchik Date: Tue, 19 Aug 2025 11:22:42 +0200 Subject: [PATCH 01/10] Use native http client --- package.json | 5 +- src/core/http-client.ts | 109 --------------------------- src/core/index.ts | 4 + src/core/linked-api-http-client.ts | 114 +++++++++++++++++++++++++++++ src/core/workflow-executor.ts | 2 +- src/index.ts | 6 +- src/types/http-client.ts | 13 ++++ src/types/index.ts | 1 + 8 files changed, 138 insertions(+), 116 deletions(-) delete mode 100644 src/core/http-client.ts create mode 100644 src/core/index.ts create mode 100644 src/core/linked-api-http-client.ts create mode 100644 src/types/http-client.ts diff --git a/package.json b/package.json index 77555b3..9307d6e 100644 --- a/package.json +++ b/package.json @@ -57,8 +57,5 @@ "ts-node": "^10.9.2", "typescript": "^5.8.3", "typescript-eslint": "^8.38.0" - }, - "dependencies": { - "axios": "^1.6.8" } -} \ No newline at end of file +} diff --git a/src/core/http-client.ts b/src/core/http-client.ts deleted file mode 100644 index 38329cd..0000000 --- a/src/core/http-client.ts +++ /dev/null @@ -1,109 +0,0 @@ -import type { - AxiosInstance, - AxiosRequestConfig, - AxiosResponse, - CreateAxiosDefaults, - InternalAxiosRequestConfig, -} from "axios"; -import axios from "axios"; -import type { TLinkedApiResponse } from "../types/responses"; -import { LinkedApiError } from "../types/errors"; - -export interface HttpClientConfig { - headers: Record; -} - -export class HttpClient { - private client: AxiosInstance; - - constructor(config: HttpClientConfig) { - this.client = axios.create({ - baseURL: "https://api.linkedapi.io", - headers: { - "Content-Type": "application/json", - ...config.headers, - }, - } as CreateAxiosDefaults); - - this.setupInterceptors(); - } - - private setupInterceptors(): void { - this.client.interceptors.request.use( - (config: InternalAxiosRequestConfig) => { - return config; - }, - (error: unknown) => { - return Promise.reject(error); - }, - ); - - this.client.interceptors.response.use( - (response: AxiosResponse) => { - return response; - }, - (error: unknown) => { - if (axios.isAxiosError(error)) { - if (error.response) { - if (error.response.data?.error) { - throw new LinkedApiError( - error.response.data.error.type, - error.response.data.error.message, - error.response.data, - ); - } else { - throw new LinkedApiError( - `HTTP ${error.response.status}: ${error.response.statusText}`, - "HTTP_ERROR", - { - status: error.response.status, - statusText: error.response.statusText, - url: `${error.config?.baseURL ?? ""}${error.config?.url ?? ""}`, - }, - ); - } - } else if (error.request) { - throw new LinkedApiError( - "Network error: No response received", - "NETWORK_ERROR", - error.request, - ); - } else { - throw new LinkedApiError( - `Request error: ${error.message}`, - "REQUEST_ERROR", - { message: error.message }, - ); - } - } else { - throw new LinkedApiError( - "Unknown error occurred", - "UNKNOWN_ERROR", - error, - ); - } - }, - ); - } - - public async get( - url: string, - config?: AxiosRequestConfig, - ): Promise> { - const response = await this.client.get>(url, config); - return response.data; - } - - public async post( - url: string, - data?: unknown, - config?: AxiosRequestConfig, - ): Promise> { - const response = await this.client.post>( - url, - data, - config, - ); - return response.data; - } -} diff --git a/src/core/index.ts b/src/core/index.ts new file mode 100644 index 0000000..98ccc82 --- /dev/null +++ b/src/core/index.ts @@ -0,0 +1,4 @@ +export * from "./linked-api-http-client"; +export * from "./workflow-executor"; +export * from "./workflow-handler"; +export * from "./workflow-restoration"; diff --git a/src/core/linked-api-http-client.ts b/src/core/linked-api-http-client.ts new file mode 100644 index 0000000..1b6ed48 --- /dev/null +++ b/src/core/linked-api-http-client.ts @@ -0,0 +1,114 @@ +import { TLinkedApiResponse, LinkedApiError, HttpClient } from "../types"; + +export interface HttpClientConfig { + headers: Record; +} +export class LinkedApiHttpClient extends HttpClient { + private readonly baseUrl: string; + private readonly headers: Record; + + constructor(config: HttpClientConfig) { + super(); + this.baseUrl = "https://api.linkedapi.io"; + this.headers = { + "Content-Type": "application/json", + ...config.headers, + }; + } + + private async handleResponse( + response: Response, + ): Promise> { + if (response.ok) { + return (await response.json()) as TLinkedApiResponse; + } + + try { + const errorData = await response.json(); + if (errorData?.error) { + throw new LinkedApiError( + errorData.error.type, + errorData.error.message, + errorData, + ); + } else { + throw new LinkedApiError( + `HTTP ${response.status}: ${response.statusText}`, + "HTTP_ERROR", + { + status: response.status, + statusText: response.statusText, + url: response.url, + }, + ); + } + } catch (e) { + if (e instanceof LinkedApiError) { + throw e; + } + throw new LinkedApiError( + `HTTP ${response.status}: ${response.statusText}`, + "HTTP_ERROR", + { + status: response.status, + statusText: response.statusText, + url: response.url, + }, + ); + } + } + + private handleError(error: unknown): never { + if (error instanceof LinkedApiError) { + throw error; + } + + if (error instanceof TypeError && error.message === "Failed to fetch") { + throw new LinkedApiError( + "Network error: No response received", + "NETWORK_ERROR", + error, + ); + } + + throw new LinkedApiError( + `Request error: ${(error as Error).message}`, + "REQUEST_ERROR", + { message: (error as Error).message }, + ); + } + + public async get( + url: string, + config?: RequestInit, + ): Promise> { + try { + const response = await fetch(`${this.baseUrl}${url}`, { + ...config, + method: "GET", + headers: this.headers, + }); + return this.handleResponse(response); + } catch (error) { + this.handleError(error); + } + } + + public async post( + url: string, + data?: unknown, + config?: RequestInit, + ): Promise> { + try { + const response = await fetch(`${this.baseUrl}${url}`, { + ...config, + method: "POST", + headers: this.headers, + body: data ? JSON.stringify(data) : null, + }); + return this.handleResponse(response); + } catch (error) { + this.handleError(error); + } + } +} diff --git a/src/core/workflow-executor.ts b/src/core/workflow-executor.ts index 15b7f4a..e1690b9 100644 --- a/src/core/workflow-executor.ts +++ b/src/core/workflow-executor.ts @@ -1,4 +1,4 @@ -import type { HttpClient } from "./http-client"; +import type { HttpClient } from "../types"; import type { TWorkflowDefinition, TWorkflowResponse, diff --git a/src/index.ts b/src/index.ts index 54885c5..fdb574f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import type { TLinkedApiConfig } from "./types/config"; import type { TWorkflowDefinition, TWorkflowResponse } from "./types/workflows"; import type { TLinkedApiResponse } from "./types/responses"; -import { HttpClient } from "./core/http-client"; +import { LinkedApiHttpClient } from "./core/linked-api-http-client"; import { WorkflowExecutor } from "./core/workflow-executor"; import { WorkflowHandler } from "./core/workflow-handler"; import type { @@ -68,6 +68,7 @@ import { TApiUsageStatsParams, TApiUsageStatsResponse, TApiUsageAction, + HttpClient, } from "./types"; import { TRestoreResultType, @@ -106,6 +107,7 @@ import { class LinkedApi { private readonly httpClient: HttpClient; private readonly workflowExecutor: WorkflowExecutor; + /** * Initialize LinkedApi client with your API tokens. * @@ -114,7 +116,7 @@ class LinkedApi { */ constructor(config: TLinkedApiConfig) { - this.httpClient = new HttpClient({ + this.httpClient = new LinkedApiHttpClient({ headers: { "linked-api-token": config.linkedApiToken, "identification-token": config.identificationToken, diff --git a/src/types/http-client.ts b/src/types/http-client.ts new file mode 100644 index 0000000..98ff5f4 --- /dev/null +++ b/src/types/http-client.ts @@ -0,0 +1,13 @@ +import type { TLinkedApiResponse } from "../types/responses"; + +export abstract class HttpClient { + public abstract get( + url: string, + config?: RequestInit, + ): Promise>; + public abstract post( + url: string, + data?: unknown, + config?: RequestInit, + ): Promise>; +} diff --git a/src/types/index.ts b/src/types/index.ts index 9f2d7ad..f34c455 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -4,3 +4,4 @@ export * from "./params"; export * from "./responses"; export * from "./workflows"; export * from "./actions/index"; +export * from "./http-client"; From c42f9396ed37e6b2ce5f185b3fa29d724d41c6fb Mon Sep 17 00:00:00 2001 From: Kiril Ivonchik Date: Tue, 19 Aug 2025 13:00:25 +0200 Subject: [PATCH 02/10] Custom http client init --- src/core/linked-api-http-client.ts | 22 ++++-------------- src/index.ts | 37 +++++++++++++++++++----------- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/src/core/linked-api-http-client.ts b/src/core/linked-api-http-client.ts index 1b6ed48..a3d8a31 100644 --- a/src/core/linked-api-http-client.ts +++ b/src/core/linked-api-http-client.ts @@ -25,23 +25,11 @@ export class LinkedApiHttpClient extends HttpClient { try { const errorData = await response.json(); - if (errorData?.error) { - throw new LinkedApiError( - errorData.error.type, - errorData.error.message, - errorData, - ); - } else { - throw new LinkedApiError( - `HTTP ${response.status}: ${response.statusText}`, - "HTTP_ERROR", - { - status: response.status, - statusText: response.statusText, - url: response.url, - }, - ); - } + throw new LinkedApiError( + `HTTP ${response.status}: ${response.statusText}`, + "HTTP_ERROR", + errorData, + ); } catch (e) { if (e instanceof LinkedApiError) { throw e; diff --git a/src/index.ts b/src/index.ts index fdb574f..becf4df 100644 --- a/src/index.ts +++ b/src/index.ts @@ -114,19 +114,30 @@ class LinkedApi { * @param config - Configuration object containing API tokens and optional settings * @returns LinkedApi instance with access to LinkedIn automation features */ - - constructor(config: TLinkedApiConfig) { - this.httpClient = new LinkedApiHttpClient({ - headers: { - "linked-api-token": config.linkedApiToken, - "identification-token": config.identificationToken, - }, - }); - this.workflowExecutor = new WorkflowExecutor({ - httpClient: this.httpClient, - apiPath: "/workflows", - workflowTimeout: config.workflowTimeout ?? 24 * 60 * 60 * 1000, - }); + constructor(config: TLinkedApiConfig); + constructor(httpClient: HttpClient); + constructor(configOrClient: TLinkedApiConfig | HttpClient) { + if (configOrClient instanceof HttpClient) { + this.httpClient = configOrClient; + this.workflowExecutor = new WorkflowExecutor({ + httpClient: this.httpClient, + apiPath: "/workflows", + workflowTimeout: 24 * 60 * 60 * 1000, + }); + } else { + const config = configOrClient as TLinkedApiConfig; + this.httpClient = new LinkedApiHttpClient({ + headers: { + "linked-api-token": config.linkedApiToken, + "identification-token": config.identificationToken, + }, + }); + this.workflowExecutor = new WorkflowExecutor({ + httpClient: this.httpClient, + apiPath: "/workflows", + workflowTimeout: config.workflowTimeout ?? 24 * 60 * 60 * 1000, + }); + } } /** From 4d46a69a7167bca5a4421efcd6cafaa762a5c9fb Mon Sep 17 00:00:00 2001 From: Kiril Ivonchik Date: Tue, 19 Aug 2025 13:42:25 +0200 Subject: [PATCH 03/10] Enum like syntax for restore functionName --- README.md | 14 -- examples/restore-workflow.ts | 6 +- src/core/linked-api-http-client.ts | 19 ++- src/core/workflow-restoration.ts | 212 +++++++++++++++-------------- src/index.ts | 53 +------- src/types/config.ts | 1 - 6 files changed, 131 insertions(+), 174 deletions(-) diff --git a/README.md b/README.md index 8380603..e96ec0c 100644 --- a/README.md +++ b/README.md @@ -111,20 +111,6 @@ const result = await workflow.result(); --- -### `getWorkflowResult(workflowId)` - -Retrieve the result of a previously started workflow by its ID. - -- **Parameters:** `string` - Workflow ID -- **Returns:** `Promise` - Workflow response with completion data -- **Documentation:** [Executing Workflows](https://linkedapi.io/docs/executing-workflows/) - -```typescript -const result = await linkedapi.getWorkflowResult("workflow-id-123"); -``` - ---- - ### `restoreWorkflow(workflowId, functionName)` Restore a WorkflowHandler for a previously started workflow using its ID and function name with type safety. diff --git a/examples/restore-workflow.ts b/examples/restore-workflow.ts index 9cdf5fb..56f2eff 100644 --- a/examples/restore-workflow.ts +++ b/examples/restore-workflow.ts @@ -1,4 +1,4 @@ -import LinkedApi from 'linkedapi-node'; +import LinkedApi, { FUNCTION_NAME } from 'linkedapi-node'; async function example(): Promise { // First run @@ -25,13 +25,13 @@ async function example(): Promise { const restoredHandler = await linkedapiAfterRestart.restoreWorkflow( savedWorkflowId, - "fetchPerson" as const, + FUNCTION_NAME.fetchPerson, ); // Or if you want to restore a raw workflow (for executeCustomWorkflow) const rawHandler = await linkedapiAfterRestart.restoreWorkflow( savedWorkflowId, - "executeCustomWorkflow" as const, + FUNCTION_NAME.executeCustomWorkflow, ); console.log("Restoration started: ", restoredHandler.workflowId); diff --git a/src/core/linked-api-http-client.ts b/src/core/linked-api-http-client.ts index a3d8a31..816c2f4 100644 --- a/src/core/linked-api-http-client.ts +++ b/src/core/linked-api-http-client.ts @@ -1,18 +1,25 @@ -import { TLinkedApiResponse, LinkedApiError, HttpClient } from "../types"; +import { + TLinkedApiResponse, + LinkedApiError, + HttpClient, + TLinkedApiConfig, +} from "../types"; -export interface HttpClientConfig { - headers: Record; +export function buildLinkedApiHttpClient(config: TLinkedApiConfig): HttpClient { + return new LinkedApiHttpClient(config); } -export class LinkedApiHttpClient extends HttpClient { + +class LinkedApiHttpClient extends HttpClient { private readonly baseUrl: string; private readonly headers: Record; - constructor(config: HttpClientConfig) { + constructor(config: TLinkedApiConfig) { super(); this.baseUrl = "https://api.linkedapi.io"; this.headers = { "Content-Type": "application/json", - ...config.headers, + "linked-api-token": config.linkedApiToken, + "identification-token": config.identificationToken, }; } diff --git a/src/core/workflow-restoration.ts b/src/core/workflow-restoration.ts index e0e666e..39fa387 100644 --- a/src/core/workflow-restoration.ts +++ b/src/core/workflow-restoration.ts @@ -7,7 +7,7 @@ import type { TWorkflowResponse } from "../types/workflows"; import type { BaseMapper } from "../mappers/base-mapper.abstract"; export type TRestoreResultType = - T extends "executeCustomWorkflow" + T extends typeof FUNCTION_NAME.executeCustomWorkflow ? TWorkflowResponse : TRestoreMapperReturnType extends BaseMapper ? R @@ -46,90 +46,94 @@ import { TBaseActionParams, } from "../types"; -export type TRestoreMapperReturnType = T extends "fetchPerson" - ? FetchPersonMapper - : T extends "fetchCompany" - ? FetchCompanyMapper - : T extends "salesNavigatorFetchCompany" - ? NvFetchCompanyMapper - : T extends "salesNavigatorFetchPerson" - ? NvFetchPersonMapper - : T extends "fetchPost" - ? SimpleWorkflowMapper - : T extends "searchCompanies" - ? SearchCompaniesMapper - : T extends "salesNavigatorSearchCompanies" - ? NvSearchCompaniesMapper - : T extends "searchPeople" - ? SearchPeopleMapper - : T extends "salesNavigatorSearchPeople" - ? NvSearchPeopleMapper - : T extends "sendMessage" - ? VoidWorkflowMapper - : T extends "syncConversation" - ? VoidWorkflowMapper - : T extends "salesNavigatorSendMessage" - ? VoidWorkflowMapper - : T extends "salesNavigatorSyncConversation" - ? VoidWorkflowMapper - : T extends "sendConnectionRequest" - ? VoidWorkflowMapper - : T extends "checkConnectionStatus" - ? SimpleWorkflowMapper< - TCheckConnectionStatusParams, - TCheckConnectionStatusResult - > - : T extends "withdrawConnectionRequest" - ? VoidWorkflowMapper - : T extends "retrievePendingRequests" - ? RetrievePendingRequestsMapper - : T extends "retrieveConnections" - ? RetrieveConnectionsMapper - : T extends "removeConnection" - ? VoidWorkflowMapper - : T extends "reactToPost" - ? VoidWorkflowMapper - : T extends "commentOnPost" - ? VoidWorkflowMapper - : T extends "retrieveSSI" - ? SimpleWorkflowMapper< - TBaseActionParams, - TRetrieveSSIResult - > - : T extends "retrievePerformance" +export type TRestoreMapperReturnType = + T extends typeof FUNCTION_NAME.fetchPerson + ? FetchPersonMapper + : T extends typeof FUNCTION_NAME.fetchCompany + ? FetchCompanyMapper + : T extends typeof FUNCTION_NAME.salesNavigatorFetchCompany + ? NvFetchCompanyMapper + : T extends typeof FUNCTION_NAME.salesNavigatorFetchPerson + ? NvFetchPersonMapper + : T extends typeof FUNCTION_NAME.fetchPost + ? SimpleWorkflowMapper + : T extends typeof FUNCTION_NAME.searchCompanies + ? SearchCompaniesMapper + : T extends typeof FUNCTION_NAME.salesNavigatorSearchCompanies + ? NvSearchCompaniesMapper + : T extends typeof FUNCTION_NAME.searchPeople + ? SearchPeopleMapper + : T extends typeof FUNCTION_NAME.salesNavigatorSearchPeople + ? NvSearchPeopleMapper + : T extends typeof FUNCTION_NAME.sendMessage + ? VoidWorkflowMapper + : T extends typeof FUNCTION_NAME.syncConversation + ? VoidWorkflowMapper + : T extends typeof FUNCTION_NAME.salesNavigatorSendMessage + ? VoidWorkflowMapper + : T extends typeof FUNCTION_NAME.salesNavigatorSyncConversation + ? VoidWorkflowMapper + : T extends typeof FUNCTION_NAME.sendConnectionRequest + ? VoidWorkflowMapper + : T extends typeof FUNCTION_NAME.checkConnectionStatus + ? SimpleWorkflowMapper< + TCheckConnectionStatusParams, + TCheckConnectionStatusResult + > + : T extends typeof FUNCTION_NAME.withdrawConnectionRequest + ? VoidWorkflowMapper + : T extends typeof FUNCTION_NAME.retrievePendingRequests + ? RetrievePendingRequestsMapper + : T extends typeof FUNCTION_NAME.retrieveConnections + ? RetrieveConnectionsMapper + : T extends typeof FUNCTION_NAME.removeConnection + ? VoidWorkflowMapper + : T extends typeof FUNCTION_NAME.reactToPost + ? VoidWorkflowMapper + : T extends typeof FUNCTION_NAME.commentOnPost + ? VoidWorkflowMapper + : T extends typeof FUNCTION_NAME.retrieveSSI ? SimpleWorkflowMapper< TBaseActionParams, - TRetrievePerformanceResult + TRetrieveSSIResult > - : T extends "executeCustomWorkflow" - ? null // Special case: no mapper needed, will return raw TWorkflowResponse - : never; + : T extends typeof FUNCTION_NAME.retrievePerformance + ? SimpleWorkflowMapper< + TBaseActionParams, + TRetrievePerformanceResult + > + : T extends typeof FUNCTION_NAME.executeCustomWorkflow + ? null // Special case: no mapper needed, will return raw TWorkflowResponse + : never; +export const FUNCTION_NAME = { + fetchPerson: "fetchPerson", + fetchCompany: "fetchCompany", + salesNavigatorFetchCompany: "salesNavigatorFetchCompany", + salesNavigatorFetchPerson: "salesNavigatorFetchPerson", + fetchPost: "fetchPost", + searchCompanies: "searchCompanies", + salesNavigatorSearchCompanies: "salesNavigatorSearchCompanies", + searchPeople: "searchPeople", + salesNavigatorSearchPeople: "salesNavigatorSearchPeople", + sendMessage: "sendMessage", + syncConversation: "syncConversation", + salesNavigatorSendMessage: "salesNavigatorSendMessage", + salesNavigatorSyncConversation: "salesNavigatorSyncConversation", + sendConnectionRequest: "sendConnectionRequest", + checkConnectionStatus: "checkConnectionStatus", + withdrawConnectionRequest: "withdrawConnectionRequest", + retrievePendingRequests: "retrievePendingRequests", + retrieveConnections: "retrieveConnections", + removeConnection: "removeConnection", + reactToPost: "reactToPost", + commentOnPost: "commentOnPost", + retrieveSSI: "retrieveSSI", + retrievePerformance: "retrievePerformance", + executeCustomWorkflow: "executeCustomWorkflow", // Special case: no mapper needed, will return raw TWorkflowResponse +} as const; export type TSupportedFunctionName = - | "fetchPerson" - | "fetchCompany" - | "salesNavigatorFetchCompany" - | "salesNavigatorFetchPerson" - | "fetchPost" - | "searchCompanies" - | "salesNavigatorSearchCompanies" - | "searchPeople" - | "salesNavigatorSearchPeople" - | "sendMessage" - | "syncConversation" - | "salesNavigatorSendMessage" - | "salesNavigatorSyncConversation" - | "sendConnectionRequest" - | "checkConnectionStatus" - | "withdrawConnectionRequest" - | "retrievePendingRequests" - | "retrieveConnections" - | "removeConnection" - | "reactToPost" - | "commentOnPost" - | "retrieveSSI" - | "retrievePerformance" - | "executeCustomWorkflow"; // Special case: no mapper needed, will return raw TWorkflowResponse + (typeof FUNCTION_NAME)[keyof typeof FUNCTION_NAME]; /** * Internal function to restore a mapper from a function name. @@ -139,62 +143,62 @@ export function createMapperFromFunctionName( functionName: T, ): TRestoreMapperReturnType { switch (functionName) { - case "fetchPerson": { + case FUNCTION_NAME.fetchPerson: { return new FetchPersonMapper() as TRestoreMapperReturnType; } - case "fetchCompany": { + case FUNCTION_NAME.fetchCompany: { return new FetchCompanyMapper() as TRestoreMapperReturnType; } - case "salesNavigatorFetchCompany": { + case FUNCTION_NAME.salesNavigatorFetchCompany: { return new NvFetchCompanyMapper() as TRestoreMapperReturnType; } - case "salesNavigatorFetchPerson": { + case FUNCTION_NAME.salesNavigatorFetchPerson: { return new NvFetchPersonMapper() as TRestoreMapperReturnType; } - case "fetchPost": { + case FUNCTION_NAME.fetchPost: { return new SimpleWorkflowMapper({ actionType: "st.openPost", defaultParams: { basicInfo: true }, }) as TRestoreMapperReturnType; } - case "searchCompanies": { + case FUNCTION_NAME.searchCompanies: { return new SearchCompaniesMapper() as TRestoreMapperReturnType; } - case "salesNavigatorSearchCompanies": { + case FUNCTION_NAME.salesNavigatorSearchCompanies: { return new NvSearchCompaniesMapper() as TRestoreMapperReturnType; } - case "searchPeople": { + case FUNCTION_NAME.searchPeople: { return new SearchPeopleMapper() as TRestoreMapperReturnType; } - case "salesNavigatorSearchPeople": { + case FUNCTION_NAME.salesNavigatorSearchPeople: { return new NvSearchPeopleMapper() as TRestoreMapperReturnType; } - case "sendMessage": { + case FUNCTION_NAME.sendMessage: { return new VoidWorkflowMapper( "st.sendMessage", ) as TRestoreMapperReturnType; } - case "syncConversation": { + case FUNCTION_NAME.syncConversation: { return new VoidWorkflowMapper( "st.syncConversation", ) as TRestoreMapperReturnType; } - case "salesNavigatorSendMessage": { + case FUNCTION_NAME.salesNavigatorSendMessage: { return new VoidWorkflowMapper( "nv.sendMessage", ) as TRestoreMapperReturnType; } - case "salesNavigatorSyncConversation": { + case FUNCTION_NAME.salesNavigatorSyncConversation: { return new VoidWorkflowMapper( "nv.syncConversation", ) as TRestoreMapperReturnType; } - case "sendConnectionRequest": { + case FUNCTION_NAME.sendConnectionRequest: { return new VoidWorkflowMapper( "st.sendConnectionRequest", ) as TRestoreMapperReturnType; } - case "checkConnectionStatus": { + case FUNCTION_NAME.checkConnectionStatus: { return new SimpleWorkflowMapper< TCheckConnectionStatusParams, TCheckConnectionStatusResult @@ -202,38 +206,38 @@ export function createMapperFromFunctionName( actionType: "st.checkConnectionStatus", }) as TRestoreMapperReturnType; } - case "withdrawConnectionRequest": { + case FUNCTION_NAME.withdrawConnectionRequest: { return new VoidWorkflowMapper( "st.withdrawConnectionRequest", ) as TRestoreMapperReturnType; } - case "retrievePendingRequests": { + case FUNCTION_NAME.retrievePendingRequests: { return new RetrievePendingRequestsMapper() as TRestoreMapperReturnType; } - case "retrieveConnections": { + case FUNCTION_NAME.retrieveConnections: { return new RetrieveConnectionsMapper() as TRestoreMapperReturnType; } - case "removeConnection": { + case FUNCTION_NAME.removeConnection: { return new VoidWorkflowMapper( "st.removeConnection", ) as TRestoreMapperReturnType; } - case "reactToPost": { + case FUNCTION_NAME.reactToPost: { return new VoidWorkflowMapper( "st.reactToPost", ) as TRestoreMapperReturnType; } - case "commentOnPost": { + case FUNCTION_NAME.commentOnPost: { return new VoidWorkflowMapper( "st.commentOnPost", ) as TRestoreMapperReturnType; } - case "retrieveSSI": { + case FUNCTION_NAME.retrieveSSI: { return new SimpleWorkflowMapper({ actionType: "st.retrieveSSI", }) as TRestoreMapperReturnType; } - case "retrievePerformance": { + case FUNCTION_NAME.retrievePerformance: { return new SimpleWorkflowMapper< TBaseActionParams, TRetrievePerformanceResult @@ -241,7 +245,7 @@ export function createMapperFromFunctionName( actionType: "st.retrievePerformance", }) as TRestoreMapperReturnType; } - case "executeCustomWorkflow": { + case FUNCTION_NAME.executeCustomWorkflow: { return null as TRestoreMapperReturnType; } default: diff --git a/src/index.ts b/src/index.ts index becf4df..f5de71e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import type { TLinkedApiConfig } from "./types/config"; import type { TWorkflowDefinition, TWorkflowResponse } from "./types/workflows"; import type { TLinkedApiResponse } from "./types/responses"; -import { LinkedApiHttpClient } from "./core/linked-api-http-client"; +import { buildLinkedApiHttpClient } from "./core/linked-api-http-client"; import { WorkflowExecutor } from "./core/workflow-executor"; import { WorkflowHandler } from "./core/workflow-handler"; import type { @@ -119,25 +119,14 @@ class LinkedApi { constructor(configOrClient: TLinkedApiConfig | HttpClient) { if (configOrClient instanceof HttpClient) { this.httpClient = configOrClient; - this.workflowExecutor = new WorkflowExecutor({ - httpClient: this.httpClient, - apiPath: "/workflows", - workflowTimeout: 24 * 60 * 60 * 1000, - }); } else { - const config = configOrClient as TLinkedApiConfig; - this.httpClient = new LinkedApiHttpClient({ - headers: { - "linked-api-token": config.linkedApiToken, - "identification-token": config.identificationToken, - }, - }); - this.workflowExecutor = new WorkflowExecutor({ - httpClient: this.httpClient, - apiPath: "/workflows", - workflowTimeout: config.workflowTimeout ?? 24 * 60 * 60 * 1000, - }); + this.httpClient = buildLinkedApiHttpClient(configOrClient); } + this.workflowExecutor = new WorkflowExecutor({ + httpClient: this.httpClient, + apiPath: "/workflows", + workflowTimeout: 24 * 60 * 60 * 1000, + }); } /** @@ -191,34 +180,6 @@ class LinkedApi { ); } - /** - * Get the result of a workflow by its ID. - * - * This method retrieves the result of a previously started workflow using its workflow ID. - * Useful for checking workflow status and retrieving results asynchronously. - * - * @param workflowId - The unique identifier of the workflow - * @returns Promise resolving to the workflow response containing completion data or failure information - * - * @see {@link https://linkedapi.io/docs/executing-workflows/ Executing Workflows Documentation} - * - * @example - * ```typescript - * const workflowResponse = await linkedapi.getWorkflowResult("workflow-id-123"); - * - * if (workflowResponse.completion) { - * console.log("Workflow completed:", workflowResponse.completion.data); - * } else if (workflowResponse.failure) { - * console.error("Workflow failed:", workflowResponse.failure.message); - * } - * ``` - */ - public async getWorkflowResult( - workflowId: string, - ): Promise { - return this.workflowExecutor.getWorkflowResult(workflowId); - } - /** * Restore a WorkflowHandler for a previously started workflow using its ID and function name. * This provides full type safety and exact result types based on the function name. diff --git a/src/types/config.ts b/src/types/config.ts index 64109a7..371d42c 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -1,5 +1,4 @@ export interface TLinkedApiConfig { linkedApiToken: string; identificationToken: string; - workflowTimeout?: number; } From b9207497c8dcc5295902aba87abdd99e9a8cf308 Mon Sep 17 00:00:00 2001 From: Kiril Ivonchik Date: Tue, 19 Aug 2025 14:15:09 +0200 Subject: [PATCH 04/10] Remove useless params --- src/core/linked-api-http-client.ts | 8 +------- src/types/http-client.ts | 6 +----- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/core/linked-api-http-client.ts b/src/core/linked-api-http-client.ts index 816c2f4..0cc4974 100644 --- a/src/core/linked-api-http-client.ts +++ b/src/core/linked-api-http-client.ts @@ -73,13 +73,9 @@ class LinkedApiHttpClient extends HttpClient { ); } - public async get( - url: string, - config?: RequestInit, - ): Promise> { + public async get(url: string): Promise> { try { const response = await fetch(`${this.baseUrl}${url}`, { - ...config, method: "GET", headers: this.headers, }); @@ -92,11 +88,9 @@ class LinkedApiHttpClient extends HttpClient { public async post( url: string, data?: unknown, - config?: RequestInit, ): Promise> { try { const response = await fetch(`${this.baseUrl}${url}`, { - ...config, method: "POST", headers: this.headers, body: data ? JSON.stringify(data) : null, diff --git a/src/types/http-client.ts b/src/types/http-client.ts index 98ff5f4..e56e461 100644 --- a/src/types/http-client.ts +++ b/src/types/http-client.ts @@ -1,13 +1,9 @@ import type { TLinkedApiResponse } from "../types/responses"; export abstract class HttpClient { - public abstract get( - url: string, - config?: RequestInit, - ): Promise>; + public abstract get(url: string): Promise>; public abstract post( url: string, data?: unknown, - config?: RequestInit, ): Promise>; } From 7c85ea18c317304f3f9bf0882d5281a4a4147549 Mon Sep 17 00:00:00 2001 From: Kiril Ivonchik Date: Wed, 20 Aug 2025 12:55:06 +0200 Subject: [PATCH 05/10] Enumerated values --- examples/search-companies.ts | 1 + src/core/linked-api-http-client.ts | 19 +++----- src/core/workflow-executor.ts | 24 +++++----- src/core/workflow-handler.ts | 2 +- src/mappers/base-mapper.abstract.ts | 11 ++++- src/types/actions/connection.ts | 8 +++- src/types/actions/message.ts | 15 ++++++- src/types/actions/person.ts | 53 ++++++++++++++-------- src/types/actions/post.ts | 22 +++++---- src/types/actions/search-company.ts | 70 ++++++++++++++++------------- src/types/errors.ts | 67 +++++++++++++++++---------- 11 files changed, 181 insertions(+), 111 deletions(-) diff --git a/examples/search-companies.ts b/examples/search-companies.ts index 6b523c9..9d30533 100644 --- a/examples/search-companies.ts +++ b/examples/search-companies.ts @@ -14,6 +14,7 @@ async function searchCompaniesExample(): Promise { } catch (error) { if (error instanceof LinkedApiError) { + console.error('🚨 Linked API Error Type:', error.type); console.error('🚨 Linked API Error:', error.message); console.error('šŸ“ Details:', error.details); } else { diff --git a/src/core/linked-api-http-client.ts b/src/core/linked-api-http-client.ts index 0cc4974..d6d62a5 100644 --- a/src/core/linked-api-http-client.ts +++ b/src/core/linked-api-http-client.ts @@ -3,6 +3,7 @@ import { LinkedApiError, HttpClient, TLinkedApiConfig, + TLinkedApiErrorType, } from "../types"; export function buildLinkedApiHttpClient(config: TLinkedApiConfig): HttpClient { @@ -33,8 +34,8 @@ class LinkedApiHttpClient extends HttpClient { try { const errorData = await response.json(); throw new LinkedApiError( - `HTTP ${response.status}: ${response.statusText}`, - "HTTP_ERROR", + errorData.error.type as TLinkedApiErrorType, + errorData.error.message, errorData, ); } catch (e) { @@ -42,8 +43,8 @@ class LinkedApiHttpClient extends HttpClient { throw e; } throw new LinkedApiError( + "httpError" as TLinkedApiErrorType, `HTTP ${response.status}: ${response.statusText}`, - "HTTP_ERROR", { status: response.status, statusText: response.statusText, @@ -58,18 +59,10 @@ class LinkedApiHttpClient extends HttpClient { throw error; } - if (error instanceof TypeError && error.message === "Failed to fetch") { - throw new LinkedApiError( - "Network error: No response received", - "NETWORK_ERROR", - error, - ); - } - throw new LinkedApiError( + "httpError" as TLinkedApiErrorType, `Request error: ${(error as Error).message}`, - "REQUEST_ERROR", - { message: (error as Error).message }, + { error }, ); } diff --git a/src/core/workflow-executor.ts b/src/core/workflow-executor.ts index e1690b9..c64c479 100644 --- a/src/core/workflow-executor.ts +++ b/src/core/workflow-executor.ts @@ -1,4 +1,4 @@ -import type { HttpClient } from "../types"; +import type { HttpClient, TLinkedApiErrorType } from "../types"; import type { TWorkflowDefinition, TWorkflowResponse, @@ -42,14 +42,14 @@ export class WorkflowExecutor { request, ); if (response.error) { - throw new LinkedApiError(response.error.type, response.error.message); - } - if (!response.result) { throw new LinkedApiError( - "No result. Please contact support.", - "noResult", + response.error.type as TLinkedApiErrorType, + response.error.message, ); } + if (!response.result) { + throw LinkedApiError.unknownError(); + } return response.result; } @@ -77,7 +77,7 @@ export class WorkflowExecutor { } throw new LinkedApiError( - "timeout", + "workflowTimeout", `Workflow ${workflowId} did not complete within ${timeout}ms`, ); } @@ -89,14 +89,14 @@ export class WorkflowExecutor { `${this.apiPath}/${workflowId}`, ); if (response.error) { - throw new LinkedApiError(response.error.type, response.error.message); - } - if (!response.result) { throw new LinkedApiError( - "noResult", - "No result. Please contact support.", + response.error.type as TLinkedApiErrorType, + response.error.message, ); } + if (!response.result) { + throw LinkedApiError.unknownError(); + } return response.result; } diff --git a/src/core/workflow-handler.ts b/src/core/workflow-handler.ts index 1234117..2f10bfd 100644 --- a/src/core/workflow-handler.ts +++ b/src/core/workflow-handler.ts @@ -37,7 +37,7 @@ export class WorkflowHandler { return this.mapper.mapResponse(rawResult); } catch (error) { - if (error instanceof LinkedApiError && error.type === "timeout") { + if (error instanceof LinkedApiError && error.type === "workflowTimeout") { throw new LinkedApiWorkflowTimeoutError( this.workflowId, this.functionName, diff --git a/src/mappers/base-mapper.abstract.ts b/src/mappers/base-mapper.abstract.ts index 85b0a25..4ae061e 100644 --- a/src/mappers/base-mapper.abstract.ts +++ b/src/mappers/base-mapper.abstract.ts @@ -1,4 +1,8 @@ -import { LinkedApiError, type TLinkedApiActionError } from "../types/errors"; +import { + LinkedApiError, + TLinkedApiErrorType, + type TLinkedApiActionError, +} from "../types/errors"; import type { TBaseActionParams } from "../types/params"; import type { TWorkflowCompletion, @@ -14,7 +18,10 @@ export abstract class BaseMapper { if (!response.completion) { const { failure } = response; if (failure) { - throw new LinkedApiError(failure.reason, failure.message); + throw new LinkedApiError( + failure.reason as TLinkedApiErrorType, + failure.message, + ); } throw LinkedApiError.unknownError(); } diff --git a/src/types/actions/connection.ts b/src/types/actions/connection.ts index c21234a..3529755 100644 --- a/src/types/actions/connection.ts +++ b/src/types/actions/connection.ts @@ -23,7 +23,13 @@ export interface TCheckConnectionStatusResult { connectionStatus: TConnectionStatus; } -export type TConnectionStatus = "connected" | "pending" | "notConnected"; +export const CONNECTION_STATUS = { + connected: "connected", + pending: "pending", + notConnected: "notConnected", +} as const; +export type TConnectionStatus = + (typeof CONNECTION_STATUS)[keyof typeof CONNECTION_STATUS]; export interface TWithdrawConnectionRequestParams extends TBaseActionParams { personUrl: string; diff --git a/src/types/actions/message.ts b/src/types/actions/message.ts index 216184a..b04514f 100644 --- a/src/types/actions/message.ts +++ b/src/types/actions/message.ts @@ -48,5 +48,16 @@ export interface TConversationPollResponse { }; } -export type TConversationType = "st" | "nv"; -export type TMessageSender = "us" | "them"; +export const CONVERSATION_TYPE = { + st: "st", + nv: "nv", +} as const; +export type TConversationType = + (typeof CONVERSATION_TYPE)[keyof typeof CONVERSATION_TYPE]; + +export const MESSAGE_SENDER = { + us: "us", + them: "them", +} as const; +export type TMessageSender = + (typeof MESSAGE_SENDER)[keyof typeof MESSAGE_SENDER]; diff --git a/src/types/actions/person.ts b/src/types/actions/person.ts index e62b6c8..803a32c 100644 --- a/src/types/actions/person.ts +++ b/src/types/actions/person.ts @@ -115,16 +115,25 @@ export interface TPersonExperience { location: string; } +export const EMPLOYMENT_TYPE = { + fullTime: "fullTime", + partTime: "partTime", + selfEmployed: "selfEmployed", + freelance: "freelance", + contract: "contract", + internship: "internship", + apprenticeship: "apprenticeship", + seasonal: "seasonal", +} as const; export type TEmploymentType = - | "fullTime" - | "partTime" - | "selfEmployed" - | "freelance" - | "contract" - | "internship" - | "apprenticeship" - | "seasonal"; -export type TLocationType = "onSite" | "remote" | "hybrid"; + (typeof EMPLOYMENT_TYPE)[keyof typeof EMPLOYMENT_TYPE]; + +export const LOCATION_TYPE = { + onSite: "onSite", + remote: "remote", + hybrid: "hybrid", +} as const; +export type TLocationType = (typeof LOCATION_TYPE)[keyof typeof LOCATION_TYPE]; export interface TPersonEducation { schoolName: string; @@ -141,16 +150,22 @@ export interface TPersonLanguage { proficiency: TLanguageProficiency; } +export const LANGUAGE_PROFICIENCY = { + elementary: "elementary", + limitedWorking: "limitedWorking", + professionalWorking: "professionalWorking", + fullProfessional: "fullProfessional", + nativeOrBilingual: "nativeOrBilingual", +} as const; export type TLanguageProficiency = - | "elementary" - | "limitedWorking" - | "professionalWorking" - | "fullProfessional" - | "nativeOrBilingual"; + (typeof LANGUAGE_PROFICIENCY)[keyof typeof LANGUAGE_PROFICIENCY]; +export const YEARS_OF_EXPERIENCE = { + lessThanOne: "lessThanOne", + oneToTwo: "oneToTwo", + threeToFive: "threeToFive", + sixToTen: "sixToTen", + moreThanTen: "moreThanTen", +} as const; export type TYearsOfExperience = - | "lessThanOne" - | "oneToTwo" - | "threeToFive" - | "sixToTen" - | "moreThanTen"; + (typeof YEARS_OF_EXPERIENCE)[keyof typeof YEARS_OF_EXPERIENCE]; diff --git a/src/types/actions/post.ts b/src/types/actions/post.ts index 89ff586..9dfb3cb 100644 --- a/src/types/actions/post.ts +++ b/src/types/actions/post.ts @@ -13,7 +13,11 @@ export interface TPost { commentCount: number; } -export type TPostType = "original" | "repost"; +export const POST_TYPE = { + original: "original", + repost: "repost", +} as const; +export type TPostType = (typeof POST_TYPE)[keyof typeof POST_TYPE]; export interface TFetchPostParams extends TBaseActionParams { postUrl: string; @@ -27,13 +31,15 @@ export interface TReaction { reactionType: TReactionType; } -export type TReactionType = - | "like" - | "celebrate" - | "support" - | "love" - | "insightful" - | "funny"; +export const REACTION_TYPE = { + like: "like", + celebrate: "celebrate", + support: "support", + love: "love", + insightful: "insightful", + funny: "funny", +} as const; +export type TReactionType = (typeof REACTION_TYPE)[keyof typeof REACTION_TYPE]; export interface TComment { postUrl: string; diff --git a/src/types/actions/search-company.ts b/src/types/actions/search-company.ts index f376bc9..71da20d 100644 --- a/src/types/actions/search-company.ts +++ b/src/types/actions/search-company.ts @@ -10,15 +10,18 @@ export interface TSearchCompanyParams extends TBaseActionParams { }; } +export const SEARCH_COMPANY_SIZE = { + size1_10: "1-10", + size11_50: "11-50", + size51_200: "51-200", + size201_500: "201-500", + size501_1000: "501-1000", + size1001_5000: "1001-5000", + size5001_10000: "5001-10000", + size10001Plus: "10001+", +} as const; export type TSearchCompanySize = - | "1-10" - | "11-50" - | "51-200" - | "201-500" - | "501-1000" - | "1001-5000" - | "5001-10000" - | "10001+"; + (typeof SEARCH_COMPANY_SIZE)[keyof typeof SEARCH_COMPANY_SIZE]; export interface TSearchCompanyResult { name: string; @@ -41,30 +44,37 @@ export interface TNvSearchCompanyParams extends TBaseActionParams { }; } +export const MIN_ANNUAL_REVENUE = { + revenue0: "0", + revenue0_5: "0.5", + revenue1: "1", + revenue2_5: "2.5", + revenue5: "5", + revenue10: "10", + revenue20: "20", + revenue50: "50", + revenue100: "100", + revenue500: "500", + revenue1000: "1000", +} as const; export type TMinAnnualRevenue = - | "0" - | "0.5" - | "1" - | "2.5" - | "5" - | "10" - | "20" - | "50" - | "100" - | "500" - | "1000"; + (typeof MIN_ANNUAL_REVENUE)[keyof typeof MIN_ANNUAL_REVENUE]; + +export const MAX_ANNUAL_REVENUE = { + revenue0_5: "0.5", + revenue1: "1", + revenue2_5: "2.5", + revenue5: "5", + revenue10: "10", + revenue20: "20", + revenue50: "50", + revenue100: "100", + revenue500: "500", + revenue1000: "1000", + revenue1000Plus: "1000+", +} as const; export type TMaxAnnualRevenue = - | "0.5" - | "1" - | "2.5" - | "5" - | "10" - | "20" - | "50" - | "100" - | "500" - | "1000" - | "1000+"; + (typeof MAX_ANNUAL_REVENUE)[keyof typeof MAX_ANNUAL_REVENUE]; export interface TNvSearchCompanyResult { name: string; diff --git a/src/types/errors.ts b/src/types/errors.ts index da1212d..be8fe89 100644 --- a/src/types/errors.ts +++ b/src/types/errors.ts @@ -11,18 +11,36 @@ import { TSupportedFunctionName } from "../core/workflow-restoration"; * - alreadyConnected (sendConnectionRequest) * - emailRequired (sendConnectionRequest) * - requestNotAllowed (sendConnectionRequest) - * - notPending (withdrawConnectionRequest) - * - retrievingNotAllowed (retrieveConnections) + * - notPending (withdrawConnectionRequest)) + * - retrievingNotAllowed (retrieveConnections, fetchCompany, salesNavigatorFetchCompany) * - connectionNotFound (removeConnection) * - searchingNotAllowed (searchCompanies, searchPeople, salesNavigatorSearchCompanies, salesNavigatorSearchPeople) * - companyNotFound (fetchCompany, salesNavigatorFetchCompany) - * - retrievingNotAllowed (fetchCompany, salesNavigatorFetchCompany) * - postNotFound (fetchPost, reactToPost, commentOnPost) * - commentingNotAllowed (commentOnPost) * - noSalesNavigator (salesNavigatorSendMessage, salesNavigatorSyncConversation, salesNavigatorSearchCompanies, salesNavigatorSearchPeople, salesNavigatorFetchCompany, salesNavigatorFetchPerson) */ +export const LINKED_API_ACTION_ERROR = { + personNotFound: "personNotFound", + messagingNotAllowed: "messagingNotAllowed", + alreadyPending: "alreadyPending", + alreadyConnected: "alreadyConnected", + emailRequired: "emailRequired", + requestNotAllowed: "requestNotAllowed", + notPending: "notPending", + retrievingNotAllowed: "retrievingNotAllowed", + connectionNotFound: "connectionNotFound", + searchingNotAllowed: "searchingNotAllowed", + companyNotFound: "companyNotFound", + postNotFound: "postNotFound", + commentingNotAllowed: "commentingNotAllowed", + noSalesNavigator: "noSalesNavigator", +} as const; +export type TLinkedApiActionErrorType = + (typeof LINKED_API_ACTION_ERROR)[keyof typeof LINKED_API_ACTION_ERROR]; + export interface TLinkedApiActionError { - type: string; + type: TLinkedApiActionErrorType; message: string; } @@ -30,40 +48,43 @@ export interface TLinkedApiActionError { * This error is thrown when a request fails. * @see {@link https://linkedapi.io/docs/making-requests/#common-errors} */ +export const LINKED_API_ERROR = { + linkedApiTokenRequired: "linkedApiTokenRequired", + invalidLinkedApiToken: "invalidLinkedApiToken", + identificationTokenRequired: "identificationTokenRequired", + invalidIdentificationToken: "invalidIdentificationToken", + subscriptionRequired: "subscriptionRequired", + invalidRequestPayload: "invalidRequestPayload", + invalidWorkflow: "invalidWorkflow", + plusPlanRequired: "plusPlanRequired", + linkedinAccountSignedOut: "linkedinAccountSignedOut", + languageNotSupported: "languageNotSupported", + workflowTimeout: "workflowTimeout", +} as const; +export type TLinkedApiErrorType = + (typeof LINKED_API_ERROR)[keyof typeof LINKED_API_ERROR]; export class LinkedApiError extends Error { - /** - * The type of the error. - * Common types: - * - linkedApiTokenRequired - * - invalidLinkedApiToken - * - identificationTokenRequired - * - invalidIdentificationToken - * - subscriptionRequired - * - invalidRequestPayload - * - invalidWorkflow - * - plusPlanRequired - * - linkedinAccountSignedOut - * - languageNotSupported - * - timeout - */ - public type: string; + public type: TLinkedApiErrorType; public override message: string; public details?: unknown; - constructor(type: string, message: string, details?: unknown) { + constructor(type: TLinkedApiErrorType, message: string, details?: unknown) { super(message); this.type = type; this.message = message; this.details = details; } - public static unknownError(message: string = ""): LinkedApiError { - return new LinkedApiError("unknownError", message); + public static unknownError( + message: string = "Unknown error. Please contact support.", + ): LinkedApiError { + return new LinkedApiError("unknownError" as TLinkedApiErrorType, message); } } /** * This error is thrown when a workflow times out. + * Contains workflowId and functionName to restore the workflow. */ export class LinkedApiWorkflowTimeoutError extends LinkedApiError { public readonly workflowId: string; From e566134004d884dc119620905e9e908072043dad Mon Sep 17 00:00:00 2001 From: Kiril Ivonchik Date: Wed, 20 Aug 2025 14:09:14 +0200 Subject: [PATCH 06/10] Direct setTimeout import --- src/core/workflow-executor.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/core/workflow-executor.ts b/src/core/workflow-executor.ts index c64c479..4a21bde 100644 --- a/src/core/workflow-executor.ts +++ b/src/core/workflow-executor.ts @@ -4,6 +4,7 @@ import type { TWorkflowResponse, } from "../types/workflows"; import { LinkedApiError } from "../types/errors"; +import { setTimeout as sleep } from "node:timers/promises"; /** * Options for waiting for a workflow to complete. @@ -73,7 +74,7 @@ export class WorkflowExecutor { return result; } - await this.delay(pollInterval); + await sleep(pollInterval); } throw new LinkedApiError( @@ -99,8 +100,4 @@ export class WorkflowExecutor { } return response.result; } - - private delay(ms: number): Promise { - return new Promise((resolve) => setTimeout(resolve, ms)); - } } From 460f78ec6dfaff174d0d81e41b8fa9e3a2f815af Mon Sep 17 00:00:00 2001 From: Kiril Ivonchik Date: Thu, 21 Aug 2025 16:52:31 +0200 Subject: [PATCH 07/10] New result handling --- .prettierrc.js | 21 + examples/connections.ts | 36 +- examples/fetch-company.ts | 28 +- examples/fetch-person.ts | 12 +- examples/fetch-post.ts | 6 +- examples/messaging.ts | 24 +- examples/post-actions.ts | 12 +- examples/restore-workflow.ts | 6 +- examples/search-companies.ts | 12 +- examples/search-people.ts | 12 +- examples/statistics.ts | 12 +- package.json | 3 +- src/core/base-operation.abstract.ts | 90 +++ src/core/index.ts | 9 +- src/core/linked-api-http-client.ts | 31 +- src/core/workflow-executor.ts | 51 +- src/core/workflow-handler.ts | 35 +- src/core/workflow-restoration.ts | 208 +++--- src/index.ts | 701 +++++------------- ...r.abstract.ts => array-workflow-mapper.ts} | 24 +- src/mappers/base-mapper.abstract.ts | 16 +- src/mappers/fetch-company-mapper.ts | 56 -- src/mappers/fetch-person-mapper.ts | 88 --- src/mappers/index.ts | 17 +- src/mappers/nv-fetch-company-mapper.ts | 47 -- src/mappers/nv-fetch-person-mapper.ts | 21 - src/mappers/nv-search-companies-mapper.ts | 16 - src/mappers/nv-search-people-mapper.ts | 16 - src/mappers/retrieve-connections-mapper.ts | 16 - .../retrieve-pending-requests-mapper.ts | 14 - src/mappers/search-companies-mapper.ts | 16 - src/mappers/search-people-mapper.ts | 16 - src/mappers/simple-workflow-mapper.ts | 28 +- src/mappers/then-workflow-mapper.abstract.ts | 54 +- src/mappers/void-workflow-mapper.ts | 23 +- src/operations/check-connection-status.ts | 17 + src/operations/comment-on-post.ts | 10 + src/operations/fetch-company.ts | 76 ++ src/operations/fetch-person.ts | 96 +++ src/operations/fetch-post.ts | 16 + src/operations/index.ts | 23 + src/operations/react-to-post.ts | 8 + src/operations/remove-connection.ts | 10 + src/operations/retrieve-connections.ts | 15 + src/operations/retrieve-pending-requests.ts | 14 + src/operations/retrieve-performance.ts | 11 + src/operations/retrieve-ssi.ts | 11 + .../sales-navigator-fetch-company.ts | 57 ++ .../sales-navigator-fetch-person.ts | 28 + .../sales-navigator-search-companies.ts | 16 + .../sales-navigator-search-people.ts | 15 + .../sales-navigator-send-message.ts | 11 + .../sales-navigator-sync-conversation.ts | 15 + src/operations/search-companies.ts | 15 + src/operations/search-people.ts | 12 + src/operations/send-connection-request.ts | 11 + src/operations/send-message.ts | 9 + src/operations/sync-conversation.ts | 11 + src/operations/withdraw-connection-request.ts | 14 + src/types/actions/company.ts | 102 +-- src/types/actions/connection.ts | 11 +- src/types/actions/index.ts | 16 +- src/types/actions/message.ts | 16 +- src/types/actions/person.ts | 137 ++-- src/types/actions/post.ts | 18 +- src/types/actions/search-companies.ts | 81 ++ src/types/actions/search-company.ts | 84 --- src/types/actions/search-people.ts | 5 +- src/types/errors.ts | 61 +- src/types/http-client.ts | 7 +- src/types/index.ts | 14 +- src/types/workflows.ts | 21 +- 72 files changed, 1307 insertions(+), 1564 deletions(-) create mode 100644 .prettierrc.js create mode 100644 src/core/base-operation.abstract.ts rename src/mappers/{array-workflow-mapper.abstract.ts => array-workflow-mapper.ts} (58%) delete mode 100644 src/mappers/fetch-company-mapper.ts delete mode 100644 src/mappers/fetch-person-mapper.ts delete mode 100644 src/mappers/nv-fetch-company-mapper.ts delete mode 100644 src/mappers/nv-fetch-person-mapper.ts delete mode 100644 src/mappers/nv-search-companies-mapper.ts delete mode 100644 src/mappers/nv-search-people-mapper.ts delete mode 100644 src/mappers/retrieve-connections-mapper.ts delete mode 100644 src/mappers/retrieve-pending-requests-mapper.ts delete mode 100644 src/mappers/search-companies-mapper.ts delete mode 100644 src/mappers/search-people-mapper.ts create mode 100644 src/operations/check-connection-status.ts create mode 100644 src/operations/comment-on-post.ts create mode 100644 src/operations/fetch-company.ts create mode 100644 src/operations/fetch-person.ts create mode 100644 src/operations/fetch-post.ts create mode 100644 src/operations/index.ts create mode 100644 src/operations/react-to-post.ts create mode 100644 src/operations/remove-connection.ts create mode 100644 src/operations/retrieve-connections.ts create mode 100644 src/operations/retrieve-pending-requests.ts create mode 100644 src/operations/retrieve-performance.ts create mode 100644 src/operations/retrieve-ssi.ts create mode 100644 src/operations/sales-navigator-fetch-company.ts create mode 100644 src/operations/sales-navigator-fetch-person.ts create mode 100644 src/operations/sales-navigator-search-companies.ts create mode 100644 src/operations/sales-navigator-search-people.ts create mode 100644 src/operations/sales-navigator-send-message.ts create mode 100644 src/operations/sales-navigator-sync-conversation.ts create mode 100644 src/operations/search-companies.ts create mode 100644 src/operations/search-people.ts create mode 100644 src/operations/send-connection-request.ts create mode 100644 src/operations/send-message.ts create mode 100644 src/operations/sync-conversation.ts create mode 100644 src/operations/withdraw-connection-request.ts create mode 100644 src/types/actions/search-companies.ts delete mode 100644 src/types/actions/search-company.ts diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..e6dcc01 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,21 @@ +module.exports = { + tabWidth: 2, + printWidth: 100, + useTabs: false, + semi: true, + singleQuote: true, + trailingComma: "all", + bracketSpacing: true, + arrowParens: "always", + endOfLine: "auto", + importOrderSeparation: true, + importOrderSortSpecifiers: true, + importOrderCaseInsensitive: true, + importOrderParserPlugins: [ + "classProperties", + "decorators-legacy", + "typescript", + ], + importOrder: ["", "^@/(.*)$", "^../(.*)", "^./(.*)"], + plugins: ["@trivago/prettier-plugin-sort-imports"], +}; diff --git a/examples/connections.ts b/examples/connections.ts index f898b44..2a50e3a 100644 --- a/examples/connections.ts +++ b/examples/connections.ts @@ -37,10 +37,10 @@ async function checkConnectionStatus(linkedapi: LinkedApi, personUrl: string): P personUrl: personUrl, }; - const statusWorkflow = await linkedapi.checkConnectionStatus(statusParams); - console.log('šŸ” Connection status workflow started:', statusWorkflow.workflowId); + const workflowId = await linkedapi.checkConnectionStatus.execute(statusParams); + console.log('šŸ” Connection status workflow started:', workflowId); - const statusResult = await statusWorkflow.result(); + const statusResult = await linkedapi.checkConnectionStatus.result(workflowId); if (statusResult.data) { console.log('āœ… Connection status check completed'); console.log(`šŸ“Š Connection status: ${statusResult.data.connectionStatus}`); @@ -59,10 +59,10 @@ async function sendConnectionRequest(linkedapi: LinkedApi, personUrl: string): P email: 'example@gmail.com', }; - const requestWorkflow = await linkedapi.sendConnectionRequest(requestParams); - console.log('šŸ“¤ Send connection request workflow started:', requestWorkflow.workflowId); + const workflowId = await linkedapi.sendConnectionRequest.execute(requestParams); + console.log('šŸ“¤ Send connection request workflow started:', workflowId); - const requestResult = await requestWorkflow.result(); + const requestResult = await linkedapi.sendConnectionRequest.result(workflowId); if (requestResult.errors.length > 0) { console.error('🚨 Errors:', JSON.stringify(requestResult.errors, null, 2)); } else { @@ -74,10 +74,10 @@ async function sendConnectionRequest(linkedapi: LinkedApi, personUrl: string): P async function retrievePendingRequests(linkedapi: LinkedApi): Promise { console.log('\nšŸ“‹ Retrieving pending connection requests...'); - const pendingWorkflow = await linkedapi.retrievePendingRequests(); - console.log('šŸ“‹ Retrieve pending requests workflow started:', pendingWorkflow.workflowId); + const workflowId = await linkedapi.retrievePendingRequests.execute(); + console.log('šŸ“‹ Retrieve pending requests workflow started:', workflowId); - const pendingResults = await pendingWorkflow.result(); + const pendingResults = await linkedapi.retrievePendingRequests.result(workflowId); if (pendingResults.data) { const pendingRequests = pendingResults.data; console.log('āœ… Pending requests retrieval completed'); @@ -101,10 +101,10 @@ async function withdrawConnectionRequest(linkedapi: LinkedApi, personUrl: string unfollow: true, }; - const withdrawWorkflow = await linkedapi.withdrawConnectionRequest(withdrawParams); - console.log('šŸ”™ Withdraw connection request workflow started:', withdrawWorkflow.workflowId); + const workflowId = await linkedapi.withdrawConnectionRequest.execute(withdrawParams); + console.log('šŸ”™ Withdraw connection request workflow started:', workflowId); - const withdrawResult = await withdrawWorkflow.result(); + const withdrawResult = await linkedapi.withdrawConnectionRequest.result(workflowId); if (withdrawResult.errors.length > 0) { console.error('🚨 Errors:', JSON.stringify(withdrawResult.errors, null, 2)); } else { @@ -124,10 +124,10 @@ async function retrieveConnections(linkedapi: LinkedApi): Promise { }, }; - const connectionsWorkflow = await linkedapi.retrieveConnections(connectionsParams); - console.log('šŸ‘„ Retrieve connections workflow started:', connectionsWorkflow.workflowId); + const workflowId = await linkedapi.retrieveConnections.execute(connectionsParams); + console.log('šŸ‘„ Retrieve connections workflow started:', workflowId); - const connectionsResults = await connectionsWorkflow.result(); + const connectionsResults = await linkedapi.retrieveConnections.result(workflowId); if (connectionsResults.data) { const connections = connectionsResults.data; @@ -152,10 +152,10 @@ async function removeConnection(linkedapi: LinkedApi, personUrl: string): Promis personUrl: personUrl, }; - const removeWorkflow = await linkedapi.removeConnection(removeParams); - console.log('āŒ Remove connection workflow started:', removeWorkflow.workflowId); + const workflowId = await linkedapi.removeConnection.execute(removeParams); + console.log('āŒ Remove connection workflow started:', workflowId); - const removeResult = await removeWorkflow.result(); + const removeResult = await linkedapi.removeConnection.result(workflowId); if (removeResult.errors.length > 0) { console.error('🚨 Errors:', JSON.stringify(removeResult.errors, null, 2)); } else { diff --git a/examples/fetch-company.ts b/examples/fetch-company.ts index 8225bbd..5f8102d 100644 --- a/examples/fetch-company.ts +++ b/examples/fetch-company.ts @@ -22,7 +22,7 @@ async function fetchCompanyExample(): Promise { } async function standardExample(linkedapi: LinkedApi): Promise { - const fetchCompanyWorkflow = await linkedapi.fetchCompany({ + const workflowId = await linkedapi.fetchCompany.execute({ companyUrl: 'https://www.linkedin.com/company/linkedin/', retrieveEmployees: true, retrieveDMs: true, @@ -43,10 +43,10 @@ async function standardExample(linkedapi: LinkedApi): Promise { }, }); - console.log('šŸ” Company workflow started: ', fetchCompanyWorkflow.workflowId); - const companyHandler = await fetchCompanyWorkflow.result(); - if (companyHandler.data) { - const company = companyHandler.data; + console.log('šŸ” Company workflow started: ', workflowId); + const companyData = await linkedapi.fetchCompany.result(workflowId); + if (companyData.data) { + const company = companyData.data; console.log('āœ… Company page opened successfully'); console.log(`šŸ¢ Company: ${company.name}`); console.log(`šŸ“– Description: ${company.description}`); @@ -57,13 +57,13 @@ async function standardExample(linkedapi: LinkedApi): Promise { console.log(`šŸ‘Øā€šŸ’¼ Employees Retrieved: ${company.employees?.length || 0}`); console.log(`šŸ“ Posts Retrieved: ${company.posts?.length || 0}`); } - if (companyHandler.errors.length > 0) { - console.error('🚨 Errors:', JSON.stringify(companyHandler.errors, null, 2)); + if (companyData.errors.length > 0) { + console.error('🚨 Errors:', JSON.stringify(companyData.errors, null, 2)); } } async function salesNavigatorExample(linkedapi: LinkedApi): Promise { - const nvCompanyResult = await linkedapi.salesNavigatorFetchCompany({ + const workflowId = await linkedapi.salesNavigatorFetchCompany.execute({ companyHashedUrl: 'https://www.linkedin.com/sales/company/1035', retrieveEmployees: true, retrieveDMs: true, @@ -78,10 +78,10 @@ async function salesNavigatorExample(linkedapi: LinkedApi): Promise { }, }); - console.log('šŸ” Sales Navigator workflow started: ', nvCompanyResult.workflowId); - const nvCompanyHandler = await nvCompanyResult.result(); - if (nvCompanyHandler.data) { - const nvCompany = nvCompanyHandler.data; + console.log('šŸ” Sales Navigator workflow started: ', workflowId); + const nvCompanyData = await linkedapi.salesNavigatorFetchCompany.result(workflowId); + if (nvCompanyData.data) { + const nvCompany = nvCompanyData.data; console.log('āœ… Sales Navigator company page opened successfully'); console.log(`šŸ¢ Company: ${nvCompany.name}`); console.log(`šŸ“– Description: ${nvCompany.description}`); @@ -93,8 +93,8 @@ async function salesNavigatorExample(linkedapi: LinkedApi): Promise { console.log(`šŸ‘Øā€šŸ’¼ Employees Retrieved: ${nvCompany.employees?.length || 0}`); console.log(`šŸŽÆ Decision Makers Retrieved: ${nvCompany.dms?.length || 0}`); } - if (nvCompanyHandler.errors.length > 0) { - console.error('🚨 Errors:', JSON.stringify(nvCompanyHandler.errors, null, 2)); + if (nvCompanyData.errors.length > 0) { + console.error('🚨 Errors:', JSON.stringify(nvCompanyData.errors, null, 2)); } } diff --git a/examples/fetch-person.ts b/examples/fetch-person.ts index 29ccca8..d438047 100644 --- a/examples/fetch-person.ts +++ b/examples/fetch-person.ts @@ -23,7 +23,7 @@ async function fetchPersonExample(): Promise { } async function standardExample(linkedapi: LinkedApi): Promise { - const personHandler = await linkedapi.fetchPerson({ + const workflowId = await linkedapi.fetchPerson.execute({ personUrl: 'https://www.linkedin.com/in/example-person/', retrieveExperience: true, retrieveEducation: true, @@ -43,8 +43,8 @@ async function standardExample(linkedapi: LinkedApi): Promise { limit: 5, }, }); - console.log('šŸ” Workflow started: ', personHandler.workflowId); - const personResult = await personHandler.result(); + console.log('šŸ” Workflow started: ', workflowId); + const personResult = await linkedapi.fetchPerson.result(workflowId); if (personResult.data) { const person = personResult.data; console.log('āœ… Person page opened successfully'); @@ -64,9 +64,9 @@ async function salesNavigatorExample(linkedapi: LinkedApi): Promise { personHashedUrl: 'https://www.linkedin.com/in/abc123', }; - const personHandler = await linkedapi.salesNavigatorFetchPerson(fetchParams); - console.log('šŸ” Workflow started: ', personHandler.workflowId); - const personResult = await personHandler.result(); + const workflowId = await linkedapi.salesNavigatorFetchPerson.execute(fetchParams); + console.log('šŸ” Workflow started: ', workflowId); + const personResult = await linkedapi.salesNavigatorFetchPerson.result(workflowId); if (personResult.data) { const person = personResult.data; console.log('āœ… Person page opened successfully'); diff --git a/examples/fetch-post.ts b/examples/fetch-post.ts index f5a3f13..40a6f26 100644 --- a/examples/fetch-post.ts +++ b/examples/fetch-post.ts @@ -20,11 +20,11 @@ async function fetchPostExample(): Promise { } async function standardExample(linkedapi: LinkedApi): Promise { - const postWorkflow = await linkedapi.fetchPost({ + const workflowId = await linkedapi.fetchPost.execute({ postUrl: 'https://www.linkedin.com/posts/post-url' }); - console.log('šŸ” Workflow started:', postWorkflow.workflowId); - const postResult = await postWorkflow.result(); + console.log('šŸ” Workflow started:', workflowId); + const postResult = await linkedapi.fetchPost.result(workflowId); if (postResult.data) { const post = postResult.data; console.log('āœ… Post fetched successfully'); diff --git a/examples/messaging.ts b/examples/messaging.ts index 301ca18..f2251ee 100644 --- a/examples/messaging.ts +++ b/examples/messaging.ts @@ -38,10 +38,10 @@ async function sendMessage(linkedapi: LinkedApi, personUrl: string): Promise 0) { console.error('🚨 Errors:', JSON.stringify(sendMessageResult.errors, null, 2)); } else { @@ -58,10 +58,10 @@ async function syncConversation(linkedapi: LinkedApi, personUrl: string): Promis personUrl: personUrl, }; - const syncWorkflow = await linkedapi.syncConversation(syncParams); - console.log('šŸ”„ Sync conversation workflow started:', syncWorkflow.workflowId); + const workflowId = await linkedapi.syncConversation.execute(syncParams); + console.log('šŸ”„ Sync conversation workflow started:', workflowId); - const syncResult = await syncWorkflow.result(); + const syncResult = await linkedapi.syncConversation.result(workflowId); if (syncResult.errors.length > 0) { console.error('🚨 Errors:', JSON.stringify(syncResult.errors, null, 2)); } else { @@ -80,10 +80,10 @@ async function salesNavigatorSendMessage(linkedapi: LinkedApi, personUrl: string subject: 'Let\'s connect!', }; - const nvMessageWorkflow = await linkedapi.salesNavigatorSendMessage(nvMessageParams); - console.log('šŸŽÆ Sales Navigator send message workflow started:', nvMessageWorkflow.workflowId); + const workflowId = await linkedapi.salesNavigatorSendMessage.execute(nvMessageParams); + console.log('šŸŽÆ Sales Navigator send message workflow started:', workflowId); - const nvMessageResult = await nvMessageWorkflow.result(); + const nvMessageResult = await linkedapi.salesNavigatorSendMessage.result(workflowId); if (nvMessageResult.errors.length > 0) { console.error('🚨 Errors:', JSON.stringify(nvMessageResult.errors, null, 2)); } else { @@ -100,10 +100,10 @@ async function salesNavigatorSyncConversation(linkedapi: LinkedApi, personUrl: s personUrl: personUrl, }; - const nvSyncWorkflow = await linkedapi.salesNavigatorSyncConversation(nvSyncParams); - console.log('šŸŽÆ Sales Navigator sync conversation workflow started:', nvSyncWorkflow.workflowId); + const workflowId = await linkedapi.salesNavigatorSyncConversation.execute(nvSyncParams); + console.log('šŸŽÆ Sales Navigator sync conversation workflow started:', workflowId); - const nvSyncResult = await nvSyncWorkflow.result(); + const nvSyncResult = await linkedapi.salesNavigatorSyncConversation.result(workflowId); if (nvSyncResult.errors.length > 0) { console.error('🚨 Errors:', JSON.stringify(nvSyncResult.errors, null, 2)); } else { diff --git a/examples/post-actions.ts b/examples/post-actions.ts index 5dabf5e..9a24be9 100644 --- a/examples/post-actions.ts +++ b/examples/post-actions.ts @@ -28,10 +28,10 @@ async function reactToPost(linkedapi: LinkedApi): Promise { type: 'like' as const, }; - const reactionWorkflow = await linkedapi.reactToPost(reactionParams); - console.log('šŸ‘ React to post workflow started:', reactionWorkflow.workflowId); + const workflowId = await linkedapi.reactToPost.execute(reactionParams); + console.log('šŸ‘ React to post workflow started:', workflowId); - const reactionResult = await reactionWorkflow.result(); + const reactionResult = await linkedapi.reactToPost.result(workflowId); if (reactionResult.errors.length > 0) { console.error('🚨 Errors:', JSON.stringify(reactionResult.errors, null, 2)); } else { @@ -47,10 +47,10 @@ async function commentOnPost(linkedapi: LinkedApi): Promise { text: 'Great post! Thanks for sharing this valuable insight. Looking forward to more content like this.', }; - const commentWorkflow = await linkedapi.commentOnPost(commentParams); - console.log('šŸ’¬ Comment on post workflow started:', commentWorkflow.workflowId); + const workflowId = await linkedapi.commentOnPost.execute(commentParams); + console.log('šŸ’¬ Comment on post workflow started:', workflowId); - const commentResult = await commentWorkflow.result(); + const commentResult = await linkedapi.commentOnPost.result(workflowId); if (commentResult.errors.length > 0) { console.error('🚨 Errors:', JSON.stringify(commentResult.errors, null, 2)); } else { diff --git a/examples/restore-workflow.ts b/examples/restore-workflow.ts index 56f2eff..ed2b943 100644 --- a/examples/restore-workflow.ts +++ b/examples/restore-workflow.ts @@ -7,14 +7,12 @@ async function example(): Promise { identificationToken: process.env.IDENTIFICATION_TOKEN!, }); - const startHandler = await linkedapi.fetchPerson({ + const savedWorkflowId = await linkedapi.fetchPerson.execute({ personUrl: 'https://www.linkedin.com/in/example-person/', retrieveExperience: true, retrieveEducation: true, }); - const savedWorkflowId = startHandler.workflowId; - // ... App stops here // App restart: rebuild LinkedApi client @@ -31,7 +29,7 @@ async function example(): Promise { // Or if you want to restore a raw workflow (for executeCustomWorkflow) const rawHandler = await linkedapiAfterRestart.restoreWorkflow( savedWorkflowId, - FUNCTION_NAME.executeCustomWorkflow, + FUNCTION_NAME.customWorkflow, ); console.log("Restoration started: ", restoredHandler.workflowId); diff --git a/examples/search-companies.ts b/examples/search-companies.ts index 9d30533..f49ec38 100644 --- a/examples/search-companies.ts +++ b/examples/search-companies.ts @@ -35,9 +35,9 @@ async function standardExample(linkedapi: LinkedApi): Promise { }; console.log('šŸ” Searching companies with Linked API...'); - const searchWorkflow = await linkedapi.searchCompanies(searchParams); - console.log('šŸ” Workflow started:', searchWorkflow.workflowId); - const result = await searchWorkflow.result(); + const workflowId = await linkedapi.searchCompanies.execute(searchParams); + console.log('šŸ” Workflow started:', workflowId); + const result = await linkedapi.searchCompanies.result(workflowId); if (result.data) { const results = result.data; console.log('āœ… Company search completed'); @@ -70,9 +70,9 @@ async function salesNavigatorExample(linkedapi: LinkedApi): Promise { }; console.log('\nšŸŽÆ Searching companies with Sales Navigator...'); - const nvSearchWorkflow = await linkedapi.salesNavigatorSearchCompanies(nvSearchParams); - console.log('šŸ” Sales Navigator workflow started:', nvSearchWorkflow.workflowId); - const nvResults = await nvSearchWorkflow.result(); + const workflowId = await linkedapi.salesNavigatorSearchCompanies.execute(nvSearchParams); + console.log('šŸ” Sales Navigator workflow started:', workflowId); + const nvResults = await linkedapi.salesNavigatorSearchCompanies.result(workflowId); if (nvResults.data) { const results = nvResults.data; diff --git a/examples/search-people.ts b/examples/search-people.ts index 8e9f514..d89079c 100644 --- a/examples/search-people.ts +++ b/examples/search-people.ts @@ -36,9 +36,9 @@ async function standardExample(linkedapi: LinkedApi): Promise { }; console.log('šŸ” Searching people with Linked API...'); - const searchWorkflow = await linkedapi.searchPeople(searchParams); - console.log('šŸ” Workflow started:', searchWorkflow.workflowId); - const searchResult = await searchWorkflow.result(); + const workflowId = await linkedapi.searchPeople.execute(searchParams); + console.log('šŸ” Workflow started:', workflowId); + const searchResult = await linkedapi.searchPeople.result(workflowId); if (searchResult.data) { const results = searchResult.data; @@ -70,9 +70,9 @@ async function salesNavigatorExample(linkedapi: LinkedApi): Promise { }; console.log('\nšŸŽÆ Searching people with Sales Navigator...'); - const nvSearchWorkflow = await linkedapi.salesNavigatorSearchPeople(nvSearchParams); - console.log('šŸ” Sales Navigator workflow started:', nvSearchWorkflow.workflowId); - const nvResults = await nvSearchWorkflow.result(); + const workflowId = await linkedapi.salesNavigatorSearchPeople.execute(nvSearchParams); + console.log('šŸ” Sales Navigator workflow started:', workflowId); + const nvResults = await linkedapi.salesNavigatorSearchPeople.result(workflowId); if (nvResults.data) { const results = nvResults.data; diff --git a/examples/statistics.ts b/examples/statistics.ts index 12d912b..2273c7f 100644 --- a/examples/statistics.ts +++ b/examples/statistics.ts @@ -26,10 +26,10 @@ async function statisticsExample(): Promise { async function retrieveSSI(linkedapi: LinkedApi): Promise { console.log('\nšŸ“Š Retrieving SSI (Social Selling Index)...'); - const ssiWorkflow = await linkedapi.retrieveSSI(); - console.log('šŸ“Š Retrieve SSI workflow started:', ssiWorkflow.workflowId); + const workflowId = await linkedapi.retrieveSSI.execute(); + console.log('šŸ“Š Retrieve SSI workflow started:', workflowId); - const ssiResult = (await ssiWorkflow.result()).data!; + const ssiResult = (await linkedapi.retrieveSSI.result(workflowId)).data!; console.log('āœ… SSI retrieval completed'); console.log(`šŸ“ˆ SSI Score: ${ssiResult.ssi}/100`); console.log(`šŸ† Industry Top: ${ssiResult.industryTop}%`); @@ -46,10 +46,10 @@ async function retrieveSSI(linkedapi: LinkedApi): Promise { async function retrievePerformance(linkedapi: LinkedApi): Promise { console.log('\nšŸ“ˆ Retrieving Performance Statistics...'); - const performanceWorkflow = await linkedapi.retrievePerformance(); - console.log('šŸ“ˆ Retrieve performance workflow started:', performanceWorkflow.workflowId); + const workflowId = await linkedapi.retrievePerformance.execute(); + console.log('šŸ“ˆ Retrieve performance workflow started:', workflowId); - const performanceResult = (await performanceWorkflow.result()).data!; + const performanceResult = (await linkedapi.retrievePerformance.result(workflowId)).data!; console.log('āœ… Performance retrieval completed'); console.log(`šŸ‘„ Followers: ${performanceResult.followersCount.toLocaleString()}`); console.log(`šŸ‘€ Post Views (Last 7 Days): ${performanceResult.postViewsLast7Days.toLocaleString()}`); diff --git a/package.json b/package.json index 9307d6e..533d5b8 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@eslint/compat": "^1.2.8", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.24.0", + "@trivago/prettier-plugin-sort-imports": "^5.2.2", "@types/jest": "^29.5.12", "@types/node": "^20.12.12", "@typescript-eslint/eslint-plugin": "^8.29.1", @@ -58,4 +59,4 @@ "typescript": "^5.8.3", "typescript-eslint": "^8.38.0" } -} +} \ No newline at end of file diff --git a/src/core/base-operation.abstract.ts b/src/core/base-operation.abstract.ts new file mode 100644 index 0000000..1cd26ba --- /dev/null +++ b/src/core/base-operation.abstract.ts @@ -0,0 +1,90 @@ +import { WaitForCompletionOptions } from '../core/workflow-executor'; +import { TSupportedFunctionName } from '../core/workflow-restoration'; +import { BaseMapper, TMappedResponse } from '../mappers/base-mapper.abstract'; +import { + HttpClient, + LinkedApiError, + LinkedApiWorkflowTimeoutError, + TLinkedApiErrorType, + TWorkflowResponse, +} from '../types'; + +export abstract class PredefinedOperation { + protected abstract readonly functionName: TSupportedFunctionName; + protected abstract readonly mapper: BaseMapper; + + constructor(private readonly httpClient: HttpClient) {} + + public async execute(params: TParams): Promise { + const request = this.mapper.mapRequest(params); + const response = await this.httpClient.post(`/workflows`, request); + if (response.error) { + throw new LinkedApiError(response.error.type as TLinkedApiErrorType, response.error.message); + } + if (!response.result) { + throw LinkedApiError.unknownError(); + } + return response.result.workflowId; + } + + public async result( + workflowId: string, + options: WaitForCompletionOptions = {}, + ): Promise> { + try { + const rawResult = await this.getResult(workflowId, options); + + if (!this.mapper) { + return { + data: rawResult as TResult, + errors: [], + }; + } + + return this.mapper.mapResponse(rawResult); + } catch (error) { + if (error instanceof LinkedApiError && error.type === 'workflowTimeout') { + throw new LinkedApiWorkflowTimeoutError(workflowId, this.functionName); + } + throw error; + } + } + + public async getResult( + workflowId: string, + options: WaitForCompletionOptions, + ): Promise { + const { pollInterval = 5000, timeout = 24 * 60 * 60 * 1000 } = options; + const startTime = Date.now(); + + while (Date.now() - startTime < timeout) { + const result = await this.getWorkflowResult(workflowId); + + if (result.workflowStatus === 'completed' || result.workflowStatus === 'failed') { + return result; + } + + await this.sleep(pollInterval); + } + + throw new LinkedApiError( + 'workflowTimeout', + `Workflow ${workflowId} did not complete within ${timeout}ms`, + ); + } + + public async getWorkflowResult(workflowId: string): Promise { + const response = await this.httpClient.get(`/workflows/${workflowId}`); + if (response.error) { + throw new LinkedApiError(response.error.type as TLinkedApiErrorType, response.error.message); + } + if (!response.result) { + throw LinkedApiError.unknownError(); + } + return response.result; + } + + private async sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } +} diff --git a/src/core/index.ts b/src/core/index.ts index 98ccc82..2c1925a 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,4 +1,5 @@ -export * from "./linked-api-http-client"; -export * from "./workflow-executor"; -export * from "./workflow-handler"; -export * from "./workflow-restoration"; +export * from './base-operation.abstract'; +export * from './linked-api-http-client'; +export * from './workflow-executor'; +export * from './workflow-handler'; +export * from './workflow-restoration'; diff --git a/src/core/linked-api-http-client.ts b/src/core/linked-api-http-client.ts index d6d62a5..5d6919b 100644 --- a/src/core/linked-api-http-client.ts +++ b/src/core/linked-api-http-client.ts @@ -1,10 +1,10 @@ import { - TLinkedApiResponse, - LinkedApiError, HttpClient, + LinkedApiError, TLinkedApiConfig, TLinkedApiErrorType, -} from "../types"; + TLinkedApiResponse, +} from '../types'; export function buildLinkedApiHttpClient(config: TLinkedApiConfig): HttpClient { return new LinkedApiHttpClient(config); @@ -16,17 +16,15 @@ class LinkedApiHttpClient extends HttpClient { constructor(config: TLinkedApiConfig) { super(); - this.baseUrl = "https://api.linkedapi.io"; + this.baseUrl = 'https://api.linkedapi.io'; this.headers = { - "Content-Type": "application/json", - "linked-api-token": config.linkedApiToken, - "identification-token": config.identificationToken, + 'Content-Type': 'application/json', + 'linked-api-token': config.linkedApiToken, + 'identification-token': config.identificationToken, }; } - private async handleResponse( - response: Response, - ): Promise> { + private async handleResponse(response: Response): Promise> { if (response.ok) { return (await response.json()) as TLinkedApiResponse; } @@ -43,7 +41,7 @@ class LinkedApiHttpClient extends HttpClient { throw e; } throw new LinkedApiError( - "httpError" as TLinkedApiErrorType, + 'httpError' as TLinkedApiErrorType, `HTTP ${response.status}: ${response.statusText}`, { status: response.status, @@ -60,7 +58,7 @@ class LinkedApiHttpClient extends HttpClient { } throw new LinkedApiError( - "httpError" as TLinkedApiErrorType, + 'httpError' as TLinkedApiErrorType, `Request error: ${(error as Error).message}`, { error }, ); @@ -69,7 +67,7 @@ class LinkedApiHttpClient extends HttpClient { public async get(url: string): Promise> { try { const response = await fetch(`${this.baseUrl}${url}`, { - method: "GET", + method: 'GET', headers: this.headers, }); return this.handleResponse(response); @@ -78,13 +76,10 @@ class LinkedApiHttpClient extends HttpClient { } } - public async post( - url: string, - data?: unknown, - ): Promise> { + public async post(url: string, data?: unknown): Promise> { try { const response = await fetch(`${this.baseUrl}${url}`, { - method: "POST", + method: 'POST', headers: this.headers, body: data ? JSON.stringify(data) : null, }); diff --git a/src/core/workflow-executor.ts b/src/core/workflow-executor.ts index 4a21bde..acf4af1 100644 --- a/src/core/workflow-executor.ts +++ b/src/core/workflow-executor.ts @@ -1,10 +1,8 @@ -import type { HttpClient, TLinkedApiErrorType } from "../types"; -import type { - TWorkflowDefinition, - TWorkflowResponse, -} from "../types/workflows"; -import { LinkedApiError } from "../types/errors"; -import { setTimeout as sleep } from "node:timers/promises"; +import { setTimeout as sleep } from 'node:timers/promises'; + +import type { HttpClient, TLinkedApiErrorType } from '../types'; +import { LinkedApiError } from '../types/errors'; +import type { TWorkflowDefinition, TWorkflowResponse } from '../types/workflows'; /** * Options for waiting for a workflow to complete. @@ -35,18 +33,10 @@ export class WorkflowExecutor { this.workflowTimeout = workflowTimeout; } - public async startWorkflow( - request: TWorkflowDefinition, - ): Promise { - const response = await this.httpClient.post( - `${this.apiPath}`, - request, - ); + public async startWorkflow(request: TWorkflowDefinition): Promise { + const response = await this.httpClient.post(`${this.apiPath}`, request); if (response.error) { - throw new LinkedApiError( - response.error.type as TLinkedApiErrorType, - response.error.message, - ); + throw new LinkedApiError(response.error.type as TLinkedApiErrorType, response.error.message); } if (!response.result) { throw LinkedApiError.unknownError(); @@ -58,19 +48,13 @@ export class WorkflowExecutor { workflowId: string, options: WaitForCompletionOptions, ): Promise { - const { - pollInterval = 5000, - timeout = this.workflowTimeout ?? 24 * 60 * 60 * 1000, - } = options; + const { pollInterval = 5000, timeout = this.workflowTimeout ?? 24 * 60 * 60 * 1000 } = options; const startTime = Date.now(); while (Date.now() - startTime < timeout) { const result = await this.getWorkflowResult(workflowId); - if ( - result.workflowStatus === "completed" || - result.workflowStatus === "failed" - ) { + if (result.workflowStatus === 'completed' || result.workflowStatus === 'failed') { return result; } @@ -78,22 +62,15 @@ export class WorkflowExecutor { } throw new LinkedApiError( - "workflowTimeout", + 'workflowTimeout', `Workflow ${workflowId} did not complete within ${timeout}ms`, ); } - public async getWorkflowResult( - workflowId: string, - ): Promise { - const response = await this.httpClient.get( - `${this.apiPath}/${workflowId}`, - ); + public async getWorkflowResult(workflowId: string): Promise { + const response = await this.httpClient.get(`${this.apiPath}/${workflowId}`); if (response.error) { - throw new LinkedApiError( - response.error.type as TLinkedApiErrorType, - response.error.message, - ); + throw new LinkedApiError(response.error.type as TLinkedApiErrorType, response.error.message); } if (!response.result) { throw LinkedApiError.unknownError(); diff --git a/src/core/workflow-handler.ts b/src/core/workflow-handler.ts index 2f10bfd..ce56673 100644 --- a/src/core/workflow-handler.ts +++ b/src/core/workflow-handler.ts @@ -1,15 +1,10 @@ -import type { TBaseActionParams } from "../types/params"; -import type { - BaseMapper, - TMappedResponse, -} from "../mappers/base-mapper.abstract"; -import type { TWorkflowResponse } from "../types/workflows"; -import type { - WaitForCompletionOptions, - WorkflowExecutor, -} from "./workflow-executor"; -import { TSupportedFunctionName } from "../core/workflow-restoration"; -import { LinkedApiError, LinkedApiWorkflowTimeoutError } from "../types"; +import { TSupportedFunctionName } from '../core/workflow-restoration'; +import type { BaseMapper, TMappedResponse } from '../mappers/base-mapper.abstract'; +import { LinkedApiError, LinkedApiWorkflowTimeoutError } from '../types'; +import type { TBaseActionParams } from '../types/params'; +import type { TWorkflowResponse } from '../types/workflows'; + +import type { WaitForCompletionOptions, WorkflowExecutor } from './workflow-executor'; export class WorkflowHandler { constructor( @@ -19,14 +14,9 @@ export class WorkflowHandler { private readonly mapper?: BaseMapper, ) {} - public async result( - options: WaitForCompletionOptions = {}, - ): Promise> { + public async result(options: WaitForCompletionOptions = {}): Promise> { try { - const rawResult = await this.workflowExecutor.result( - this.workflowId, - options, - ); + const rawResult = await this.workflowExecutor.result(this.workflowId, options); if (!this.mapper) { return { @@ -37,11 +27,8 @@ export class WorkflowHandler { return this.mapper.mapResponse(rawResult); } catch (error) { - if (error instanceof LinkedApiError && error.type === "workflowTimeout") { - throw new LinkedApiWorkflowTimeoutError( - this.workflowId, - this.functionName, - ); + if (error instanceof LinkedApiError && error.type === 'workflowTimeout') { + throw new LinkedApiWorkflowTimeoutError(this.workflowId, this.functionName); } throw error; } diff --git a/src/core/workflow-restoration.ts b/src/core/workflow-restoration.ts index 39fa387..6ae5536 100644 --- a/src/core/workflow-restoration.ts +++ b/src/core/workflow-restoration.ts @@ -1,50 +1,53 @@ -import type { TBaseFetchPersonParamsWide } from "../types/actions/person"; -import type { - TBaseFetchCompanyParamsWide, - TNvBaseFetchCompanyParamsWide, -} from "../types/actions/company"; -import type { TWorkflowResponse } from "../types/workflows"; -import type { BaseMapper } from "../mappers/base-mapper.abstract"; - -export type TRestoreResultType = - T extends typeof FUNCTION_NAME.executeCustomWorkflow - ? TWorkflowResponse - : TRestoreMapperReturnType extends BaseMapper - ? R - : never; - +import { ArrayWorkflowMapper, SimpleWorkflowMapper, VoidWorkflowMapper } from '../mappers'; +import type { BaseMapper } from '../mappers/base-mapper.abstract'; import { - FetchPersonMapper, FetchCompanyMapper, + FetchPersonMapper, NvFetchCompanyMapper, NvFetchPersonMapper, - SearchCompaniesMapper, - NvSearchCompaniesMapper, - SearchPeopleMapper, - NvSearchPeopleMapper, - RetrieveConnectionsMapper, - RetrievePendingRequestsMapper, - SimpleWorkflowMapper, - VoidWorkflowMapper, -} from "../mappers"; +} from '../operations'; import { + TBaseActionParams, + TCheckConnectionStatusParams, + TCheckConnectionStatusResult, + TCommentOnPostParams, TFetchPostParams, TFetchPostResult, - TSendMessageParams, - TSyncConversationParams, + TNvSearchCompaniesParams, + TNvSearchCompanyResult, + TNvSearchPeopleParams, + TNvSearchPeopleResult, TNvSendMessageParams, TNvSyncConversationParams, - TSendConnectionRequestParams, - TCheckConnectionStatusParams, - TCheckConnectionStatusResult, - TWithdrawConnectionRequestParams, - TRemoveConnectionParams, TReactToPostParams, - TCommentOnPostParams, - TRetrieveSSIResult, + TRemoveConnectionParams, + TRetrieveConnectionsParams, + TRetrieveConnectionsResult, + TRetrievePendingRequestsResult, TRetrievePerformanceResult, - TBaseActionParams, -} from "../types"; + TRetrieveSSIResult, + TSearchCompaniesParams, + TSearchCompanyResult, + TSearchPeopleParams, + TSearchPeopleResult, + TSendConnectionRequestParams, + TSendMessageParams, + TSyncConversationParams, + TWithdrawConnectionRequestParams, +} from '../types'; +import type { + TBaseFetchCompanyParamsWide, + TNvBaseFetchCompanyParamsWide, +} from '../types/actions/company'; +import type { TBaseFetchPersonParamsWide } from '../types/actions/person'; +import type { TWorkflowResponse } from '../types/workflows'; + +export type TRestoreResultType = + T extends typeof FUNCTION_NAME.customWorkflow + ? TWorkflowResponse + : TRestoreMapperReturnType extends BaseMapper + ? R + : never; export type TRestoreMapperReturnType = T extends typeof FUNCTION_NAME.fetchPerson @@ -58,13 +61,13 @@ export type TRestoreMapperReturnType = : T extends typeof FUNCTION_NAME.fetchPost ? SimpleWorkflowMapper : T extends typeof FUNCTION_NAME.searchCompanies - ? SearchCompaniesMapper + ? ArrayWorkflowMapper : T extends typeof FUNCTION_NAME.salesNavigatorSearchCompanies - ? NvSearchCompaniesMapper + ? ArrayWorkflowMapper : T extends typeof FUNCTION_NAME.searchPeople - ? SearchPeopleMapper + ? ArrayWorkflowMapper : T extends typeof FUNCTION_NAME.salesNavigatorSearchPeople - ? NvSearchPeopleMapper + ? ArrayWorkflowMapper : T extends typeof FUNCTION_NAME.sendMessage ? VoidWorkflowMapper : T extends typeof FUNCTION_NAME.syncConversation @@ -83,9 +86,15 @@ export type TRestoreMapperReturnType = : T extends typeof FUNCTION_NAME.withdrawConnectionRequest ? VoidWorkflowMapper : T extends typeof FUNCTION_NAME.retrievePendingRequests - ? RetrievePendingRequestsMapper + ? ArrayWorkflowMapper< + TBaseActionParams, + TRetrievePendingRequestsResult + > : T extends typeof FUNCTION_NAME.retrieveConnections - ? RetrieveConnectionsMapper + ? ArrayWorkflowMapper< + TRetrieveConnectionsParams, + TRetrieveConnectionsResult + > : T extends typeof FUNCTION_NAME.removeConnection ? VoidWorkflowMapper : T extends typeof FUNCTION_NAME.reactToPost @@ -102,38 +111,37 @@ export type TRestoreMapperReturnType = TBaseActionParams, TRetrievePerformanceResult > - : T extends typeof FUNCTION_NAME.executeCustomWorkflow + : T extends typeof FUNCTION_NAME.customWorkflow ? null // Special case: no mapper needed, will return raw TWorkflowResponse : never; export const FUNCTION_NAME = { - fetchPerson: "fetchPerson", - fetchCompany: "fetchCompany", - salesNavigatorFetchCompany: "salesNavigatorFetchCompany", - salesNavigatorFetchPerson: "salesNavigatorFetchPerson", - fetchPost: "fetchPost", - searchCompanies: "searchCompanies", - salesNavigatorSearchCompanies: "salesNavigatorSearchCompanies", - searchPeople: "searchPeople", - salesNavigatorSearchPeople: "salesNavigatorSearchPeople", - sendMessage: "sendMessage", - syncConversation: "syncConversation", - salesNavigatorSendMessage: "salesNavigatorSendMessage", - salesNavigatorSyncConversation: "salesNavigatorSyncConversation", - sendConnectionRequest: "sendConnectionRequest", - checkConnectionStatus: "checkConnectionStatus", - withdrawConnectionRequest: "withdrawConnectionRequest", - retrievePendingRequests: "retrievePendingRequests", - retrieveConnections: "retrieveConnections", - removeConnection: "removeConnection", - reactToPost: "reactToPost", - commentOnPost: "commentOnPost", - retrieveSSI: "retrieveSSI", - retrievePerformance: "retrievePerformance", - executeCustomWorkflow: "executeCustomWorkflow", // Special case: no mapper needed, will return raw TWorkflowResponse + fetchPerson: 'fetchPerson', + fetchCompany: 'fetchCompany', + salesNavigatorFetchCompany: 'salesNavigatorFetchCompany', + salesNavigatorFetchPerson: 'salesNavigatorFetchPerson', + fetchPost: 'fetchPost', + searchCompanies: 'searchCompanies', + salesNavigatorSearchCompanies: 'salesNavigatorSearchCompanies', + searchPeople: 'searchPeople', + salesNavigatorSearchPeople: 'salesNavigatorSearchPeople', + sendMessage: 'sendMessage', + syncConversation: 'syncConversation', + salesNavigatorSendMessage: 'salesNavigatorSendMessage', + salesNavigatorSyncConversation: 'salesNavigatorSyncConversation', + sendConnectionRequest: 'sendConnectionRequest', + checkConnectionStatus: 'checkConnectionStatus', + withdrawConnectionRequest: 'withdrawConnectionRequest', + retrievePendingRequests: 'retrievePendingRequests', + retrieveConnections: 'retrieveConnections', + removeConnection: 'removeConnection', + reactToPost: 'reactToPost', + commentOnPost: 'commentOnPost', + retrieveSSI: 'retrieveSSI', + retrievePerformance: 'retrievePerformance', + customWorkflow: 'customWorkflow', // Special case: no mapper needed, will return raw TWorkflowResponse } as const; -export type TSupportedFunctionName = - (typeof FUNCTION_NAME)[keyof typeof FUNCTION_NAME]; +export type TSupportedFunctionName = (typeof FUNCTION_NAME)[keyof typeof FUNCTION_NAME]; /** * Internal function to restore a mapper from a function name. @@ -157,95 +165,101 @@ export function createMapperFromFunctionName( } case FUNCTION_NAME.fetchPost: { return new SimpleWorkflowMapper({ - actionType: "st.openPost", + actionType: 'st.openPost', defaultParams: { basicInfo: true }, }) as TRestoreMapperReturnType; } case FUNCTION_NAME.searchCompanies: { - return new SearchCompaniesMapper() as TRestoreMapperReturnType; + return new ArrayWorkflowMapper( + 'st.searchCompanies', + ) as TRestoreMapperReturnType; } case FUNCTION_NAME.salesNavigatorSearchCompanies: { - return new NvSearchCompaniesMapper() as TRestoreMapperReturnType; + return new ArrayWorkflowMapper( + 'nv.searchCompanies', + ) as TRestoreMapperReturnType; } case FUNCTION_NAME.searchPeople: { - return new SearchPeopleMapper() as TRestoreMapperReturnType; + return new ArrayWorkflowMapper( + 'st.searchPeople', + ) as TRestoreMapperReturnType; } case FUNCTION_NAME.salesNavigatorSearchPeople: { - return new NvSearchPeopleMapper() as TRestoreMapperReturnType; + return new ArrayWorkflowMapper( + 'nv.searchPeople', + ) as TRestoreMapperReturnType; } case FUNCTION_NAME.sendMessage: { return new VoidWorkflowMapper( - "st.sendMessage", + 'st.sendMessage', ) as TRestoreMapperReturnType; } case FUNCTION_NAME.syncConversation: { return new VoidWorkflowMapper( - "st.syncConversation", + 'st.syncConversation', ) as TRestoreMapperReturnType; } case FUNCTION_NAME.salesNavigatorSendMessage: { return new VoidWorkflowMapper( - "nv.sendMessage", + 'nv.sendMessage', ) as TRestoreMapperReturnType; } case FUNCTION_NAME.salesNavigatorSyncConversation: { return new VoidWorkflowMapper( - "nv.syncConversation", + 'nv.syncConversation', ) as TRestoreMapperReturnType; } case FUNCTION_NAME.sendConnectionRequest: { return new VoidWorkflowMapper( - "st.sendConnectionRequest", + 'st.sendConnectionRequest', ) as TRestoreMapperReturnType; } case FUNCTION_NAME.checkConnectionStatus: { - return new SimpleWorkflowMapper< - TCheckConnectionStatusParams, - TCheckConnectionStatusResult - >({ - actionType: "st.checkConnectionStatus", + return new SimpleWorkflowMapper({ + actionType: 'st.checkConnectionStatus', }) as TRestoreMapperReturnType; } case FUNCTION_NAME.withdrawConnectionRequest: { return new VoidWorkflowMapper( - "st.withdrawConnectionRequest", + 'st.withdrawConnectionRequest', ) as TRestoreMapperReturnType; } case FUNCTION_NAME.retrievePendingRequests: { - return new RetrievePendingRequestsMapper() as TRestoreMapperReturnType; + return new ArrayWorkflowMapper( + 'st.retrievePendingRequests', + ) as TRestoreMapperReturnType; } case FUNCTION_NAME.retrieveConnections: { - return new RetrieveConnectionsMapper() as TRestoreMapperReturnType; + return new ArrayWorkflowMapper( + 'st.retrieveConnections', + ) as TRestoreMapperReturnType; } case FUNCTION_NAME.removeConnection: { return new VoidWorkflowMapper( - "st.removeConnection", + 'st.removeConnection', ) as TRestoreMapperReturnType; } case FUNCTION_NAME.reactToPost: { return new VoidWorkflowMapper( - "st.reactToPost", + 'st.reactToPost', ) as TRestoreMapperReturnType; } case FUNCTION_NAME.commentOnPost: { return new VoidWorkflowMapper( - "st.commentOnPost", + 'st.commentOnPost', ) as TRestoreMapperReturnType; } case FUNCTION_NAME.retrieveSSI: { return new SimpleWorkflowMapper({ - actionType: "st.retrieveSSI", + actionType: 'st.retrieveSSI', }) as TRestoreMapperReturnType; } case FUNCTION_NAME.retrievePerformance: { - return new SimpleWorkflowMapper< - TBaseActionParams, - TRetrievePerformanceResult - >({ - actionType: "st.retrievePerformance", + return new SimpleWorkflowMapper({ + actionType: 'st.retrievePerformance', }) as TRestoreMapperReturnType; } - case FUNCTION_NAME.executeCustomWorkflow: { + case FUNCTION_NAME.customWorkflow: { return null as TRestoreMapperReturnType; } default: diff --git a/src/index.ts b/src/index.ts index f5de71e..c4411ea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,80 +1,50 @@ -import type { TLinkedApiConfig } from "./types/config"; -import type { TWorkflowDefinition, TWorkflowResponse } from "./types/workflows"; -import type { TLinkedApiResponse } from "./types/responses"; -import { buildLinkedApiHttpClient } from "./core/linked-api-http-client"; -import { WorkflowExecutor } from "./core/workflow-executor"; -import { WorkflowHandler } from "./core/workflow-handler"; -import type { - TMappedResponse, - BaseMapper, -} from "./mappers/base-mapper.abstract"; - +import { buildLinkedApiHttpClient } from './core/linked-api-http-client'; +import { WorkflowExecutor } from './core/workflow-executor'; +import { WorkflowHandler } from './core/workflow-handler'; +import { + createMapperFromFunctionName, + TRestoreResultType, + TSupportedFunctionName, +} from './core/workflow-restoration'; +import type { BaseMapper, TMappedResponse } from './mappers/base-mapper.abstract'; import { - FetchCompanyMapper, - NvFetchCompanyMapper, - FetchPersonMapper, - NvFetchPersonMapper, - RetrieveConnectionsMapper, - RetrievePendingRequestsMapper, - NvSearchCompaniesMapper, - NvSearchPeopleMapper, - SearchCompaniesMapper, - SearchPeopleMapper, - SimpleWorkflowMapper, - VoidWorkflowMapper, -} from "./mappers"; + CheckConnectionStatus, + CommentOnPost, + FetchCompany, + FetchPerson, + FetchPost, + ReactToPost, + RemoveConnection, + RetrieveConnections, + RetrievePendingRequests, + RetrievePerformance, + RetrieveSSI, + SalesNavigatorFetchCompany, + SalesNavigatorFetchPerson, + SalesNavigatorSearchCompanies, + SalesNavigatorSearchPeople, + SalesNavigatorSendMessage, + SalesNavigatorSyncConversation, + SearchCompanies, + SearchPeople, + SendConnectionRequest, + SendMessage, + SyncConversation, + WithdrawConnectionRequest, +} from './operations'; import { - TSendMessageParams, - TSyncConversationParams, - TNvSendMessageParams, - TNvSyncConversationParams, + HttpClient, + TApiUsageAction, + TApiUsageStatsParams, + TApiUsageStatsResponse, + TBaseActionParams, TConversationPollRequest, TConversationPollResponse, TConversationPollResult, - TBaseFetchPersonParams, - TFetchPersonParams, - TFetchPersonResult, - TNvOpenPersonPageParams, - TNvOpenPersonPageResult, - TBaseFetchCompanyParams, - TFetchCompanyParams, - TFetchCompanyResult, - TNvBaseFetchCompanyParams, - TNvFetchCompanyParams, - TNvFetchCompanyResult, - TFetchPostParams, - TFetchPostResult, - TSearchCompanyParams, - TSearchCompanyResult, - TNvSearchCompanyParams, - TNvSearchCompanyResult, - TSearchPeopleParams, - TSearchPeopleResult, - TNvSearchPeopleParams, - TNvSearchPeopleResult, - TSendConnectionRequestParams, - TCheckConnectionStatusParams, - TCheckConnectionStatusResult, - TWithdrawConnectionRequestParams, - TRetrievePendingRequestsResult, - TRetrieveConnectionsParams, - TRetrieveConnectionsResult, - TRemoveConnectionParams, - TReactToPostParams, - TCommentOnPostParams, - TRetrieveSSIResult, - TBaseActionParams, - TRetrievePerformanceResult, - TApiUsageStatsParams, - TApiUsageStatsResponse, - TApiUsageAction, - HttpClient, -} from "./types"; -import { - TRestoreResultType, - TSupportedFunctionName, - createMapperFromFunctionName, -} from "./core/workflow-restoration"; +} from './types'; +import type { TLinkedApiConfig } from './types/config'; +import type { TLinkedApiResponse } from './types/responses'; +import type { TWorkflowDefinition, TWorkflowResponse } from './types/workflows'; /** * LinkedApi - Official TypeScript SDK for Linked API @@ -114,19 +84,37 @@ class LinkedApi { * @param config - Configuration object containing API tokens and optional settings * @returns LinkedApi instance with access to LinkedIn automation features */ - constructor(config: TLinkedApiConfig); - constructor(httpClient: HttpClient); - constructor(configOrClient: TLinkedApiConfig | HttpClient) { - if (configOrClient instanceof HttpClient) { - this.httpClient = configOrClient; - } else { - this.httpClient = buildLinkedApiHttpClient(configOrClient); - } + constructor(config: TLinkedApiConfig) { + this.httpClient = buildLinkedApiHttpClient(config); this.workflowExecutor = new WorkflowExecutor({ httpClient: this.httpClient, - apiPath: "/workflows", + apiPath: '/workflows', workflowTimeout: 24 * 60 * 60 * 1000, }); + + this.fetchPerson = new FetchPerson(this.httpClient); + this.searchCompanies = new SearchCompanies(this.httpClient); + this.fetchCompany = new FetchCompany(this.httpClient); + this.salesNavigatorFetchCompany = new SalesNavigatorFetchCompany(this.httpClient); + this.sendMessage = new SendMessage(this.httpClient); + this.salesNavigatorSendMessage = new SalesNavigatorSendMessage(this.httpClient); + this.syncConversation = new SyncConversation(this.httpClient); + this.salesNavigatorSyncConversation = new SalesNavigatorSyncConversation(this.httpClient); + this.salesNavigatorFetchPerson = new SalesNavigatorFetchPerson(this.httpClient); + this.salesNavigatorSearchCompanies = new SalesNavigatorSearchCompanies(this.httpClient); + this.fetchPost = new FetchPost(this.httpClient); + this.searchPeople = new SearchPeople(this.httpClient); + this.salesNavigatorSearchPeople = new SalesNavigatorSearchPeople(this.httpClient); + this.sendConnectionRequest = new SendConnectionRequest(this.httpClient); + this.checkConnectionStatus = new CheckConnectionStatus(this.httpClient); + this.withdrawConnectionRequest = new WithdrawConnectionRequest(this.httpClient); + this.retrievePendingRequests = new RetrievePendingRequests(this.httpClient); + this.retrieveConnections = new RetrieveConnections(this.httpClient); + this.removeConnection = new RemoveConnection(this.httpClient); + this.reactToPost = new ReactToPost(this.httpClient); + this.commentOnPost = new CommentOnPost(this.httpClient); + this.retrieveSSI = new RetrieveSSI(this.httpClient); + this.retrievePerformance = new RetrievePerformance(this.httpClient); } /** @@ -169,15 +157,9 @@ class LinkedApi { * const result = await workflow.result(); * ``` */ - public async executeCustomWorkflow( - params: TWorkflowDefinition, - ): Promise { + public async executeCustomWorkflow(params: TWorkflowDefinition): Promise { const workflow = await this.workflowExecutor.startWorkflow(params); - return new WorkflowHandler( - workflow.workflowId, - "executeCustomWorkflow", - this.workflowExecutor, - ); + return new WorkflowHandler(workflow.workflowId, 'customWorkflow', this.workflowExecutor); } /** @@ -239,31 +221,16 @@ class LinkedApi { * * @example * ```typescript - * const messageWorkflow = await linkedapi.sendMessage({ + * const workflowId = await linkedapi.sendMessage.execute({ * personUrl: "https://www.linkedin.com/in/john-doe", * text: "Hi John! I saw your recent post about AI and would love to discuss further." * }); * - * await messageWorkflow.result(); + * await linkedapi.sendMessage.result(workflowId); * console.log("Message sent successfully"); * ``` */ - public async sendMessage( - params: TSendMessageParams, - ): Promise> { - const sendMessageMapper = new VoidWorkflowMapper( - "st.sendMessage", - ); - const workflowDefinition = sendMessageMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "sendMessage" as const, - this.workflowExecutor, - sendMessageMapper, - ); - } + public sendMessage: SendMessage; /** * Sync a conversation with a LinkedIn user for standard LinkedIn messaging. @@ -280,29 +247,15 @@ class LinkedApi { * * @example * ```typescript - * const syncWorkflow = await linkedapi.syncConversation({ + * const workflowId = await linkedapi.syncConversation.execute({ * personUrl: "https://www.linkedin.com/in/john-doe" * }); * - * await syncWorkflow.result(); + * await linkedapi.syncConversation.result(workflowId); * console.log("Conversation synced and ready for polling"); * ``` */ - public async syncConversation( - params: TSyncConversationParams, - ): Promise> { - const syncConversationMapper = - new VoidWorkflowMapper("st.syncConversation"); - const workflowDefinition = syncConversationMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "syncConversation" as const, - this.workflowExecutor, - syncConversationMapper, - ); - } + public syncConversation: SyncConversation; /** * Send a message to a LinkedIn user via Sales Navigator. @@ -318,32 +271,17 @@ class LinkedApi { * * @example * ```typescript - * const nvMessageWorkflow = await linkedapi.salesNavigatorSendMessage({ + * const workflowId = await linkedapi.salesNavigatorSendMessage.execute({ * personUrl: "https://www.linkedin.com/in/john-doe", * text: "Hi John! I'm reaching out regarding potential collaboration opportunities.", * subject: "Partnership Opportunity" * }); * - * await nvMessageWorkflow.result(); + * await linkedapi.salesNavigatorSendMessage.result(workflowId); * console.log("Sales Navigator message sent successfully"); * ``` */ - public async salesNavigatorSendMessage( - params: TNvSendMessageParams, - ): Promise> { - const nvSendMessageMapper = new VoidWorkflowMapper( - "nv.sendMessage", - ); - const workflowDefinition = nvSendMessageMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "salesNavigatorSendMessage" as const, - this.workflowExecutor, - nvSendMessageMapper, - ); - } + public salesNavigatorSendMessage: SalesNavigatorSendMessage; /** * Sync a conversation with a LinkedIn user for Sales Navigator messaging. @@ -360,29 +298,15 @@ class LinkedApi { * * @example * ```typescript - * const nvSyncWorkflow = await linkedapi.salesNavigatorSyncConversation({ + * const workflowId = await linkedapi.salesNavigatorSyncConversation.execute({ * personUrl: "https://www.linkedin.com/in/john-doe" * }); * - * await nvSyncWorkflow.result(); + * await linkedapi.salesNavigatorSyncConversation.result(workflowId); * console.log("Sales Navigator conversation synced and ready for polling"); * ``` */ - public async salesNavigatorSyncConversation( - params: TNvSyncConversationParams, - ): Promise> { - const nvSyncConversationMapper = - new VoidWorkflowMapper("nv.syncConversation"); - const workflowDefinition = nvSyncConversationMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "salesNavigatorSyncConversation" as const, - this.workflowExecutor, - nvSyncConversationMapper, - ); - } + public salesNavigatorSyncConversation: SalesNavigatorSyncConversation; /** * Poll multiple conversations to retrieve message history and new messages. @@ -429,7 +353,7 @@ class LinkedApi { conversations: TConversationPollRequest[], ): Promise { const response = await this.httpClient.post( - "/conversations/poll", + '/conversations/poll', conversations, ); @@ -462,7 +386,7 @@ class LinkedApi { * @example * ```typescript * // Fetch comprehensive person information with type-safe parameters - * const personWorkflow = await linkedapi.fetchPerson({ + * const workflowId = await linkedapi.fetchPerson.execute({ * personUrl: "https://www.linkedin.com/in/john-doe", * retrieveExperience: true, * retrieveEducation: true, @@ -485,7 +409,7 @@ class LinkedApi { * } * }); * - * const personResult = await personWorkflow.result(); + * const personResult = await linkedapi.fetchPerson.result(workflowId); * if (personResult.data) { * console.log("Person name:", personResult.data.name); * console.log("Headline:", personResult.data.headline); @@ -497,30 +421,17 @@ class LinkedApi { * @example * ```typescript * // Simple fetch without additional data - no config objects needed - * const basicPersonWorkflow = await linkedapi.fetchPerson({ + * const workflowId = await linkedapi.fetchPerson.execute({ * personUrl: "https://www.linkedin.com/in/john-doe" * }); * - * const basicResult = await basicPersonWorkflow.result(); + * const basicResult = await linkedapi.fetchPerson.result(workflowId); * if (basicResult.data) { * console.log("Basic info:", basicResult.data.name, basicResult.data.headline); * } * ``` */ - public async fetchPerson( - params: TFetchPersonParams, - ): Promise>> { - const fetchPersonMapper = new FetchPersonMapper(); - const workflowDefinition = fetchPersonMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler>( - workflowId, - "fetchPerson", - this.workflowExecutor, - fetchPersonMapper, - ); - } + public fetchPerson: FetchPerson; /** * Retrieve person information via Sales Navigator. @@ -536,28 +447,15 @@ class LinkedApi { * * @example * ```typescript - * const nvPersonWorkflow = await linkedapi.salesNavigatorFetchPerson({ + * const workflowId = await linkedapi.salesNavigatorFetchPerson.execute({ * personHashedUrl: "https://www.linkedin.com/in/ABC123", * }); * - * const personResult = await nvPersonWorkflow.result(); + * const personResult = await linkedapi.salesNavigatorFetchPerson.result(workflowId); * console.log("Sales Navigator data:", personResult.data); * ``` */ - public async salesNavigatorFetchPerson( - params: TNvOpenPersonPageParams, - ): Promise> { - const nvOpenPersonPageMapper = new NvFetchPersonMapper(); - const workflowDefinition = nvOpenPersonPageMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "salesNavigatorFetchPerson", - this.workflowExecutor, - nvOpenPersonPageMapper, - ); - } + public salesNavigatorFetchPerson: SalesNavigatorFetchPerson; /** * Retrieve detailed information about a LinkedIn company profile. @@ -576,7 +474,7 @@ class LinkedApi { * @example * ```typescript * // Fetch company information with employees and posts (new simplified syntax) - * const companyWorkflow = await linkedapi.fetchCompany({ + * const workflowId = await linkedapi.fetchCompany.execute({ * companyUrl: "https://www.linkedin.com/company/microsoft", * retrieveEmployees: true, * retrievePosts: true, @@ -596,7 +494,7 @@ class LinkedApi { * dmsRetrievalConfig: { limit: 3 } * }); * - * const companyResult = await companyWorkflow.result(); + * const companyResult = await linkedapi.fetchCompany.result(workflowId); * if (companyResult.data) { * console.log("Company name:", companyResult.data.name); * console.log("Employee count:", companyResult.data.employees?.length); @@ -604,20 +502,7 @@ class LinkedApi { * } * ``` */ - public async fetchCompany( - params: TFetchCompanyParams, - ): Promise>> { - const fetchCompanyMapper = new FetchCompanyMapper(); - const workflowDefinition = fetchCompanyMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler>( - workflowId, - "fetchCompany" as const, - this.workflowExecutor, - fetchCompanyMapper, - ); - } + public fetchCompany: FetchCompany; /** * Retrieve company information via Sales Navigator. @@ -635,7 +520,7 @@ class LinkedApi { * @example * ```typescript * // Sales Navigator company fetch (new simplified syntax) - * const nvCompanyWorkflow = await linkedapi.salesNavigatorFetchCompany({ + * const workflowId = await linkedapi.salesNavigatorFetchCompany.execute({ * companyHashedUrl: 'https://www.linkedin.com/sales/company/1035', * retrieveEmployees: true, * retrieveDMs: true, @@ -653,7 +538,7 @@ class LinkedApi { * }, * }); * - * const companyResult = await nvCompanyWorkflow.result(); + * const companyResult = await linkedapi.salesNavigatorFetchCompany.result(workflowId); * if (companyResult.data) { * console.log("Company name:", companyResult.data.name); * console.log("Employees:", companyResult.data.employees?.length); @@ -661,22 +546,7 @@ class LinkedApi { * } * ``` */ - public async salesNavigatorFetchCompany< - TParams extends TNvBaseFetchCompanyParams, - >( - params: TNvFetchCompanyParams, - ): Promise>> { - const fetchNvCompanyMapper = new NvFetchCompanyMapper(); - const workflowDefinition = fetchNvCompanyMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler>( - workflowId, - "salesNavigatorFetchCompany" as const, - this.workflowExecutor, - fetchNvCompanyMapper, - ); - } + public salesNavigatorFetchCompany: SalesNavigatorFetchCompany; /** * Retrieve detailed information about a LinkedIn post. @@ -691,40 +561,19 @@ class LinkedApi { * * @example * ```typescript - * const postWorkflow = await linkedapi.fetchPost({ + * const workflowId = await linkedapi.fetchPost.execute({ * postUrl: "https://www.linkedin.com/posts/john-doe_activity-123456789" * }); * - * const postResult = await postWorkflow.result(); - * if (postResult.data) { - * console.log("Post content:", postResult.data.text); - * console.log("Author:", postResult.data.author); - * console.log("Reactions:", postResult.data.reactions); + * const result = await linkedapi.fetchPost.result(workflowId); + * if (result.data) { + * console.log("Post content:", result.data.text); + * console.log("Author:", result.data.author); + * console.log("Reactions:", result.data.reactions); * } * ``` */ - public async fetchPost( - params: TFetchPostParams, - ): Promise> { - const fetchPostMapper = new SimpleWorkflowMapper< - TFetchPostParams, - TFetchPostResult - >({ - actionType: "st.openPost", - defaultParams: { - basicInfo: true, - }, - }); - const workflowDefinition = fetchPostMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "fetchPost" as const, - this.workflowExecutor, - fetchPostMapper, - ); - } + public fetchPost: FetchPost; /** * Search for companies on LinkedIn using standard search. @@ -739,7 +588,7 @@ class LinkedApi { * * @example * ```typescript - * const companySearchWorkflow = await linkedapi.searchCompanies({ + * const workflowId = await linkedapi.searchCompanies.execute({ * term: "software development", * filter: { * locations: ["San Francisco", "New York"], @@ -749,26 +598,13 @@ class LinkedApi { * limit: 25 * }); * - * const companiesResult = await companySearchWorkflow.result(); + * const companiesResult = await linkedapi.searchCompanies.result(workflowId); * if (companiesResult.data) { * console.log("Found companies:", companiesResult.data.length); * } * ``` */ - public async searchCompanies( - params: TSearchCompanyParams, - ): Promise> { - const searchCompaniesMapper = new SearchCompaniesMapper(); - const workflowDefinition = searchCompaniesMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "searchCompanies" as const, - this.workflowExecutor, - searchCompaniesMapper, - ); - } + public searchCompanies: SearchCompanies; /** * Search for companies on LinkedIn using Sales Navigator. @@ -783,7 +619,7 @@ class LinkedApi { * * @example * ```typescript - * const nvCompanySearchWorkflow = await linkedapi.salesNavigatorSearchCompanies({ + * const workflowId = await linkedapi.salesNavigatorSearchCompanies.execute({ * term: "fintech startup", * filter: { * locations: ["United States"], @@ -797,27 +633,13 @@ class LinkedApi { * limit: 50 * }); * - * const companiesResult = await nvCompanySearchWorkflow.result(); + * const companiesResult = await linkedapi.salesNavigatorSearchCompanies.result(workflowId); * if (companiesResult.data) { * console.log("Sales Navigator companies:", companiesResult.data.length); * } * ``` */ - public async salesNavigatorSearchCompanies( - params: TNvSearchCompanyParams, - ): Promise> { - const salesNavigatorSearchCompaniesMapper = new NvSearchCompaniesMapper(); - const workflowDefinition = - salesNavigatorSearchCompaniesMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "salesNavigatorSearchCompanies" as const, - this.workflowExecutor, - salesNavigatorSearchCompaniesMapper, - ); - } + public salesNavigatorSearchCompanies: SalesNavigatorSearchCompanies; /** * Search for people on LinkedIn using standard search. @@ -832,7 +654,7 @@ class LinkedApi { * * @example * ```typescript - * const peopleSearchWorkflow = await linkedapi.searchPeople({ + * const workflowId = await linkedapi.searchPeople.execute({ * term: "software engineer React", * filter: { * locations: ["San Francisco Bay Area"], @@ -842,26 +664,13 @@ class LinkedApi { * limit: 50 * }); * - * const peopleResult = await peopleSearchWorkflow.result(); - * if (peopleResult.data) { - * console.log("Found professionals:", peopleResult.data.length); + * const result = await linkedapi.searchPeople.result(workflowId); + * if (result.data) { + * console.log("Found professionals:", result.data.length); * } * ``` */ - public async searchPeople( - params: TSearchPeopleParams, - ): Promise> { - const searchPeopleMapper = new SearchPeopleMapper(); - const workflowDefinition = searchPeopleMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "searchPeople" as const, - this.workflowExecutor, - searchPeopleMapper, - ); - } + public searchPeople: SearchPeople; /** * Search for people on LinkedIn using Sales Navigator. @@ -876,7 +685,7 @@ class LinkedApi { * * @example * ```typescript - * const nvPeopleSearchWorkflow = await linkedapi.salesNavigatorSearchPeople({ + * const workflowId = await linkedapi.salesNavigatorSearchPeople.execute({ * term: "VP Marketing B2B SaaS", * filter: { * locations: ["United States"], @@ -886,27 +695,13 @@ class LinkedApi { * limit: 25 * }); * - * const prospectsResult = await nvPeopleSearchWorkflow.result(); - * if (prospectsResult.data) { - * console.log("Sales Navigator prospects:", prospectsResult.data.length); + * const result = await linkedapi.salesNavigatorSearchPeople.result(workflowId); + * if (result.data) { + * console.log("Sales Navigator prospects:", result.data.length); * } * ``` */ - public async salesNavigatorSearchPeople( - params: TNvSearchPeopleParams, - ): Promise> { - const salesNavigatorSearchPeopleMapper = new NvSearchPeopleMapper(); - const workflowDefinition = - salesNavigatorSearchPeopleMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "salesNavigatorSearchPeople" as const, - this.workflowExecutor, - salesNavigatorSearchPeopleMapper, - ); - } + public salesNavigatorSearchPeople: SalesNavigatorSearchPeople; /** * Send a connection request to a LinkedIn user. @@ -922,32 +717,16 @@ class LinkedApi { * * @example * ```typescript - * const connectionWorkflow = await linkedapi.sendConnectionRequest({ + * const workflowId = await linkedapi.sendConnectionRequest.execute({ * personUrl: "https://www.linkedin.com/in/john-doe", * note: "Hi John, I'd love to connect and discuss opportunities in tech!" * }); * - * await connectionWorkflow.result(); + * await linkedapi.sendConnectionRequest.result(workflowId); * console.log("Connection request sent successfully"); * ``` */ - public async sendConnectionRequest( - params: TSendConnectionRequestParams, - ): Promise> { - const sendConnectionRequestMapper = - new VoidWorkflowMapper( - "st.sendConnectionRequest", - ); - const workflowDefinition = sendConnectionRequestMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "sendConnectionRequest" as const, - this.workflowExecutor, - sendConnectionRequestMapper, - ); - } + public sendConnectionRequest: SendConnectionRequest; /** * Check the connection status with a specific LinkedIn user. @@ -963,35 +742,17 @@ class LinkedApi { * * @example * ```typescript - * const statusWorkflow = await linkedapi.checkConnectionStatus({ + * const workflowId = await linkedapi.checkConnectionStatus.execute({ * personUrl: "https://www.linkedin.com/in/john-doe" * }); * - * const statusResult = await statusWorkflow.result(); - * if (statusResult.data) { - * console.log("Connection status:", statusResult.data.connectionStatus); + * const result = await linkedapi.checkConnectionStatus.result(workflowId); + * if (result.data) { + * console.log("Connection status:", result.data.connectionStatus); * } * ``` */ - public async checkConnectionStatus( - params: TCheckConnectionStatusParams, - ): Promise> { - const checkConnectionStatusMapper = new SimpleWorkflowMapper< - TCheckConnectionStatusParams, - TCheckConnectionStatusResult - >({ - actionType: "st.checkConnectionStatus", - }); - const workflowDefinition = checkConnectionStatusMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "checkConnectionStatus" as const, - this.workflowExecutor, - checkConnectionStatusMapper, - ); - } + public checkConnectionStatus: CheckConnectionStatus; /** * Withdraw a previously sent connection request. @@ -1007,32 +768,15 @@ class LinkedApi { * * @example * ```typescript - * const withdrawWorkflow = await linkedapi.withdrawConnectionRequest({ + * const workflowId = await linkedapi.withdrawConnectionRequest.execute({ * personUrl: "https://www.linkedin.com/in/john-doe" * }); * - * await withdrawWorkflow.result(); + * await linkedapi.withdrawConnectionRequest.result(workflowId); * console.log("Connection request withdrawn successfully"); * ``` */ - public async withdrawConnectionRequest( - params: TWithdrawConnectionRequestParams, - ): Promise> { - const withdrawConnectionRequestMapper = - new VoidWorkflowMapper( - "st.withdrawConnectionRequest", - ); - const workflowDefinition = - withdrawConnectionRequestMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "withdrawConnectionRequest" as const, - this.workflowExecutor, - withdrawConnectionRequestMapper, - ); - } + public withdrawConnectionRequest: WithdrawConnectionRequest; /** * Retrieve all pending connection requests you have received. @@ -1047,33 +791,20 @@ class LinkedApi { * * @example * ```typescript - * const pendingWorkflow = await linkedapi.retrievePendingRequests(); + * const workflowId = await linkedapi.retrievePendingRequests.execute(); * - * const pendingResult = await pendingWorkflow.result(); - * if (pendingResult.data) { - * console.log("Pending requests:", pendingResult.data.length); + * const result = await linkedapi.retrievePendingRequests.result(workflowId); + * if (result.data) { + * console.log("Pending requests:", result.data.length); * - * pendingResult.data.forEach(request => { + * result.data.forEach(request => { * console.log(`${request.name}: ${request.headline}`); * console.log(`Profile: ${request.publicUrl}`); * }); * } * ``` */ - public async retrievePendingRequests(): Promise< - WorkflowHandler - > { - const retrievePendingRequestsMapper = new RetrievePendingRequestsMapper(); - const workflowDefinition = retrievePendingRequestsMapper.mapRequest({}); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "retrievePendingRequests" as const, - this.workflowExecutor, - retrievePendingRequestsMapper, - ); - } + public retrievePendingRequests: RetrievePendingRequests; /** * Retrieve your LinkedIn connections with optional filtering. @@ -1089,7 +820,7 @@ class LinkedApi { * * @example * ```typescript - * const connectionsWorkflow = await linkedapi.retrieveConnections({ + * const workflowId = await linkedapi.retrieveConnections.execute({ * filter: { * firstName: "John", * industries: ["Technology", "Software"], @@ -1099,26 +830,13 @@ class LinkedApi { * limit: 50 * }); * - * const connectionsResult = await connectionsWorkflow.result(); - * if (connectionsResult.data) { - * console.log("Filtered connections:", connectionsResult.data.length); + * const result = await linkedapi.retrieveConnections.result(workflowId); + * if (result.data) { + * console.log("Filtered connections:", result.data.length); * } * ``` */ - public async retrieveConnections( - params: TRetrieveConnectionsParams = {}, - ): Promise> { - const retrieveConnectionsMapper = new RetrieveConnectionsMapper(); - const workflowDefinition = retrieveConnectionsMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "retrieveConnections" as const, - this.workflowExecutor, - retrieveConnectionsMapper, - ); - } + public retrieveConnections: RetrieveConnections; /** * Remove an existing connection from your LinkedIn network. @@ -1134,29 +852,15 @@ class LinkedApi { * * @example * ```typescript - * const removeWorkflow = await linkedapi.removeConnection({ + * const workflowId = await linkedapi.removeConnection.execute({ * personUrl: "https://www.linkedin.com/in/john-doe" * }); * - * await removeWorkflow.result(); + * await linkedapi.removeConnection.result(workflowId); * console.log("Connection removed successfully"); * ``` */ - public async removeConnection( - params: TRemoveConnectionParams, - ): Promise> { - const removeConnectionMapper = - new VoidWorkflowMapper("st.removeConnection"); - const workflowDefinition = removeConnectionMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "removeConnection" as const, - this.workflowExecutor, - removeConnectionMapper, - ); - } + public removeConnection: RemoveConnection; /** * React to a LinkedIn post with an emoji reaction. @@ -1172,31 +876,16 @@ class LinkedApi { * * @example * ```typescript - * const reactionWorkflow = await linkedapi.reactToPost({ + * const workflowId = await linkedapi.reactToPost.execute({ * postUrl: "https://www.linkedin.com/posts/john-doe_activity-123456789", * type: "like" * }); * - * await reactionWorkflow.result(); + * await linkedapi.reactToPost.result(workflowId); * console.log("Post reaction added successfully"); * ``` */ - public async reactToPost( - params: TReactToPostParams, - ): Promise> { - const reactToPostMapper = new VoidWorkflowMapper( - "st.reactToPost", - ); - const workflowDefinition = reactToPostMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "reactToPost" as const, - this.workflowExecutor, - reactToPostMapper, - ); - } + public reactToPost: ReactToPost; /** * Comment on a LinkedIn post. @@ -1212,31 +901,16 @@ class LinkedApi { * * @example * ```typescript - * const commentWorkflow = await linkedapi.commentOnPost({ + * const workflowId = await linkedapi.commentOnPost.execute({ * postUrl: "https://www.linkedin.com/posts/john-doe_activity-123456789", * text: "Great insights! Thanks for sharing this valuable information." * }); * - * await commentWorkflow.result(); + * await linkedapi.commentOnPost.result(workflowId); * console.log("Comment posted successfully"); * ``` */ - public async commentOnPost( - params: TCommentOnPostParams, - ): Promise> { - const commentOnPostMapper = new VoidWorkflowMapper( - "st.commentOnPost", - ); - const workflowDefinition = commentOnPostMapper.mapRequest(params); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "commentOnPost" as const, - this.workflowExecutor, - commentOnPostMapper, - ); - } + public commentOnPost: CommentOnPost; /** * Retrieve your LinkedIn Social Selling Index (SSI) score. @@ -1252,33 +926,17 @@ class LinkedApi { * * @example * ```typescript - * const ssiWorkflow = await linkedapi.retrieveSSI(); + * const workflowId = await linkedapi.retrieveSSI.execute(); * - * const ssiResult = await ssiWorkflow.result(); - * if (ssiResult.data) { - * console.log("SSI Score:", ssiResult.data.ssi); - * console.log("Industry Ranking:", ssiResult.data.industryTop); - * console.log("Network Ranking:", ssiResult.data.networkTop); + * const result = await linkedapi.retrieveSSI.result(workflowId); + * if (result.data) { + * console.log("SSI Score:", result.data.ssi); + * console.log("Industry Ranking:", result.data.industryTop); + * console.log("Network Ranking:", result.data.networkTop); * } * ``` */ - public async retrieveSSI(): Promise> { - const retrieveSSIMapper = new SimpleWorkflowMapper< - TBaseActionParams, - TRetrieveSSIResult - >({ - actionType: "st.retrieveSSI", - }); - const workflowDefinition = retrieveSSIMapper.mapRequest({}); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "retrieveSSI" as const, - this.workflowExecutor, - retrieveSSIMapper, - ); - } + public retrieveSSI: RetrieveSSI; /** * Retrieve your LinkedIn performance and analytics data. @@ -1293,35 +951,17 @@ class LinkedApi { * * @example * ```typescript - * const performanceWorkflow = await linkedapi.retrievePerformance(); + * const workflowId = await linkedapi.retrievePerformance.execute(); * - * const performanceResult = await performanceWorkflow.result(); - * if (performanceResult.data) { - * console.log("Profile views:", performanceResult.data.profileViews); - * console.log("Search appearances:", performanceResult.data.searchAppearances); - * console.log("Post impressions:", performanceResult.data.postImpressions); + * const result = await linkedapi.retrievePerformance.result(workflowId); + * if (result.data) { + * console.log("Profile views:", result.data.profileViews); + * console.log("Search appearances:", result.data.searchAppearances); + * console.log("Post impressions:", result.data.postImpressions); * } * ``` */ - public async retrievePerformance(): Promise< - WorkflowHandler - > { - const retrievePerformanceMapper = new SimpleWorkflowMapper< - TBaseActionParams, - TRetrievePerformanceResult - >({ - actionType: "st.retrievePerformance", - }); - const workflowDefinition = retrievePerformanceMapper.mapRequest({}); - const { workflowId } = - await this.workflowExecutor.startWorkflow(workflowDefinition); - return new WorkflowHandler( - workflowId, - "retrievePerformance" as const, - this.workflowExecutor, - retrievePerformanceMapper, - ); - } + public retrievePerformance: RetrievePerformance; /** * Retrieve Linked API usage statistics for a specific time period. @@ -1357,9 +997,7 @@ class LinkedApi { * } * ``` */ - public async getApiUsageStats( - params: TApiUsageStatsParams, - ): Promise { + public async getApiUsageStats(params: TApiUsageStatsParams): Promise { const queryParams = new URLSearchParams({ start: params.start, end: params.end, @@ -1389,5 +1027,6 @@ export type { TMappedResponse, }; -export * from "./types"; -export * from "./core/workflow-restoration"; +export * from './types'; +export * from './core/workflow-restoration'; +export * from './operations'; diff --git a/src/mappers/array-workflow-mapper.abstract.ts b/src/mappers/array-workflow-mapper.ts similarity index 58% rename from src/mappers/array-workflow-mapper.abstract.ts rename to src/mappers/array-workflow-mapper.ts index 65be4c9..e12f98e 100644 --- a/src/mappers/array-workflow-mapper.abstract.ts +++ b/src/mappers/array-workflow-mapper.ts @@ -1,22 +1,12 @@ -import { TLinkedApiActionError } from "../types/errors"; -import type { TBaseActionParams } from "../types/params"; -import type { - TWorkflowDefinition, - TWorkflowResponse, -} from "../types/workflows"; -import { BaseMapper, TMappedResponse } from "./base-mapper.abstract"; +import { TLinkedApiActionError } from '../types/errors'; +import type { TWorkflowDefinition, TWorkflowResponse } from '../types/workflows'; -export interface TDefaultParameters { - [key: string]: unknown; -} +import { BaseMapper, TMappedResponse } from './base-mapper.abstract'; -export abstract class ArrayWorkflowMapper< - TParams extends TBaseActionParams, - TResult, -> extends BaseMapper { +export class ArrayWorkflowMapper extends BaseMapper { private readonly baseActionType: string; - constructor({ baseActionType }: { baseActionType: string }) { + constructor(baseActionType: string) { super(); this.baseActionType = baseActionType; } @@ -34,9 +24,7 @@ export abstract class ArrayWorkflowMapper< if (Array.isArray(completion)) { return { data: completion.map((item) => item.data) as TResult[], - errors: completion - .map((item) => item.error) - .filter(Boolean) as TLinkedApiActionError[], + errors: completion.map((item) => item.error).filter(Boolean) as TLinkedApiActionError[], }; } diff --git a/src/mappers/base-mapper.abstract.ts b/src/mappers/base-mapper.abstract.ts index 4ae061e..b53dbf8 100644 --- a/src/mappers/base-mapper.abstract.ts +++ b/src/mappers/base-mapper.abstract.ts @@ -1,16 +1,11 @@ -import { - LinkedApiError, - TLinkedApiErrorType, - type TLinkedApiActionError, -} from "../types/errors"; -import type { TBaseActionParams } from "../types/params"; +import { LinkedApiError, type TLinkedApiActionError, TLinkedApiErrorType } from '../types/errors'; import type { TWorkflowCompletion, TWorkflowDefinition, TWorkflowResponse, -} from "../types/workflows"; +} from '../types/workflows'; -export abstract class BaseMapper { +export abstract class BaseMapper { abstract mapRequest(params: TParams): TWorkflowDefinition; abstract mapResponse(response: TWorkflowResponse): TMappedResponse; @@ -18,10 +13,7 @@ export abstract class BaseMapper { if (!response.completion) { const { failure } = response; if (failure) { - throw new LinkedApiError( - failure.reason as TLinkedApiErrorType, - failure.message, - ); + throw new LinkedApiError(failure.reason as TLinkedApiErrorType, failure.message); } throw LinkedApiError.unknownError(); } diff --git a/src/mappers/fetch-company-mapper.ts b/src/mappers/fetch-company-mapper.ts deleted file mode 100644 index 17a5b11..0000000 --- a/src/mappers/fetch-company-mapper.ts +++ /dev/null @@ -1,56 +0,0 @@ -import type { - TBaseFetchCompanyParams, - TFetchCompanyResult, -} from "../types/actions/company"; -import { - ThenWorkflowMapper, - type TActionConfig, -} from "./then-workflow-mapper.abstract"; - -const FETCH_COMPANY_ACTIONS: TActionConfig[] = [ - { - paramName: "retrieveEmployees", - actionType: "st.retrieveCompanyEmployees", - configSource: "employeesRetrievalConfig", - }, - { - paramName: "retrieveDMs", - actionType: "st.retrieveCompanyDMs", - configSource: "dmsRetrievalConfig", - }, - { - paramName: "retrievePosts", - actionType: "st.retrieveCompanyPosts", - configSource: "postsRetrievalConfig", - }, -]; - -const RESPONSE_MAPPINGS = [ - { - actionType: "st.retrieveCompanyEmployees", - targetProperty: "employees", - }, - { - actionType: "st.retrieveCompanyDMs", - targetProperty: "dms", - }, - { - actionType: "st.retrieveCompanyPosts", - targetProperty: "posts", - }, -]; - -export class FetchCompanyMapper< - TParams extends TBaseFetchCompanyParams, -> extends ThenWorkflowMapper> { - constructor() { - super({ - actionConfigs: FETCH_COMPANY_ACTIONS, - responseMappings: RESPONSE_MAPPINGS, - baseActionType: "st.openCompanyPage", - defaultParams: { - basicInfo: true, - }, - }); - } -} diff --git a/src/mappers/fetch-person-mapper.ts b/src/mappers/fetch-person-mapper.ts deleted file mode 100644 index fe97700..0000000 --- a/src/mappers/fetch-person-mapper.ts +++ /dev/null @@ -1,88 +0,0 @@ -import type { - TBaseFetchPersonParams, - TFetchPersonResult, -} from "../types/actions/person"; -import { - ThenWorkflowMapper, - type TActionConfig, -} from "./then-workflow-mapper.abstract"; - -const FETCH_PERSON_ACTIONS: TActionConfig[] = [ - { - paramName: "retrieveExperience", - actionType: "st.retrievePersonExperience", - }, - { - paramName: "retrieveEducation", - actionType: "st.retrievePersonEducation", - }, - { - paramName: "retrieveSkills", - actionType: "st.retrievePersonSkills", - }, - { - paramName: "retrieveLanguages", - actionType: "st.retrievePersonLanguages", - }, - { - paramName: "retrievePosts", - actionType: "st.retrievePersonPosts", - configSource: "postsRetrievalConfig", - }, - { - paramName: "retrieveComments", - actionType: "st.retrievePersonComments", - configSource: "commentsRetrievalConfig", - }, - { - paramName: "retrieveReactions", - actionType: "st.retrievePersonReactions", - configSource: "reactionsRetrievalConfig", - }, -]; - -const RESPONSE_MAPPINGS = [ - { - actionType: "st.retrievePersonExperience", - targetProperty: "experiences", - }, - { - actionType: "st.retrievePersonEducation", - targetProperty: "education", - }, - { - actionType: "st.retrievePersonSkills", - targetProperty: "skills", - }, - { - actionType: "st.retrievePersonLanguages", - targetProperty: "languages", - }, - { - actionType: "st.retrievePersonPosts", - targetProperty: "posts", - }, - { - actionType: "st.retrievePersonComments", - targetProperty: "comments", - }, - { - actionType: "st.retrievePersonReactions", - targetProperty: "reactions", - }, -]; - -export class FetchPersonMapper< - TParams extends TBaseFetchPersonParams, -> extends ThenWorkflowMapper> { - constructor() { - super({ - actionConfigs: FETCH_PERSON_ACTIONS, - responseMappings: RESPONSE_MAPPINGS, - baseActionType: "st.openPersonPage", - defaultParams: { - basicInfo: true, - }, - }); - } -} diff --git a/src/mappers/index.ts b/src/mappers/index.ts index eafb4e8..ce47d84 100644 --- a/src/mappers/index.ts +++ b/src/mappers/index.ts @@ -1,12 +1,5 @@ -export * from "./fetch-company-mapper"; -export * from "./fetch-person-mapper"; -export * from "./nv-fetch-company-mapper"; -export * from "./nv-fetch-person-mapper"; -export * from "./retrieve-connections-mapper"; -export * from "./retrieve-pending-requests-mapper"; -export * from "./nv-search-companies-mapper"; -export * from "./nv-search-people-mapper"; -export * from "./search-companies-mapper"; -export * from "./search-people-mapper"; -export * from "./simple-workflow-mapper"; -export * from "./void-workflow-mapper"; +export * from './base-mapper.abstract'; +export * from './then-workflow-mapper.abstract'; +export * from './array-workflow-mapper'; +export * from './simple-workflow-mapper'; +export * from './void-workflow-mapper'; diff --git a/src/mappers/nv-fetch-company-mapper.ts b/src/mappers/nv-fetch-company-mapper.ts deleted file mode 100644 index 43733d0..0000000 --- a/src/mappers/nv-fetch-company-mapper.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { - TNvBaseFetchCompanyParams, - TNvFetchCompanyResult, -} from "../types/actions/company"; -import { - ThenWorkflowMapper, - type TActionConfig, -} from "./then-workflow-mapper.abstract"; - -const FETCH_NV_COMPANY_ACTIONS: TActionConfig[] = [ - { - paramName: "retrieveEmployees", - actionType: "nv.retrieveCompanyEmployees", - configSource: "employeesRetrievalConfig", - }, - { - paramName: "retrieveDMs", - actionType: "nv.retrieveCompanyDMs", - configSource: "dmsRetrievalConfig", - }, -]; - -const RESPONSE_MAPPINGS = [ - { - actionType: "nv.retrieveCompanyEmployees", - targetProperty: "employees", - }, - { - actionType: "nv.retrieveCompanyDMs", - targetProperty: "dms", - }, -]; - -export class NvFetchCompanyMapper< - TParams extends TNvBaseFetchCompanyParams, -> extends ThenWorkflowMapper> { - constructor() { - super({ - actionConfigs: FETCH_NV_COMPANY_ACTIONS, - responseMappings: RESPONSE_MAPPINGS, - baseActionType: "nv.openCompanyPage", - defaultParams: { - basicInfo: true, - }, - }); - } -} diff --git a/src/mappers/nv-fetch-person-mapper.ts b/src/mappers/nv-fetch-person-mapper.ts deleted file mode 100644 index c0ddda1..0000000 --- a/src/mappers/nv-fetch-person-mapper.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { - TNvOpenPersonPageParams, - TNvOpenPersonPageResult, -} from "../types/actions/connection"; -import { ThenWorkflowMapper } from "./then-workflow-mapper.abstract"; - -export class NvFetchPersonMapper extends ThenWorkflowMapper< - TNvOpenPersonPageParams, - TNvOpenPersonPageResult -> { - constructor() { - super({ - actionConfigs: [], - responseMappings: [], - baseActionType: "nv.openPersonPage", - defaultParams: { - basicInfo: true, - }, - }); - } -} diff --git a/src/mappers/nv-search-companies-mapper.ts b/src/mappers/nv-search-companies-mapper.ts deleted file mode 100644 index 275d459..0000000 --- a/src/mappers/nv-search-companies-mapper.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { - TNvSearchCompanyParams, - TNvSearchCompanyResult, -} from "../types/actions/search-company"; -import { ArrayWorkflowMapper } from "./array-workflow-mapper.abstract"; - -export class NvSearchCompaniesMapper extends ArrayWorkflowMapper< - TNvSearchCompanyParams, - TNvSearchCompanyResult -> { - constructor() { - super({ - baseActionType: "nv.searchCompanies", - }); - } -} diff --git a/src/mappers/nv-search-people-mapper.ts b/src/mappers/nv-search-people-mapper.ts deleted file mode 100644 index 2ef6336..0000000 --- a/src/mappers/nv-search-people-mapper.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { - TNvSearchPeopleParams, - TNvSearchPeopleResult, -} from "../types/actions/search-people"; -import { ArrayWorkflowMapper } from "./array-workflow-mapper.abstract"; - -export class NvSearchPeopleMapper extends ArrayWorkflowMapper< - TNvSearchPeopleParams, - TNvSearchPeopleResult -> { - constructor() { - super({ - baseActionType: "nv.searchPeople", - }); - } -} diff --git a/src/mappers/retrieve-connections-mapper.ts b/src/mappers/retrieve-connections-mapper.ts deleted file mode 100644 index fe58c8c..0000000 --- a/src/mappers/retrieve-connections-mapper.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { - TRetrieveConnectionsParams, - TRetrieveConnectionsResult, -} from "../types/actions/connection"; -import { ArrayWorkflowMapper } from "./array-workflow-mapper.abstract"; - -export class RetrieveConnectionsMapper extends ArrayWorkflowMapper< - TRetrieveConnectionsParams, - TRetrieveConnectionsResult -> { - constructor() { - super({ - baseActionType: "st.retrieveConnections", - }); - } -} diff --git a/src/mappers/retrieve-pending-requests-mapper.ts b/src/mappers/retrieve-pending-requests-mapper.ts deleted file mode 100644 index e38da9a..0000000 --- a/src/mappers/retrieve-pending-requests-mapper.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { TRetrievePendingRequestsResult } from "../types/actions/connection"; -import { ArrayWorkflowMapper } from "./array-workflow-mapper.abstract"; -import { TBaseActionParams } from "../types/params"; - -export class RetrievePendingRequestsMapper extends ArrayWorkflowMapper< - TBaseActionParams, - TRetrievePendingRequestsResult -> { - constructor() { - super({ - baseActionType: "st.retrievePendingRequests", - }); - } -} diff --git a/src/mappers/search-companies-mapper.ts b/src/mappers/search-companies-mapper.ts deleted file mode 100644 index f0f4756..0000000 --- a/src/mappers/search-companies-mapper.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { - TSearchCompanyParams, - TSearchCompanyResult, -} from "../types/actions/search-company"; -import { ArrayWorkflowMapper } from "./array-workflow-mapper.abstract"; - -export class SearchCompaniesMapper extends ArrayWorkflowMapper< - TSearchCompanyParams, - TSearchCompanyResult -> { - constructor() { - super({ - baseActionType: "st.searchCompanies", - }); - } -} diff --git a/src/mappers/search-people-mapper.ts b/src/mappers/search-people-mapper.ts deleted file mode 100644 index 13ae03f..0000000 --- a/src/mappers/search-people-mapper.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { - TSearchPeopleParams, - TSearchPeopleResult, -} from "../types/actions/search-people"; -import { ArrayWorkflowMapper } from "./array-workflow-mapper.abstract"; - -export class SearchPeopleMapper extends ArrayWorkflowMapper< - TSearchPeopleParams, - TSearchPeopleResult -> { - constructor() { - super({ - baseActionType: "st.searchPeople", - }); - } -} diff --git a/src/mappers/simple-workflow-mapper.ts b/src/mappers/simple-workflow-mapper.ts index 71db114..d003603 100644 --- a/src/mappers/simple-workflow-mapper.ts +++ b/src/mappers/simple-workflow-mapper.ts @@ -1,16 +1,10 @@ -import { TDefaultParameters } from "./base-mapper.abstract"; -import { TLinkedApiActionError } from "../types/errors"; -import type { TBaseActionParams } from "../types/params"; -import type { - TWorkflowDefinition, - TWorkflowResponse, -} from "../types/workflows"; -import { BaseMapper, TMappedResponse } from "./base-mapper.abstract"; - -export class SimpleWorkflowMapper< - TParams extends TBaseActionParams, - TResult, -> extends BaseMapper { +import { TLinkedApiActionError } from '../types/errors'; +import type { TWorkflowDefinition, TWorkflowResponse } from '../types/workflows'; + +import { TDefaultParameters } from './base-mapper.abstract'; +import { BaseMapper, TMappedResponse } from './base-mapper.abstract'; + +export class SimpleWorkflowMapper extends BaseMapper { private readonly actionType: string; private readonly defaultParams: TDefaultParameters; @@ -39,12 +33,8 @@ export class SimpleWorkflowMapper< if (Array.isArray(completion)) { return { - data: completion - .map((action) => action.data) - .filter(Boolean) as TResult, - errors: completion - .map((action) => action.error) - .filter(Boolean) as TLinkedApiActionError[], + data: completion.map((action) => action.data).filter(Boolean) as TResult, + errors: completion.map((action) => action.error).filter(Boolean) as TLinkedApiActionError[], }; } diff --git a/src/mappers/then-workflow-mapper.abstract.ts b/src/mappers/then-workflow-mapper.abstract.ts index 684b804..4d0e5c5 100644 --- a/src/mappers/then-workflow-mapper.abstract.ts +++ b/src/mappers/then-workflow-mapper.abstract.ts @@ -1,16 +1,13 @@ -import { LinkedApiError, TLinkedApiActionError } from "../types/errors"; -import type { TBaseActionParams } from "../types/params"; +import { LinkedApiError, TLinkedApiActionError } from '../types/errors'; +import type { TBaseActionParams } from '../types/params'; import type { TActionResponse, - TWorkflowSingleData, TWorkflowDefinition, TWorkflowResponse, -} from "../types/workflows"; -import { - BaseMapper, - TDefaultParameters, - TMappedResponse, -} from "./base-mapper.abstract"; + TWorkflowSingleData, +} from '../types/workflows'; + +import { BaseMapper, TDefaultParameters, TMappedResponse } from './base-mapper.abstract'; export interface TActionConfig { paramName: string; @@ -62,19 +59,13 @@ export abstract class ThenWorkflowMapper< } as unknown as TWorkflowDefinition; } - public override mapResponse( - response: TWorkflowResponse, - ): TMappedResponse { + public override mapResponse(response: TWorkflowResponse): TMappedResponse { const completion = this.getCompletion(response); if (Array.isArray(completion)) { return { - data: completion - .map((action) => action.data) - .filter(Boolean) as TResult, - errors: completion - .map((action) => action.error) - .filter(Boolean) as TLinkedApiActionError[], + data: completion.map((action) => action.data).filter(Boolean) as TResult, + errors: completion.map((action) => action.error).filter(Boolean) as TLinkedApiActionError[], }; } @@ -91,9 +82,7 @@ export abstract class ThenWorkflowMapper< throw LinkedApiError.unknownError(); } - private mapThenFromResponse( - data: TWorkflowSingleData, - ): TMappedResponse { + private mapThenFromResponse(data: TWorkflowSingleData): TMappedResponse { const result = { ...data }; const thenActions = data.then; const errors: TLinkedApiActionError[] = []; @@ -111,8 +100,7 @@ export abstract class ThenWorkflowMapper< (action: TActionResponse) => action.actionType === mapping.actionType, ); if (thenAction) { - (result as Record)[mapping.targetProperty] = - thenAction.data; + (result as Record)[mapping.targetProperty] = thenAction.data; if (thenAction.error) { errors.push(thenAction.error); } @@ -122,14 +110,13 @@ export abstract class ThenWorkflowMapper< const thenAction = thenActions as TActionResponse; if (thenAction.actionType === mapping.actionType) { - (result as Record)[mapping.targetProperty] = - thenAction.data; + (result as Record)[mapping.targetProperty] = thenAction.data; if (thenAction.error) { errors.push(thenAction.error); } } } - delete (result as Record)["then"]; + delete (result as Record)['then']; return { data: result as TResult, errors, @@ -156,25 +143,16 @@ export abstract class ThenWorkflowMapper< return cleanedParams; } - private shouldIncludeActionToRequest( - params: TParams, - config: TActionConfig, - ): boolean { + private shouldIncludeActionToRequest(params: TParams, config: TActionConfig): boolean { const paramValue = params[config.paramName as keyof TParams]; return paramValue === true; } - private buildRequestAction( - params: TParams, - config: TActionConfig, - ): Record { + private buildRequestAction(params: TParams, config: TActionConfig): Record { return { actionType: config.actionType, ...(config.configSource - ? (params[config.configSource as keyof TParams] as Record< - string, - unknown - >) + ? (params[config.configSource as keyof TParams] as Record) : {}), }; } diff --git a/src/mappers/void-workflow-mapper.ts b/src/mappers/void-workflow-mapper.ts index ae0dc8f..bd7495c 100644 --- a/src/mappers/void-workflow-mapper.ts +++ b/src/mappers/void-workflow-mapper.ts @@ -1,14 +1,13 @@ -import { TLinkedApiActionError } from "../types/errors"; -import type { TBaseActionParams } from "../types/params"; -import type { - TWorkflowDefinition, - TWorkflowResponse, -} from "../types/workflows"; -import { BaseMapper, TMappedResponse } from "./base-mapper.abstract"; +import { TLinkedApiActionError } from '../types/errors'; +import type { TBaseActionParams } from '../types/params'; +import type { TWorkflowDefinition, TWorkflowResponse } from '../types/workflows'; -export class VoidWorkflowMapper< - TParams extends TBaseActionParams, -> extends BaseMapper { +import { BaseMapper, TMappedResponse } from './base-mapper.abstract'; + +export class VoidWorkflowMapper extends BaseMapper< + TParams, + void +> { private readonly actionType: string; constructor(actionType: string) { @@ -29,9 +28,7 @@ export class VoidWorkflowMapper< if (Array.isArray(completion)) { return { data: undefined, - errors: completion - .map((action) => action.error) - .filter(Boolean) as TLinkedApiActionError[], + errors: completion.map((action) => action.error).filter(Boolean) as TLinkedApiActionError[], }; } if (completion.error) { diff --git a/src/operations/check-connection-status.ts b/src/operations/check-connection-status.ts new file mode 100644 index 0000000..e00bb53 --- /dev/null +++ b/src/operations/check-connection-status.ts @@ -0,0 +1,17 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { SimpleWorkflowMapper } from '../mappers'; +import { TCheckConnectionStatusParams, TCheckConnectionStatusResult } from '../types'; + +export class CheckConnectionStatus extends PredefinedOperation< + TCheckConnectionStatusParams, + TCheckConnectionStatusResult +> { + protected override readonly functionName: TSupportedFunctionName = 'checkConnectionStatus'; + protected override readonly mapper = new SimpleWorkflowMapper< + TCheckConnectionStatusParams, + TCheckConnectionStatusResult + >({ + actionType: 'st.checkConnectionStatus', + }); +} diff --git a/src/operations/comment-on-post.ts b/src/operations/comment-on-post.ts new file mode 100644 index 0000000..b2dbada --- /dev/null +++ b/src/operations/comment-on-post.ts @@ -0,0 +1,10 @@ +import { PredefinedOperation, TSupportedFunctionName } from '../core'; +import { VoidWorkflowMapper } from '../mappers'; +import { TCommentOnPostParams } from '../types'; + +export class CommentOnPost extends PredefinedOperation { + protected override readonly functionName: TSupportedFunctionName = 'commentOnPost'; + protected override readonly mapper = new VoidWorkflowMapper( + 'st.commentOnPost', + ); +} diff --git a/src/operations/fetch-company.ts b/src/operations/fetch-company.ts new file mode 100644 index 0000000..e4010cd --- /dev/null +++ b/src/operations/fetch-company.ts @@ -0,0 +1,76 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { TActionConfig, ThenWorkflowMapper } from '../mappers/then-workflow-mapper.abstract'; +import { + HttpClient, + TBaseFetchCompanyParams, + TFetchCompanyParams, + TFetchCompanyResult, +} from '../types'; + +export class FetchCompany extends PredefinedOperation< + TBaseFetchCompanyParams, + TFetchCompanyResult +> { + protected override readonly functionName: TSupportedFunctionName = 'fetchCompany'; + protected override readonly mapper = new FetchCompanyMapper(); + + constructor(httpClient: HttpClient) { + super(httpClient); + } + + public override async execute( + params: TFetchCompanyParams, + ): Promise { + return super.execute(params); + } +} + +const FETCH_COMPANY_ACTIONS: TActionConfig[] = [ + { + paramName: 'retrieveEmployees', + actionType: 'st.retrieveCompanyEmployees', + configSource: 'employeesRetrievalConfig', + }, + { + paramName: 'retrieveDMs', + actionType: 'st.retrieveCompanyDMs', + configSource: 'dmsRetrievalConfig', + }, + { + paramName: 'retrievePosts', + actionType: 'st.retrieveCompanyPosts', + configSource: 'postsRetrievalConfig', + }, +]; + +const RESPONSE_MAPPINGS = [ + { + actionType: 'st.retrieveCompanyEmployees', + targetProperty: 'employees', + }, + { + actionType: 'st.retrieveCompanyDMs', + targetProperty: 'dms', + }, + { + actionType: 'st.retrieveCompanyPosts', + targetProperty: 'posts', + }, +]; + +export class FetchCompanyMapper extends ThenWorkflowMapper< + TParams, + TFetchCompanyResult +> { + constructor() { + super({ + actionConfigs: FETCH_COMPANY_ACTIONS, + responseMappings: RESPONSE_MAPPINGS, + baseActionType: 'st.openCompanyPage', + defaultParams: { + basicInfo: true, + }, + }); + } +} diff --git a/src/operations/fetch-person.ts b/src/operations/fetch-person.ts new file mode 100644 index 0000000..ac488f3 --- /dev/null +++ b/src/operations/fetch-person.ts @@ -0,0 +1,96 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { TActionConfig, ThenWorkflowMapper } from '../mappers/then-workflow-mapper.abstract'; +import { TBaseFetchPersonParams, TFetchPersonParams, TFetchPersonResult } from '../types'; + +export class FetchPerson extends PredefinedOperation { + protected override readonly functionName: TSupportedFunctionName = 'fetchPerson'; + protected override readonly mapper = new FetchPersonMapper(); + + public override async execute( + params: TFetchPersonParams, + ): Promise { + return super.execute(params); + } +} + +const FETCH_PERSON_ACTIONS: TActionConfig[] = [ + { + paramName: 'retrieveExperience', + actionType: 'st.retrievePersonExperience', + }, + { + paramName: 'retrieveEducation', + actionType: 'st.retrievePersonEducation', + }, + { + paramName: 'retrieveSkills', + actionType: 'st.retrievePersonSkills', + }, + { + paramName: 'retrieveLanguages', + actionType: 'st.retrievePersonLanguages', + }, + { + paramName: 'retrievePosts', + actionType: 'st.retrievePersonPosts', + configSource: 'postsRetrievalConfig', + }, + { + paramName: 'retrieveComments', + actionType: 'st.retrievePersonComments', + configSource: 'commentsRetrievalConfig', + }, + { + paramName: 'retrieveReactions', + actionType: 'st.retrievePersonReactions', + configSource: 'reactionsRetrievalConfig', + }, +]; + +const RESPONSE_MAPPINGS = [ + { + actionType: 'st.retrievePersonExperience', + targetProperty: 'experiences', + }, + { + actionType: 'st.retrievePersonEducation', + targetProperty: 'education', + }, + { + actionType: 'st.retrievePersonSkills', + targetProperty: 'skills', + }, + { + actionType: 'st.retrievePersonLanguages', + targetProperty: 'languages', + }, + { + actionType: 'st.retrievePersonPosts', + targetProperty: 'posts', + }, + { + actionType: 'st.retrievePersonComments', + targetProperty: 'comments', + }, + { + actionType: 'st.retrievePersonReactions', + targetProperty: 'reactions', + }, +]; + +export class FetchPersonMapper extends ThenWorkflowMapper< + TParams, + TFetchPersonResult +> { + constructor() { + super({ + actionConfigs: FETCH_PERSON_ACTIONS, + responseMappings: RESPONSE_MAPPINGS, + baseActionType: 'st.openPersonPage', + defaultParams: { + basicInfo: true, + }, + }); + } +} diff --git a/src/operations/fetch-post.ts b/src/operations/fetch-post.ts new file mode 100644 index 0000000..5c0490f --- /dev/null +++ b/src/operations/fetch-post.ts @@ -0,0 +1,16 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { SimpleWorkflowMapper } from '../mappers'; +import { TFetchPostParams, TFetchPostResult } from '../types'; + +export class FetchPost extends PredefinedOperation { + protected override readonly functionName: TSupportedFunctionName = 'fetchPost'; + protected override readonly mapper = new SimpleWorkflowMapper( + { + actionType: 'st.openPost', + defaultParams: { + basicInfo: true, + }, + }, + ); +} diff --git a/src/operations/index.ts b/src/operations/index.ts new file mode 100644 index 0000000..8ec68ab --- /dev/null +++ b/src/operations/index.ts @@ -0,0 +1,23 @@ +export * from './fetch-person'; +export * from './search-companies'; +export * from './fetch-company'; +export * from './sales-navigator-fetch-company'; +export * from './send-message'; +export * from './sales-navigator-send-message'; +export * from './sync-conversation'; +export * from './sales-navigator-sync-conversation'; +export * from './sales-navigator-fetch-person'; +export * from './sales-navigator-search-companies'; +export * from './fetch-post'; +export * from './search-people'; +export * from './sales-navigator-search-people'; +export * from './send-connection-request'; +export * from './check-connection-status'; +export * from './withdraw-connection-request'; +export * from './retrieve-pending-requests'; +export * from './retrieve-connections'; +export * from './remove-connection'; +export * from './react-to-post'; +export * from './comment-on-post'; +export * from './retrieve-ssi'; +export * from './retrieve-performance'; diff --git a/src/operations/react-to-post.ts b/src/operations/react-to-post.ts new file mode 100644 index 0000000..a24811a --- /dev/null +++ b/src/operations/react-to-post.ts @@ -0,0 +1,8 @@ +import { PredefinedOperation, TSupportedFunctionName } from '../core'; +import { VoidWorkflowMapper } from '../mappers'; +import { TReactToPostParams } from '../types'; + +export class ReactToPost extends PredefinedOperation { + protected override readonly functionName: TSupportedFunctionName = 'reactToPost'; + protected override readonly mapper = new VoidWorkflowMapper('st.reactToPost'); +} diff --git a/src/operations/remove-connection.ts b/src/operations/remove-connection.ts new file mode 100644 index 0000000..9615d56 --- /dev/null +++ b/src/operations/remove-connection.ts @@ -0,0 +1,10 @@ +import { PredefinedOperation, TSupportedFunctionName } from '../core'; +import { VoidWorkflowMapper } from '../mappers'; +import { TRemoveConnectionParams } from '../types'; + +export class RemoveConnection extends PredefinedOperation { + protected override readonly functionName: TSupportedFunctionName = 'removeConnection'; + protected override readonly mapper = new VoidWorkflowMapper( + 'st.removeConnection', + ); +} diff --git a/src/operations/retrieve-connections.ts b/src/operations/retrieve-connections.ts new file mode 100644 index 0000000..cea4128 --- /dev/null +++ b/src/operations/retrieve-connections.ts @@ -0,0 +1,15 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { ArrayWorkflowMapper } from '../mappers/array-workflow-mapper'; +import { TRetrieveConnectionsParams, TRetrieveConnectionsResult } from '../types'; + +export class RetrieveConnections extends PredefinedOperation< + TRetrieveConnectionsParams, + TRetrieveConnectionsResult[] +> { + protected override readonly functionName: TSupportedFunctionName = 'retrieveConnections'; + protected override readonly mapper = new ArrayWorkflowMapper< + TRetrieveConnectionsParams, + TRetrieveConnectionsResult + >('st.retrieveConnections'); +} diff --git a/src/operations/retrieve-pending-requests.ts b/src/operations/retrieve-pending-requests.ts new file mode 100644 index 0000000..41017aa --- /dev/null +++ b/src/operations/retrieve-pending-requests.ts @@ -0,0 +1,14 @@ +import { PredefinedOperation, TSupportedFunctionName } from '../core'; +import { ArrayWorkflowMapper } from '../mappers/array-workflow-mapper'; +import { TRetrievePendingRequestsResult } from '../types'; + +export class RetrievePendingRequests extends PredefinedOperation< + void, + TRetrievePendingRequestsResult[] +> { + protected override readonly functionName: TSupportedFunctionName = 'retrievePendingRequests'; + protected override readonly mapper = new ArrayWorkflowMapper< + void, + TRetrievePendingRequestsResult + >('st.retrievePendingRequests'); +} diff --git a/src/operations/retrieve-performance.ts b/src/operations/retrieve-performance.ts new file mode 100644 index 0000000..523c3ce --- /dev/null +++ b/src/operations/retrieve-performance.ts @@ -0,0 +1,11 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { SimpleWorkflowMapper } from '../mappers'; +import { TRetrievePerformanceResult } from '../types'; + +export class RetrievePerformance extends PredefinedOperation { + protected override readonly functionName: TSupportedFunctionName = 'retrievePerformance'; + protected override readonly mapper = new SimpleWorkflowMapper({ + actionType: 'st.retrievePerformance', + }); +} diff --git a/src/operations/retrieve-ssi.ts b/src/operations/retrieve-ssi.ts new file mode 100644 index 0000000..819beb4 --- /dev/null +++ b/src/operations/retrieve-ssi.ts @@ -0,0 +1,11 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { SimpleWorkflowMapper } from '../mappers'; +import { TRetrieveSSIResult } from '../types'; + +export class RetrieveSSI extends PredefinedOperation { + protected override readonly functionName: TSupportedFunctionName = 'retrieveSSI'; + protected override readonly mapper = new SimpleWorkflowMapper({ + actionType: 'st.retrieveSSI', + }); +} diff --git a/src/operations/sales-navigator-fetch-company.ts b/src/operations/sales-navigator-fetch-company.ts new file mode 100644 index 0000000..4d524af --- /dev/null +++ b/src/operations/sales-navigator-fetch-company.ts @@ -0,0 +1,57 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { TActionConfig, ThenWorkflowMapper } from '../mappers/then-workflow-mapper.abstract'; +import { TNvBaseFetchCompanyParams, TNvFetchCompanyParams, TNvFetchCompanyResult } from '../types'; + +export class SalesNavigatorFetchCompany extends PredefinedOperation< + TNvBaseFetchCompanyParams, + TNvFetchCompanyResult +> { + protected override readonly functionName: TSupportedFunctionName = 'salesNavigatorFetchCompany'; + protected override readonly mapper = new NvFetchCompanyMapper(); + + public override async execute( + params: TNvFetchCompanyParams, + ): Promise { + return super.execute(params); + } +} + +const FETCH_NV_COMPANY_ACTIONS: TActionConfig[] = [ + { + paramName: 'retrieveEmployees', + actionType: 'nv.retrieveCompanyEmployees', + configSource: 'employeesRetrievalConfig', + }, + { + paramName: 'retrieveDMs', + actionType: 'nv.retrieveCompanyDMs', + configSource: 'dmsRetrievalConfig', + }, +]; + +const RESPONSE_MAPPINGS = [ + { + actionType: 'nv.retrieveCompanyEmployees', + targetProperty: 'employees', + }, + { + actionType: 'nv.retrieveCompanyDMs', + targetProperty: 'dms', + }, +]; + +export class NvFetchCompanyMapper< + TParams extends TNvBaseFetchCompanyParams, +> extends ThenWorkflowMapper { + constructor() { + super({ + actionConfigs: FETCH_NV_COMPANY_ACTIONS, + responseMappings: RESPONSE_MAPPINGS, + baseActionType: 'nv.openCompanyPage', + defaultParams: { + basicInfo: true, + }, + }); + } +} diff --git a/src/operations/sales-navigator-fetch-person.ts b/src/operations/sales-navigator-fetch-person.ts new file mode 100644 index 0000000..6e14785 --- /dev/null +++ b/src/operations/sales-navigator-fetch-person.ts @@ -0,0 +1,28 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { ThenWorkflowMapper } from '../mappers'; +import { TNvOpenPersonPageParams, TNvOpenPersonPageResult } from '../types'; + +export class SalesNavigatorFetchPerson extends PredefinedOperation< + TNvOpenPersonPageParams, + TNvOpenPersonPageResult +> { + protected override readonly functionName: TSupportedFunctionName = 'salesNavigatorFetchPerson'; + protected override readonly mapper = new NvFetchPersonMapper(); +} + +export class NvFetchPersonMapper extends ThenWorkflowMapper< + TNvOpenPersonPageParams, + TNvOpenPersonPageResult +> { + constructor() { + super({ + actionConfigs: [], + responseMappings: [], + baseActionType: 'nv.openPersonPage', + defaultParams: { + basicInfo: true, + }, + }); + } +} diff --git a/src/operations/sales-navigator-search-companies.ts b/src/operations/sales-navigator-search-companies.ts new file mode 100644 index 0000000..7e06dff --- /dev/null +++ b/src/operations/sales-navigator-search-companies.ts @@ -0,0 +1,16 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { ArrayWorkflowMapper } from '../mappers/array-workflow-mapper'; +import { TNvSearchCompaniesParams, TNvSearchCompanyResult } from '../types'; + +export class SalesNavigatorSearchCompanies extends PredefinedOperation< + TNvSearchCompaniesParams, + TNvSearchCompanyResult[] +> { + protected override readonly functionName: TSupportedFunctionName = + 'salesNavigatorSearchCompanies'; + protected override readonly mapper = new ArrayWorkflowMapper< + TNvSearchCompaniesParams, + TNvSearchCompanyResult + >('nv.searchCompanies'); +} diff --git a/src/operations/sales-navigator-search-people.ts b/src/operations/sales-navigator-search-people.ts new file mode 100644 index 0000000..e6b8054 --- /dev/null +++ b/src/operations/sales-navigator-search-people.ts @@ -0,0 +1,15 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { ArrayWorkflowMapper } from '../mappers/array-workflow-mapper'; +import { TNvSearchPeopleParams, TNvSearchPeopleResult } from '../types'; + +export class SalesNavigatorSearchPeople extends PredefinedOperation< + TNvSearchPeopleParams, + TNvSearchPeopleResult[] +> { + protected override readonly functionName: TSupportedFunctionName = 'salesNavigatorSearchPeople'; + protected override readonly mapper = new ArrayWorkflowMapper< + TNvSearchPeopleParams, + TNvSearchPeopleResult + >('nv.searchPeople'); +} diff --git a/src/operations/sales-navigator-send-message.ts b/src/operations/sales-navigator-send-message.ts new file mode 100644 index 0000000..255a102 --- /dev/null +++ b/src/operations/sales-navigator-send-message.ts @@ -0,0 +1,11 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { VoidWorkflowMapper } from '../mappers/void-workflow-mapper'; +import { TNvSendMessageParams } from '../types'; + +export class SalesNavigatorSendMessage extends PredefinedOperation { + protected override readonly functionName: TSupportedFunctionName = 'salesNavigatorSendMessage'; + protected override readonly mapper = new VoidWorkflowMapper( + 'nv.sendMessage', + ); +} diff --git a/src/operations/sales-navigator-sync-conversation.ts b/src/operations/sales-navigator-sync-conversation.ts new file mode 100644 index 0000000..fe75f7f --- /dev/null +++ b/src/operations/sales-navigator-sync-conversation.ts @@ -0,0 +1,15 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { VoidWorkflowMapper } from '../mappers'; +import { TNvSyncConversationParams } from '../types'; + +export class SalesNavigatorSyncConversation extends PredefinedOperation< + TNvSyncConversationParams, + void +> { + protected override readonly functionName: TSupportedFunctionName = + 'salesNavigatorSyncConversation'; + protected override readonly mapper = new VoidWorkflowMapper( + 'nv.syncConversation', + ); +} diff --git a/src/operations/search-companies.ts b/src/operations/search-companies.ts new file mode 100644 index 0000000..3b24cf7 --- /dev/null +++ b/src/operations/search-companies.ts @@ -0,0 +1,15 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { ArrayWorkflowMapper } from '../mappers/array-workflow-mapper'; +import { TSearchCompaniesParams, TSearchCompanyResult } from '../types'; + +export class SearchCompanies extends PredefinedOperation< + TSearchCompaniesParams, + TSearchCompanyResult[] +> { + protected override readonly functionName: TSupportedFunctionName = 'searchCompanies'; + protected override readonly mapper = new ArrayWorkflowMapper< + TSearchCompaniesParams, + TSearchCompanyResult + >('st.searchCompanies'); +} diff --git a/src/operations/search-people.ts b/src/operations/search-people.ts new file mode 100644 index 0000000..f1a9453 --- /dev/null +++ b/src/operations/search-people.ts @@ -0,0 +1,12 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { ArrayWorkflowMapper } from '../mappers/array-workflow-mapper'; +import { TSearchPeopleParams, TSearchPeopleResult } from '../types'; + +export class SearchPeople extends PredefinedOperation { + protected override readonly functionName: TSupportedFunctionName = 'searchPeople'; + protected override readonly mapper = new ArrayWorkflowMapper< + TSearchPeopleParams, + TSearchPeopleResult + >('st.searchPeople'); +} diff --git a/src/operations/send-connection-request.ts b/src/operations/send-connection-request.ts new file mode 100644 index 0000000..0fb6b9c --- /dev/null +++ b/src/operations/send-connection-request.ts @@ -0,0 +1,11 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { VoidWorkflowMapper } from '../mappers'; +import { TSendConnectionRequestParams } from '../types'; + +export class SendConnectionRequest extends PredefinedOperation { + protected override readonly functionName: TSupportedFunctionName = 'sendConnectionRequest'; + protected override readonly mapper = new VoidWorkflowMapper( + 'st.sendConnectionRequest', + ); +} diff --git a/src/operations/send-message.ts b/src/operations/send-message.ts new file mode 100644 index 0000000..5d0ed8d --- /dev/null +++ b/src/operations/send-message.ts @@ -0,0 +1,9 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { VoidWorkflowMapper } from '../mappers/void-workflow-mapper'; +import { TSendMessageParams } from '../types'; + +export class SendMessage extends PredefinedOperation { + protected override readonly functionName: TSupportedFunctionName = 'sendMessage'; + protected override readonly mapper = new VoidWorkflowMapper('st.sendMessage'); +} diff --git a/src/operations/sync-conversation.ts b/src/operations/sync-conversation.ts new file mode 100644 index 0000000..3820613 --- /dev/null +++ b/src/operations/sync-conversation.ts @@ -0,0 +1,11 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { VoidWorkflowMapper } from '../mappers'; +import { TSyncConversationParams } from '../types'; + +export class SyncConversation extends PredefinedOperation { + protected override readonly functionName: TSupportedFunctionName = 'syncConversation'; + protected override readonly mapper = new VoidWorkflowMapper( + 'st.syncConversation', + ); +} diff --git a/src/operations/withdraw-connection-request.ts b/src/operations/withdraw-connection-request.ts new file mode 100644 index 0000000..295303e --- /dev/null +++ b/src/operations/withdraw-connection-request.ts @@ -0,0 +1,14 @@ +import { TSupportedFunctionName } from '../core'; +import { PredefinedOperation } from '../core/base-operation.abstract'; +import { VoidWorkflowMapper } from '../mappers'; +import { TWithdrawConnectionRequestParams } from '../types'; + +export class WithdrawConnectionRequest extends PredefinedOperation< + TWithdrawConnectionRequestParams, + void +> { + protected override readonly functionName: TSupportedFunctionName = 'withdrawConnectionRequest'; + protected override readonly mapper = new VoidWorkflowMapper( + 'st.withdrawConnectionRequest', + ); +} diff --git a/src/types/actions/company.ts b/src/types/actions/company.ts index 19070e4..403db9f 100644 --- a/src/types/actions/company.ts +++ b/src/types/actions/company.ts @@ -1,6 +1,7 @@ -import type { TPost } from "./post"; -import { TBaseActionParams, TLimitParams, TLimitSinceParams } from "../params"; -import { TYearsOfExperience } from "./person"; +import { TBaseActionParams, TLimitParams, TLimitSinceParams } from '../params'; + +import { TYearsOfExperience } from './person'; +import type { TPost } from './post'; export interface TCompany { name: string; @@ -33,53 +34,15 @@ export interface TBaseFetchCompanyParamsWide extends TBaseFetchCompanyParams { retrievePosts: true; } -export type TFetchCompanyParams< - T extends TBaseFetchCompanyParams = TBaseFetchCompanyParams, -> = T & { - employeesRetrievalConfig?: T["retrieveEmployees"] extends true +export type TFetchCompanyParams = T & { + employeesRetrievalConfig?: T['retrieveEmployees'] extends true ? TStCompanyEmployeesRetrievalConfig | undefined : never; - dmsRetrievalConfig?: T["retrieveDMs"] extends true - ? TLimitParams | undefined - : never; - postsRetrievalConfig?: T["retrievePosts"] extends true - ? TLimitSinceParams | undefined - : never; + dmsRetrievalConfig?: T['retrieveDMs'] extends true ? TLimitParams | undefined : never; + postsRetrievalConfig?: T['retrievePosts'] extends true ? TLimitSinceParams | undefined : never; }; -type TBaseCompany = Pick< - TCompany, - | "name" - | "publicUrl" - | "description" - | "location" - | "headquarters" - | "industry" - | "specialties" - | "website" - | "employeeCount" - | "yearFounded" - | "ventureFinancing" - | "jobsCount" ->; - -export type TFetchCompanyResult = - TBaseCompany & - (TParams["retrieveEmployees"] extends true - ? { employees: ReadonlyArray } - : Record) & - (TParams["retrieveDMs"] extends true - ? { dms: ReadonlyArray } - : Record) & - (TParams["retrievePosts"] extends true - ? { posts: ReadonlyArray } - : Record); - -export type TFetchCompanyResultWide = TBaseCompany & { - employees?: ReadonlyArray; - dms?: ReadonlyArray; - posts?: ReadonlyArray; -}; +export type TFetchCompanyResult = TCompany; export interface TStCompanyEmployee { name: string; @@ -142,49 +105,20 @@ export interface TNvBaseFetchCompanyParams extends TBaseActionParams { retrieveDMs?: boolean; } -export interface TNvBaseFetchCompanyParamsWide - extends TNvBaseFetchCompanyParams { +export interface TNvBaseFetchCompanyParamsWide extends TNvBaseFetchCompanyParams { retrieveEmployees: true; retrieveDMs: true; } -export type TNvFetchCompanyParams< - T extends TNvBaseFetchCompanyParams = TNvBaseFetchCompanyParams, -> = T & { - employeesRetrievalConfig?: T["retrieveEmployees"] extends true - ? TNvCompanyEmployeeRetrievalConfig | undefined - : never; - dmsRetrievalConfig?: T["retrieveDMs"] extends true - ? TLimitParams | undefined - : never; -}; +export type TNvFetchCompanyParams = + T & { + employeesRetrievalConfig?: T['retrieveEmployees'] extends true + ? TNvCompanyEmployeeRetrievalConfig | undefined + : never; + dmsRetrievalConfig?: T['retrieveDMs'] extends true ? TLimitParams | undefined : never; + }; -type TNvBaseCompany = Pick< - TNvCompany, - | "name" - | "publicUrl" - | "description" - | "location" - | "headquarters" - | "industry" - | "website" - | "employeeCount" - | "yearFounded" ->; - -export type TNvFetchCompanyResult = - TNvBaseCompany & - (TParams["retrieveEmployees"] extends true - ? { employees: ReadonlyArray | undefined } - : Record) & - (TParams["retrieveDMs"] extends true - ? { dms: ReadonlyArray | undefined } - : Record); - -export type TNvFetchCompanyResultWide = TNvBaseCompany & { - employees?: ReadonlyArray; - dms?: ReadonlyArray; -}; +export type TNvFetchCompanyResult = TNvCompany; export interface TNvCompanyEmployeeRetrievalConfig extends TLimitParams { filter?: { diff --git a/src/types/actions/connection.ts b/src/types/actions/connection.ts index 3529755..8a68292 100644 --- a/src/types/actions/connection.ts +++ b/src/types/actions/connection.ts @@ -1,4 +1,4 @@ -import { TBaseActionParams, TLimitParams } from "../params"; +import { TBaseActionParams, TLimitParams } from '../params'; // Base connection interfaces export interface TConnectionPerson { @@ -24,12 +24,11 @@ export interface TCheckConnectionStatusResult { } export const CONNECTION_STATUS = { - connected: "connected", - pending: "pending", - notConnected: "notConnected", + connected: 'connected', + pending: 'pending', + notConnected: 'notConnected', } as const; -export type TConnectionStatus = - (typeof CONNECTION_STATUS)[keyof typeof CONNECTION_STATUS]; +export type TConnectionStatus = (typeof CONNECTION_STATUS)[keyof typeof CONNECTION_STATUS]; export interface TWithdrawConnectionRequestParams extends TBaseActionParams { personUrl: string; diff --git a/src/types/actions/index.ts b/src/types/actions/index.ts index d89ca87..51e9e24 100644 --- a/src/types/actions/index.ts +++ b/src/types/actions/index.ts @@ -1,8 +1,8 @@ -export * from "./company"; -export * from "./connection"; -export * from "./message"; -export * from "./person"; -export * from "./post"; -export * from "./search-company"; -export * from "./search-people"; -export * from "./statistics"; +export * from './company'; +export * from './connection'; +export * from './message'; +export * from './person'; +export * from './post'; +export * from './search-companies'; +export * from './search-people'; +export * from './statistics'; diff --git a/src/types/actions/message.ts b/src/types/actions/message.ts index b04514f..81b5564 100644 --- a/src/types/actions/message.ts +++ b/src/types/actions/message.ts @@ -1,4 +1,4 @@ -import { TBaseActionParams } from "../params"; +import { TBaseActionParams } from '../params'; export interface TSendMessageParams extends TBaseActionParams { personUrl: string; @@ -49,15 +49,13 @@ export interface TConversationPollResponse { } export const CONVERSATION_TYPE = { - st: "st", - nv: "nv", + st: 'st', + nv: 'nv', } as const; -export type TConversationType = - (typeof CONVERSATION_TYPE)[keyof typeof CONVERSATION_TYPE]; +export type TConversationType = (typeof CONVERSATION_TYPE)[keyof typeof CONVERSATION_TYPE]; export const MESSAGE_SENDER = { - us: "us", - them: "them", + us: 'us', + them: 'them', } as const; -export type TMessageSender = - (typeof MESSAGE_SENDER)[keyof typeof MESSAGE_SENDER]; +export type TMessageSender = (typeof MESSAGE_SENDER)[keyof typeof MESSAGE_SENDER]; diff --git a/src/types/actions/person.ts b/src/types/actions/person.ts index 803a32c..4f8434b 100644 --- a/src/types/actions/person.ts +++ b/src/types/actions/person.ts @@ -1,24 +1,6 @@ -import type { TBaseActionParams, TLimitSinceParams } from "../params"; -import type { TComment, TPost, TReaction } from "./post"; +import type { TBaseActionParams, TLimitSinceParams } from '../params'; -export interface TPerson { - name: string; - publicUrl: string; - hashedUrl: string; - headline: string; - location: string; - countryCode: string; - position: string; - companyName: string; - companyHashedUrl: string; - experiences?: ReadonlyArray; - education?: ReadonlyArray; - skills?: ReadonlyArray; - languages?: ReadonlyArray; - posts?: ReadonlyArray; - comments?: ReadonlyArray; - reactions?: ReadonlyArray; -} +import type { TComment, TPost, TReaction } from './post'; export interface TBaseFetchPersonParams extends TBaseActionParams { personUrl: string; @@ -41,58 +23,26 @@ export interface TBaseFetchPersonParamsWide extends TBaseFetchPersonParams { retrieveReactions: true; } -export type TFetchPersonParams< - T extends TBaseFetchPersonParams = TBaseFetchPersonParams, -> = T & { - postsRetrievalConfig?: T["retrievePosts"] extends true +export type TFetchPersonParams = T & { + postsRetrievalConfig?: T['retrievePosts'] extends true ? TLimitSinceParams | undefined : never; + commentsRetrievalConfig?: T['retrieveComments'] extends true ? TLimitSinceParams | undefined : never; - commentsRetrievalConfig?: T["retrieveComments"] extends true - ? TLimitSinceParams | undefined - : never; - reactionsRetrievalConfig?: T["retrieveReactions"] extends true + reactionsRetrievalConfig?: T['retrieveReactions'] extends true ? TLimitSinceParams | undefined : never; }; -type TBasePerson = Pick< - TPerson, - | "name" - | "publicUrl" - | "hashedUrl" - | "headline" - | "location" - | "countryCode" - | "position" - | "companyName" - | "companyHashedUrl" ->; - -export type TFetchPersonResult = - TBasePerson & - (TParams["retrieveExperience"] extends true - ? { experiences: ReadonlyArray | undefined } - : Record) & - (TParams["retrieveEducation"] extends true - ? { education: ReadonlyArray | undefined } - : Record) & - (TParams["retrieveSkills"] extends true - ? { skills: ReadonlyArray | undefined } - : Record) & - (TParams["retrieveLanguages"] extends true - ? { languages: ReadonlyArray | undefined } - : Record) & - (TParams["retrievePosts"] extends true - ? { posts: ReadonlyArray | undefined } - : Record) & - (TParams["retrieveComments"] extends true - ? { comments: ReadonlyArray | undefined } - : Record) & - (TParams["retrieveReactions"] extends true - ? { reactions: ReadonlyArray | undefined } - : Record); - -export type TFetchPersonResultWide = TBasePerson & { +export interface TPerson { + name: string; + publicUrl: string; + hashedUrl: string; + headline: string; + location: string; + countryCode: string; + position: string; + companyName: string; + companyHashedUrl: string; experiences?: ReadonlyArray; education?: ReadonlyArray; skills?: ReadonlyArray; @@ -100,7 +50,9 @@ export type TFetchPersonResultWide = TBasePerson & { posts?: ReadonlyArray; comments?: ReadonlyArray; reactions?: ReadonlyArray; -}; +} + +export type TFetchPersonResult = TPerson; export interface TPersonExperience { position: string; @@ -116,22 +68,21 @@ export interface TPersonExperience { } export const EMPLOYMENT_TYPE = { - fullTime: "fullTime", - partTime: "partTime", - selfEmployed: "selfEmployed", - freelance: "freelance", - contract: "contract", - internship: "internship", - apprenticeship: "apprenticeship", - seasonal: "seasonal", + fullTime: 'fullTime', + partTime: 'partTime', + selfEmployed: 'selfEmployed', + freelance: 'freelance', + contract: 'contract', + internship: 'internship', + apprenticeship: 'apprenticeship', + seasonal: 'seasonal', } as const; -export type TEmploymentType = - (typeof EMPLOYMENT_TYPE)[keyof typeof EMPLOYMENT_TYPE]; +export type TEmploymentType = (typeof EMPLOYMENT_TYPE)[keyof typeof EMPLOYMENT_TYPE]; export const LOCATION_TYPE = { - onSite: "onSite", - remote: "remote", - hybrid: "hybrid", + onSite: 'onSite', + remote: 'remote', + hybrid: 'hybrid', } as const; export type TLocationType = (typeof LOCATION_TYPE)[keyof typeof LOCATION_TYPE]; @@ -151,21 +102,19 @@ export interface TPersonLanguage { } export const LANGUAGE_PROFICIENCY = { - elementary: "elementary", - limitedWorking: "limitedWorking", - professionalWorking: "professionalWorking", - fullProfessional: "fullProfessional", - nativeOrBilingual: "nativeOrBilingual", + elementary: 'elementary', + limitedWorking: 'limitedWorking', + professionalWorking: 'professionalWorking', + fullProfessional: 'fullProfessional', + nativeOrBilingual: 'nativeOrBilingual', } as const; -export type TLanguageProficiency = - (typeof LANGUAGE_PROFICIENCY)[keyof typeof LANGUAGE_PROFICIENCY]; +export type TLanguageProficiency = (typeof LANGUAGE_PROFICIENCY)[keyof typeof LANGUAGE_PROFICIENCY]; export const YEARS_OF_EXPERIENCE = { - lessThanOne: "lessThanOne", - oneToTwo: "oneToTwo", - threeToFive: "threeToFive", - sixToTen: "sixToTen", - moreThanTen: "moreThanTen", + lessThanOne: 'lessThanOne', + oneToTwo: 'oneToTwo', + threeToFive: 'threeToFive', + sixToTen: 'sixToTen', + moreThanTen: 'moreThanTen', } as const; -export type TYearsOfExperience = - (typeof YEARS_OF_EXPERIENCE)[keyof typeof YEARS_OF_EXPERIENCE]; +export type TYearsOfExperience = (typeof YEARS_OF_EXPERIENCE)[keyof typeof YEARS_OF_EXPERIENCE]; diff --git a/src/types/actions/post.ts b/src/types/actions/post.ts index 9dfb3cb..da69e15 100644 --- a/src/types/actions/post.ts +++ b/src/types/actions/post.ts @@ -1,4 +1,4 @@ -import { TBaseActionParams } from "../params"; +import { TBaseActionParams } from '../params'; export interface TPost { url: string; @@ -14,8 +14,8 @@ export interface TPost { } export const POST_TYPE = { - original: "original", - repost: "repost", + original: 'original', + repost: 'repost', } as const; export type TPostType = (typeof POST_TYPE)[keyof typeof POST_TYPE]; @@ -32,12 +32,12 @@ export interface TReaction { } export const REACTION_TYPE = { - like: "like", - celebrate: "celebrate", - support: "support", - love: "love", - insightful: "insightful", - funny: "funny", + like: 'like', + celebrate: 'celebrate', + support: 'support', + love: 'love', + insightful: 'insightful', + funny: 'funny', } as const; export type TReactionType = (typeof REACTION_TYPE)[keyof typeof REACTION_TYPE]; diff --git a/src/types/actions/search-companies.ts b/src/types/actions/search-companies.ts new file mode 100644 index 0000000..c03f8b1 --- /dev/null +++ b/src/types/actions/search-companies.ts @@ -0,0 +1,81 @@ +import { TBaseActionParams } from '../params'; + +export interface TSearchCompaniesParams extends TBaseActionParams { + term?: string; + limit?: number; + filter?: { + sizes?: TSearchCompanySize[]; + locations?: string[]; + industries?: string[]; + }; +} + +export const SEARCH_COMPANY_SIZE = { + size1_10: '1-10', + size11_50: '11-50', + size51_200: '51-200', + size201_500: '201-500', + size501_1000: '501-1000', + size1001_5000: '1001-5000', + size5001_10000: '5001-10000', + size10001Plus: '10001+', +} as const; +export type TSearchCompanySize = (typeof SEARCH_COMPANY_SIZE)[keyof typeof SEARCH_COMPANY_SIZE]; + +export interface TSearchCompanyResult { + name: string; + publicUrl: string; + industry: string; + location: string; +} + +export interface TNvSearchCompaniesParams extends TBaseActionParams { + term?: string; + limit?: number; + filter?: { + sizes?: TSearchCompanySize[]; + locations?: string[]; + industries?: string[]; + annualRevenue?: { + min: TMinAnnualRevenue; + max: TMaxAnnualRevenue; + }; + }; +} + +export const MIN_ANNUAL_REVENUE = { + revenue0: '0', + revenue0_5: '0.5', + revenue1: '1', + revenue2_5: '2.5', + revenue5: '5', + revenue10: '10', + revenue20: '20', + revenue50: '50', + revenue100: '100', + revenue500: '500', + revenue1000: '1000', +} as const; +export type TMinAnnualRevenue = (typeof MIN_ANNUAL_REVENUE)[keyof typeof MIN_ANNUAL_REVENUE]; + +export const MAX_ANNUAL_REVENUE = { + revenue0_5: '0.5', + revenue1: '1', + revenue2_5: '2.5', + revenue5: '5', + revenue10: '10', + revenue20: '20', + revenue50: '50', + revenue100: '100', + revenue500: '500', + revenue1000: '1000', + revenue1000Plus: '1000+', +} as const; +export type TMaxAnnualRevenue = (typeof MAX_ANNUAL_REVENUE)[keyof typeof MAX_ANNUAL_REVENUE]; + +export interface TNvSearchCompanyResult { + name: string; + hashedUrl: string; + industry: string; + employeeCount: number; +} diff --git a/src/types/actions/search-company.ts b/src/types/actions/search-company.ts deleted file mode 100644 index 71da20d..0000000 --- a/src/types/actions/search-company.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { TBaseActionParams } from "../params"; - -export interface TSearchCompanyParams extends TBaseActionParams { - term?: string; - limit?: number; - filter?: { - sizes?: TSearchCompanySize[]; - locations?: string[]; - industries?: string[]; - }; -} - -export const SEARCH_COMPANY_SIZE = { - size1_10: "1-10", - size11_50: "11-50", - size51_200: "51-200", - size201_500: "201-500", - size501_1000: "501-1000", - size1001_5000: "1001-5000", - size5001_10000: "5001-10000", - size10001Plus: "10001+", -} as const; -export type TSearchCompanySize = - (typeof SEARCH_COMPANY_SIZE)[keyof typeof SEARCH_COMPANY_SIZE]; - -export interface TSearchCompanyResult { - name: string; - publicUrl: string; - industry: string; - location: string; -} - -export interface TNvSearchCompanyParams extends TBaseActionParams { - term?: string; - limit?: number; - filter?: { - sizes?: TSearchCompanySize[]; - locations?: string[]; - industries?: string[]; - annualRevenue?: { - min: TMinAnnualRevenue; - max: TMaxAnnualRevenue; - }; - }; -} - -export const MIN_ANNUAL_REVENUE = { - revenue0: "0", - revenue0_5: "0.5", - revenue1: "1", - revenue2_5: "2.5", - revenue5: "5", - revenue10: "10", - revenue20: "20", - revenue50: "50", - revenue100: "100", - revenue500: "500", - revenue1000: "1000", -} as const; -export type TMinAnnualRevenue = - (typeof MIN_ANNUAL_REVENUE)[keyof typeof MIN_ANNUAL_REVENUE]; - -export const MAX_ANNUAL_REVENUE = { - revenue0_5: "0.5", - revenue1: "1", - revenue2_5: "2.5", - revenue5: "5", - revenue10: "10", - revenue20: "20", - revenue50: "50", - revenue100: "100", - revenue500: "500", - revenue1000: "1000", - revenue1000Plus: "1000+", -} as const; -export type TMaxAnnualRevenue = - (typeof MAX_ANNUAL_REVENUE)[keyof typeof MAX_ANNUAL_REVENUE]; - -export interface TNvSearchCompanyResult { - name: string; - hashedUrl: string; - industry: string; - employeeCount: number; -} diff --git a/src/types/actions/search-people.ts b/src/types/actions/search-people.ts index 0f3975e..0cf26ba 100644 --- a/src/types/actions/search-people.ts +++ b/src/types/actions/search-people.ts @@ -1,5 +1,6 @@ -import { TYearsOfExperience } from "./person"; -import { TBaseActionParams } from "../params"; +import { TBaseActionParams } from '../params'; + +import { TYearsOfExperience } from './person'; export interface TSearchPeopleParams extends TBaseActionParams { term?: string; diff --git a/src/types/errors.ts b/src/types/errors.ts index be8fe89..049cdc4 100644 --- a/src/types/errors.ts +++ b/src/types/errors.ts @@ -1,4 +1,4 @@ -import { TSupportedFunctionName } from "../core/workflow-restoration"; +import { TSupportedFunctionName } from '../core/workflow-restoration'; /** * This error is exposed when a workflow action fails to complete. @@ -21,20 +21,20 @@ import { TSupportedFunctionName } from "../core/workflow-restoration"; * - noSalesNavigator (salesNavigatorSendMessage, salesNavigatorSyncConversation, salesNavigatorSearchCompanies, salesNavigatorSearchPeople, salesNavigatorFetchCompany, salesNavigatorFetchPerson) */ export const LINKED_API_ACTION_ERROR = { - personNotFound: "personNotFound", - messagingNotAllowed: "messagingNotAllowed", - alreadyPending: "alreadyPending", - alreadyConnected: "alreadyConnected", - emailRequired: "emailRequired", - requestNotAllowed: "requestNotAllowed", - notPending: "notPending", - retrievingNotAllowed: "retrievingNotAllowed", - connectionNotFound: "connectionNotFound", - searchingNotAllowed: "searchingNotAllowed", - companyNotFound: "companyNotFound", - postNotFound: "postNotFound", - commentingNotAllowed: "commentingNotAllowed", - noSalesNavigator: "noSalesNavigator", + personNotFound: 'personNotFound', + messagingNotAllowed: 'messagingNotAllowed', + alreadyPending: 'alreadyPending', + alreadyConnected: 'alreadyConnected', + emailRequired: 'emailRequired', + requestNotAllowed: 'requestNotAllowed', + notPending: 'notPending', + retrievingNotAllowed: 'retrievingNotAllowed', + connectionNotFound: 'connectionNotFound', + searchingNotAllowed: 'searchingNotAllowed', + companyNotFound: 'companyNotFound', + postNotFound: 'postNotFound', + commentingNotAllowed: 'commentingNotAllowed', + noSalesNavigator: 'noSalesNavigator', } as const; export type TLinkedApiActionErrorType = (typeof LINKED_API_ACTION_ERROR)[keyof typeof LINKED_API_ACTION_ERROR]; @@ -49,20 +49,19 @@ export interface TLinkedApiActionError { * @see {@link https://linkedapi.io/docs/making-requests/#common-errors} */ export const LINKED_API_ERROR = { - linkedApiTokenRequired: "linkedApiTokenRequired", - invalidLinkedApiToken: "invalidLinkedApiToken", - identificationTokenRequired: "identificationTokenRequired", - invalidIdentificationToken: "invalidIdentificationToken", - subscriptionRequired: "subscriptionRequired", - invalidRequestPayload: "invalidRequestPayload", - invalidWorkflow: "invalidWorkflow", - plusPlanRequired: "plusPlanRequired", - linkedinAccountSignedOut: "linkedinAccountSignedOut", - languageNotSupported: "languageNotSupported", - workflowTimeout: "workflowTimeout", + linkedApiTokenRequired: 'linkedApiTokenRequired', + invalidLinkedApiToken: 'invalidLinkedApiToken', + identificationTokenRequired: 'identificationTokenRequired', + invalidIdentificationToken: 'invalidIdentificationToken', + subscriptionRequired: 'subscriptionRequired', + invalidRequestPayload: 'invalidRequestPayload', + invalidWorkflow: 'invalidWorkflow', + plusPlanRequired: 'plusPlanRequired', + linkedinAccountSignedOut: 'linkedinAccountSignedOut', + languageNotSupported: 'languageNotSupported', + workflowTimeout: 'workflowTimeout', } as const; -export type TLinkedApiErrorType = - (typeof LINKED_API_ERROR)[keyof typeof LINKED_API_ERROR]; +export type TLinkedApiErrorType = (typeof LINKED_API_ERROR)[keyof typeof LINKED_API_ERROR]; export class LinkedApiError extends Error { public type: TLinkedApiErrorType; public override message: string; @@ -76,9 +75,9 @@ export class LinkedApiError extends Error { } public static unknownError( - message: string = "Unknown error. Please contact support.", + message: string = 'Unknown error. Please contact support.', ): LinkedApiError { - return new LinkedApiError("unknownError" as TLinkedApiErrorType, message); + return new LinkedApiError('unknownError' as TLinkedApiErrorType, message); } } @@ -92,7 +91,7 @@ export class LinkedApiWorkflowTimeoutError extends LinkedApiError { constructor(workflowId: string, functionName: TSupportedFunctionName) { super( - "workflowTimeout", + 'workflowTimeout', `Workflow ${workflowId} timed out. Use restoreWorkflow(${workflowId}, ${functionName}) to restore the workflow.`, { workflowId, diff --git a/src/types/http-client.ts b/src/types/http-client.ts index e56e461..97a2d8c 100644 --- a/src/types/http-client.ts +++ b/src/types/http-client.ts @@ -1,9 +1,6 @@ -import type { TLinkedApiResponse } from "../types/responses"; +import type { TLinkedApiResponse } from '../types/responses'; export abstract class HttpClient { public abstract get(url: string): Promise>; - public abstract post( - url: string, - data?: unknown, - ): Promise>; + public abstract post(url: string, data?: unknown): Promise>; } diff --git a/src/types/index.ts b/src/types/index.ts index f34c455..2a759cf 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,7 +1,7 @@ -export * from "./config"; -export * from "./errors"; -export * from "./params"; -export * from "./responses"; -export * from "./workflows"; -export * from "./actions/index"; -export * from "./http-client"; +export * from './config'; +export * from './errors'; +export * from './params'; +export * from './responses'; +export * from './workflows'; +export * from './actions/index'; +export * from './http-client'; diff --git a/src/types/workflows.ts b/src/types/workflows.ts index 380c15c..3be1c9f 100644 --- a/src/types/workflows.ts +++ b/src/types/workflows.ts @@ -1,4 +1,4 @@ -import { TLinkedApiActionError } from "../types/errors"; +import { TLinkedApiActionError } from '../types/errors'; export interface TSingleActionWorkflowDefinition { actionType: string; @@ -9,16 +9,13 @@ export type TWorkflowDefinition = | TSingleActionWorkflowDefinition | TSingleActionWorkflowDefinition[]; -export type TWorkflowStatus = "running" | "completed" | "failed"; +export type TWorkflowStatus = 'running' | 'completed' | 'failed'; export type TWorkflowCompletion = + | TWorkflowCompletionSingleAction + | TWorkflowCompletionSingleAction[]; - | TWorkflowCompletionSingleAction - | TWorkflowCompletionSingleAction[]; - -export interface TWorkflowCompletionSingleAction< - TResult extends TWorkflowData = TWorkflowData, -> { +export interface TWorkflowCompletionSingleAction { data?: TResult; error?: TLinkedApiActionError; actionType: string; @@ -31,9 +28,7 @@ export interface TWorkflowFailure { message: string; } -export interface TWorkflowResponse< - TResult extends TWorkflowData = TWorkflowData, -> { +export interface TWorkflowResponse { workflowId: string; workflowStatus: TWorkflowStatus; completion?: TWorkflowCompletion; @@ -47,9 +42,7 @@ export interface TWorkflowSingleData { export type TWorkflowData = TWorkflowSingleData | TWorkflowSingleData[]; -export interface TSingleActionResponse< - TResult extends TWorkflowData = TWorkflowData, -> { +export interface TSingleActionResponse { data?: TResult; error?: TLinkedApiActionError; actionType: string; From 4a89e87556d4624a30c7e7357fc3e3ed134ca99f Mon Sep 17 00:00:00 2001 From: Kiril Ivonchik Date: Fri, 22 Aug 2025 12:41:22 +0200 Subject: [PATCH 08/10] Custom workflow --- README.md | 14 +- examples/custom-workflow.ts | 11 +- examples/restore-workflow.ts | 53 ---- src/core/base-operation.abstract.ts | 90 ------ src/core/index.ts | 5 +- src/core/operation.ts | 150 ++++++++++ src/core/poll-results.ts | 28 ++ src/core/workflow-executor.ts | 80 ------ src/core/workflow-handler.ts | 36 --- src/core/workflow-restoration.ts | 268 ------------------ src/index.ts | 73 ++--- src/mappers/array-workflow-mapper.ts | 6 +- src/mappers/base-mapper.abstract.ts | 21 +- src/mappers/simple-workflow-mapper.ts | 6 +- src/mappers/then-workflow-mapper.abstract.ts | 6 +- src/mappers/void-workflow-mapper.ts | 6 +- src/operations/check-connection-status.ts | 5 +- src/operations/comment-on-post.ts | 4 +- src/operations/custom-workflow.ts | 3 + src/operations/fetch-company.ts | 5 +- src/operations/fetch-person.ts | 5 +- src/operations/fetch-post.ts | 5 +- src/operations/index.ts | 1 + src/operations/react-to-post.ts | 4 +- src/operations/remove-connection.ts | 4 +- src/operations/retrieve-connections.ts | 5 +- src/operations/retrieve-pending-requests.ts | 4 +- src/operations/retrieve-performance.ts | 5 +- src/operations/retrieve-ssi.ts | 5 +- .../sales-navigator-fetch-company.ts | 5 +- .../sales-navigator-fetch-person.ts | 5 +- .../sales-navigator-search-companies.ts | 6 +- .../sales-navigator-search-people.ts | 5 +- .../sales-navigator-send-message.ts | 5 +- .../sales-navigator-sync-conversation.ts | 6 +- src/operations/search-companies.ts | 5 +- src/operations/search-people.ts | 5 +- src/operations/send-connection-request.ts | 5 +- src/operations/send-message.ts | 5 +- src/operations/sync-conversation.ts | 5 +- src/operations/withdraw-connection-request.ts | 5 +- src/types/errors.ts | 6 +- src/types/workflows.ts | 6 +- 43 files changed, 290 insertions(+), 692 deletions(-) delete mode 100644 examples/restore-workflow.ts delete mode 100644 src/core/base-operation.abstract.ts create mode 100644 src/core/operation.ts create mode 100644 src/core/poll-results.ts delete mode 100644 src/core/workflow-executor.ts delete mode 100644 src/core/workflow-handler.ts delete mode 100644 src/core/workflow-restoration.ts create mode 100644 src/operations/custom-workflow.ts diff --git a/README.md b/README.md index e96ec0c..6446267 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ Restore a WorkflowHandler for a previously started workflow using its ID and fun - **Parameters:** - `workflowId: string` - The unique identifier of the workflow to restore - - `functionName: TSupportedFunctionName` - The name of the function that was used to create the workflow + - `functionName: TOperationName` - The name of the function that was used to create the workflow - **Returns:** `Promise>>` - WorkflowHandler with exact result type based on the function name - **Type Safety:** Full TypeScript inference for exact return types based on the function name - **Documentation:** [Executing Workflows](https://linkedapi.io/docs/executing-workflows/) @@ -126,14 +126,14 @@ Restore a WorkflowHandler for a previously started workflow using its ID and fun // Restore a person fetching workflow with full type safety const personHandler = await linkedapi.restoreWorkflow( "workflow-id-123", - "fetchPerson" + "fetchPerson", ); const personResult = await personHandler.result(); // Restore a company fetching workflow const companyHandler = await linkedapi.restoreWorkflow( "workflow-id-456", - "fetchCompany" + "fetchCompany", ); const companyResult = await companyHandler.result(); ``` @@ -560,7 +560,7 @@ if (statsResponse.success) { console.log("Total actions:", statsResponse.result?.length); statsResponse.result?.forEach((action) => { console.log( - `${action.actionType}: ${action.success ? "SUCCESS" : "FAILED"}` + `${action.actionType}: ${action.success ? "SUCCESS" : "FAILED"}`, ); }); } @@ -654,7 +654,7 @@ if (response.success) { response.result?.forEach((conversation) => { console.log( `Messages from ${conversation.personUrl}:`, - conversation.messages + conversation.messages, ); }); } @@ -814,7 +814,7 @@ Linked API provides structured error handling for different failure scenarios. T - **Action-specific errors** - errors from individual actions within a workflow that don't cause the entire workflow to fail ```typescript -import LinkedApi, { LinkedApiError } from 'linkedapi-node'; +import LinkedApi, { LinkedApiError } from "linkedapi-node"; try { const workflow = await linkedapi.fetchPerson({ @@ -858,7 +858,7 @@ try { - **`invalidWorkflow`** - Workflow configuration is not valid due to violated action constraints or invalid action parameters: {validation_details}. - **`plusPlanRequired`** - Some actions in this workflow require the Plus plan. - **`linkedinAccountSignedOut`** - Your LinkedIn account has been signed out in our cloud browser. This occasionally happens as LinkedIn may sign out accounts after an extended period. You'll need to visit our platform and reconnect your account. -- **`languageNotSupported`** - Your LinkedIn account uses a language other than English, which is currently the only supported option. +- **`languageNotSupported`** - Your LinkedIn account uses a language other than English, which is currently the only supported option. - **`timeout`** - Local execution timeout. Contains `workflowId` and `functionName` for future restoration. ### Common Action Error Types diff --git a/examples/custom-workflow.ts b/examples/custom-workflow.ts index 3e9bafb..e044a2a 100644 --- a/examples/custom-workflow.ts +++ b/examples/custom-workflow.ts @@ -9,9 +9,10 @@ async function customWorkflowExample(): Promise { try { console.log('šŸš€ Linked API custom workflow example starting...'); - const customWorkflow = await linkedapi.executeCustomWorkflow({ + const workflowId = await linkedapi.customWorkflow.execute({ actionType: 'st.searchPeople', - limit: 5, + term: "John", + limit: 3, filter: { locations: ["San Francisco"], }, @@ -26,11 +27,11 @@ async function customWorkflowExample(): Promise { } } }); - console.log('šŸ” Workflow started: ', customWorkflow.workflowId); - const result = (await customWorkflow.result()).data!; + console.log('šŸ” Workflow started: ', workflowId); + const result = await linkedapi.customWorkflow.result(workflowId); console.log('āœ… Custom workflow executed successfully'); - console.log('šŸ” Result: ', result.completion); + console.log('šŸ” Result: ', JSON.stringify(result, null, 2)); } catch (error) { if (error instanceof LinkedApiError) { console.error('🚨 Linked API Error:', error.message); diff --git a/examples/restore-workflow.ts b/examples/restore-workflow.ts deleted file mode 100644 index ed2b943..0000000 --- a/examples/restore-workflow.ts +++ /dev/null @@ -1,53 +0,0 @@ -import LinkedApi, { FUNCTION_NAME } from 'linkedapi-node'; - -async function example(): Promise { - // First run - const linkedapi = new LinkedApi({ - linkedApiToken: process.env.LINKED_API_TOKEN!, - identificationToken: process.env.IDENTIFICATION_TOKEN!, - }); - - const savedWorkflowId = await linkedapi.fetchPerson.execute({ - personUrl: 'https://www.linkedin.com/in/example-person/', - retrieveExperience: true, - retrieveEducation: true, - }); - - // ... App stops here - - // App restart: rebuild LinkedApi client - const linkedapiAfterRestart = new LinkedApi({ - linkedApiToken: process.env.LINKED_API_TOKEN!, - identificationToken: process.env.IDENTIFICATION_TOKEN!, - }); - - const restoredHandler = await linkedapiAfterRestart.restoreWorkflow( - savedWorkflowId, - FUNCTION_NAME.fetchPerson, - ); - - // Or if you want to restore a raw workflow (for executeCustomWorkflow) - const rawHandler = await linkedapiAfterRestart.restoreWorkflow( - savedWorkflowId, - FUNCTION_NAME.customWorkflow, - ); - - console.log("Restoration started: ", restoredHandler.workflowId); - - const result = await restoredHandler.result(); - if (result.data) { - console.log("šŸ‘¤ Restored name:", result.data.name); - console.log("šŸ’¼ Restored experience:", result.data.experiences?.length); - console.log("Restored result:", JSON.stringify(result, null, 2)); - } - if (result.errors.length > 0) { - console.error('🚨 Errors:', JSON.stringify(result.errors, null, 2)); - } - - const rawResult = await rawHandler.result(); - console.log("Raw result:", JSON.stringify(rawResult, null, 2)); -} - -if (require.main === module) { - example(); -} diff --git a/src/core/base-operation.abstract.ts b/src/core/base-operation.abstract.ts deleted file mode 100644 index 1cd26ba..0000000 --- a/src/core/base-operation.abstract.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { WaitForCompletionOptions } from '../core/workflow-executor'; -import { TSupportedFunctionName } from '../core/workflow-restoration'; -import { BaseMapper, TMappedResponse } from '../mappers/base-mapper.abstract'; -import { - HttpClient, - LinkedApiError, - LinkedApiWorkflowTimeoutError, - TLinkedApiErrorType, - TWorkflowResponse, -} from '../types'; - -export abstract class PredefinedOperation { - protected abstract readonly functionName: TSupportedFunctionName; - protected abstract readonly mapper: BaseMapper; - - constructor(private readonly httpClient: HttpClient) {} - - public async execute(params: TParams): Promise { - const request = this.mapper.mapRequest(params); - const response = await this.httpClient.post(`/workflows`, request); - if (response.error) { - throw new LinkedApiError(response.error.type as TLinkedApiErrorType, response.error.message); - } - if (!response.result) { - throw LinkedApiError.unknownError(); - } - return response.result.workflowId; - } - - public async result( - workflowId: string, - options: WaitForCompletionOptions = {}, - ): Promise> { - try { - const rawResult = await this.getResult(workflowId, options); - - if (!this.mapper) { - return { - data: rawResult as TResult, - errors: [], - }; - } - - return this.mapper.mapResponse(rawResult); - } catch (error) { - if (error instanceof LinkedApiError && error.type === 'workflowTimeout') { - throw new LinkedApiWorkflowTimeoutError(workflowId, this.functionName); - } - throw error; - } - } - - public async getResult( - workflowId: string, - options: WaitForCompletionOptions, - ): Promise { - const { pollInterval = 5000, timeout = 24 * 60 * 60 * 1000 } = options; - const startTime = Date.now(); - - while (Date.now() - startTime < timeout) { - const result = await this.getWorkflowResult(workflowId); - - if (result.workflowStatus === 'completed' || result.workflowStatus === 'failed') { - return result; - } - - await this.sleep(pollInterval); - } - - throw new LinkedApiError( - 'workflowTimeout', - `Workflow ${workflowId} did not complete within ${timeout}ms`, - ); - } - - public async getWorkflowResult(workflowId: string): Promise { - const response = await this.httpClient.get(`/workflows/${workflowId}`); - if (response.error) { - throw new LinkedApiError(response.error.type as TLinkedApiErrorType, response.error.message); - } - if (!response.result) { - throw LinkedApiError.unknownError(); - } - return response.result; - } - - private async sleep(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); - } -} diff --git a/src/core/index.ts b/src/core/index.ts index 2c1925a..244ba76 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,5 +1,2 @@ -export * from './base-operation.abstract'; +export * from './operation'; export * from './linked-api-http-client'; -export * from './workflow-executor'; -export * from './workflow-handler'; -export * from './workflow-restoration'; diff --git a/src/core/operation.ts b/src/core/operation.ts new file mode 100644 index 0000000..7e71251 --- /dev/null +++ b/src/core/operation.ts @@ -0,0 +1,150 @@ +import { BaseMapper, TMappedResponse } from '../mappers/base-mapper.abstract'; +import { + HttpClient, + LinkedApiError, + LinkedApiWorkflowTimeoutError, + TLinkedApiErrorType, + TWorkflowCompletion, + TWorkflowDefinition, + TWorkflowResponse, + TWorkflowStatus, +} from '../types'; + +import { pollWorkflowResult } from './poll-results'; + +export const OPERATION_NAME = { + fetchPerson: 'fetchPerson', + fetchCompany: 'fetchCompany', + salesNavigatorFetchCompany: 'salesNavigatorFetchCompany', + salesNavigatorFetchPerson: 'salesNavigatorFetchPerson', + fetchPost: 'fetchPost', + searchCompanies: 'searchCompanies', + salesNavigatorSearchCompanies: 'salesNavigatorSearchCompanies', + searchPeople: 'searchPeople', + salesNavigatorSearchPeople: 'salesNavigatorSearchPeople', + sendMessage: 'sendMessage', + syncConversation: 'syncConversation', + salesNavigatorSendMessage: 'salesNavigatorSendMessage', + salesNavigatorSyncConversation: 'salesNavigatorSyncConversation', + sendConnectionRequest: 'sendConnectionRequest', + checkConnectionStatus: 'checkConnectionStatus', + withdrawConnectionRequest: 'withdrawConnectionRequest', + retrievePendingRequests: 'retrievePendingRequests', + retrieveConnections: 'retrieveConnections', + removeConnection: 'removeConnection', + reactToPost: 'reactToPost', + commentOnPost: 'commentOnPost', + retrieveSSI: 'retrieveSSI', + retrievePerformance: 'retrievePerformance', + customWorkflow: 'customWorkflow', +} as const; +export type TOperationName = (typeof OPERATION_NAME)[keyof typeof OPERATION_NAME]; + +export interface WaitForCompletionOptions { + pollInterval?: number; + timeout?: number; +} + +export abstract class PredefinedOperation { + protected abstract readonly functionName: TOperationName; + protected abstract readonly mapper: BaseMapper; + + private readonly operation: CustomWorkflowOperation; + + constructor(httpClient: HttpClient) { + this.operation = new CustomWorkflowOperation(httpClient); + } + + public async execute(params: TParams): Promise { + const request = this.mapper.mapRequest(params); + return this.operation.execute(request); + } + + public async result( + workflowId: string, + options: WaitForCompletionOptions = {}, + ): Promise> { + try { + const rawResult = await this.operation.result(workflowId, options); + return this.mapper.mapResponse(rawResult); + } catch (error) { + if (error instanceof LinkedApiError && error.type === 'workflowTimeout') { + throw new LinkedApiWorkflowTimeoutError(workflowId, this.functionName); + } + throw error; + } + } + + public async immediateResult( + workflowId: string, + ): Promise> { + const result = await this.operation.immediateResult(workflowId); + if (result === 'running') { + return result; + } + return this.mapper.mapResponse(result as TWorkflowCompletion); + } +} + +export class CustomWorkflowOperation { + constructor(private readonly httpClient: HttpClient) {} + + public async execute(params: TWorkflowDefinition): Promise { + const response = await this.httpClient.post(`/workflows`, params); + if (response.error) { + throw new LinkedApiError(response.error.type as TLinkedApiErrorType, response.error.message); + } + if (!response.result) { + throw LinkedApiError.unknownError(); + } + return response.result.workflowId; + } + + public async result( + workflowId: string, + options: WaitForCompletionOptions = {}, + ): Promise { + try { + return pollWorkflowResult( + workflowId, + (workflowId) => this.immediateResult(workflowId), + options, + ); + } catch (error) { + if (error instanceof LinkedApiError && error.type === 'workflowTimeout') { + throw new LinkedApiWorkflowTimeoutError(workflowId, 'customWorkflow'); + } + throw error; + } + } + + public async immediateResult(workflowId: string): Promise { + const workflowResult = await this.getWorkflowResult(workflowId); + if (workflowResult.workflowStatus === 'running') { + return workflowResult.workflowStatus; + } + return this.getCompletion(workflowResult); + } + + private async getWorkflowResult(workflowId: string): Promise { + const response = await this.httpClient.get(`/workflows/${workflowId}`); + if (response.error) { + throw new LinkedApiError(response.error.type as TLinkedApiErrorType, response.error.message); + } + if (!response.result) { + throw LinkedApiError.unknownError(); + } + return response.result; + } + + private getCompletion(response: TWorkflowResponse): TWorkflowCompletion { + if (!response.completion) { + const { failure } = response; + if (failure) { + throw new LinkedApiError(failure.reason as TLinkedApiErrorType, failure.message); + } + throw LinkedApiError.unknownError(); + } + return response.completion; + } +} diff --git a/src/core/poll-results.ts b/src/core/poll-results.ts new file mode 100644 index 0000000..13381d4 --- /dev/null +++ b/src/core/poll-results.ts @@ -0,0 +1,28 @@ +import { LinkedApiError, TWorkflowCompletion, TWorkflowStatus } from '../types'; + +import { WaitForCompletionOptions } from './operation'; + +export async function pollWorkflowResult( + workflowId: string, + workflowResultFn: (workflowId: string) => Promise, + options: WaitForCompletionOptions, +): Promise { + const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + + const { pollInterval = 5000, timeout = 24 * 60 * 60 * 1000 } = options; + const startTime = Date.now(); + + while (Date.now() - startTime < timeout) { + const result = await workflowResultFn(workflowId); + + if (result !== 'running') { + return result as TWorkflowCompletion; + } + + await sleep(pollInterval); + } + throw new LinkedApiError( + 'workflowTimeout', + `Workflow ${workflowId} did not complete within ${timeout}ms`, + ); +} diff --git a/src/core/workflow-executor.ts b/src/core/workflow-executor.ts deleted file mode 100644 index acf4af1..0000000 --- a/src/core/workflow-executor.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { setTimeout as sleep } from 'node:timers/promises'; - -import type { HttpClient, TLinkedApiErrorType } from '../types'; -import { LinkedApiError } from '../types/errors'; -import type { TWorkflowDefinition, TWorkflowResponse } from '../types/workflows'; - -/** - * Options for waiting for a workflow to complete. - * @property pollInterval - The interval in milliseconds to poll for the workflow result. Defaults to 5000ms. - * @property timeout - The maximum time in milliseconds to wait for the workflow to complete. Defaults to 24 hours. - */ -export interface WaitForCompletionOptions { - pollInterval?: number; - timeout?: number; -} - -export class WorkflowExecutor { - private readonly httpClient: HttpClient; - private readonly apiPath: string; - private readonly workflowTimeout: number; - - constructor({ - httpClient, - apiPath, - workflowTimeout, - }: { - httpClient: HttpClient; - apiPath: string; - workflowTimeout: number; - }) { - this.httpClient = httpClient; - this.apiPath = apiPath; - this.workflowTimeout = workflowTimeout; - } - - public async startWorkflow(request: TWorkflowDefinition): Promise { - const response = await this.httpClient.post(`${this.apiPath}`, request); - if (response.error) { - throw new LinkedApiError(response.error.type as TLinkedApiErrorType, response.error.message); - } - if (!response.result) { - throw LinkedApiError.unknownError(); - } - return response.result; - } - - public async result( - workflowId: string, - options: WaitForCompletionOptions, - ): Promise { - const { pollInterval = 5000, timeout = this.workflowTimeout ?? 24 * 60 * 60 * 1000 } = options; - const startTime = Date.now(); - - while (Date.now() - startTime < timeout) { - const result = await this.getWorkflowResult(workflowId); - - if (result.workflowStatus === 'completed' || result.workflowStatus === 'failed') { - return result; - } - - await sleep(pollInterval); - } - - throw new LinkedApiError( - 'workflowTimeout', - `Workflow ${workflowId} did not complete within ${timeout}ms`, - ); - } - - public async getWorkflowResult(workflowId: string): Promise { - const response = await this.httpClient.get(`${this.apiPath}/${workflowId}`); - if (response.error) { - throw new LinkedApiError(response.error.type as TLinkedApiErrorType, response.error.message); - } - if (!response.result) { - throw LinkedApiError.unknownError(); - } - return response.result; - } -} diff --git a/src/core/workflow-handler.ts b/src/core/workflow-handler.ts deleted file mode 100644 index ce56673..0000000 --- a/src/core/workflow-handler.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { TSupportedFunctionName } from '../core/workflow-restoration'; -import type { BaseMapper, TMappedResponse } from '../mappers/base-mapper.abstract'; -import { LinkedApiError, LinkedApiWorkflowTimeoutError } from '../types'; -import type { TBaseActionParams } from '../types/params'; -import type { TWorkflowResponse } from '../types/workflows'; - -import type { WaitForCompletionOptions, WorkflowExecutor } from './workflow-executor'; - -export class WorkflowHandler { - constructor( - public readonly workflowId: string, - public readonly functionName: TSupportedFunctionName, - private readonly workflowExecutor: WorkflowExecutor, - private readonly mapper?: BaseMapper, - ) {} - - public async result(options: WaitForCompletionOptions = {}): Promise> { - try { - const rawResult = await this.workflowExecutor.result(this.workflowId, options); - - if (!this.mapper) { - return { - data: rawResult as TResult, - errors: [], - }; - } - - return this.mapper.mapResponse(rawResult); - } catch (error) { - if (error instanceof LinkedApiError && error.type === 'workflowTimeout') { - throw new LinkedApiWorkflowTimeoutError(this.workflowId, this.functionName); - } - throw error; - } - } -} diff --git a/src/core/workflow-restoration.ts b/src/core/workflow-restoration.ts deleted file mode 100644 index 6ae5536..0000000 --- a/src/core/workflow-restoration.ts +++ /dev/null @@ -1,268 +0,0 @@ -import { ArrayWorkflowMapper, SimpleWorkflowMapper, VoidWorkflowMapper } from '../mappers'; -import type { BaseMapper } from '../mappers/base-mapper.abstract'; -import { - FetchCompanyMapper, - FetchPersonMapper, - NvFetchCompanyMapper, - NvFetchPersonMapper, -} from '../operations'; -import { - TBaseActionParams, - TCheckConnectionStatusParams, - TCheckConnectionStatusResult, - TCommentOnPostParams, - TFetchPostParams, - TFetchPostResult, - TNvSearchCompaniesParams, - TNvSearchCompanyResult, - TNvSearchPeopleParams, - TNvSearchPeopleResult, - TNvSendMessageParams, - TNvSyncConversationParams, - TReactToPostParams, - TRemoveConnectionParams, - TRetrieveConnectionsParams, - TRetrieveConnectionsResult, - TRetrievePendingRequestsResult, - TRetrievePerformanceResult, - TRetrieveSSIResult, - TSearchCompaniesParams, - TSearchCompanyResult, - TSearchPeopleParams, - TSearchPeopleResult, - TSendConnectionRequestParams, - TSendMessageParams, - TSyncConversationParams, - TWithdrawConnectionRequestParams, -} from '../types'; -import type { - TBaseFetchCompanyParamsWide, - TNvBaseFetchCompanyParamsWide, -} from '../types/actions/company'; -import type { TBaseFetchPersonParamsWide } from '../types/actions/person'; -import type { TWorkflowResponse } from '../types/workflows'; - -export type TRestoreResultType = - T extends typeof FUNCTION_NAME.customWorkflow - ? TWorkflowResponse - : TRestoreMapperReturnType extends BaseMapper - ? R - : never; - -export type TRestoreMapperReturnType = - T extends typeof FUNCTION_NAME.fetchPerson - ? FetchPersonMapper - : T extends typeof FUNCTION_NAME.fetchCompany - ? FetchCompanyMapper - : T extends typeof FUNCTION_NAME.salesNavigatorFetchCompany - ? NvFetchCompanyMapper - : T extends typeof FUNCTION_NAME.salesNavigatorFetchPerson - ? NvFetchPersonMapper - : T extends typeof FUNCTION_NAME.fetchPost - ? SimpleWorkflowMapper - : T extends typeof FUNCTION_NAME.searchCompanies - ? ArrayWorkflowMapper - : T extends typeof FUNCTION_NAME.salesNavigatorSearchCompanies - ? ArrayWorkflowMapper - : T extends typeof FUNCTION_NAME.searchPeople - ? ArrayWorkflowMapper - : T extends typeof FUNCTION_NAME.salesNavigatorSearchPeople - ? ArrayWorkflowMapper - : T extends typeof FUNCTION_NAME.sendMessage - ? VoidWorkflowMapper - : T extends typeof FUNCTION_NAME.syncConversation - ? VoidWorkflowMapper - : T extends typeof FUNCTION_NAME.salesNavigatorSendMessage - ? VoidWorkflowMapper - : T extends typeof FUNCTION_NAME.salesNavigatorSyncConversation - ? VoidWorkflowMapper - : T extends typeof FUNCTION_NAME.sendConnectionRequest - ? VoidWorkflowMapper - : T extends typeof FUNCTION_NAME.checkConnectionStatus - ? SimpleWorkflowMapper< - TCheckConnectionStatusParams, - TCheckConnectionStatusResult - > - : T extends typeof FUNCTION_NAME.withdrawConnectionRequest - ? VoidWorkflowMapper - : T extends typeof FUNCTION_NAME.retrievePendingRequests - ? ArrayWorkflowMapper< - TBaseActionParams, - TRetrievePendingRequestsResult - > - : T extends typeof FUNCTION_NAME.retrieveConnections - ? ArrayWorkflowMapper< - TRetrieveConnectionsParams, - TRetrieveConnectionsResult - > - : T extends typeof FUNCTION_NAME.removeConnection - ? VoidWorkflowMapper - : T extends typeof FUNCTION_NAME.reactToPost - ? VoidWorkflowMapper - : T extends typeof FUNCTION_NAME.commentOnPost - ? VoidWorkflowMapper - : T extends typeof FUNCTION_NAME.retrieveSSI - ? SimpleWorkflowMapper< - TBaseActionParams, - TRetrieveSSIResult - > - : T extends typeof FUNCTION_NAME.retrievePerformance - ? SimpleWorkflowMapper< - TBaseActionParams, - TRetrievePerformanceResult - > - : T extends typeof FUNCTION_NAME.customWorkflow - ? null // Special case: no mapper needed, will return raw TWorkflowResponse - : never; - -export const FUNCTION_NAME = { - fetchPerson: 'fetchPerson', - fetchCompany: 'fetchCompany', - salesNavigatorFetchCompany: 'salesNavigatorFetchCompany', - salesNavigatorFetchPerson: 'salesNavigatorFetchPerson', - fetchPost: 'fetchPost', - searchCompanies: 'searchCompanies', - salesNavigatorSearchCompanies: 'salesNavigatorSearchCompanies', - searchPeople: 'searchPeople', - salesNavigatorSearchPeople: 'salesNavigatorSearchPeople', - sendMessage: 'sendMessage', - syncConversation: 'syncConversation', - salesNavigatorSendMessage: 'salesNavigatorSendMessage', - salesNavigatorSyncConversation: 'salesNavigatorSyncConversation', - sendConnectionRequest: 'sendConnectionRequest', - checkConnectionStatus: 'checkConnectionStatus', - withdrawConnectionRequest: 'withdrawConnectionRequest', - retrievePendingRequests: 'retrievePendingRequests', - retrieveConnections: 'retrieveConnections', - removeConnection: 'removeConnection', - reactToPost: 'reactToPost', - commentOnPost: 'commentOnPost', - retrieveSSI: 'retrieveSSI', - retrievePerformance: 'retrievePerformance', - customWorkflow: 'customWorkflow', // Special case: no mapper needed, will return raw TWorkflowResponse -} as const; -export type TSupportedFunctionName = (typeof FUNCTION_NAME)[keyof typeof FUNCTION_NAME]; - -/** - * Internal function to restore a mapper from a function name. - * This provides type-safe mapper creation for workflow restoration. - */ -export function createMapperFromFunctionName( - functionName: T, -): TRestoreMapperReturnType { - switch (functionName) { - case FUNCTION_NAME.fetchPerson: { - return new FetchPersonMapper() as TRestoreMapperReturnType; - } - case FUNCTION_NAME.fetchCompany: { - return new FetchCompanyMapper() as TRestoreMapperReturnType; - } - case FUNCTION_NAME.salesNavigatorFetchCompany: { - return new NvFetchCompanyMapper() as TRestoreMapperReturnType; - } - case FUNCTION_NAME.salesNavigatorFetchPerson: { - return new NvFetchPersonMapper() as TRestoreMapperReturnType; - } - case FUNCTION_NAME.fetchPost: { - return new SimpleWorkflowMapper({ - actionType: 'st.openPost', - defaultParams: { basicInfo: true }, - }) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.searchCompanies: { - return new ArrayWorkflowMapper( - 'st.searchCompanies', - ) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.salesNavigatorSearchCompanies: { - return new ArrayWorkflowMapper( - 'nv.searchCompanies', - ) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.searchPeople: { - return new ArrayWorkflowMapper( - 'st.searchPeople', - ) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.salesNavigatorSearchPeople: { - return new ArrayWorkflowMapper( - 'nv.searchPeople', - ) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.sendMessage: { - return new VoidWorkflowMapper( - 'st.sendMessage', - ) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.syncConversation: { - return new VoidWorkflowMapper( - 'st.syncConversation', - ) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.salesNavigatorSendMessage: { - return new VoidWorkflowMapper( - 'nv.sendMessage', - ) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.salesNavigatorSyncConversation: { - return new VoidWorkflowMapper( - 'nv.syncConversation', - ) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.sendConnectionRequest: { - return new VoidWorkflowMapper( - 'st.sendConnectionRequest', - ) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.checkConnectionStatus: { - return new SimpleWorkflowMapper({ - actionType: 'st.checkConnectionStatus', - }) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.withdrawConnectionRequest: { - return new VoidWorkflowMapper( - 'st.withdrawConnectionRequest', - ) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.retrievePendingRequests: { - return new ArrayWorkflowMapper( - 'st.retrievePendingRequests', - ) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.retrieveConnections: { - return new ArrayWorkflowMapper( - 'st.retrieveConnections', - ) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.removeConnection: { - return new VoidWorkflowMapper( - 'st.removeConnection', - ) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.reactToPost: { - return new VoidWorkflowMapper( - 'st.reactToPost', - ) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.commentOnPost: { - return new VoidWorkflowMapper( - 'st.commentOnPost', - ) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.retrieveSSI: { - return new SimpleWorkflowMapper({ - actionType: 'st.retrieveSSI', - }) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.retrievePerformance: { - return new SimpleWorkflowMapper({ - actionType: 'st.retrievePerformance', - }) as TRestoreMapperReturnType; - } - case FUNCTION_NAME.customWorkflow: { - return null as TRestoreMapperReturnType; - } - default: - throw new Error(`Unsupported functionName: ${functionName}`); - } -} diff --git a/src/index.ts b/src/index.ts index c4411ea..843903d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,9 @@ import { buildLinkedApiHttpClient } from './core/linked-api-http-client'; -import { WorkflowExecutor } from './core/workflow-executor'; -import { WorkflowHandler } from './core/workflow-handler'; -import { - createMapperFromFunctionName, - TRestoreResultType, - TSupportedFunctionName, -} from './core/workflow-restoration'; -import type { BaseMapper, TMappedResponse } from './mappers/base-mapper.abstract'; +import type { TMappedResponse } from './mappers/base-mapper.abstract'; import { CheckConnectionStatus, CommentOnPost, + CustomWorkflow, FetchCompany, FetchPerson, FetchPost, @@ -37,7 +31,6 @@ import { TApiUsageAction, TApiUsageStatsParams, TApiUsageStatsResponse, - TBaseActionParams, TConversationPollRequest, TConversationPollResponse, TConversationPollResult, @@ -76,7 +69,6 @@ import type { TWorkflowDefinition, TWorkflowResponse } from './types/workflows'; */ class LinkedApi { private readonly httpClient: HttpClient; - private readonly workflowExecutor: WorkflowExecutor; /** * Initialize LinkedApi client with your API tokens. @@ -86,12 +78,8 @@ class LinkedApi { */ constructor(config: TLinkedApiConfig) { this.httpClient = buildLinkedApiHttpClient(config); - this.workflowExecutor = new WorkflowExecutor({ - httpClient: this.httpClient, - apiPath: '/workflows', - workflowTimeout: 24 * 60 * 60 * 1000, - }); + this.customWorkflow = new CustomWorkflow(this.httpClient); this.fetchPerson = new FetchPerson(this.httpClient); this.searchCompanies = new SearchCompanies(this.httpClient); this.fetchCompany = new FetchCompany(this.httpClient); @@ -133,7 +121,7 @@ class LinkedApi { * * @example * ```typescript - * const workflow = await linkedapi.executeCustomWorkflow({ + * const workflowId = await linkedapi.customWorkflow.execute({ * actionType: "st.searchCompanies", * term: "Tech Inc", * filter: { @@ -154,13 +142,10 @@ class LinkedApi { * } * }); * - * const result = await workflow.result(); + * const result = await linkedapi.customWorkflow.result(workflowId); * ``` */ - public async executeCustomWorkflow(params: TWorkflowDefinition): Promise { - const workflow = await this.workflowExecutor.startWorkflow(params); - return new WorkflowHandler(workflow.workflowId, 'customWorkflow', this.workflowExecutor); - } + public customWorkflow: CustomWorkflow; /** * Restore a WorkflowHandler for a previously started workflow using its ID and function name. @@ -183,27 +168,27 @@ class LinkedApi { * // TypeScript knows exact type: TCheckConnectionStatusResult * ``` */ - public async restoreWorkflow( - workflowId: string, - functionName: TFunctionName, - ): Promise>> { - const mapper = createMapperFromFunctionName(functionName); - - if (mapper === null) { - return new WorkflowHandler( - workflowId, - functionName, - this.workflowExecutor, - ) as WorkflowHandler>; - } - - return new WorkflowHandler( - workflowId, - functionName, - this.workflowExecutor, - mapper as BaseMapper, - ) as WorkflowHandler>; - } + // public async restoreWorkflow( + // workflowId: string, + // functionName: TFunctionName, + // ): Promise>> { + // const mapper = createMapperFromFunctionName(functionName); + + // if (mapper === null) { + // return new WorkflowHandler( + // workflowId, + // functionName, + // this.workflowExecutor, + // ) as WorkflowHandler>; + // } + + // return new WorkflowHandler( + // workflowId, + // functionName, + // this.workflowExecutor, + // mapper as BaseMapper, + // ) as WorkflowHandler>; + // } // Mapper descriptor based restoration was removed in favor of restoreMapper(functionName, parameters) @@ -1017,7 +1002,7 @@ class LinkedApi { export default LinkedApi; -export { LinkedApi, WorkflowHandler }; +export { LinkedApi }; export type { TLinkedApiConfig, @@ -1028,5 +1013,5 @@ export type { }; export * from './types'; -export * from './core/workflow-restoration'; export * from './operations'; +export * from './core/operation'; diff --git a/src/mappers/array-workflow-mapper.ts b/src/mappers/array-workflow-mapper.ts index e12f98e..fe21495 100644 --- a/src/mappers/array-workflow-mapper.ts +++ b/src/mappers/array-workflow-mapper.ts @@ -1,5 +1,5 @@ import { TLinkedApiActionError } from '../types/errors'; -import type { TWorkflowDefinition, TWorkflowResponse } from '../types/workflows'; +import type { TWorkflowCompletion, TWorkflowDefinition } from '../types/workflows'; import { BaseMapper, TMappedResponse } from './base-mapper.abstract'; @@ -18,9 +18,7 @@ export class ArrayWorkflowMapper extends BaseMapper { - const completion = this.getCompletion(response); - + public mapResponse(completion: TWorkflowCompletion): TMappedResponse { if (Array.isArray(completion)) { return { data: completion.map((item) => item.data) as TResult[], diff --git a/src/mappers/base-mapper.abstract.ts b/src/mappers/base-mapper.abstract.ts index b53dbf8..bbbe583 100644 --- a/src/mappers/base-mapper.abstract.ts +++ b/src/mappers/base-mapper.abstract.ts @@ -1,24 +1,9 @@ -import { LinkedApiError, type TLinkedApiActionError, TLinkedApiErrorType } from '../types/errors'; -import type { - TWorkflowCompletion, - TWorkflowDefinition, - TWorkflowResponse, -} from '../types/workflows'; +import { type TLinkedApiActionError } from '../types/errors'; +import type { TWorkflowCompletion, TWorkflowDefinition } from '../types/workflows'; export abstract class BaseMapper { abstract mapRequest(params: TParams): TWorkflowDefinition; - abstract mapResponse(response: TWorkflowResponse): TMappedResponse; - - protected getCompletion(response: TWorkflowResponse): TWorkflowCompletion { - if (!response.completion) { - const { failure } = response; - if (failure) { - throw new LinkedApiError(failure.reason as TLinkedApiErrorType, failure.message); - } - throw LinkedApiError.unknownError(); - } - return response.completion; - } + abstract mapResponse(completion: TWorkflowCompletion): TMappedResponse; } export interface TDefaultParameters { diff --git a/src/mappers/simple-workflow-mapper.ts b/src/mappers/simple-workflow-mapper.ts index d003603..1b44046 100644 --- a/src/mappers/simple-workflow-mapper.ts +++ b/src/mappers/simple-workflow-mapper.ts @@ -1,5 +1,5 @@ import { TLinkedApiActionError } from '../types/errors'; -import type { TWorkflowDefinition, TWorkflowResponse } from '../types/workflows'; +import type { TWorkflowCompletion, TWorkflowDefinition } from '../types/workflows'; import { TDefaultParameters } from './base-mapper.abstract'; import { BaseMapper, TMappedResponse } from './base-mapper.abstract'; @@ -28,9 +28,7 @@ export class SimpleWorkflowMapper extends BaseMapper { - const completion = this.getCompletion(response); - + public mapResponse(completion: TWorkflowCompletion): TMappedResponse { if (Array.isArray(completion)) { return { data: completion.map((action) => action.data).filter(Boolean) as TResult, diff --git a/src/mappers/then-workflow-mapper.abstract.ts b/src/mappers/then-workflow-mapper.abstract.ts index 4d0e5c5..76e1daf 100644 --- a/src/mappers/then-workflow-mapper.abstract.ts +++ b/src/mappers/then-workflow-mapper.abstract.ts @@ -2,8 +2,8 @@ import { LinkedApiError, TLinkedApiActionError } from '../types/errors'; import type { TBaseActionParams } from '../types/params'; import type { TActionResponse, + TWorkflowCompletion, TWorkflowDefinition, - TWorkflowResponse, TWorkflowSingleData, } from '../types/workflows'; @@ -59,9 +59,7 @@ export abstract class ThenWorkflowMapper< } as unknown as TWorkflowDefinition; } - public override mapResponse(response: TWorkflowResponse): TMappedResponse { - const completion = this.getCompletion(response); - + public override mapResponse(completion: TWorkflowCompletion): TMappedResponse { if (Array.isArray(completion)) { return { data: completion.map((action) => action.data).filter(Boolean) as TResult, diff --git a/src/mappers/void-workflow-mapper.ts b/src/mappers/void-workflow-mapper.ts index bd7495c..e8954fe 100644 --- a/src/mappers/void-workflow-mapper.ts +++ b/src/mappers/void-workflow-mapper.ts @@ -1,6 +1,6 @@ import { TLinkedApiActionError } from '../types/errors'; import type { TBaseActionParams } from '../types/params'; -import type { TWorkflowDefinition, TWorkflowResponse } from '../types/workflows'; +import type { TWorkflowCompletion, TWorkflowDefinition } from '../types/workflows'; import { BaseMapper, TMappedResponse } from './base-mapper.abstract'; @@ -22,9 +22,7 @@ export class VoidWorkflowMapper extends BaseM } as unknown as TWorkflowDefinition; } - public mapResponse(response: TWorkflowResponse): TMappedResponse { - const completion = this.getCompletion(response); - + public mapResponse(completion: TWorkflowCompletion): TMappedResponse { if (Array.isArray(completion)) { return { data: undefined, diff --git a/src/operations/check-connection-status.ts b/src/operations/check-connection-status.ts index e00bb53..109ddff 100644 --- a/src/operations/check-connection-status.ts +++ b/src/operations/check-connection-status.ts @@ -1,5 +1,4 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { SimpleWorkflowMapper } from '../mappers'; import { TCheckConnectionStatusParams, TCheckConnectionStatusResult } from '../types'; @@ -7,7 +6,7 @@ export class CheckConnectionStatus extends PredefinedOperation< TCheckConnectionStatusParams, TCheckConnectionStatusResult > { - protected override readonly functionName: TSupportedFunctionName = 'checkConnectionStatus'; + protected override readonly functionName: TOperationName = 'checkConnectionStatus'; protected override readonly mapper = new SimpleWorkflowMapper< TCheckConnectionStatusParams, TCheckConnectionStatusResult diff --git a/src/operations/comment-on-post.ts b/src/operations/comment-on-post.ts index b2dbada..d939ed7 100644 --- a/src/operations/comment-on-post.ts +++ b/src/operations/comment-on-post.ts @@ -1,9 +1,9 @@ -import { PredefinedOperation, TSupportedFunctionName } from '../core'; +import { PredefinedOperation, TOperationName } from '../core'; import { VoidWorkflowMapper } from '../mappers'; import { TCommentOnPostParams } from '../types'; export class CommentOnPost extends PredefinedOperation { - protected override readonly functionName: TSupportedFunctionName = 'commentOnPost'; + protected override readonly functionName: TOperationName = 'commentOnPost'; protected override readonly mapper = new VoidWorkflowMapper( 'st.commentOnPost', ); diff --git a/src/operations/custom-workflow.ts b/src/operations/custom-workflow.ts new file mode 100644 index 0000000..89d3f90 --- /dev/null +++ b/src/operations/custom-workflow.ts @@ -0,0 +1,3 @@ +import { CustomWorkflowOperation } from '../core'; + +export class CustomWorkflow extends CustomWorkflowOperation {} diff --git a/src/operations/fetch-company.ts b/src/operations/fetch-company.ts index e4010cd..7f04942 100644 --- a/src/operations/fetch-company.ts +++ b/src/operations/fetch-company.ts @@ -1,5 +1,4 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { TActionConfig, ThenWorkflowMapper } from '../mappers/then-workflow-mapper.abstract'; import { HttpClient, @@ -12,7 +11,7 @@ export class FetchCompany extends PredefinedOperation< TBaseFetchCompanyParams, TFetchCompanyResult > { - protected override readonly functionName: TSupportedFunctionName = 'fetchCompany'; + protected override readonly functionName: TOperationName = 'fetchCompany'; protected override readonly mapper = new FetchCompanyMapper(); constructor(httpClient: HttpClient) { diff --git a/src/operations/fetch-person.ts b/src/operations/fetch-person.ts index ac488f3..04b5d64 100644 --- a/src/operations/fetch-person.ts +++ b/src/operations/fetch-person.ts @@ -1,10 +1,9 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { TActionConfig, ThenWorkflowMapper } from '../mappers/then-workflow-mapper.abstract'; import { TBaseFetchPersonParams, TFetchPersonParams, TFetchPersonResult } from '../types'; export class FetchPerson extends PredefinedOperation { - protected override readonly functionName: TSupportedFunctionName = 'fetchPerson'; + protected override readonly functionName: TOperationName = 'fetchPerson'; protected override readonly mapper = new FetchPersonMapper(); public override async execute( diff --git a/src/operations/fetch-post.ts b/src/operations/fetch-post.ts index 5c0490f..4cdfb5a 100644 --- a/src/operations/fetch-post.ts +++ b/src/operations/fetch-post.ts @@ -1,10 +1,9 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { SimpleWorkflowMapper } from '../mappers'; import { TFetchPostParams, TFetchPostResult } from '../types'; export class FetchPost extends PredefinedOperation { - protected override readonly functionName: TSupportedFunctionName = 'fetchPost'; + protected override readonly functionName: TOperationName = 'fetchPost'; protected override readonly mapper = new SimpleWorkflowMapper( { actionType: 'st.openPost', diff --git a/src/operations/index.ts b/src/operations/index.ts index 8ec68ab..4d528c4 100644 --- a/src/operations/index.ts +++ b/src/operations/index.ts @@ -1,3 +1,4 @@ +export * from './custom-workflow'; export * from './fetch-person'; export * from './search-companies'; export * from './fetch-company'; diff --git a/src/operations/react-to-post.ts b/src/operations/react-to-post.ts index a24811a..8be95bd 100644 --- a/src/operations/react-to-post.ts +++ b/src/operations/react-to-post.ts @@ -1,8 +1,8 @@ -import { PredefinedOperation, TSupportedFunctionName } from '../core'; +import { PredefinedOperation, TOperationName } from '../core'; import { VoidWorkflowMapper } from '../mappers'; import { TReactToPostParams } from '../types'; export class ReactToPost extends PredefinedOperation { - protected override readonly functionName: TSupportedFunctionName = 'reactToPost'; + protected override readonly functionName: TOperationName = 'reactToPost'; protected override readonly mapper = new VoidWorkflowMapper('st.reactToPost'); } diff --git a/src/operations/remove-connection.ts b/src/operations/remove-connection.ts index 9615d56..628325e 100644 --- a/src/operations/remove-connection.ts +++ b/src/operations/remove-connection.ts @@ -1,9 +1,9 @@ -import { PredefinedOperation, TSupportedFunctionName } from '../core'; +import { PredefinedOperation, TOperationName } from '../core'; import { VoidWorkflowMapper } from '../mappers'; import { TRemoveConnectionParams } from '../types'; export class RemoveConnection extends PredefinedOperation { - protected override readonly functionName: TSupportedFunctionName = 'removeConnection'; + protected override readonly functionName: TOperationName = 'removeConnection'; protected override readonly mapper = new VoidWorkflowMapper( 'st.removeConnection', ); diff --git a/src/operations/retrieve-connections.ts b/src/operations/retrieve-connections.ts index cea4128..28849c1 100644 --- a/src/operations/retrieve-connections.ts +++ b/src/operations/retrieve-connections.ts @@ -1,5 +1,4 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { ArrayWorkflowMapper } from '../mappers/array-workflow-mapper'; import { TRetrieveConnectionsParams, TRetrieveConnectionsResult } from '../types'; @@ -7,7 +6,7 @@ export class RetrieveConnections extends PredefinedOperation< TRetrieveConnectionsParams, TRetrieveConnectionsResult[] > { - protected override readonly functionName: TSupportedFunctionName = 'retrieveConnections'; + protected override readonly functionName: TOperationName = 'retrieveConnections'; protected override readonly mapper = new ArrayWorkflowMapper< TRetrieveConnectionsParams, TRetrieveConnectionsResult diff --git a/src/operations/retrieve-pending-requests.ts b/src/operations/retrieve-pending-requests.ts index 41017aa..079d057 100644 --- a/src/operations/retrieve-pending-requests.ts +++ b/src/operations/retrieve-pending-requests.ts @@ -1,4 +1,4 @@ -import { PredefinedOperation, TSupportedFunctionName } from '../core'; +import { PredefinedOperation, TOperationName } from '../core'; import { ArrayWorkflowMapper } from '../mappers/array-workflow-mapper'; import { TRetrievePendingRequestsResult } from '../types'; @@ -6,7 +6,7 @@ export class RetrievePendingRequests extends PredefinedOperation< void, TRetrievePendingRequestsResult[] > { - protected override readonly functionName: TSupportedFunctionName = 'retrievePendingRequests'; + protected override readonly functionName: TOperationName = 'retrievePendingRequests'; protected override readonly mapper = new ArrayWorkflowMapper< void, TRetrievePendingRequestsResult diff --git a/src/operations/retrieve-performance.ts b/src/operations/retrieve-performance.ts index 523c3ce..7f4da41 100644 --- a/src/operations/retrieve-performance.ts +++ b/src/operations/retrieve-performance.ts @@ -1,10 +1,9 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { SimpleWorkflowMapper } from '../mappers'; import { TRetrievePerformanceResult } from '../types'; export class RetrievePerformance extends PredefinedOperation { - protected override readonly functionName: TSupportedFunctionName = 'retrievePerformance'; + protected override readonly functionName: TOperationName = 'retrievePerformance'; protected override readonly mapper = new SimpleWorkflowMapper({ actionType: 'st.retrievePerformance', }); diff --git a/src/operations/retrieve-ssi.ts b/src/operations/retrieve-ssi.ts index 819beb4..7957a41 100644 --- a/src/operations/retrieve-ssi.ts +++ b/src/operations/retrieve-ssi.ts @@ -1,10 +1,9 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { SimpleWorkflowMapper } from '../mappers'; import { TRetrieveSSIResult } from '../types'; export class RetrieveSSI extends PredefinedOperation { - protected override readonly functionName: TSupportedFunctionName = 'retrieveSSI'; + protected override readonly functionName: TOperationName = 'retrieveSSI'; protected override readonly mapper = new SimpleWorkflowMapper({ actionType: 'st.retrieveSSI', }); diff --git a/src/operations/sales-navigator-fetch-company.ts b/src/operations/sales-navigator-fetch-company.ts index 4d524af..b2e8a99 100644 --- a/src/operations/sales-navigator-fetch-company.ts +++ b/src/operations/sales-navigator-fetch-company.ts @@ -1,5 +1,4 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { TActionConfig, ThenWorkflowMapper } from '../mappers/then-workflow-mapper.abstract'; import { TNvBaseFetchCompanyParams, TNvFetchCompanyParams, TNvFetchCompanyResult } from '../types'; @@ -7,7 +6,7 @@ export class SalesNavigatorFetchCompany extends PredefinedOperation< TNvBaseFetchCompanyParams, TNvFetchCompanyResult > { - protected override readonly functionName: TSupportedFunctionName = 'salesNavigatorFetchCompany'; + protected override readonly functionName: TOperationName = 'salesNavigatorFetchCompany'; protected override readonly mapper = new NvFetchCompanyMapper(); public override async execute( diff --git a/src/operations/sales-navigator-fetch-person.ts b/src/operations/sales-navigator-fetch-person.ts index 6e14785..18dd59c 100644 --- a/src/operations/sales-navigator-fetch-person.ts +++ b/src/operations/sales-navigator-fetch-person.ts @@ -1,5 +1,4 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { ThenWorkflowMapper } from '../mappers'; import { TNvOpenPersonPageParams, TNvOpenPersonPageResult } from '../types'; @@ -7,7 +6,7 @@ export class SalesNavigatorFetchPerson extends PredefinedOperation< TNvOpenPersonPageParams, TNvOpenPersonPageResult > { - protected override readonly functionName: TSupportedFunctionName = 'salesNavigatorFetchPerson'; + protected override readonly functionName: TOperationName = 'salesNavigatorFetchPerson'; protected override readonly mapper = new NvFetchPersonMapper(); } diff --git a/src/operations/sales-navigator-search-companies.ts b/src/operations/sales-navigator-search-companies.ts index 7e06dff..91e110f 100644 --- a/src/operations/sales-navigator-search-companies.ts +++ b/src/operations/sales-navigator-search-companies.ts @@ -1,5 +1,4 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { ArrayWorkflowMapper } from '../mappers/array-workflow-mapper'; import { TNvSearchCompaniesParams, TNvSearchCompanyResult } from '../types'; @@ -7,8 +6,7 @@ export class SalesNavigatorSearchCompanies extends PredefinedOperation< TNvSearchCompaniesParams, TNvSearchCompanyResult[] > { - protected override readonly functionName: TSupportedFunctionName = - 'salesNavigatorSearchCompanies'; + protected override readonly functionName: TOperationName = 'salesNavigatorSearchCompanies'; protected override readonly mapper = new ArrayWorkflowMapper< TNvSearchCompaniesParams, TNvSearchCompanyResult diff --git a/src/operations/sales-navigator-search-people.ts b/src/operations/sales-navigator-search-people.ts index e6b8054..1c46106 100644 --- a/src/operations/sales-navigator-search-people.ts +++ b/src/operations/sales-navigator-search-people.ts @@ -1,5 +1,4 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { ArrayWorkflowMapper } from '../mappers/array-workflow-mapper'; import { TNvSearchPeopleParams, TNvSearchPeopleResult } from '../types'; @@ -7,7 +6,7 @@ export class SalesNavigatorSearchPeople extends PredefinedOperation< TNvSearchPeopleParams, TNvSearchPeopleResult[] > { - protected override readonly functionName: TSupportedFunctionName = 'salesNavigatorSearchPeople'; + protected override readonly functionName: TOperationName = 'salesNavigatorSearchPeople'; protected override readonly mapper = new ArrayWorkflowMapper< TNvSearchPeopleParams, TNvSearchPeopleResult diff --git a/src/operations/sales-navigator-send-message.ts b/src/operations/sales-navigator-send-message.ts index 255a102..ad9a139 100644 --- a/src/operations/sales-navigator-send-message.ts +++ b/src/operations/sales-navigator-send-message.ts @@ -1,10 +1,9 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { VoidWorkflowMapper } from '../mappers/void-workflow-mapper'; import { TNvSendMessageParams } from '../types'; export class SalesNavigatorSendMessage extends PredefinedOperation { - protected override readonly functionName: TSupportedFunctionName = 'salesNavigatorSendMessage'; + protected override readonly functionName: TOperationName = 'salesNavigatorSendMessage'; protected override readonly mapper = new VoidWorkflowMapper( 'nv.sendMessage', ); diff --git a/src/operations/sales-navigator-sync-conversation.ts b/src/operations/sales-navigator-sync-conversation.ts index fe75f7f..e35ae5c 100644 --- a/src/operations/sales-navigator-sync-conversation.ts +++ b/src/operations/sales-navigator-sync-conversation.ts @@ -1,5 +1,4 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { VoidWorkflowMapper } from '../mappers'; import { TNvSyncConversationParams } from '../types'; @@ -7,8 +6,7 @@ export class SalesNavigatorSyncConversation extends PredefinedOperation< TNvSyncConversationParams, void > { - protected override readonly functionName: TSupportedFunctionName = - 'salesNavigatorSyncConversation'; + protected override readonly functionName: TOperationName = 'salesNavigatorSyncConversation'; protected override readonly mapper = new VoidWorkflowMapper( 'nv.syncConversation', ); diff --git a/src/operations/search-companies.ts b/src/operations/search-companies.ts index 3b24cf7..edc4068 100644 --- a/src/operations/search-companies.ts +++ b/src/operations/search-companies.ts @@ -1,5 +1,4 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { ArrayWorkflowMapper } from '../mappers/array-workflow-mapper'; import { TSearchCompaniesParams, TSearchCompanyResult } from '../types'; @@ -7,7 +6,7 @@ export class SearchCompanies extends PredefinedOperation< TSearchCompaniesParams, TSearchCompanyResult[] > { - protected override readonly functionName: TSupportedFunctionName = 'searchCompanies'; + protected override readonly functionName: TOperationName = 'searchCompanies'; protected override readonly mapper = new ArrayWorkflowMapper< TSearchCompaniesParams, TSearchCompanyResult diff --git a/src/operations/search-people.ts b/src/operations/search-people.ts index f1a9453..88a943c 100644 --- a/src/operations/search-people.ts +++ b/src/operations/search-people.ts @@ -1,10 +1,9 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { ArrayWorkflowMapper } from '../mappers/array-workflow-mapper'; import { TSearchPeopleParams, TSearchPeopleResult } from '../types'; export class SearchPeople extends PredefinedOperation { - protected override readonly functionName: TSupportedFunctionName = 'searchPeople'; + protected override readonly functionName: TOperationName = 'searchPeople'; protected override readonly mapper = new ArrayWorkflowMapper< TSearchPeopleParams, TSearchPeopleResult diff --git a/src/operations/send-connection-request.ts b/src/operations/send-connection-request.ts index 0fb6b9c..b2d6eba 100644 --- a/src/operations/send-connection-request.ts +++ b/src/operations/send-connection-request.ts @@ -1,10 +1,9 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { VoidWorkflowMapper } from '../mappers'; import { TSendConnectionRequestParams } from '../types'; export class SendConnectionRequest extends PredefinedOperation { - protected override readonly functionName: TSupportedFunctionName = 'sendConnectionRequest'; + protected override readonly functionName: TOperationName = 'sendConnectionRequest'; protected override readonly mapper = new VoidWorkflowMapper( 'st.sendConnectionRequest', ); diff --git a/src/operations/send-message.ts b/src/operations/send-message.ts index 5d0ed8d..822e981 100644 --- a/src/operations/send-message.ts +++ b/src/operations/send-message.ts @@ -1,9 +1,8 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { VoidWorkflowMapper } from '../mappers/void-workflow-mapper'; import { TSendMessageParams } from '../types'; export class SendMessage extends PredefinedOperation { - protected override readonly functionName: TSupportedFunctionName = 'sendMessage'; + protected override readonly functionName: TOperationName = 'sendMessage'; protected override readonly mapper = new VoidWorkflowMapper('st.sendMessage'); } diff --git a/src/operations/sync-conversation.ts b/src/operations/sync-conversation.ts index 3820613..9b74cf5 100644 --- a/src/operations/sync-conversation.ts +++ b/src/operations/sync-conversation.ts @@ -1,10 +1,9 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { VoidWorkflowMapper } from '../mappers'; import { TSyncConversationParams } from '../types'; export class SyncConversation extends PredefinedOperation { - protected override readonly functionName: TSupportedFunctionName = 'syncConversation'; + protected override readonly functionName: TOperationName = 'syncConversation'; protected override readonly mapper = new VoidWorkflowMapper( 'st.syncConversation', ); diff --git a/src/operations/withdraw-connection-request.ts b/src/operations/withdraw-connection-request.ts index 295303e..3abcf1e 100644 --- a/src/operations/withdraw-connection-request.ts +++ b/src/operations/withdraw-connection-request.ts @@ -1,5 +1,4 @@ -import { TSupportedFunctionName } from '../core'; -import { PredefinedOperation } from '../core/base-operation.abstract'; +import { PredefinedOperation, TOperationName } from '../core'; import { VoidWorkflowMapper } from '../mappers'; import { TWithdrawConnectionRequestParams } from '../types'; @@ -7,7 +6,7 @@ export class WithdrawConnectionRequest extends PredefinedOperation< TWithdrawConnectionRequestParams, void > { - protected override readonly functionName: TSupportedFunctionName = 'withdrawConnectionRequest'; + protected override readonly functionName: TOperationName = 'withdrawConnectionRequest'; protected override readonly mapper = new VoidWorkflowMapper( 'st.withdrawConnectionRequest', ); diff --git a/src/types/errors.ts b/src/types/errors.ts index 049cdc4..42db42b 100644 --- a/src/types/errors.ts +++ b/src/types/errors.ts @@ -1,4 +1,4 @@ -import { TSupportedFunctionName } from '../core/workflow-restoration'; +import { TOperationName } from '../core'; /** * This error is exposed when a workflow action fails to complete. @@ -87,9 +87,9 @@ export class LinkedApiError extends Error { */ export class LinkedApiWorkflowTimeoutError extends LinkedApiError { public readonly workflowId: string; - public readonly functionName: TSupportedFunctionName; + public readonly functionName: TOperationName; - constructor(workflowId: string, functionName: TSupportedFunctionName) { + constructor(workflowId: string, functionName: TOperationName) { super( 'workflowTimeout', `Workflow ${workflowId} timed out. Use restoreWorkflow(${workflowId}, ${functionName}) to restore the workflow.`, diff --git a/src/types/workflows.ts b/src/types/workflows.ts index 3be1c9f..a1745d3 100644 --- a/src/types/workflows.ts +++ b/src/types/workflows.ts @@ -28,9 +28,13 @@ export interface TWorkflowFailure { message: string; } -export interface TWorkflowResponse { +export interface TWorkflowStatusResponse { workflowId: string; workflowStatus: TWorkflowStatus; +} + +export interface TWorkflowResponse + extends TWorkflowStatusResponse { completion?: TWorkflowCompletion; failure?: TWorkflowFailure; } From c12c1efa93458fa841805bb94b0947d98ec9daaf Mon Sep 17 00:00:00 2001 From: Kiril Ivonchik Date: Fri, 22 Aug 2025 14:08:08 +0200 Subject: [PATCH 09/10] Renames --- README.md | 336 ++++++++---------- src/core/operation.ts | 22 +- src/core/poll-results.ts | 14 +- src/index.ts | 101 ++---- src/operations/check-connection-status.ts | 2 +- src/operations/comment-on-post.ts | 2 +- src/operations/fetch-company.ts | 2 +- src/operations/fetch-person.ts | 2 +- src/operations/fetch-post.ts | 2 +- src/operations/react-to-post.ts | 2 +- src/operations/remove-connection.ts | 2 +- src/operations/retrieve-connections.ts | 2 +- src/operations/retrieve-pending-requests.ts | 2 +- src/operations/retrieve-performance.ts | 2 +- src/operations/retrieve-ssi.ts | 2 +- .../sales-navigator-fetch-company.ts | 2 +- .../sales-navigator-fetch-person.ts | 2 +- .../sales-navigator-search-companies.ts | 2 +- .../sales-navigator-search-people.ts | 2 +- .../sales-navigator-send-message.ts | 2 +- .../sales-navigator-sync-conversation.ts | 2 +- src/operations/search-companies.ts | 2 +- src/operations/search-people.ts | 2 +- src/operations/send-connection-request.ts | 2 +- src/operations/send-message.ts | 2 +- src/operations/sync-conversation.ts | 2 +- src/operations/withdraw-connection-request.ts | 2 +- src/types/errors.ts | 12 +- src/types/workflows.ts | 4 +- 29 files changed, 230 insertions(+), 305 deletions(-) diff --git a/README.md b/README.md index 6446267..58d0924 100644 --- a/README.md +++ b/README.md @@ -32,39 +32,42 @@ const linkedapi = new LinkedApi({ }); // Use Linked API for your LinkedIn account automation -const connectionWorkflow = await linkedapi.sendConnectionRequest({ +const connectionWorkflowId = await linkedapi.sendConnectionRequest.execute({ personUrl: "https://www.linkedin.com/in/person1", message: "It would be great to add you to my network!", }); -await connectionWorkflow.result(); +await linkedapi.sendConnectionRequest.result(connectionWorkflowId); -const commentWorkflow = await linkedapi.commentOnPost({ +const commentWorkflowId = await linkedapi.commentOnPost.execute({ postUrl: "https://www.linkedin.com/posts/post1", text: "Great post! Thanks for sharing.", }); -await commentWorkflow.result(); +await linkedapi.commentOnPost.result(commentWorkflowId); // Search companies -const searchCompaniesWorkflow = await linkedapi.searchCompanies({ +const searchWorkflowId = await linkedapi.searchCompanies.execute({ + term: "software development", // Search term for company name or description filter: { - sizes: ["11-50", "51-200", "201-500", "501-1000"], - locations: ["California", "Wyoming", "Texas"], - industries: ["Software Development", "Robotics Engineering"], + locations: ["San Francisco", "New York", "Seattle"], + industries: ["Technology", "Software", "Artificial Intelligence"], + sizes: ["51-200", "201-500", "501-1000"], }, + limit: 50, // Maximum number of results to return (1-100, default: 10) }); -const companiesResult = await searchCompaniesWorkflow.result(); +const companiesResult = + await linkedapi.searchCompanies.result(searchWorkflowId); // Retrieving company basic info, employees -const companyWorkflow = await linkedapi.fetchCompany({ - companyUrl: "https://www.linkedin.com/company/company1", - retrieveEmployees: true, +const companyWorkflowId = await linkedapi.fetchCompany.execute({ + companyUrl: "https://www.linkedin.com/company/microsoft", + retrieveEmployees: true, // Get company employees with their profiles employeesRetrievalConfig: { filter: { schools: ["Harvard University", "Stanford University"], }, }, }); -const companyResult = await companyWorkflow.result(); +const companyResult = await linkedapi.fetchCompany.result(companyWorkflowId); ``` --- @@ -92,63 +95,34 @@ You can obtain these tokens through [Linked API Platform](https://app.linkedapi. --- -### `executeCustomWorkflow(params)` +### `customWorkflow` Execute custom LinkedIn automation workflows with raw workflow definitions. - **Parameters:** `TWorkflowDefinition` - Custom workflow definition -- **Returns:** `Promise` - Workflow handler for result management +- **Result:** `TWorkflowCompletion` - Workflow completion object - **Documentation:** [Building Workflows](https://linkedapi.io/docs/building-workflows/) | [Executing Workflows](https://linkedapi.io/docs/executing-workflows/) | [Actions Overview](https://linkedapi.io/docs/actions-overview/) ```typescript -const workflow = await linkedapi.executeCustomWorkflow({ +const workflowId = await linkedapi.customWorkflow.execute({ actionType: "st.searchCompanies", term: "Tech Inc", then: { actionType: "st.doForCompanies", ... } }); -const result = await workflow.result(); -``` - ---- - -### `restoreWorkflow(workflowId, functionName)` - -Restore a WorkflowHandler for a previously started workflow using its ID and function name with type safety. - -- **Parameters:** - - `workflowId: string` - The unique identifier of the workflow to restore - - `functionName: TOperationName` - The name of the function that was used to create the workflow -- **Returns:** `Promise>>` - WorkflowHandler with exact result type based on the function name -- **Type Safety:** Full TypeScript inference for exact return types based on the function name -- **Documentation:** [Executing Workflows](https://linkedapi.io/docs/executing-workflows/) - -```typescript -// Restore a person fetching workflow with full type safety -const personHandler = await linkedapi.restoreWorkflow( - "workflow-id-123", - "fetchPerson", -); -const personResult = await personHandler.result(); - -// Restore a company fetching workflow -const companyHandler = await linkedapi.restoreWorkflow( - "workflow-id-456", - "fetchCompany", -); -const companyResult = await companyHandler.result(); +const result = await linkedapi.customWorkflow.result(workflowId); ``` --- -### `fetchPerson(params)` +### `fetchPerson` Retrieve comprehensive LinkedIn person profile data including experience, education, skills, and posts. - **Parameters:** `TBaseFetchPersonParams` - Person URL and data retrieval options -- **Returns:** `Promise>` - Person profile data +- **Result:** `TMappedResponse` - Person profile data ```typescript -const personWorkflow = await linkedapi.fetchPerson({ +const personWorkflowId = await linkedapi.fetchPerson.execute({ personUrl: "https://www.linkedin.com/in/john-doe", retrieveExperience: true, // Get work experience and job history retrieveEducation: true, // Get educational background and degrees @@ -170,36 +144,37 @@ const personWorkflow = await linkedapi.fetchPerson({ since: "2024-01-01", // Retrieve reactions since this date }, }); -const personResult = await personWorkflow.result(); +const personResult = await linkedapi.fetchPerson.result(personWorkflowId); ``` --- -### `salesNavigatorFetchPerson(params)` +### `salesNavigatorFetchPerson` Retrieve person data through Sales Navigator for enhanced prospecting capabilities. - **Parameters:** `TNvOpenPersonPageParams` - Sales Navigator person parameters -- **Returns:** `Promise>` - Enhanced person data +- **Result:** `TMappedResponse` - Person data from Sales Navigator ```typescript -const nvPersonWorkflow = await linkedapi.salesNavigatorFetchPerson({ +const nvPersonWorkflowId = await linkedapi.salesNavigatorFetchPerson.execute({ personHashedUrl: "https://www.linkedin.com/in/ABC123", }); -const personResult = await nvPersonWorkflow.result(); +const personResult = + await linkedapi.salesNavigatorFetchPerson.result(nvPersonWorkflowId); ``` --- -### `fetchCompany(params)` +### `fetchCompany` Retrieve detailed LinkedIn company profile data including employees, posts, and direct messages. - **Parameters:** `TBaseFetchCompanyParams` - Company URL and data retrieval options -- **Returns:** `Promise>` - Company profile data +- **Result:** `TMappedResponse` - Company profile data ```typescript -const companyWorkflow = await linkedapi.fetchCompany({ +const companyWorkflowId = await linkedapi.fetchCompany.execute({ companyUrl: "https://www.linkedin.com/company/microsoft", retrieveEmployees: true, // Get company employees with their profiles retrievePosts: true, // Get recent company posts and updates @@ -225,20 +200,20 @@ const companyWorkflow = await linkedapi.fetchCompany({ limit: 5, // Maximum number of decision makers to retrieve }, }); -const companyResult = await companyWorkflow.result(); +const companyResult = await linkedapi.fetchCompany.result(companyWorkflowId); ``` --- -### `salesNavigatorFetchCompany(params)` +### `salesNavigatorFetchCompany` Retrieve company data through Sales Navigator with advanced filtering and prospecting features. - **Parameters:** `TNvBaseFetchCompanyParams` - Sales Navigator company parameters -- **Returns:** `Promise>` - Enhanced company data +- **Result:** `TMappedResponse` - Company data from Sales Navigator ```typescript -const nvCompanyWorkflow = await linkedapi.salesNavigatorFetchCompany({ +const nvCompanyWorkflowId = await linkedapi.salesNavigatorFetchCompany.execute({ companyHashedUrl: "https://www.linkedin.com/sales/company/1035", retrieveEmployees: true, // Get company employees with Sales Navigator retrieveDMs: true, // Get decision makers @@ -258,36 +233,37 @@ const nvCompanyWorkflow = await linkedapi.salesNavigatorFetchCompany({ limit: 10, // Maximum number of decision makers to retrieve (1-20) }, }); -const companyResult = await nvCompanyWorkflow.result(); +const companyResult = + await linkedapi.salesNavigatorFetchCompany.result(nvCompanyWorkflowId); ``` --- -### `fetchPost(params)` +### `fetchPost` Retrieve detailed information about a LinkedIn post including content, engagement, and comments. - **Parameters:** `TFetchPostParams` - Post URL -- **Returns:** `Promise>` - Post data and metrics +- **Result:** `TMappedResponse` - Post data ```typescript -const postWorkflow = await linkedapi.fetchPost({ +const postWorkflowId = await linkedapi.fetchPost.execute({ postUrl: "https://www.linkedin.com/posts/john-doe_activity-123456789", }); -const postResult = await postWorkflow.result(); +const postResult = await linkedapi.fetchPost.result(postWorkflowId); ``` --- -### `searchCompanies(params)` +### `searchCompanies` Search for companies on LinkedIn using standard search with advanced filtering options. - **Parameters:** `TSearchCompanyParams` - Search term, filters, and pagination -- **Returns:** `Promise>` - Array of company search results +- **Result:** `TMappedResponse` - Array of company search results ```typescript -const companySearchWorkflow = await linkedapi.searchCompanies({ +const companySearchWorkflowId = await linkedapi.searchCompanies.execute({ term: "software development", // Search term/keywords for company name or description filter: { locations: ["San Francisco", "New York", "Seattle"], @@ -296,46 +272,51 @@ const companySearchWorkflow = await linkedapi.searchCompanies({ }, limit: 50, // Maximum number of results to return (1-100, default: 10) }); -const companiesResult = await companySearchWorkflow.result(); +const companiesResult = await linkedapi.searchCompanies.result( + companySearchWorkflowId, +); ``` --- -### `salesNavigatorSearchCompanies(params)` +### `salesNavigatorSearchCompanies` Search for companies using Sales Navigator with advanced prospecting filters. - **Parameters:** `TNvSearchCompanyParams` - Enhanced search parameters for Sales Navigator -- **Returns:** `Promise>` - Enhanced company search results +- **Result:** `TMappedResponse` - Company search results from Sales Navigator ```typescript -const nvCompanySearch = await linkedapi.salesNavigatorSearchCompanies({ - term: "enterprise software", // Search term for company name, description, or keywords - filter: { - locations: ["San Francisco", "New York", "London"], - industries: ["Software Development", "Enterprise Software", "SaaS"], - sizes: ["201-500", "501-1000", "1001-5000"], - annualRevenue: { - min: "10", // Minimum annual revenue in millions USD - max: "500", // Maximum annual revenue in millions USD +const nvCompanySearchId = await linkedapi.salesNavigatorSearchCompanies.execute( + { + term: "enterprise software", // Search term for company name, description, or keywords + filter: { + locations: ["San Francisco", "New York", "London"], + industries: ["Software Development", "Enterprise Software", "SaaS"], + sizes: ["201-500", "501-1000", "1001-5000"], + annualRevenue: { + min: "10", // Minimum annual revenue in millions USD + max: "500", // Maximum annual revenue in millions USD + }, }, + limit: 75, // Maximum number of results to return (1-100) }, - limit: 75, // Maximum number of results to return (1-100) -}); -const companiesResult = await nvCompanySearch.result(); +); +const companiesResult = + await linkedapi.salesNavigatorSearchCompanies.result(nvCompanySearchId); ``` --- -### `searchPeople(params)` +### `searchPeople` Search for people on LinkedIn using standard search with location, experience, and company filters. - **Parameters:** `TSearchPeopleParams` - Search term, filters, and pagination -- **Returns:** `Promise>` - Array of people search results +- **Result:** `TMappedResponse` - Array of people search results ```typescript -const peopleSearchWorkflow = await linkedapi.searchPeople({ +const peopleSearchWorkflowId = await linkedapi.searchPeople.execute({ term: "product manager", // Search term filter: { firstName: "John", @@ -349,20 +330,22 @@ const peopleSearchWorkflow = await linkedapi.searchPeople({ }, limit: 20, // Maximum number of results to return (1-100, default: 10) }); -const peopleResult = await peopleSearchWorkflow.result(); +const peopleResult = await linkedapi.searchPeople.result( + peopleSearchWorkflowId, +); ``` --- -### `salesNavigatorSearchPeople(params)` +### `salesNavigatorSearchPeople` Search for people using Sales Navigator with advanced prospecting and lead generation filters. - **Parameters:** `TNvSearchPeopleParams` - Enhanced search parameters for Sales Navigator -- **Returns:** `Promise>` - Enhanced people search results +- **Result:** `TMappedResponse` - People search results from Sales Navigator ```typescript -const nvPeopleSearch = await linkedapi.salesNavigatorSearchPeople({ +const nvPeopleSearchId = await linkedapi.salesNavigatorSearchPeople.execute({ term: "VP Engineering", limit: 20, // Maximum number of results to return (1-100, default: 10) filter: { @@ -377,82 +360,87 @@ const nvPeopleSearch = await linkedapi.salesNavigatorSearchPeople({ yearsOfExperience: ["lessThanOne", "oneToTwo", "threeToFive"], }, }); -const prospectsResult = await nvPeopleSearch.result(); +const prospectsResult = + await linkedapi.salesNavigatorSearchPeople.result(nvPeopleSearchId); ``` --- -### `sendConnectionRequest(params)` +### `sendConnectionRequest` Send connection requests to LinkedIn users with optional personalized messages. - **Parameters:** `TSendConnectionRequestParams` - Person URL and optional message -- **Returns:** `Promise>` - Workflow handler (void) +- **Result:** `TMappedResponse` - Connection request sent successfully ```typescript -await linkedapi.sendConnectionRequest({ +const workflowId = await linkedapi.sendConnectionRequest.execute({ personUrl: "https://www.linkedin.com/in/john-doe", note: "Hello! I'd love to connect and discuss opportunities.", }); +await linkedapi.sendConnectionRequest.result(workflowId); ``` --- -### `checkConnectionStatus(params)` +### `checkConnectionStatus` Check the current connection status with a LinkedIn user. - **Parameters:** `TCheckConnectionStatusParams` - Person URL -- **Returns:** `Promise>` - Connection status information +- **Result:** `TMappedResponse` - Connection status information ```typescript -const statusWorkflow = await linkedapi.checkConnectionStatus({ +const statusWorkflowId = await linkedapi.checkConnectionStatus.execute({ personUrl: "https://www.linkedin.com/in/john-doe", }); -const statusResult = await statusWorkflow.result(); +const statusResult = + await linkedapi.checkConnectionStatus.result(statusWorkflowId); ``` --- -### `withdrawConnectionRequest(params)` +### `withdrawConnectionRequest` Withdraw previously sent connection requests. - **Parameters:** `TWithdrawConnectionRequestParams` - Person URL -- **Returns:** `Promise>` - Workflow handler (void) +- **Result:** `TMappedResponse` - Connection request withdrawn successfully ```typescript -await linkedapi.withdrawConnectionRequest({ +const workflowId = await linkedapi.withdrawConnectionRequest.execute({ personUrl: "https://www.linkedin.com/in/john-doe", }); +await linkedapi.withdrawConnectionRequest.result(workflowId); ``` --- -### `retrievePendingRequests()` +### `retrievePendingRequests` Retrieve all pending connection requests sent by your account. - **Parameters:** None -- **Returns:** `Promise>` - List of pending requests +- **Result:** `TMappedResponse` - List of pending connection requests ```typescript -const pendingWorkflow = await linkedapi.retrievePendingRequests(); -const pendingResult = await pendingWorkflow.result(); +const pendingWorkflowId = await linkedapi.retrievePendingRequests.execute(); +const pendingResult = + await linkedapi.retrievePendingRequests.result(pendingWorkflowId); console.log("Pending requests:", pendingResult.data.length); ``` --- -### `retrieveConnections(params)` +### `retrieveConnections` Retrieve existing connections with advanced filtering options. - **Parameters:** `TRetrieveConnectionsParams` - Filter and pagination options -- **Returns:** `Promise>` - List of connections +- **Result:** `TMappedResponse` - List of connections ```typescript -const connectionsWorkflow = await linkedapi.retrieveConnections({ +const connectionsWorkflowId = await linkedapi.retrieveConnections.execute({ filter: { firstName: "John", positions: ["Engineer", "Manager"], @@ -460,86 +448,94 @@ const connectionsWorkflow = await linkedapi.retrieveConnections({ }, limit: 100, }); +const connectionsResult = await linkedapi.retrieveConnections.result( + connectionsWorkflowId, +); ``` --- -### `removeConnection(params)` +### `removeConnection` Remove existing connections from your LinkedIn network. - **Parameters:** `TRemoveConnectionParams` - Person URL -- **Returns:** `Promise>` - Workflow handler (void) +- **Result:** `TMappedResponse` - Connection removed successfully ```typescript -await linkedapi.removeConnection({ +const workflowId = await linkedapi.removeConnection.execute({ personUrl: "https://www.linkedin.com/in/former-colleague", }); +await linkedapi.removeConnection.result(workflowId); ``` --- -### `reactToPost(params)` +### `reactToPost` React to LinkedIn posts with various reaction types (like, love, support, etc.). - **Parameters:** `TReactToPostParams` - Post URL and reaction type -- **Returns:** `Promise>` - Workflow handler (void) +- **Result:** `TMappedResponse` - Reaction applied successfully ```typescript -await linkedapi.reactToPost({ +const workflowId = await linkedapi.reactToPost.execute({ postUrl: "https://www.linkedin.com/posts/john-doe_activity-123456789", reactionType: "like", }); +await linkedapi.reactToPost.result(workflowId); ``` --- -### `commentOnPost(params)` +### `commentOnPost` Comment on LinkedIn posts to engage with your network. - **Parameters:** `TCommentOnPostParams` - Post URL and comment text -- **Returns:** `Promise>` - Workflow handler (void) +- **Result:** `TMappedResponse` - Comment posted successfully ```typescript -await linkedapi.commentOnPost({ +const workflowId = await linkedapi.commentOnPost.execute({ postUrl: "https://www.linkedin.com/posts/john-doe_activity-123456789", text: "Great insight! Thanks for sharing.", }); +await linkedapi.commentOnPost.result(workflowId); ``` --- -### `retrieveSSI()` +### `retrieveSSI` Retrieve your LinkedIn Social Selling Index (SSI) score and rankings. - **Parameters:** None -- **Returns:** `Promise>` - SSI score and industry/network rankings +- **Result:** `TMappedResponse` - SSI score and industry/network rankings ```typescript -const ssiWorkflow = await linkedapi.retrieveSSI(); -const ssiResult = await ssiWorkflow.result(); +const ssiWorkflowId = await linkedapi.retrieveSSI.execute(); +const ssiResult = await linkedapi.retrieveSSI.result(ssiWorkflowId); ``` --- -### `retrievePerformance()` +### `retrievePerformance` Retrieve LinkedIn account performance metrics including profile views and post engagement. - **Parameters:** None -- **Returns:** `Promise>` - Performance metrics +- **Result:** `TMappedResponse` - Performance metrics and analytics ```typescript -const performanceWorkflow = await linkedapi.retrievePerformance(); -const performanceResult = await performanceWorkflow.result(); +const performanceWorkflowId = await linkedapi.retrievePerformance.execute(); +const performanceResult = await linkedapi.retrievePerformance.result( + performanceWorkflowId, +); ``` --- -### `getApiUsageStats(params)` +### `getApiUsageStats` Retrieve Linked API usage statistics for monitoring and optimization. @@ -568,66 +564,70 @@ if (statsResponse.success) { --- -### `sendMessage(params)` +### `sendMessage` Send messages to LinkedIn users through standard LinkedIn messaging. - **Parameters:** `TSendMessageParams` - Person URL and message text -- **Returns:** `Promise>` - Workflow handler (void) +- **Result:** `TMappedResponse` - Message sent successfully ```typescript -await linkedapi.sendMessage({ +const workflowId = await linkedapi.sendMessage.execute({ personUrl: "https://www.linkedin.com/in/john-doe", text: "Hello! I saw your post about AI and wanted to connect.", }); +await linkedapi.sendMessage.result(workflowId); ``` --- -### `syncConversation(params)` +### `syncConversation` Sync conversation history with a LinkedIn user for message polling. - **Parameters:** `TSyncConversationParams` - Person URL -- **Returns:** `Promise>` - Workflow handler (void) +- **Result:** `TMappedResponse` - Conversation synced successfully - **Related Methods:** Use with `pollConversations()` to retrieve message history ```typescript -await linkedapi.syncConversation({ +const workflowId = await linkedapi.syncConversation.execute({ personUrl: "https://www.linkedin.com/in/john-doe", }); +await linkedapi.syncConversation.result(workflowId); ``` --- -### `salesNavigatorSendMessage(params)` +### `salesNavigatorSendMessage` Send messages through Sales Navigator with enhanced messaging capabilities. - **Parameters:** `TNvSendMessageParams` - Person URL, message text, and optional subject -- **Returns:** `Promise>` - Workflow handler (void) +- **Result:** `TMappedResponse` - Sales Navigator message sent successfully ```typescript -await linkedapi.salesNavigatorSendMessage({ +const workflowId = await linkedapi.salesNavigatorSendMessage.execute({ personUrl: "https://www.linkedin.com/sales/people/ABC123", subject: "Partnership Opportunity", text: "Hi! I'd love to discuss potential collaboration opportunities.", }); +await linkedapi.salesNavigatorSendMessage.result(workflowId); ``` --- -### `salesNavigatorSyncConversation(params)` +### `salesNavigatorSyncConversation` Sync Sales Navigator conversation for message polling. - **Parameters:** `TNvSyncConversationParams` - Person URL -- **Returns:** `Promise>` - Workflow handler (void) +- **Result:** `TMappedResponse` - Sales Navigator conversation synced successfully ```typescript -await linkedapi.salesNavigatorSyncConversation({ +const workflowId = await linkedapi.salesNavigatorSyncConversation.execute({ personUrl: "https://www.linkedin.com/sales/people/ABC123", }); +await linkedapi.salesNavigatorSyncConversation.result(workflowId); ``` --- @@ -676,7 +676,7 @@ For complex multi-step workflows involving multiple actions, it's recommended to ```typescript // āœ… Recommended: Custom workflow for complex operations -const customWorkflow = await linkedapi.executeCustomWorkflow({ +const customWorkflowId = await linkedapi.customWorkflow.execute({ actionType: "st.searchCompanies", term: "AI startup", filter: { locations: ["San Francisco"], sizes: ["11-50", "51-200"] }, @@ -698,8 +698,8 @@ const customWorkflow = await linkedapi.executeCustomWorkflow({ note: "Hi! I'd love to connect.", email: "example@example.com" }, + }, }, - } }, }, }); @@ -707,15 +707,15 @@ const customWorkflow = await linkedapi.executeCustomWorkflow({ // vs. āŒ Less efficient: Multiple separate API calls // (Multiple network requests, no atomicity, complex error handling) try { - const searchCompaniesWorkflow = await linkedapi.searchCompanies({ + const searchCompaniesWorkflowId = await linkedapi.searchCompanies.execute({ term: "AI startup", filter: { locations: ["San Francisco"], sizes: ["11-50", "51-200"] }, limit: 10, }) - const companies = await searchCompaniesWorkflow.result(); + const companies = (await linkedapi.searchCompanies.result(searchCompaniesWorkflowId)).data!; for (company of companies) { try { - const companyEmployeesWorkflow = await linkedapi.fetchCompany({ + const companyEmployeesWorkflowId = await linkedapi.fetchCompany.execute({ retrieveEmployees: true, employeesRetrievalConfig: { limit: 5, @@ -724,15 +724,15 @@ try { } } }); - const employees = await companyEmployeesWorkflow.result(); + const employees = (await linkedapi.fetchCompany.result(companyEmployeesWorkflowId)).data!; for (employee of employees) { try { - const sendConnectionWorkflow = await linkedapi.sendConnectionRequest({ + const sendConnectionWorkflowId = await linkedapi.sendConnectionRequest.execute({ personUrl: employee.publicUrl, note: "Hi! I'd love to connect.", email: "example@example.com" }); - await sendConnectionWorflow.result(); + await linkedapi.sendConnectionRequest.result(sendConnectionWorkflowId); } catch (sendConnectionError) { console.error(sendConnectionError); } @@ -752,53 +752,27 @@ try { Linked API workflows are designed to be resilient and stateful. Each workflow receives a unique `workflowId` that can be used to retrieve results even if your application restarts or loses connection during execution. -#### Saving Workflow IDs for Later Retrieval - When you start a workflow, you can access its ID immediately and store it for later use: ```typescript // Start a workflow and save the ID -const personWorkflow = await linkedapi.fetchPerson({ +const personWorkflowId = await linkedapi.fetchPerson.execute({ personUrl: "https://www.linkedin.com/in/john-doe", retrieveExperience: true, }); // Save the workflow ID to your database or persistent storage -const workflowId = personWorkflow.workflowId; -console.log("Workflow started with ID:", workflowId); +console.log("Workflow started with ID:", personWorkflowId); // Store in database/file/memory for later retrieval -await saveWorkflowToDatabase(workflowId, "fetchPerson"); +await saveWorkflowToDatabase(personWorkflowId); try { - const person = await personWorkflow.result(); + const person = await linkedapi.fetchPerson.result(personWorkflowId); } finally { - await deleteWorkflowFromDatabase(workflowId); + await deleteWorkflowFromDatabase(personWorkflowId); } ``` -#### Retrieving Results After App Restart - -If your application restarts or you need to check workflow status later, you can: - -- Restore a `WorkflowHandler` using `restoreWorkflow(workflowId, functionName)` with full type safety - -```typescript -// 1) Raw handler -const rawHandler = await linkedapi.restoreWorkflow(savedWorkflowId); - -// 2) Streamlined restoration with function name (wide result type with full type safety) -const handler = await linkedapi.restoreWorkflow(savedWorkflowId, "fetchPerson"); -const result = await handler.result(); -if (result.data) { - console.log("Person name:", result.data.name); - console.log("Experience count:", result.data.experiences?.length); -} -``` - -See `examples/restore-workflow.ts` for a full example. - ---- - ## 🚨 Error Handling Linked API provides structured error handling for different failure scenarios. There are two types of errors to handle: @@ -806,7 +780,6 @@ Linked API provides structured error handling for different failure scenarios. T ### 1. Exceptions (try/catch) - **`LinkedApiError`** - throws if a [common error](https://linkedapi.io/docs/making-requests/#common-errors) occurs -- **`LinkedApiWorkflowTimeoutError`** - throws in case of timeout. Contains `workflowId` and `functionName` for future restoration ### 2. Action Errors (errors array) @@ -817,10 +790,10 @@ Linked API provides structured error handling for different failure scenarios. T import LinkedApi, { LinkedApiError } from "linkedapi-node"; try { - const workflow = await linkedapi.fetchPerson({ + const workflowId = await linkedapi.fetchPerson.execute({ personUrl: "https://www.linkedin.com/in/invalid-profile", }); - const result = await workflow.result(); + const result = await linkedapi.fetchPerson.result(workflowId); // Check for partial errors in the response if (result.errors && result.errors.length > 0) { @@ -859,7 +832,6 @@ try { - **`plusPlanRequired`** - Some actions in this workflow require the Plus plan. - **`linkedinAccountSignedOut`** - Your LinkedIn account has been signed out in our cloud browser. This occasionally happens as LinkedIn may sign out accounts after an extended period. You'll need to visit our platform and reconnect your account. - **`languageNotSupported`** - Your LinkedIn account uses a language other than English, which is currently the only supported option. -- **`timeout`** - Local execution timeout. Contains `workflowId` and `functionName` for future restoration. ### Common Action Error Types diff --git a/src/core/operation.ts b/src/core/operation.ts index 7e71251..7fc7699 100644 --- a/src/core/operation.ts +++ b/src/core/operation.ts @@ -7,7 +7,7 @@ import { TWorkflowCompletion, TWorkflowDefinition, TWorkflowResponse, - TWorkflowStatus, + TWorkflowRunningStatus, } from '../types'; import { pollWorkflowResult } from './poll-results'; @@ -46,7 +46,7 @@ export interface WaitForCompletionOptions { } export abstract class PredefinedOperation { - protected abstract readonly functionName: TOperationName; + protected abstract readonly operationName: TOperationName; protected abstract readonly mapper: BaseMapper; private readonly operation: CustomWorkflowOperation; @@ -69,20 +69,20 @@ export abstract class PredefinedOperation { return this.mapper.mapResponse(rawResult); } catch (error) { if (error instanceof LinkedApiError && error.type === 'workflowTimeout') { - throw new LinkedApiWorkflowTimeoutError(workflowId, this.functionName); + throw new LinkedApiWorkflowTimeoutError(workflowId, this.operationName); } throw error; } } - public async immediateResult( + public async status( workflowId: string, - ): Promise> { - const result = await this.operation.immediateResult(workflowId); + ): Promise> { + const result = await this.operation.status(workflowId); if (result === 'running') { return result; } - return this.mapper.mapResponse(result as TWorkflowCompletion); + return this.mapper.mapResponse(result); } } @@ -105,11 +105,7 @@ export class CustomWorkflowOperation { options: WaitForCompletionOptions = {}, ): Promise { try { - return pollWorkflowResult( - workflowId, - (workflowId) => this.immediateResult(workflowId), - options, - ); + return pollWorkflowResult(() => this.status(workflowId), options); } catch (error) { if (error instanceof LinkedApiError && error.type === 'workflowTimeout') { throw new LinkedApiWorkflowTimeoutError(workflowId, 'customWorkflow'); @@ -118,7 +114,7 @@ export class CustomWorkflowOperation { } } - public async immediateResult(workflowId: string): Promise { + public async status(workflowId: string): Promise { const workflowResult = await this.getWorkflowResult(workflowId); if (workflowResult.workflowStatus === 'running') { return workflowResult.workflowStatus; diff --git a/src/core/poll-results.ts b/src/core/poll-results.ts index 13381d4..0c81e31 100644 --- a/src/core/poll-results.ts +++ b/src/core/poll-results.ts @@ -1,10 +1,9 @@ -import { LinkedApiError, TWorkflowCompletion, TWorkflowStatus } from '../types'; +import { LinkedApiError, TWorkflowCompletion, TWorkflowRunningStatus } from '../types'; import { WaitForCompletionOptions } from './operation'; export async function pollWorkflowResult( - workflowId: string, - workflowResultFn: (workflowId: string) => Promise, + workflowResultFn: () => Promise, options: WaitForCompletionOptions, ): Promise { const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); @@ -13,16 +12,13 @@ export async function pollWorkflowResult( const startTime = Date.now(); while (Date.now() - startTime < timeout) { - const result = await workflowResultFn(workflowId); + const result = await workflowResultFn(); if (result !== 'running') { - return result as TWorkflowCompletion; + return result; } await sleep(pollInterval); } - throw new LinkedApiError( - 'workflowTimeout', - `Workflow ${workflowId} did not complete within ${timeout}ms`, - ); + throw new LinkedApiError('workflowTimeout', `Workflow did not complete within ${timeout}ms`); } diff --git a/src/index.ts b/src/index.ts index 843903d..557689b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -76,8 +76,12 @@ class LinkedApi { * @param config - Configuration object containing API tokens and optional settings * @returns LinkedApi instance with access to LinkedIn automation features */ - constructor(config: TLinkedApiConfig) { - this.httpClient = buildLinkedApiHttpClient(config); + public constructor(config: TLinkedApiConfig | HttpClient) { + if (config instanceof HttpClient) { + this.httpClient = config; + } else { + this.httpClient = buildLinkedApiHttpClient(config); + } this.customWorkflow = new CustomWorkflow(this.httpClient); this.fetchPerson = new FetchPerson(this.httpClient); @@ -112,7 +116,7 @@ class LinkedApi { * Use this for advanced use cases when you need to create custom action sequences. * * @param params - The workflow definition containing action types and parameters - * @returns Promise resolving to a WorkflowHandler for managing the workflow execution + * @returns Promise resolving to a WorkflowCompletion for managing the workflow execution * * @see {@link https://linkedapi.io/docs/ Linked API Documentation} * @see {@link https://linkedapi.io/docs/executing-workflows/ Executing Workflows Documentation} @@ -147,51 +151,6 @@ class LinkedApi { */ public customWorkflow: CustomWorkflow; - /** - * Restore a WorkflowHandler for a previously started workflow using its ID and function name. - * This provides full type safety and exact result types based on the function name. - * - * @param workflowId - The unique identifier of the workflow to restore - * @param functionName - The name of the function that was used to create the workflow - * @returns Promise resolving to a WorkflowHandler with exact result type based on the function name - * - * @see {@link https://linkedapi.io/docs/executing-workflows/ Executing Workflows Documentation} - * - * @example - * ```typescript - * // Restore a person fetching workflow with full type safety - * const personHandler = await linkedapi.restoreWorkflow("workflow-id-123", "fetchPerson"); - * const personResult = await personHandler.result(); - * - * const statusHandler = await linkedapi.restoreWorkflow("workflow-id-456", "checkConnectionStatus"); - * const statusResult = await statusHandler.result(); - * // TypeScript knows exact type: TCheckConnectionStatusResult - * ``` - */ - // public async restoreWorkflow( - // workflowId: string, - // functionName: TFunctionName, - // ): Promise>> { - // const mapper = createMapperFromFunctionName(functionName); - - // if (mapper === null) { - // return new WorkflowHandler( - // workflowId, - // functionName, - // this.workflowExecutor, - // ) as WorkflowHandler>; - // } - - // return new WorkflowHandler( - // workflowId, - // functionName, - // this.workflowExecutor, - // mapper as BaseMapper, - // ) as WorkflowHandler>; - // } - - // Mapper descriptor based restoration was removed in favor of restoreMapper(functionName, parameters) - /** * Send a message to a LinkedIn user via standard LinkedIn messaging. * @@ -199,7 +158,7 @@ class LinkedApi { * or allow messages from anyone. This uses the standard LinkedIn messaging interface. * * @param params - Parameters including the person's URL and message text - * @returns Promise resolving to a WorkflowHandler for the message sending action + * @returns Promise resolving to the message sending action * * @see {@link https://linkedapi.io/docs/sending-message/ Sending Messages Documentation} * @see {@link https://linkedapi.io/docs/action-st-send-message/ st.sendMessage Action Documentation} @@ -225,7 +184,7 @@ class LinkedApi { * process that retrieves the conversation history and prepares it for future updates. * * @param params - Parameters including the person's URL - * @returns Promise resolving to a WorkflowHandler for the sync action + * @returns Promise resolving to the sync action * * @see {@link https://linkedapi.io/docs/working-with-conversations/ Working with Conversations Documentation} * @see {@link https://linkedapi.io/docs/action-st-sync-conversation/ st.syncConversation Action Documentation} @@ -249,7 +208,7 @@ class LinkedApi { * Sales Navigator allows messaging people who are not connections and provides enhanced messaging features. * * @param params - Parameters including the person's URL, message text, and subject line - * @returns Promise resolving to a WorkflowHandler for the message sending action + * @returns Promise resolving to the message sending action * * @see {@link https://linkedapi.io/docs/sending-message/ Sending Messages Documentation} * @see {@link https://linkedapi.io/docs/action-nv-send-message/ nv.sendMessage Action Documentation} @@ -276,7 +235,7 @@ class LinkedApi { * history from Sales Navigator and prepares it for future updates. * * @param params - Parameters including the person's URL - * @returns Promise resolving to a WorkflowHandler for the sync action + * @returns Promise resolving to the sync action * * @see {@link https://linkedapi.io/docs/working-with-conversations/ Working with Conversations Documentation} * @see {@link https://linkedapi.io/docs/action-nv-sync-conversation/ nv.syncConversation Action Documentation} @@ -356,7 +315,7 @@ class LinkedApi { * including basic information, experience, education, skills, and more based on the specified parameters. * * @param params - Parameters specifying the person URL and what data to retrieve - * @returns Promise resolving to a WorkflowHandler containing the person's profile data + * @returns Promise resolving to an object containing the person's profile data * * @see {@link https://linkedapi.io/docs/visiting-person-page/ Visiting Person Page Documentation} * @see {@link https://linkedapi.io/docs/action-st-open-person-page/ st.openPersonPage Action Documentation} @@ -425,7 +384,7 @@ class LinkedApi { * Sales Navigator provides enhanced data and is useful for sales prospecting activities. * * @param params - Parameters including the person's hashed URL and data options - * @returns Promise resolving to a WorkflowHandler containing Sales Navigator person data + * @returns Promise resolving to an object containing Sales Navigator person data * * @see {@link https://linkedapi.io/docs/visiting-person-page/ Visiting Person Page Documentation} * @see {@link https://linkedapi.io/docs/action-nv-open-person-page/ nv.openPersonPage Action Documentation} @@ -449,7 +408,7 @@ class LinkedApi { * including basic information, employee data, posts, and more based on the specified parameters. * * @param params - Parameters specifying the company URL and what data to retrieve - * @returns Promise resolving to a WorkflowHandler containing the company's profile data + * @returns Promise resolving to an object containing the company's profile data * * @see {@link https://linkedapi.io/docs/action-st-open-company-page/ st.openCompanyPage Action Documentation} * @see {@link https://linkedapi.io/docs/action-st-retrieve-company-employees/ st.retrieveCompanyEmployees Child Action} @@ -496,7 +455,7 @@ class LinkedApi { * Sales Navigator provides enhanced company data and is useful for B2B sales prospecting. * * @param params - Parameters including the company's hashed URL and data options - * @returns Promise resolving to a WorkflowHandler containing Sales Navigator company data + * @returns Promise resolving to an object containing Sales Navigator company data * * @see {@link https://linkedapi.io/docs/action-nv-open-company-page/ nv.openCompanyPage Action Documentation} * @see {@link https://linkedapi.io/docs/action-nv-retrieve-company-employees/ nv.retrieveCompanyEmployees Child Action} @@ -540,7 +499,7 @@ class LinkedApi { * including content, author information, engagement metrics, and comments. * * @param params - Parameters specifying the post URL - * @returns Promise resolving to a WorkflowHandler containing the post data + * @returns Promise resolving to an object containing the post data * * @see {@link https://linkedapi.io/docs/action-st-open-post/ st.openPost Action Documentation} * @@ -567,7 +526,7 @@ class LinkedApi { * You can filter by various criteria like location, industry, company size, and more. * * @param params - Search parameters including keywords, filters, and pagination options - * @returns Promise resolving to a WorkflowHandler containing an array of company search results + * @returns Promise resolving to an object containing an array of company search results * * @see {@link https://linkedapi.io/docs/action-st-search-companies/ st.searchCompanies Action Documentation} * @@ -598,7 +557,7 @@ class LinkedApi { * Sales Navigator provides more detailed filtering options and enhanced company data. * * @param params - Sales Navigator search parameters with advanced filtering options - * @returns Promise resolving to a WorkflowHandler containing an array of Sales Navigator company results + * @returns Promise resolving to an object containing an array of Sales Navigator company results * * @see {@link https://linkedapi.io/docs/action-nv-search-companies/ nv.searchCompanies Action Documentation} * @@ -633,7 +592,7 @@ class LinkedApi { * You can filter by keywords, location, current company, past company, industry, and more. * * @param params - Search parameters including keywords, filters, and pagination options - * @returns Promise resolving to a WorkflowHandler containing an array of people search results + * @returns Promise resolving to an object containing an array of people search results * * @see {@link https://linkedapi.io/docs/action-st-search-people/ st.searchPeople Action Documentation} * @@ -664,7 +623,7 @@ class LinkedApi { * Sales Navigator provides more sophisticated filtering options and enhanced prospect data. * * @param params - Sales Navigator search parameters with advanced filtering options - * @returns Promise resolving to a WorkflowHandler containing an array of Sales Navigator people results + * @returns Promise resolving to an object containing an array of Sales Navigator people results * * @see {@link https://linkedapi.io/docs/action-nv-search-people/ nv.searchPeople Action Documentation} * @@ -695,7 +654,7 @@ class LinkedApi { * The request will appear in the recipient's connection requests section. * * @param params - Parameters including the person's URL and optional connection message - * @returns Promise resolving to a WorkflowHandler for the connection request action + * @returns Promise resolving to the connection request action * * @see {@link https://linkedapi.io/docs/working-with-connection-requests/ Working with Connection Requests Documentation} * @see {@link https://linkedapi.io/docs/action-st-send-connection-request/ st.sendConnectionRequest Action Documentation} @@ -720,7 +679,7 @@ class LinkedApi { * or have no connection with them. * * @param params - Parameters including the person's URL - * @returns Promise resolving to a WorkflowHandler containing the connection status result + * @returns Promise resolving to an object containing the connection status result * * @see {@link https://linkedapi.io/docs/checking-connection-status/ Checking Connection Status Documentation} * @see {@link https://linkedapi.io/docs/action-st-check-connection-status/ st.checkConnectionStatus Action Documentation} @@ -746,7 +705,7 @@ class LinkedApi { * The request will be removed from their pending connection requests. * * @param params - Parameters including the person's URL - * @returns Promise resolving to a WorkflowHandler for the withdrawal action + * @returns Promise resolving to the withdrawal action * * @see {@link https://linkedapi.io/docs/working-with-connection-requests/ Working with Connection Requests Documentation} * @see {@link https://linkedapi.io/docs/action-st-withdraw-connection-request/ st.withdrawConnectionRequest Action Documentation} @@ -769,7 +728,7 @@ class LinkedApi { * This method fetches a list of all pending connection requests that others have sent to you. * You can optionally filter the results by label. * - * @returns Promise resolving to a WorkflowHandler containing an array of pending requests + * @returns Promise resolving to an object containing an array of pending requests * * @see {@link https://linkedapi.io/docs/working-with-connection-requests/ Working with Connection Requests Documentation} * @see {@link https://linkedapi.io/docs/action-st-retrieve-pending-requests/ st.retrievePendingRequests Action Documentation} @@ -798,7 +757,7 @@ class LinkedApi { * like name, position, location, industry, company, and school. * * @param params - Parameters including optional filters and pagination options - * @returns Promise resolving to a WorkflowHandler containing an array of connections + * @returns Promise resolving to an object containing an array of connections * * @see {@link https://linkedapi.io/docs/managing-existing-connections/ Managing Existing Connections Documentation} * @see {@link https://linkedapi.io/docs/action-st-retrieve-connections/ st.retrieveConnections Action Documentation} @@ -830,7 +789,7 @@ class LinkedApi { * be in your connections list and you will lose the connection relationship. * * @param params - Parameters including the person's URL - * @returns Promise resolving to a WorkflowHandler for the removal action + * @returns Promise resolving to the removal action * * @see {@link https://linkedapi.io/docs/managing-existing-connections/ Managing Existing Connections Documentation} * @see {@link https://linkedapi.io/docs/action-st-remove-connection/ st.removeConnection Action Documentation} @@ -854,7 +813,7 @@ class LinkedApi { * You can only have one reaction per post, and adding a new reaction will replace any existing one. * * @param params - Parameters including the post URL and reaction type - * @returns Promise resolving to a WorkflowHandler for the reaction action + * @returns Promise resolving to the reaction action * * @see {@link https://linkedapi.io/docs/reacting-and-commenting/ Reacting and Commenting Documentation} * @see {@link https://linkedapi.io/docs/action-st-react-to-post/ st.reactToPost Action Documentation} @@ -879,7 +838,7 @@ class LinkedApi { * and can help increase engagement with the post. * * @param params - Parameters including the post URL and comment text - * @returns Promise resolving to a WorkflowHandler for the comment action + * @returns Promise resolving to the comment action * * @see {@link https://linkedapi.io/docs/reacting-and-commenting/ Reacting and Commenting Documentation} * @see {@link https://linkedapi.io/docs/action-st-comment-on-post/ st.commentOnPost Action Documentation} @@ -904,7 +863,7 @@ class LinkedApi { * performance across four key areas: establishing professional brand, finding right people, * engaging with insights, and building strong relationships. * - * @returns Promise resolving to a WorkflowHandler containing SSI data + * @returns Promise resolving to an object containing SSI data * * @see {@link https://linkedapi.io/docs/retrieving-ssi-and-performance/ Retrieving SSI and Performance Documentation} * @see {@link https://linkedapi.io/docs/action-st-retrieve-ssi/ st.retrieveSSI Action Documentation} @@ -929,7 +888,7 @@ class LinkedApi { * This method fetches your LinkedIn performance metrics including profile views, * search appearances, post impressions, and other engagement statistics. * - * @returns Promise resolving to a WorkflowHandler containing performance data + * @returns Promise resolving to an object containing performance data * * @see {@link https://linkedapi.io/docs/retrieving-ssi-and-performance/ Retrieving SSI and Performance Documentation} * @see {@link https://linkedapi.io/docs/action-st-retrieve-performance/ st.retrievePerformance Action Documentation} diff --git a/src/operations/check-connection-status.ts b/src/operations/check-connection-status.ts index 109ddff..ba25019 100644 --- a/src/operations/check-connection-status.ts +++ b/src/operations/check-connection-status.ts @@ -6,7 +6,7 @@ export class CheckConnectionStatus extends PredefinedOperation< TCheckConnectionStatusParams, TCheckConnectionStatusResult > { - protected override readonly functionName: TOperationName = 'checkConnectionStatus'; + protected override readonly operationName: TOperationName = 'checkConnectionStatus'; protected override readonly mapper = new SimpleWorkflowMapper< TCheckConnectionStatusParams, TCheckConnectionStatusResult diff --git a/src/operations/comment-on-post.ts b/src/operations/comment-on-post.ts index d939ed7..1965a91 100644 --- a/src/operations/comment-on-post.ts +++ b/src/operations/comment-on-post.ts @@ -3,7 +3,7 @@ import { VoidWorkflowMapper } from '../mappers'; import { TCommentOnPostParams } from '../types'; export class CommentOnPost extends PredefinedOperation { - protected override readonly functionName: TOperationName = 'commentOnPost'; + protected override readonly operationName: TOperationName = 'commentOnPost'; protected override readonly mapper = new VoidWorkflowMapper( 'st.commentOnPost', ); diff --git a/src/operations/fetch-company.ts b/src/operations/fetch-company.ts index 7f04942..1aef8a4 100644 --- a/src/operations/fetch-company.ts +++ b/src/operations/fetch-company.ts @@ -11,7 +11,7 @@ export class FetchCompany extends PredefinedOperation< TBaseFetchCompanyParams, TFetchCompanyResult > { - protected override readonly functionName: TOperationName = 'fetchCompany'; + protected override readonly operationName: TOperationName = 'fetchCompany'; protected override readonly mapper = new FetchCompanyMapper(); constructor(httpClient: HttpClient) { diff --git a/src/operations/fetch-person.ts b/src/operations/fetch-person.ts index 04b5d64..e97e794 100644 --- a/src/operations/fetch-person.ts +++ b/src/operations/fetch-person.ts @@ -3,7 +3,7 @@ import { TActionConfig, ThenWorkflowMapper } from '../mappers/then-workflow-mapp import { TBaseFetchPersonParams, TFetchPersonParams, TFetchPersonResult } from '../types'; export class FetchPerson extends PredefinedOperation { - protected override readonly functionName: TOperationName = 'fetchPerson'; + protected override readonly operationName: TOperationName = 'fetchPerson'; protected override readonly mapper = new FetchPersonMapper(); public override async execute( diff --git a/src/operations/fetch-post.ts b/src/operations/fetch-post.ts index 4cdfb5a..e3d729e 100644 --- a/src/operations/fetch-post.ts +++ b/src/operations/fetch-post.ts @@ -3,7 +3,7 @@ import { SimpleWorkflowMapper } from '../mappers'; import { TFetchPostParams, TFetchPostResult } from '../types'; export class FetchPost extends PredefinedOperation { - protected override readonly functionName: TOperationName = 'fetchPost'; + protected override readonly operationName: TOperationName = 'fetchPost'; protected override readonly mapper = new SimpleWorkflowMapper( { actionType: 'st.openPost', diff --git a/src/operations/react-to-post.ts b/src/operations/react-to-post.ts index 8be95bd..abb96f3 100644 --- a/src/operations/react-to-post.ts +++ b/src/operations/react-to-post.ts @@ -3,6 +3,6 @@ import { VoidWorkflowMapper } from '../mappers'; import { TReactToPostParams } from '../types'; export class ReactToPost extends PredefinedOperation { - protected override readonly functionName: TOperationName = 'reactToPost'; + protected override readonly operationName: TOperationName = 'reactToPost'; protected override readonly mapper = new VoidWorkflowMapper('st.reactToPost'); } diff --git a/src/operations/remove-connection.ts b/src/operations/remove-connection.ts index 628325e..ac2cf9f 100644 --- a/src/operations/remove-connection.ts +++ b/src/operations/remove-connection.ts @@ -3,7 +3,7 @@ import { VoidWorkflowMapper } from '../mappers'; import { TRemoveConnectionParams } from '../types'; export class RemoveConnection extends PredefinedOperation { - protected override readonly functionName: TOperationName = 'removeConnection'; + protected override readonly operationName: TOperationName = 'removeConnection'; protected override readonly mapper = new VoidWorkflowMapper( 'st.removeConnection', ); diff --git a/src/operations/retrieve-connections.ts b/src/operations/retrieve-connections.ts index 28849c1..ea47e70 100644 --- a/src/operations/retrieve-connections.ts +++ b/src/operations/retrieve-connections.ts @@ -6,7 +6,7 @@ export class RetrieveConnections extends PredefinedOperation< TRetrieveConnectionsParams, TRetrieveConnectionsResult[] > { - protected override readonly functionName: TOperationName = 'retrieveConnections'; + protected override readonly operationName: TOperationName = 'retrieveConnections'; protected override readonly mapper = new ArrayWorkflowMapper< TRetrieveConnectionsParams, TRetrieveConnectionsResult diff --git a/src/operations/retrieve-pending-requests.ts b/src/operations/retrieve-pending-requests.ts index 079d057..fd034e4 100644 --- a/src/operations/retrieve-pending-requests.ts +++ b/src/operations/retrieve-pending-requests.ts @@ -6,7 +6,7 @@ export class RetrievePendingRequests extends PredefinedOperation< void, TRetrievePendingRequestsResult[] > { - protected override readonly functionName: TOperationName = 'retrievePendingRequests'; + protected override readonly operationName: TOperationName = 'retrievePendingRequests'; protected override readonly mapper = new ArrayWorkflowMapper< void, TRetrievePendingRequestsResult diff --git a/src/operations/retrieve-performance.ts b/src/operations/retrieve-performance.ts index 7f4da41..03d5eeb 100644 --- a/src/operations/retrieve-performance.ts +++ b/src/operations/retrieve-performance.ts @@ -3,7 +3,7 @@ import { SimpleWorkflowMapper } from '../mappers'; import { TRetrievePerformanceResult } from '../types'; export class RetrievePerformance extends PredefinedOperation { - protected override readonly functionName: TOperationName = 'retrievePerformance'; + protected override readonly operationName: TOperationName = 'retrievePerformance'; protected override readonly mapper = new SimpleWorkflowMapper({ actionType: 'st.retrievePerformance', }); diff --git a/src/operations/retrieve-ssi.ts b/src/operations/retrieve-ssi.ts index 7957a41..4e9df1e 100644 --- a/src/operations/retrieve-ssi.ts +++ b/src/operations/retrieve-ssi.ts @@ -3,7 +3,7 @@ import { SimpleWorkflowMapper } from '../mappers'; import { TRetrieveSSIResult } from '../types'; export class RetrieveSSI extends PredefinedOperation { - protected override readonly functionName: TOperationName = 'retrieveSSI'; + protected override readonly operationName: TOperationName = 'retrieveSSI'; protected override readonly mapper = new SimpleWorkflowMapper({ actionType: 'st.retrieveSSI', }); diff --git a/src/operations/sales-navigator-fetch-company.ts b/src/operations/sales-navigator-fetch-company.ts index b2e8a99..4461cd8 100644 --- a/src/operations/sales-navigator-fetch-company.ts +++ b/src/operations/sales-navigator-fetch-company.ts @@ -6,7 +6,7 @@ export class SalesNavigatorFetchCompany extends PredefinedOperation< TNvBaseFetchCompanyParams, TNvFetchCompanyResult > { - protected override readonly functionName: TOperationName = 'salesNavigatorFetchCompany'; + protected override readonly operationName: TOperationName = 'salesNavigatorFetchCompany'; protected override readonly mapper = new NvFetchCompanyMapper(); public override async execute( diff --git a/src/operations/sales-navigator-fetch-person.ts b/src/operations/sales-navigator-fetch-person.ts index 18dd59c..13b7e63 100644 --- a/src/operations/sales-navigator-fetch-person.ts +++ b/src/operations/sales-navigator-fetch-person.ts @@ -6,7 +6,7 @@ export class SalesNavigatorFetchPerson extends PredefinedOperation< TNvOpenPersonPageParams, TNvOpenPersonPageResult > { - protected override readonly functionName: TOperationName = 'salesNavigatorFetchPerson'; + protected override readonly operationName: TOperationName = 'salesNavigatorFetchPerson'; protected override readonly mapper = new NvFetchPersonMapper(); } diff --git a/src/operations/sales-navigator-search-companies.ts b/src/operations/sales-navigator-search-companies.ts index 91e110f..1db2858 100644 --- a/src/operations/sales-navigator-search-companies.ts +++ b/src/operations/sales-navigator-search-companies.ts @@ -6,7 +6,7 @@ export class SalesNavigatorSearchCompanies extends PredefinedOperation< TNvSearchCompaniesParams, TNvSearchCompanyResult[] > { - protected override readonly functionName: TOperationName = 'salesNavigatorSearchCompanies'; + protected override readonly operationName: TOperationName = 'salesNavigatorSearchCompanies'; protected override readonly mapper = new ArrayWorkflowMapper< TNvSearchCompaniesParams, TNvSearchCompanyResult diff --git a/src/operations/sales-navigator-search-people.ts b/src/operations/sales-navigator-search-people.ts index 1c46106..7c05c01 100644 --- a/src/operations/sales-navigator-search-people.ts +++ b/src/operations/sales-navigator-search-people.ts @@ -6,7 +6,7 @@ export class SalesNavigatorSearchPeople extends PredefinedOperation< TNvSearchPeopleParams, TNvSearchPeopleResult[] > { - protected override readonly functionName: TOperationName = 'salesNavigatorSearchPeople'; + protected override readonly operationName: TOperationName = 'salesNavigatorSearchPeople'; protected override readonly mapper = new ArrayWorkflowMapper< TNvSearchPeopleParams, TNvSearchPeopleResult diff --git a/src/operations/sales-navigator-send-message.ts b/src/operations/sales-navigator-send-message.ts index ad9a139..ae9f466 100644 --- a/src/operations/sales-navigator-send-message.ts +++ b/src/operations/sales-navigator-send-message.ts @@ -3,7 +3,7 @@ import { VoidWorkflowMapper } from '../mappers/void-workflow-mapper'; import { TNvSendMessageParams } from '../types'; export class SalesNavigatorSendMessage extends PredefinedOperation { - protected override readonly functionName: TOperationName = 'salesNavigatorSendMessage'; + protected override readonly operationName: TOperationName = 'salesNavigatorSendMessage'; protected override readonly mapper = new VoidWorkflowMapper( 'nv.sendMessage', ); diff --git a/src/operations/sales-navigator-sync-conversation.ts b/src/operations/sales-navigator-sync-conversation.ts index e35ae5c..6be2dd1 100644 --- a/src/operations/sales-navigator-sync-conversation.ts +++ b/src/operations/sales-navigator-sync-conversation.ts @@ -6,7 +6,7 @@ export class SalesNavigatorSyncConversation extends PredefinedOperation< TNvSyncConversationParams, void > { - protected override readonly functionName: TOperationName = 'salesNavigatorSyncConversation'; + protected override readonly operationName: TOperationName = 'salesNavigatorSyncConversation'; protected override readonly mapper = new VoidWorkflowMapper( 'nv.syncConversation', ); diff --git a/src/operations/search-companies.ts b/src/operations/search-companies.ts index edc4068..cc27dca 100644 --- a/src/operations/search-companies.ts +++ b/src/operations/search-companies.ts @@ -6,7 +6,7 @@ export class SearchCompanies extends PredefinedOperation< TSearchCompaniesParams, TSearchCompanyResult[] > { - protected override readonly functionName: TOperationName = 'searchCompanies'; + protected override readonly operationName: TOperationName = 'searchCompanies'; protected override readonly mapper = new ArrayWorkflowMapper< TSearchCompaniesParams, TSearchCompanyResult diff --git a/src/operations/search-people.ts b/src/operations/search-people.ts index 88a943c..03748a3 100644 --- a/src/operations/search-people.ts +++ b/src/operations/search-people.ts @@ -3,7 +3,7 @@ import { ArrayWorkflowMapper } from '../mappers/array-workflow-mapper'; import { TSearchPeopleParams, TSearchPeopleResult } from '../types'; export class SearchPeople extends PredefinedOperation { - protected override readonly functionName: TOperationName = 'searchPeople'; + protected override readonly operationName: TOperationName = 'searchPeople'; protected override readonly mapper = new ArrayWorkflowMapper< TSearchPeopleParams, TSearchPeopleResult diff --git a/src/operations/send-connection-request.ts b/src/operations/send-connection-request.ts index b2d6eba..495275a 100644 --- a/src/operations/send-connection-request.ts +++ b/src/operations/send-connection-request.ts @@ -3,7 +3,7 @@ import { VoidWorkflowMapper } from '../mappers'; import { TSendConnectionRequestParams } from '../types'; export class SendConnectionRequest extends PredefinedOperation { - protected override readonly functionName: TOperationName = 'sendConnectionRequest'; + protected override readonly operationName: TOperationName = 'sendConnectionRequest'; protected override readonly mapper = new VoidWorkflowMapper( 'st.sendConnectionRequest', ); diff --git a/src/operations/send-message.ts b/src/operations/send-message.ts index 822e981..c86543a 100644 --- a/src/operations/send-message.ts +++ b/src/operations/send-message.ts @@ -3,6 +3,6 @@ import { VoidWorkflowMapper } from '../mappers/void-workflow-mapper'; import { TSendMessageParams } from '../types'; export class SendMessage extends PredefinedOperation { - protected override readonly functionName: TOperationName = 'sendMessage'; + protected override readonly operationName: TOperationName = 'sendMessage'; protected override readonly mapper = new VoidWorkflowMapper('st.sendMessage'); } diff --git a/src/operations/sync-conversation.ts b/src/operations/sync-conversation.ts index 9b74cf5..6aff759 100644 --- a/src/operations/sync-conversation.ts +++ b/src/operations/sync-conversation.ts @@ -3,7 +3,7 @@ import { VoidWorkflowMapper } from '../mappers'; import { TSyncConversationParams } from '../types'; export class SyncConversation extends PredefinedOperation { - protected override readonly functionName: TOperationName = 'syncConversation'; + protected override readonly operationName: TOperationName = 'syncConversation'; protected override readonly mapper = new VoidWorkflowMapper( 'st.syncConversation', ); diff --git a/src/operations/withdraw-connection-request.ts b/src/operations/withdraw-connection-request.ts index 3abcf1e..01d1908 100644 --- a/src/operations/withdraw-connection-request.ts +++ b/src/operations/withdraw-connection-request.ts @@ -6,7 +6,7 @@ export class WithdrawConnectionRequest extends PredefinedOperation< TWithdrawConnectionRequestParams, void > { - protected override readonly functionName: TOperationName = 'withdrawConnectionRequest'; + protected override readonly operationName: TOperationName = 'withdrawConnectionRequest'; protected override readonly mapper = new VoidWorkflowMapper( 'st.withdrawConnectionRequest', ); diff --git a/src/types/errors.ts b/src/types/errors.ts index 42db42b..3130e00 100644 --- a/src/types/errors.ts +++ b/src/types/errors.ts @@ -83,22 +83,22 @@ export class LinkedApiError extends Error { /** * This error is thrown when a workflow times out. - * Contains workflowId and functionName to restore the workflow. + * Contains workflowId and operationName to continue checking the workflow. */ export class LinkedApiWorkflowTimeoutError extends LinkedApiError { public readonly workflowId: string; - public readonly functionName: TOperationName; + public readonly operationName: TOperationName; - constructor(workflowId: string, functionName: TOperationName) { + constructor(workflowId: string, operationName: TOperationName) { super( 'workflowTimeout', - `Workflow ${workflowId} timed out. Use restoreWorkflow(${workflowId}, ${functionName}) to restore the workflow.`, + `Workflow ${workflowId} timed out. Call .result again to continue checking the workflow.`, { workflowId, - functionName, + operationName, }, ); this.workflowId = workflowId; - this.functionName = functionName; + this.operationName = operationName; } } diff --git a/src/types/workflows.ts b/src/types/workflows.ts index a1745d3..b62263b 100644 --- a/src/types/workflows.ts +++ b/src/types/workflows.ts @@ -9,7 +9,9 @@ export type TWorkflowDefinition = | TSingleActionWorkflowDefinition | TSingleActionWorkflowDefinition[]; -export type TWorkflowStatus = 'running' | 'completed' | 'failed'; +export type TWorkflowRunningStatus = 'running'; + +export type TWorkflowStatus = TWorkflowRunningStatus | 'completed' | 'failed'; export type TWorkflowCompletion = | TWorkflowCompletionSingleAction From 6d35c32426f3ea706fe2bcd2710bf775e8765863 Mon Sep 17 00:00:00 2001 From: Kiril Ivonchik Date: Fri, 22 Aug 2025 14:10:18 +0200 Subject: [PATCH 10/10] 1.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 533d5b8..e7cee58 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "linkedapi-node", - "version": "1.1.1", + "version": "1.2.0", "description": "Official TypeScript SDK for Linked API", "main": "dist/index.js", "types": "dist/index.d.ts",