@jskit-ai/workspaces-core 0.2.4 → 0.2.5

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 (75) hide show
  1. package/locales/en.json +2 -2
  2. package/package.json +38 -11
  3. package/patterns/workspace-server/PATTERN.md +42 -17
  4. package/patterns/workspace-server/example/packages/main/src/server/WorkspaceInvitationsFeature.js +27 -0
  5. package/patterns/workspace-server/example/packages/main/src/server/email/workspaceInviteEmail.js +11 -10
  6. package/patterns/workspace-server/example/packages/main/src/server/email/workspaceInviteMailer.js +27 -0
  7. package/src/server/WorkspacesFeature.js +74 -183
  8. package/src/server/WorkspacesIntegrationsProvider.js +26 -10
  9. package/src/server/common/contributors/workspaceActionContextContributor.js +60 -70
  10. package/src/server/common/contributors/workspaceAuthPolicyContextResolver.js +12 -27
  11. package/src/server/common/contributors/workspaceRouteVisibilityResolver.js +48 -63
  12. package/src/server/common/formatters/workspaceFormatter.js +12 -10
  13. package/src/server/common/repositories/workspaceInvitesRepository.js +74 -75
  14. package/src/server/common/repositories/workspaceMembershipsRepository.js +23 -45
  15. package/src/server/common/repositories/workspacesRepository.js +17 -25
  16. package/src/server/common/services/{workspaceContextService.js → workspaceDirectoryService.js} +52 -72
  17. package/src/server/common/support/realtimeServiceEvents.js +20 -12
  18. package/src/server/common/support/workspaceRecordId.js +21 -0
  19. package/src/server/common/validators/routeParamsValidator.js +3 -2
  20. package/src/server/i18n.js +5 -2
  21. package/src/server/support/resolveWorkspace.js +10 -7
  22. package/src/server/support/workspaceActionSurfaces.js +5 -41
  23. package/src/server/support/workspaceCrud.js +25 -0
  24. package/src/server/support/workspaceInvitationsPolicy.js +16 -27
  25. package/src/server/support/workspaceRouteInput.js +19 -2
  26. package/src/server/{common/support → support}/workspaceRoutePaths.js +8 -2
  27. package/src/server/types.js +47 -0
  28. package/src/server/workspaceBootstrapContributor.js +105 -142
  29. package/src/server/workspaceDirectory/workspaceDirectoryActions.js +105 -137
  30. package/src/server/workspaceDirectory/{bootWorkspaceDirectoryRoutes.js → workspaceDirectoryRoutes.js} +7 -19
  31. package/src/server/workspaceMembers/defaultWorkspaceInviteEmail.js +15 -29
  32. package/src/server/workspaceMembers/unconfiguredWorkspaceInviteMailer.js +25 -0
  33. package/src/server/workspaceMembers/workspaceInviteUrls.js +24 -23
  34. package/src/server/workspaceMembers/workspaceMembersActions.js +189 -219
  35. package/src/server/workspaceMembers/{bootWorkspaceMembers.js → workspaceMembersRoutes.js} +14 -26
  36. package/src/server/workspaceMembers/workspaceMembersService.js +110 -97
  37. package/src/server/workspacePendingInvitations/noInviteAcceptanceParticipant.js +8 -0
  38. package/src/server/workspacePendingInvitations/workspacePendingInvitationsActions.js +79 -112
  39. package/src/server/workspacePendingInvitations/{bootWorkspacePendingInvitations.js → workspacePendingInvitationsRoutes.js} +11 -8
  40. package/src/server/workspacePendingInvitations/workspacePendingInvitationsService.js +54 -57
  41. package/src/server/workspaceSettings/workspaceSettingsActions.js +53 -73
  42. package/src/server/workspaceSettings/workspaceSettingsRepository.js +20 -20
  43. package/src/server/workspaceSettings/{bootWorkspaceSettings.js → workspaceSettingsRoutes.js} +9 -21
  44. package/src/server/workspaceSettings/workspaceSettingsService.js +24 -26
  45. package/src/server/workspacesRuntime.js +185 -0
  46. package/src/shared/operationMessages.js +6 -15
  47. package/src/shared/resources/workspaceMembersResource.js +2 -2
  48. package/src/shared/resources/workspacePendingInvitationsResource.js +2 -2
  49. package/src/shared/resources/workspaceResource.js +4 -1
  50. package/src/shared/roles.js +15 -12
  51. package/src/shared/tenancyProfile.js +2 -2
  52. package/test/exportsContract.test.js +5 -0
  53. package/test/featureRuntime.test.js +5 -6
  54. package/test/packageMetadata.test.js +12 -0
  55. package/test/repositories.integration.test.js +1 -1
  56. package/test/requestedWorkspaceScope.test.js +24 -0
  57. package/test/roleLabels.test.js +29 -7
  58. package/test/support/invitationTransactions.js +35 -19
  59. package/test/workspaceActionSurfaces.test.js +0 -55
  60. package/test/workspaceAuthPolicyContextResolver.test.js +1 -1
  61. package/test/workspaceBootstrapContributor.test.js +7 -6
  62. package/test/workspaceCrud.test.js +23 -0
  63. package/test/workspaceInvitesRepository.test.js +9 -8
  64. package/test/workspaceMembersService.test.js +131 -28
  65. package/test/workspacePendingInvitationsResource.test.js +2 -2
  66. package/test/workspacePendingInvitationsService.test.js +69 -22
  67. package/test/workspaceService.test.js +58 -47
  68. package/test/workspaceSettingsActions.test.js +14 -7
  69. package/test/workspaceSettingsRepository.test.js +11 -0
  70. package/test/workspaceSettingsService.test.js +11 -9
  71. package/test/workspacesRepository.test.js +16 -16
  72. package/test/workspacesRouteRequestInputValidator.test.js +4 -4
  73. package/src/server/common/repositories/repositoryUtils.js +0 -43
  74. package/src/server/common/support/deepFreeze.js +0 -1
  75. package/src/server/common/support/resolveActionUser.js +0 -9
