docpouch-client 1.1.1 → 1.1.3

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.
package/dist/index.d.ts CHANGED
@@ -11,6 +11,18 @@ export default class docPouchClient {
11
11
  * @type {string}
12
12
  */
13
13
  baseUrl: string;
14
+ /**
15
+ * The port number to use for HTTP and WebSocket connections when the
16
+ * base URL does not already include an explicit port. Stored from
17
+ * the constructor argument so the {@link request} method can
18
+ * build full URLs (e.g. for the OIDC dynamic client registration
19
+ * endpoint at `/oidc/reg`) even when the caller passes a host
20
+ * without a port. A port that already appears in the base URL
21
+ * takes precedence.
22
+ *
23
+ * @type {number}
24
+ */
25
+ port: number;
14
26
  /**
15
27
  * Socket.IO socket instance for real-time communication with the server.
16
28
  *
@@ -58,8 +70,14 @@ export default class docPouchClient {
58
70
  /**
59
71
  * Creates an instance of docPouchClient.
60
72
  *
61
- * @param {string} host - The base URL for the server.
62
- * @param {number} [port=80] - The port number to connect to (default is 80).
73
+ * @param {string} host - The base URL for the server. May be a bare
74
+ * host (`http://localhost`), a host with an explicit port
75
+ * (`http://localhost:3030`), or a full URL with a path. When the
76
+ * value does not already contain a port number, the supplied
77
+ * `port` argument is appended to every HTTP request built by
78
+ * {@link request} (in addition to the WebSocket connection).
79
+ * @param {number} [port=80] - The port number to use when the
80
+ * `host` does not already specify one (default is 80).
63
81
  * @param {(event: I_EventString, data: I_WsMessage) => void} [callback] - Optional callback function for socket events.
64
82
  */
65
83
  constructor(host: string, port?: number, callback?: (event: I_EventString, data: I_WsMessage) => void);
package/dist/index.js CHANGED
@@ -9,8 +9,14 @@ export default class docPouchClient {
9
9
  /**
10
10
  * Creates an instance of docPouchClient.
11
11
  *
12
- * @param {string} host - The base URL for the server.
13
- * @param {number} [port=80] - The port number to connect to (default is 80).
12
+ * @param {string} host - The base URL for the server. May be a bare
13
+ * host (`http://localhost`), a host with an explicit port
14
+ * (`http://localhost:3030`), or a full URL with a path. When the
15
+ * value does not already contain a port number, the supplied
16
+ * `port` argument is appended to every HTTP request built by
17
+ * {@link request} (in addition to the WebSocket connection).
18
+ * @param {number} [port=80] - The port number to use when the
19
+ * `host` does not already specify one (default is 80).
14
20
  * @param {(event: I_EventString, data: I_WsMessage) => void} [callback] - Optional callback function for socket events.
15
21
  */
16
22
  constructor(host, port = 80, callback) {
@@ -47,6 +53,7 @@ export default class docPouchClient {
47
53
  */
48
54
  this.events = {};
49
55
  this.baseUrl = host;
56
+ this.port = port;
50
57
  const socketUrl = host.includes('://') ? host : `https://${host}`;
51
58
  const socketUrlWithPort = socketUrl.includes(':') && !socketUrl.endsWith(':')
52
59
  ? socketUrl
@@ -499,12 +506,11 @@ export default class docPouchClient {
499
506
  if (idToken) {
500
507
  url += `&id_token_hint=${idToken}`;
501
508
  }
502
- // Clear tokens before redirect
503
- this.authToken = null;
504
- this.clearOidcTokens();
505
- this.authMethod = 'none';
506
- if (this.socket.connected)
507
- this.socket.disconnect();
509
+ // DO NOT clear tokens before redirect - wait for redirect back to determine actual logout status
510
+ // Instead, set a flag to indicate we're in logout process
511
+ if (typeof window !== 'undefined' && window.sessionStorage) {
512
+ sessionStorage.setItem('docpouch_logout_in_progress', 'true');
513
+ }
508
514
  // Redirect to OIDC logout endpoint
509
515
  window.location.href = url;
510
516
  return;
@@ -552,12 +558,11 @@ export default class docPouchClient {
552
558
  else if (this.oidcIdToken) {
553
559
  url += `&id_token_hint=${this.oidcIdToken}`;
554
560
  }
555
- // Clear tokens before redirect
556
- this.authToken = null;
557
- this.clearOidcTokens();
558
- this.authMethod = 'none';
559
- if (this.socket.connected)
560
- this.socket.disconnect();
561
+ // DO NOT clear tokens before redirect - wait for redirect back to determine actual logout status
562
+ // Instead, set a flag to indicate we're in logout process
563
+ if (typeof window !== 'undefined' && window.sessionStorage) {
564
+ sessionStorage.setItem('docpouch_logout_in_progress', 'true');
565
+ }
561
566
  // Emit OIDC logout event
562
567
  this.emit('oidc-logout');
563
568
  this.emit('logout');
@@ -613,18 +618,71 @@ export default class docPouchClient {
613
618
  * @returns {boolean} True if user was just logged out
614
619
  */
615
620
  wasJustLoggedOut() {
616
- // Check URL query parameter
621
+ // Check if we're in the middle of a logout process
622
+ const logoutInProgress = typeof window !== 'undefined' && window.sessionStorage
623
+ ? sessionStorage.getItem('docpouch_logout_in_progress') === 'true'
624
+ : false;
625
+ if (!logoutInProgress) {
626
+ return false;
627
+ }
628
+ // Check URL query parameters for logout confirmation
617
629
  const urlParams = new URLSearchParams(window.location.search);
618
- if (urlParams.get('logout') === 'true') {
619
- return true;
630
+ const logoutParam = urlParams.get('logout');
631
+ // If logout parameter is explicitly 'no', user cancelled
632
+ if (logoutParam === 'no') {
633
+ // Clear the logout in progress flag
634
+ if (typeof window !== 'undefined' && window.sessionStorage) {
635
+ sessionStorage.removeItem('docpouch_logout_in_progress');
636
+ }
637
+ return false;
620
638
  }
621
- // Check localStorage flag
622
- const lastAuthMethod = localStorage.getItem('lastAuthMethod');
623
- const currentAuthMethod = localStorage.getItem('authMethod');
624
- // If last was OIDC/JWT but current is none, was logged out
625
- if (lastAuthMethod && currentAuthMethod === null) {
639
+ // If logout parameter is 'yes' or 'true', user confirmed logout
640
+ if (logoutParam === 'yes' || logoutParam === 'true') {
641
+ // Clear the logout in progress flag
642
+ if (typeof window !== 'undefined' && window.sessionStorage) {
643
+ sessionStorage.removeItem('docpouch_logout_in_progress');
644
+ }
645
+ // Clear tokens as user confirmed logout
646
+ this.authToken = null;
647
+ this.clearOidcTokens();
648
+ this.authMethod = 'none';
649
+ if (this.socket.connected)
650
+ this.socket.disconnect();
626
651
  return true;
627
652
  }
653
+ // If no logout parameter but we were in logout process,
654
+ // check if OIDC provider sent us back without confirmation
655
+ // This could happen if there was an error or user cancelled in another way
656
+ if (logoutParam === null) {
657
+ // Check if this is a post-logout redirect - assume logout was successful
658
+ // unless there's an error parameter
659
+ const errorParam = urlParams.get('error');
660
+ if (!errorParam) {
661
+ // Clear the logout in progress flag
662
+ if (typeof window !== 'undefined' && window.sessionStorage) {
663
+ sessionStorage.removeItem('docpouch_logout_in_progress');
664
+ }
665
+ // Clear tokens
666
+ this.authToken = null;
667
+ this.clearOidcTokens();
668
+ this.authMethod = 'none';
669
+ if (this.socket.connected)
670
+ this.socket.disconnect();
671
+ return true;
672
+ }
673
+ else {
674
+ // There was an error, assume logout was cancelled
675
+ // Clear the logout in progress flag
676
+ if (typeof window !== 'undefined' && window.sessionStorage) {
677
+ sessionStorage.removeItem('docpouch_logout_in_progress');
678
+ }
679
+ return false;
680
+ }
681
+ }
682
+ // Default case - clear the flag to prevent stuck state
683
+ if (typeof window !== 'undefined' && window.sessionStorage) {
684
+ sessionStorage.removeItem('docpouch_logout_in_progress');
685
+ }
628
686
  return false;
629
687
  }
630
688
  // OIDC Dynamic Client Registration Methods
@@ -780,7 +838,16 @@ export default class docPouchClient {
780
838
  body: body ? JSON.stringify(body) : undefined
781
839
  };
782
840
  const normalizedEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
783
- const normalizedBaseUrl = this.baseUrl.endsWith('/') ? this.baseUrl.slice(0, -1) : this.baseUrl;
841
+ const trimmedBaseUrl = this.baseUrl.endsWith('/') ? this.baseUrl.slice(0, -1) : this.baseUrl;
842
+ // If the caller's `host` already includes a port (e.g.
843
+ // `http://localhost:3030`) we must not append `this.port` or
844
+ // the URL would end up like `http://localhost:3030:3030` or
845
+ // `http://localhost:3030:80`. Only fall back to the constructor
846
+ // `port` argument when the URL has no port of its own.
847
+ const hasExplicitPort = /:\d+(?=\/|$)/.test(trimmedBaseUrl);
848
+ const normalizedBaseUrl = !hasExplicitPort && this.port
849
+ ? `${trimmedBaseUrl}:${this.port}`
850
+ : trimmedBaseUrl;
784
851
  const url = `${normalizedBaseUrl}${normalizedEndpoint}`;
785
852
  const response = await fetch(url, options);
786
853
  if (!response.ok) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docpouch-client",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "exports": {