Repository: Azure/azure-functions-durable-js
Pull request #532 (2023-07-25 18:51:29)

Rename DurableOrchestrationClient to DurableClient
This is an internal change only. The publicly exported type which this class implements is already called `DurableClient` and the `DurableOrchestrationClient` class was not publicly exported. This changes makes the names match.


Patch:
diff --git a/src/durableClient/DurableOrchestrationClient.ts b/src/durableClient/DurableClient.ts
similarity index 97%
rename from src/durableClient/DurableOrchestrationClient.ts
rename to src/durableClient/DurableClient.ts
index 456ba7d..e405b0d 100644
--- a/src/durableClient/DurableOrchestrationClient.ts
+++ b/src/durableClient/DurableClient.ts
@@ -1,805 +1,805 @@
-// tslint:disable:member-access
-
-import { HttpRequest, HttpResponse } from "@azure/functions";
-import axios, { AxiosInstance, AxiosResponse } from "axios";
-/** @hidden */
-import process = require("process");
-/** @hidden */
-import url = require("url");
-import { isURL } from "validator";
-import {
-    StartNewOptions,
-    DurableClient,
-    GetStatusOptions,
-    OrchestrationFilter,
-    TaskHubOptions,
-    WaitForCompletionOptions,
-} from "durable-functions";
-import { WebhookUtils } from "../util/WebhookUtils";
-import { OrchestrationClientInputData } from "./OrchestrationClientInputData";
-import { HttpManagementPayload } from "../http/HttpManagementPayload";
-import { DurableOrchestrationStatus } from "../orchestrations/DurableOrchestrationStatus";
-import { PurgeHistoryResult } from "./PurgeHistoryResult";
-import { EntityId } from "../entities/entityid";
-import { EntityStateResponse } from "../entities/entitystateresponse";
-import { OrchestrationRuntimeStatus } from "../orchestrations/OrchestrationRuntimeStatus";
-import { Utils } from "../util/Utils";
-
-/**
- * Client for starting, querying, terminating and raising events to
- * orchestration instances.
- */
-export class DurableOrchestrationClient implements DurableClient {
-    public readonly taskHubName: string;
-    /** @hidden */
-    public readonly uniqueWebhookOrigins: string[];
-
-    private readonly axiosInstance: AxiosInstance;
-
-    private readonly eventNamePlaceholder = "{eventName}";
-    private readonly functionNamePlaceholder = "{functionName}";
-    private readonly instanceIdPlaceholder = "[/{instanceId}]";
-    private readonly reasonPlaceholder = "{text}";
-
-    private readonly createdTimeFromQueryKey = "createdTimeFrom";
-    private readonly createdTimeToQueryKey = "createdTimeTo";
-    private readonly runtimeStatusQueryKey = "runtimeStatus";
-    private readonly showHistoryQueryKey = "showHistory";
-    private readonly showHistoryOutputQueryKey = "showHistoryOutput";
-    private readonly showInputQueryKey = "showInput";
-
-    private urlValidationOptions: ValidatorJS.IsURLOptions = {
-        protocols: ["http", "https"],
-        require_tld: false,
-        require_protocol: true,
-        require_valid_protocol: true,
-    };
-
-    /**
-     * @param clientData The object representing the orchestrationClient input
-     *  binding of the Azure function that will use this client.
-     */
-    constructor(private readonly clientData: OrchestrationClientInputData) {
-        if (!clientData) {
-            throw new TypeError(
-                `clientData: Expected OrchestrationClientInputData but got ${typeof clientData}`
-            );
-        }
-
-        this.axiosInstance = axios.create({
-            validateStatus: (status: number) => status < 600,
-            headers: {
-                post: {
-                    "Content-Type": "application/json",
-                },
-            },
-            maxContentLength: Infinity,
-        });
-        this.taskHubName = this.clientData.taskHubName;
-        this.uniqueWebhookOrigins = this.extractUniqueWebhookOrigins(this.clientData);
-    }
-
-    public createCheckStatusResponse(
-        request: HttpRequest | undefined,
-        instanceId: string
-    ): HttpResponse {
-        const httpManagementPayload: HttpManagementPayload = this.getClientResponseLinks(
-            request,
-            instanceId
-        );
-
-        return new HttpResponse({
-            status: 202,
-            jsonBody: httpManagementPayload,
-            headers: {
-                "Content-Type": "application/json",
-                Location: httpManagementPayload.statusQueryGetUri,
-                "Retry-After": "10",
-            },
-        });
-    }
-
-    public createHttpManagementPayload(instanceId: string): HttpManagementPayload {
-        return this.getClientResponseLinks(undefined, instanceId);
-    }
-
-    public async getStatus(
-        instanceId: string,
-        options: GetStatusOptions = {}
-    ): Promise<DurableOrchestrationStatus> {
-        const internalOptions: GetStatusInternalOptions = {
-            instanceId,
-            ...options,
-        };
-        const response: AxiosResponse = await this.getStatusInternal(internalOptions);
-
-        switch (response.status) {
-            case 200: // instance completed, failed or terminated
-            case 202: // instance running or pending
-            case 400:
-                if (!response.data) {
-                    throw new Error(
-                        `DurableClient error: the Durable Functions extension replied with an empty HTTP ${response.status} response.`
-                    );
-                }
-
-                try {
-                    return new DurableOrchestrationStatus(response.data);
-                } catch (error) {
-                    throw new Error(
-                        `DurableClient error: could not construct a DurableOrchestrationStatus object using the data received from the Durable Functions extension: ${error.message}`
-                    );
-                }
-
-            case 404: // instance not found
-                let msg =
-                    `DurableClient error: Durable Functions extension replied with HTTP 404 response. ` +
-                    `This usually means we could not find any data associated with the instanceId provided: ${instanceId}.`;
-                if (response.data) {
-                    msg += ` Details: ${JSON.stringify(response.data)}`;
-                }
-                throw new Error(msg);
-            case 500: // request failed with unhandled exception (response data contains exception details)
-            default:
-                return Promise.reject(this.createGenericError(response));
-        }
-    }
-
-    public async getStatusAll(): Promise<DurableOrchestrationStatus[]> {
-        const response = await this.getStatusInternal({});
-        switch (response.status) {
-            case 200:
-                return response.data as DurableOrchestrationStatus[];
-            default:
-                return Promise.reject(this.createGenericError(response));
-        }
-    }
-
-    public async getStatusBy(filter: OrchestrationFilter): Promise<DurableOrchestrationStatus[]> {
-        const response = await this.getStatusInternal(filter);
-        switch (response.status) {
-            case 200:
-                return response.data as DurableOrchestrationStatus[];
-            default:
-                return Promise.reject(this.createGenericError(response));
-        }
-    }
-
-    public async purgeInstanceHistory(instanceId: string): Promise<PurgeHistoryResult> {
-        let requestUrl: string;
-        if (this.clientData.rpcBaseUrl) {
-            // Fast local RPC path
-            requestUrl = new URL(`instances/${instanceId}`, this.clientData.rpcBaseUrl).href;
-        } else {
-            // Legacy app frontend path
-            const template = this.clientData.managementUrls.purgeHistoryDeleteUri;
-            const idPlaceholder = this.clientData.managementUrls.id;
-            requestUrl = template.replace(idPlaceholder, instanceId);
-        }
-
-        const response = await this.axiosInstance.delete(requestUrl);
-        switch (response.status) {
-            case 200: // instance found
-                return response.data as PurgeHistoryResult;
-            case 404: // instance not found
-                return new PurgeHistoryResult(0);
-            default:
-                return Promise.reject(this.createGenericError(response));
-        }
-    }
-
-    public async purgeInstanceHistoryBy(filter: OrchestrationFilter): Promise<PurgeHistoryResult> {
-        let requestUrl: string;
-        const { createdTimeFrom, createdTimeTo, runtimeStatus } = filter;
-        if (this.clientData.rpcBaseUrl) {
-            // Fast local RPC path
-            let path = new URL("instances/", this.clientData.rpcBaseUrl).href;
-            const query: string[] = [];
-            if (createdTimeFrom) {
-                query.push(`createdTimeFrom=${createdTimeFrom.toISOString()}`);
-            }
-            if (createdTimeTo) {
-                query.push(`createdTimeTo=${createdTimeTo.toISOString()}`);
-            }
-            if (runtimeStatus && runtimeStatus.length > 0) {
-                const statusList: string = runtimeStatus.map((value) => value.toString()).join(",");
-                query.push(`runtimeStatus=${statusList}`);
-            }
-
-            if (query.length > 0) {
-                path += "?" + query.join("&");
-            }
-
-            requestUrl = new URL(path, this.clientData.rpcBaseUrl).href;
-        } else {
-            // Legacy app frontend path
-            const idPlaceholder = this.clientData.managementUrls.id;
-            requestUrl = this.clientData.managementUrls.statusQueryGetUri.replace(
-                idPlaceholder,
-                ""
-            );
-
-            if (!(createdTimeFrom instanceof Date)) {
-                throw new Error("createdTimeFrom must be a valid Date");
-            }
-
-            if (createdTimeFrom) {
-                requestUrl += `&${this.createdTimeFromQueryKey}=${createdTimeFrom.toISOString()}`;
-            }
-
-            if (createdTimeTo) {
-                requestUrl += `&${this.createdTimeToQueryKey}=${createdTimeTo.toISOString()}`;
-            }
-
-            if (runtimeStatus && runtimeStatus.length > 0) {
-                const statusesString = runtimeStatus
-                    .map((value) => value.toString())
-                    .reduce((acc, curr, i) => {
-                        return acc + (i > 0 ? "," : "") + curr;
-                    });
-
-                requestUrl += `&${this.runtimeStatusQueryKey}=${statusesString}`;
-            }
-        }
-
-        const response = await this.axiosInstance.delete(requestUrl);
-        switch (response.status) {
-            case 200: // instance found
-                return response.data as PurgeHistoryResult;
-            case 404: // instance not found
-                return new PurgeHistoryResult(0);
-            default:
-                return Promise.reject(this.createGenericError(response));
-        }
-    }
-
-    public async raiseEvent(
-        instanceId: string,
-        eventName: string,
-        eventData: unknown,
-        options: TaskHubOptions = {}
-    ): Promise<void> {
-        const { taskHubName, connectionName } = options;
-        if (!eventName) {
-            throw new Error("eventName must be a valid string.");
-        }
-
-        let requestUrl: string;
-        if (this.clientData.rpcBaseUrl) {
-            // Fast local RPC path
-            let path = `instances/${instanceId}/raiseEvent/${eventName}`;
-            const query: string[] = [];
-            if (taskHubName) {
-                query.push(`taskHub=${taskHubName}`);
-            }
-            if (connectionName) {
-                query.push(`connection=${connectionName}`);
-            }
-
-            if (query.length > 0) {
-                path += "?" + query.join("&");
-            }
-
-            requestUrl = new URL(path, this.clientData.rpcBaseUrl).href;
-        } else {
-            // Legacy app frontend path
-            const idPlaceholder = this.clientData.managementUrls.id;
-            requestUrl = this.clientData.managementUrls.sendEventPostUri
-                .replace(idPlaceholder, instanceId)
-                .replace(this.eventNamePlaceholder, eventName);
-
-            if (taskHubName) {
-                requestUrl = requestUrl.replace(this.clientData.taskHubName, taskHubName);
-            }
-
-            if (connectionName) {
-                requestUrl = requestUrl.replace(/(connection=)([\w]+)/gi, "$1" + connectionName);
-            }
-        }
-
-        const response = await this.axiosInstance.post(requestUrl, JSON.stringify(eventData));
-        switch (response.status) {
-            case 202: // event accepted
-            case 410: // instance completed or failed
-                return;
-            case 404:
-                return Promise.reject(new Error(`No instance with ID '${instanceId}' found.`));
-            default:
-                return Promise.reject(this.createGenericError(response));
-        }
-    }
-
-    public async readEntityState<T>(
-        entityId: EntityId,
-        options: TaskHubOptions = {}
-    ): Promise<EntityStateResponse<T>> {
-        let requestUrl: string;
-        const { taskHubName, connectionName } = options;
-        if (this.clientData.rpcBaseUrl) {
-            // Fast local RPC path
-            let path = `entities/${entityId.name}/${entityId.key}`;
-            const query: string[] = [];
-            if (taskHubName) {
-                query.push(`taskHub=${taskHubName}`);
-            }
-            if (connectionName) {
-                query.push(`connection=${connectionName}`);
-            }
-
-            if (query.length > 0) {
-                path += "?" + query.join("&");
-            }
-
-            requestUrl = new URL(path, this.clientData.rpcBaseUrl).href;
-        } else {
-            // Legacy app frontend path
-            if (!(this.clientData.baseUrl && this.clientData.requiredQueryStringParameters)) {
-                throw new Error(
-                    "Cannot use the readEntityState API with this version of the Durable Task Extension."
-                );
-            }
-
-            requestUrl = WebhookUtils.getReadEntityUrl(
-                this.clientData.baseUrl,
-                this.clientData.requiredQueryStringParameters,
-                entityId.name,
-                entityId.key,
-                taskHubName,
-                connectionName
-            );
-        }
-
-        const response = await this.axiosInstance.get<T>(requestUrl);
-        switch (response.status) {
-            case 200: // entity exists
-                return new EntityStateResponse(true, response.data);
-            case 404: // entity does not exist
-                return new EntityStateResponse(false);
-            default:
-                return Promise.reject(this.createGenericError(response));
-        }
-    }
-
-    public async rewind(
-        instanceId: string,
-        reason: string,
-        options: TaskHubOptions = {}
-    ): Promise<void> {
-        const { taskHubName, connectionName } = options;
-        const idPlaceholder = this.clientData.managementUrls.id;
-
-        let requestUrl: string;
-        if (this.clientData.rpcBaseUrl) {
-            // Fast local RPC path
-            let path = `instances/${instanceId}/rewind?reason=${reason}`;
-            const query: string[] = [];
-            if (taskHubName) {
-                query.push(`taskHub=${taskHubName}`);
-            }
-            if (connectionName) {
-                query.push(`connection=${connectionName}`);
-            }
-
-            if (query.length > 0) {
-                path += "&" + query.join("&");
-            }
-
-            requestUrl = new URL(path, this.clientData.rpcBaseUrl).href;
-        } else {
-            // Legacy app frontend path
-            requestUrl = this.clientData.managementUrls.rewindPostUri
-                .replace(idPlaceholder, instanceId)
-                .replace(this.reasonPlaceholder, reason);
-        }
-
-        const response = await this.axiosInstance.post(requestUrl);
-        switch (response.status) {
-            case 202:
-                return;
-            case 404:
-                return Promise.reject(new Error(`No instance with ID '${instanceId}' found.`));
-            case 410:
-                return Promise.reject(
-                    new Error(
-                        "The rewind operation is only supported on failed orchestration instances."
-                    )
-                );
-            default:
-                return Promise.reject(this.createGenericError(response));
-        }
-    }
-
-    public async signalEntity(
-        entityId: EntityId,
-        operationName?: string,
-        operationContent?: unknown,
-        options: TaskHubOptions = {}
-    ): Promise<void> {
-        const { taskHubName, connectionName } = options;
-        let requestUrl: string;
-        if (this.clientData.rpcBaseUrl) {
-            // Fast local RPC path
-            let path = `entities/${entityId.name}/${entityId.key}`;
-            const query: string[] = [];
-            if (operationName) {
-                query.push(`op=${operationName}`);
-            }
-            if (taskHubName) {
-                query.push(`taskHub=${taskHubName}`);
-            }
-            if (connectionName) {
-                query.push(`connection=${connectionName}`);
-            }
-
-            if (query.length > 0) {
-                path += "?" + query.join("&");
-            }
-
-            requestUrl = new URL(path, this.clientData.rpcBaseUrl).href;
-        } else {
-            // Legacy app frontend path
-            if (!(this.clientData.baseUrl && this.clientData.requiredQueryStringParameters)) {
-                throw new Error(
-                    "Cannot use the signalEntity API with this version of the Durable Task Extension."
-                );
-            }
-
-            requestUrl = WebhookUtils.getSignalEntityUrl(
-                this.clientData.baseUrl,
-                this.clientData.requiredQueryStringParameters,
-                entityId.name,
-                entityId.key,
-                operationName,
-                taskHubName,
-                connectionName
-            );
-        }
-
-        const response = await this.axiosInstance.post(
-            requestUrl,
-            JSON.stringify(operationContent)
-        );
-        switch (response.status) {
-            case 202: // signal accepted
-                return;
-            default:
-                return Promise.reject(this.createGenericError(response));
-        }
-    }
-
-    public async startNew(
-        orchestratorFunctionName: string,
-        options?: StartNewOptions
-    ): Promise<string> {
-        if (!orchestratorFunctionName) {
-            throw new Error("orchestratorFunctionName must be a valid string.");
-        }
-
-        // TODO: Add support for specifying a task hub and a connection name
-        let requestUrl: string;
-        const instanceIdPath: string = options?.instanceId ? `/${options.instanceId}` : "";
-        if (this.clientData.rpcBaseUrl) {
-            // Fast local RPC path
-            requestUrl = new URL(
-                `orchestrators/${orchestratorFunctionName}${instanceIdPath}`,
-                this.clientData.rpcBaseUrl
-            ).href;
-        } else {
-            // Legacy app frontend path
-            requestUrl = this.clientData.creationUrls.createNewInstancePostUri;
-            requestUrl = requestUrl
-                .replace(this.functionNamePlaceholder, orchestratorFunctionName)
-                .replace(this.instanceIdPlaceholder, instanceIdPath);
-        }
-
-        const input: unknown = options?.input !== undefined ? JSON.stringify(options.input) : "";
-        const response = await this.axiosInstance.post(requestUrl, input);
-        if (response.data && response.status <= 202) {
-            return (response.data as HttpManagementPayload).id;
-        } else {
-            return Promise.reject(this.createGenericError(response));
-        }
-    }
-
-    public async terminate(instanceId: string, reason: string): Promise<void> {
-        const idPlaceholder = this.clientData.managementUrls.id;
-        let requestUrl: string;
-        if (this.clientData.rpcBaseUrl) {
-            // Fast local RPC path
-            requestUrl = new URL(
-                `instances/${instanceId}/terminate?reason=${reason}`,
-                this.clientData.rpcBaseUrl
-            ).href;
-        } else {
-            // Legacy app frontend path
-            requestUrl = this.clientData.managementUrls.terminatePostUri
-                .replace(idPlaceholder, instanceId)
-                .replace(this.reasonPlaceholder, reason);
-        }
-
-        const response = await this.axiosInstance.post(requestUrl);
-        switch (response.status) {
-            case 202: // terminate accepted
-            case 410: // instance completed or failed
-                return;
-            case 404:
-                return Promise.reject(new Error(`No instance with ID '${instanceId}' found.`));
-            default:
-                return Promise.reject(this.createGenericError(response));
-        }
-    }
-
-    public async waitForCompletionOrCreateCheckStatusResponse(
-        request: HttpRequest,
-        instanceId: string,
-        waitOptions: WaitForCompletionOptions = {}
-    ): Promise<HttpResponse> {
-        const timeoutInMilliseconds: number =
-            waitOptions.timeoutInMilliseconds !== undefined
-                ? waitOptions.timeoutInMilliseconds
-                : 10000;
-        const retryIntervalInMilliseconds: number =
-            waitOptions.retryIntervalInMilliseconds !== undefined
-                ? waitOptions.retryIntervalInMilliseconds
-                : 1000;
-
-        if (retryIntervalInMilliseconds > timeoutInMilliseconds) {
-            throw new Error(
-                `Total timeout ${timeoutInMilliseconds} (ms) should be bigger than retry timeout ${retryIntervalInMilliseconds} (ms)`
-            );
-        }
-
-        const hrStart = process.hrtime();
-        while (true) {
-            const status = await this.getStatus(instanceId);
-
-            if (status) {
-                switch (status.runtimeStatus) {
-                    case OrchestrationRuntimeStatus.Completed:
-                        return this.createHttpResponse(200, status.output);
-                    case OrchestrationRuntimeStatus.Canceled:
-                    case OrchestrationRuntimeStatus.Terminated:
-                        return this.createHttpResponse(200, status);
-                    case OrchestrationRuntimeStatus.Failed:
-                        return this.createHttpResponse(500, status);
-                }
-            }
-
-            const hrElapsed = process.hrtime(hrStart);
-            const hrElapsedMilliseconds = Utils.getHrMilliseconds(hrElapsed);
-
-            if (hrElapsedMilliseconds < timeoutInMilliseconds) {
-                const remainingTime = timeoutInMilliseconds - hrElapsedMilliseconds;
-                await Utils.sleep(
-                    remainingTime > retryIntervalInMilliseconds
-                        ? retryIntervalInMilliseconds
-                        : remainingTime
-                );
-            } else {
-                return this.createCheckStatusResponse(request, instanceId);
-            }
-        }
-    }
-
-    private createHttpResponse(statusCode: number, body: unknown): HttpResponse {
-        return new HttpResponse({
-            status: statusCode,
-            jsonBody: body,
-            headers: {
-                "Content-Type": "application/json",
-            },
-        });
-    }
-
-    private getClientResponseLinks(
-        request: HttpRequest | undefined,
-        instanceId: string
-    ): HttpManagementPayload {
-        const payload = { ...this.clientData.managementUrls };
-
-        (Object.keys(payload) as Array<keyof HttpManagementPayload>).forEach((key) => {
-            if (
-                this.hasValidRequestUrl(request) &&
-                isURL(payload[key], this.urlValidationOptions)
-            ) {
-                const requestUrl = new url.URL((request as HttpRequest).url);
-                const dataUrl = new url.URL(payload[key]);
-                payload[key] = payload[key].replace(dataUrl.origin, requestUrl.origin);
-            }
-
-            payload[key] = payload[key].replace(this.clientData.managementUrls.id, instanceId);
-        });
-
-        return payload;
-    }
-
-    private hasValidRequestUrl(request: HttpRequest | undefined): boolean {
-        return request !== undefined && (request as HttpRequest).url !== undefined;
-    }
-
-    private extractUniqueWebhookOrigins(clientData: OrchestrationClientInputData): string[] {
-        const origins = this.extractWebhookOrigins(clientData.creationUrls).concat(
-            this.extractWebhookOrigins(clientData.managementUrls)
-        );
-
-        const uniqueOrigins = origins.reduce<string[]>((acc, curr) => {
-            if (acc.indexOf(curr) === -1) {
-                acc.push(curr);
-            }
-            return acc;
-        }, []);
-
-        return uniqueOrigins;
-    }
-
-    private extractWebhookOrigins(obj: { [key: string]: string }): string[] {
-        const origins: string[] = [];
-
-        const keys = Object.getOwnPropertyNames(obj);
-        keys.forEach((key) => {
-            const value = obj[key];
-
-            if (isURL(value, this.urlValidationOptions)) {
-                const valueAsUrl = new url.URL(value);
-                const origin = valueAsUrl.origin;
-                origins.push(origin);
-            }
-        });
-
-        return origins;
-    }
-
-    /**
-     * Internal method that gets the status of all orchestration instances that
-     * match the specified conditions. It handles pagination automatically by
-     * calling itself recursively until no more continuation tokens are found.
-     * @param options Return orchestration instances which were created
-     *  after this Date.
-     * @param [continuationToken] Continuation token corresponding to the
-     * `x-ms-continuation-token` header, for getting the next batch
-     * of results. Used for recursion.
-     * @param [prevData] Results of a previous request, used internally
-     * to aggregate results during recursion.
-     */
-    private async getStatusInternal(
-        options: GetStatusInternalOptions,
-        continuationToken?: string,
-        prevData?: unknown[]
-    ): Promise<AxiosResponse> {
-        let requestUrl: string;
-        if (this.clientData.rpcBaseUrl) {
-            // Fast local RPC path
-            let path = new URL(`instances/${options.instanceId || ""}`, this.clientData.rpcBaseUrl)
-                .href;
-            const query: string[] = [];
-            if (options.taskHubName) {
-                query.push(`taskHub=${options.taskHubName}`);
-            }
-            if (options.connectionName) {
-                query.push(`connection=${options.connectionName}`);
-            }
-            if (options.showHistory) {
-                query.push(`showHistory=${options.showHistory}`);
-            }
-            if (options.showHistoryOutput) {
-                query.push(`showHistoryOutput=${options.showHistoryOutput}`);
-            }
-            if (options.showInput) {
-                query.push(`showInput=${options.showInput}`);
-            }
-            if (options.createdTimeFrom) {
-                query.push(`createdTimeFrom=${options.createdTimeFrom.toISOString()}`);
-            }
-            if (options.createdTimeTo) {
-                query.push(`createdTimeTo=${options.createdTimeTo.toISOString()}`);
-            }
-            if (options.runtimeStatus && options.runtimeStatus.length > 0) {
-                const statusList: string = options.runtimeStatus
-                    .map((value) => value.toString())
-                    .join(",");
-                query.push(`runtimeStatus=${statusList}`);
-            }
-
-            if (query.length > 0) {
-                path += "?" + query.join("&");
-            }
-
-            requestUrl = new URL(path, this.clientData.rpcBaseUrl).href;
-        } else {
-            // Legacy app frontend code path
-            const template = this.clientData.managementUrls.statusQueryGetUri;
-            const idPlaceholder = this.clientData.managementUrls.id;
-
-            requestUrl = template.replace(
-                idPlaceholder,
-                typeof options.instanceId === "string" ? options.instanceId : ""
-            );
-            if (options.taskHubName) {
-                requestUrl = requestUrl.replace(this.clientData.taskHubName, options.taskHubName);
-            }
-            if (options.connectionName) {
-                requestUrl = requestUrl.replace(
-                    /(connection=)([\w]+)/gi,
-                    "$1" + options.connectionName
-                );
-            }
-            if (options.showHistory) {
-                requestUrl += `&${this.showHistoryQueryKey}=${options.showHistory}`;
-            }
-            if (options.showHistoryOutput) {
-                requestUrl += `&${this.showHistoryOutputQueryKey}=${options.showHistoryOutput}`;
-            }
-            if (options.createdTimeFrom) {
-                requestUrl += `&${
-                    this.createdTimeFromQueryKey
-                }=${options.createdTimeFrom.toISOString()}`;
-            }
-            if (options.createdTimeTo) {
-                requestUrl += `&${
-                    this.createdTimeToQueryKey
-                }=${options.createdTimeTo.toISOString()}`;
-            }
-            if (options.runtimeStatus && options.runtimeStatus.length > 0) {
-                const statusesString = options.runtimeStatus
-                    .map((value) => value.toString())
-                    .reduce((acc, curr, i) => {
-                        return acc + (i > 0 ? "," : "") + curr;
-                    });
-
-                requestUrl += `&${this.runtimeStatusQueryKey}=${statusesString}`;
-            }
-            if (typeof options.showInput === "boolean") {
-                requestUrl += `&${this.showInputQueryKey}=${options.showInput}`;
-            }
-        }
-
-        // If a continuation token is provided, we add it to the request's header
-        let axiosConfig = undefined;
-        if (continuationToken) {
-            axiosConfig = {
-                headers: {
-                    "x-ms-continuation-token": continuationToken,
-                },
-            };
-        }
-
-        // We call the getStatus endpoint and construct a promise callback to handle the recursion
-        // This assumes that, so long as continuation tokens are found, that the http response status
-        // can be safely ignored (either 200 or 202).
-        const response = this.axiosInstance.get(requestUrl, axiosConfig).then((httpResponse) => {
-            // Aggregate results so far
-            const headers = httpResponse.headers;
-            if (prevData) {
-                httpResponse.data = prevData.concat(httpResponse.data);
-            }
-            // If a new continuation token is found, recurse. Otherwise, return the results
-            const token = headers["x-ms-continuation-token"];
-            if (token) {
-                return this.getStatusInternal(options, token, httpResponse.data);
-            }
-            return httpResponse;
-        });
-
-        return response;
-    }
-
-    private createGenericError(response: AxiosResponse<any>): Error {
-        return new Error(
-            `The operation failed with an unexpected status code: ${
-                response.status
-            }. Details: ${JSON.stringify(response.data)}`
-        );
-    }
-}
-
-interface GetStatusInternalOptions {
-    instanceId?: string;
-    taskHubName?: string;
-    connectionName?: string;
-    showHistory?: boolean;
-    showHistoryOutput?: boolean;
-    createdTimeFrom?: Date;
-    createdTimeTo?: Date;
-    runtimeStatus?: OrchestrationRuntimeStatus[];
-    showInput?: boolean;
-}
+// tslint:disable:member-access
+
+import { HttpRequest, HttpResponse } from "@azure/functions";
+import axios, { AxiosInstance, AxiosResponse } from "axios";
+/** @hidden */
+import process = require("process");
+/** @hidden */
+import url = require("url");
+import { isURL } from "validator";
+import {
+    StartNewOptions,
+    GetStatusOptions,
+    OrchestrationFilter,
+    TaskHubOptions,
+    WaitForCompletionOptions,
+} from "durable-functions";
+import * as types from "durable-functions";
+import { WebhookUtils } from "../util/WebhookUtils";
+import { OrchestrationClientInputData } from "./OrchestrationClientInputData";
+import { HttpManagementPayload } from "../http/HttpManagementPayload";
+import { DurableOrchestrationStatus } from "../orchestrations/DurableOrchestrationStatus";
+import { PurgeHistoryResult } from "./PurgeHistoryResult";
+import { EntityId } from "../entities/entityid";
+import { EntityStateResponse } from "../entities/entitystateresponse";
+import { OrchestrationRuntimeStatus } from "../orchestrations/OrchestrationRuntimeStatus";
+import { Utils } from "../util/Utils";
+
+/**
+ * Client for starting, querying, terminating and raising events to
+ * orchestration instances.
+ */
+export class DurableClient implements types.DurableClient {
+    public readonly taskHubName: string;
+    /** @hidden */
+    public readonly uniqueWebhookOrigins: string[];
+
+    private readonly axiosInstance: AxiosInstance;
+
+    private readonly eventNamePlaceholder = "{eventName}";
+    private readonly functionNamePlaceholder = "{functionName}";
+    private readonly instanceIdPlaceholder = "[/{instanceId}]";
+    private readonly reasonPlaceholder = "{text}";
+
+    private readonly createdTimeFromQueryKey = "createdTimeFrom";
+    private readonly createdTimeToQueryKey = "createdTimeTo";
+    private readonly runtimeStatusQueryKey = "runtimeStatus";
+    private readonly showHistoryQueryKey = "showHistory";
+    private readonly showHistoryOutputQueryKey = "showHistoryOutput";
+    private readonly showInputQueryKey = "showInput";
+
+    private urlValidationOptions: ValidatorJS.IsURLOptions = {
+        protocols: ["http", "https"],
+        require_tld: false,
+        require_protocol: true,
+        require_valid_protocol: true,
+    };
+
+    /**
+     * @param clientData The object representing the orchestrationClient input
+     *  binding of the Azure function that will use this client.
+     */
+    constructor(private readonly clientData: OrchestrationClientInputData) {
+        if (!clientData) {
+            throw new TypeError(
+                `clientData: Expected OrchestrationClientInputData but got ${typeof clientData}`
+            );
+        }
+
+        this.axiosInstance = axios.create({
+            validateStatus: (status: number) => status < 600,
+            headers: {
+                post: {
+                    "Content-Type": "application/json",
+                },
+            },
+            maxContentLength: Infinity,
+        });
+        this.taskHubName = this.clientData.taskHubName;
+        this.uniqueWebhookOrigins = this.extractUniqueWebhookOrigins(this.clientData);
+    }
+
+    public createCheckStatusResponse(
+        request: HttpRequest | undefined,
+        instanceId: string
+    ): HttpResponse {
+        const httpManagementPayload: HttpManagementPayload = this.getClientResponseLinks(
+            request,
+            instanceId
+        );
+
+        return new HttpResponse({
+            status: 202,
+            jsonBody: httpManagementPayload,
+            headers: {
+                "Content-Type": "application/json",
+                Location: httpManagementPayload.statusQueryGetUri,
+                "Retry-After": "10",
+            },
+        });
+    }
+
+    public createHttpManagementPayload(instanceId: string): HttpManagementPayload {
+        return this.getClientResponseLinks(undefined, instanceId);
+    }
+
+    public async getStatus(
+        instanceId: string,
+        options: GetStatusOptions = {}
+    ): Promise<DurableOrchestrationStatus> {
+        const internalOptions: GetStatusInternalOptions = {
+            instanceId,
+            ...options,
+        };
+        const response: AxiosResponse = await this.getStatusInternal(internalOptions);
+
+        switch (response.status) {
+            case 200: // instance completed, failed or terminated
+            case 202: // instance running or pending
+            case 400:
+                if (!response.data) {
+                    throw new Error(
+                        `DurableClient error: the Durable Functions extension replied with an empty HTTP ${response.status} response.`
+                    );
+                }
+
+                try {
+                    return new DurableOrchestrationStatus(response.data);
+                } catch (error) {
+                    throw new Error(
+                        `DurableClient error: could not construct a DurableOrchestrationStatus object using the data received from the Durable Functions extension: ${error.message}`
+                    );
+                }
+
+            case 404: // instance not found
+                let msg =
+                    `DurableClient error: Durable Functions extension replied with HTTP 404 response. ` +
+                    `This usually means we could not find any data associated with the instanceId provided: ${instanceId}.`;
+                if (response.data) {
+                    msg += ` Details: ${JSON.stringify(response.data)}`;
+                }
+                throw new Error(msg);
+            case 500: // request failed with unhandled exception (response data contains exception details)
+            default:
+                return Promise.reject(this.createGenericError(response));
+        }
+    }
+
+    public async getStatusAll(): Promise<DurableOrchestrationStatus[]> {
+        const response = await this.getStatusInternal({});
+        switch (response.status) {
+            case 200:
+                return response.data as DurableOrchestrationStatus[];
+            default:
+                return Promise.reject(this.createGenericError(response));
+        }
+    }
+
+    public async getStatusBy(filter: OrchestrationFilter): Promise<DurableOrchestrationStatus[]> {
+        const response = await this.getStatusInternal(filter);
+        switch (response.status) {
+            case 200:
+                return response.data as DurableOrchestrationStatus[];
+            default:
+                return Promise.reject(this.createGenericError(response));
+        }
+    }
+
+    public async purgeInstanceHistory(instanceId: string): Promise<PurgeHistoryResult> {
+        let requestUrl: string;
+        if (this.clientData.rpcBaseUrl) {
+            // Fast local RPC path
+            requestUrl = new URL(`instances/${instanceId}`, this.clientData.rpcBaseUrl).href;
+        } else {
+            // Legacy app frontend path
+            const template = this.clientData.managementUrls.purgeHistoryDeleteUri;
+            const idPlaceholder = this.clientData.managementUrls.id;
+            requestUrl = template.replace(idPlaceholder, instanceId);
+        }
+
+        const response = await this.axiosInstance.delete(requestUrl);
+        switch (response.status) {
+            case 200: // instance found
+                return response.data as PurgeHistoryResult;
+            case 404: // instance not found
+                return new PurgeHistoryResult(0);
+            default:
+                return Promise.reject(this.createGenericError(response));
+        }
+    }
+
+    public async purgeInstanceHistoryBy(filter: OrchestrationFilter): Promise<PurgeHistoryResult> {
+        let requestUrl: string;
+        const { createdTimeFrom, createdTimeTo, runtimeStatus } = filter;
+        if (this.clientData.rpcBaseUrl) {
+            // Fast local RPC path
+            let path = new URL("instances/", this.clientData.rpcBaseUrl).href;
+            const query: string[] = [];
+            if (createdTimeFrom) {
+                query.push(`createdTimeFrom=${createdTimeFrom.toISOString()}`);
+            }
+            if (createdTimeTo) {
+                query.push(`createdTimeTo=${createdTimeTo.toISOString()}`);
+            }
+            if (runtimeStatus && runtimeStatus.length > 0) {
+                const statusList: string = runtimeStatus.map((value) => value.toString()).join(",");
+                query.push(`runtimeStatus=${statusList}`);
+            }
+
+            if (query.length > 0) {
+                path += "?" + query.join("&");
+            }
+
+            requestUrl = new URL(path, this.clientData.rpcBaseUrl).href;
+        } else {
+            // Legacy app frontend path
+            const idPlaceholder = this.clientData.managementUrls.id;
+            requestUrl = this.clientData.managementUrls.statusQueryGetUri.replace(
+                idPlaceholder,
+                ""
+            );
+
+            if (!(createdTimeFrom instanceof Date)) {
+                throw new Error("createdTimeFrom must be a valid Date");
+            }
+
+            if (createdTimeFrom) {
+                requestUrl += `&${this.createdTimeFromQueryKey}=${createdTimeFrom.toISOString()}`;
+            }
+
+            if (createdTimeTo) {
+                requestUrl += `&${this.createdTimeToQueryKey}=${createdTimeTo.toISOString()}`;
+            }
+
+            if (runtimeStatus && runtimeStatus.length > 0) {
+                const statusesString = runtimeStatus
+                    .map((value) => value.toString())
+                    .reduce((acc, curr, i) => {
+                        return acc + (i > 0 ? "," : "") + curr;
+                    });
+
+                requestUrl += `&${this.runtimeStatusQueryKey}=${statusesString}`;
+            }
+        }
+
+        const response = await this.axiosInstance.delete(requestUrl);
+        switch (response.status) {
+            case 200: // instance found
+                return response.data as PurgeHistoryResult;
+            case 404: // instance not found
+                return new PurgeHistoryResult(0);
+            default:
+                return Promise.reject(this.createGenericError(response));
+        }
+    }
+
+    public async raiseEvent(
+        instanceId: string,
+        eventName: string,
+        eventData: unknown,
+        options: TaskHubOptions = {}
+    ): Promise<void> {
+        const { taskHubName, connectionName } = options;
+        if (!eventName) {
+            throw new Error("eventName must be a valid string.");
+        }
+
+        let requestUrl: string;
+        if (this.clientData.rpcBaseUrl) {
+            // Fast local RPC path
+            let path = `instances/${instanceId}/raiseEvent/${eventName}`;
+            const query: string[] = [];
+            if (taskHubName) {
+                query.push(`taskHub=${taskHubName}`);
+            }
+            if (connectionName) {
+                query.push(`connection=${connectionName}`);
+            }
+
+            if (query.length > 0) {
+                path += "?" + query.join("&");
+            }
+
+            requestUrl = new URL(path, this.clientData.rpcBaseUrl).href;
+        } else {
+            // Legacy app frontend path
+            const idPlaceholder = this.clientData.managementUrls.id;
+            requestUrl = this.clientData.managementUrls.sendEventPostUri
+                .replace(idPlaceholder, instanceId)
+                .replace(this.eventNamePlaceholder, eventName);
+
+            if (taskHubName) {
+                requestUrl = requestUrl.replace(this.clientData.taskHubName, taskHubName);
+            }
+
+            if (connectionName) {
+                requestUrl = requestUrl.replace(/(connection=)([\w]+)/gi, "$1" + connectionName);
+            }
+        }
+
+        const response = await this.axiosInstance.post(requestUrl, JSON.stringify(eventData));
+        switch (response.status) {
+            case 202: // event accepted
+            case 410: // instance completed or failed
+                return;
+            case 404:
+                return Promise.reject(new Error(`No instance with ID '${instanceId}' found.`));
+            default:
+                return Promise.reject(this.createGenericError(response));
+        }
+    }
+
+    public async readEntityState<T>(
+        entityId: EntityId,
+        options: TaskHubOptions = {}
+    ): Promise<EntityStateResponse<T>> {
+        let requestUrl: string;
+        const { taskHubName, connectionName } = options;
+        if (this.clientData.rpcBaseUrl) {
+            // Fast local RPC path
+            let path = `entities/${entityId.name}/${entityId.key}`;
+            const query: string[] = [];
+            if (taskHubName) {
+                query.push(`taskHub=${taskHubName}`);
+            }
+            if (connectionName) {
+                query.push(`connection=${connectionName}`);
+            }
+
+            if (query.length > 0) {
+                path += "?" + query.join("&");
+            }
+
+            requestUrl = new URL(path, this.clientData.rpcBaseUrl).href;
+        } else {
+            // Legacy app frontend path
+            if (!(this.clientData.baseUrl && this.clientData.requiredQueryStringParameters)) {
+                throw new Error(
+                    "Cannot use the readEntityState API with this version of the Durable Task Extension."
+                );
+            }
+
+            requestUrl = WebhookUtils.getReadEntityUrl(
+                this.clientData.baseUrl,
+                this.clientData.requiredQueryStringParameters,
+                entityId.name,
+                entityId.key,
+                taskHubName,
+                connectionName
+            );
+        }
+
+        const response = await this.axiosInstance.get<T>(requestUrl);
+        switch (response.status) {
+            case 200: // entity exists
+                return new EntityStateResponse(true, response.data);
+            case 404: // entity does not exist
+                return new EntityStateResponse(false);
+            default:
+                return Promise.reject(this.createGenericError(response));
+        }
+    }
+
+    public async rewind(
+        instanceId: string,
+        reason: string,
+        options: TaskHubOptions = {}
+    ): Promise<void> {
+        const { taskHubName, connectionName } = options;
+        const idPlaceholder = this.clientData.managementUrls.id;
+
+        let requestUrl: string;
+        if (this.clientData.rpcBaseUrl) {
+            // Fast local RPC path
+            let path = `instances/${instanceId}/rewind?reason=${reason}`;
+            const query: string[] = [];
+            if (taskHubName) {
+                query.push(`taskHub=${taskHubName}`);
+            }
+            if (connectionName) {
+                query.push(`connection=${connectionName}`);
+            }
+
+            if (query.length > 0) {
+                path += "&" + query.join("&");
+            }
+
+            requestUrl = new URL(path, this.clientData.rpcBaseUrl).href;
+        } else {
+            // Legacy app frontend path
+            requestUrl = this.clientData.managementUrls.rewindPostUri
+                .replace(idPlaceholder, instanceId)
+                .replace(this.reasonPlaceholder, reason);
+        }
+
+        const response = await this.axiosInstance.post(requestUrl);
+        switch (response.status) {
+            case 202:
+                return;
+            case 404:
+                return Promise.reject(new Error(`No instance with ID '${instanceId}' found.`));
+            case 410:
+                return Promise.reject(
+                    new Error(
+                        "The rewind operation is only supported on failed orchestration instances."
+                    )
+                );
+            default:
+                return Promise.reject(this.createGenericError(response));
+        }
+    }
+
+    public async signalEntity(
+        entityId: EntityId,
+        operationName?: string,
+        operationContent?: unknown,
+        options: TaskHubOptions = {}
+    ): Promise<void> {
+        const { taskHubName, connectionName } = options;
+        let requestUrl: string;
+        if (this.clientData.rpcBaseUrl) {
+            // Fast local RPC path
+            let path = `entities/${entityId.name}/${entityId.key}`;
+            const query: string[] = [];
+            if (operationName) {
+                query.push(`op=${operationName}`);
+            }
+            if (taskHubName) {
+                query.push(`taskHub=${taskHubName}`);
+            }
+            if (connectionName) {
+                query.push(`connection=${connectionName}`);
+            }
+
+            if (query.length > 0) {
+                path += "?" + query.join("&");
+            }
+
+            requestUrl = new URL(path, this.clientData.rpcBaseUrl).href;
+        } else {
+            // Legacy app frontend path
+            if (!(this.clientData.baseUrl && this.clientData.requiredQueryStringParameters)) {
+                throw new Error(
+                    "Cannot use the signalEntity API with this version of the Durable Task Extension."
+                );
+            }
+
+            requestUrl = WebhookUtils.getSignalEntityUrl(
+                this.clientData.baseUrl,
+                this.clientData.requiredQueryStringParameters,
+                entityId.name,
+                entityId.key,
+                operationName,
+                taskHubName,
+                connectionName
+            );
+        }
+
+        const response = await this.axiosInstance.post(
+            requestUrl,
+            JSON.stringify(operationContent)
+        );
+        switch (response.status) {
+            case 202: // signal accepted
+                return;
+            default:
+                return Promise.reject(this.createGenericError(response));
+        }
+    }
+
+    public async startNew(
+        orchestratorFunctionName: string,
+        options?: StartNewOptions
+    ): Promise<string> {
+        if (!orchestratorFunctionName) {
+            throw new Error("orchestratorFunctionName must be a valid string.");
+        }
+
+        // TODO: Add support for specifying a task hub and a connection name
+        let requestUrl: string;
+        const instanceIdPath: string = options?.instanceId ? `/${options.instanceId}` : "";
+        if (this.clientData.rpcBaseUrl) {
+            // Fast local RPC path
+            requestUrl = new URL(
+                `orchestrators/${orchestratorFunctionName}${instanceIdPath}`,
+                this.clientData.rpcBaseUrl
+            ).href;
+        } else {
+            // Legacy app frontend path
+            requestUrl = this.clientData.creationUrls.createNewInstancePostUri;
+            requestUrl = requestUrl
+                .replace(this.functionNamePlaceholder, orchestratorFunctionName)
+                .replace(this.instanceIdPlaceholder, instanceIdPath);
+        }
+
+        const input: unknown = options?.input !== undefined ? JSON.stringify(options.input) : "";
+        const response = await this.axiosInstance.post(requestUrl, input);
+        if (response.data && response.status <= 202) {
+            return (response.data as HttpManagementPayload).id;
+        } else {
+            return Promise.reject(this.createGenericError(response));
+        }
+    }
+
+    public async terminate(instanceId: string, reason: string): Promise<void> {
+        const idPlaceholder = this.clientData.managementUrls.id;
+        let requestUrl: string;
+        if (this.clientData.rpcBaseUrl) {
+            // Fast local RPC path
+            requestUrl = new URL(
+                `instances/${instanceId}/terminate?reason=${reason}`,
+                this.clientData.rpcBaseUrl
+            ).href;
+        } else {
+            // Legacy app frontend path
+            requestUrl = this.clientData.managementUrls.terminatePostUri
+                .replace(idPlaceholder, instanceId)
+                .replace(this.reasonPlaceholder, reason);
+        }
+
+        const response = await this.axiosInstance.post(requestUrl);
+        switch (response.status) {
+            case 202: // terminate accepted
+            case 410: // instance completed or failed
+                return;
+            case 404:
+                return Promise.reject(new Error(`No instance with ID '${instanceId}' found.`));
+            default:
+                return Promise.reject(this.createGenericError(response));
+        }
+    }
+
+    public async waitForCompletionOrCreateCheckStatusResponse(
+        request: HttpRequest,
+        instanceId: string,
+        waitOptions: WaitForCompletionOptions = {}
+    ): Promise<HttpResponse> {
+        const timeoutInMilliseconds: number =
+            waitOptions.timeoutInMilliseconds !== undefined
+                ? waitOptions.timeoutInMilliseconds
+                : 10000;
+        const retryIntervalInMilliseconds: number =
+            waitOptions.retryIntervalInMilliseconds !== undefined
+                ? waitOptions.retryIntervalInMilliseconds
+                : 1000;
+
+        if (retryIntervalInMilliseconds > timeoutInMilliseconds) {
+            throw new Error(
+                `Total timeout ${timeoutInMilliseconds} (ms) should be bigger than retry timeout ${retryIntervalInMilliseconds} (ms)`
+            );
+        }
+
+        const hrStart = process.hrtime();
+        while (true) {
+            const status = await this.getStatus(instanceId);
+
+            if (status) {
+                switch (status.runtimeStatus) {
+                    case OrchestrationRuntimeStatus.Completed:
+                        return this.createHttpResponse(200, status.output);
+                    case OrchestrationRuntimeStatus.Canceled:
+                    case OrchestrationRuntimeStatus.Terminated:
+                        return this.createHttpResponse(200, status);
+                    case OrchestrationRuntimeStatus.Failed:
+                        return this.createHttpResponse(500, status);
+                }
+            }
+
+            const hrElapsed = process.hrtime(hrStart);
+            const hrElapsedMilliseconds = Utils.getHrMilliseconds(hrElapsed);
+
+            if (hrElapsedMilliseconds < timeoutInMilliseconds) {
+                const remainingTime = timeoutInMilliseconds - hrElapsedMilliseconds;
+                await Utils.sleep(
+                    remainingTime > retryIntervalInMilliseconds
+                        ? retryIntervalInMilliseconds
+                        : remainingTime
+                );
+            } else {
+                return this.createCheckStatusResponse(request, instanceId);
+            }
+        }
+    }
+
+    private createHttpResponse(statusCode: number, body: unknown): HttpResponse {
+        return new HttpResponse({
+            status: statusCode,
+            jsonBody: body,
+            headers: {
+                "Content-Type": "application/json",
+            },
+        });
+    }
+
+    private getClientResponseLinks(
+        request: HttpRequest | undefined,
+        instanceId: string
+    ): HttpManagementPayload {
+        const payload = { ...this.clientData.managementUrls };
+
+        (Object.keys(payload) as Array<keyof HttpManagementPayload>).forEach((key) => {
+            if (
+                this.hasValidRequestUrl(request) &&
+                isURL(payload[key], this.urlValidationOptions)
+            ) {
+                const requestUrl = new url.URL((request as HttpRequest).url);
+                const dataUrl = new url.URL(payload[key]);
+                payload[key] = payload[key].replace(dataUrl.origin, requestUrl.origin);
+            }
+
+            payload[key] = payload[key].replace(this.clientData.managementUrls.id, instanceId);
+        });
+
+        return payload;
+    }
+
+    private hasValidRequestUrl(request: HttpRequest | undefined): boolean {
+        return request !== undefined && (request as HttpRequest).url !== undefined;
+    }
+
+    private extractUniqueWebhookOrigins(clientData: OrchestrationClientInputData): string[] {
+        const origins = this.extractWebhookOrigins(clientData.creationUrls).concat(
+            this.extractWebhookOrigins(clientData.managementUrls)
+        );
+
+        const uniqueOrigins = origins.reduce<string[]>((acc, curr) => {
+            if (acc.indexOf(curr) === -1) {
+                acc.push(curr);
+            }
+            return acc;
+        }, []);
+
+        return uniqueOrigins;
+    }
+
+    private extractWebhookOrigins(obj: { [key: string]: string }): string[] {
+        const origins: string[] = [];
+
+        const keys = Object.getOwnPropertyNames(obj);
+        keys.forEach((key) => {
+            const value = obj[key];
+
+            if (isURL(value, this.urlValidationOptions)) {
+                const valueAsUrl = new url.URL(value);
+                const origin = valueAsUrl.origin;
+                origins.push(origin);
+            }
+        });
+
+        return origins;
+    }
+
+    /**
+     * Internal method that gets the status of all orchestration instances that
+     * match the specified conditions. It handles pagination automatically by
+     * calling itself recursively until no more continuation tokens are found.
+     * @param options Return orchestration instances which were created
+     *  after this Date.
+     * @param [continuationToken] Continuation token corresponding to the
+     * `x-ms-continuation-token` header, for getting the next batch
+     * of results. Used for recursion.
+     * @param [prevData] Results of a previous request, used internally
+     * to aggregate results during recursion.
+     */
+    private async getStatusInternal(
+        options: GetStatusInternalOptions,
+        continuationToken?: string,
+        prevData?: unknown[]
+    ): Promise<AxiosResponse> {
+        let requestUrl: string;
+        if (this.clientData.rpcBaseUrl) {
+            // Fast local RPC path
+            let path = new URL(`instances/${options.instanceId || ""}`, this.clientData.rpcBaseUrl)
+                .href;
+            const query: string[] = [];
+            if (options.taskHubName) {
+                query.push(`taskHub=${options.taskHubName}`);
+            }
+            if (options.connectionName) {
+                query.push(`connection=${options.connectionName}`);
+            }
+            if (options.showHistory) {
+                query.push(`showHistory=${options.showHistory}`);
+            }
+            if (options.showHistoryOutput) {
+                query.push(`showHistoryOutput=${options.showHistoryOutput}`);
+            }
+            if (options.showInput) {
+                query.push(`showInput=${options.showInput}`);
+            }
+            if (options.createdTimeFrom) {
+                query.push(`createdTimeFrom=${options.createdTimeFrom.toISOString()}`);
+            }
+            if (options.createdTimeTo) {
+                query.push(`createdTimeTo=${options.createdTimeTo.toISOString()}`);
+            }
+            if (options.runtimeStatus && options.runtimeStatus.length > 0) {
+                const statusList: string = options.runtimeStatus
+                    .map((value) => value.toString())
+                    .join(",");
+                query.push(`runtimeStatus=${statusList}`);
+            }
+
+            if (query.length > 0) {
+                path += "?" + query.join("&");
+            }
+
+            requestUrl = new URL(path, this.clientData.rpcBaseUrl).href;
+        } else {
+            // Legacy app frontend code path
+            const template = this.clientData.managementUrls.statusQueryGetUri;
+            const idPlaceholder = this.clientData.managementUrls.id;
+
+            requestUrl = template.replace(
+                idPlaceholder,
+                typeof options.instanceId === "string" ? options.instanceId : ""
+            );
+            if (options.taskHubName) {
+                requestUrl = requestUrl.replace(this.clientData.taskHubName, options.taskHubName);
+            }
+            if (options.connectionName) {
+                requestUrl = requestUrl.replace(
+                    /(connection=)([\w]+)/gi,
+                    "$1" + options.connectionName
+                );
+            }
+            if (options.showHistory) {
+                requestUrl += `&${this.showHistoryQueryKey}=${options.showHistory}`;
+            }
+            if (options.showHistoryOutput) {
+                requestUrl += `&${this.showHistoryOutputQueryKey}=${options.showHistoryOutput}`;
+            }
+            if (options.createdTimeFrom) {
+                requestUrl += `&${
+                    this.createdTimeFromQueryKey
+                }=${options.createdTimeFrom.toISOString()}`;
+            }
+            if (options.createdTimeTo) {
+                requestUrl += `&${
+                    this.createdTimeToQueryKey
+                }=${options.createdTimeTo.toISOString()}`;
+            }
+            if (options.runtimeStatus && options.runtimeStatus.length > 0) {
+                const statusesString = options.runtimeStatus
+                    .map((value) => value.toString())
+                    .reduce((acc, curr, i) => {
+                        return acc + (i > 0 ? "," : "") + curr;
+                    });
+
+                requestUrl += `&${this.runtimeStatusQueryKey}=${statusesString}`;
+            }
+            if (typeof options.showInput === "boolean") {
+                requestUrl += `&${this.showInputQueryKey}=${options.showInput}`;
+            }
+        }
+
+        // If a continuation token is provided, we add it to the request's header
+        let axiosConfig = undefined;
+        if (continuationToken) {
+            axiosConfig = {
+                headers: {
+                    "x-ms-continuation-token": continuationToken,
+                },
+            };
+        }
+
+        // We call the getStatus endpoint and construct a promise callback to handle the recursion
+        // This assumes that, so long as continuation tokens are found, that the http response status
+        // can be safely ignored (either 200 or 202).
+        const response = this.axiosInstance.get(requestUrl, axiosConfig).then((httpResponse) => {
+            // Aggregate results so far
+            const headers = httpResponse.headers;
+            if (prevData) {
+                httpResponse.data = prevData.concat(httpResponse.data);
+            }
+            // If a new continuation token is found, recurse. Otherwise, return the results
+            const token = headers["x-ms-continuation-token"];
+            if (token) {
+                return this.getStatusInternal(options, token, httpResponse.data);
+            }
+            return httpResponse;
+        });
+
+        return response;
+    }
+
+    private createGenericError(response: AxiosResponse<any>): Error {
+        return new Error(
+            `The operation failed with an unexpected status code: ${
+                response.status
+            }. Details: ${JSON.stringify(response.data)}`
+        );
+    }
+}
+
+interface GetStatusInternalOptions {
+    instanceId?: string;
+    taskHubName?: string;
+    connectionName?: string;
+    showHistory?: boolean;
+    showHistoryOutput?: boolean;
+    createdTimeFrom?: Date;
+    createdTimeTo?: Date;
+    runtimeStatus?: OrchestrationRuntimeStatus[];
+    showInput?: boolean;
+}
diff --git a/src/durableClient/getClient.ts b/src/durableClient/getClient.ts
index bda9eae..2d73499 100644
--- a/src/durableClient/getClient.ts
+++ b/src/durableClient/getClient.ts
@@ -1,6 +1,6 @@
 import { FunctionInput, InvocationContext } from "@azure/functions";
