@oxyhq/core 1.11.22 → 1.11.23
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/README.md +2 -2
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/HttpService.js +52 -0
- package/dist/cjs/OxyServices.base.js +16 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/HttpService.js +52 -0
- package/dist/esm/OxyServices.base.js +16 -0
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/HttpService.d.ts +31 -0
- package/dist/types/OxyServices.base.d.ts +14 -0
- package/dist/types/mixins/OxyServices.analytics.d.ts +1 -0
- package/dist/types/mixins/OxyServices.appData.d.ts +1 -0
- package/dist/types/mixins/OxyServices.assets.d.ts +1 -0
- package/dist/types/mixins/OxyServices.auth.d.ts +1 -0
- package/dist/types/mixins/OxyServices.contacts.d.ts +1 -0
- package/dist/types/mixins/OxyServices.developer.d.ts +1 -0
- package/dist/types/mixins/OxyServices.devices.d.ts +1 -0
- package/dist/types/mixins/OxyServices.features.d.ts +6 -1
- package/dist/types/mixins/OxyServices.fedcm.d.ts +1 -0
- package/dist/types/mixins/OxyServices.karma.d.ts +1 -0
- package/dist/types/mixins/OxyServices.language.d.ts +1 -0
- package/dist/types/mixins/OxyServices.location.d.ts +1 -0
- package/dist/types/mixins/OxyServices.managedAccounts.d.ts +1 -0
- package/dist/types/mixins/OxyServices.payment.d.ts +1 -0
- package/dist/types/mixins/OxyServices.popup.d.ts +1 -0
- package/dist/types/mixins/OxyServices.privacy.d.ts +1 -0
- package/dist/types/mixins/OxyServices.redirect.d.ts +1 -0
- package/dist/types/mixins/OxyServices.security.d.ts +1 -0
- package/dist/types/mixins/OxyServices.topics.d.ts +1 -0
- package/dist/types/mixins/OxyServices.user.d.ts +1 -0
- package/dist/types/mixins/OxyServices.utility.d.ts +1 -0
- package/package.json +1 -1
- package/src/HttpService.ts +53 -0
- package/src/OxyServices.base.ts +17 -0
- package/src/mixins/__tests__/onTokensChanged.test.ts +130 -0
package/dist/cjs/HttpService.js
CHANGED
|
@@ -109,6 +109,16 @@ class HttpService {
|
|
|
109
109
|
this.tokenRefreshPromise = null;
|
|
110
110
|
this.tokenRefreshCooldownUntil = 0;
|
|
111
111
|
this._onTokenRefreshed = null;
|
|
112
|
+
/**
|
|
113
|
+
* Fan-out listeners notified on EVERY access-token change on this instance:
|
|
114
|
+
* explicit `setTokens`, `clearTokens`, a successful silent refresh, and the
|
|
115
|
+
* internal 401-driven clear. Unlike the single-slot `_onTokenRefreshed`
|
|
116
|
+
* (owned by AuthManager for the refresh path only), this is a Set so multiple
|
|
117
|
+
* independent observers can mirror token state without clobbering each other.
|
|
118
|
+
*
|
|
119
|
+
* Each listener receives the resulting access token, or `null` when cleared.
|
|
120
|
+
*/
|
|
121
|
+
this._tokenChangeListeners = new Set();
|
|
112
122
|
// Acting-as identity for managed accounts
|
|
113
123
|
this._actingAsUserId = null;
|
|
114
124
|
// Performance monitoring
|
|
@@ -312,6 +322,7 @@ class HttpService {
|
|
|
312
322
|
// Refresh failed or no token — clear tokens and stale CSRF
|
|
313
323
|
this.tokenStore.clearTokens();
|
|
314
324
|
this.tokenStore.clearCsrfToken();
|
|
325
|
+
this.notifyTokenChange();
|
|
315
326
|
}
|
|
316
327
|
// On 403 with CSRF error, clear cached token and retry once
|
|
317
328
|
if (response.status === 403 && !config._isCsrfRetry) {
|
|
@@ -691,6 +702,7 @@ class HttpService {
|
|
|
691
702
|
const { accessToken: newToken } = await response.json();
|
|
692
703
|
this.tokenStore.setTokens(newToken);
|
|
693
704
|
this._onTokenRefreshed?.(newToken);
|
|
705
|
+
this.notifyTokenChange();
|
|
694
706
|
this.logger.debug('Token refreshed');
|
|
695
707
|
return `Bearer ${newToken}`;
|
|
696
708
|
}
|
|
@@ -756,6 +768,7 @@ class HttpService {
|
|
|
756
768
|
// Token management
|
|
757
769
|
setTokens(accessToken, refreshToken = '') {
|
|
758
770
|
this.tokenStore.setTokens(accessToken, refreshToken);
|
|
771
|
+
this.notifyTokenChange();
|
|
759
772
|
}
|
|
760
773
|
set onTokenRefreshed(callback) {
|
|
761
774
|
this._onTokenRefreshed = callback;
|
|
@@ -763,6 +776,45 @@ class HttpService {
|
|
|
763
776
|
clearTokens() {
|
|
764
777
|
this.tokenStore.clearTokens();
|
|
765
778
|
this.tokenStore.clearCsrfToken();
|
|
779
|
+
this.notifyTokenChange();
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* Subscribe to access-token changes on this instance.
|
|
783
|
+
*
|
|
784
|
+
* Fires on every mutation of the access token — `setTokens`, `clearTokens`,
|
|
785
|
+
* a successful silent refresh, and the internal 401-driven clear — passing
|
|
786
|
+
* the resulting token (or `null` when cleared). Returns an unsubscribe
|
|
787
|
+
* function; call it on teardown to avoid leaks.
|
|
788
|
+
*
|
|
789
|
+
* This is the single hook downstream code (e.g. @oxyhq/services' OxyProvider)
|
|
790
|
+
* uses to keep an external token sink — such as the shared `oxyClient`
|
|
791
|
+
* singleton — in lockstep with the active session, regardless of which code
|
|
792
|
+
* path mutated the token.
|
|
793
|
+
*/
|
|
794
|
+
addTokenChangeListener(listener) {
|
|
795
|
+
this._tokenChangeListeners.add(listener);
|
|
796
|
+
return () => {
|
|
797
|
+
this._tokenChangeListeners.delete(listener);
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Notify all token-change listeners with the current access token.
|
|
802
|
+
* Listener exceptions are isolated so one bad subscriber cannot break token
|
|
803
|
+
* propagation to the others or to the calling auth flow.
|
|
804
|
+
* @internal
|
|
805
|
+
*/
|
|
806
|
+
notifyTokenChange() {
|
|
807
|
+
if (this._tokenChangeListeners.size === 0)
|
|
808
|
+
return;
|
|
809
|
+
const accessToken = this.tokenStore.getAccessToken();
|
|
810
|
+
for (const listener of this._tokenChangeListeners) {
|
|
811
|
+
try {
|
|
812
|
+
listener(accessToken);
|
|
813
|
+
}
|
|
814
|
+
catch (error) {
|
|
815
|
+
this.logger.error('Token change listener threw:', error);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
766
818
|
}
|
|
767
819
|
getAccessToken() {
|
|
768
820
|
return this.tokenStore.getAccessToken();
|
|
@@ -113,6 +113,22 @@ class OxyServicesBase {
|
|
|
113
113
|
this._cachedUserId = undefined;
|
|
114
114
|
this._cachedAccessToken = null;
|
|
115
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Subscribe to access-token changes on this client.
|
|
118
|
+
*
|
|
119
|
+
* The listener fires on every access-token mutation — explicit
|
|
120
|
+
* `setTokens`/`clearTokens`, a successful silent refresh, and the internal
|
|
121
|
+
* 401-driven clear — receiving the resulting token, or `null` when cleared.
|
|
122
|
+
* Returns an unsubscribe function.
|
|
123
|
+
*
|
|
124
|
+
* Primary use: keeping an external token sink (e.g. the shared `oxyClient`
|
|
125
|
+
* singleton) in lockstep with whichever `OxyServices` instance actually owns
|
|
126
|
+
* the session, so imperative consumers reading the singleton always observe
|
|
127
|
+
* the live token regardless of the code path that changed it.
|
|
128
|
+
*/
|
|
129
|
+
onTokensChanged(listener) {
|
|
130
|
+
return this.httpService.addTokenChangeListener(listener);
|
|
131
|
+
}
|
|
116
132
|
/**
|
|
117
133
|
* Get the current user ID from the access token.
|
|
118
134
|
* Caches the decoded value and invalidates when the token changes.
|