@jskit-ai/users-core 0.1.4

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 (148) hide show
  1. package/package.descriptor.mjs +464 -0
  2. package/package.json +35 -0
  3. package/src/server/UsersCoreServiceProvider.js +74 -0
  4. package/src/server/accountNotifications/accountNotificationsActions.js +39 -0
  5. package/src/server/accountNotifications/accountNotificationsService.js +41 -0
  6. package/src/server/accountNotifications/bootAccountNotificationsRoutes.js +41 -0
  7. package/src/server/accountNotifications/registerAccountNotifications.js +39 -0
  8. package/src/server/accountPreferences/accountPreferencesActions.js +39 -0
  9. package/src/server/accountPreferences/accountPreferencesService.js +41 -0
  10. package/src/server/accountPreferences/bootAccountPreferencesRoutes.js +41 -0
  11. package/src/server/accountPreferences/registerAccountPreferences.js +39 -0
  12. package/src/server/accountProfile/accountProfileActions.js +137 -0
  13. package/src/server/accountProfile/accountProfileService.js +124 -0
  14. package/src/server/accountProfile/avatarService.js +141 -0
  15. package/src/server/accountProfile/avatarStorageService.js +132 -0
  16. package/src/server/accountProfile/bootAccountProfileRoutes.js +166 -0
  17. package/src/server/accountProfile/registerAccountProfile.js +62 -0
  18. package/src/server/accountProfile/registerAvatarMultipartSupport.js +43 -0
  19. package/src/server/accountSecurity/accountSecurityActions.js +144 -0
  20. package/src/server/accountSecurity/accountSecurityService.js +103 -0
  21. package/src/server/accountSecurity/bootAccountSecurityRoutes.js +183 -0
  22. package/src/server/accountSecurity/registerAccountSecurity.js +31 -0
  23. package/src/server/common/README.md +21 -0
  24. package/src/server/common/contributors/README.md +11 -0
  25. package/src/server/common/contributors/workspaceActionContextContributor.js +79 -0
  26. package/src/server/common/contributors/workspaceAuthPolicyContextResolver.js +34 -0
  27. package/src/server/common/contributors/workspaceRouteVisibilityResolver.js +79 -0
  28. package/src/server/common/diTokens.js +21 -0
  29. package/src/server/common/formatters/README.md +11 -0
  30. package/src/server/common/formatters/accountAvatarFormatter.js +42 -0
  31. package/src/server/common/formatters/accountSecurityStatusFormatter.js +71 -0
  32. package/src/server/common/formatters/accountSettingsResponseFormatter.js +62 -0
  33. package/src/server/common/formatters/workspaceFormatter.js +46 -0
  34. package/src/server/common/registerCommonRepositories.js +45 -0
  35. package/src/server/common/registerSharedApi.js +9 -0
  36. package/src/server/common/repositories/README.md +24 -0
  37. package/src/server/common/repositories/repositoryUtils.js +50 -0
  38. package/src/server/common/repositories/userProfilesRepository.js +251 -0
  39. package/src/server/common/repositories/userSettingsRepository.js +179 -0
  40. package/src/server/common/repositories/workspaceInvitesRepository.js +172 -0
  41. package/src/server/common/repositories/workspaceMembershipsRepository.js +157 -0
  42. package/src/server/common/repositories/workspacesRepository.js +183 -0
  43. package/src/server/common/routes/README.md +11 -0
  44. package/src/server/common/services/README.md +12 -0
  45. package/src/server/common/services/accountContextService.js +31 -0
  46. package/src/server/common/services/authProfileSyncService.js +128 -0
  47. package/src/server/common/services/workspaceContextService.js +270 -0
  48. package/src/server/common/support/deepFreeze.js +17 -0
  49. package/src/server/common/support/realtimeServiceEvents.js +94 -0
  50. package/src/server/common/support/resolveActionUser.js +11 -0
  51. package/src/server/common/support/workspaceRoutePaths.js +17 -0
  52. package/src/server/common/validators/README.md +11 -0
  53. package/src/server/common/validators/authenticatedUserValidator.js +42 -0
  54. package/src/server/common/validators/routeParamsValidator.js +62 -0
  55. package/src/server/consoleSettings/bootConsoleSettingsRoutes.js +64 -0
  56. package/src/server/consoleSettings/consoleService.js +36 -0
  57. package/src/server/consoleSettings/consoleSettingsActions.js +55 -0
  58. package/src/server/consoleSettings/consoleSettingsRepository.js +111 -0
  59. package/src/server/consoleSettings/consoleSettingsService.js +40 -0
  60. package/src/server/consoleSettings/registerConsoleSettings.js +57 -0
  61. package/src/server/registerWorkspaceBootstrap.js +36 -0
  62. package/src/server/registerWorkspaceCore.js +95 -0
  63. package/src/server/support/resolveWorkspace.js +16 -0
  64. package/src/server/support/workspaceActionSurfaces.js +135 -0
  65. package/src/server/support/workspaceInvitationsPolicy.js +45 -0
  66. package/src/server/support/workspaceRouteInput.js +22 -0
  67. package/src/server/workspaceBootstrapContributor.js +401 -0
  68. package/src/server/workspaceDirectory/bootWorkspaceDirectoryRoutes.js +73 -0
  69. package/src/server/workspaceDirectory/registerWorkspaceDirectory.js +19 -0
  70. package/src/server/workspaceDirectory/workspaceDirectoryActions.js +65 -0
  71. package/src/server/workspaceMembers/bootWorkspaceMembers.js +238 -0
  72. package/src/server/workspaceMembers/registerWorkspaceMembers.js +112 -0
  73. package/src/server/workspaceMembers/workspaceMembersActions.js +186 -0
  74. package/src/server/workspaceMembers/workspaceMembersService.js +210 -0
  75. package/src/server/workspacePendingInvitations/bootWorkspacePendingInvitations.js +63 -0
  76. package/src/server/workspacePendingInvitations/registerWorkspacePendingInvitations.js +128 -0
  77. package/src/server/workspacePendingInvitations/workspacePendingInvitationsActions.js +74 -0
  78. package/src/server/workspacePendingInvitations/workspacePendingInvitationsService.js +137 -0
  79. package/src/server/workspaceSettings/bootWorkspaceSettings.js +77 -0
  80. package/src/server/workspaceSettings/registerWorkspaceSettings.js +67 -0
  81. package/src/server/workspaceSettings/workspaceSettingsActions.js +72 -0
  82. package/src/server/workspaceSettings/workspaceSettingsRepository.js +135 -0
  83. package/src/server/workspaceSettings/workspaceSettingsService.js +65 -0
  84. package/src/shared/events/usersEvents.js +19 -0
  85. package/src/shared/index.js +91 -0
  86. package/src/shared/operationMessages.js +16 -0
  87. package/src/shared/resources/consoleSettingsFields.js +55 -0
  88. package/src/shared/resources/consoleSettingsResource.js +139 -0
  89. package/src/shared/resources/resolveGlobalArrayRegistry.js +6 -0
  90. package/src/shared/resources/userProfileResource.js +148 -0
  91. package/src/shared/resources/userSettingsFields.js +71 -0
  92. package/src/shared/resources/userSettingsResource.js +416 -0
  93. package/src/shared/resources/workspaceMembersResource.js +352 -0
  94. package/src/shared/resources/workspacePendingInvitationsResource.js +87 -0
  95. package/src/shared/resources/workspaceResource.js +149 -0
  96. package/src/shared/resources/workspaceSettingsFields.js +60 -0
  97. package/src/shared/resources/workspaceSettingsResource.js +178 -0
  98. package/src/shared/roles.js +136 -0
  99. package/src/shared/settings.js +31 -0
  100. package/src/shared/support/usersApiPaths.js +34 -0
  101. package/src/shared/support/usersVisibility.js +45 -0
  102. package/src/shared/support/workspacePathModel.js +145 -0
  103. package/src/shared/tenancyMode.js +35 -0
  104. package/src/shared/tenancyProfile.js +73 -0
  105. package/templates/config/workspaceRoles.js +30 -0
  106. package/templates/migrations/users_core_console_owner.cjs +39 -0
  107. package/templates/migrations/users_core_initial.cjs +118 -0
  108. package/templates/migrations/users_core_profile_username.cjs +98 -0
  109. package/templates/packages/main/src/shared/resources/consoleSettingsFields.js +11 -0
  110. package/templates/packages/main/src/shared/resources/userSettingsFields.js +138 -0
  111. package/templates/packages/main/src/shared/resources/workspaceSettingsFields.js +105 -0
  112. package/test/authProfileSyncService.test.js +119 -0
  113. package/test/avatarService.test.js +114 -0
  114. package/test/avatarStorageService.test.js +61 -0
  115. package/test/consoleService.test.js +57 -0
  116. package/test/consoleSettingsService.test.js +86 -0
  117. package/test/exportsContract.test.js +38 -0
  118. package/test/registerAvatarMultipartSupport.test.js +64 -0
  119. package/test/registerServiceRealtimeEvents.test.js +160 -0
  120. package/test/registerWorkspaceDirectory.test.js +26 -0
  121. package/test/registerWorkspaceSettings.test.js +44 -0
  122. package/test/resourcesCanonical.test.js +90 -0
  123. package/test/roles.test.js +74 -0
  124. package/test/settingsFieldRegistriesSingleton.test.js +24 -0
  125. package/test/tenancyProfile.test.js +67 -0
  126. package/test/userSettingsResource.test.js +31 -0
  127. package/test/usersApiPaths.test.js +31 -0
  128. package/test/usersRouteRequestInputValidator.test.js +556 -0
  129. package/test/usersRouteResources.test.js +113 -0
  130. package/test/usersRouteValidators.test.js +49 -0
  131. package/test/usersVisibility.test.js +22 -0
  132. package/test/workspaceActionContextContributor.test.js +251 -0
  133. package/test/workspaceActionSurfaces.test.js +105 -0
  134. package/test/workspaceAuthPolicyContextResolver.test.js +119 -0
  135. package/test/workspaceBootstrapContributor.test.js +466 -0
  136. package/test/workspaceInvitationsPolicy.test.js +71 -0
  137. package/test/workspaceInvitesRepository.test.js +111 -0
  138. package/test/workspaceMembersService.test.js +400 -0
  139. package/test/workspacePathModel.test.js +93 -0
  140. package/test/workspacePendingInvitationsResource.test.js +38 -0
  141. package/test/workspacePendingInvitationsService.test.js +151 -0
  142. package/test/workspaceRouteVisibilityResolver.test.js +83 -0
  143. package/test/workspaceService.test.js +480 -0
  144. package/test/workspaceSettingsActions.test.js +42 -0
  145. package/test/workspaceSettingsRepository.test.js +156 -0
  146. package/test/workspaceSettingsResource.test.js +156 -0
  147. package/test/workspaceSettingsService.test.js +120 -0
  148. package/test-support/registerDefaultSettingsFields.js +3 -0
