@oxyhq/core 3.4.15 → 3.4.16

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.
@@ -80,6 +80,8 @@ export interface User {
80
80
  id: string;
81
81
  publicKey: string;
82
82
  username: string;
83
+ /** Canonical display name resolved by the API. */
84
+ displayName: string;
83
85
  email?: string;
84
86
  avatar?: string;
85
87
  color?: string;
@@ -32,13 +32,7 @@ export interface DisplayNameUserShape {
32
32
  full?: string;
33
33
  [key: string]: unknown;
34
34
  };
35
- /**
36
- * Pre-resolved display name as emitted by the server's `displayName` virtual
37
- * (raw `/users/me` responses). NOTE: the server virtual resolves to
38
- * `username || truncatedPublicKey || 'Anonymous'` — it does NOT compose the
39
- * structured `name`. It is therefore preferred only AFTER a real structured
40
- * name, so a first-name-only account never collapses to its username/key.
41
- */
35
+ /** Canonical display name resolved by the API. */
42
36
  displayName?: string;
43
37
  username?: string;
44
38
  publicKey?: string;
@@ -52,13 +46,9 @@ export declare const formatPublicKeyHandle: (publicKey: string) => string;
52
46
  * Resolve a friendly display name for a user.
53
47
  *
54
48
  * Order of preference:
55
- * 1. `name.full`, or composed `name.first name.last` (FIRST-NAME-ONLY SAFE —
56
- * a user with only a first name resolves to that first name, never to the
57
- * lowercase username; this is the exact drift bug the auth app hit).
58
- * 2. `name` (when stored as a plain string)
59
- * 3. `displayName` (server `displayName` virtual — `username || truncatedKey`).
60
- * Placed AFTER the structured name on purpose: the server virtual ignores
61
- * `name`, so preferring it first would re-introduce the first-only bug.
49
+ * 1. `displayName` from the API contract.
50
+ * 2. `name.full`, or composed `name.first name.last` for local unsaved shapes.
51
+ * 3. `name` when stored as a plain string.
62
52
  * 4. `username`
63
53
  * 5. `Account 0x12345678…` (derived from publicKey, when present)
64
54
  * 6. Translated fallback (e.g. "Unnamed")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/core",
3
- "version": "3.4.15",
3
+ "version": "3.4.16",
4
4
  "description": "OxyHQ SDK Foundation — API client, authentication, cryptographic identity, and shared utilities",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -87,6 +87,8 @@ export interface User {
87
87
  id: string;
88
88
  publicKey: string;
89
89
  username: string;
90
+ /** Canonical display name resolved by the API. */
91
+ displayName: string;
90
92
  email?: string;
91
93
  // Avatar file id (asset id)
92
94
  avatar?: string;
@@ -4,16 +4,17 @@ import {
4
4
  formatPublicKeyHandle,
5
5
  } from '../accountUtils';
6
6
 
7
- /**
8
- * Regression coverage for the auth-app display-name drift (Phase 1 of the
9
- * contract-centralisation refactor).
10
- *
11
- * The auth app previously required BOTH `name.first` AND `name.last` to compose
12
- * a display name, falling back to the lowercase `username` for first-name-only
13
- * accounts. The canonical resolver in core MUST be first-name-only safe: a user
14
- * with only a first name resolves to that first name, never to the username.
15
- */
16
7
  describe('getAccountDisplayName', () => {
8
+ it('prefers the API displayName when present', () => {
9
+ expect(
10
+ getAccountDisplayName({
11
+ name: { first: 'Nate' },
12
+ displayName: 'Nate Isern',
13
+ username: 'nateus',
14
+ }),
15
+ ).toBe('Nate Isern');
16
+ });
17
+
17
18
  it('returns first name for first-name-only accounts (NOT the username)', () => {
18
19
  const result = getAccountDisplayName({
19
20
  name: { first: 'Nate' },
@@ -50,19 +51,6 @@ describe('getAccountDisplayName', () => {
50
51
  ).toBe('Nathaniel Isern');
51
52
  });
52
53
 
53
- it('uses the structured name over a server displayName virtual', () => {
54
- // Server `displayName` virtual = `username || truncatedKey`; it ignores
55
- // the structured name. A real name must win so a first-only account is
56
- // never collapsed to its username.
57
- expect(
58
- getAccountDisplayName({
59
- name: { first: 'Nate' },
60
- displayName: 'nateus',
61
- username: 'nateus',
62
- }),
63
- ).toBe('Nate');
64
- });
65
-
66
54
  it('uses displayName when there is no structured name', () => {
67
55
  expect(
68
56
  getAccountDisplayName({
@@ -31,13 +31,7 @@ export interface QuickAccount {
31
31
  /** Minimal user shape accepted by display-name helpers. Avoids importing the full User type. */
32
32
  export interface DisplayNameUserShape {
33
33
  name?: string | { first?: string; last?: string; full?: string; [key: string]: unknown };
34
- /**
35
- * Pre-resolved display name as emitted by the server's `displayName` virtual
36
- * (raw `/users/me` responses). NOTE: the server virtual resolves to
37
- * `username || truncatedPublicKey || 'Anonymous'` — it does NOT compose the
38
- * structured `name`. It is therefore preferred only AFTER a real structured
39
- * name, so a first-name-only account never collapses to its username/key.
40
- */
34
+ /** Canonical display name resolved by the API. */
41
35
  displayName?: string;
42
36
  username?: string;
43
37
  publicKey?: string;
@@ -57,13 +51,9 @@ export const formatPublicKeyHandle = (publicKey: string): string => {
57
51
  * Resolve a friendly display name for a user.
58
52
  *
59
53
  * Order of preference:
60
- * 1. `name.full`, or composed `name.first name.last` (FIRST-NAME-ONLY SAFE —
61
- * a user with only a first name resolves to that first name, never to the
62
- * lowercase username; this is the exact drift bug the auth app hit).
63
- * 2. `name` (when stored as a plain string)
64
- * 3. `displayName` (server `displayName` virtual — `username || truncatedKey`).
65
- * Placed AFTER the structured name on purpose: the server virtual ignores
66
- * `name`, so preferring it first would re-introduce the first-only bug.
54
+ * 1. `displayName` from the API contract.
55
+ * 2. `name.full`, or composed `name.first name.last` for local unsaved shapes.
56
+ * 3. `name` when stored as a plain string.
67
57
  * 4. `username`
68
58
  * 5. `Account 0x12345678…` (derived from publicKey, when present)
69
59
  * 6. Translated fallback (e.g. "Unnamed")
@@ -79,6 +69,8 @@ export const getAccountDisplayName = (
79
69
 
80
70
  const { name, displayName, username, publicKey } = user;
81
71
 
72
+ if (typeof displayName === 'string' && displayName.trim()) return displayName.trim();
73
+
82
74
  if (name && typeof name === 'object') {
83
75
  if (typeof name.full === 'string' && name.full.trim()) return name.full.trim();
84
76
  const first = typeof name.first === 'string' ? name.first.trim() : '';
@@ -89,8 +81,6 @@ export const getAccountDisplayName = (
89
81
  return name.trim();
90
82
  }
91
83
 
92
- if (typeof displayName === 'string' && displayName.trim()) return displayName.trim();
93
-
94
84
  if (typeof username === 'string' && username.trim()) return username.trim();
95
85
 
96
86
  if (typeof publicKey === 'string' && publicKey.length > 0) {
@@ -277,4 +277,4 @@ export async function retryOnError<T>(
277
277
  const errorCode = error?.code || error?.status || error?.message;
278
278
  return retryableErrors.includes(errorCode);
279
279
  });
280
- }
280
+ }