@refoldai/refold-js 10.0.0 → 10.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CLAUDE.md +7 -5
  2. package/docs/assets/hierarchy.js +1 -1
  3. package/docs/assets/icons.js +1 -1
  4. package/docs/assets/icons.svg +1 -1
  5. package/docs/assets/main.js +3 -3
  6. package/docs/assets/navigation.js +1 -1
  7. package/docs/assets/search.js +1 -1
  8. package/docs/classes/Refold.html +29 -27
  9. package/docs/enums/AuthStatus.html +2 -2
  10. package/docs/enums/AuthType.html +2 -2
  11. package/docs/hierarchy.html +1 -1
  12. package/docs/interfaces/Application.html +14 -14
  13. package/docs/interfaces/Config.html +2 -2
  14. package/docs/interfaces/ConfigField.html +4 -4
  15. package/docs/interfaces/ConfigPayload.html +4 -4
  16. package/docs/interfaces/ConfigWorkflow.html +2 -2
  17. package/docs/interfaces/ConnectParams.html +11 -0
  18. package/docs/interfaces/ExecuteWorkflowPayload.html +5 -5
  19. package/docs/interfaces/Execution.html +2 -2
  20. package/docs/interfaces/ExecutionFilters.html +8 -8
  21. package/docs/interfaces/GetExecutionsParams.html +9 -9
  22. package/docs/interfaces/InputField.html +9 -9
  23. package/docs/interfaces/KeyBasedParams.html +5 -0
  24. package/docs/interfaces/Label.html +3 -3
  25. package/docs/interfaces/OAuthParams.html +9 -0
  26. package/docs/interfaces/PublicWorkflow.html +8 -8
  27. package/docs/interfaces/PublicWorkflowPayload.html +4 -4
  28. package/docs/interfaces/PublicWorkflowsPayload.html +7 -7
  29. package/docs/interfaces/RefoldOptions.html +3 -3
  30. package/docs/interfaces/RuleOptions.html +2 -2
  31. package/docs/interfaces/UpdateConfigPayload.html +5 -5
  32. package/docs/interfaces/WorkflowPayload.html +4 -4
  33. package/docs/interfaces/WorkflowPayloadResponse.html +2 -2
  34. package/docs/llms.txt +248 -197
  35. package/docs/modules.html +1 -1
  36. package/docs/types/ExecutionSource.html +1 -1
  37. package/docs/types/ExecutionStatus.html +1 -1
  38. package/docs/types/ExecutionType.html +1 -1
  39. package/package.json +2 -2
  40. package/refold.d.ts +31 -9
  41. package/refold.js +84 -47
  42. package/refold.ts +123 -48
package/refold.ts CHANGED
@@ -92,6 +92,29 @@ export interface InputField {
92
92
  }[];
93
93
  }
94
94
 
95
+ export interface OAuthParams {
96
+ /** The application slug. */
97
+ slug: string;
98
+ /** The key value pairs of auth data. */
99
+ payload?: Record<string, string>;
100
+ /** Whether to close the authentication window automatically. */
101
+ autoClose?: boolean;
102
+ /** Maximum time in milliseconds to wait for authentication before giving up. Set to `0` to wait indefinitely. Defaults to 5 minutes. */
103
+ timeout?: number;
104
+ }
105
+
106
+ export interface KeyBasedParams {
107
+ /** The application slug. */
108
+ slug: string;
109
+ /** The key value pairs of auth data. */
110
+ payload?: Record<string, string>;
111
+ }
112
+
113
+ export interface ConnectParams extends OAuthParams {
114
+ /** The authentication type to use. If not provided, it defaults to `keybased` if payload is provided, otherwise `oauth2`. */
115
+ type?: AuthType;
116
+ }
117
+
95
118
  /** The payload object for config. */