@@ -1,8 +1,8 @@
1
1
  import { workspaceInviteAssistantOutput } from "../../shared/resources/workspaceAssistantOutputs.js";
2
2
  import { composeSchemaDefinitions } from "@jskit-ai/kernel/shared/validators";
3
- import { returnJsonApiData } from "@jskit-ai/http-runtime/shared";
3
+ import { jsonApiResultValue } from "@jskit-ai/http-runtime/shared";
4
4
  import { resolveWorkspace } from "../support/resolveWorkspace.js";
5
- import { resolveActionUser } from "../common/support/resolveActionUser.js";
5
+ import { resolveActionUser } from "@jskit-ai/users-core/server/resolveActionUser";
6
6
  import { workspaceMembersResource } from "../../shared/resources/workspaceMembersResource.js";
7
7
  import {
8
8
  INVITE_RECIPIENT_BOOTSTRAP_AUDIENCE,
@@ -20,7 +20,7 @@ const WORKSPACE_INVITE_CREATED_EVENTS = createWorkspaceEntityAndBootstrapEvents(
20
20
  workspaceEntity: "invite",
21
21
  workspaceOperation: "created",
22
22
  workspaceRealtimeEvent: "workspace.invites.changed",
23
- bootstrapEntityId: ({ result } = {}) => result?.value?.createdInviteId,
23
+ bootstrapEntityId: ({ result }) => jsonApiResultValue(result).createdInviteId,
24
24
  bootstrapAudience: INVITE_RECIPIENT_BOOTSTRAP_AUDIENCE
25
25
  });
26
26
 
@@ -28,7 +28,7 @@ const WORKSPACE_INVITE_REVOKED_EVENTS = createWorkspaceEntityAndBootstrapEvents(
28
28
  workspaceEntity: "invite",
29
29
  workspaceOperation: "updated",
30
30
  workspaceRealtimeEvent: "workspace.invites.changed",
31
- bootstrapEntityId: ({ result } = {}) => result?.value?.revokedInviteId,
31
+ bootstrapEntityId: ({ result }) => jsonApiResultValue(result).revokedInviteId,
32
32
  bootstrapAudience: INVITE_RECIPIENT_BOOTSTRAP_AUDIENCE
33
33
  });
34
34
 
@@ -36,7 +36,7 @@ const updateMemberRoleActionInput = composeSchemaDefinitions([
36
36
  workspaceSlugParamsValidator,
37
37
  workspaceMembersResource.operations.updateMemberRole.input
38
38
  ], {
39
- mode: workspaceMembersResource.operations.updateMemberRole.input.mode,
39
+ mode: "create",
40
40
  context: "workspaceMembersActions.updateMemberRoleActionInput"
41
41
  });
