@oxyhq/core 1.11.22 → 1.11.24

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 (41) hide show
  1. package/README.md +2 -2
  2. package/dist/cjs/.tsbuildinfo +1 -1
  3. package/dist/cjs/HttpService.js +52 -0
  4. package/dist/cjs/OxyServices.base.js +16 -0
  5. package/dist/cjs/mixins/OxyServices.fedcm.js +169 -73
  6. package/dist/esm/.tsbuildinfo +1 -1
  7. package/dist/esm/HttpService.js +52 -0
  8. package/dist/esm/OxyServices.base.js +16 -0
  9. package/dist/esm/mixins/OxyServices.fedcm.js +169 -73
  10. package/dist/types/.tsbuildinfo +1 -1
  11. package/dist/types/HttpService.d.ts +31 -0
  12. package/dist/types/OxyServices.base.d.ts +14 -0
  13. package/dist/types/OxyServices.d.ts +9 -0
  14. package/dist/types/mixins/OxyServices.analytics.d.ts +1 -0
  15. package/dist/types/mixins/OxyServices.appData.d.ts +1 -0
  16. package/dist/types/mixins/OxyServices.assets.d.ts +1 -0
  17. package/dist/types/mixins/OxyServices.auth.d.ts +1 -0
  18. package/dist/types/mixins/OxyServices.contacts.d.ts +1 -0
  19. package/dist/types/mixins/OxyServices.developer.d.ts +1 -0
  20. package/dist/types/mixins/OxyServices.devices.d.ts +1 -0
  21. package/dist/types/mixins/OxyServices.features.d.ts +6 -1
  22. package/dist/types/mixins/OxyServices.fedcm.d.ts +20 -1
  23. package/dist/types/mixins/OxyServices.karma.d.ts +1 -0
  24. package/dist/types/mixins/OxyServices.language.d.ts +1 -0
  25. package/dist/types/mixins/OxyServices.location.d.ts +1 -0
  26. package/dist/types/mixins/OxyServices.managedAccounts.d.ts +1 -0
  27. package/dist/types/mixins/OxyServices.payment.d.ts +1 -0
  28. package/dist/types/mixins/OxyServices.popup.d.ts +1 -0
  29. package/dist/types/mixins/OxyServices.privacy.d.ts +1 -0
  30. package/dist/types/mixins/OxyServices.redirect.d.ts +1 -0
  31. package/dist/types/mixins/OxyServices.security.d.ts +1 -0
  32. package/dist/types/mixins/OxyServices.topics.d.ts +1 -0
  33. package/dist/types/mixins/OxyServices.user.d.ts +1 -0
  34. package/dist/types/mixins/OxyServices.utility.d.ts +1 -0
  35. package/package.json +1 -1
  36. package/src/HttpService.ts +53 -0
  37. package/src/OxyServices.base.ts +17 -0
  38. package/src/OxyServices.ts +10 -0
  39. package/src/mixins/OxyServices.fedcm.ts +187 -78
  40. package/src/mixins/__tests__/fedcm.test.ts +231 -0
  41. package/src/mixins/__tests__/onTokensChanged.test.ts +130 -0
