@oxyhq/core 1.11.24 → 2.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 (150) hide show
  1. package/README.md +5 -6
  2. package/dist/cjs/.tsbuildinfo +1 -1
  3. package/dist/cjs/AuthManager.js +678 -4
  4. package/dist/cjs/AuthManagerTypes.js +13 -0
  5. package/dist/cjs/CrossDomainAuth.js +45 -3
  6. package/dist/cjs/OxyServices.base.js +16 -0
  7. package/dist/cjs/i18n/locales/ar-SA.json +83 -0
  8. package/dist/cjs/i18n/locales/ca-ES.json +83 -0
  9. package/dist/cjs/i18n/locales/de-DE.json +83 -0
  10. package/dist/cjs/i18n/locales/en-US.json +83 -0
  11. package/dist/cjs/i18n/locales/es-ES.json +99 -4
  12. package/dist/cjs/i18n/locales/fr-FR.json +83 -0
  13. package/dist/cjs/i18n/locales/it-IT.json +83 -0
  14. package/dist/cjs/i18n/locales/ja-JP.json +83 -0
  15. package/dist/cjs/i18n/locales/ko-KR.json +83 -0
  16. package/dist/cjs/i18n/locales/locales/ar-SA.json +83 -1
  17. package/dist/cjs/i18n/locales/locales/ca-ES.json +83 -1
  18. package/dist/cjs/i18n/locales/locales/de-DE.json +83 -1
  19. package/dist/cjs/i18n/locales/locales/en-US.json +83 -0
  20. package/dist/cjs/i18n/locales/locales/es-ES.json +99 -4
  21. package/dist/cjs/i18n/locales/locales/fr-FR.json +83 -1
  22. package/dist/cjs/i18n/locales/locales/it-IT.json +83 -1
  23. package/dist/cjs/i18n/locales/locales/ja-JP.json +200 -117
  24. package/dist/cjs/i18n/locales/locales/ko-KR.json +83 -1
  25. package/dist/cjs/i18n/locales/locales/pt-PT.json +83 -1
  26. package/dist/cjs/i18n/locales/locales/zh-CN.json +83 -1
  27. package/dist/cjs/i18n/locales/pt-PT.json +83 -0
  28. package/dist/cjs/i18n/locales/zh-CN.json +83 -0
  29. package/dist/cjs/index.js +121 -57
  30. package/dist/cjs/mixins/OxyServices.auth.js +235 -0
  31. package/dist/cjs/mixins/OxyServices.fedcm.js +36 -0
  32. package/dist/cjs/mixins/OxyServices.popup.js +61 -1
  33. package/dist/cjs/mixins/OxyServices.user.js +18 -0
  34. package/dist/cjs/utils/accountUtils.js +64 -1
  35. package/dist/cjs/utils/coldBoot.js +71 -0
  36. package/dist/cjs/utils/fapiAutoDetect.js +88 -0
  37. package/dist/esm/.tsbuildinfo +1 -1
  38. package/dist/esm/AuthManager.js +678 -4
  39. package/dist/esm/AuthManagerTypes.js +12 -0
  40. package/dist/esm/CrossDomainAuth.js +45 -3
  41. package/dist/esm/OxyServices.base.js +16 -0
  42. package/dist/esm/i18n/locales/ar-SA.json +83 -0
  43. package/dist/esm/i18n/locales/ca-ES.json +83 -0
  44. package/dist/esm/i18n/locales/de-DE.json +83 -0
  45. package/dist/esm/i18n/locales/en-US.json +83 -0
  46. package/dist/esm/i18n/locales/es-ES.json +99 -4
  47. package/dist/esm/i18n/locales/fr-FR.json +83 -0
  48. package/dist/esm/i18n/locales/it-IT.json +83 -0
  49. package/dist/esm/i18n/locales/ja-JP.json +83 -0
  50. package/dist/esm/i18n/locales/ko-KR.json +83 -0
  51. package/dist/esm/i18n/locales/locales/ar-SA.json +83 -1
  52. package/dist/esm/i18n/locales/locales/ca-ES.json +83 -1
  53. package/dist/esm/i18n/locales/locales/de-DE.json +83 -1
  54. package/dist/esm/i18n/locales/locales/en-US.json +83 -0
  55. package/dist/esm/i18n/locales/locales/es-ES.json +99 -4
  56. package/dist/esm/i18n/locales/locales/fr-FR.json +83 -1
  57. package/dist/esm/i18n/locales/locales/it-IT.json +83 -1
  58. package/dist/esm/i18n/locales/locales/ja-JP.json +200 -117
  59. package/dist/esm/i18n/locales/locales/ko-KR.json +83 -1
  60. package/dist/esm/i18n/locales/locales/pt-PT.json +83 -1
  61. package/dist/esm/i18n/locales/locales/zh-CN.json +83 -1
  62. package/dist/esm/i18n/locales/pt-PT.json +83 -0
  63. package/dist/esm/i18n/locales/zh-CN.json +83 -0
  64. package/dist/esm/index.js +74 -26
  65. package/dist/esm/mixins/OxyServices.auth.js +235 -0
  66. package/dist/esm/mixins/OxyServices.fedcm.js +36 -0
  67. package/dist/esm/mixins/OxyServices.popup.js +61 -1
  68. package/dist/esm/mixins/OxyServices.user.js +18 -0
  69. package/dist/esm/utils/accountUtils.js +61 -0
  70. package/dist/esm/utils/coldBoot.js +68 -0
  71. package/dist/esm/utils/fapiAutoDetect.js +85 -0
  72. package/dist/types/.tsbuildinfo +1 -1
  73. package/dist/types/AuthManager.d.ts +243 -3
  74. package/dist/types/AuthManagerTypes.d.ts +68 -0
  75. package/dist/types/CrossDomainAuth.d.ts +23 -0
  76. package/dist/types/OxyServices.base.d.ts +14 -0
  77. package/dist/types/OxyServices.d.ts +7 -0
  78. package/dist/types/index.d.ts +31 -17
  79. package/dist/types/mixins/OxyServices.analytics.d.ts +1 -0
  80. package/dist/types/mixins/OxyServices.appData.d.ts +1 -0
  81. package/dist/types/mixins/OxyServices.assets.d.ts +4 -1
  82. package/dist/types/mixins/OxyServices.auth.d.ts +73 -1
  83. package/dist/types/mixins/OxyServices.contacts.d.ts +1 -0
  84. package/dist/types/mixins/OxyServices.developer.d.ts +1 -0
  85. package/dist/types/mixins/OxyServices.devices.d.ts +1 -0
  86. package/dist/types/mixins/OxyServices.features.d.ts +2 -5
  87. package/dist/types/mixins/OxyServices.fedcm.d.ts +34 -0
  88. package/dist/types/mixins/OxyServices.karma.d.ts +1 -0
  89. package/dist/types/mixins/OxyServices.language.d.ts +1 -0
  90. package/dist/types/mixins/OxyServices.location.d.ts +1 -0
  91. package/dist/types/mixins/OxyServices.managedAccounts.d.ts +1 -0
  92. package/dist/types/mixins/OxyServices.payment.d.ts +1 -0
  93. package/dist/types/mixins/OxyServices.popup.d.ts +40 -0
  94. package/dist/types/mixins/OxyServices.privacy.d.ts +1 -0
  95. package/dist/types/mixins/OxyServices.redirect.d.ts +1 -0
  96. package/dist/types/mixins/OxyServices.security.d.ts +1 -0
  97. package/dist/types/mixins/OxyServices.topics.d.ts +1 -0
  98. package/dist/types/mixins/OxyServices.user.d.ts +16 -1
  99. package/dist/types/mixins/OxyServices.utility.d.ts +1 -0
  100. package/dist/types/models/interfaces.d.ts +98 -0
  101. package/dist/types/models/session.d.ts +8 -0
  102. package/dist/types/utils/accountUtils.d.ts +33 -0
  103. package/dist/types/utils/coldBoot.d.ts +102 -0
  104. package/dist/types/utils/fapiAutoDetect.d.ts +37 -0
  105. package/package.json +9 -18
  106. package/src/AuthManager.ts +776 -7
  107. package/src/AuthManagerTypes.ts +72 -0
  108. package/src/CrossDomainAuth.ts +54 -3
  109. package/src/OxyServices.base.ts +17 -0
  110. package/src/OxyServices.ts +7 -0
  111. package/src/__tests__/authManager.cookiePath.test.ts +339 -0
  112. package/src/__tests__/authManager.security.test.ts +342 -0
  113. package/src/__tests__/crossDomainAuth.test.ts +191 -0
  114. package/src/i18n/locales/ar-SA.json +83 -1
  115. package/src/i18n/locales/ca-ES.json +83 -1
  116. package/src/i18n/locales/de-DE.json +83 -1
  117. package/src/i18n/locales/en-US.json +83 -0
  118. package/src/i18n/locales/es-ES.json +99 -4
  119. package/src/i18n/locales/fr-FR.json +83 -1
  120. package/src/i18n/locales/it-IT.json +83 -1
  121. package/src/i18n/locales/ja-JP.json +200 -117
  122. package/src/i18n/locales/ko-KR.json +83 -1
  123. package/src/i18n/locales/pt-PT.json +83 -1
  124. package/src/i18n/locales/zh-CN.json +83 -1
  125. package/src/index.ts +309 -112
  126. package/src/mixins/OxyServices.auth.ts +268 -1
  127. package/src/mixins/OxyServices.fedcm.ts +63 -0
  128. package/src/mixins/OxyServices.popup.ts +79 -1
  129. package/src/mixins/OxyServices.user.ts +33 -1
  130. package/src/mixins/__tests__/popup.test.ts +307 -0
  131. package/src/mixins/__tests__/sessionBaseUrl.test.ts +61 -0
  132. package/src/models/interfaces.ts +116 -0
  133. package/src/models/session.ts +8 -0
  134. package/src/utils/__tests__/coldBoot.test.ts +226 -0
  135. package/src/utils/__tests__/fapiAutoDetect.test.ts +93 -0
  136. package/src/utils/accountUtils.ts +84 -0
  137. package/src/utils/coldBoot.ts +136 -0
  138. package/src/utils/fapiAutoDetect.ts +82 -0
  139. package/dist/cjs/crypto/index.js +0 -22
  140. package/dist/cjs/shared/index.js +0 -70
  141. package/dist/cjs/utils/index.js +0 -26
  142. package/dist/esm/crypto/index.js +0 -13
  143. package/dist/esm/shared/index.js +0 -31
  144. package/dist/esm/utils/index.js +0 -7
  145. package/dist/types/crypto/index.d.ts +0 -11
  146. package/dist/types/shared/index.d.ts +0 -28
  147. package/dist/types/utils/index.d.ts +0 -6
  148. package/src/crypto/index.ts +0 -30
  149. package/src/shared/index.ts +0 -82
  150. package/src/utils/index.ts +0 -21