-import { DurableClient, DurableClientInput } from "durable-functions";
-import { DurableOrchestrationClient } from "./DurableOrchestrationClient";
+import { DurableClientInput } from "durable-functions";
+import { DurableClient } from "./DurableClient";
 import { OrchestrationClientInputData } from "./OrchestrationClientInputData";
 /** @hidden */
 import cloneDeep = require("lodash/cloneDeep");
@@ -28,7 +28,7 @@ export function getClient(context: InvocationContext): DurableClient {
         clientData = correctClientData(clientData);
     }
 
-    return new DurableOrchestrationClient(clientData);
+    return new DurableClient(clientData);
 }
 
 /** @hidden */
diff --git a/src/orchestrations/DurableOrchestrationContext.ts b/src/orchestrations/DurableOrchestrationContext.ts
index 71d28cc..3ff446b 100644
--- a/src/orchestrations/DurableOrchestrationContext.ts
+++ b/src/orchestrations/DurableOrchestrationContext.ts
@@ -26,7 +26,7 @@ import { ContinueAsNewAction } from "../actions/continueasnewaction";
 import { CreateTimerAction } from "../actions/createtimeraction";
 import { ExternalEventType } from "../actions/externaleventtype";
 import { WaitForExternalEventAction } from "../actions/waitforexternaleventaction";
