@jskit-ai/users-core 0.1.31 → 0.1.33

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 (55) hide show
  1. package/package.descriptor.mjs +21 -19
  2. package/package.json +6 -6
  3. package/src/server/UsersCoreServiceProvider.js +1 -3
  4. package/src/server/accountNotifications/accountNotificationsService.js +3 -3
  5. package/src/server/accountNotifications/registerAccountNotifications.js +1 -1
  6. package/src/server/accountPreferences/accountPreferencesService.js +3 -3
  7. package/src/server/accountPreferences/registerAccountPreferences.js +1 -1
  8. package/src/server/accountProfile/accountProfileActions.js +8 -2
  9. package/src/server/accountProfile/accountProfileService.js +10 -10
  10. package/src/server/accountProfile/avatarService.js +26 -67
  11. package/src/server/accountProfile/avatarStorageService.js +14 -95
  12. package/src/server/accountProfile/bootAccountProfileRoutes.js +13 -15
  13. package/src/server/accountProfile/registerAccountProfile.js +2 -2
  14. package/src/server/accountSecurity/accountSecurityService.js +3 -3
  15. package/src/server/accountSecurity/registerAccountSecurity.js +1 -1
  16. package/src/server/common/contributors/workspaceActionContextContributor.js +24 -17
  17. package/src/server/common/formatters/workspaceFormatter.js +2 -2
  18. package/src/server/common/registerCommonRepositories.js +3 -3
  19. package/src/server/common/repositories/{userProfilesRepository.js → usersRepository.js} +7 -7
  20. package/src/server/common/repositories/workspaceInvitesRepository.js +2 -2
  21. package/src/server/common/repositories/workspaceMembershipsRepository.js +9 -9
  22. package/src/server/common/repositories/workspacesRepository.js +2 -2
  23. package/src/server/common/services/accountContextService.js +4 -4
  24. package/src/server/common/services/authProfileSyncService.js +15 -15
  25. package/src/server/common/services/workspaceContextService.js +3 -3
  26. package/src/server/common/validators/authenticatedUserValidator.js +2 -2
  27. package/src/server/registerWorkspaceBootstrap.js +1 -1
  28. package/src/server/registerWorkspaceCore.js +5 -2
  29. package/src/server/workspaceBootstrapContributor.js +6 -6
  30. package/src/server/workspaceMembers/bootWorkspaceMembers.js +2 -2
  31. package/src/server/workspaceMembers/workspaceMembersActions.js +2 -2
  32. package/src/server/workspaceMembers/workspaceMembersService.js +11 -11
  33. package/src/server/workspacePendingInvitations/workspacePendingInvitationsService.js +1 -1
  34. package/src/shared/resources/workspaceMembersResource.js +11 -11
  35. package/src/shared/resources/workspacePendingInvitationsResource.js +2 -2
  36. package/src/shared/resources/workspaceResource.js +2 -2
  37. package/src/shared/roles.js +37 -12
  38. package/templates/config/roles.js +27 -0
  39. package/templates/migrations/users_core_initial.cjs +5 -5
  40. package/test/authProfileSyncService.test.js +8 -8
  41. package/test/avatarService.test.js +6 -6
  42. package/test/roles.test.js +90 -5
  43. package/test/usersRouteRequestInputValidator.test.js +4 -4
  44. package/test/workspaceActionContextContributor.test.js +107 -14
  45. package/test/workspaceAuthPolicyContextResolver.test.js +2 -2
  46. package/test/workspaceBootstrapContributor.test.js +8 -8
  47. package/test/workspaceInvitesRepository.test.js +3 -3
  48. package/test/workspaceMembersService.test.js +14 -12
  49. package/test/workspacePendingInvitationsResource.test.js +2 -2
  50. package/test/workspacePendingInvitationsService.test.js +3 -3
  51. package/test/workspaceService.test.js +22 -18
  52. package/test/workspaceSettingsResource.test.js +4 -2
  53. package/src/server/accountProfile/registerAvatarMultipartSupport.js +0 -40
  54. package/templates/config/workspaceRoles.js +0 -30
  55. package/test/registerAvatarMultipartSupport.test.js +0 -63
@@ -19,12 +19,13 @@ test("workspace action context contributor resolves workspace context for worksp
19
19
  slug: "acme"
20
20
  },
21
21
  membership: {
22
- roleId: "owner"
22
+ roleSid: "owner"
23
23
  },