@@ -53,6 +53,7 @@ export declare function OxyServicesContactsMixin<T extends typeof OxyServicesBas
53
53
  __resetTokensForTests(): void;
54
54
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
55
55
  getBaseURL(): string;
56
+ getSessionBaseUrl(): string;
56
57
  getClient(): import("../HttpService").HttpService;
57
58
  getMetrics(): {
58
59
  totalRequests: number;
@@ -58,6 +58,7 @@ export declare function OxyServicesDeveloperMixin<T extends typeof OxyServicesBa
58
58
  __resetTokensForTests(): void;
59
59
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
60
60
  getBaseURL(): string;
61
+ getSessionBaseUrl(): string;
61
62
  getClient(): import("../HttpService").HttpService;
62
63
  getMetrics(): {
63
64
  totalRequests: number;
@@ -55,6 +55,7 @@ export declare function OxyServicesDevicesMixin<T extends typeof OxyServicesBase
55
55
  __resetTokensForTests(): void;
56
56
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
57
57
  getBaseURL(): string;
58
+ getSessionBaseUrl(): string;
58
59
  getClient(): import("../HttpService").HttpService;
59
60
  getMetrics(): {
60
61
  totalRequests: number;
@@ -183,6 +183,7 @@ export declare function OxyServicesFeaturesMixin<T extends typeof OxyServicesBas
183
183
  __resetTokensForTests(): void;
184
184
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
185
185
  getBaseURL(): string;
186
+ getSessionBaseUrl(): string;
186
187
  getClient(): import("../HttpService").HttpService;
187
188
  getMetrics(): {
188
189
  totalRequests: number;
@@ -216,11 +217,7 @@ export declare function OxyServicesFeaturesMixin<T extends typeof OxyServicesBas
216
217
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
217
218
  maxRetries?: number;
218
219
  retryDelay?: number;
219
- authTimeoutMs
220
- /**
221
- * Get user statistics
222
- */
223
- ?: number;
220
+ authTimeoutMs?: number;
224
221
  }): Promise<T_1>;
225
222
  validate(): Promise<boolean>;
226
223
  handleError(error: unknown): Error;
@@ -234,12 +234,30 @@ export declare function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(
234
234
  storeLoginHint(userId: string): void;
235
235
  /** @internal */
236
236
  clearLoginHint(): void;
237
+ /**
238
+ * List the authenticated user's authorized RP apps.
239
+ *
240
+ * Returns the intersection of the user's FedCM grants and the currently-
241
+ * approved RP catalog — what powers the "Connected apps" management UI in
242
+ * @oxyhq/services. Requires a real user session; service tokens are
243
+ * rejected by the underlying endpoint.
244
+ */
245
+ listAuthorizedApps(): Promise<AuthorizedApp[]>;
246
+ /**
247
+ * Revoke the authenticated user's authorization for a specific RP origin.
248
+ *
249
+ * The next FedCM sign-in from that origin will require explicit re-consent.
250
+ * The corresponding cache entry is invalidated so a subsequent
251
+ * `listAuthorizedApps()` call sees fresh data.
252
+ */
253
+ revokeAuthorizedApp(origin: string): Promise<void>;
237
254
  httpService: import("../HttpService").HttpService;
238
255
  cloudURL: string;
239
256
  config: import("../OxyServices.base").OxyConfig;
240
257
  __resetTokensForTests(): void;
241
258
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
242
259
  getBaseURL(): string;
260
+ getSessionBaseUrl(): string;
243
261
  getClient(): import("../HttpService").HttpService;
244
262
  getMetrics(): {
245
263
  totalRequests: number;
@@ -292,4 +310,20 @@ export declare function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(
292
310
  */
293
311
  isFedCMSupported(): boolean;
294
312
  } & T;
313
+ /**
314
+ * Public summary of an RP application the user has authorized — mirrors the
315
+ * `AuthorizedAppSummary` shape returned by `GET /fedcm/me/authorized-apps`.
316
+ */
317
+ export interface AuthorizedApp {
318
+ /** Normalised RP origin. */
319
+ origin: string;
320
+ /** Friendly display name. */
321
+ name: string;
322
+ /** Optional human-readable description. */
323
+ description?: string;
324
+ /** ISO-8601 timestamp of when the user first authorized this RP. */
325
+ firstGrantedAt: string;
326
+ /** ISO-8601 timestamp of the most recent FedCM exchange for this user+RP. */
327
+ lastUsedAt: string;
328
+ }
295
329
  export { OxyServicesFedCMMixin as FedCMMixin };
@@ -44,6 +44,7 @@ export declare function OxyServicesKarmaMixin<T extends typeof OxyServicesBase>(
44
44
  __resetTokensForTests(): void;
45
45
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
46
46
  getBaseURL(): string;
47
+ getSessionBaseUrl(): string;
47
48
  getClient(): import("../HttpService").HttpService;
48
49
  getMetrics(): {
49
50
  totalRequests: number;
@@ -40,6 +40,7 @@ export declare function OxyServicesLanguageMixin<T extends typeof OxyServicesBas
40
40
  __resetTokensForTests(): void;
41
41
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
42
42
  getBaseURL(): string;
43
+ getSessionBaseUrl(): string;
43
44
  getClient(): import("../HttpService").HttpService;
44
45
  getMetrics(): {
45
46
  totalRequests: number;
@@ -23,6 +23,7 @@ export declare function OxyServicesLocationMixin<T extends typeof OxyServicesBas
23
23
  __resetTokensForTests(): void;
24
24
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
25
25
  getBaseURL(): string;
26
+ getSessionBaseUrl(): string;
26
27
  getClient(): import("../HttpService").HttpService;
27
28
  getMetrics(): {
28
29
  totalRequests: number;
@@ -80,6 +80,7 @@ export declare function OxyServicesManagedAccountsMixin<T extends typeof OxyServ
80
80
  __resetTokensForTests(): void;
81
81
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
82
82
  getBaseURL(): string;
83
+ getSessionBaseUrl(): string;
83
84
  getClient(): import("../HttpService").HttpService;
84
85
  getMetrics(): {
85
86
  totalRequests: number;
@@ -70,6 +70,7 @@ export declare function OxyServicesPaymentMixin<T extends typeof OxyServicesBase
70
70
  __resetTokensForTests(): void;
71
71
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
72
72
  getBaseURL(): string;
73
+ getSessionBaseUrl(): string;
73
74
  getClient(): import("../HttpService").HttpService;
74
75
  getMetrics(): {
75
76
  totalRequests: number;
@@ -5,6 +5,23 @@ export interface PopupAuthOptions {
5
5
  height?: number;
6
6
  timeout?: number;
7
7
  mode?: 'login' | 'signup';
8
+ /**
9
+ * A popup window handle the caller already opened SYNCHRONOUSLY inside the
10
+ * user-gesture event handler (e.g. an `onClick`). The mixin will navigate
11
+ * this window to the auth URL instead of calling `window.open` itself.
12
+ *
13
+ * Why this exists: Chrome (and other modern browsers) only honor
14
+ * `window.open` while a transient user-activation is in scope. The
15
+ * activation is consumed by the FIRST `await` in the click handler — so any
16
+ * caller that awaits FedCM / silent SSO before reaching `signInWithPopup`
17
+ * loses the activation and sees the popup blocked. The caller can dodge
18
+ * this by opening a blank popup on the raw click via `openBlankPopup()`,
19
+ * then passing the handle in here.
20
+ *
21
+ * `null` is accepted (and is the same as omitting the option) so consumers
22
+ * can pass through the result of `openBlankPopup()` without an extra guard.
23
+ */
24
+ popup?: Window | null;
8
25
  }
9
26
  export interface SilentAuthOptions {
10
27
  timeout?: number;
@@ -100,6 +117,28 @@ export declare function OxyServicesPopupAuthMixin<T extends typeof OxyServicesBa
100
117
  * ```
101
118
  */
102
119
  silentSignIn(options?: SilentAuthOptions): Promise<SessionLoginResponse | null>;
120
+ /**
121
+ * Open a blank, centered popup window SYNCHRONOUSLY.
122
+ *
123
+ * Use this in a click (or other user-gesture) handler BEFORE any `await`
124
+ * to capture the transient user-activation. Pass the returned handle into
125
+ * `signInWithPopup({ popup })` once the async portion of the flow runs.
126
+ *
127
+ * Returns `null` if the browser's popup blocker rejected the open.
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * const onSignInClick = () => {
132
+ * const popup = oxyServices.openBlankPopup();
133
+ * (async () => {
134
+ * const silent = await oxyServices.silentSignInWithFedCM();
135
+ * if (silent) { popup?.close(); return; }
136
+ * await oxyServices.signInWithPopup({ popup });
137
+ * })();
138
+ * };
139
+ * ```
140
+ */
141
+ openBlankPopup(width?: number, height?: number): Window | null;
103
142
  /**
104
143
  * Open a centered popup window
105
144
  *
@@ -160,6 +199,7 @@ export declare function OxyServicesPopupAuthMixin<T extends typeof OxyServicesBa
160
199
  __resetTokensForTests(): void;
161
200
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
162
201
  getBaseURL(): string;
202
+ getSessionBaseUrl(): string;
163
203
  getClient(): import("../HttpService").HttpService;
164
204
  getMetrics(): {
165
205
  totalRequests: number;
@@ -81,6 +81,7 @@ export declare function OxyServicesPrivacyMixin<T extends typeof OxyServicesBase
81
81
  __resetTokensForTests(): void;
82
82
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
83
83
  getBaseURL(): string;
84
+ getSessionBaseUrl(): string;
84
85
  getClient(): import("../HttpService").HttpService;
85
86
  getMetrics(): {
86
87
  totalRequests: number;
@@ -197,6 +197,7 @@ export declare function OxyServicesRedirectAuthMixin<T extends typeof OxyService
197
197
  __resetTokensForTests(): void;
198
198
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
199
199
  getBaseURL(): string;
200
+ getSessionBaseUrl(): string;
200
201
  getClient(): import("../HttpService").HttpService;
201
202
  getMetrics(): {
202
203
  totalRequests: number;
@@ -37,6 +37,7 @@ export declare function OxyServicesSecurityMixin<T extends typeof OxyServicesBas
37
37
  __resetTokensForTests(): void;
38
38
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
39
39
  getBaseURL(): string;
40
+ getSessionBaseUrl(): string;
40
41
  getClient(): import("../HttpService").HttpService;
41
42
  getMetrics(): {
42
43
  totalRequests: number;
@@ -63,6 +63,7 @@ export declare function OxyServicesTopicsMixin<T extends typeof OxyServicesBase>
63
63
  __resetTokensForTests(): void;
64
64
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
65
65
  getBaseURL(): string;
66
+ getSessionBaseUrl(): string;
66
67
  getClient(): import("../HttpService").HttpService;
67
68
  getMetrics(): {
68
69
  totalRequests: number;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * User Management Methods Mixin
3
3
  */
4
- import type { User, Notification, SearchProfilesResponse, PrivacySettings } from '../models/interfaces';
4
+ import type { User, Notification, NotificationPreferences, UserPreferences, SearchProfilesResponse, PrivacySettings } from '../models/interfaces';
5
5
  import type { OxyServicesBase } from '../OxyServices.base';
6
6
  import { type PaginationParams } from '../utils/apiUtils';
7
7
  export declare function OxyServicesUserMixin<T extends typeof OxyServicesBase>(Base: T): {
@@ -116,6 +116,20 @@ export declare function OxyServicesUserMixin<T extends typeof OxyServicesBase>(B
116
116
  * @param userId - The user ID (defaults to current user)
117
117
  */
118
118
  updatePrivacySettings(settings: Partial<PrivacySettings>, userId?: string): Promise<PrivacySettings>;
119
+ /**
120
+ * Update the authenticated user's notification preferences.
121
+ *
122
+ * Thin wrapper over `updateProfile` that constrains the patch to known
123
+ * notification channels — same persistence path, same cache invalidation,
124
+ * but type-safe at the call site.
125
+ */
126
+ updateNotificationPreferences(preferences: Partial<NotificationPreferences>): Promise<User>;
127
+ /**
128
+ * Update the authenticated user's general preferences (language, theme,
129
+ * reduce-motion, timezone). Persisted on the User document via
130
+ * `PUT /users/me` — same cache-invalidation behaviour as `updateProfile`.
131
+ */
132
+ updateUserPreferences(preferences: Partial<UserPreferences>): Promise<User>;
119
133
  /**
120
134
  * Request account verification
121
135
  */
@@ -208,6 +222,7 @@ export declare function OxyServicesUserMixin<T extends typeof OxyServicesBase>(B
208
222
  __resetTokensForTests(): void;
209
223
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
210
224
  getBaseURL(): string;
225
+ getSessionBaseUrl(): string;
211
226
  getClient(): import("../HttpService").HttpService;
212
227
  getMetrics(): {
213
228
  totalRequests: number;
@@ -246,6 +246,7 @@ export declare function OxyServicesUtilityMixin<T extends typeof OxyServicesBase
246
246
  __resetTokensForTests(): void;
247
247
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
248
248
  getBaseURL(): string;
249
+ getSessionBaseUrl(): string;
249
250
  getClient(): import("../HttpService").HttpService;
250
251
  getMetrics(): {
251
252
  totalRequests: number;
@@ -1,6 +1,20 @@
1
1
  export interface OxyConfig {
2
2
  baseURL: string;
3
3
  cloudURL?: string;
4
+ /**
5
+ * Base URL the SDK's first-party session/refresh calls target.
6
+ *
7
+ * Per the 2026 session architecture (docs/SESSION-ARCHITECTURE.md), every app
8
+ * keeps its OWN first-party session on its OWN domain. For non-`oxy.so` apps
9
+ * this is the app's own same-site backend (e.g. `https://api.mention.earth`),
10
+ * whose session bridge forwards the user's refresh credential to
11
+ * `api.oxy.so`. For `*.oxy.so` apps this is omitted and falls back to
12
+ * `baseURL` (`https://api.oxy.so`), so their behavior is unchanged.
13
+ *
14
+ * Resolve via {@link OxyServices.getSessionBaseUrl}; when unset it returns
15
+ * `baseURL`. This is purely additive — no refresh/auth logic reads it yet.
16
+ */
17
+ sessionBaseUrl?: string;
4
18
  authWebUrl?: string;
5
19
  authRedirectUri?: string;
6
20
  enableCache?: boolean;
@@ -98,8 +112,37 @@ export interface User {
98
112
  };
99
113
  isManagedAccount?: boolean;
100
114
  managedBy?: string;
115
+ notificationPreferences?: NotificationPreferences;
116
+ userPreferences?: UserPreferences;
101
117
  [key: string]: unknown;
102
118
  }
119
+ /**
120
+ * User-controlled notification channels. Persisted on the User document.
121
+ */
122
+ export interface NotificationPreferences {
123
+ /** Push notifications on registered devices. */
124
+ pushEnabled?: boolean;
125
+ /** Periodic email digest of activity. */
126
+ emailDigest?: boolean;
127
+ /** Security/account alerts (sign-ins, recovery, key changes). */
128
+ securityAlerts?: boolean;
129
+ /** Marketing / product update emails. */
130
+ marketingEmails?: boolean;
131
+ }
132
+ /**
133
+ * General per-user preferences applied across all Oxy apps for the user.
134
+ * Persisted on the User document.
135
+ */
136
+ export interface UserPreferences {
137
+ /** BCP-47 language tag, e.g. "en-US", "es-ES". Empty string = follow device. */
138
+ language?: string;
139
+ /** Theme mode preference. */
140
+ theme?: 'light' | 'dark' | 'system';
141
+ /** Mirror of OS reduce-motion preference, persisted server-side. */
142
+ reduceMotion?: boolean;
143
+ /** IANA timezone, e.g. "Europe/Madrid". Empty string = follow device. */
144
+ timezone?: string;
145
+ }
103
146
  export interface LoginResponse {
104
147
  accessToken?: string;
105
148
  refreshToken?: string;
@@ -488,3 +531,58 @@ export interface UpdateDeviceNameResponse {
488
531
  message: string;
489
532
  deviceName: string;
490
533
  }
534
+ /**
535
+ * Minimal user shape included in a `RefreshAllAccount` entry. The server
536
+ * projects a small whitelist (`username name avatar email color`) so the
537
+ * client can render the account chooser without an extra `/users/me` round
538
+ * trip per account.
539
+ *
540
+ * `avatar` and `color` are `string | null` because they are stored as nullable
541
+ * fields in the user document.
542
+ */
543
+ export interface RefreshAllAccountUser {
544
+ id: string;
545
+ username: string;
546
+ name?: string;
547
+ avatar?: string | null;
548
+ email?: string;
549
+ color?: string | null;
550
+ }
551
+ /**
552
+ * One rotated account entry returned by `POST /auth/refresh-all`. `authuser` is
553
+ * the device-local slot index (0..N-1) the cookie was bound to. The legacy
554
+ * un-suffixed `oxy_rt` cookie yields `authuser: null` server-side, but the SDK
555
+ * normalises that to `0` before exposing it (the chooser always operates on
556
+ * numeric indices).
557
+ *
558
+ * `user` is `null` only on the SDK-side synthesised legacy fallback (when the
559
+ * server is too old to support `/auth/refresh-all` and we wrap a
560
+ * `/auth/refresh` response — that endpoint does not project a user shape).
561
+ * On the modern path every accepted entry carries a non-null user.
562
+ */
563
+ export interface RefreshAllAccount {
564
+ authuser: number;
565
+ accessToken: string;
566
+ expiresAt: string;
567
+ sessionId: string;
568
+ user: RefreshAllAccountUser | null;
569
+ }
570
+ /**
571
+ * Wire shape of `POST /auth/refresh-all`. Always 200 with a (possibly empty)
572
+ * accounts array — 401 means "no accounts signed in on this device" and is
573
+ * normalised to `{ accounts: [] }` at the SDK layer.
574
+ */
575
+ export interface RefreshAllResponse {
576
+ accounts: RefreshAllAccount[];
577
+ }
578
+ /**
579
+ * Wire shape of `POST /auth/refresh` (single-account refresh, optionally
580
+ * targeting a specific `?authuser=N` slot). The server includes `authuser` in
581
+ * the response when an indexed slot was rotated; the legacy slot yields
582
+ * `authuser: null`.
583
+ */
584
+ export interface RefreshCookieResponse {
585
+ accessToken: string;
586
+ expiresAt: string;
587
+ authuser: number | null;
588
+ }
@@ -5,6 +5,14 @@ export interface ClientSession {
5
5
  lastActive: string;
6
6
  userId?: string;
7
7
  isCurrent?: boolean;
8
+ /**
9
+ * Web-only: the device-local refresh-cookie slot index (0..N) that backs
10
+ * this session. Populated from `POST /auth/refresh-all` and from login /
11
+ * signup / fedcm-exchange responses. Required for per-session web token
12
+ * refresh via `refreshTokenViaCookie({ authuser })` without a bearer token.
13
+ * Absent on native (RN uses the bearer-protected session id directly).
14
+ */
15
+ authuser?: number;
8
16
  }
9
17
  export interface StorageKeys {
10
18
  sessions: string;
@@ -2,6 +2,7 @@
2
2
  * Shared account types and pure helper functions.
3
3
  * Used by both @oxyhq/services (React Native) and @oxyhq/auth (Web) account stores.
4
4
  */
5
+ import type { RefreshAllAccount } from '../models/interfaces';
5
6
  export interface QuickAccount {
6
7
  sessionId: string;
7
8
  userId?: string;
@@ -9,6 +10,19 @@ export interface QuickAccount {
9
10
  displayName: string;
10
11
  avatar?: string;
11
12
  avatarUrl?: string;
13
+ /**
14
+ * Device-local account slot index, 0..N-1 (Google-style multi-account).
15
+ * Mirrors the server's `oxy_rt_${authuser}` cookie slot. Optional so that
16
+ * pre-multi-account QuickAccounts (sessionId-only, non-cookie auth on RN)
17
+ * remain valid; web flows always populate it after `refreshAllSessions`.
18
+ */
19
+ authuser?: number;
20
+ /**
21
+ * Account's preferred Bloom color preset (e.g. `"blue"`, `"oxy"`). Drives
22
+ * per-account theming in the account chooser. `null` / `undefined` means
23
+ * the account has no preference and the base theme should be used.
24
+ */
25
+ color?: string | null;
12
26
  }
13
27
  /** Minimal user shape accepted by display-name helpers. Avoids importing the full User type. */
14
28
  export interface DisplayNameUserShape {
@@ -74,3 +88,22 @@ export declare const createQuickAccount: (sessionId: string, userData: {
74
88
  } | string;
75
89
  avatar?: string;
76
90
  }, existingAccount?: QuickAccount, getFileDownloadUrl?: (fileId: string, variant: string) => string) => QuickAccount;
91
+ /**
92
+ * Merge a fresh `/auth/refresh-all` snapshot into an existing QuickAccount
93
+ * list, preserving any cached fields (`avatarUrl`) for slots that didn't
94
+ * change. The fresh response is canonical: the resulting list contains EXACTLY
95
+ * the slots present in `fresh`, sorted by `authuser` ascending. Stale stored
96
+ * accounts that no longer appear in `fresh` are dropped (the server already
97
+ * authoritatively cleared the corresponding cookie).
98
+ *
99
+ * @param stored Previously persisted QuickAccount list (any order).
100
+ * @param fresh Server's authoritative refresh-all response.
101
+ * @returns Canonical merged list, sorted by `authuser` asc.
102
+ */
103
+ export declare const mergeAccountsFromRefreshAll: (stored: QuickAccount[] | undefined, fresh: RefreshAllAccount[]) => QuickAccount[];
104
+ /**
105
+ * Return the account's preferred Bloom color preset, or `null` if it has no
106
+ * preference. Centralises the `color ?? null` normalisation so consumers can
107
+ * drive per-account theming without duplicating the nullish-handling.
108
+ */
109
+ export declare const getAccountColor: (account: QuickAccount) => string | null;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * coldBoot — a pure, ordered, short-circuit runner for "cold boot"
3
+ * authentication resolution.
4
+ *
5
+ * On a fresh page load / app launch the SDK may have several ways to recover an
6
+ * existing session (silent FedCM, a persisted refresh token, a cross-domain
7
+ * claim, an explicit popup flow, …). They must be attempted in a *deterministic
8
+ * order*, and the FIRST one that yields a session wins — every later step is
9
+ * skipped. This module encodes exactly that contract and nothing else.
10
+ *
11
+ * Design constraints (all enforced):
12
+ * - PURE: no DOM, no `navigator`, no `window`, no React, no platform globals.
13
+ * - NO module-level mutable state. Every call to {@link runColdBoot} is fully
14
+ * self-contained, so it is safe under bundler re-evaluation (e.g. the Metro
15
+ * web bundle, which is precisely why the FedCM silent-SSO guard had to live
16
+ * in consumers rather than a core singleton).
17
+ * - Architecture-agnostic: both candidate cross-domain SSO designs consume
18
+ * this same primitive; it knows nothing about HOW a step resolves a session.
19
+ *
20
+ * A step is skipped (without running) when its `enabled` predicate returns
21
+ * false. Any thrown error — from either `enabled` or `run` — is reported via
22
+ * `onStepError` and treated as a non-fatal skip, so one broken recovery path
23
+ * can never prevent a later, healthy one from succeeding.
24
+ */
25
+ /**
26
+ * A successful step result carrying the recovered session.
27
+ */
28
+ export interface ColdBootSession<S> {
29
+ readonly kind: 'session';
30
+ readonly session: S;
31
+ }
32
+ /**
33
+ * A step result indicating this step has nothing to contribute; the runner
34
+ * should fall through to the next step.
35
+ */
36
+ export interface ColdBootSkip {
37
+ readonly kind: 'skip';
38
+ }
39
+ /**
40
+ * The result of running a single cold-boot step.
41
+ */
42
+ export type ColdBootStepResult<S> = ColdBootSession<S> | ColdBootSkip;
43
+ /**
44
+ * A single ordered cold-boot recovery step.
45
+ */
46
+ export interface ColdBootStep<S> {
47
+ /** Stable identifier; surfaced as {@link ColdBootOutcome.via} on success. */
48
+ readonly id: string;
49
+ /**
50
+ * Optional gate. When provided and it returns `false`, `run` is NOT called
51
+ * and the runner moves to the next step. A throw is treated as disabled
52
+ * (and reported via `onStepError`).
53
+ */
54
+ readonly enabled?: () => boolean;
55
+ /**
56
+ * Attempts to recover a session. Resolve with `{ kind: 'session' }` to win
57
+ * the cold boot, or `{ kind: 'skip' }` to defer to the next step. A throw is
58
+ * treated as a skip (and reported via `onStepError`).
59
+ */
60
+ readonly run: () => Promise<ColdBootStepResult<S>>;
61
+ }
62
+ /**
63
+ * The terminal outcome of a cold boot: either the winning step's session
64
+ * (with the step `id` it came from), or `unauthenticated` if every step
65
+ * skipped, was disabled, or errored.
66
+ */
67
+ export type ColdBootOutcome<S> = {
68
+ readonly kind: 'session';
69
+ readonly via: string;
70
+ readonly session: S;
71
+ } | {
72
+ readonly kind: 'unauthenticated';
73
+ };
74
+ /**
75
+ * Options for {@link runColdBoot}.
76
+ */
77
+ export interface RunColdBootOptions<S> {
78
+ /** Ordered steps; evaluated front to back, first session wins. */
79
+ readonly steps: ReadonlyArray<ColdBootStep<S>>;
80
+ /**
81
+ * Optional observer invoked whenever a step's `enabled` or `run` throws.
82
+ * Receives the offending step `id` and the thrown value. Must not throw;
83
+ * the runner does not guard against an observer that itself throws.
84
+ */
85
+ readonly onStepError?: (id: string, error: unknown) => void;
86
+ }
87
+ /**
88
+ * Run the ordered cold-boot steps and resolve to the first recovered session,
89
+ * or `unauthenticated` if none recovers one.
90
+ *
91
+ * Semantics:
92
+ * 1. Iterate `steps` in order.
93
+ * 2. If a step has an `enabled` predicate, call it inside try/catch:
94
+ * - throw → report via `onStepError(id, err)` → treat as disabled → continue.
95
+ * - returns false → continue (skip, `run` not called).
96
+ * 3. Otherwise await `step.run()` inside try/catch:
97
+ * - throw → report via `onStepError(id, err)` → continue.
98
+ * - `{ kind: 'session' }` → return `{ kind: 'session', via: step.id, session }`.
99
+ * - `{ kind: 'skip' }` → continue.
100
+ * 4. After the loop with no winner → `{ kind: 'unauthenticated' }`.
101
+ */
102
+ export declare function runColdBoot<S>(options: RunColdBootOptions<S>): Promise<ColdBootOutcome<S>>;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Auto-detect the FAPI (IdP) URL from the current browser hostname.
3
+ *
4
+ * This is the canonical cross-domain IdP-resolution primitive for the Oxy
5
+ * ecosystem. Both candidate cross-domain SSO designs derive `auth.<rp-apex>`
6
+ * through this helper; do not fork it.
7
+ *
8
+ * Clerk-style multi-domain SSO depends on the IdP being reachable on a
9
+ * subdomain of the RP's own apex (e.g. `auth.mention.earth` CNAMEd to the
10
+ * central Oxy IdP). That way every FedCM endpoint, the session cookie,
11
+ * and any popup/redirect target are same-site with the RP — the only way
12
+ * to get first-party cookies in Safari ITP and Firefox Total Cookie
13
+ * Protection.
14
+ *
15
+ * This helper computes `https://auth.<rp-apex>` from
16
+ * `window.location.hostname` so a consuming app doesn't have to pass
17
+ * `authWebUrl` explicitly. Returns `undefined` for environments where
18
+ * auto-detection would be wrong:
19
+ *
20
+ * - SSR / non-browser (no `window`).
21
+ * - `localhost`, `127.0.0.1`, IPv4/IPv6 literals.
22
+ * - Hostnames with fewer than two labels.
23
+ * - Hostnames whose trailing two labels form a known multi-part public
24
+ * suffix (e.g. `co.uk`), where the naive `labels.slice(-2)` apex would be
25
+ * an attacker-registrable suffix like `auth.co.uk` rather than the real
26
+ * registrable domain.
27
+ *
28
+ * When the page is already loaded ON the IdP itself (`auth.<anything>`),
29
+ * the helper returns the current origin so the SDK keeps everything
30
+ * same-origin instead of hopping to a different IdP host.
31
+ *
32
+ * The IdP backend independently derives `iss`, `provider_urls`, and the
33
+ * `fedcm.json` icon URLs from the request host
34
+ * (`packages/auth/server/index.ts`), so an honest CNAME pair is all that
35
+ * is required for end-to-end FedCM correctness — no per-RP config.
36
+ */
37
+ export declare function autoDetectAuthWebUrl(location?: Pick<Location, 'hostname' | 'protocol'> | undefined): string | undefined;