44// operations, like connecting accounts via Pipedream Connect. See the server/
55// directory for the server client.
66
7+ import {
8+ BaseClient , ConnectTokenResponse ,
9+ } from "../shared" ;
10+
711/**
812 * Options for creating a browser-side client. This is used to configure the
913 * BrowserClient instance.
@@ -20,8 +24,31 @@ type CreateBrowserClientOpts = {
2024 * "pipedream.com" if not provided.
2125 */
2226 frontendHost ?: string ;
27+
28+ /**
29+ * The API host URL. Used by Pipedream employees. Defaults to
30+ * "api.pipedream.com" if not provided.
31+ */
32+ apiHost ?: string ;
33+
34+ /**
35+ * Will be called whenever we need a new token.
36+ *
37+ * The callback function should return the response from
38+ * `serverClient.createConnectToken`.
39+ */
40+ tokenCallback ?: TokenCallback ;
41+
42+ /**
43+ * An external user ID associated with the token.
44+ */
45+ externalUserId ?: string ;
2346} ;
2447
48+ export type TokenCallback = ( opts : {
49+ externalUserId : string ;
50+ } ) => Promise < ConnectTokenResponse > ;
51+
2552/**
2653 * The name slug for an app, a unique, human-readable identifier like "github"
2754 * or "google_sheets". Find this in the Authentication section for any app's
@@ -51,8 +78,10 @@ class ConnectError extends Error {}
5178type StartConnectOpts = {
5279 /**
5380 * The token used for authenticating the connection.
81+ *
82+ * Optional if client already initialized with token
5483 */
55- token : string ;
84+ token ? : string ;
5685
5786 /**
5887 * The app to connect to, either as an ID or an object containing the ID.
@@ -98,22 +127,48 @@ export function createFrontendClient(opts: CreateBrowserClientOpts = {}) {
98127/**
99128 * A client for interacting with the Pipedream Connect API from the browser.
100129 */
101- class BrowserClient {
102- private environment ?: string ;
130+ export class BrowserClient extends BaseClient {
103131 private baseURL : string ;
104132 private iframeURL : string ;
105133 private iframe ?: HTMLIFrameElement ;
106134 private iframeId = 0 ;
135+ private tokenCallback ?: TokenCallback ;
136+ private _token ?: string ;
137+ externalUserId ?: string ;
107138
108139 /**
109140 * Constructs a new `BrowserClient` instance.
110141 *
111142 * @param opts - The options for configuring the browser client.
112143 */
113144 constructor ( opts : CreateBrowserClientOpts ) {
114- this . environment = opts . environment ;
145+ super ( opts ) ;
115146 this . baseURL = `https://${ opts . frontendHost || "pipedream.com" } ` ;
116147 this . iframeURL = `${ this . baseURL } /_static/connect.html` ;
148+ this . tokenCallback = opts . tokenCallback ;
149+ this . externalUserId = opts . externalUserId ;
150+ }
151+
152+ private async token ( ) {
153+ // TODO: handle token expiration
154+ if ( this . _token ) {
155+ return this . _token ;
156+ }
157+ if ( this . tokenCallback ) {
158+ if ( ! this . externalUserId ) {
159+ throw new Error ( "No external user ID provided" ) ;
160+ }
161+ const token = await this . tokenCallback ( {
162+ externalUserId : this . externalUserId ,
163+ } ) ;
164+ this . _token = token . token ;
165+ return token . token ;
166+ }
167+ throw new Error ( "No token provided" ) ;
168+ }
169+
170+ private refreshToken ( ) {
171+ this . _token = undefined ;
117172 }
118173
119174 /**
@@ -161,6 +216,7 @@ class BrowserClient {
161216 } catch ( err ) {
162217 opts . onError ?.( err as ConnectError ) ;
163218 }
219+ this . refreshToken ( ) ; // token is used only once
164220 }
165221
166222 /**
@@ -182,9 +238,10 @@ class BrowserClient {
182238 *
183239 * @throws {ConnectError } If the app option is not a string.
184240 */
185- private createIframe ( opts : StartConnectOpts ) {
241+ private async createIframe ( opts : StartConnectOpts ) {
242+ const token = opts . token || ( await this . token ( ) ) ;
186243 const qp = new URLSearchParams ( {
187- token : opts . token ,
244+ token,
188245 } ) ;
189246
190247 if ( this . environment ) {
@@ -216,4 +273,11 @@ class BrowserClient {
216273
217274 document . body . appendChild ( iframe ) ;
218275 }
276+
277+ protected async authHeaders ( ) : Promise < string > {
278+ if ( ! ( await this . token ( ) ) ) {
279+ throw new Error ( "No token provided" ) ;
280+ }
281+ return `Bearer ${ await this . token ( ) } ` ;
282+ }
219283}
0 commit comments