@oxyhq/core 3.4.12 → 3.4.14
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/README.md +1 -1
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/index.js +5 -4
- package/dist/cjs/utils/userIdentity.js +18 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/utils/userIdentity.js +17 -0
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/server/auth.d.ts +1 -1
- package/dist/types/utils/userIdentity.d.ts +11 -0
- package/package.json +1 -1
- package/src/__tests__/userIdentity.test.ts +46 -1
- package/src/index.ts +7 -1
- package/src/server/auth.ts +1 -1
- package/src/utils/userIdentity.ts +31 -0
package/dist/types/index.d.ts
CHANGED
|
@@ -34,7 +34,8 @@ export type { ServiceApp, ServiceActingAsVerification } from './mixins/OxyServic
|
|
|
34
34
|
export type { CreateManagedAccountInput, ManagedAccountManager, ManagedAccount, } from './mixins/OxyServices.managedAccounts';
|
|
35
35
|
export type { ContactDiscoveryMatch, ContactDiscoveryResponse, } from './mixins/OxyServices.contacts';
|
|
36
36
|
export { OxyAppDataIdentifierError } from './mixins/OxyServices.appData';
|
|
37
|
-
export { getNormalizedUserId, normalizeUserIdentity, normalizeUserIdentityOrNull } from './utils/userIdentity';
|
|
37
|
+
export { getCanonicalUserHandle, getNormalizedUserId, normalizeUserIdentity, normalizeUserIdentityOrNull, } from './utils/userIdentity';
|
|
38
|
+
export type { CanonicalUserHandleInput } from './utils/userIdentity';
|
|
38
39
|
export type { Application, PublicApplication, ApplicationMember, ApplicationCredential, ApplicationRole, ApplicationType, ApplicationStatus, ApplicationMemberStatus, ApplicationCredentialType, ApplicationCredentialStatus, ApplicationEnvironment, CreateApplicationInput, UpdateApplicationInput, InviteApplicationMemberInput, UpdateApplicationMemberInput, TransferApplicationOwnershipInput, CreateApplicationCredentialInput, ApplicationCredentialWithSecret, RotateApplicationCredentialResult, ApplicationUsagePeriod, ApplicationUsageSummary, ApplicationUsageByDay, ApplicationUsageByEndpoint, ApplicationUsageStats, ApplicationSuccessResult, } from './mixins/OxyServices.applications';
|
|
39
40
|
export type { Workspace, WorkspaceMember, WorkspaceRole, WorkspaceType, WorkspaceStatus, WorkspaceMemberStatus, CreateWorkspaceInput, UpdateWorkspaceInput, InviteWorkspaceMemberInput, UpdateWorkspaceMemberInput, TransferWorkspaceOwnershipInput, WorkspaceSuccessResult, } from './mixins/OxyServices.workspaces';
|
|
40
41
|
export type { ReputationCategory, TrustTier, ReputationTransactionStatus, ReputationTargetEntityType, ReputationDisputeStatus, ReputationInfluenceContext, ReputationTransaction, ReputationBalanceBreakdown, ReputationInfluence, ReputationReliability, ReputationBalance, ReputationDispute, ReputationRule, ReputationLeaderboardEntry, ReputationInfluenceResult, ReverseReputationTransactionResult, AwardReputationInput, CreateReputationDisputeInput, ResolveReputationDisputeInput, UpsertReputationRuleInput, ReverseReputationTransactionInput, } from './mixins/OxyServices.reputation';
|
|
@@ -27,7 +27,7 @@ export interface OxyAuthRequest extends Request {
|
|
|
27
27
|
user?: OxyRequestUser | null;
|
|
28
28
|
originalUser?: OxyRequestUser | null;
|
|
29
29
|
actingAs?: OxyActingAsContext;
|
|
30
|
-
accessToken?: string
|
|
30
|
+
accessToken?: string;
|
|
31
31
|
sessionId?: string | null;
|
|
32
32
|
serviceApp?: OxyServiceAppContext;
|
|
33
33
|
serviceActingAs?: OxyServiceActingAsContext;
|
|
@@ -2,6 +2,16 @@ interface UserIdentityInput {
|
|
|
2
2
|
id?: unknown;
|
|
3
3
|
_id?: unknown;
|
|
4
4
|
}
|
|
5
|
+
export interface CanonicalUserHandleInput {
|
|
6
|
+
username?: string | null;
|
|
7
|
+
handle?: string | null;
|
|
8
|
+
instance?: string | null;
|
|
9
|
+
isFederated?: boolean | null;
|
|
10
|
+
type?: string | null;
|
|
11
|
+
federation?: {
|
|
12
|
+
domain?: string | null;
|
|
13
|
+
} | null;
|
|
14
|
+
}
|
|
5
15
|
export declare function getNormalizedUserId(user: UserIdentityInput | null | undefined): string | null;
|
|
6
16
|
export declare function normalizeUserIdentity<T extends UserIdentityInput>(user: T): T & {
|
|
7
17
|
id: string;
|
|
@@ -9,4 +19,5 @@ export declare function normalizeUserIdentity<T extends UserIdentityInput>(user:
|
|
|
9
19
|
export declare function normalizeUserIdentityOrNull<T extends UserIdentityInput>(user: T | null | undefined): (T & {
|
|
10
20
|
id: string;
|
|
11
21
|
}) | null;
|
|
22
|
+
export declare function getCanonicalUserHandle(user: CanonicalUserHandleInput | null | undefined): string | null;
|
|
12
23
|
export {};
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { OxyServices } from '../OxyServices';
|
|
2
|
-
import { getNormalizedUserId, normalizeUserIdentity } from '../utils/userIdentity';
|
|
2
|
+
import { getCanonicalUserHandle, getNormalizedUserId, normalizeUserIdentity } from '../utils/userIdentity';
|
|
3
3
|
|
|
4
4
|
interface FetchCall {
|
|
5
5
|
url: string;
|
|
@@ -71,3 +71,48 @@ describe('user identity normalization', () => {
|
|
|
71
71
|
expect(validation.user.username).toBe('nate');
|
|
72
72
|
});
|
|
73
73
|
});
|
|
74
|
+
|
|
75
|
+
describe('getCanonicalUserHandle', () => {
|
|
76
|
+
it('normalizes local usernames without route prefixes', () => {
|
|
77
|
+
expect(getCanonicalUserHandle({ username: ' nate ' })).toBe('nate');
|
|
78
|
+
expect(getCanonicalUserHandle({ username: '@nate' })).toBe('nate');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('builds federated handles from username and instance', () => {
|
|
82
|
+
expect(getCanonicalUserHandle({
|
|
83
|
+
username: 'joannastern',
|
|
84
|
+
isFederated: true,
|
|
85
|
+
instance: 'threads.net',
|
|
86
|
+
})).toBe('joannastern@threads.net');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('does not append an instance twice', () => {
|
|
90
|
+
expect(getCanonicalUserHandle({
|
|
91
|
+
username: '@joannastern@threads.net',
|
|
92
|
+
type: 'federated',
|
|
93
|
+
instance: 'threads.net',
|
|
94
|
+
})).toBe('joannastern@threads.net');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('uses federation domain as the federated instance source', () => {
|
|
98
|
+
expect(getCanonicalUserHandle({
|
|
99
|
+
username: 'alice',
|
|
100
|
+
isFederated: true,
|
|
101
|
+
federation: { domain: '@example.social' },
|
|
102
|
+
})).toBe('alice@example.social');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('does not use instance for local users', () => {
|
|
106
|
+
expect(getCanonicalUserHandle({
|
|
107
|
+
username: 'alice',
|
|
108
|
+
instance: 'example.social',
|
|
109
|
+
})).toBe('alice');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('rejects empty and route-like values', () => {
|
|
113
|
+
expect(getCanonicalUserHandle({ username: '' })).toBeNull();
|
|
114
|
+
expect(getCanonicalUserHandle({ username: 'alice/about' })).toBeNull();
|
|
115
|
+
expect(getCanonicalUserHandle({ username: 'alice?tab=posts' })).toBeNull();
|
|
116
|
+
expect(getCanonicalUserHandle({ username: 'alice#posts' })).toBeNull();
|
|
117
|
+
});
|
|
118
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -62,7 +62,13 @@ export type {
|
|
|
62
62
|
ContactDiscoveryResponse,
|
|
63
63
|
} from './mixins/OxyServices.contacts';
|
|
64
64
|
export { OxyAppDataIdentifierError } from './mixins/OxyServices.appData';
|
|
65
|
-
export {
|
|
65
|
+
export {
|
|
66
|
+
getCanonicalUserHandle,
|
|
67
|
+
getNormalizedUserId,
|
|
68
|
+
normalizeUserIdentity,
|
|
69
|
+
normalizeUserIdentityOrNull,
|
|
70
|
+
} from './utils/userIdentity';
|
|
71
|
+
export type { CanonicalUserHandleInput } from './utils/userIdentity';
|
|
66
72
|
|
|
67
73
|
// ---------------------------------------------------------------------------
|
|
68
74
|
// Applications (multi-user apps: membership, roles, credentials)
|
package/src/server/auth.ts
CHANGED
|
@@ -32,7 +32,7 @@ export interface OxyAuthRequest extends Request {
|
|
|
32
32
|
user?: OxyRequestUser | null;
|
|
33
33
|
originalUser?: OxyRequestUser | null;
|
|
34
34
|
actingAs?: OxyActingAsContext;
|
|
35
|
-
accessToken?: string
|
|
35
|
+
accessToken?: string;
|
|
36
36
|
sessionId?: string | null;
|
|
37
37
|
serviceApp?: OxyServiceAppContext;
|
|
38
38
|
serviceActingAs?: OxyServiceActingAsContext;
|
|
@@ -3,6 +3,17 @@ interface UserIdentityInput {
|
|
|
3
3
|
_id?: unknown;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
export interface CanonicalUserHandleInput {
|
|
7
|
+
username?: string | null;
|
|
8
|
+
handle?: string | null;
|
|
9
|
+
instance?: string | null;
|
|
10
|
+
isFederated?: boolean | null;
|
|
11
|
+
type?: string | null;
|
|
12
|
+
federation?: {
|
|
13
|
+
domain?: string | null;
|
|
14
|
+
} | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
6
17
|
function stringifyIdentity(value: unknown): string | null {
|
|
7
18
|
if (typeof value === 'string') {
|
|
8
19
|
const trimmed = value.trim();
|
|
@@ -27,6 +38,12 @@ function stringifyIdentity(value: unknown): string | null {
|
|
|
27
38
|
return null;
|
|
28
39
|
}
|
|
29
40
|
|
|
41
|
+
function normalizeHandle(value?: string | null): string | null {
|
|
42
|
+
const trimmed = value?.trim().replace(/^@+/, '');
|
|
43
|
+
if (!trimmed || /[/?#]/.test(trimmed)) return null;
|
|
44
|
+
return trimmed;
|
|
45
|
+
}
|
|
46
|
+
|
|
30
47
|
export function getNormalizedUserId(user: UserIdentityInput | null | undefined): string | null {
|
|
31
48
|
if (!user) {
|
|
32
49
|
return null;
|
|
@@ -49,3 +66,17 @@ export function normalizeUserIdentityOrNull<T extends UserIdentityInput>(
|
|
|
49
66
|
): (T & { id: string }) | null {
|
|
50
67
|
return user ? normalizeUserIdentity(user) : null;
|
|
51
68
|
}
|
|
69
|
+
|
|
70
|
+
export function getCanonicalUserHandle(user: CanonicalUserHandleInput | null | undefined): string | null {
|
|
71
|
+
const username = normalizeHandle(user?.username ?? user?.handle);
|
|
72
|
+
if (!username) return null;
|
|
73
|
+
|
|
74
|
+
const isFederated = user?.isFederated === true || user?.type === 'federated';
|
|
75
|
+
const instance = normalizeHandle(user?.instance ?? user?.federation?.domain);
|
|
76
|
+
|
|
77
|
+
if (isFederated && instance && !username.includes('@')) {
|
|
78
|
+
return `${username}@${instance}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return username;
|
|
82
|
+
}
|