-import { GuidManager } from "../guidmanager";
+import { GuidManager } from "../util/GuidManager";
 import { HistoryEvent } from "../history/historyevent";
 import { DurableHttpRequest } from "../http/DurableHttpRequest";
 
diff --git a/src/guidmanager.ts b/src/util/GuidManager.ts
similarity index 93%
rename from src/guidmanager.ts
rename to src/util/GuidManager.ts
index 0b5d5d6..483f59d 100644
--- a/src/guidmanager.ts
+++ b/src/util/GuidManager.ts
@@ -1,27 +1,27 @@
-import * as crypto from "crypto";
-/** @hidden */
-import uuidv5 = require("uuid/v5");
-import { Utils } from "./util/Utils";
-
-/** @hidden */
-export class GuidManager {
-    // I don't anticipate these changing often.
-    public static DnsNamespaceValue = "9e952958-5e33-4daf-827f-2fa12937b875";
-    public static UrlNamespaceValue = "9e952958-5e33-4daf-827f-2fa12937b875";
-    public static IsoOidNamespaceValue = "9e952958-5e33-4daf-827f-2fa12937b875";
-
-    public static createDeterministicGuid(namespaceValue: string, name: string): string {
-        return this.createDeterministicGuidCore(namespaceValue, name);
-    }
-
-    private static createDeterministicGuidCore(namespaceValue: string, name: string): string {
-        Utils.throwIfEmpty(namespaceValue, "namespaceValue");
-        Utils.throwIfEmpty(name, "name");
-
-        const hash = crypto.createHash("sha1");
-        hash.update(name);
-        const bytes: number[] = Array.prototype.slice.call(hash.digest(), 0, 16);
-
-        return uuidv5(namespaceValue, bytes);
-    }
-}
+import * as crypto from "crypto";
+/** @hidden */
+import uuidv5 = require("uuid/v5");
+import { Utils } from "./Utils";
+
+/** @hidden */
+export class GuidManager {
+    // I don't anticipate these changing often.
+    public static DnsNamespaceValue = "9e952958-5e33-4daf-827f-2fa12937b875";
+    public static UrlNamespaceValue = "9e952958-5e33-4daf-827f-2fa12937b875";
+    public static IsoOidNamespaceValue = "9e952958-5e33-4daf-827f-2fa12937b875";
+
+    public static createDeterministicGuid(namespaceValue: string, name: string): string {
+        return this.createDeterministicGuidCore(namespaceValue, name);
+    }
+
+    private static createDeterministicGuidCore(namespaceValue: string, name: string): string {
+        Utils.throwIfEmpty(namespaceValue, "namespaceValue");
+        Utils.throwIfEmpty(name, "name");
+
+        const hash = crypto.createHash("sha1");
+        hash.update(name);
+        const bytes: number[] = Array.prototype.slice.call(hash.digest(), 0, 16);
+
+        return uuidv5(namespaceValue, bytes);
+    }
+}
diff --git a/types/orchestration.d.ts b/types/orchestration.d.ts
index 07a7e60..fc50916 100644
--- a/types/orchestration.d.ts
+++ b/types/orchestration.d.ts
@@ -325,7 +325,7 @@ export declare class DurableOrchestrationStatus {
 
     /**
      * The custom status payload (if any) that was set by
-     * `DurableOrchestrationClient.setCustomStatus()`.
+     * `DurableClient.setCustomStatus()`.
      */
     readonly customStatus?: unknown;
 
@@ -334,7 +334,7 @@ export declare class DurableOrchestrationStatus {
      *
      * The history log can be large and is therefore `undefined` by
      * default. It is populated only when explicitly requested in the call
-     * to `DurableOrchestrationClient.getStatus()`.
+     * to `DurableClient.getStatus()`.
      */
     readonly history?: Array<unknown>;
 }
diff --git a/types/task.d.ts b/types/task.d.ts
index c3b3246..b4c3ddf 100644
--- a/types/task.d.ts
+++ b/types/task.d.ts
@@ -18,7 +18,7 @@ export interface Task {
 }
 
 /**
- * Returned from [[DurableOrchestrationClient]].[[createTimer]] if the call is
+ * Returned from [[DurableClient]].[[createTimer]] if the call is
  * not `yield`-ed. Represents a pending timer. See documentation on [[Task]]
  * for more information.
  *


Test patch:
diff --git a/test/unit/durableclient-spec.ts b/test/unit/durableclient-spec.ts
index 13d921f..dae2746 100644
--- a/test/unit/durableclient-spec.ts
+++ b/test/unit/durableclient-spec.ts
@@ -9,7 +9,7 @@ import { DurableOrchestrationStatus } from "../../src/orchestrations/DurableOrch
 import { EntityId } from "../../src/entities/entityid";
 import { EntityStateResponse } from "../../src/entities/entitystateresponse";
 import { OrchestrationClientInputData } from "../../src/durableClient/OrchestrationClientInputData";
-import { DurableOrchestrationClient } from "../../src/durableClient/DurableOrchestrationClient";
+import { DurableClient } from "../../src/durableClient/DurableClient";
 import { PurgeHistoryResult } from "../../src/durableClient/PurgeHistoryResult";
 
 chai.use(chaiString);
@@ -66,7 +66,7 @@ describe("Durable client RPC endpoint", () => {
     describe("createCheckStatusResponse()", () => {
         it("does NOT reference the RPC endpoint", async () => {
             const input = JSON.parse(durableClientBindingInputJson) as OrchestrationClientInputData;
-            const client = new DurableOrchestrationClient(input);
+            const client = new DurableClient(input);
             const request: HttpRequest = new HttpRequest({
                 method: "GET",
                 url: `${externalOrigin}/api/Foo`,
@@ -95,7 +95,7 @@ describe("Durable client RPC endpoint", () => {
     describe("startNew()", () => {
         it("uses the RPC endpoint", async () => {
             const input = JSON.parse(durableClientBindingInputJson) as OrchestrationClientInputData;
-            const client = new DurableOrchestrationClient(input);
+            const client = new DurableClient(input);
 
             // The startNew() method should do a POST to http://127.0.0.1:17071/durabletask/orchestrators/MyFunction
             const functionName = "MyFunction";
@@ -117,7 +117,7 @@ describe("Durable client RPC endpoint", () => {
     describe("raiseEvent()", () => {
         it("uses the RPC endpoint (no query params)", async () => {
             const input = JSON.parse(durableClientBindingInputJson) as OrchestrationClientInputData;
-            const client = new DurableOrchestrationClient(input);
+            const client = new DurableClient(input);
 
             // The raiseEvent() method should do a POST to http://127.0.0.1:17071/durabletask/instances/abc123/raiseEvent/MyEvent
             // with a application/json payload matching { "value": 5 }.
@@ -139,7 +139,7 @@ describe("Durable client RPC endpoint", () => {
 
         it("uses the RPC endpoint (with query params)", async () => {
             const input = JSON.parse(durableClientBindingInputJson) as OrchestrationClientInputData;
-            const client = new DurableOrchestrationClient(input);
+            const client = new DurableClient(input);
 
             // The raiseEvent() method should do a POST to http://127.0.0.1:17071/durabletask/instances/abc123/raiseEvent/MyEvent?taskHub=hub&connection=Storage
             // with a application/json payload matching { "value": 42 }.
@@ -169,7 +169,7 @@ describe("Durable client RPC endpoint", () => {
     describe("getStatus()", () => {
         it("uses the RPC endpoint (no query params)", async () => {
             const input = JSON.parse(durableClientBindingInputJson) as OrchestrationClientInputData;
-            const client = new DurableOrchestrationClient(input);
+            const client = new DurableClient(input);
 
             // The getStatus() method should do a GET to http://127.0.0.1:17071/durabletask/instances/abc123
             const instanceId = "abc123";
@@ -192,7 +192,7 @@ describe("Durable client RPC endpoint", () => {
 
         it("uses the RPC endpoint (with all query params)", async () => {
             const input = JSON.parse(durableClientBindingInputJson) as OrchestrationClientInputData;
-            const client = new DurableOrchestrationClient(input);
+            const client = new DurableClient(input);
 
             // The getStatus() method should do a GET to http://127.0.0.1:17071/durabletask/instances/abc123?showInput=true&showHistory=true&showHistoryOutput=true
             const instanceId = "abc123";
@@ -229,7 +229,7 @@ describe("Durable client RPC endpoint", () => {
     describe("getStatusBy()", () => {
         it("uses the RPC endpoint (all query params)", async () => {
             const input = JSON.parse(durableClientBindingInputJson) as OrchestrationClientInputData;
-            const client = new DurableOrchestrationClient(input);
+            const client = new DurableClient(input);
 
             // The getStatusBy() method should do a GET to http://127.0.0.1:17071/durabletask/instances/?createdTimeFrom=2020-01-01T00:00:00Z&createdTimeTo=2020-01-01T23:59:59Z&runtimeStatus=Pending,Running,Completed,Terminated,Failed
             const expectedUrl = new URL(`${testRpcOrigin}/durabletask/instances/`);
@@ -261,7 +261,7 @@ describe("Durable client RPC endpoint", () => {
 
         it("uses continuation header", async () => {
             const input = JSON.parse(durableClientBindingInputJson) as OrchestrationClientInputData;
-            const client = new DurableOrchestrationClient(input);
+            const client = new DurableClient(input);
 
             // The getStatusBy() method should do a GET to http://127.0.0.1:17071/durabletask/instances/?createdTimeFrom=2020-01-01T00:00:00Z&createdTimeTo=2020-01-01T23:59:59Z&runtimeStatus=Pending,Running,Completed,Terminated,Failed
             const expectedUrl = new URL(`${testRpcOrigin}/durabletask/instances/`);
@@ -321,7 +321,7 @@ describe("Durable client RPC endpoint", () => {
     describe("terminate()", () => {
         it("uses the RPC endpoint", async () => {
             const input = JSON.parse(durableClientBindingInputJson) as OrchestrationClientInputData;
-            const client = new DurableOrchestrationClient(input);
+            const client = new DurableClient(input);
 
             // The terminate() method should do a POST to http://127.0.0.1:17071/durabletask/instances/abc123/terminate?reason=because
             const instanceId = "abc123";
@@ -342,7 +342,7 @@ describe("Durable client RPC endpoint", () => {
     describe("purgeInstanceHistory[By]()", () => {
         it("uses the RPC endpoint (single instance)", async () => {
             const input = JSON.parse(durableClientBindingInputJson) as OrchestrationClientInputData;
-            const client = new DurableOrchestrationClient(input);
+            const client = new DurableClient(input);
 
             // The purgeInstanceHistory() method should do a DELETE to http://127.0.0.1:17071/durabletask/instances/abc123
             const instanceId = "abc123";
@@ -359,7 +359,7 @@ describe("Durable client RPC endpoint", () => {
 
         it("uses the RPC endpoint (multiple instances)", async () => {
             const input = JSON.parse(durableClientBindingInputJson) as OrchestrationClientInputData;
-            const client = new DurableOrchestrationClient(input);
+            const client = new DurableClient(input);
 
             // The purgeInstanceHistoryBy() method should do a DELETE to
             // http://127.0.0.1:17071/durabletask/instances/?createdTimeFrom=2020-01-01T00:00:00Z&createdTimeTo=2020-01-01T23:59:59Z&runtimeStatus=Pending,Running,Completed,Terminated,Failed
@@ -394,7 +394,7 @@ describe("Durable client RPC endpoint", () => {
     describe("rewind()", () => {
         it("uses the RPC endpoint (no optional query params)", async () => {
             const input = JSON.parse(durableClientBindingInputJson) as OrchestrationClientInputData;
-            const client = new DurableOrchestrationClient(input);
+            const client = new DurableClient(input);
 
             // The rewind() method should do a POST to http://127.0.0.1:17071/durabletask/instances/abc123/rewind?reason=because
             const instanceId = "abc123";
@@ -413,7 +413,7 @@ describe("Durable client RPC endpoint", () => {
 
         it("uses the RPC endpoint (all query params)", async () => {
             const input = JSON.parse(durableClientBindingInputJson) as OrchestrationClientInputData;
-            const client = new DurableOrchestrationClient(input);
+            const client = new DurableClient(input);
 
             // The rewind() method should do a POST to http://127.0.0.1:17071/durabletask/instances/abc123/rewind?reason=because&taskHub=hub&connection=Storage
             const instanceId = "abc123";
@@ -440,7 +440,7 @@ describe("Durable client RPC endpoint", () => {
     describe("signalEntity()", () => {
         it("uses the RPC endpoint (no arguments)", async () => {
             const input = JSON.parse(durableClientBindingInputJson) as OrchestrationClientInputData;
-            const client = new DurableOrchestrationClient(input);
+            const client = new DurableClient(input);
 
             // The signalEntity() method should do a POST to http://127.0.0.1:17071/durabletask/entities/counter/abc123?op=MyEvent
             const entityId = new EntityId("counter", "abc123");
@@ -456,7 +456,7 @@ describe("Durable client RPC endpoint", () => {
 
         it("uses the RPC endpoint (with all arguments)", async () => {
             const input = JSON.parse(durableClientBindingInputJson) as OrchestrationClientInputData;
-            const client = new DurableOrchestrationClient(input);
+            const client = new DurableClient(input);
 
             // The signalEntity() method should do a POST to http://127.0.0.1:17071/durabletask/entities/counter/abc123?op=incr&taskHub=hub&connection=Storage
             // with a application/json payload matching { "value": 42 }.
@@ -486,7 +486,7 @@ describe("Durable client RPC endpoint", () => {
     describe("readEntityState()", () => {
         it("uses the RPC endpoint (no optional params)", async () => {
             const input = JSON.parse(durableClientBindingInputJson) as OrchestrationClientInputData;
-            const client = new DurableOrchestrationClient(input);
+            const client = new DurableClient(input);
 
             // The readEntityState() method should do a GET to http://127.0.0.1:17071/durabletask/entities/counter/abc123
             const entityId = new EntityId("counter", "abc123");
@@ -509,7 +509,7 @@ describe("Durable client RPC endpoint", () => {
 
         it("uses the RPC endpoint (with optional params)", async () => {
             const input = JSON.parse(durableClientBindingInputJson) as OrchestrationClientInputData;
-            const client = new DurableOrchestrationClient(input);
+            const client = new DurableClient(input);
 
             // The readEntityState() method should do a GET to http://127.0.0.1:17071/durabletask/entities/counter/abc123?taskHub=hub&connection=Storage
             const entityId = new EntityId("counter", "abc123");
diff --git a/test/unit/getclient-spec.ts b/test/unit/getclient-spec.ts
index 2f0180a..cf2bf1f 100644
--- a/test/unit/getclient-spec.ts
+++ b/test/unit/getclient-spec.ts
@@ -10,7 +10,7 @@ import { TestUtils } from "../testobjects/testutils";
 import { getClient, input } from "../../src";
 import { Constants } from "../../src/constants";
 import { OrchestrationClientInputData } from "../../src/durableClient/OrchestrationClientInputData";
-import { DurableOrchestrationClient } from "../../src/durableClient/DurableOrchestrationClient";
+import { DurableClient } from "../../src/durableClient/DurableClient";
 
 chai.use(chaiAsPromised);
 
@@ -68,14 +68,14 @@ describe("getClient()", () => {
         );
     });
 
-    it("returns DurableOrchestrationClient if called with valid context", async () => {
+    it("returns DurableClient if called with valid context", async () => {
         const client = getClient(defaultContext);
-        expect(client).to.be.instanceOf(DurableOrchestrationClient);
+        expect(client).to.be.instanceOf(DurableClient);
     });
 
-    it("returns DurableOrchestrationClient if called with V1 context", async () => {
+    it("returns DurableClient if called with V1 context", async () => {
         const client = getClient(v1Context);
-        expect(client).to.be.instanceOf(DurableOrchestrationClient);
+        expect(client).to.be.instanceOf(DurableClient);
     });
 
     describe("Azure/azure-functions-durable-js#28 patch", () => {
diff --git a/test/unit/guidmanager-spec.ts b/test/unit/guidmanager-spec.ts
index dc67c3d..b5cfd08 100644
--- a/test/unit/guidmanager-spec.ts
+++ b/test/unit/guidmanager-spec.ts
@@ -1,7 +1,7 @@
 import { expect } from "chai";
 import "mocha";
 import moment = require("moment");
-import { GuidManager } from "../../src/guidmanager";
+import { GuidManager } from "../../src/util/GuidManager";
 import uuidv1 = require("uuid/v1");
 import { isUUID } from "validator";
 
diff --git a/test/unit/orchestrationclient-spec.ts b/test/unit/orchestrationclient-spec.ts
index 4562d48..391a5f0 100644
--- a/test/unit/orchestrationclient-spec.ts
+++ b/test/unit/orchestrationclient-spec.ts
@@ -16,7 +16,7 @@ import { DurableOrchestrationStatus } from "../../src/orchestrations/DurableOrch
 import { OrchestrationRuntimeStatus } from "../../src/orchestrations/OrchestrationRuntimeStatus";
 import { EntityStateResponse } from "../../src/entities/entitystateresponse";
 import { HttpManagementPayload } from "../../src/http/HttpManagementPayload";
-import { DurableOrchestrationClient } from "../../src/durableClient/DurableOrchestrationClient";
+import { DurableClient } from "../../src/durableClient/DurableClient";
 import { PurgeHistoryResult } from "../../src/durableClient/PurgeHistoryResult";
 
 const expect = chai.expect;
@@ -55,14 +55,14 @@ describe("Orchestration Client", () => {
 
     describe("Properties", () => {
         it("assigns taskHubName", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
             expect(client.taskHubName).to.be.equal(defaultClientInputData.taskHubName);
         });
     });
 
     describe("createCheckStatusResponse()", () => {
         it(`returns a proper response object from request.url`, async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
             const requestObj: HttpRequest = new HttpRequest({
                 method: "GET",
                 url: defaultRequestUrl,
@@ -89,7 +89,7 @@ describe("Orchestration Client", () => {
         });
 
         it("returns a proper response object when request is undefined", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedPayload = TestUtils.createHttpManagementPayload(
                 defaultInstanceId,
@@ -111,7 +111,7 @@ describe("Orchestration Client", () => {
 
     describe("createHttpManagementPayload()", () => {
         it("returns a proper payload", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
             const expectedPayload = TestUtils.createHttpManagementPayload(
                 defaultInstanceId,
                 Constants.DefaultLocalOrigin,
@@ -134,7 +134,7 @@ describe("Orchestration Client", () => {
         });
 
         it("calls expected webhook", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedStatus = new DurableOrchestrationStatus({
                 name: defaultOrchestrationName,
@@ -165,7 +165,7 @@ describe("Orchestration Client", () => {
         });
 
         it("calls expected webhook when showHistory = true", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedStatus = new DurableOrchestrationStatus({
                 name: defaultOrchestrationName,
@@ -198,7 +198,7 @@ describe("Orchestration Client", () => {
         });
 
         it("calls expected webhook when showHistoryOutput = true", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedStatus = new DurableOrchestrationStatus({
                 name: defaultOrchestrationName,
@@ -232,7 +232,7 @@ describe("Orchestration Client", () => {
         });
 
         it("calls expected webhook when showInput = false", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedStatus = new DurableOrchestrationStatus({
                 name: defaultOrchestrationName,
@@ -267,7 +267,7 @@ describe("Orchestration Client", () => {
         });
 
         describe("correctly handles 200 responses", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedWebhookUrl = new url.URL(
                 defaultClientInputData.managementUrls.statusQueryGetUri.replace(
@@ -347,7 +347,7 @@ describe("Orchestration Client", () => {
         });
 
         describe("correctly handles 202 responses", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedWebhookUrl = new url.URL(
                 defaultClientInputData.managementUrls.statusQueryGetUri.replace(
@@ -404,7 +404,7 @@ describe("Orchestration Client", () => {
         });
 
         it("throws on 500 responses", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedWebhookUrl = new url.URL(
                 defaultClientInputData.managementUrls.statusQueryGetUri.replace(
@@ -437,7 +437,7 @@ describe("Orchestration Client", () => {
         });
 
         it("Throws on empty response data", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedWebhookUrl = new url.URL(
                 defaultClientInputData.managementUrls.statusQueryGetUri.replace(
@@ -459,7 +459,7 @@ describe("Orchestration Client", () => {
         });
 
         it("throws on malformed response data", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedWebhookUrl = new url.URL(
                 defaultClientInputData.managementUrls.statusQueryGetUri.replace(
@@ -491,7 +491,7 @@ describe("Orchestration Client", () => {
         });
 
         it("throws on invalid instanceId", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedWebhookUrl = new url.URL(
                 defaultClientInputData.managementUrls.statusQueryGetUri.replace(
@@ -523,7 +523,7 @@ describe("Orchestration Client", () => {
         });
 
         it("calls expected webhook", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedWebhookUrl = new url.URL(
                 defaultClientInputData.managementUrls.statusQueryGetUri.replace(
@@ -566,7 +566,7 @@ describe("Orchestration Client", () => {
         });
 
         it("calls expected webhook with all filters", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const createdTimeTo = new Date();
             const createdTimeFrom = new Date(createdTimeTo.getTime() - 1000 * 60 * 60 * 24 * 3); // last three days
@@ -611,7 +611,7 @@ describe("Orchestration Client", () => {
         });
 
         it("calls expected webhook with some filters", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const runtimeStatuses = [
                 OrchestrationRuntimeStatus.Failed,
@@ -648,7 +648,7 @@ describe("Orchestration Client", () => {
         });
 
         it("calls expected webhook and returns expected result when instance exists", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedResult = new PurgeHistoryResult(1);
             const expectedWebhookUrl = new url.URL(
@@ -671,7 +671,7 @@ describe("Orchestration Client", () => {
         });
 
         it("calls expected webhook and returns expected result when instance does not exist", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedResult = new PurgeHistoryResult(0);
             const expectedWebhookUrl = new url.URL(
@@ -704,7 +704,7 @@ describe("Orchestration Client", () => {
         });
 
         it("calls expected webhook with all filters and returns expected result when instance(s) found", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const createdTimeTo = new Date();
             const createdTimeFrom = new Date(createdTimeTo.getTime() - 1000 * 60 * 60 * 24 * 3); // last three days
@@ -739,7 +739,7 @@ describe("Orchestration Client", () => {
         });
 
         it("calls expected webhook with some filters and returns expected result when no instance(s) found", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const createdTimeTo = new Date();
             const createdTimeFrom = new Date(createdTimeTo.getTime() - 1000 * 60 * 60 * 24 * 3); // last three days
@@ -785,7 +785,7 @@ describe("Orchestration Client", () => {
         });
 
         it("calls expected webhook and completes when event request accepted", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedWebhookUrl = new url.URL(
                 defaultClientInputData.managementUrls.sendEventPostUri
@@ -810,7 +810,7 @@ describe("Orchestration Client", () => {
         });
 
         it("calls expected webhook when task hub specified", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const testTaskHub = "SpecialTaskHub";
             const expectedWebhookUrl = new url.URL(
@@ -839,7 +839,7 @@ describe("Orchestration Client", () => {
         });
 
         it("calls expected webhook when connection specified", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const testConnection = "RainbowConnection";
             const expectedWebhookUrl = new url.URL(
@@ -868,7 +868,7 @@ describe("Orchestration Client", () => {
         });
 
         it("throws when specified instance not found", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const id = "badId";
             const expectedWebhookUrl = new url.URL(
@@ -901,7 +901,7 @@ describe("Orchestration Client", () => {
         });
 
         it("calls expected webhook", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedEntityStateResponse: TestEntityState = {
                 count: 30,
@@ -930,7 +930,7 @@ describe("Orchestration Client", () => {
         });
 
         it("calls expected webhook with specific task hub and connection", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const testTaskHub = "EntityHub";
             const testConn = "EntityConnStr";
@@ -965,7 +965,7 @@ describe("Orchestration Client", () => {
         });
 
         it("returns default EntityStateResponse when if entity does not exist", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedResponse: EntityStateResponse<unknown> = {
                 entityExists: false,
@@ -999,7 +999,7 @@ describe("Orchestration Client", () => {
         });
 
         it("calls expected webhook and completes for valid instance", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const testReason = "test";
             const expectedWebhookUrl = new url.URL(
@@ -1020,7 +1020,7 @@ describe("Orchestration Client", () => {
         });
 
         it(`throws when webhook returns invalid status code 404`, async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const testId = "badId";
             const testReason = "test";
@@ -1043,7 +1043,7 @@ describe("Orchestration Client", () => {
         });
 
         it(`throws when webhook returns invalid status code 410`, async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const testId = "badId";
             const testReason = "test";
@@ -1066,7 +1066,7 @@ describe("Orchestration Client", () => {
         });
 
         it(`throws when webhook returns invalid status code 500`, async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const testId = "badId";
             const testReason = "test";
@@ -1101,7 +1101,7 @@ describe("Orchestration Client", () => {
         it("calls expected webhook", async () => {
             const testSignalData = { data: "foo" };
 
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedWebhookUrl = new url.URL(
                 `${defaultClientInputData.baseUrl}/entities/${defaultEntityName}/${defaultEntityKey}?op=${defaultEntityOp}&${TestConstants.testCode}`
@@ -1128,7 +1128,7 @@ describe("Orchestration Client", () => {
             const testConn = "EntityConnStr";
             const testSignalData = { data: "foo" };
 
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedWebhookUrl = new url.URL(
                 `${defaultClientInputData.baseUrl}/entities/${defaultEntityName}/${defaultEntityKey}?op=${defaultEntityOp}&taskHub=${testTaskHub}&connection=${testConn}&${TestConstants.testCode}`
@@ -1157,7 +1157,7 @@ describe("Orchestration Client", () => {
         it("calls expected webhook with empty operation", async () => {
             const testSignalData = { data: "foo" };
 
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedWebhookUrl = new url.URL(
                 `${defaultClientInputData.baseUrl}/entities/${defaultEntityName}/${defaultEntityKey}?${TestConstants.testCode}`
@@ -1188,7 +1188,7 @@ describe("Orchestration Client", () => {
         });
 
         it("starts new instance with random id and no input", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const functionName = defaultOrchestrationName;
             const expectedWebhookUrl = createInstanceWebhookUrl(
@@ -1208,7 +1208,7 @@ describe("Orchestration Client", () => {
         });
 
         it("starts new instance with specific id and input", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const functionName = defaultOrchestrationName;
             const testData = { key: "value" };
@@ -1234,7 +1234,7 @@ describe("Orchestration Client", () => {
         });
 
         it("throws if webhook client returns error", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const functionName = "BadOrchestration";
             const functionBody = "Something went wrong!";
@@ -1264,7 +1264,7 @@ describe("Orchestration Client", () => {
         });
 
         it("calls expected webhook and completes for valid instance", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const testReason = "test";
             const expectedWebhookUrl = new url.URL(
@@ -1285,7 +1285,7 @@ describe("Orchestration Client", () => {
         });
 
         it(`throws when webhook returns invalid status code 404`, async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const id = "badId";
             const testReason = "test";
@@ -1308,7 +1308,7 @@ describe("Orchestration Client", () => {
         });
 
         it(`throws when webhook returns invalid status code 500`, async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const id = "badId";
             const testReason = "test";
@@ -1351,7 +1351,7 @@ describe("Orchestration Client", () => {
         });
 
         it("throws when retryIntervalInMilliseconds > timeoutInMilliseconds", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const badInterval = 1e6;
 
@@ -1370,7 +1370,7 @@ describe("Orchestration Client", () => {
         });
 
         it("returns expected result for completed instance", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedOutput = 42;
 
@@ -1402,7 +1402,7 @@ describe("Orchestration Client", () => {
         });
 
         it("returns expected result for canceled instance", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedStatus = new DurableOrchestrationStatus({
                 name: defaultOrchestrationName,
@@ -1434,7 +1434,7 @@ describe("Orchestration Client", () => {
         });
 
         it("returns expected result for terminated instance", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedStatus = new DurableOrchestrationStatus({
                 name: defaultOrchestrationName,
@@ -1466,7 +1466,7 @@ describe("Orchestration Client", () => {
         });
 
         it("returns expected result for failed instance", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedStatus = new DurableOrchestrationStatus({
                 name: defaultOrchestrationName,
@@ -1498,7 +1498,7 @@ describe("Orchestration Client", () => {
         });
 
         it("continues polling for running instance", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const expectedOutput = 42;
 
@@ -1556,7 +1556,7 @@ describe("Orchestration Client", () => {
         });
 
         it("returns check status response if timeout expires", async () => {
-            const client = new DurableOrchestrationClient(defaultClientInputData);
+            const client = new DurableClient(defaultClientInputData);
 
             const runningStatus = new DurableOrchestrationStatus({
                 name: defaultOrchestrationName,