@oxyhq/core 6.0.0 → 7.0.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 (53) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/HttpService.js +1 -1
  3. package/dist/cjs/index.js +17 -11
  4. package/dist/cjs/mixins/OxyServices.accounts.js +7 -13
  5. package/dist/cjs/mixins/index.js +0 -5
  6. package/dist/cjs/server/index.js +2 -2
  7. package/dist/cjs/shared/utils/debugUtils.js +3 -3
  8. package/dist/cjs/utils/authWebUrl.js +10 -30
  9. package/dist/cjs/utils/coldBoot.js +10 -8
  10. package/dist/cjs/utils/{fapiAutoDetect.js → registrableApex.js} +8 -10
  11. package/dist/esm/.tsbuildinfo +1 -1
  12. package/dist/esm/HttpService.js +1 -1
  13. package/dist/esm/index.js +15 -8
  14. package/dist/esm/mixins/OxyServices.accounts.js +7 -13
  15. package/dist/esm/mixins/index.js +0 -5
  16. package/dist/esm/server/index.js +1 -1
  17. package/dist/esm/shared/utils/debugUtils.js +3 -3
  18. package/dist/esm/utils/authWebUrl.js +9 -29
  19. package/dist/esm/utils/coldBoot.js +10 -8
  20. package/dist/esm/utils/{fapiAutoDetect.js → registrableApex.js} +8 -10
  21. package/dist/types/.tsbuildinfo +1 -1
  22. package/dist/types/HttpService.d.ts +1 -1
  23. package/dist/types/index.d.ts +2 -3
  24. package/dist/types/mixins/OxyServices.accounts.d.ts +24 -27
  25. package/dist/types/mixins/index.d.ts +1 -2
  26. package/dist/types/models/session.d.ts +4 -5
  27. package/dist/types/server/index.d.ts +1 -1
  28. package/dist/types/session/SessionClient.d.ts +1 -1
  29. package/dist/types/shared/utils/debugUtils.d.ts +3 -3
  30. package/dist/types/utils/accountUtils.d.ts +2 -3
  31. package/dist/types/utils/authWebUrl.d.ts +9 -29
  32. package/dist/types/utils/coldBoot.d.ts +17 -14
  33. package/dist/types/utils/{fapiAutoDetect.d.ts → registrableApex.d.ts} +8 -10
  34. package/package.json +2 -2
  35. package/src/HttpService.ts +1 -1
  36. package/src/index.ts +12 -11
  37. package/src/mixins/OxyServices.accounts.ts +21 -26
  38. package/src/mixins/index.ts +0 -7
  39. package/src/models/session.ts +4 -5
  40. package/src/server/index.ts +1 -1
  41. package/src/session/SessionClient.ts +1 -1
  42. package/src/shared/utils/debugUtils.ts +3 -3
  43. package/src/utils/__tests__/authWebUrl.test.ts +5 -16
  44. package/src/utils/__tests__/{fapiAutoDetect.test.ts → registrableApex.test.ts} +1 -1
  45. package/src/utils/accountUtils.ts +2 -3
  46. package/src/utils/authWebUrl.ts +9 -30
  47. package/src/utils/coldBoot.ts +17 -14
  48. package/src/utils/{fapiAutoDetect.ts → registrableApex.ts} +8 -10
  49. package/dist/cjs/mixins/OxyServices.authorizedApps.js +0 -38
  50. package/dist/esm/mixins/OxyServices.authorizedApps.js +0 -35
  51. package/dist/types/mixins/OxyServices.authorizedApps.d.ts +0 -94
  52. package/src/mixins/OxyServices.authorizedApps.ts +0 -75
  53. package/src/mixins/__tests__/authorizedApps.test.ts +0 -63
