@oxyhq/core 3.17.0 → 3.18.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.auth.js +6 -2
- package/dist/cjs/mixins/OxyServices.links.js +6 -2
- package/dist/cjs/mixins/OxyServices.sso.js +5 -2
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/mixins/OxyServices.auth.js +6 -2
- package/dist/esm/mixins/OxyServices.links.js +6 -2
- package/dist/esm/mixins/OxyServices.sso.js +5 -2
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/mixins/OxyServices.links.d.ts +5 -1
- package/dist/types/models/interfaces.d.ts +4 -2
- package/package.json +1 -1
- package/src/mixins/OxyServices.auth.ts +6 -2
- package/src/mixins/OxyServices.links.ts +8 -4
- package/src/mixins/OxyServices.sso.ts +5 -2
- package/src/mixins/__tests__/OxyServices.links.test.ts +8 -8
- package/src/models/interfaces.ts +4 -2
|
@@ -37,11 +37,15 @@ export declare function OxyServicesLinksMixin<T extends typeof OxyServicesBase>(
|
|
|
37
37
|
* Resolve multiple link previews via `POST /links/previews` (body `{ urls }`).
|
|
38
38
|
*
|
|
39
39
|
* Inputs are de-duplicated and split into chunks of {@link LINK_PREVIEWS_CHUNK_SIZE}
|
|
40
|
-
* (the server-side cap). Chunks run concurrently and their
|
|
40
|
+
* (the server-side cap). Chunks run concurrently and their result maps are
|
|
41
41
|
* merged into a single result keyed by the REQUESTED url (the exact string
|
|
42
42
|
* passed in `urls`) — matching the batch contract — so a caller can always
|
|
43
43
|
* look its own input back up.
|
|
44
44
|
*
|
|
45
|
+
* `makeRequest` unwraps the API's top-level `{ data }` envelope (same as
|
|
46
|
+
* `getUsersByIds`'s `makeServiceRequest<User[]>`), so each chunk response is
|
|
47
|
+
* already the `Record<url, LinkPreview>` map — merge it directly.
|
|
48
|
+
*
|
|
45
49
|
* An empty / whitespace-only input resolves immediately with `{}` and
|
|
46
50
|
* performs no network call. A failure in any chunk surfaces (via
|
|
47
51
|
* `handleError`) rather than being swallowed.
|
|
@@ -98,8 +98,10 @@ export interface User {
|
|
|
98
98
|
privacySettings?: PrivacySettings;
|
|
99
99
|
/**
|
|
100
100
|
* Structured human name. `name.displayName` is the canonical display string
|
|
101
|
-
* resolved by the API; consumers render it directly instead of
|
|
102
|
-
* names from `first` / `last` / `full` / `username`.
|
|
101
|
+
* resolved by the API when present; consumers render it directly instead of
|
|
102
|
+
* recomposing names from `first` / `last` / `full` / `username`. It is now
|
|
103
|
+
* OPTIONAL (see `UserNameResponse`) — when absent, fall back to a handle
|
|
104
|
+
* (e.g. `getNormalizedUserHandle`) rather than recomposing a name locally.
|
|
103
105
|
*/
|
|
104
106
|
name: UserNameResponse;
|
|
105
107
|
bio?: string;
|
package/package.json
CHANGED
|
@@ -1026,7 +1026,9 @@ export function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(Base: T)
|
|
|
1026
1026
|
continue;
|
|
1027
1027
|
}
|
|
1028
1028
|
const userId = e.user.id ?? e.user._id;
|
|
1029
|
-
|
|
1029
|
+
// `name.displayName` is optional on the contract — do NOT drop no-name
|
|
1030
|
+
// accounts. Only an absent userId or username makes the entry unusable.
|
|
1031
|
+
if (!userId || !e.user.username) {
|
|
1030
1032
|
continue;
|
|
1031
1033
|
}
|
|
1032
1034
|
if (typeof e.authuser !== 'number') {
|
|
@@ -1040,7 +1042,9 @@ export function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(Base: T)
|
|
|
1040
1042
|
user: {
|
|
1041
1043
|
id: userId,
|
|
1042
1044
|
username: e.user.username,
|
|
1043
|
-
name
|
|
1045
|
+
// `name.displayName` is optional; carry whatever structured name the
|
|
1046
|
+
// server sent (possibly an empty object) and let consumers fall back.
|
|
1047
|
+
name: e.user.name ?? {},
|
|
1044
1048
|
avatar: e.user.avatar ?? null,
|
|
1045
1049
|
email: e.user.email,
|
|
1046
1050
|
color: e.user.color ?? null,
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* later read, so an SDK GET cache would pin the stale `'pending'` snapshot.
|
|
16
16
|
* App-side caching (React Query / stores) owns this responsibility.
|
|
17
17
|
*/
|
|
18
|
-
import type { LinkPreview
|
|
18
|
+
import type { LinkPreview } from '@oxyhq/contracts';
|
|
19
19
|
import type { OxyServicesBase } from '../OxyServices.base';
|
|
20
20
|
import { buildUrl } from '../utils/apiUtils';
|
|
21
21
|
|
|
@@ -57,11 +57,15 @@ export function OxyServicesLinksMixin<T extends typeof OxyServicesBase>(Base: T)
|
|
|
57
57
|
* Resolve multiple link previews via `POST /links/previews` (body `{ urls }`).
|
|
58
58
|
*
|
|
59
59
|
* Inputs are de-duplicated and split into chunks of {@link LINK_PREVIEWS_CHUNK_SIZE}
|
|
60
|
-
* (the server-side cap). Chunks run concurrently and their
|
|
60
|
+
* (the server-side cap). Chunks run concurrently and their result maps are
|
|
61
61
|
* merged into a single result keyed by the REQUESTED url (the exact string
|
|
62
62
|
* passed in `urls`) — matching the batch contract — so a caller can always
|
|
63
63
|
* look its own input back up.
|
|
64
64
|
*
|
|
65
|
+
* `makeRequest` unwraps the API's top-level `{ data }` envelope (same as
|
|
66
|
+
* `getUsersByIds`'s `makeServiceRequest<User[]>`), so each chunk response is
|
|
67
|
+
* already the `Record<url, LinkPreview>` map — merge it directly.
|
|
68
|
+
*
|
|
65
69
|
* An empty / whitespace-only input resolves immediately with `{}` and
|
|
66
70
|
* performs no network call. A failure in any chunk surfaces (via
|
|
67
71
|
* `handleError`) rather than being swallowed.
|
|
@@ -82,7 +86,7 @@ export function OxyServicesLinksMixin<T extends typeof OxyServicesBase>(Base: T)
|
|
|
82
86
|
try {
|
|
83
87
|
const responses = await Promise.all(
|
|
84
88
|
chunks.map((chunk) =>
|
|
85
|
-
this.makeRequest<
|
|
89
|
+
this.makeRequest<Record<string, LinkPreview>>(
|
|
86
90
|
'POST',
|
|
87
91
|
'/links/previews',
|
|
88
92
|
{ urls: chunk },
|
|
@@ -92,7 +96,7 @@ export function OxyServicesLinksMixin<T extends typeof OxyServicesBase>(Base: T)
|
|
|
92
96
|
);
|
|
93
97
|
|
|
94
98
|
return responses.reduce<Record<string, LinkPreview>>(
|
|
95
|
-
(merged, response) => Object.assign(merged, response
|
|
99
|
+
(merged, response) => Object.assign(merged, response ?? {}),
|
|
96
100
|
{},
|
|
97
101
|
);
|
|
98
102
|
} catch (error) {
|
|
@@ -171,14 +171,17 @@ export function OxyServicesSsoMixin<T extends typeof OxyServicesBase>(Base: T) {
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
const userId = payload.user?.id ?? payload.user?._id;
|
|
174
|
-
if (!userId || typeof payload.user?.username !== 'string'
|
|
174
|
+
if (!userId || typeof payload.user?.username !== 'string') {
|
|
175
175
|
throw this.handleError(new Error('SSO exchange returned an invalid user'));
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
const user: MinimalUserData = {
|
|
179
179
|
id: userId,
|
|
180
180
|
username: payload.user.username,
|
|
181
|
-
name
|
|
181
|
+
// `name.displayName` is optional on the contract. A no-name user is
|
|
182
|
+
// valid; carry whatever structured name the server sent (possibly an
|
|
183
|
+
// empty object) and let consumers fall back to a handle.
|
|
184
|
+
name: payload.user.name ?? {},
|
|
182
185
|
avatar: payload.user.avatar,
|
|
183
186
|
};
|
|
184
187
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* `data` map keyed by the requested url, and surfaces a chunk failure.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import type { LinkPreview
|
|
14
|
+
import type { LinkPreview } from '@oxyhq/contracts';
|
|
15
15
|
import { OxyServices } from '../../OxyServices';
|
|
16
16
|
|
|
17
17
|
const sampleResolved: LinkPreview = {
|
|
@@ -82,10 +82,10 @@ describe('OxyServices.links', () => {
|
|
|
82
82
|
});
|
|
83
83
|
|
|
84
84
|
it('de-duplicates and sends a single chunk for <= 50 unique URLs', async () => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
};
|
|
88
|
-
makeRequestSpy.mockResolvedValueOnce(
|
|
85
|
+
// makeRequest unwraps the API's `{ data }` envelope, so the resolved value
|
|
86
|
+
// is the bare record (NOT `{ data: {...} }`) — mirror that real shape here.
|
|
87
|
+
const record: Record<string, LinkPreview> = { 'https://a.test/': sampleResolved };
|
|
88
|
+
makeRequestSpy.mockResolvedValueOnce(record);
|
|
89
89
|
|
|
90
90
|
const result = await oxy.getLinkPreviews([
|
|
91
91
|
'https://a.test/',
|
|
@@ -93,7 +93,7 @@ describe('OxyServices.links', () => {
|
|
|
93
93
|
' ', // dropped
|
|
94
94
|
]);
|
|
95
95
|
|
|
96
|
-
expect(result).toEqual(
|
|
96
|
+
expect(result).toEqual(record);
|
|
97
97
|
expect(makeRequestSpy).toHaveBeenCalledTimes(1);
|
|
98
98
|
expect(makeRequestSpy).toHaveBeenCalledWith(
|
|
99
99
|
'POST',
|
|
@@ -111,13 +111,13 @@ describe('OxyServices.links', () => {
|
|
|
111
111
|
_method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
|
|
112
112
|
_url: string,
|
|
113
113
|
data?: { urls: string[] },
|
|
114
|
-
): Promise<
|
|
114
|
+
): Promise<Record<string, LinkPreview>> => {
|
|
115
115
|
const chunkUrls = data?.urls ?? [];
|
|
116
116
|
const dataMap: Record<string, LinkPreview> = {};
|
|
117
117
|
for (const u of chunkUrls) {
|
|
118
118
|
dataMap[u] = { url: u, status: 'resolved', title: `t-${u}` };
|
|
119
119
|
}
|
|
120
|
-
return
|
|
120
|
+
return dataMap;
|
|
121
121
|
},
|
|
122
122
|
);
|
|
123
123
|
|
package/src/models/interfaces.ts
CHANGED
|
@@ -108,8 +108,10 @@ export interface User {
|
|
|
108
108
|
privacySettings?: PrivacySettings;
|
|
109
109
|
/**
|
|
110
110
|
* Structured human name. `name.displayName` is the canonical display string
|
|
111
|
-
* resolved by the API; consumers render it directly instead of
|
|
112
|
-
* names from `first` / `last` / `full` / `username`.
|
|
111
|
+
* resolved by the API when present; consumers render it directly instead of
|
|
112
|
+
* recomposing names from `first` / `last` / `full` / `username`. It is now
|
|
113
|
+
* OPTIONAL (see `UserNameResponse`) — when absent, fall back to a handle
|
|
114
|
+
* (e.g. `getNormalizedUserHandle`) rather than recomposing a name locally.
|
|
113
115
|
*/
|
|
114
116
|
name: UserNameResponse;
|
|
115
117
|
bio?: string;
|