24
24
  permissions: ["workspace.settings.update"]
25
25
  };
26
26
  }
27
- }
27
+ },
28
+ workspaceSurfaceIds: ["admin", "app"]
28
29
  });
29
30
 
30
31
  const request = {
@@ -35,7 +36,10 @@ test("workspace action context contributor resolves workspace context for worksp
35
36
  };
36
37
 
37
38
  const contribution = await contributor.contribute({
38
- actionId: "workspace.settings.update",
39
+ definition: {
40
+ id: "workspace.settings.update",
41
+ surfaces: ["admin", "app"]
42
+ },
39
43
  input: {
40
44
  workspaceSlug: "Acme"
41
45
  },
@@ -64,7 +68,7 @@ test("workspace action context contributor resolves workspace context for worksp
64
68
  slug: "acme"
65
69
  },
66
70
  membership: {
67
- roleId: "owner"
71
+ roleSid: "owner"
68
72
  },
69
73
  permissions: ["workspace.settings.update"]
70
74
  }
@@ -74,7 +78,7 @@ test("workspace action context contributor resolves workspace context for worksp
74
78
  slug: "acme"
75
79
  },
76
80
  membership: {
77
- roleId: "owner"
81
+ roleSid: "owner"
78
82
  },
79
83
  permissions: ["workspace.settings.update"]
80
84
  });
@@ -113,12 +117,13 @@ test("workspace action context contributor always resolves and stores resolved c
113
117
  ownerUserId: 77
114
118
  },
115
119
  membership: {
116
- roleId: "owner"
120
+ roleSid: "owner"
117
121
  },
118
122
  permissions: ["workspace.settings.update"]
119
123
  };
120
124
  }
121
- }
125
+ },
126
+ workspaceSurfaceIds: ["admin", "app"]
122
127
  });
123
128
 
124
129
  const request = {
@@ -128,7 +133,10 @@ test("workspace action context contributor always resolves and stores resolved c
128
133
  };
129
134
 
130
135
  const contribution = await contributor.contribute({
131
- actionId: "workspace.members.list",
136
+ definition: {
137
+ id: "workspace.members.list",
138
+ surfaces: ["admin", "app"]
139
+ },
132
140
  input: {
133
141
  workspaceSlug: "acme"
134
142
  },
@@ -161,13 +169,13 @@ test("workspace action context contributor always resolves and stores resolved c
161
169
  ownerUserId: 77
162
170
  },
163
171
  membership: {
164
- roleId: "owner"
172
+ roleSid: "owner"
165
173
  },
166
174
  permissions: ["workspace.settings.update"]
167
175
  }
168
176
  },
169
177
  membership: {
170
- roleId: "owner"
178
+ roleSid: "owner"
171
179
  },
172
180
  permissions: ["workspace.settings.update"]
173
181
  });
@@ -185,7 +193,7 @@ test("workspace action context contributor resolves context for workspace-visibl
185
193
  slug: "acme"
186
194
  },
187
195
  membership: {
188
- roleId: "admin"
196
+ roleSid: "admin"
189
197
  },
190
198
  permissions: ["assistant.chat.use"]
191
199
  };
@@ -205,7 +213,10 @@ test("workspace action context contributor resolves context for workspace-visibl
205
213
  };
206
214
 
207
215
  const contribution = await contributor.contribute({
208
- actionId: "assistant.conversations.list",
216
+ definition: {
217
+ id: "assistant.conversations.list",
218
+ surfaces: ["admin"]
219
+ },
209
220
  input: {
210
221
  workspaceSlug: "acme"
211
222
  },
@@ -234,7 +245,7 @@ test("workspace action context contributor resolves context for workspace-visibl
234
245
  slug: "acme"
235
246
  },
236
247
  membership: {
237
- roleId: "admin"
248
+ roleSid: "admin"
238
249
  },
239
250
  permissions: ["assistant.chat.use"]
240
251
  }
@@ -244,8 +255,90 @@ test("workspace action context contributor resolves context for workspace-visibl
244
255
  slug: "acme"
245
256
  },
246
257
  membership: {
247
- roleId: "admin"
258
+ roleSid: "admin"
248
259
  },
249
260
  permissions: ["assistant.chat.use"]
250
261
  });
251
262
  });