@@ -1,26 +1,15 @@
1
1
  /**
2
- * Central IdP apex/origin constants.
2
+ * Central IdP apex constant.
3
3
  *
4
- * `resolveCentralAuthUrl` was removed in the device-first cutover (the SDK no
5
- * longer resolves an IdP host). The `CENTRAL_IDP_APEX` / `CENTRAL_AUTH_URL`
6
- * constants survive because the IdP worker imports them to brand assertions.
4
+ * `resolveCentralAuthUrl` + `CENTRAL_AUTH_URL` were removed in the device-first /
5
+ * legacy-final cutovers. `CENTRAL_IDP_APEX` survives because live server-side
6
+ * callers (the `@oxyhq/core/server` CORS helper + the IdP worker) import it.
7
7
  */
8
8
 
9
- import { CENTRAL_AUTH_URL, CENTRAL_IDP_APEX } from '../authWebUrl';
9
+ import { CENTRAL_IDP_APEX } from '../authWebUrl';
10
10
 
11
11
  describe('CENTRAL_IDP_APEX', () => {
12
12
  it('is the central IdP registrable apex', () => {
13
13
  expect(CENTRAL_IDP_APEX).toBe('oxy.so');
14
14
  });
15
15
  });
16
-
17
- describe('CENTRAL_AUTH_URL', () => {
18
- it('is the central IdP origin with no trailing slash', () => {
19
- expect(CENTRAL_AUTH_URL).toBe('https://auth.oxy.so');
20
- expect(CENTRAL_AUTH_URL.endsWith('/')).toBe(false);
21
- });
22
-
23
- it('is derived from CENTRAL_IDP_APEX (apex and origin never drift)', () => {
24
- expect(CENTRAL_AUTH_URL).toBe(`https://auth.${CENTRAL_IDP_APEX}`);
25
- });
26
- });
@@ -6,7 +6,7 @@
6
6
  * surface + the IdP). These cases lock its Public-Suffix-List behaviour.
7
7
  */
8
8
 
9
- import { registrableApex } from '../fapiAutoDetect';
9
+ import { registrableApex } from '../registrableApex';
10
10
 
