@oxyhq/core 3.15.0 → 3.16.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.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/mixins/OxyServices.assets.js +14 -35
- package/dist/cjs/mixins/OxyServices.auth.js +12 -1
- package/dist/cjs/mixins/OxyServices.links.js +68 -0
- package/dist/cjs/mixins/index.js +4 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/mixins/OxyServices.assets.js +14 -35
- package/dist/esm/mixins/OxyServices.auth.js +12 -1
- package/dist/esm/mixins/OxyServices.links.js +65 -0
- package/dist/esm/mixins/index.js +4 -0
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/mixins/OxyServices.assets.d.ts +8 -29
- package/dist/types/mixins/OxyServices.auth.d.ts +8 -0
- package/dist/types/mixins/OxyServices.links.d.ts +102 -0
- package/dist/types/mixins/index.d.ts +2 -1
- package/package.json +1 -1
- package/src/mixins/OxyServices.assets.ts +14 -44
- package/src/mixins/OxyServices.auth.ts +35 -1
- package/src/mixins/OxyServices.links.ts +103 -0
- package/src/mixins/__tests__/OxyServices.links.test.ts +154 -0
- package/src/mixins/__tests__/commonsSignIn.test.ts +41 -16
- package/src/mixins/__tests__/getFileDownloadUrl.test.ts +10 -24
- package/src/mixins/index.ts +5 -0
|
@@ -102,26 +102,27 @@ describe('OxyServices — "Sign in with Oxy" handoff', () => {
|
|
|
102
102
|
});
|
|
103
103
|
|
|
104
104
|
describe('getCommonsApprovalInfo (approver)', () => {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
isInternal: false,
|
|
113
|
-
scopes: ['profile'],
|
|
114
|
-
},
|
|
105
|
+
const baseInfo = {
|
|
106
|
+
application: {
|
|
107
|
+
id: 'app1',
|
|
108
|
+
name: 'Mention',
|
|
109
|
+
type: 'first_party' as const,
|
|
110
|
+
isOfficial: true,
|
|
111
|
+
isInternal: false,
|
|
115
112
|
scopes: ['profile'],
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
113
|
+
},
|
|
114
|
+
scopes: ['profile'],
|
|
115
|
+
boundOrigin: 'https://mention.earth',
|
|
116
|
+
expiresAt: 1700000300000,
|
|
117
|
+
status: 'pending',
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
it('GETs the server-resolved approval info by authorizeCode', async () => {
|
|
121
|
+
makeRequestSpy.mockResolvedValue({ ...baseInfo, originVerified: true });
|
|
121
122
|
|
|
122
123
|
const result = await oxy.getCommonsApprovalInfo('code-1');
|
|
123
124
|
|
|
124
|
-
expect(result).toEqual(
|
|
125
|
+
expect(result).toEqual({ ...baseInfo, originVerified: true });
|
|
125
126
|
expect(makeRequestSpy).toHaveBeenCalledWith(
|
|
126
127
|
'GET',
|
|
127
128
|
'/auth/session/approve-info/code-1',
|
|
@@ -129,6 +130,30 @@ describe('OxyServices — "Sign in with Oxy" handoff', () => {
|
|
|
129
130
|
expect.objectContaining({ cache: false }),
|
|
130
131
|
);
|
|
131
132
|
});
|
|
133
|
+
|
|
134
|
+
it('coerces a missing originVerified to false (fail-safe to "not verified")', async () => {
|
|
135
|
+
makeRequestSpy.mockResolvedValue(baseInfo);
|
|
136
|
+
|
|
137
|
+
const result = await oxy.getCommonsApprovalInfo('code-1');
|
|
138
|
+
|
|
139
|
+
expect(result.originVerified).toBe(false);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('coerces a non-boolean originVerified to false', async () => {
|
|
143
|
+
makeRequestSpy.mockResolvedValue({ ...baseInfo, originVerified: 'yes' });
|
|
144
|
+
|
|
145
|
+
const result = await oxy.getCommonsApprovalInfo('code-1');
|
|
146
|
+
|
|
147
|
+
expect(result.originVerified).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('passes through a server originVerified:false unchanged', async () => {
|
|
151
|
+
makeRequestSpy.mockResolvedValue({ ...baseInfo, originVerified: false });
|
|
152
|
+
|
|
153
|
+
const result = await oxy.getCommonsApprovalInfo('code-1');
|
|
154
|
+
|
|
155
|
+
expect(result.originVerified).toBe(false);
|
|
156
|
+
});
|
|
132
157
|
});
|
|
133
158
|
|
|
134
159
|
describe('approveCommonsSignIn (approver)', () => {
|
|
@@ -7,10 +7,9 @@
|
|
|
7
7
|
* - PUBLIC (no access token planted, no `expiresIn`) → the clean CDN form
|
|
8
8
|
* `${cloudURL}/<id>[?variant=...]` (default `https://cloud.oxy.so/<id>`),
|
|
9
9
|
* which CloudFront resolves against the public media origin.
|
|
10
|
-
* -
|
|
11
|
-
* the
|
|
12
|
-
*
|
|
13
|
-
* the public CDN.
|
|
10
|
+
* - EXPIRING ORIGIN FALLBACK (`expiresIn` is passed) → the API origin
|
|
11
|
+
* stream form without a bearer token in the query string. Callers that need
|
|
12
|
+
* private access should use `getFileDownloadUrlAsync()` for a scoped URL.
|
|
14
13
|
*/
|
|
15
14
|
|
|
16
15
|
import { OxyServices } from '../../OxyServices';
|
|
@@ -50,38 +49,24 @@ describe('OxyServices.getFileDownloadUrl', () => {
|
|
|
50
49
|
);
|
|
51
50
|
});
|
|
52
51
|
|
|
53
|
-
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('token-safe URL generation', () => {
|
|
55
|
+
it('does not include the in-memory access token in synchronous image URLs', () => {
|
|
54
56
|
const oxy = new OxyServices({ baseURL: 'https://api.oxy.so' });
|
|
55
57
|
oxy.setTokens('access-token-abc');
|
|
56
58
|
|
|
57
|
-
const url = oxy.getFileDownloadUrl('file123', 'thumb'
|
|
58
|
-
omitToken: true,
|
|
59
|
-
});
|
|
59
|
+
const url = oxy.getFileDownloadUrl('file123', 'thumb');
|
|
60
60
|
|
|
61
61
|
expect(url).toBe('https://cloud.oxy.so/file123?variant=thumb');
|
|
62
62
|
expect(url).not.toContain('access-token-abc');
|
|
63
63
|
expect(url).not.toContain('token=');
|
|
64
64
|
});
|
|
65
|
-
});
|
|
66
65
|
|
|
67
|
-
|
|
68
|
-
it('returns the stream endpoint with the token when an access token is present', () => {
|
|
66
|
+
it('routes through the stream endpoint when expiresIn is requested without embedding a token', () => {
|
|
69
67
|
const oxy = new OxyServices({ baseURL: 'https://api.oxy.so' });
|
|
70
68
|
oxy.setTokens('access-token-abc');
|
|
71
69
|
|
|
72
|
-
const url = oxy.getFileDownloadUrl('file123', 'thumb');
|
|
73
|
-
|
|
74
|
-
expect(url.startsWith('https://api.oxy.so/assets/file123/stream?')).toBe(true);
|
|
75
|
-
const params = new URLSearchParams(url.split('?')[1]);
|
|
76
|
-
expect(params.get('variant')).toBe('thumb');
|
|
77
|
-
expect(params.get('token')).toBe('access-token-abc');
|
|
78
|
-
expect(params.get('fallback')).toBe('placeholderVisible');
|
|
79
|
-
expect(url).not.toContain('cloud.oxy.so');
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('routes through the stream endpoint when expiresIn is requested even without a token', () => {
|
|
83
|
-
const oxy = new OxyServices({ baseURL: 'https://api.oxy.so' });
|
|
84
|
-
|
|
85
70
|
const url = oxy.getFileDownloadUrl('file123', 'thumb', 3600);
|
|
86
71
|
|
|
87
72
|
expect(url.startsWith('https://api.oxy.so/assets/file123/stream?')).toBe(true);
|
|
@@ -90,6 +75,7 @@ describe('OxyServices.getFileDownloadUrl', () => {
|
|
|
90
75
|
expect(params.get('variant')).toBe('thumb');
|
|
91
76
|
expect(params.get('fallback')).toBe('placeholderVisible');
|
|
92
77
|
expect(params.get('token')).toBeNull();
|
|
78
|
+
expect(url).not.toContain('access-token-abc');
|
|
93
79
|
expect(url).not.toContain('cloud.oxy.so');
|
|
94
80
|
});
|
|
95
81
|
});
|
package/src/mixins/index.ts
CHANGED
|
@@ -32,6 +32,7 @@ import { OxyServicesContactsMixin } from './OxyServices.contacts';
|
|
|
32
32
|
import { OxyServicesAppDataMixin } from './OxyServices.appData';
|
|
33
33
|
import { OxyServicesCivicMixin } from './OxyServices.civic';
|
|
34
34
|
import { OxyServicesNodesMixin } from './OxyServices.nodes';
|
|
35
|
+
import { OxyServicesLinksMixin } from './OxyServices.links';
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* Instance shape of every mixin in the pipeline, intersected. The runtime
|
|
@@ -68,6 +69,7 @@ type AllMixinInstances =
|
|
|
68
69
|
& InstanceType<ReturnType<typeof OxyServicesAppDataMixin<typeof OxyServicesBase>>>
|
|
69
70
|
& InstanceType<ReturnType<typeof OxyServicesCivicMixin<typeof OxyServicesBase>>>
|
|
70
71
|
& InstanceType<ReturnType<typeof OxyServicesNodesMixin<typeof OxyServicesBase>>>
|
|
72
|
+
& InstanceType<ReturnType<typeof OxyServicesLinksMixin<typeof OxyServicesBase>>>
|
|
71
73
|
& InstanceType<ReturnType<typeof OxyServicesUtilityMixin<typeof OxyServicesBase>>>;
|
|
72
74
|
|
|
73
75
|
/**
|
|
@@ -139,6 +141,9 @@ const MIXIN_PIPELINE: MixinFunction[] = [
|
|
|
139
141
|
// User nodes / decentralization (Fase 5): register/read/revoke/manage the
|
|
140
142
|
// caller's personal data node + ingest hint.
|
|
141
143
|
OxyServicesNodesMixin,
|
|
144
|
+
// Link previews / unfurls: SDK-owned link-metadata resolution via oxy-api,
|
|
145
|
+
// so apps stop scraping link metadata locally.
|
|
146
|
+
OxyServicesLinksMixin,
|
|
142
147
|
|
|
143
148
|
// Utility (last, can use all above)
|
|
144
149
|
OxyServicesUtilityMixin,
|