@jskit-ai/users-core 0.1.65 → 0.1.66

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.
Files changed (56) hide show
  1. package/package.descriptor.mjs +14 -65
  2. package/package.json +10 -10
  3. package/src/server/UsersCoreServiceProvider.js +18 -2
  4. package/src/server/accountNotifications/accountNotificationsActions.js +3 -5
  5. package/src/server/accountNotifications/accountNotificationsService.js +3 -2
  6. package/src/server/accountNotifications/bootAccountNotificationsRoutes.js +12 -11
  7. package/src/server/accountPreferences/accountPreferencesActions.js +3 -5
  8. package/src/server/accountPreferences/accountPreferencesService.js +3 -2
  9. package/src/server/accountPreferences/bootAccountPreferencesRoutes.js +12 -11
  10. package/src/server/accountProfile/accountProfileActions.js +15 -32
  11. package/src/server/accountProfile/accountProfileService.js +9 -8
  12. package/src/server/accountProfile/bootAccountProfileRoutes.js +25 -19
  13. package/src/server/accountSecurity/accountSecurityActions.js +21 -16
  14. package/src/server/accountSecurity/accountSecurityService.js +16 -6
  15. package/src/server/accountSecurity/bootAccountSecurityRoutes.js +52 -40
  16. package/src/server/common/formatters/accountSettingsResponseFormatter.js +8 -18
  17. package/src/server/common/registerCommonRepositories.js +5 -2
  18. package/src/server/common/repositories/userProfilesRepository.js +227 -88
  19. package/src/server/common/repositories/userSettingsRepository.js +108 -100
  20. package/src/server/common/support/accountSettingsJsonApiTransport.js +10 -0
  21. package/src/server/usersBootstrapContributor.js +13 -32
  22. package/src/shared/resources/accountSettingsSchemas.js +83 -0
  23. package/src/shared/resources/userProfileResource.js +146 -126
  24. package/src/shared/resources/userSettingsResource.js +376 -353
  25. package/templates/packages/users/package.descriptor.mjs +4 -5
  26. package/templates/packages/users/package.json +0 -1
  27. package/templates/packages/users/src/server/UsersProvider.js +23 -24
  28. package/templates/packages/users/src/server/actions.js +26 -28
  29. package/templates/packages/users/src/server/registerRoutes.js +29 -15
  30. package/templates/packages/users/src/server/repository.js +35 -28
  31. package/templates/packages/users/src/server/service.js +20 -15
  32. package/templates/packages/users/src/shared/userResource.js +55 -68
  33. package/templates/packages/users-workspace/package.descriptor.mjs +4 -5
  34. package/templates/packages/users-workspace/src/server/UsersProvider.js +23 -24
  35. package/templates/packages/users-workspace/src/server/actions.js +28 -28
  36. package/templates/packages/users-workspace/src/server/registerRoutes.js +34 -16
  37. package/test/accountSecurityService.test.js +32 -0
  38. package/test/providerLifecycle.test.js +63 -0
  39. package/test/registerCommonRepositories.test.js +28 -8
  40. package/test/repositoryContracts.test.js +177 -28
  41. package/test/resourcesCanonical.test.js +18 -11
  42. package/test/userSettingsInternalResource.test.js +8 -0
  43. package/test/userSettingsResource.test.js +24 -7
  44. package/test/usersBootstrapContributor.test.js +40 -1
  45. package/test/usersPackageScaffoldContract.test.js +70 -3
  46. package/test/usersRouteRequestInputValidator.test.js +92 -23
  47. package/test/usersRouteResources.test.js +28 -18
  48. package/src/server/common/resources/userProfilesResource.js +0 -203
  49. package/src/server/common/validators/authenticatedUserValidator.js +0 -43
  50. package/src/shared/resources/resolveGlobalArrayRegistry.js +0 -6
  51. package/src/shared/resources/userSettingsFields.js +0 -76
  52. package/templates/packages/main/src/shared/resources/userSettingsFields.js +0 -138
  53. package/templates/packages/users/src/server/actionIds.js +0 -6
  54. package/templates/packages/users/src/server/listConfig.js +0 -16
  55. package/test/settingsFieldRegistriesSingleton.test.js +0 -14
  56. package/test-support/registerDefaultSettingsFields.js +0 -2