263
+
264
+ test("workspace action context contributor resolves context for workspace surfaces even when route visibility is public", async () => {
265
+ const calls = [];
266
+ const contributor = createWorkspaceActionContextContributor({
267
+ workspaceService: {
268
+ async resolveWorkspaceContextForUserBySlug(user, workspaceSlug, options) {
269
+ calls.push({ user, workspaceSlug, options });
270
+ return {
271
+ workspace: {
272
+ id: 77,
273
+ slug: "acme"
274
+ },
275
+ membership: {
276
+ roleSid: "member"
277
+ },
278
+ permissions: ["crud.breeds.list"]
279
+ };
280
+ }
281
+ },
282
+ workspaceSurfaceIds: ["admin", "app"]
283
+ });
284
+
285
+ const request = {
286
+ user: {
287
+ id: 42
288
+ },
289
+ routeOptions: {
290
+ config: {
291
+ surface: "admin",
292
+ visibility: "public"
293
+ }
294
+ }
295
+ };
296
+
297
+ const contribution = await contributor.contribute({
298
+ definition: {
299
+ id: "crud.breeds.list",
300
+ surfaces: ["admin"]
301
+ },
302
+ input: {
303
+ workspaceSlug: "acme"
304
+ },
305
+ context: {
306
+ requestMeta: {
307
+ request
308
+ }
309
+ },
310
+ request
311
+ });
312
+
313
+ assert.deepEqual(calls, [
314
+ {
315
+ user: request.user,
316
+ workspaceSlug: "acme",
317
+ options: {
318
+ request
319
+ }
320
+ }
321
+ ]);
322
+ assert.deepEqual(contribution, {
323
+ requestMeta: {
324
+ resolvedWorkspaceContext: {
325
+ workspace: {
326
+ id: 77,
327
+ slug: "acme"
328
+ },
329
+ membership: {
330
+ roleSid: "member"
331
+ },
332
+ permissions: ["crud.breeds.list"]
333
+ }
334
+ },
335
+ workspace: {
336
+ id: 77,
337
+ slug: "acme"
338
+ },
339
+ membership: {
340
+ roleSid: "member"
341
+ },
342
+ permissions: ["crud.breeds.list"]
343
+ });
344
+ });
@@ -45,7 +45,7 @@ test("workspace auth policy context resolver resolves workspace context from use
45
45
  slug: workspaceSlug
46
46
  },
47
47
  membership: {
48
- roleId: "owner"
48
+ roleSid: "owner"
49
49
  },
50
50
  permissions: ["projects.read"]
51
51
  };
@@ -85,7 +85,7 @@ test("workspace auth policy context resolver resolves workspace context from use
85
85
  slug: "acme"
86
86
  },
87
87
  membership: {
88
- roleId: "owner"
88
+ roleSid: "owner"
89
89
  },
90
90
  permissions: ["projects.read"]
91
91
  });