@@ -0,0 +1,156 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import "../test-support/registerDefaultSettingsFields.js";
4
+ import { createRepository } from "../src/server/workspaceSettings/workspaceSettingsRepository.js";
5
+
6
+ function createDefaultWorkspaceSettings() {
7
+ return true;
8
+ }
9
+
10
+ function createKnexStub(rowOverrides = {}) {
11
+ const state = {
12
+ insertedRow: null,
13
+ updatePayload: null,
14
+ row: {
15
+ workspace_id: 1,
16
+ name: "Workspace",
17
+ avatar_url: "",
18
+ color: "#0F6B54",
19
+ invites_enabled: 1,
20
+ created_at: "2026-03-09 00:26:35.710",
21
+ updated_at: "2026-03-09 00:26:35.710",
22
+ ...rowOverrides
23
+ }
24
+ };
25
+
26
+ function tableBuilder(tableName) {
27
+ assert.equal(tableName, "workspace_settings");
28
+
29
+ return {
30
+ insert(payload) {
31
+ state.insertedRow = { ...payload };
32
+ state.row = {
33
+ workspace_id: payload.workspace_id,
34
+ name: payload.name,
35
+ avatar_url: payload.avatar_url,
36
+ color: payload.color,
37
+ invites_enabled: payload.invites_enabled,
38
+ created_at: "2026-03-10 00:00:00.000",
39
+ updated_at: "2026-03-10 00:00:00.000"
40
+ };
41
+ return Promise.resolve([1]);
42
+ },
43
+ where(criteria) {
44
+ assert.equal(typeof criteria, "object");
45
+
46
+ return {
47
+ first() {
48
+ return Promise.resolve(state.row ? { ...state.row } : null);
49
+ },
50
+ update(payload) {
51
+ state.updatePayload = payload;
52
+ if (Object.hasOwn(payload, "invites_enabled")) {
53
+ state.row.invites_enabled = payload.invites_enabled;
54
+ }
55
+ if (Object.hasOwn(payload, "name")) {
56
+ state.row.name = payload.name;
57
+ }
58
+ if (Object.hasOwn(payload, "avatar_url")) {
59
+ state.row.avatar_url = payload.avatar_url;
60
+ }
61
+ if (Object.hasOwn(payload, "color")) {
62
+ state.row.color = payload.color;
63
+ }
64
+ if (Object.hasOwn(payload, "updated_at")) {
65
+ state.row.updated_at = payload.updated_at;
66
+ }
67
+ return Promise.resolve(1);
68
+ }
69
+ };
70
+ }
71
+ };
72
+ }
73
+
74
+ return { knexStub: tableBuilder, state };
75
+ }
76
+
77
+ test("workspaceSettingsRepository.findByWorkspaceId maps the stored row", async () => {
78
+ const { knexStub } = createKnexStub();
79
+ const repository = createRepository(knexStub, {
80
+ defaultInvitesEnabled: createDefaultWorkspaceSettings()
81
+ });
82
+
83
+ const record = await repository.findByWorkspaceId(1);
84
+
85
+ assert.deepEqual(record, {
86
+ workspaceId: 1,
87
+ name: "Workspace",
88
+ avatarUrl: "",
89
+ color: "#0F6B54",
90
+ invitesEnabled: true,
91
+ createdAt: "2026-03-08T16:26:35.710Z",
92
+ updatedAt: "2026-03-08T16:26:35.710Z"
93
+ });
94
+ });
95
+
96
+ test("workspaceSettingsRepository.updateSettingsByWorkspaceId updates invitesEnabled only", async () => {
97
+ const { knexStub, state } = createKnexStub();
98
+ const repository = createRepository(knexStub, {
99
+ defaultInvitesEnabled: createDefaultWorkspaceSettings()
100
+ });
101
+
102
+ const updated = await repository.updateSettingsByWorkspaceId(1, {
103
+ invitesEnabled: false
104
+ });
105
+
106
+ assert.equal(state.updatePayload.invites_enabled, false);
107
+ assert.equal(updated.invitesEnabled, false);
108
+ });
109
+
110
+ test("workspaceSettingsRepository.ensureForWorkspaceId inserts the injected defaults exactly", async () => {
111
+ const { knexStub, state } = createKnexStub();
112
+ state.row = null;
113
+ const repository = createRepository(knexStub, {
114
+ defaultInvitesEnabled: false
115
+ });
116
+
117
+ const record = await repository.ensureForWorkspaceId(5);
118
+
119
+ assert.equal(state.insertedRow.workspace_id, 5);
120
+ assert.equal(state.insertedRow.name, "Workspace");
121
+ assert.equal(state.insertedRow.avatar_url, "");
122
+ assert.equal(state.insertedRow.color, "#0F6B54");
123
+ assert.equal(state.insertedRow.invites_enabled, false);
124
+ assert.equal(record.name, "Workspace");
125
+ assert.equal(record.avatarUrl, "");
126
+ assert.equal(record.color, "#0F6B54");
127
+ assert.equal(record.invitesEnabled, false);
128
+ });
129
+
130
+ test("workspaceSettingsRepository.updateSettingsByWorkspaceId updates name/avatar/color columns", async () => {
131
+ const { knexStub, state } = createKnexStub();
132
+ const repository = createRepository(knexStub, {
133
+ defaultInvitesEnabled: true
134
+ });
135
+
136
+ const updated = await repository.updateSettingsByWorkspaceId(1, {
137
+ name: "New name",
138
+ avatarUrl: "https://example.com/avatar.png",
139
+ color: "#123abc"
140
+ });
141
+
142
+ assert.equal(state.updatePayload.name, "New name");
143
+ assert.equal(state.updatePayload.avatar_url, "https://example.com/avatar.png");
144
+ assert.equal(state.updatePayload.color, "#123ABC");
145
+ assert.equal(updated.name, "New name");
146
+ assert.equal(updated.avatarUrl, "https://example.com/avatar.png");
147
+ assert.equal(updated.color, "#123ABC");
148
+ });
149
+
150
+ test("workspaceSettingsRepository can be constructed without validating app config shape", () => {
151
+ const { knexStub } = createKnexStub();
152
+
153
+ const repository = createRepository(knexStub);
154
+
155
+ assert.ok(repository);
156
+ });
@@ -0,0 +1,156 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { validateOperationSection } from "@jskit-ai/http-runtime/shared/validators/operationValidation";
4
+ import "../test-support/registerDefaultSettingsFields.js";
5
+ import { workspaceSettingsResource } from "../src/shared/resources/workspaceSettingsResource.js";
6
+ import { createWorkspaceRoleCatalog } from "../src/shared/roles.js";
7
+
8
+ function createRoleCatalog() {
9
+ return createWorkspaceRoleCatalog({
10
+ workspaceRoles: {
11
+ defaultInviteRole: "member",
12
+ roles: {
13
+ owner: {
14
+ assignable: false,
15
+ permissions: ["*"]
16
+ },
17
+ admin: {
18
+ assignable: true,
19
+ permissions: [
20
+ "workspace.roles.view",
21
+ "workspace.settings.view",
22
+ "workspace.settings.update",
23
+ "workspace.members.view",
24
+ "workspace.members.invite",
25
+ "workspace.members.manage",
26
+ "workspace.invites.revoke"
27
+ ]
28
+ },
29
+ member: {
30
+ assignable: true,
31
+ permissions: ["workspace.settings.view"]
32
+ }
33
+ }
34
+ }
35
+ });
36
+ }
37
+
38
+ function parseBody(operation, payload = {}) {
39
+ return validateOperationSection({
40
+ operation,
41
+ section: "bodyValidator",
42
+ value: payload
43
+ });
44
+ }
45
+
46
+ test("workspace settings patch body normalizes valid payload before validation", () => {
47
+ const parsed = parseBody(workspaceSettingsResource.operations.patch, {
48
+ name: " Team Mercury ",
49
+ avatarUrl: "https://example.com/avatar.png",
50
+ color: "#0f6b54",
51
+ invitesEnabled: false
52
+ });
53
+
54
+ assert.equal(parsed.ok, true);
55
+ assert.deepEqual(parsed.fieldErrors, {});
56
+ assert.deepEqual(parsed.value, {
57
+ name: "Team Mercury",
58
+ avatarUrl: "https://example.com/avatar.png",
59
+ color: "#0F6B54",
60
+ invitesEnabled: false
61
+ });
62
+ });
63
+
64
+ test("workspace settings patch body validates avatar URL protocol", () => {
65
+ const parsed = parseBody(workspaceSettingsResource.operations.patch, {
66
+ avatarUrl: "ftp://example.com/avatar.png"
67
+ });
68
+
69
+ assert.equal(parsed.ok, false);
70
+ assert.equal(
71
+ parsed.fieldErrors.avatarUrl,
72
+ "Workspace avatar URL must be a valid absolute URL (http:// or https://)."
73
+ );
74
+ });
75
+
76
+ test("workspace settings patch body keeps max-length name rule", () => {
77
+ const parsed = parseBody(workspaceSettingsResource.operations.patch, {
78
+ name: "x".repeat(161)
79
+ });
80
+
81
+ assert.equal(parsed.ok, false);
82
+ assert.equal(parsed.fieldErrors.name, "Workspace name must be at most 160 characters.");
83
+ });
84
+
85
+ test("workspace settings create body requires full-write fields", () => {
86
+ const parsed = parseBody(workspaceSettingsResource.operations.create, {
87
+ name: "Mercury Workspace"
88
+ });
89
+
90
+ assert.equal(parsed.ok, false);
91
+ assert.equal(parsed.fieldErrors.color, "Workspace color is required.");
92
+ assert.equal(parsed.fieldErrors.invitesEnabled, "invitesEnabled is required.");
93
+ });
94
+
95
+ test("workspace settings output normalizes raw service payloads", () => {
96
+ const normalized = workspaceSettingsResource.operations.view.outputValidator.normalize({
97
+ workspace: {
98
+ id: "7",
99
+ slug: " mercury ",
100
+ ownerUserId: "9"
101
+ },
102
+ settings: {
103
+ name: " Mercury Workspace ",
104
+ avatarUrl: " https://example.com/avatar.png ",
105
+ color: "#0f6b54",
106
+ invitesEnabled: false
107
+ },
108
+ roleCatalog: createRoleCatalog()
109
+ });
110
+
111
+ assert.deepEqual(normalized, {
112
+ workspace: {
113
+ id: 7,
114
+ slug: "mercury",
115
+ ownerUserId: 9
116
+ },
117
+ settings: {
118
+ name: "Mercury Workspace",
119
+ avatarUrl: "https://example.com/avatar.png",
120
+ color: "#0F6B54",
121
+ invitesEnabled: false,
122
+ invitesAvailable: true,
123
+ invitesEffective: false
124
+ },
125
+ roleCatalog: {
126
+ collaborationEnabled: true,
127
+ defaultInviteRole: "member",
128
+ roles: [
129
+ {
130
+ id: "owner",
131
+ assignable: false,
132
+ permissions: ["*"]
133
+ },
134
+ {
135
+ id: "admin",
136
+ assignable: true,
137
+ permissions: [
138
+ "workspace.roles.view",
139
+ "workspace.settings.view",
140
+ "workspace.settings.update",
141
+ "workspace.members.view",
142
+ "workspace.members.invite",
143
+ "workspace.members.manage",
144
+ "workspace.invites.revoke"
145
+ ]
146
+ },
147
+ {
148
+ id: "member",
149
+ assignable: true,
150
+ permissions: ["workspace.settings.view"]
151
+ }
152
+ ],
153
+ assignableRoleIds: ["admin", "member"]
154
+ }
155
+ });
156
+ });
@@ -0,0 +1,120 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import "../test-support/registerDefaultSettingsFields.js";
4
+ import { createService } from "../src/server/workspaceSettings/workspaceSettingsService.js";
5
+
6
+ function authorizedOptions(permissions = []) {
7
+ return {
8
+ context: {
9
+ actor: {
10
+ id: 1
11
+ },
12
+ permissions
13
+ }
14
+ };
15
+ }
16
+
17
+ function createFixture({ workspaceInvitationsEnabled = true } = {}) {
18
+ const state = {
19
+ settingsPatch: null,
20
+ workspace: {
21
+ id: 7,
22
+ slug: "tonymobily3",
23
+ name: "TonyMobily3",
24
+ ownerUserId: 9,
25
+ avatarUrl: "",
26
+ color: "#0F6B54"
27
+ },
28
+ settings: {
29
+ name: "TonyMobily3",
30
+ avatarUrl: "",
31
+ color: "#0F6B54",
32
+ invitesEnabled: true
33
+ }
34
+ };
35
+
36
+ const service = createService({
37
+ workspaceInvitationsEnabled,
38
+ workspaceSettingsRepository: {
39
+ async ensureForWorkspaceId(workspaceId) {
40
+ assert.equal(Number(workspaceId), 7);
41
+ return { ...state.settings };
42
+ },
43
+ async updateSettingsByWorkspaceId(workspaceId, patch) {
44
+ assert.equal(Number(workspaceId), 7);
45
+ state.settingsPatch = { ...patch };
46
+ state.settings = {
47
+ ...state.settings,
48
+ ...patch
49
+ };
50
+ return state.settings;
51
+ }
52
+ }
53
+ });
54
+
55
+ return { service, state };
56
+ }
57
+
58
+ test("workspaceSettingsService.getWorkspaceSettings returns the stored invitesEnabled flag", async () => {
59
+ const { service, state } = createFixture();
60
+
61
+ const response = await service.getWorkspaceSettings(
62
+ state.workspace,
63
+ authorizedOptions(["workspace.settings.view"])
64
+ );
65
+
66
+ assert.deepEqual(response.settings, {
67
+ name: "TonyMobily3",
68
+ avatarUrl: "",
69
+ color: "#0F6B54",
70
+ invitesEnabled: true,
71
+ invitesAvailable: true,
72
+ invitesEffective: true
73
+ });
74
+ });
75
+
76
+ test("workspaceSettingsService.updateWorkspaceSettings writes editable fields through workspaceSettingsRepository only", async () => {
77
+ const { service, state } = createFixture();
78
+
79
+ const response = await service.updateWorkspaceSettings(
80
+ state.workspace,
81
+ {
82
+ name: "New Name",
83
+ invitesEnabled: false
84
+ },
85
+ authorizedOptions(["workspace.settings.update"])
86
+ );
87
+
88
+ assert.deepEqual(state.settingsPatch, {
89
+ name: "New Name",
90
+ invitesEnabled: false
91
+ });
92
+ assert.deepEqual(response.settings, {
93
+ name: "New Name",
94
+ avatarUrl: "",
95
+ color: "#0F6B54",
96
+ invitesEnabled: false,
97
+ invitesAvailable: true,
98
+ invitesEffective: false
99
+ });
100
+ });
101
+
102
+ test("workspaceSettingsService disables invite settings in output when app policy disables invitations", async () => {
103
+ const { service, state } = createFixture({
104
+ workspaceInvitationsEnabled: false
105
+ });
106
+
107
+ const response = await service.getWorkspaceSettings(
108
+ state.workspace,
109
+ authorizedOptions(["workspace.settings.view"])
110
+ );
111
+
112
+ assert.deepEqual(response.settings, {
113
+ name: "TonyMobily3",
114
+ avatarUrl: "",
115
+ color: "#0F6B54",
116
+ invitesEnabled: false,
117
+ invitesAvailable: false,
118
+ invitesEffective: false
119
+ });
120
+ });
@@ -0,0 +1,3 @@
1
+ import "../templates/packages/main/src/shared/resources/workspaceSettingsFields.js";
2
+ import "../templates/packages/main/src/shared/resources/consoleSettingsFields.js";
3
+ import "../templates/packages/main/src/shared/resources/userSettingsFields.js";