@@ -1,148 +1,168 @@
1
- import { Type } from "typebox";
2
- import {
3
- createCursorListValidator,
4
- normalizeObjectInput
5
- } from "@jskit-ai/kernel/shared/validators";
6
- import { normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
1
+ import { createSchema } from "json-rest-schema";
2
+ import { normalizeLowerText, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
3
+ import { defineCrudResource } from "@jskit-ai/resource-crud-core/shared/crudResource";
7
4
  import { createOperationMessages } from "../operationMessages.js";
5
+ import { userProfileOutputSchema } from "./accountSettingsSchemas.js";
6
+ import { userSettingsOutputSchema } from "./userSettingsResource.js";
8
7
 
9
- function normalizeProfileInput(payload = {}) {
10
- const source = normalizeObjectInput(payload);
11
- const normalized = {};
8
+ const USERNAME_MAX_LENGTH = 120;
12
9
 
13
- if (Object.hasOwn(source, "displayName")) {
14
- normalized.displayName = normalizeText(source.displayName);
15
- }
10
+ function normalizeUsername(value) {
11
+ const normalized = normalizeLowerText(value)
12
+ .replace(/[^a-z0-9]+/g, "-")
13
+ .replace(/^-+|-+$/g, "")
14
+ .slice(0, USERNAME_MAX_LENGTH);
16
15
 
17
- return normalized;
16
+ return normalized || "";
18
17
  }
19
18
 
20
- const userProfileOutputSchema = Type.Object(
21
- {
22
- displayName: Type.String(),
23
- email: Type.String(),
24
- emailManagedBy: Type.Optional(Type.String()),
25
- emailChangeFlow: Type.Optional(Type.String()),
26
- avatar: Type.Optional(Type.Object({}, { additionalProperties: true }))
27
- },
28
- { additionalProperties: true }
29
- );
30
-
31
- const userProfileOutputValidator = Object.freeze({
32
- schema: userProfileOutputSchema,
33
- normalize: normalizeObjectInput
34
- });
19
+ function normalizeNullableString(value) {
20
+ if (value === null || value === undefined) {
21
+ return null;
22
+ }
35
23
 
36
- const userProfileCreateBodySchema = Type.Object(
37
- {
38
- displayName: Type.String({ minLength: 1, maxLength: 120 })
39
- },
40
- { additionalProperties: false }
41
- );
24
+ return normalizeText(value);
25
+ }
42
26
 
43
- const userProfilePatchBodySchema = Type.Partial(userProfileCreateBodySchema, {
44
- additionalProperties: false,
45
- minProperties: 1
46
- });
27
+ function normalizeNullableVersion(value) {
28
+ if (value === null || value === undefined || value === "") {
29
+ return null;
30
+ }
47
31
 
48
- const avatarUploadBodyValidator = Object.freeze({
49
- schema: Type.Object(
50
- {
51
- mimeType: Type.Optional(
52
- Type.String({
53
- minLength: 1,
54
- messages: {
55
- default: "Avatar mimeType is invalid."
56
- }
57
- })
58
- ),
59
- fileName: Type.Optional(
60
- Type.String({
61
- minLength: 1,
62
- messages: {
63
- default: "Avatar fileName is invalid."
64
- }
65
- })
66
- ),
67
- uploadDimension: Type.Optional(
68
- Type.String({
69
- minLength: 1,
70
- messages: {
71
- default: "Avatar uploadDimension is invalid."
72
- }
73
- })
74
- )
75
- },
76
- { additionalProperties: true }
77
- ),
78
- normalize: normalizeObjectInput
79
- });
32
+ return String(value);
33
+ }
80
34
 
81
- const avatarDeleteBodyValidator = Object.freeze({
82
- schema: Type.Object({}, { additionalProperties: false }),
83
- normalize: normalizeObjectInput
35
+ const userProfileBodySchema = createSchema({
36
+ displayName: {
37
+ type: "string",
38
+ required: true,
39
+ minLength: 1,
40
+ maxLength: 120
41
+ }
84
42
  });
85
43
 
86
- const avatarOperationOutputValidator = Object.freeze({
87
- schema: Type.Object({}, { additionalProperties: true }),
88
- normalize: normalizeObjectInput
44
+ const avatarUploadBodySchema = createSchema({
45
+ mimeType: {
46
+ type: "string",
47
+ required: false,
48
+ minLength: 1,
49
+ messages: {
50
+ default: "Avatar mimeType is invalid."
51
+ }
52
+ },
53
+ fileName: {
54
+ type: "string",
55
+ required: false,
56
+ minLength: 1,
57
+ messages: {
58
+ default: "Avatar fileName is invalid."
59
+ }
60
+ },
61
+ uploadDimension: {
62
+ type: "string",
63
+ required: false,
64
+ minLength: 1,
65
+ messages: {
66
+ default: "Avatar uploadDimension is invalid."
67
+ }
68
+ }
89
69
  });
90
70
 
91
71
  const USER_PROFILE_OPERATION_MESSAGES = createOperationMessages();
92
72
 
93
- const userProfileResource = Object.freeze({
73
+ const userProfileResource = defineCrudResource({
94
74
  namespace: "userProfile",
95
- operations: Object.freeze({
96
- view: Object.freeze({
97
- method: "GET",
98
- messages: USER_PROFILE_OPERATION_MESSAGES,
99
- outputValidator: userProfileOutputValidator
100
- }),
101
- list: Object.freeze({
102
- method: "GET",
103
- messages: USER_PROFILE_OPERATION_MESSAGES,
104
- outputValidator: createCursorListValidator(userProfileOutputValidator)
105
- }),
106
- create: Object.freeze({
107
- method: "POST",
108
- messages: USER_PROFILE_OPERATION_MESSAGES,
109
- bodyValidator: Object.freeze({
110
- schema: userProfileCreateBodySchema,
111
- normalize: normalizeProfileInput
112
- }),
113
- outputValidator: userProfileOutputValidator
114
- }),
115
- replace: Object.freeze({
116
- method: "PUT",
117
- messages: USER_PROFILE_OPERATION_MESSAGES,
118
- bodyValidator: Object.freeze({
119
- schema: userProfileCreateBodySchema,
120
- normalize: normalizeProfileInput
121
- }),
122
- outputValidator: userProfileOutputValidator
123
- }),
124
- patch: Object.freeze({
125
- method: "PATCH",
126
- messages: USER_PROFILE_OPERATION_MESSAGES,
127
- bodyValidator: Object.freeze({
128
- schema: userProfilePatchBodySchema,
129
- normalize: normalizeProfileInput
130
- }),
131
- outputValidator: userProfileOutputValidator
132
- }),
133
- avatarUpload: Object.freeze({
75
+ tableName: "users",
76
+ searchSchema: {
77
+ id: { type: "id", actualField: "id" }
78
+ },
79
+ schema: {
80
+ authProvider: {
81
+ type: "string",
82
+ required: true,
83
+ max: 64,
84
+ search: true,
85
+ storage: { column: "auth_provider" },
86
+ setter: (value) => normalizeLowerText(value)
87
+ },
88
+ authProviderUserSid: {
89
+ type: "string",
90
+ required: true,
91
+ max: 191,
92
+ search: true,
93
+ storage: { column: "auth_provider_user_sid" },
94
+ setter: (value) => normalizeText(value)
95
+ },
96
+ email: {
97
+ type: "string",
98
+ required: true,
99
+ max: 255,
100
+ search: true,
101
+ setter: (value) => normalizeLowerText(value)
102
+ },
103
+ username: {
104
+ type: "string",
105
+ required: true,
106
+ max: USERNAME_MAX_LENGTH,
107
+ search: true,
108
+ setter: (value) => normalizeUsername(value)
109
+ },
110
+ displayName: {
111
+ type: "string",
112
+ required: true,
113
+ max: 160,
114
+ storage: { column: "display_name" },
115
+ setter: (value) => normalizeText(value)
116
+ },
117
+ avatarStorageKey: {
118
+ type: "string",
119
+ nullable: true,
120
+ max: 512,
121
+ storage: { column: "avatar_storage_key" },
122
+ setter: (value) => normalizeNullableString(value)
123
+ },
124
+ avatarVersion: {
125
+ type: "string",
126
+ nullable: true,
127
+ max: 64,
128
+ storage: { column: "avatar_version" },
129
+ setter: (value) => normalizeNullableVersion(value)
130
+ },
131
+ avatarUpdatedAt: {
132
+ type: "dateTime",
133
+ nullable: true,
134
+ storage: {
135
+ column: "avatar_updated_at",
136
+ writeSerializer: "datetime-utc"
137
+ }
138
+ },
139
+ createdAt: {
140
+ type: "dateTime",
141
+ default: "now()",
142
+ storage: {
143
+ column: "created_at",
144
+ writeSerializer: "datetime-utc"
145
+ }
146
+ }
147
+ },
148
+ messages: USER_PROFILE_OPERATION_MESSAGES,
149
+ crudOperations: ["view", "list", "create", "replace", "patch"],
150
+ crud: {
151
+ output: userProfileOutputSchema,
152
+ body: userProfileBodySchema
153
+ },
154
+ operations: {
155
+ avatarUpload: {
134
156
  method: "POST",
135
- messages: USER_PROFILE_OPERATION_MESSAGES,
136
- bodyValidator: avatarUploadBodyValidator,
137
- outputValidator: avatarOperationOutputValidator
138
- }),
139
- avatarDelete: Object.freeze({
157
+ body: avatarUploadBodySchema,
158
+ output: userSettingsOutputSchema
159
+ },
160
+ avatarDelete: {
140
161
  method: "DELETE",
141
- messages: USER_PROFILE_OPERATION_MESSAGES,
142
- bodyValidator: avatarDeleteBodyValidator,
143
- outputValidator: avatarOperationOutputValidator
144
- })
145
- })
162
+ body: createSchema({}),
163
+ output: userSettingsOutputSchema
164
+ }
165
+ }
146
166
  });
147
167
 
148
168
  export { userProfileResource };