96
119
  export interface ConfigPayload {
97
120
  /** The application slug. */
@@ -361,6 +384,15 @@ export interface Execution {
361
384
 
362
385
  type Field = any;
363
386
 
387
+ /** How often, in milliseconds, connection status is polled during authentication. */
388
+ const POLL_INTERVAL = 3e3;
389
+ /** How long, in milliseconds, polling continues after the auth window closes or the wait times out, since the connection may complete moments later. */
390
+ const POLL_GRACE = 6e3;
391
+ /** The number of consecutive polling failures tolerated before authentication is aborted. */
392
+ const MAX_POLL_FAILURES = 3;
393
+ /** The default maximum time, in milliseconds, to wait for authentication. */
394
+ const DEFAULT_CONNECT_TIMEOUT = 300e3;
395
+
364
396
  class Refold {
365
397
  private baseUrl: string;
366
398
  public token: string;
@@ -509,56 +541,99 @@ class Refold {
509
541
  /**
510
542
  * Handle OAuth for the specified application.
511
543
  * @private
512
- * @param {String} slug The application slug.
513
- * @param {Object.<string, string>} [params] The key value pairs of auth data.
544
+ * @param params - The parameters for the OAuth flow.
545
+ * @param params.slug - The application slug.
546
+ * @param params.payload - The key value pairs of auth data.
547
+ * @param params.autoClose - Whether to close the authentication window automatically. Defaults to `true`.
548
+ * @param params.timeout - Maximum time in milliseconds to wait for authentication before giving up. Set to `0` to wait indefinitely. Defaults to 5 minutes.
514
549
  * @returns {Promise<Boolean>} Whether the user authenticated.
515
550
  */
516
- private async oauth(slug: string, params?: Record<string, string>): Promise<boolean> {
551
+ private async oauth({
552
+ slug,
553
+ payload,
554
+ autoClose = true,
555
+ timeout = DEFAULT_CONNECT_TIMEOUT,
556
+ }: OAuthParams): Promise<boolean> {
557
+ const oauthUrl = await this.getOAuthUrl(slug, payload);
558
+
559
+ const connectWindow = window.open(oauthUrl);
560
+ if (!connectWindow) {
561
+ throw Object.assign(
562
+ new Error("The authentication window could not be opened. It may have been blocked by the browser."),
563
+ { code: "POPUP_BLOCKED" },
564
+ );
565
+ }
566
+
567
+ const hasActiveOAuthAccount = (app: Application) =>
568
+ Boolean(app?.connected_accounts?.filter(a => a.auth_type === AuthType.OAuth2).some(a => a.status === AuthStatus.Active));
569
+
517
570
  return new Promise((resolve, reject) => {
518
- this.getOAuthUrl(slug, params)
519
- .then(oauthUrl => {
520
- const connectWindow = window.open(oauthUrl);
521
-
522
- // keep checking connection status
523
- const interval = setInterval(() => {
524
- this.getApp(slug)
525
- .then(app => {
526
- if (app && app.connected_accounts?.filter(a => a.auth_type === AuthType.OAuth2).some(a => a.status === AuthStatus.Active)) {
527
- // close auth window
528
- connectWindow && connectWindow.close();
529
- // clear interval
530
- clearInterval(interval);
531
- // resovle status
532
- resolve(true);
533
- } else {
534
- // user closed oauth window without authenticating
535
- if (connectWindow && connectWindow.closed) {
536
- // clear interval
537
- clearInterval(interval);
538
- // resolve status
539
- resolve(false);
540
- }
541
- }
542
- })
543
- .catch(e => {
544
- console.error(e);
545
- // connectWindow?.close();
571
+ const startedAt = Date.now();
572
+ let inFlight = false;
573
+ let consecutiveFailures = 0;
574
+ let firstFailure: unknown;
575
+ let graceStartedAt: number | undefined;
576
+
577
+ // keep checking connection status
578
+ const interval = setInterval(() => {
579
+ const timedOut = timeout > 0 && Date.now() - startedAt >= timeout;
580
+ if (connectWindow.closed || timedOut) {
581
+ // the connection may complete moments around the window
582
+ // closing or the wait timing out, so keep polling for a
583
+ // little longer before giving up
584
+ if (timedOut && autoClose) connectWindow.close();
585
+ graceStartedAt ??= Date.now();
586
+ if (Date.now() - graceStartedAt >= POLL_GRACE) {
546
587
  clearInterval(interval);
547
- reject(e);
548
- });
549
- }, 3e3);
550
- })
551
- .catch(reject);
588
+ resolve(false);
589
+ return;
590
+ }
591
+ }
592
+
593
+ // don't check the status again until the previous check settles
594
+ if (inFlight) return;
595
+ inFlight = true;
596
+
597
+ this.getApp(slug)
598
+ .then(app => {
599
+ inFlight = false;
600
+ consecutiveFailures = 0;
601
+ firstFailure = undefined;
602
+ if (hasActiveOAuthAccount(app)) {
603
+ // close auth window
604
+ if (autoClose) connectWindow.close();
605
+ // clear interval
606
+ clearInterval(interval);
607
+ // resolve status
608
+ resolve(true);
609
+ }
610
+ })
611
+ .catch(e => {
612
+ console.error(e);
613
+ inFlight = false;
614
+ // tolerate transient errors while the user authenticates
615
+ consecutiveFailures += 1;
616
+ firstFailure ??= e;
617
+ if (consecutiveFailures >= MAX_POLL_FAILURES) {
618
+ clearInterval(interval);
619
+ reject(firstFailure);
620
+ }
621
+ });
622
+ }, POLL_INTERVAL);
552
623
  });
553
624
  }
554
625
 
555
626
  /**
556
627
  * Save auth data for the specified keybased application.
557
- * @param {String} slug The application slug.
558
- * @param {Object.<string, string>} [payload] The key value pairs of auth data.
628
+ * @param params - The parameters for key-based auth.
629
+ * @param params.slug - The application slug.
630
+ * @param params.payload - The key value pairs of auth data.
559
631
  * @returns {Promise<Boolean>} Whether the auth data was saved successfully.
560
632
  */
561
- private async keybased(slug: string, payload?: Record<string, string>): Promise<boolean> {
633
+ private async keybased({
634
+ slug,
635
+ payload,
636
+ }: KeyBasedParams): Promise<boolean> {
562
637
  const res = await fetch(`${this.baseUrl}/api/v2/app/${slug}/save`, {
563
638
  method: "POST",
564
639
  headers: {
@@ -585,6 +660,8 @@ class Refold {
585
660
  * @param params.slug - The application slug.
586
661
  * @param params.type - The authentication type to use. If not provided, it defaults to `keybased` if payload is provided, otherwise `oauth2`.
587
662
  * @param params.payload - key-value pairs of authentication data required for the specified auth type.
663
+ * @param params.autoClose - Whether to close the authentication window automatically. If not provided, it defaults to `true`.
664
+ * @param params.timeout - Maximum time in milliseconds to wait for authentication before giving up. Only applicable to the OAuth2 flow. Set to `0` to wait indefinitely. If not provided, it defaults to 5 minutes.
588
665
  * @returns A promise that resolves to true if the connection was successful, otherwise false.
589
666
  * @throws Throws an error if the authentication type is invalid or the connection fails.
590
667
  */
@@ -592,19 +669,17 @@ class Refold {
592
669
  slug,
593
670
  type,
594
671
  payload,
595
- }: {
596
- slug: string;
597
- type?: AuthType;
598
- payload?: Record<string, string>;
599
- }): Promise<boolean> {
672
+ autoClose = true,
673
+ timeout = DEFAULT_CONNECT_TIMEOUT,
674
+ }: ConnectParams): Promise<boolean> {
600
675
  switch (type) {
601
676
  case AuthType.OAuth2:
602
- return this.oauth(slug, payload);
677
+ return this.oauth({ slug, payload, autoClose, timeout });
603
678
  case AuthType.KeyBased:
604
- return this.keybased(slug, payload);
679
+ return this.keybased({ slug, payload });
605
680
  default:
606
- if (payload) return this.keybased(slug, payload);
607
- return this.oauth(slug);
681
+ if (payload) return this.keybased({ slug, payload });
682
+ return this.oauth({ slug, autoClose, timeout });
608
683
  }
609
684
  }
610
685