42
42
 
@@ -44,7 +44,7 @@ const removeMemberActionInput = composeSchemaDefinitions([
44
44
  workspaceSlugParamsValidator,
45
45
  workspaceMembersResource.operations.removeMember.input
46
46
  ], {
47
- mode: workspaceMembersResource.operations.removeMember.input.mode,
47
+ mode: "create",
48
48
  context: "workspaceMembersActions.removeMemberActionInput"
49
49
  });
50
50
 
@@ -52,7 +52,7 @@ const createInviteActionInput = composeSchemaDefinitions([
52
52
  workspaceSlugParamsValidator,
53
53
  workspaceMembersResource.operations.createInvite.body
54
54
  ], {
55
- mode: workspaceMembersResource.operations.createInvite.body.mode,
55
+ mode: "create",
56
56
  context: "workspaceMembersActions.createInviteActionInput"
57
57
  });
58
58
 
@@ -60,223 +60,193 @@ const revokeInviteActionInput = composeSchemaDefinitions([
60
60
  workspaceSlugParamsValidator,
61
61
  workspaceMembersResource.operations.revokeInvite.input
62
62
  ], {
63
- mode: workspaceMembersResource.operations.revokeInvite.input.mode,
63
+ mode: "create",
64
64
  context: "workspaceMembersActions.revokeInviteActionInput"
65
65
  });
66
66
 