@@ -0,0 +1,130 @@
1
+ /**
2
+ * `OxyServices.onTokensChanged` token-mirroring subscription tests.
3
+ *
4
+ * `onTokensChanged(listener)` is the single hook @oxyhq/services' OxyProvider
5
+ * uses to keep the shared `oxyClient` singleton's token store in lockstep with
6
+ * whichever OxyServices instance actually owns the session. It must fire on
7
+ * EVERY access-token mutation — explicit `setTokens`, `clearTokens`, and the
8
+ * token-planting auth flows (`verifyChallenge` / `claimSessionByToken`) which
9
+ * route through `setTokens` internally — passing the resulting token or `null`.
10
+ *
11
+ * These tests exercise the listener against a real OxyServices instance. The
12
+ * one network-dependent path (`verifyChallenge`) stubs `makeRequest` so the
13
+ * internal planting is observed end-to-end through the public subscription.
14
+ */
15
+
16
+ import { OxyServices } from '../../OxyServices';
17
+
18
+ function makeOxy(): OxyServices {
19
+ return new OxyServices({ baseURL: 'https://api.oxy.so' });
20
+ }
21
+
22
+ describe('OxyServices.onTokensChanged', () => {
23
+ afterEach(() => {
24
+ jest.restoreAllMocks();
25
+ });
26
+
27
+ it('fires with the access token on setTokens', () => {
28
+ const oxy = makeOxy();
29
+ const listener = jest.fn();
30
+ oxy.onTokensChanged(listener);
31
+
32
+ oxy.setTokens('access_1', 'refresh_1');
33
+
34
+ expect(listener).toHaveBeenCalledTimes(1);
35
+ expect(listener).toHaveBeenCalledWith('access_1');
36
+ });
37
+
38
+ it('fires with null on clearTokens', () => {
39
+ const oxy = makeOxy();
40
+ oxy.setTokens('access_1');
41
+
42
+ const listener = jest.fn();
43
+ oxy.onTokensChanged(listener);
44
+
45
+ oxy.clearTokens();
46
+
47
+ expect(listener).toHaveBeenCalledTimes(1);
48
+ expect(listener).toHaveBeenCalledWith(null);
49
+ });
50
+
51
+ it('reflects the live token on every change (set → set → clear)', () => {
52
+ const oxy = makeOxy();
53
+ const seen: Array<string | null> = [];
54
+ oxy.onTokensChanged((token) => seen.push(token));
55
+
56
+ oxy.setTokens('access_1');
57
+ oxy.setTokens('access_2');
58
+ oxy.clearTokens();
59
+
60
+ expect(seen).toEqual(['access_1', 'access_2', null]);
61
+ });
62
+
63
+ it('stops firing after the returned unsubscribe is called', () => {
64
+ const oxy = makeOxy();
65
+ const listener = jest.fn();
66
+ const unsubscribe = oxy.onTokensChanged(listener);
67
+
68
+ oxy.setTokens('access_1');
69
+ expect(listener).toHaveBeenCalledTimes(1);
70
+
71
+ unsubscribe();
72
+ oxy.setTokens('access_2');
73
+ oxy.clearTokens();
74
+
75
+ // No further notifications after unsubscribe.
76
+ expect(listener).toHaveBeenCalledTimes(1);
77
+ });
78
+
79
+ it('notifies multiple independent listeners without clobbering', () => {
80
+ const oxy = makeOxy();
81
+ const a = jest.fn();
82
+ const b = jest.fn();
83
+ oxy.onTokensChanged(a);
84
+ oxy.onTokensChanged(b);
85
+
86
+ oxy.setTokens('access_1');
87
+
88
+ expect(a).toHaveBeenCalledWith('access_1');
89
+ expect(b).toHaveBeenCalledWith('access_1');
90
+ });
91
+
92
+ it('isolates a throwing listener so others (and the auth flow) still proceed', () => {
93
+ const oxy = makeOxy();
94
+ const bad = jest.fn(() => {
95
+ throw new Error('listener boom');
96
+ });
97
+ const good = jest.fn();
98
+ oxy.onTokensChanged(bad);
99
+ oxy.onTokensChanged(good);
100
+
101
+ // Must not throw out of setTokens.
102
+ expect(() => oxy.setTokens('access_1')).not.toThrow();
103
+ expect(good).toHaveBeenCalledWith('access_1');
104
+ });
105
+
106
+ it('fires when verifyChallenge plants the first token from /auth/verify', async () => {
107
+ const oxy = makeOxy();
108
+ const listener = jest.fn();
109
+ oxy.onTokensChanged(listener);
110
+
111
+ jest.spyOn(oxy, 'makeRequest').mockImplementation(async (_method, url) => {
112
+ if (url === '/auth/verify') {
113
+ return {
114
+ sessionId: 'sess_1',
115
+ deviceId: 'dev_1',
116
+ expiresAt: new Date(Date.now() + 60_000).toISOString(),
117
+ accessToken: 'access_verify',
118
+ refreshToken: 'refresh_verify',
119
+ user: { id: 'user_1', username: 'tester' },
120
+ } as never;
121
+ }
122
+ throw new Error(`unexpected request to ${url}`);
123
+ });
124
+
125
+ await oxy.verifyChallenge('pubkey', 'challenge', 'sig', 123, 'Device', 'fp');
126
+
127
+ // The planted token propagated through the subscription with no extra call.
128
+ expect(listener).toHaveBeenCalledWith('access_verify');
129
+ });
130
+ });