11
11
  describe('registrableApex', () => {
12
12
  it('returns the eTLD+1 for a normal two-label host', () => {
@@ -14,9 +14,8 @@ export interface QuickAccount {
14
14
  avatarUrl?: string;
15
15
  /**
16
16
  * Device-local account slot index, 0..N-1 (Google-style multi-account).
17
- * Mirrors the server's `oxy_rt_${authuser}` cookie slot. Optional so that
18
- * pre-multi-account QuickAccounts (sessionId-only, non-cookie auth on RN)
19
- * remain valid; web flows always populate it after `refreshAllSessions`.
17
+ * Optional so that pre-multi-account QuickAccounts (sessionId-only) remain
18
+ * valid; the device session set populates it where available.
20
19
  */
21
20
  authuser?: number;
22
21
  /**
@@ -1,38 +1,17 @@
1
1
  /**
2
- * Central IdP (auth web) URL resolution for cross-domain SSO.
2
+ * Central IdP apex constant.
3
3
  *
4
- * The Oxy ecosystem runs a single, central Identity Provider at
5
- * `auth.oxy.so`. For TRUE central cross-domain SSO (Google/Meta/Clerk style),
6
- * FedCM and the opaque-code SSO bounce always target this one origin it owns
7
- * the host-only `fedcm_session` cookie and the central session store reachable
8
- * via `api.oxy.so`. Relying Parties (mention.earth, homiio.com, alia.onl, …)
9
- * delegate to it rather than standing up a per-apex IdP.
10
- *
11
- * This module is intentionally pure: it performs no DOM access, reads no
12
- * `window`/`location`, and has no side effects. It is the single source of
13
- * truth for the central IdP origin so call sites never hardcode the literal.
14
- *
15
- * LEGACY(old-sdk): the client resolver (`resolveCentralAuthUrl`) was removed in
16
- * the device-first cutover; `CENTRAL_IDP_APEX` / `CENTRAL_AUTH_URL` survive ONLY
17
- * because the lista-B IdP worker imports them to brand assertions. Deletable
18
- * once Homiio/Allo/Alia/Syra are bumped off the old SDK AND CloudWatch
19
- * `/oxy/ecs` shows the `/sso*` + `/fedcm/*` routes quiet — F-final sweep.
4
+ * The client SSO/FedCM resolvers (`resolveCentralAuthUrl`, `CENTRAL_AUTH_URL`)
5
+ * were removed in the device-first / legacy-final cutovers. The lone survivor is
6
+ * `CENTRAL_IDP_APEX`, kept because it has a LIVE consumer
7
+ * `@oxyhq/core/server`'s CORS helper (`server/cors.ts`'s `createOxyCors`)
8
+ * auto-allows `*.oxy.so` from it. That CORS use is permanent, so this stays
9
+ * past the SSO/FedCM teardown.
20
10
  */
21
11
 
22
12
  /**
23
13
  * The registrable apex (eTLD+1) of the Oxy ecosystem's central Identity
24
- * Provider. The central IdP is reachable at `auth.${CENTRAL_IDP_APEX}` and the
25
- * ID-token assertion issuer is always `https://auth.${CENTRAL_IDP_APEX}`
26
- * regardless of which per-apex `auth.<rp>` host served a given request.
27
- *
28
- * Kept as a standalone constant so the IdP worker and the SDK derive the same
29
- * literal from one source of truth (the worker imports it to brand assertions).
14
+ * Provider, reachable at `auth.${CENTRAL_IDP_APEX}`. Single source of truth so
15
+ * the CORS helper (and anything else that needs the central apex) never drifts.
30
16
  */
31
17
  export const CENTRAL_IDP_APEX = 'oxy.so';
32
-
33
- /**
34
- * The canonical central Identity Provider origin for the Oxy ecosystem.
35
- * No trailing slash. Derived from {@link CENTRAL_IDP_APEX} so the apex and the
36
- * full origin never drift apart. The IdP worker imports it to brand assertions.
37
- */
38
- export const CENTRAL_AUTH_URL = `https://auth.${CENTRAL_IDP_APEX}`;
@@ -3,19 +3,21 @@
3
3
  * authentication resolution.
4
4
  *
5
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, a redirect SSO return, ...). 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.
6
+ * existing session (a persisted refresh-token family, a shared-keychain
7
+ * identity, a cross-domain boot-fragment return, ...). They must be attempted
8
+ * in a deterministic order, and the FIRST one that yields a session wins —
9
+ * every later step is skipped. This module encodes exactly that contract and
10
+ * nothing else.
10
11
  *
11
12
  * Design constraints (all enforced):
12
13
  * - PURE: no DOM, no `navigator`, no `window`, no React, no platform globals.
13
14
  * - NO module-level mutable state. Every call to {@link runColdBoot} is fully
14
15
  * 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.
16
+ * web bundle the reason any run-once guard for a step must live in the
17
+ * calling consumer, never in a core module-level singleton).
18
+ * - Architecture-agnostic: it knows nothing about HOW a step resolves a
19
+ * session; `runSessionColdBoot` (`boot/coldBootV2.ts`) is the current
20
+ * device-first consumer.
19
21
  *
20
22
  * A step is skipped (without running) when its `enabled` predicate returns
21
23
  * false. Any thrown error — from either `enabled` or `run` — is reported via
@@ -104,15 +106,16 @@ export interface RunColdBootOptions<S> {
104
106
  * fails to settle before the deadline, the runner abandons the await for that
105
107
  * step (reporting it via `onStepDeadline`) and CONTINUES to the next step,
106
108
  * each now racing against an already-expired deadline. This is deliberate:
107
- * the runner keeps iterating so the TERMINAL step (e.g. the `/sso` bounce,
108
- * whose `run()` performs its side effect synchronously before its first
109
- * `await`) still gets to fire. A step that has nothing to contribute after
110
- * the deadline simply doesn't settle and is skipped in turn.
109
+ * the runner keeps iterating so the TERMINAL step (e.g. `coldBootV2`'s
110
+ * `bootstrap-hop`, whose `run()` performs its navigation side effect
111
+ * synchronously before its first `await`) still gets to fire. A step that
112
+ * has nothing to contribute after the deadline simply doesn't settle and is
113
+ * skipped in turn.
111
114
  *
112
115
  * Per-step timeouts inside `run()` remain the first line of defense and
113
116
  * should keep every step well under this budget on a healthy load; this only
114
- * trips when one of them regresses (the production FedCM-silent hang). When
115
- * omitted there is no overall deadline.
117
+ * trips when one of them regresses (a step hanging past its own timeout).
118
+ * When omitted there is no overall deadline.
116
119
  */
117
120
  readonly overallDeadlineMs?: number;
118
121
  /**
@@ -1,17 +1,15 @@
1
1
  /**
2
2
  * Registrable-apex (eTLD+1) host kernel.
3
3
  *
4
- * The client FAPI auto-detection helper (`autoDetectAuthWebUrl`) was removed in
5
- * the device-first cutover the SDK no longer derives a per-apex `auth.<rp>`
6
- * IdP host. What survives is the pure registrable-domain kernel, still used by
7
- * the api SSO surface, `fedcm.service`, `deviceAuth` (same-apex trust checks),
8
- * and the IdP worker (all lista B / server-side). `@oxyhq/core/server`
9
- * re-exports it for the api.
4
+ * The client FAPI auto-detection helper was removed in the device-first cutover
5
+ * (which is why this file is now named for what it actually is, not the old
6
+ * `fapiAutoDetect`). What survives is the pure registrable-domain kernel, still
7
+ * used server-side by the api's device-first same-apex trust checks
8
+ * (`deviceAuth.ts`'s `POST /auth/device/web-session`, via `sameSite.ts`'s
9
+ * `isSameSiteTrustedRequest`) and the `@oxyhq/core/server` CORS/re-export layer.
10
10
  *
11
- * LEGACY(old-sdk): `registrableApex` survives ONLY for the lista-B server/IdP
12
- * SSO surface. Deletable once Homiio/Allo/Alia/Syra are bumped off the old SDK
13
- * AND CloudWatch `/oxy/ecs` shows the `/sso*` + `/fedcm/*` routes quiet — the
14
- * F-final sweep should remove this file then.
11
+ * `registrableApex` is NOT legacy the device-first same-apex check is a live
12
+ * consumer, so this kernel stays regardless of the SSO/FedCM removal.
15
13
  */
16
14
 
17
15
  import { getDomain } from 'tldts';
@@ -1,38 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OxyServicesAuthorizedAppsMixin = OxyServicesAuthorizedAppsMixin;
4
- function OxyServicesAuthorizedAppsMixin(Base) {
5
- return class extends Base {
6
- /**
7
- * List the authenticated user's authorized RP apps — the intersection of the
8
- * user's FedCM grants and the currently-approved RP catalog. Powers the
9
- * "Connected apps" management UI. Requires a real user session.
10
- */
11
- async listAuthorizedApps() {
12
- try {
13
- const response = await this.makeRequest('GET', '/fedcm/me/authorized-apps', undefined, {
14
- cache: true,
15
- cacheTTL: 30 * 1000, // 30 second cache — short, this drives a manageable UI
16
- });
17
- return response?.apps ?? [];
18
- }
19
- catch (error) {
20
- throw this.handleError(error);
21
- }
22
- }
23
- /**
24
- * Revoke the authenticated user's authorization for a specific RP origin.
25
- * The corresponding cache entry is invalidated so a subsequent
26
- * `listAuthorizedApps()` sees fresh data.
27
- */
28
- async revokeAuthorizedApp(origin) {
29
- try {
30
- await this.makeRequest('DELETE', `/fedcm/me/authorized-apps/${encodeURIComponent(origin)}`, undefined, { cache: false });
31
- this.clearCacheEntry('GET:/fedcm/me/authorized-apps');
32
- }
33
- catch (error) {
34
- throw this.handleError(error);
35
- }
36
- }
37
- };
38
- }
@@ -1,35 +0,0 @@
1
- export function OxyServicesAuthorizedAppsMixin(Base) {
2
- return class extends Base {
3
- /**
4
- * List the authenticated user's authorized RP apps — the intersection of the
5
- * user's FedCM grants and the currently-approved RP catalog. Powers the
6
- * "Connected apps" management UI. Requires a real user session.
7
- */
8
- async listAuthorizedApps() {
9
- try {
10
- const response = await this.makeRequest('GET', '/fedcm/me/authorized-apps', undefined, {
11
- cache: true,
12
- cacheTTL: 30 * 1000, // 30 second cache — short, this drives a manageable UI
13
- });
14
- return response?.apps ?? [];
15
- }
16
- catch (error) {
17
- throw this.handleError(error);
18
- }
19
- }
20
- /**
21
- * Revoke the authenticated user's authorization for a specific RP origin.
22
- * The corresponding cache entry is invalidated so a subsequent
23
- * `listAuthorizedApps()` sees fresh data.
24
- */
25
- async revokeAuthorizedApp(origin) {
26
- try {
27
- await this.makeRequest('DELETE', `/fedcm/me/authorized-apps/${encodeURIComponent(origin)}`, undefined, { cache: false });
28
- this.clearCacheEntry('GET:/fedcm/me/authorized-apps');
29
- }
30
- catch (error) {
31
- throw this.handleError(error);
32
- }
33
- }
34
- };
35
- }
@@ -1,94 +0,0 @@
1
- /**
2
- * Authorized-apps mixin — the "Connected apps" management surface that survived
3
- * the FedCM removal.
4
- *
5
- * These two methods read/revoke the user's LEGACY FedCM authorization grants
6
- * (`GET`/`DELETE /fedcm/me/authorized-apps`). They are the last live consumers
7
- * of that endpoint (services' `ConnectedAppsScreen` via `useAccountQueries` /
8
- * `useAccountMutations`) and stay until the AppGrant migration retires the
9
- * FedCM grant store. All FedCM SIGN-IN machinery was deleted with
10
- * `OxyServices.fedcm.ts`; this is management-only, so it has no dependency on
11
- * the credential/nonce pipeline.
12
- */
13
- import type { OxyServicesBase } from '../OxyServices.base';
14
- /**
15
- * Public summary of an RP application the user has authorized — mirrors the
16
- * `AuthorizedAppSummary` shape returned by `GET /fedcm/me/authorized-apps`.
17
- */
18
- export interface AuthorizedApp {
19
- /** Normalised RP origin. */
20
- origin: string;
21
- /** Friendly display name. */
22
- name: string;
23
- /** Optional human-readable description. */
24
- description?: string;
25
- /** ISO-8601 timestamp of when the user first authorized this RP. */
26
- firstGrantedAt: string;
27
- /** ISO-8601 timestamp of the most recent FedCM exchange for this user+RP. */
28
- lastUsedAt: string;
29
- }
30
- export declare function OxyServicesAuthorizedAppsMixin<T extends typeof OxyServicesBase>(Base: T): {
31
- new (...args: any[]): {
32
- /**
33
- * List the authenticated user's authorized RP apps — the intersection of the
34
- * user's FedCM grants and the currently-approved RP catalog. Powers the
35
- * "Connected apps" management UI. Requires a real user session.
36
- */
37
- listAuthorizedApps(): Promise<AuthorizedApp[]>;
38
- /**
39
- * Revoke the authenticated user's authorization for a specific RP origin.
40
- * The corresponding cache entry is invalidated so a subsequent
41
- * `listAuthorizedApps()` sees fresh data.
42
- */
43
- revokeAuthorizedApp(origin: string): Promise<void>;
44
- httpService: import("../HttpService").HttpService;
45
- cloudURL: string;
46
- config: import("../OxyServices.base").OxyConfig;
47
- __resetTokensForTests(): void;
48
- makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
49
- getBaseURL(): string;
50
- getClient(): import("../HttpService").HttpService;
51
- createLinkedClient(config: import("../OxyServices.base").OxyConfig): import("..").LinkedHttpClient;
52
- getMetrics(): {
53
- totalRequests: number;
54
- successfulRequests: number;
55
- failedRequests: number;
56
- cacheHits: number;
57
- cacheMisses: number;
58
- averageResponseTime: number;
59
- };
60
- clearCache(): void;
61
- clearCacheEntry(key: string): void;
62
- clearCacheByPrefix(prefix: string): number;
63
- getCacheStats(): {
64
- size: number;
65
- hits: number;
66
- misses: number;
67
- hitRate: number;
68
- };
69
- getCloudURL(): string;
70
- setTokens(accessToken: string): void;
71
- clearTokens(): void;
72
- onTokensChanged(listener: (accessToken: string | null) => void): () => void;
73
- _cachedUserId: string | null | undefined;
74
- _cachedAccessToken: string | null;
75
- getCurrentUserId(): string | null;
76
- hasValidToken(): boolean;
77
- getAccessToken(): string | null;
78
- getAccessTokenExpiry(): number | null;
79
- waitForAuth(timeoutMs?: number): Promise<boolean>;
80
- withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
81
- maxRetries?: number;
82
- retryDelay?: number;
83
- authTimeoutMs?: number;
84
- }): Promise<T_1>;
85
- validate(): Promise<boolean>;
86
- handleError(error: unknown): Error;
87
- healthCheck(): Promise<{
88
- status: string;
89
- users?: number;
90
- timestamp?: string;
91
- [key: string]: any;
92
- }>;
93
- };
94
- } & T;
@@ -1,75 +0,0 @@
1
- /**
2
- * Authorized-apps mixin — the "Connected apps" management surface that survived
3
- * the FedCM removal.
4
- *
5
- * These two methods read/revoke the user's LEGACY FedCM authorization grants
6
- * (`GET`/`DELETE /fedcm/me/authorized-apps`). They are the last live consumers
7
- * of that endpoint (services' `ConnectedAppsScreen` via `useAccountQueries` /
8
- * `useAccountMutations`) and stay until the AppGrant migration retires the
9
- * FedCM grant store. All FedCM SIGN-IN machinery was deleted with
10
- * `OxyServices.fedcm.ts`; this is management-only, so it has no dependency on
11
- * the credential/nonce pipeline.
12
- */
13
- import type { OxyServicesBase } from '../OxyServices.base';
14
-
15
- /**
16
- * Public summary of an RP application the user has authorized — mirrors the
17
- * `AuthorizedAppSummary` shape returned by `GET /fedcm/me/authorized-apps`.
18
- */
19
- export interface AuthorizedApp {
20
- /** Normalised RP origin. */
21
- origin: string;
22
- /** Friendly display name. */
23
- name: string;
24
- /** Optional human-readable description. */
25
- description?: string;
26
- /** ISO-8601 timestamp of when the user first authorized this RP. */
27
- firstGrantedAt: string;
28
- /** ISO-8601 timestamp of the most recent FedCM exchange for this user+RP. */
29
- lastUsedAt: string;
30
- }
31
-
32
- export function OxyServicesAuthorizedAppsMixin<T extends typeof OxyServicesBase>(Base: T) {
33
- return class extends Base {
34
- /**
35
- * List the authenticated user's authorized RP apps — the intersection of the
36
- * user's FedCM grants and the currently-approved RP catalog. Powers the
37
- * "Connected apps" management UI. Requires a real user session.
38
- */
39
- async listAuthorizedApps(): Promise<AuthorizedApp[]> {
40
- try {
41
- const response = await this.makeRequest<{ apps: AuthorizedApp[] }>(
42
- 'GET',
43
- '/fedcm/me/authorized-apps',
44
- undefined,
45
- {
46
- cache: true,
47
- cacheTTL: 30 * 1000, // 30 second cache — short, this drives a manageable UI
48
- },
49
- );
50
- return response?.apps ?? [];
51
- } catch (error) {
52
- throw this.handleError(error);
53
- }
54
- }
55
-
56
- /**
57
- * Revoke the authenticated user's authorization for a specific RP origin.
58
- * The corresponding cache entry is invalidated so a subsequent
59
- * `listAuthorizedApps()` sees fresh data.
60
- */
61
- async revokeAuthorizedApp(origin: string): Promise<void> {
62
- try {
63
- await this.makeRequest(
64
- 'DELETE',
65
- `/fedcm/me/authorized-apps/${encodeURIComponent(origin)}`,
66
- undefined,
67
- { cache: false },
68
- );
69
- this.clearCacheEntry('GET:/fedcm/me/authorized-apps');
70
- } catch (error) {
71
- throw this.handleError(error);
72
- }
73
- }
74
- };
75
- }
@@ -1,63 +0,0 @@
1
- /**
2
- * Authorized-apps mixin tests — the "Connected apps" management pair that
3
- * survived the FedCM removal. Stubs `makeRequest` (no network).
4
- */
5
- import type { AuthorizedApp } from '../OxyServices.authorizedApps';
6
- import { OxyServices } from '../../OxyServices';
7
-
8
- const APP: AuthorizedApp = {
9
- origin: 'https://mention.earth',
10
- name: 'Mention',
11
- firstGrantedAt: '2026-01-01T00:00:00.000Z',
12
- lastUsedAt: '2026-06-01T00:00:00.000Z',
13
- };
14
-
15
- describe('OxyServices.authorizedApps', () => {
16
- let oxy: OxyServices;
17
- let makeRequest: jest.SpyInstance;
18
-
19
- beforeEach(() => {
20
- oxy = new OxyServices({ baseURL: 'http://test.invalid' });
21
- makeRequest = jest.spyOn(oxy, 'makeRequest');
22
- });
23
-
24
- afterEach(() => jest.restoreAllMocks());
25
-
26
- describe('listAuthorizedApps', () => {
27
- it('GETs the authorized-apps and returns the array', async () => {
28
- makeRequest.mockResolvedValueOnce({ apps: [APP] });
29
- expect(await oxy.listAuthorizedApps()).toEqual([APP]);
30
- expect(makeRequest).toHaveBeenCalledWith(
31
- 'GET',
32
- '/fedcm/me/authorized-apps',
33
- undefined,
34
- expect.objectContaining({ cache: true }),
35
- );
36
- });
37
-
38
- it('defaults to [] when the response has no apps', async () => {
39
- makeRequest.mockResolvedValueOnce({});
40
- expect(await oxy.listAuthorizedApps()).toEqual([]);
41
- });
42
-
43
- it('defaults to [] when the response is null (no throw on null access)', async () => {
44
- makeRequest.mockResolvedValueOnce(null);
45
- expect(await oxy.listAuthorizedApps()).toEqual([]);
46
- });
47
- });
48
-
49
- describe('revokeAuthorizedApp', () => {
50
- it('DELETEs the encoded origin and sweeps the list cache', async () => {
51
- const clearCacheEntry = jest.spyOn(oxy, 'clearCacheEntry');
52
- makeRequest.mockResolvedValueOnce(undefined);
53
- await oxy.revokeAuthorizedApp('https://mention.earth');
54
- expect(makeRequest).toHaveBeenCalledWith(
55
- 'DELETE',
56
- '/fedcm/me/authorized-apps/https%3A%2F%2Fmention.earth',
57
- undefined,
58
- { cache: false },
59
- );
60
- expect(clearCacheEntry).toHaveBeenCalledWith('GET:/fedcm/me/authorized-apps');
61
- });
62
- });
63
- });