67
- const workspaceMembersActionSpecifications = Object.freeze([
68
- {
69
- id: "workspace.roles.list",
70
- version: 1,
71
- kind: "query",
72
- channels: ["api", "automation", "internal"],
73
- surfaces: ["*"],
74
- permission: {
75
- require: "all",
76
- permissions: ["workspace.roles.view"]
77
- },
78
- input: workspaceSlugParamsValidator,
79
- output: null,
80
- extensions: {
81
- assistant: {
82
- description: "List the active workspace roles and assignable permissions.",
83
- output: workspaceMembersResource.operations.rolesList.output,
84
- transformResult: (result) => result.value
85
- }
86
- },
87
- idempotency: "none",
88
- async run(workspaceMembersService, _input, context) {
89
- return returnJsonApiData(await workspaceMembersService.listRoles({ context }));
90
- }
91
- },
92
- {
93
- id: "workspace.members.list",
94
- version: 1,
95
- kind: "query",
96
- channels: ["api", "automation", "internal"],
97
- surfaces: ["*"],
98
- permission: {
99
- require: "all",
100
- permissions: ["workspace.members.view"]
101
- },
102
- input: workspaceSlugParamsValidator,
103
- output: null,
104
- extensions: {
105
- assistant: {
106
- description: "List members of the active workspace.",
107
- output: workspaceMembersResource.operations.membersList.output,
108
- transformResult: (result) => result.value
109
- }
110
- },
111
- idempotency: "none",
112
- async run(workspaceMembersService, input, context) {
113
- return returnJsonApiData(await workspaceMembersService.listMembers(resolveWorkspace(context, input), {
114
- context
115
- }));
116
- }
117
- },
118
- {
119
- id: "workspace.member.role.update",
120
- version: 1,
121
- kind: "command",
122
- channels: ["api", "automation", "internal"],
123
- surfaces: ["*"],
124
- permission: {
125
- require: "all",
126
- permissions: ["workspace.members.manage"]
127
- },
128
- input: updateMemberRoleActionInput,
129
- output: null,
130
- extensions: {
131
- assistant: {
132
- description: "Change a member’s role in the active workspace.",
133
- output: workspaceMembersResource.operations.updateMemberRole.output,
134
- transformResult: (result) => result.value
135
- }
136
- },
137
- idempotency: "optional",
138
- events: WORKSPACE_MEMBER_CHANGED_EVENTS,
139
- async run(workspaceMembersService, input, context) {
140
- return returnJsonApiData(await workspaceMembersService.updateMemberRole(resolveWorkspace(context, input), {
141
- memberUserId: input.memberUserId,
142
- roleSid: input.roleSid
143
- }, {
144
- context
145
- }));
146
- }
147
- },
148
- {
149
- id: "workspace.member.remove",
150
- version: 1,
151
- kind: "command",
152
- channels: ["api", "automation", "internal"],
153
- surfaces: ["*"],
154
- permission: {
155
- require: "all",
156
- permissions: ["workspace.members.manage"]
157
- },
158
- input: removeMemberActionInput,
159
- output: null,
160
- extensions: {
161
- assistant: {
162
- description: "Remove a member from the active workspace.",
163
- output: workspaceMembersResource.operations.removeMember.output,
164
- transformResult: (result) => result.value
165
- }
166
- },
167
- idempotency: "optional",
168
- events: WORKSPACE_MEMBER_CHANGED_EVENTS,
169
- async run(workspaceMembersService, input, context) {
170
- return returnJsonApiData(await workspaceMembersService.removeMember(resolveWorkspace(context, input), {
171
- memberUserId: input.memberUserId
172
- }, {
173
- context
174
- }));
175
- }
176
- },
177
- {
178
- id: "workspace.invites.list",
179
- version: 1,
180
- kind: "query",
181
- channels: ["api", "automation", "internal"],
182
- surfaces: ["*"],
183
- permission: {
184
- require: "all",
185
- permissions: ["workspace.members.view"]
186
- },
187
- input: workspaceSlugParamsValidator,
188
- output: null,
189
- extensions: {
190
- assistant: {
191
- description: "List invitations for the active workspace without invitation tokens.",
192
- output: workspaceMembersResource.operations.invitesList.output,
193
- transformResult: (result) => result.value
194
- }
195
- },
196
- idempotency: "none",
197
- async run(workspaceMembersService, input, context) {
198
- return returnJsonApiData(await workspaceMembersService.listInvites(resolveWorkspace(context, input), {
199
- context
200
- }));
201
- }
202
- },
203
- {
204
- id: "workspace.invite.create",
205
- version: 1,
206
- kind: "command",
207
- channels: ["api", "assistant_tool", "automation", "internal"],
208
- surfaces: ["*"],
209
- permission: {
210
- require: "all",
211
- permissions: ["workspace.members.invite"]
212
- },
213
- input: createInviteActionInput,
214
- output: null,
215
- extensions: {
216
- assistant: {
217
- description: "Invite a person to the active workspace and report delivery status without exposing invitation links.",
218
- output: workspaceInviteAssistantOutput,
219
- transformResult: (result) => ({
220
- createdInviteId: result.value.createdInviteId,
221
- invites: result.value.invites.map(({ id, email, roleSid, status, expiresAt }) =>
222
- ({ id, email, roleSid, status, expiresAt })),
223
- deliveryStatus: result.value.inviteDelivery.status
224
- })
225
- }
226
- },
227
- idempotency: "optional",
228
- events: WORKSPACE_INVITE_CREATED_EVENTS,
229
- async run(workspaceMembersService, input, context) {
230
- return returnJsonApiData(await workspaceMembersService.createInvite(
67
+ /** @param {{ workspaceMembersService: import("../types.js").WorkspaceMembersService }} dependencies */
68
+ function createWorkspaceMembersActions({ workspaceMembersService }) {
69
+ return Object.freeze([
70
+ Object.freeze({
71
+ id: "workspace.roles.list",
72
+ version: 1,
73
+ kind: "query",
74
+ channels: ["api", "automation", "internal"],
75
+ surfaces: ["*"],
76
+ permission: {
77
+ require: "all",
78
+ permissions: ["workspace.roles.view"]
79
+ },
80
+ input: workspaceSlugParamsValidator,
81
+ output: null,
82
+ extensions: {
83
+ assistant: {
84
+ description: "List the active workspace roles and assignable permissions.",
85
+ output: workspaceMembersResource.operations.rolesList.output,
86
+ transformResult: (result) => result.value
87
+ }
88
+ },
89
+ execute: () => workspaceMembersService.listRolesResponse()
90
+ }),
91
+ Object.freeze({
92
+ id: "workspace.members.list",
93
+ version: 1,
94
+ kind: "query",
95
+ channels: ["api", "automation", "internal"],
96
+ surfaces: ["*"],
97
+ permission: {
98
+ require: "all",
99
+ permissions: ["workspace.members.view"]
100
+ },
101
+ input: workspaceSlugParamsValidator,
102
+ output: null,
103
+ extensions: {
104
+ assistant: {
105
+ description: "List members of the active workspace.",
106
+ output: workspaceMembersResource.operations.membersList.output,
107
+ transformResult: (result) => result.value
108
+ }
109
+ },
110
+ execute: (input, context) => workspaceMembersService.listMembersResponse(
111
+ resolveWorkspace(context, input),
112
+ { context }
113
+ )
114
+ }),
115
+ Object.freeze({
116
+ id: "workspace.member.role.update",
117
+ version: 1,
118
+ kind: "command",
119
+ channels: ["api", "automation", "internal"],
120
+ surfaces: ["*"],
121
+ permission: {
122
+ require: "all",
123
+ permissions: ["workspace.members.manage"]
124
+ },
125
+ input: updateMemberRoleActionInput,
126
+ output: null,
127
+ extensions: {
128
+ assistant: {
129
+ description: "Change a member’s role in the active workspace.",
130
+ output: workspaceMembersResource.operations.updateMemberRole.output,
131
+ transformResult: (result) => result.value
132
+ }
133
+ },
134
+ events: WORKSPACE_MEMBER_CHANGED_EVENTS,
135
+ execute: (input, context) => workspaceMembersService.updateMemberRole(
136
+ resolveWorkspace(context, input),
137
+ input,
138
+ { context }
139
+ )
140
+ }),
141
+ Object.freeze({
142
+ id: "workspace.member.remove",
143
+ version: 1,
144
+ kind: "command",
145
+ channels: ["api", "automation", "internal"],
146
+ surfaces: ["*"],
147
+ permission: {
148
+ require: "all",
149
+ permissions: ["workspace.members.manage"]
150
+ },
151
+ input: removeMemberActionInput,
152
+ output: null,
153
+ extensions: {
154
+ assistant: {
155
+ description: "Remove a member from the active workspace.",
156
+ output: workspaceMembersResource.operations.removeMember.output,
157
+ transformResult: (result) => result.value
158
+ }
159
+ },
160
+ events: WORKSPACE_MEMBER_CHANGED_EVENTS,
161
+ execute: (input, context) => workspaceMembersService.removeMember(
162
+ resolveWorkspace(context, input),
163
+ input,
164
+ { context }
165
+ )
166
+ }),
167
+ Object.freeze({
168
+ id: "workspace.invites.list",
169
+ version: 1,
170
+ kind: "query",
171
+ channels: ["api", "automation", "internal"],
172
+ surfaces: ["*"],
173
+ permission: {
174
+ require: "all",
175
+ permissions: ["workspace.members.view"]
176
+ },
177
+ input: workspaceSlugParamsValidator,
178
+ output: null,
179
+ extensions: {
180
+ assistant: {
181
+ description: "List invitations for the active workspace without invitation tokens.",
182
+ output: workspaceMembersResource.operations.invitesList.output,
183
+ transformResult: (result) => result.value
184
+ }
185
+ },
186
+ execute: (input, context) => workspaceMembersService.listInvitesResponse(
187
+ resolveWorkspace(context, input),
188
+ { context }
189
+ )
190
+ }),
191
+ Object.freeze({
192
+ id: "workspace.invite.create",
193
+ version: 1,
194
+ kind: "command",
195
+ channels: ["api", "assistant_tool", "automation", "internal"],
196
+ surfaces: ["*"],
197
+ permission: {
198
+ require: "all",
199
+ permissions: ["workspace.members.invite"]
200
+ },
201
+ input: createInviteActionInput,
202
+ output: null,
203
+ extensions: {
204
+ assistant: {
205
+ description: "Invite a person to the active workspace and report delivery status without exposing invitation links.",
206
+ output: workspaceInviteAssistantOutput,
207
+ transformResult: (result) => ({
208
+ createdInviteId: result.value.createdInviteId,
209
+ invites: result.value.invites.map(({ id, email, roleSid, status, expiresAt }) =>
210
+ ({ id, email, roleSid, status, expiresAt })),
211
+ deliveryStatus: result.value.inviteDelivery.status
212
+ })
213
+ }
214
+ },
215
+ events: WORKSPACE_INVITE_CREATED_EVENTS,
216
+ execute: (input, context) => workspaceMembersService.createInvite(
231
217
  resolveWorkspace(context, input),
232
218
  resolveActionUser(context, input),
233
- {
234
- email: input.email,
235
- roleSid: input.roleSid
236
- },
237
- {
238
- context
219
+ input,
220
+ { context }
221
+ )
222
+ }),
223
+ Object.freeze({
224
+ id: "workspace.invite.revoke",
225
+ version: 1,
226
+ kind: "command",
227
+ channels: ["api", "automation", "internal"],
228
+ surfaces: ["*"],
229
+ permission: {
230
+ require: "all",
231
+ permissions: ["workspace.invites.revoke"]
232
+ },
233
+ input: revokeInviteActionInput,
234
+ output: null,
235
+ extensions: {
236
+ assistant: {
237
+ description: "Revoke an invitation to the active workspace.",
238
+ output: workspaceMembersResource.operations.revokeInvite.output,
239
+ transformResult: (result) => result.value
239
240
  }
240
- ));
241
- }
242
- },
243
- {
244
- id: "workspace.invite.revoke",
245
- version: 1,
246
- kind: "command",
247
- channels: ["api", "automation", "internal"],
248
- surfaces: ["*"],
249
- permission: {
250
- require: "all",
251
- permissions: ["workspace.invites.revoke"]
252
- },
253
- input: revokeInviteActionInput,
254
- output: null,
255
- extensions: {
256
- assistant: {
257
- description: "Revoke an invitation to the active workspace.",
258
- output: workspaceMembersResource.operations.revokeInvite.output,
259
- transformResult: (result) => result.value
260
- }
261
- },
262
- idempotency: "optional",
263
- events: WORKSPACE_INVITE_REVOKED_EVENTS,
264
- async run(workspaceMembersService, input, context) {
265
- return returnJsonApiData(await workspaceMembersService.revokeInvite(resolveWorkspace(context, input), input.inviteId, {
266
- context
267
- }));
268
- }
269
- }
270
- ]);
271
-
272
- function buildWorkspaceMembersActions({ workspaceMembersService } = {}) {
273
- if (!workspaceMembersService) throw new TypeError("buildWorkspaceMembersActions requires workspaceMembersService.");
274
- return workspaceMembersActionSpecifications.map(({ run, ...definition }) => Object.freeze({
275
- ...definition,
276
- execute(input, context) {
277
- return run(workspaceMembersService, input, context);
278
- }
279
- }));
241
+ },
242
+ events: WORKSPACE_INVITE_REVOKED_EVENTS,
243
+ execute: (input, context) => workspaceMembersService.revokeInvite(
244
+ resolveWorkspace(context, input),
245
+ input.inviteId,
246
+ { context }
247
+ )
248
+ })
249
+ ]);
280
250
  }
281
251
 
282
- export { workspaceMembersActionSpecifications, buildWorkspaceMembersActions };
252
+ export { createWorkspaceMembersActions };
@@ -7,31 +7,19 @@ import {
7
7
  WORKSPACE_ROLE_CATALOG_TRANSPORT
8
8
  } from "../../shared/jsonApiTransports.js";
9
9
  import { workspaceMembersResource } from "../../shared/resources/workspaceMembersResource.js";
10
- import { resolveWorkspaceRoutePath } from "../common/support/workspaceRoutePaths.js";
10
+ import { resolveWorkspaceRecordId } from "../common/support/workspaceRecordId.js";
11
+ import { resolveWorkspaceRoutePath } from "../support/workspaceRoutePaths.js";
11
12
  import {
12
13
  routeParamsValidator,
13
14
  workspaceSlugParamsValidator
14
15
  } from "../common/validators/routeParamsValidator.js";
15
16
  import { resolveDefaultWorkspaceRouteSurfaceIdFromAppConfig } from "../support/workspaceActionSurfaces.js";
16
17
 
17
- function resolveWorkspaceAggregateRecordId(record = {}, context = {}) {
18
- const workspaceId = record?.workspace?.id;
19
- if (workspaceId != null && String(workspaceId).trim()) {
20
- return workspaceId;
21
- }
22
-
23
- const workspaceSlug = context?.request?.params?.workspaceSlug;
24
- if (workspaceSlug != null && String(workspaceSlug).trim()) {
25
- return workspaceSlug;
26
- }
27
-
28
- throw new Error("Workspace JSON:API response requires workspace id.");
29
- }
30
-
31
- function registerWorkspaceMembersRoutes(router, { config = {}, workspaceInvitationsEnabled = false } = {}) {
32
- if (!router || typeof router.register !== "function") {
33
- throw new TypeError("registerWorkspaceMembersRoutes requires router.register().");
34
- }
18
+ /**
19
+ * @param {import("@jskit-ai/kernel/server/types").RuntimeRouter} router
20
+ * @param {{ config: import("@jskit-ai/kernel/server/types").RuntimeConfig, workspaceInvitationsEnabled: boolean }} options
21
+ */
22
+ function registerWorkspaceMembersRoutes(router, { config, workspaceInvitationsEnabled }) {
35
23
  const workspaceRouteSurfaceId = resolveDefaultWorkspaceRouteSurfaceIdFromAppConfig(config);
36
24
 
37
25
  router.register(
@@ -51,7 +39,7 @@ function registerWorkspaceMembersRoutes(router, { config = {}, workspaceInvitati
51
39
  ...WORKSPACE_ROLE_CATALOG_TRANSPORT,
52
40
  output: workspaceMembersResource.operations.rolesList.output,
53
41
  outputKind: "record",
54
- getRecordId: resolveWorkspaceAggregateRecordId
42
+ getRecordId: resolveWorkspaceRecordId
55
43
  })
56
44
  },
57
45
  async function (request, reply) {
@@ -82,7 +70,7 @@ function registerWorkspaceMembersRoutes(router, { config = {}, workspaceInvitati
82
70
  ...WORKSPACE_MEMBERS_TRANSPORT,
83
71
  output: workspaceMembersResource.operations.membersList.output,
84
72
  outputKind: "record",
85
- getRecordId: resolveWorkspaceAggregateRecordId
73
+ getRecordId: resolveWorkspaceRecordId
86
74
  })
87
75
  },
88
76
  async function (request, reply) {
@@ -114,7 +102,7 @@ function registerWorkspaceMembersRoutes(router, { config = {}, workspaceInvitati
114
102
  body: workspaceMembersResource.operations.updateMemberRole.body,
115
103
  output: workspaceMembersResource.operations.updateMemberRole.output,
116
104
  outputKind: "record",
117
- getRecordId: resolveWorkspaceAggregateRecordId,
105
+ getRecordId: resolveWorkspaceRecordId,
118
106
  includeValidation400: true
119
107
  })
120
108
  },
@@ -148,7 +136,7 @@ function registerWorkspaceMembersRoutes(router, { config = {}, workspaceInvitati
148
136
  ...WORKSPACE_MEMBERS_TRANSPORT,
149
137
  output: workspaceMembersResource.operations.removeMember.output,
150
138
  outputKind: "record",
151
- getRecordId: resolveWorkspaceAggregateRecordId
139
+ getRecordId: resolveWorkspaceRecordId
152
140
  })
153
141
  },
154
142
  async function (request, reply) {
@@ -181,7 +169,7 @@ function registerWorkspaceMembersRoutes(router, { config = {}, workspaceInvitati
181
169
  ...WORKSPACE_INVITES_TRANSPORT,
182
170
  output: workspaceMembersResource.operations.invitesList.output,
183
171
  outputKind: "record",
184
- getRecordId: resolveWorkspaceAggregateRecordId
172
+ getRecordId: resolveWorkspaceRecordId
185
173
  })
186
174
  },
187
175
  async function (request, reply) {
@@ -213,7 +201,7 @@ function registerWorkspaceMembersRoutes(router, { config = {}, workspaceInvitati
213
201
  body: workspaceMembersResource.operations.createInvite.body,
214
202
  output: workspaceMembersResource.operations.createInvite.output,
215
203
  outputKind: "record",
216
- getRecordId: resolveWorkspaceAggregateRecordId,
204
+ getRecordId: resolveWorkspaceRecordId,
217
205
  includeValidation400: true
218
206
  })
219
207
  },
@@ -247,7 +235,7 @@ function registerWorkspaceMembersRoutes(router, { config = {}, workspaceInvitati
247
235
  ...WORKSPACE_INVITES_TRANSPORT,
248
236
  output: workspaceMembersResource.operations.revokeInvite.output,
249
237
  outputKind: "record",
250
- getRecordId: resolveWorkspaceAggregateRecordId
238
+ getRecordId: resolveWorkspaceRecordId
251
239
  })
252
240
  },
253
241
  async function (request, reply) {