@oxyhq/core 21.1.0 → 22.0.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/index.js +3 -5
- package/dist/cjs/utils/validationUtils.js +6 -13
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/utils/validationUtils.js +5 -11
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/mixins/OxyServices.accounts.d.ts +24 -0
- package/dist/types/utils/validationUtils.d.ts +0 -8
- package/package.json +2 -2
- package/src/index.ts +0 -2
- package/src/mixins/OxyServices.accounts.ts +24 -0
- package/src/mixins/__tests__/accounts.test.ts +11 -4
- package/src/utils/__tests__/validationUtils.test.ts +14 -16
- package/src/utils/validationUtils.ts +5 -13
package/dist/types/index.d.ts
CHANGED
|
@@ -95,7 +95,7 @@ export type { PaginationParams, FollowGraphParams, FollowGraphSort, ApiResponse,
|
|
|
95
95
|
export { ErrorCodes, createApiError, handleHttpError, isHttpRequestError, parseHttpErrorBody, validateRequiredFields, } from './utils/errorUtils';
|
|
96
96
|
export type { HttpRequestError, ParsedHttpErrorBody } from './utils/errorUtils';
|
|
97
97
|
export { retryAsync } from './utils/asyncUtils';
|
|
98
|
-
export { EMAIL_REGEX,
|
|
98
|
+
export { EMAIL_REGEX, PASSWORD_REGEX, MAX_DISPLAY_NAME_LENGTH, DISPLAY_NAME_INVALID_MESSAGE, isValidEmail, isValidPassword, isValidDisplayName, DISPLAY_NAME_ALLOWED_SCRIPTS, DISPLAY_NAME_DISALLOWED_SOURCE, DISPLAY_NAME_ORPHANED_MARK_SOURCE, DISPLAY_NAME_UNFLANKED_SEPARATOR_SOURCE, isRequiredString, isRequiredNumber, isRequiredBoolean, isValidArray, isValidObject, isValidUUID, isValidURL, isValidDate, isValidFileSize, isValidFileType, sanitizeString, sanitizeHTML, isValidObjectId, validateAndSanitizeUserInput, } from './utils/validationUtils';
|
|
99
99
|
export { normalizeInlineText, normalizeMultilineText, } from './utils/textNormalization';
|
|
100
100
|
export { logger, createLogger, configureLogger, getLoggerConfig, resetLoggerConfig, consoleSink, isDev, } from './logger';
|
|
101
101
|
export type { Logger, LogLevel, EmittableLogLevel, LogContext, LogEntry, LogSink, LoggerConfig, } from './logger';
|
|
@@ -180,6 +180,21 @@ export interface CreateAccountInput {
|
|
|
180
180
|
};
|
|
181
181
|
bio?: string;
|
|
182
182
|
avatar?: string;
|
|
183
|
+
/**
|
|
184
|
+
* Named color preset KEY — `'blue'`, `'mint'`, … — never a hex value. The
|
|
185
|
+
* account graph's half of `User.color`, which every account DTO already
|
|
186
|
+
* carries; this is how one gets WRITTEN for an account you administer.
|
|
187
|
+
*
|
|
188
|
+
* Set it HERE rather than after the fact. For a managed account the colour is
|
|
189
|
+
* a visual identity, and an account that is discoverable without one and
|
|
190
|
+
* acquires it on a second request is a face that changes by itself.
|
|
191
|
+
*
|
|
192
|
+
* Omitted is not "no colour": the platform assigns a random preset, exactly as
|
|
193
|
+
* it did before this field existed. A reserved preset is refused unless the
|
|
194
|
+
* account has a claim to it — the administrator's own entitlements are not the
|
|
195
|
+
* ones weighed.
|
|
196
|
+
*/
|
|
197
|
+
color?: string;
|
|
183
198
|
/**
|
|
184
199
|
* What the account is about. ORDERED — the FIRST element is the primary
|
|
185
200
|
* category, so a picker must submit them in the order the user arranged them
|
|
@@ -224,6 +239,15 @@ export interface UpdateAccountInput {
|
|
|
224
239
|
};
|
|
225
240
|
bio?: string | null;
|
|
226
241
|
avatar?: string | null;
|
|
242
|
+
/**
|
|
243
|
+
* Named color preset KEY, same vocabulary as `CreateAccountInput['color']`.
|
|
244
|
+
*
|
|
245
|
+
* NOT nullable, unlike `bio` and `avatar`: the column is `NOT NULL` with a
|
|
246
|
+
* default, so an account always HAS a colour and there is no "clear" to
|
|
247
|
+
* express. Sending the value the account already carries is always accepted,
|
|
248
|
+
* so a client may PATCH back the object it was served.
|
|
249
|
+
*/
|
|
250
|
+
color?: string;
|
|
227
251
|
/**
|
|
228
252
|
* Replaces the WHOLE list, in the order given — there is no add/remove verb,
|
|
229
253
|
* because a partial edit cannot express a re-ordering and the order is what
|
|
@@ -12,10 +12,6 @@ export declare const DISPLAY_NAME_INVALID_MESSAGE = "Name may only contain lette
|
|
|
12
12
|
* Email validation regex
|
|
13
13
|
*/
|
|
14
14
|
export declare const EMAIL_REGEX: RegExp;
|
|
15
|
-
/**
|
|
16
|
-
* Username validation regex (alphanumeric, underscores, and hyphens, 3-30 chars)
|
|
17
|
-
*/
|
|
18
|
-
export declare const USERNAME_REGEX: RegExp;
|
|
19
15
|
/**
|
|
20
16
|
* Password validation regex (at least 8 chars, 1 uppercase, 1 lowercase, 1 number)
|
|
21
17
|
*/
|
|
@@ -24,10 +20,6 @@ export declare const PASSWORD_REGEX: RegExp;
|
|
|
24
20
|
* Validate email format
|
|
25
21
|
*/
|
|
26
22
|
export declare function isValidEmail(email: string): boolean;
|
|
27
|
-
/**
|
|
28
|
-
* Validate username format
|
|
29
|
-
*/
|
|
30
|
-
export declare function isValidUsername(username: string): boolean;
|
|
31
23
|
/**
|
|
32
24
|
* Validate password strength
|
|
33
25
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "22.0.0",
|
|
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",
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
"dependencies": {
|
|
117
117
|
"@noble/ciphers": "^1.3.0",
|
|
118
118
|
"@noble/hashes": "^1.8.0",
|
|
119
|
-
"@oxyhq/contracts": "^0.
|
|
119
|
+
"@oxyhq/contracts": "^0.33.0",
|
|
120
120
|
"@oxyhq/protocol": "^0.2.0",
|
|
121
121
|
"@scure/bip39": "^1.6.0",
|
|
122
122
|
"@types/elliptic": "^6.4.18",
|
package/src/index.ts
CHANGED
|
@@ -521,12 +521,10 @@ export { retryAsync } from './utils/asyncUtils';
|
|
|
521
521
|
// ---------------------------------------------------------------------------
|
|
522
522
|
export {
|
|
523
523
|
EMAIL_REGEX,
|
|
524
|
-
USERNAME_REGEX,
|
|
525
524
|
PASSWORD_REGEX,
|
|
526
525
|
MAX_DISPLAY_NAME_LENGTH,
|
|
527
526
|
DISPLAY_NAME_INVALID_MESSAGE,
|
|
528
527
|
isValidEmail,
|
|
529
|
-
isValidUsername,
|
|
530
528
|
isValidPassword,
|
|
531
529
|
isValidDisplayName,
|
|
532
530
|
DISPLAY_NAME_ALLOWED_SCRIPTS,
|
|
@@ -201,6 +201,21 @@ export interface CreateAccountInput {
|
|
|
201
201
|
name?: { first?: string; last?: string; displayName?: string };
|
|
202
202
|
bio?: string;
|
|
203
203
|
avatar?: string;
|
|
204
|
+
/**
|
|
205
|
+
* Named color preset KEY — `'blue'`, `'mint'`, … — never a hex value. The
|
|
206
|
+
* account graph's half of `User.color`, which every account DTO already
|
|
207
|
+
* carries; this is how one gets WRITTEN for an account you administer.
|
|
208
|
+
*
|
|
209
|
+
* Set it HERE rather than after the fact. For a managed account the colour is
|
|
210
|
+
* a visual identity, and an account that is discoverable without one and
|
|
211
|
+
* acquires it on a second request is a face that changes by itself.
|
|
212
|
+
*
|
|
213
|
+
* Omitted is not "no colour": the platform assigns a random preset, exactly as
|
|
214
|
+
* it did before this field existed. A reserved preset is refused unless the
|
|
215
|
+
* account has a claim to it — the administrator's own entitlements are not the
|
|
216
|
+
* ones weighed.
|
|
217
|
+
*/
|
|
218
|
+
color?: string;
|
|
204
219
|
/**
|
|
205
220
|
* What the account is about. ORDERED — the FIRST element is the primary
|
|
206
221
|
* category, so a picker must submit them in the order the user arranged them
|
|
@@ -242,6 +257,15 @@ export interface UpdateAccountInput {
|
|
|
242
257
|
name?: { first?: string; last?: string; displayName?: string };
|
|
243
258
|
bio?: string | null;
|
|
244
259
|
avatar?: string | null;
|
|
260
|
+
/**
|
|
261
|
+
* Named color preset KEY, same vocabulary as `CreateAccountInput['color']`.
|
|
262
|
+
*
|
|
263
|
+
* NOT nullable, unlike `bio` and `avatar`: the column is `NOT NULL` with a
|
|
264
|
+
* default, so an account always HAS a colour and there is no "clear" to
|
|
265
|
+
* express. Sending the value the account already carries is always accepted,
|
|
266
|
+
* so a client may PATCH back the object it was served.
|
|
267
|
+
*/
|
|
268
|
+
color?: string;
|
|
245
269
|
/**
|
|
246
270
|
* Replaces the WHOLE list, in the order given — there is no add/remove verb,
|
|
247
271
|
* because a partial edit cannot express a re-ordering and the order is what
|
|
@@ -255,13 +255,20 @@ describe('OxyServices.accounts', () => {
|
|
|
255
255
|
it('posts the payload, unwraps `account`, and busts every list', async () => {
|
|
256
256
|
makeRequestSpy.mockResolvedValue({ account: accountNodeFixture });
|
|
257
257
|
|
|
258
|
-
const result = await oxy.createAccount({
|
|
258
|
+
const result = await oxy.createAccount({
|
|
259
|
+
kind: 'organization',
|
|
260
|
+
username: 'oxy-org',
|
|
261
|
+
color: 'purple',
|
|
262
|
+
});
|
|
259
263
|
|
|
260
264
|
expect(result).toEqual(accountNodeFixture);
|
|
265
|
+
// `color` rides the CREATE body, not a follow-up patch: an account that is
|
|
266
|
+
// discoverable without its colour and acquires one on a second request is
|
|
267
|
+
// a face that changes by itself.
|
|
261
268
|
expect(makeRequestSpy).toHaveBeenCalledWith(
|
|
262
269
|
'POST',
|
|
263
270
|
'/accounts',
|
|
264
|
-
{ kind: 'organization', username: 'oxy-org' },
|
|
271
|
+
{ kind: 'organization', username: 'oxy-org', color: 'purple' },
|
|
265
272
|
expect.objectContaining({ cache: false }),
|
|
266
273
|
);
|
|
267
274
|
expect(clearEntrySpy).toHaveBeenCalledWith('GET:/accounts');
|
|
@@ -273,13 +280,13 @@ describe('OxyServices.accounts', () => {
|
|
|
273
280
|
it('patches, unwraps `account`, and busts the detail + lists', async () => {
|
|
274
281
|
makeRequestSpy.mockResolvedValue({ account: accountNodeFixture });
|
|
275
282
|
|
|
276
|
-
const result = await oxy.updateAccount('acc1', { bio: 'hello' });
|
|
283
|
+
const result = await oxy.updateAccount('acc1', { bio: 'hello', color: 'mint' });
|
|
277
284
|
|
|
278
285
|
expect(result).toEqual(accountNodeFixture);
|
|
279
286
|
expect(makeRequestSpy).toHaveBeenCalledWith(
|
|
280
287
|
'PATCH',
|
|
281
288
|
'/accounts/acc1',
|
|
282
|
-
{ bio: 'hello' },
|
|
289
|
+
{ bio: 'hello', color: 'mint' },
|
|
283
290
|
expect.objectContaining({ cache: false }),
|
|
284
291
|
);
|
|
285
292
|
expect(clearEntrySpy).toHaveBeenCalledWith('GET:/accounts/acc1');
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
isValidArray,
|
|
8
8
|
isValidObject,
|
|
9
9
|
isValidEmail,
|
|
10
|
-
isValidUsername,
|
|
11
10
|
isValidPassword,
|
|
12
11
|
isValidDisplayName,
|
|
13
12
|
DISPLAY_NAME_ALLOWED_SCRIPTS,
|
|
@@ -126,21 +125,14 @@ describe('Validation Utils', () => {
|
|
|
126
125
|
});
|
|
127
126
|
});
|
|
128
127
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
expect(isValidUsername('')).toBe(false);
|
|
138
|
-
expect(isValidUsername('a')).toBe(false); // too short
|
|
139
|
-
expect(isValidUsername('ab')).toBe(false); // too short
|
|
140
|
-
expect(isValidUsername('user@domain')).toBe(false); // invalid characters
|
|
141
|
-
expect(isValidUsername('user with spaces')).toBe(false); // spaces
|
|
142
|
-
});
|
|
143
|
-
});
|
|
128
|
+
/**
|
|
129
|
+
* `isValidUsername` and `USERNAME_REGEX` were REMOVED from this module: they
|
|
130
|
+
* were a second username policy, looser than the one the server enforced, so
|
|
131
|
+
* the SDK could call a name valid and the API 400 it. The rule now lives once,
|
|
132
|
+
* in `@oxyhq/contracts`, and its own suite covers it. What survives here is the
|
|
133
|
+
* one thing this module still does with a username — sanitise-then-validate,
|
|
134
|
+
* asserted below to answer from that single policy.
|
|
135
|
+
*/
|
|
144
136
|
|
|
145
137
|
describe('isValidPassword', () => {
|
|
146
138
|
it('should return true for valid passwords', () => {
|
|
@@ -676,6 +668,12 @@ describe('Validation Utils', () => {
|
|
|
676
668
|
expect(validateAndSanitizeUserInput(' testuser ', 'username')).toBe('testuser');
|
|
677
669
|
expect(validateAndSanitizeUserInput('ab', 'username')).toBeNull(); // too short
|
|
678
670
|
expect(validateAndSanitizeUserInput(123, 'username')).toBeNull();
|
|
671
|
+
// Answers from the one policy in `@oxyhq/contracts`, not from a rule of its
|
|
672
|
+
// own: a dot and an edge separator are rejected here because they are
|
|
673
|
+
// rejected there.
|
|
674
|
+
expect(validateAndSanitizeUserInput('my-bot', 'username')).toBe('my-bot');
|
|
675
|
+
expect(validateAndSanitizeUserInput('my.bot', 'username')).toBeNull();
|
|
676
|
+
expect(validateAndSanitizeUserInput('-mybot', 'username')).toBeNull();
|
|
679
677
|
});
|
|
680
678
|
|
|
681
679
|
it('should validate and sanitize string input', () => {
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
DISPLAY_NAME_LETTERS_RANGES,
|
|
10
10
|
DISPLAY_NAME_NAME_SEPARATORS_RANGES,
|
|
11
11
|
} from './displayNamePolicyRanges.generated';
|
|
12
|
+
import { usernameSchema } from '@oxyhq/contracts';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Maximum stored length of a display name, in code units after cleaning.
|
|
@@ -25,11 +26,6 @@ export const DISPLAY_NAME_INVALID_MESSAGE =
|
|
|
25
26
|
*/
|
|
26
27
|
export const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
27
28
|
|
|
28
|
-
/**
|
|
29
|
-
* Username validation regex (alphanumeric, underscores, and hyphens, 3-30 chars)
|
|
30
|
-
*/
|
|
31
|
-
export const USERNAME_REGEX = /^[a-zA-Z0-9_-]{3,30}$/;
|
|
32
|
-
|
|
33
29
|
/**
|
|
34
30
|
* Password validation regex (at least 8 chars, 1 uppercase, 1 lowercase, 1 number)
|
|
35
31
|
*/
|
|
@@ -43,13 +39,6 @@ export function isValidEmail(email: string): boolean {
|
|
|
43
39
|
return EMAIL_REGEX.test(email);
|
|
44
40
|
}
|
|
45
41
|
|
|
46
|
-
/**
|
|
47
|
-
* Validate username format
|
|
48
|
-
*/
|
|
49
|
-
export function isValidUsername(username: string): boolean {
|
|
50
|
-
return USERNAME_REGEX.test(username);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
42
|
/**
|
|
54
43
|
* Validate password strength
|
|
55
44
|
*/
|
|
@@ -378,7 +367,10 @@ export function validateAndSanitizeUserInput(input: unknown, type: 'string' | 'e
|
|
|
378
367
|
case 'email':
|
|
379
368
|
return isValidEmail(sanitized) ? sanitized : null;
|
|
380
369
|
case 'username':
|
|
381
|
-
|
|
370
|
+
// The ONE policy, from `@oxyhq/contracts`. This module used to declare a
|
|
371
|
+
// second one (`^[a-zA-Z0-9_-]{3,30}$`) that the server did not enforce, so
|
|
372
|
+
// the SDK could call a name valid and the API 400 it.
|
|
373
|
+
return usernameSchema.safeParse(sanitized).success ? sanitized : null;
|
|
382
374
|
case 'string':
|
|
383
375
|
return isRequiredString(sanitized) ? sanitized : null;
|
|
384
376
|
default:
|