@@ -10,7 +10,7 @@ function createAuthenticatedProfile(overrides = {}) {
10
10
  return {
11
11
  id: 7,
12
12
  authProvider: "local",
13
- authProviderUserId: "user-7",
13
+ authProviderUserSid: "user-7",
14
14
  username: "tester",
15
15
  displayName: "Test User",
16
16
  email: "test@example.com",
@@ -39,7 +39,7 @@ test("workspace bootstrap contributor passes actor context to pending invites se
39
39
  return [];
40
40
  }
41
41
  },
42
- userProfilesRepository: {
42
+ usersRepository: {
43
43
  async findByIdentity() {
44
44
  return profile;
45
45
  }
@@ -101,7 +101,7 @@ test("workspace bootstrap contributor seeds the initial console owner on authent
101
101
  return [];
102
102
  }
103
103
  },
104
- userProfilesRepository: {
104
+ usersRepository: {
105
105
  async findByIdentity() {
106
106
  return profile;
107
107
  }
@@ -162,7 +162,7 @@ test("workspace bootstrap contributor emits canonical tenancy profile from users
162
162
  return [];
163
163
  }
164
164
  },
165
- userProfilesRepository: {
165
+ usersRepository: {
166
166
  async findByIdentity() {
167
167
  return null;
168
168
  }
@@ -228,7 +228,7 @@ test("workspace bootstrap contributor resolves workspace slug from bootstrap que
228
228
  return [];
229
229
  }
230
230
  },
231
- userProfilesRepository: {
231
+ usersRepository: {
232
232
  async findByIdentity() {
233
233
  return profile;
234
234
  }
@@ -291,7 +291,7 @@ test("workspace bootstrap contributor returns global payload with requestedWorks
291
291
  return [];
292
292
  }
293
293
  },
294
- userProfilesRepository: {
294
+ usersRepository: {
295
295
  async findByIdentity() {
296
296
  return profile;
297
297
  }
@@ -363,7 +363,7 @@ test("workspace bootstrap contributor returns requestedWorkspace=not_found when
363
363
  return [];
364
364
  }
365
365
  },
366
- userProfilesRepository: {
366
+ usersRepository: {
367
367
  async findByIdentity() {
368
368
  return profile;
369
369
  }
@@ -427,7 +427,7 @@ test("workspace bootstrap contributor returns requestedWorkspace=unauthenticated
427
427
  return [];
428
428
  }
429
429
  },
430
- userProfilesRepository: {
430
+ usersRepository: {
431
431
  async findByIdentity() {
432
432
  return null;
433
433
  }
@@ -11,7 +11,7 @@ function createKnexStub() {
11
11
  id: 1,
12
12
  workspace_id: 1,
13
13
  email: "invitee@example.com",
14
- role_id: "member",
14
+ role_sid: "member",
15
15
  status: "pending",
16
16
  token_hash: "hash",
17
17
  invited_by_user_id: 1,
@@ -57,7 +57,7 @@ test("workspaceInvitesRepository.insert normalizes expiresAt ISO input to databa
57
57
  await repository.insert({
58
58
  workspaceId: 1,
59
59
  email: "invitee@example.com",
60
- roleId: "member",
60
+ roleSid: "member",
61
61
  status: "pending",
62
62
  tokenHash: "hash",
63
63
  invitedByUserId: 1,
@@ -76,7 +76,7 @@ test("workspaceInvitesRepository.findPendingByTokenHash reads from invites table
76
76
  id: 44,
77
77
  workspace_id: 9,
78
78
  email: "invitee@example.com",
79
- role_id: "member",
79
+ role_sid: "member",
80
80
  status: "pending",
81
81
  token_hash: "hash-token",
82
82
  invited_by_user_id: 1,
@@ -16,8 +16,10 @@ function authorizedOptions(permissions = []) {
16
16
 
17
17
  function createRoleCatalog() {
18
18
  return createWorkspaceRoleCatalog({
19
- workspaceRoles: {
20
- defaultInviteRole: "member",
19
+ roleCatalog: {
20
+ workspace: {
21
+ defaultInviteRole: "member"
22
+ },
21
23
  roles: {
22
24
  owner: {
23
25
  assignable: false,
@@ -52,7 +54,7 @@ function createFixture() {
52
54
  return [
53
55
  {
54
56
  userId: 11,
55
- roleId: "member",
57
+ roleSid: "member",
56
58
  status: "active",
57
59
  displayName: "Alice",
58
60
  email: "alice@example.com"
@@ -65,7 +67,7 @@ function createFixture() {
65
67
  return {
66
68
  workspaceId: 7,
67
69
  userId: 11,
68
- roleId: "member",
70
+ roleSid: "member",
69
71
  status: "active"
70
72
  };
71
73
  },
@@ -73,7 +75,7 @@ function createFixture() {
73
75
  assert.equal(Number(workspaceId), 7);
74
76
  assert.equal(Number(userId), 11);
75
77
  assert.deepEqual(patch, {
76
- roleId: "admin",
78
+ roleSid: "admin",
77
79
  status: "active"
78
80
  });
79
81
  }
@@ -134,7 +136,7 @@ test("workspaceMembersService.createInvite uses configured inviteExpiresInMs", a
134
136
  { id: 11 },
135
137
  {
136
138
  email: "alice@example.com",
137
- roleId: "member"
139
+ roleSid: "member"
138
140
  },
139
141
  authorizedOptions(["workspace.members.invite"])
140
142
  );
@@ -260,13 +262,13 @@ test("workspaceMembersService.updateMemberRole returns the refreshed member list
260
262
  workspace,
261
263
  {
262
264
  memberUserId: 11,
263
- roleId: "admin"
265
+ roleSid: "admin"
264
266
  },
265
267
  authorizedOptions(["workspace.members.manage"])
266
268
  );
267
269
 
268
270
  assert.equal(response.members.length, 1);
269
- assert.equal(response.members[0].roleId, "member");
271
+ assert.equal(response.members[0].roleSid, "member");
270
272
  });
271
273
 
272
274
  test("workspaceMembersService.removeMember marks membership revoked and returns refreshed members", async () => {
@@ -287,7 +289,7 @@ test("workspaceMembersService.removeMember marks membership revoked and returns
287
289
  : [
288
290
  {
289
291
  userId: 11,
290
- roleId: "member",
292
+ roleSid: "member",
291
293
  status: "active",
292
294
  displayName: "Alice",
293
295
  email: "alice@example.com"
@@ -300,7 +302,7 @@ test("workspaceMembersService.removeMember marks membership revoked and returns
300
302
  return {
301
303
  workspaceId: 7,
302
304
  userId: 11,
303
- roleId: "member",
305
+ roleSid: "member",
304
306
  status: "active"
305
307
  };
306
308
  },
@@ -308,7 +310,7 @@ test("workspaceMembersService.removeMember marks membership revoked and returns
308
310
  assert.equal(Number(workspaceId), 7);
309
311
  assert.equal(Number(userId), 11);
310
312
  assert.deepEqual(patch, {
311
- roleId: "member",
313
+ roleSid: "member",
312
314
  status: "revoked"
313
315
  });
314
316
  removed = true;
@@ -359,7 +361,7 @@ test("workspaceMembersService.removeMember rejects removing the owner", async ()
359
361
  return {
360
362
  workspaceId: 7,
361
363
  userId: 9,
362
- roleId: "owner",
364
+ roleSid: "owner",
363
365
  status: "active"
364
366
  };
365
367
  },
@@ -14,7 +14,7 @@ test("workspacePendingInvitationsResource output normalizer shapes raw invite ro
14
14
  workspaceSlug: "tonymobily3",
15
15
  workspaceName: "",
16
16
  workspaceAvatarUrl: "",
17
- roleId: "Member",
17
+ roleSid: "Member",
18
18
  status: "Pending",
19
19
  expiresAt: "2030-01-01T00:00:00.000Z",
20
20
  tokenHash
@@ -29,7 +29,7 @@ test("workspacePendingInvitationsResource output normalizer shapes raw invite ro
29
29
  workspaceSlug: "tonymobily3",
30
30
  workspaceName: "tonymobily3",
31
31
  workspaceAvatarUrl: "",
32
- roleId: "member",
32
+ roleSid: "member",
33
33
  status: "pending",
34
34
  expiresAt: "2030-01-01T00:00:00.000Z",
35
35
  token: encodeInviteTokenHash(tokenHash)
@@ -64,7 +64,7 @@ test("listPendingInvitesForUser returns raw pending invite rows for the action l
64
64
  workspaceSlug: "tonymobily3",
65
65
  workspaceName: "TonyMobily3",
66
66
  workspaceAvatarUrl: "",
67
- roleId: "member",
67
+ roleSid: "member",
68
68
  status: "pending",
69
69
  expiresAt: "2030-01-01T00:00:00.000Z",
70
70
  tokenHash
@@ -91,7 +91,7 @@ test("acceptInviteByToken accepts opaque invite token and resolves invite by dec
91
91
  id: 44,
92
92
  workspaceId: 1,
93
93
  email: "chiaramobily@gmail.com",
94
- roleId: "member",
94
+ roleSid: "member",
95
95
  status: "pending",
96
96
  tokenHash,
97
97
  expiresAt: "2030-01-01T00:00:00.000Z"
@@ -125,7 +125,7 @@ test("refuseInviteByToken revokes the invite and returns refused", async () => {
125
125
  id: 45,
126
126
  workspaceId: 1,
127
127
  email: "chiaramobily@gmail.com",
128
- roleId: "member",
128
+ roleSid: "member",
129
129
  status: "pending",
130
130
  tokenHash,
131
131
  expiresAt: "2030-01-01T00:00:00.000Z"
@@ -2,9 +2,11 @@ import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
3
  import { createService } from "../src/server/common/services/workspaceContextService.js";
4
4
 
5
- function createWorkspaceRoles() {
5
+ function createRoleCatalog() {
6
6
  return {
7
- defaultInviteRole: "member",
7
+ workspace: {
8
+ defaultInviteRole: "member"
9
+ },
8
10
  roles: {
9
11
  owner: {
10
12
  assignable: false,
@@ -21,7 +23,7 @@ function createWorkspaceRoles() {
21
23
  function createWorkspaceServiceFixture({
22
24
  tenancyMode = "workspaces",
23
25
  tenancyPolicy = {},
24
- workspaceRoles = createWorkspaceRoles(),
26
+ roleCatalog = createRoleCatalog(),
25
27
  additionalWorkspaces = [],
26
28
  userWorkspaceRows = null,
27
29
  membershipResolver = null,
@@ -69,7 +71,7 @@ function createWorkspaceServiceFixture({
69
71
  appConfig: {
70
72
  tenancyMode,
71
73
  tenancyPolicy,
72
- workspaceRoles: workspaceRoles && typeof workspaceRoles === "object" ? { ...workspaceRoles } : workspaceRoles
74
+ roleCatalog: roleCatalog && typeof roleCatalog === "object" ? { ...roleCatalog } : roleCatalog
73
75
  },
74
76
  workspacesRepository: {
75
77
  async findBySlug(slug) {
@@ -95,7 +97,7 @@ function createWorkspaceServiceFixture({
95
97
  slug: "tonymobily3",
96
98
  name: "TonyMobily3",
97
99
  avatarUrl: "",
98
- roleId: "owner",
100
+ roleSid: "owner",
99
101
  membershipStatus: "active"
100
102
  },
101
103
  {
@@ -103,7 +105,7 @@ function createWorkspaceServiceFixture({
103
105
  slug: "pending-workspace",
104
106
  name: "Pending Workspace",
105
107
  avatarUrl: "",
106
- roleId: "member",
108
+ roleSid: "member",
107
109
  membershipStatus: "pending"
108
110
  }
109
111
  ];
@@ -158,7 +160,7 @@ function createWorkspaceServiceFixture({
158
160
  return {
159
161
  workspaceId,
160
162
  userId,
161
- roleId: "owner",
163
+ roleSid: "owner",
162
164
  status: "active"
163
165
  };
164
166
  }
@@ -190,7 +192,7 @@ test("workspaceService.listWorkspacesForUser returns only accessible workspaces"
190
192
 
191
193
  assert.equal(workspaces.length, 1);
192
194
  assert.equal(workspaces[0].slug, "tonymobily3");
193
- assert.equal(workspaces[0].roleId, "owner");
195
+ assert.equal(workspaces[0].roleSid, "owner");
194
196
  assert.equal(calls.listForUserId, 1);
195
197
  assert.equal(calls.insert, 0);
196
198
  });
@@ -220,7 +222,7 @@ test("workspaceService.listWorkspacesForUser returns all active memberships in p
220
222
  slug: "chiaramobily",
221
223
  name: "Chiara Personal",
222
224
  avatarUrl: "",
223
- roleId: "owner",
225
+ roleSid: "owner",
224
226
  membershipStatus: "active"
225
227
  },
226
228
  {
@@ -228,7 +230,7 @@ test("workspaceService.listWorkspacesForUser returns all active memberships in p
228
230
  slug: "tonymobily",
229
231
  name: "Tony Workspace",
230
232
  avatarUrl: "",
231
- roleId: "member",
233
+ roleSid: "member",
232
234
  membershipStatus: "active"
233
235
  },
234
236
  {
@@ -236,7 +238,7 @@ test("workspaceService.listWorkspacesForUser returns all active memberships in p
236
238
  slug: "pending-workspace",
237
239
  name: "Pending Workspace",
238
240
  avatarUrl: "",
239
- roleId: "member",
241
+ roleSid: "member",
240
242
  membershipStatus: "pending"
241
243
  }
242
244
  ]
@@ -393,7 +395,7 @@ test("workspaceService.resolveWorkspaceContextForUserBySlug allows personal tena
393
395
  );
394
396
 
395
397
  assert.equal(context.workspace.slug, "team-alpha");
396
- assert.equal(context.membership.roleId, "owner");
398
+ assert.equal(context.membership.roleSid, "owner");
397
399
  assert.deepEqual(context.permissions, ["*"]);
398
400
  });
399
401
 
@@ -404,7 +406,7 @@ test("workspaceService.resolveWorkspaceContextForUserBySlug grants owner access
404
406
  const service = createService({
405
407
  appConfig: {
406
408
  tenancyMode: "personal",
407
- workspaceRoles: createWorkspaceRoles()
409
+ roleCatalog: createRoleCatalog()
408
410
  },
409
411
  workspacesRepository: {
410
412
  async findBySlug(slug) {
@@ -439,7 +441,7 @@ test("workspaceService.resolveWorkspaceContextForUserBySlug grants owner access
439
441
  membershipRecord = {
440
442
  workspaceId,
441
443
  userId,
442
- roleId: "owner",
444
+ roleSid: "owner",
443
445
  status: "active"
444
446
  };
445
447
  return membershipRecord;
@@ -464,14 +466,16 @@ test("workspaceService.resolveWorkspaceContextForUserBySlug grants owner access
464
466
  );
465
467
 
466
468
  assert.equal(ensuredMembershipCount, 1);
467
- assert.equal(context.membership.roleId, "owner");
469
+ assert.equal(context.membership.roleSid, "owner");
468
470
  assert.deepEqual(context.permissions, ["*"]);
469
471
  });
470
472
 
471
- test("workspaceService.resolveWorkspaceContextForUserBySlug resolves permissions from appConfig.workspaceRoles", async () => {
473
+ test("workspaceService.resolveWorkspaceContextForUserBySlug resolves permissions from appConfig.roleCatalog", async () => {
472
474
  const { service } = createWorkspaceServiceFixture({
473
- workspaceRoles: {
474
- defaultInviteRole: "member",
475
+ roleCatalog: {
476
+ workspace: {
477
+ defaultInviteRole: "member"
478
+ },
475
479
  roles: {
476
480
  owner: {
477
481
  assignable: false,
@@ -8,8 +8,10 @@ import { createWorkspaceRoleCatalog } from "../src/shared/roles.js";
8
8
 
9
9
  function createRoleCatalog() {
10
10
  return createWorkspaceRoleCatalog({
11
- workspaceRoles: {
12
- defaultInviteRole: "member",
11
+ roleCatalog: {
12
+ workspace: {
13
+ defaultInviteRole: "member"
14
+ },
13
15
  roles: {
14
16
  owner: {
15
17
  assignable: false,
@@ -1,40 +0,0 @@
1
- import fastifyMultipart from "@fastify/multipart";
2
-
3
- async function registerAvatarMultipartSupport(app) {
4
- if (!app || typeof app.has !== "function" || typeof app.make !== "function") {
5
- throw new Error("registerAvatarMultipartSupport requires application has()/make().");
6
- }
7
-
8
- if (!app.has("jskit.fastify")) {
9
- return;
10
- }
11
-
12
- const fastify = app.make("jskit.fastify");
13
- if (!fastify || typeof fastify.register !== "function") {
14
- throw new Error("registerAvatarMultipartSupport requires Fastify register().");
15
- }
16
-
17
- if (fastify["jskit.users-core.avatar.multipart.support"] === true) {
18
- return;
19
- }
20
-
21
- if (typeof fastify.hasContentTypeParser === "function" && fastify.hasContentTypeParser("multipart")) {
22
- Object.defineProperty(fastify, "jskit.users-core.avatar.multipart.support", {
23
- value: true,
24
- configurable: false,
25
- enumerable: false,
26
- writable: false
27
- });
28
- return;
29
- }
30
-
31
- await fastify.register(fastifyMultipart);
32
- Object.defineProperty(fastify, "jskit.users-core.avatar.multipart.support", {
33
- value: true,
34
- configurable: false,
35
- enumerable: false,
36
- writable: false
37
- });
38
- }
39
-
40
- export { registerAvatarMultipartSupport };
@@ -1,30 +0,0 @@
1
- export const workspaceRoles = {};
2
-
3
- workspaceRoles.defaultInviteRole = "member";
4
- workspaceRoles.roles = {};
5
-
6
- workspaceRoles.roles.owner = {
7
- assignable: false,
8
- permissions: []
9
- };
10
- workspaceRoles.roles.owner.permissions.push("*");
11
-
12
- workspaceRoles.roles.admin = {
13
- assignable: true,
14
- permissions: []
15
- };
16
- workspaceRoles.roles.admin.permissions.push(
17
- "workspace.roles.view",
18
- "workspace.settings.view",
19
- "workspace.settings.update",
20
- "workspace.members.view",
21
- "workspace.members.invite",
22
- "workspace.members.manage",
23
- "workspace.invites.revoke"
24
- );
25
-
26
- workspaceRoles.roles.member = {
27
- assignable: true,
28
- permissions: []
29
- };
30
- workspaceRoles.roles.member.permissions.push("workspace.settings.view");