diff --git a/.fernignore b/.fernignore
index 11e5282..9db674a 100644
--- a/.fernignore
+++ b/.fernignore
@@ -5,6 +5,9 @@
.prettierrc.yml
LICENSE
+src/api/resources/index.ts
src/api/resources/proxy/client/*
+src/api/resources/workflows/client/*
+src/api/types/index.ts
src/index.ts
src/wrapper
diff --git a/package.json b/package.json
index 89d8150..6a8c6e3 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@pipedream/sdk",
- "version": "2.0.0",
+ "version": "2.0.0-rc.1",
"private": false,
"repository": "github:PipedreamHQ/pipedream-sdk-typescript",
"type": "commonjs",
@@ -39,17 +39,17 @@
"test:wire": "jest --selectProjects wire"
},
"devDependencies": {
- "webpack": "^5.97.1",
- "ts-loader": "^9.5.1",
- "jest": "^29.7.0",
"@jest/globals": "^29.7.0",
"@types/jest": "^29.5.14",
- "ts-jest": "^29.3.4",
+ "@types/node": "^18.19.70",
+ "jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"msw": "^2.8.4",
- "@types/node": "^18.19.70",
"prettier": "^3.4.2",
- "typescript": "~5.7.2"
+ "ts-jest": "^29.3.4",
+ "ts-loader": "^9.5.1",
+ "typescript": "~5.7.2",
+ "webpack": "^5.97.1"
},
"browser": {
"fs": false,
diff --git a/reference.md b/reference.md
index 9e65bab..3ffa14e 100644
--- a/reference.md
+++ b/reference.md
@@ -2291,3 +2291,139 @@ await client.oauthTokens.create({
+
+## Workflows
+
+client.workflows.invoke({ ...params }, authType?) -> unknown
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```typescript
+// Invoke with URL
+await client.workflows.invoke({
+ urlOrEndpoint: "https://en-your-endpoint.m.pipedream.net",
+ body: {
+ foo: 123,
+ bar: "abc",
+ baz: null,
+ },
+ headers: {
+ Accept: "application/json",
+ },
+});
+
+// Invoke with endpoint ID
+await client.workflows.invoke({
+ urlOrEndpoint: "en123",
+ body: {
+ message: "Hello, World\!",
+ },
+}, Pipedream.HTTPAuthType.OAuth);
+```
+
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**request:** `Pipedream.InvokeWorkflowOpts`
+
+
+
+
+
+-
+
+**authType:** `Pipedream.HTTPAuthType` — The type of authorization to use for the request (defaults to None)
+
+
+
+
+
+-
+
+**requestOptions:** `Workflows.RequestOptions`
+
+
+
+
+
+
+
+
+
+
+client.workflows.invokeForExternalUser({ ...params }) -> unknown
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```typescript
+await client.workflows.invokeForExternalUser({
+ urlOrEndpoint: "https://your-workflow-url.m.pipedream.net",
+ externalUserId: "your-external-user-id",
+ body: {
+ foo: 123,
+ bar: "abc",
+ baz: null,
+ },
+ headers: {
+ Accept: "application/json",
+ },
+});
+```
+
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**request:** `Pipedream.InvokeWorkflowForExternalUserOpts`
+
+
+
+
+
+-
+
+**requestOptions:** `Workflows.RequestOptions`
+
+
+
+
+
+
+
+
+
diff --git a/src/Client.ts b/src/Client.ts
index 6cece9c..8a1fab1 100644
--- a/src/Client.ts
+++ b/src/Client.ts
@@ -51,7 +51,7 @@ export declare namespace PipedreamClient {
export class PipedreamClient {
protected readonly _options: PipedreamClient.Options;
- private readonly _oauthTokenProvider: core.OAuthTokenProvider;
+ protected readonly _oauthTokenProvider: core.OAuthTokenProvider;
protected _appCategories: AppCategories | undefined;
protected _apps: Apps | undefined;
protected _accounts: Accounts | undefined;
diff --git a/src/api/resources/index.ts b/src/api/resources/index.ts
index e98254e..8de5037 100644
--- a/src/api/resources/index.ts
+++ b/src/api/resources/index.ts
@@ -11,6 +11,7 @@ export * as projects from "./projects/index.js";
export * as proxy from "./proxy/index.js";
export * as tokens from "./tokens/index.js";
export * as oauthTokens from "./oauthTokens/index.js";
+export * as workflows from "./workflows/index.js";
export * from "./apps/client/requests/index.js";
export * from "./accounts/client/requests/index.js";
export * from "./components/client/requests/index.js";
@@ -20,3 +21,4 @@ export * from "./deployedTriggers/client/requests/index.js";
export * from "./proxy/client/requests/index.js";
export * from "./tokens/client/requests/index.js";
export * from "./oauthTokens/client/requests/index.js";
+export * from "./workflows/client/requests/index.js";
diff --git a/src/api/resources/workflows/client/Client.ts b/src/api/resources/workflows/client/Client.ts
new file mode 100644
index 0000000..be17955
--- /dev/null
+++ b/src/api/resources/workflows/client/Client.ts
@@ -0,0 +1,278 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+
+import * as environments from "../../../../environments.js";
+import * as core from "../../../../core/index.js";
+import * as Pipedream from "../../../index.js";
+import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../core/headers.js";
+import * as errors from "../../../../errors/index.js";
+
+export declare namespace Workflows {
+ export interface Options {
+ environment?: core.Supplier;
+ /** Specify a custom URL to connect the client to. */
+ baseUrl?: core.Supplier;
+ projectId: string;
+ token?: core.Supplier;
+ /** Override the x-pd-environment header */
+ projectEnvironment?: core.Supplier;
+ /** Additional headers to include in requests. */
+ headers?: Record | undefined>;
+ /** Base domain for workflows. Used for custom domains. */
+ workflowDomain?: string;
+ }
+
+ export interface RequestOptions {
+ /** The maximum time to wait for a response in seconds. */
+ timeoutInSeconds?: number;
+ /** The number of times to retry the request. Defaults to 2. */
+ maxRetries?: number;
+ /** A hook to abort the request. */
+ abortSignal?: AbortSignal;
+ /** Override the x-pd-environment header */
+ projectEnvironment?: Pipedream.ProjectEnvironment | undefined;
+ /** Additional query string parameters to include in the request. */
+ queryParams?: Record;
+ /** Additional headers to include in the request. */
+ headers?: Record | undefined>;
+ }
+}
+
+export class Workflows {
+ protected readonly _options: Workflows.Options;
+ private readonly workflowDomain: string;
+
+ constructor(_options: Workflows.Options) {
+ this._options = _options;
+ this.workflowDomain = _options.workflowDomain ?? this._defaultWorkflowDomain;
+ }
+
+ private get _defaultWorkflowDomain(): string {
+ return this._options.environment !== environments.PipedreamEnvironment.Prod &&
+ this._options.environment !== environments.PipedreamEnvironment.Canary
+ ? "m.d.pipedream.net"
+ : "m.pipedream.net";
+ }
+
+ private get _urlProtocol(): string {
+ return this._options.environment !== environments.PipedreamEnvironment.Prod &&
+ this._options.environment !== environments.PipedreamEnvironment.Canary
+ ? "http"
+ : "https";
+ }
+
+ /**
+ * Invokes a workflow using the URL of its HTTP interface(s), by sending an
+ * HTTP request.
+ *
+ * @param {Pipedream.InvokeWorkflowOpts} request
+ * @param {Pipedream.HTTPAuthType} authType - The type of authorization to use
+ * for the request (defaults to None).
+ * @param {Workflows.RequestOptions} requestOptions - Request-specific
+ * configuration.
+ *
+ * @example
+ * await client.workflows.invoke({
+ * urlOrEndpoint: "https://en-your-endpoint.m.pipedream.net",
+ * body: {
+ * foo: 123,
+ * bar: "abc",
+ * baz: null
+ * },
+ * headers: {
+ * "Accept": "application/json"
+ * }
+ * }, Pipedream.HTTPAuthType.OAuth)
+ */
+ public invoke(
+ request: Pipedream.InvokeWorkflowOpts,
+ authType: Pipedream.HTTPAuthType = Pipedream.HTTPAuthType.None,
+ requestOptions?: Workflows.RequestOptions,
+ ): core.HttpResponsePromise {
+ return core.HttpResponsePromise.fromPromise(this.__invoke(request, authType, requestOptions));
+ }
+
+ private async __invoke(
+ request: Pipedream.InvokeWorkflowOpts,
+ authType: Pipedream.HTTPAuthType = Pipedream.HTTPAuthType.None,
+ requestOptions?: Workflows.RequestOptions,
+ ): Promise> {
+ const { urlOrEndpoint, body, method = "POST", headers = {} } = request;
+
+ const url = this._buildWorkflowUrl(urlOrEndpoint);
+
+ let authHeader: string | undefined;
+ switch (authType) {
+ case Pipedream.HTTPAuthType.StaticBearer:
+ // It's expected that users will pass their own Authorization header in
+ // the static bearer case
+ authHeader = headers["Authorization"];
+ break;
+ case Pipedream.HTTPAuthType.OAuth:
+ authHeader = await this._getAuthorizationHeader();
+ break;
+ default:
+ break;
+ }
+
+ const _response = await core.fetcher({
+ url,
+ method: method.toUpperCase(),
+ headers: mergeHeaders(
+ this._options?.headers,
+ mergeOnlyDefinedHeaders({
+ Authorization: authHeader,
+ "x-pd-environment": requestOptions?.projectEnvironment,
+ }),
+ headers,
+ requestOptions?.headers,
+ ),
+ contentType: body != null ? "application/json" : undefined,
+ queryParameters: requestOptions?.queryParams,
+ requestType: body != null ? "json" : undefined,
+ body,
+ timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000,
+ maxRetries: requestOptions?.maxRetries,
+ abortSignal: requestOptions?.abortSignal,
+ });
+
+ if (!_response.ok) {
+ throw new errors.PipedreamError({
+ message: _response.error.reason,
+ statusCode: _response.rawResponse.status,
+ rawResponse: _response.rawResponse,
+ });
+ }
+
+ return {
+ data: _response.rawResponse,
+ rawResponse: _response.rawResponse,
+ };
+ }
+
+ /**
+ * Invokes a workflow for a Pipedream Connect user in a project.
+ *
+ * @param {Pipedream.InvokeWorkflowForExternalUserOpts} request
+ * @param {Workflows.RequestOptions} requestOptions - Request-specific
+ * configuration.
+ *
+ * @example
+ * await client.workflows.invokeForExternalUser({
+ * urlOrEndpoint: "https://your-workflow-url.m.pipedream.net",
+ * externalUserId: "your-external-user-id",
+ * body: {
+ * foo: 123,
+ * bar: "abc",
+ * baz: null
+ * },
+ * headers: {
+ * "Accept": "application/json"
+ * }
+ * })
+ */
+ public invokeForExternalUser(
+ request: Pipedream.InvokeWorkflowForExternalUserOpts,
+ requestOptions?: Workflows.RequestOptions,
+ ): core.HttpResponsePromise {
+ return core.HttpResponsePromise.fromPromise(this.__invokeForExternalUser(request, requestOptions));
+ }
+
+ private async __invokeForExternalUser(
+ request: Pipedream.InvokeWorkflowForExternalUserOpts,
+ requestOptions?: Workflows.RequestOptions,
+ ): Promise> {
+ const { urlOrEndpoint, externalUserId, body, method, headers = {} } = request;
+
+ if (!externalUserId?.trim()) {
+ throw new Error("External user ID is required");
+ }
+
+ if (!urlOrEndpoint.trim()) {
+ throw new Error("Workflow URL or endpoint ID is required");
+ }
+
+ const authHeader = await this._getAuthorizationHeader();
+ if (!authHeader) {
+ throw new Error(
+ "OAuth or token is required for invoking workflows for external users. Please pass credentials for a valid OAuth client",
+ );
+ }
+
+ // Delegate to invoke method with external user ID header and OAuth auth
+ return this.__invoke(
+ {
+ urlOrEndpoint,
+ body,
+ method,
+ headers: {
+ ...headers,
+ "X-PD-External-User-ID": externalUserId,
+ },
+ },
+ Pipedream.HTTPAuthType.OAuth,
+ requestOptions,
+ );
+ }
+
+ /**
+ * Builds a full workflow URL based on the input.
+ *
+ * @param input - Either a full URL (with or without protocol) or just an
+ * endpoint ID.
+ * @returns The fully constructed URL.
+ * @throws If the input is a malformed URL, throws an error with a clear
+ * message.
+ */
+ private _buildWorkflowUrl(input: string): string {
+ const sanitizedInput = input
+ .trim()
+ .replace(/[^\w-./:]/g, "")
+ .toLowerCase();
+ if (!sanitizedInput) {
+ throw new Error("URL or endpoint ID is required");
+ }
+
+ let url: string;
+ const isUrl = sanitizedInput.includes(".") || sanitizedInput.startsWith("http");
+
+ if (isUrl) {
+ // Try to parse the input as a URL
+ let parsedUrl: URL;
+ try {
+ const urlString = sanitizedInput.startsWith("http") ? sanitizedInput : `https://${sanitizedInput}`;
+ parsedUrl = new URL(urlString);
+ } catch {
+ throw new Error(`The provided URL is malformed: "${sanitizedInput}". Please provide a valid URL.`);
+ }
+
+ // Validate the hostname to prevent potential DNS rebinding attacks
+ if (!parsedUrl.hostname.endsWith(this.workflowDomain)) {
+ throw new Error(`Invalid workflow domain. URL must end with ${this.workflowDomain}`);
+ }
+
+ url = parsedUrl.href;
+ } else {
+ // If the input is an ID, construct the full URL using the base domain
+ if (!/^e(n|o)[a-z0-9-]+$/i.test(sanitizedInput)) {
+ throw new Error(
+ `Invalid endpoint ID format. Must contain only letters, numbers, and hyphens, and start with either "en" or "eo".`,
+ );
+ }
+
+ url = `${this._urlProtocol}://${sanitizedInput}.${this.workflowDomain}`;
+ }
+
+ return url;
+ }
+
+ protected async _getAuthorizationHeader(): Promise {
+ const bearer = await core.Supplier.get(this._options.token);
+ if (bearer != null) {
+ return `Bearer ${bearer}`;
+ }
+
+ return undefined;
+ }
+}
diff --git a/src/api/resources/workflows/client/index.ts b/src/api/resources/workflows/client/index.ts
new file mode 100644
index 0000000..10df5b2
--- /dev/null
+++ b/src/api/resources/workflows/client/index.ts
@@ -0,0 +1,2 @@
+export * from "./Client.js";
+export * from "./requests/index.js";
diff --git a/src/api/resources/workflows/client/requests/InvokeWorkflowForExternalUserOpts.ts b/src/api/resources/workflows/client/requests/InvokeWorkflowForExternalUserOpts.ts
new file mode 100644
index 0000000..c05b417
--- /dev/null
+++ b/src/api/resources/workflows/client/requests/InvokeWorkflowForExternalUserOpts.ts
@@ -0,0 +1,26 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+
+export interface InvokeWorkflowForExternalUserOpts {
+ /**
+ * The URL of the workflow's HTTP interface, or the ID of the endpoint.
+ */
+ urlOrEndpoint: string;
+ /**
+ * Your end user ID, for whom you're invoking the workflow.
+ */
+ externalUserId: string;
+ /**
+ * The body of the request. It must be a JSON-serializable value (e.g. an object, null, a string, etc.).
+ */
+ body?: unknown;
+ /**
+ * HTTP method to use for the request (defaults to POST if not specified).
+ */
+ method?: string;
+ /**
+ * Additional headers to include in the request.
+ */
+ headers?: Record;
+}
diff --git a/src/api/resources/workflows/client/requests/InvokeWorkflowOpts.ts b/src/api/resources/workflows/client/requests/InvokeWorkflowOpts.ts
new file mode 100644
index 0000000..570df77
--- /dev/null
+++ b/src/api/resources/workflows/client/requests/InvokeWorkflowOpts.ts
@@ -0,0 +1,22 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+
+export interface InvokeWorkflowOpts {
+ /**
+ * The URL of the workflow's HTTP interface, or the ID of the endpoint.
+ */
+ urlOrEndpoint: string;
+ /**
+ * The body of the request. It must be a JSON-serializable value (e.g. an object, null, a string, etc.).
+ */
+ body?: unknown;
+ /**
+ * HTTP method to use for the request (defaults to POST if not specified).
+ */
+ method?: string;
+ /**
+ * Additional headers to include in the request.
+ */
+ headers?: Record;
+}
diff --git a/src/api/resources/workflows/client/requests/index.ts b/src/api/resources/workflows/client/requests/index.ts
new file mode 100644
index 0000000..99a5d0a
--- /dev/null
+++ b/src/api/resources/workflows/client/requests/index.ts
@@ -0,0 +1,2 @@
+export { type InvokeWorkflowOpts } from "./InvokeWorkflowOpts.js";
+export { type InvokeWorkflowForExternalUserOpts } from "./InvokeWorkflowForExternalUserOpts.js";
diff --git a/src/api/resources/workflows/index.ts b/src/api/resources/workflows/index.ts
new file mode 100644
index 0000000..914b8c3
--- /dev/null
+++ b/src/api/resources/workflows/index.ts
@@ -0,0 +1 @@
+export * from "./client/index.js";
diff --git a/src/api/types/HTTPAuthType.ts b/src/api/types/HTTPAuthType.ts
new file mode 100644
index 0000000..5cf0500
--- /dev/null
+++ b/src/api/types/HTTPAuthType.ts
@@ -0,0 +1,12 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+
+/**
+ * Different ways in which customers can authorize requests to HTTP endpoints
+ */
+export enum HTTPAuthType {
+ None = "none",
+ StaticBearer = "static_bearer_token",
+ OAuth = "oauth",
+}
diff --git a/src/api/types/index.ts b/src/api/types/index.ts
index ec4f2ae..94e5645 100644
--- a/src/api/types/index.ts
+++ b/src/api/types/index.ts
@@ -22,6 +22,7 @@ export * from "./CreateBrowserClientOpts.js";
export * from "./CreateOAuthTokenResponse.js";
export * from "./CreateTokenResponse.js";
export * from "./DeleteTriggerOpts.js";
+export * from "./HTTPAuthType.js";
export * from "./DeployedComponent.js";
export * from "./DeployTriggerResponse.js";
export * from "./EmittedEvent.js";
diff --git a/src/wrapper/Pipedream.ts b/src/wrapper/Pipedream.ts
index ad4d580..0623217 100644
--- a/src/wrapper/Pipedream.ts
+++ b/src/wrapper/Pipedream.ts
@@ -1,4 +1,5 @@
import { ProjectEnvironment } from "../api/index.js";
+import { Workflows } from "../api/resources/workflows/client/Client.js";
import { PipedreamClient } from "../Client.js";
import * as environments from "../environments.js";
@@ -8,6 +9,7 @@ export interface BackendOpts {
environment?: environments.PipedreamEnvironment;
projectEnvironment?: ProjectEnvironment;
projectId: string;
+ workflowDomain?: string;
}
function expandEnvVars(template: string) {
@@ -15,6 +17,9 @@ function expandEnvVars(template: string) {
}
export class Pipedream extends PipedreamClient {
+ private _workflowDomain?: string;
+ private _workflows: Workflows | undefined;
+
public constructor(opts: BackendOpts) {
const {
clientId = process.env.PIPEDREAM_CLIENT_ID,
@@ -22,6 +27,7 @@ export class Pipedream extends PipedreamClient {
environment: rawEnvironment = environments.PipedreamEnvironment.Prod,
projectEnvironment = process.env.PIPEDREAM_PROJECT_ENVIRONMENT ?? "production",
projectId = process.env.PIPEDREAM_PROJECT_ID,
+ workflowDomain,
} = opts;
if (!projectEnvironment) {
throw new Error("Project environment cannot be empty");
@@ -42,5 +48,15 @@ export class Pipedream extends PipedreamClient {
projectEnvironment,
projectId,
});
+
+ this._workflowDomain = workflowDomain;
+ }
+
+ public get workflows(): Workflows {
+ return (this._workflows ??= new Workflows({
+ ...this._options,
+ token: async () => await this._oauthTokenProvider.getToken(),
+ workflowDomain: this._workflowDomain,
+ }));
}
}