@jskit-ai/users-core 0.1.47 → 0.1.49

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 (104) hide show
  1. package/package.descriptor.mjs +9 -46
  2. package/package.json +8 -19
  3. package/src/server/UsersCoreServiceProvider.js +0 -4
  4. package/src/server/common/registerCommonRepositories.js +0 -5
  5. package/src/server/common/services/authProfileSyncService.js +28 -7
  6. package/src/server/common/support/realtimeServiceEvents.js +1 -59
  7. package/src/server/profileSyncLifecycleContributorRegistry.js +56 -0
  8. package/src/server/registerUsersBootstrap.js +1 -3
  9. package/src/server/registerUsersCore.js +2 -14
  10. package/src/server/usersBootstrapContributor.js +10 -85
  11. package/src/shared/index.js +2 -99
  12. package/src/shared/settings.js +1 -119
  13. package/templates/migrations/users_core_generic_initial.cjs +0 -16
  14. package/test/authProfileSyncService.test.js +19 -10
  15. package/test/registerServiceRealtimeEvents.test.js +0 -94
  16. package/test/registerUsersCore.test.js +6 -19
  17. package/test/repositoryContracts.test.js +1 -11
  18. package/test/resourcesCanonical.test.js +1 -19
  19. package/test/settingsFieldRegistriesSingleton.test.js +0 -10
  20. package/test/usersBootstrapContributor.test.js +20 -38
  21. package/test/usersRouteRequestInputValidator.test.js +2 -43
  22. package/test/usersRouteResources.test.js +2 -20
  23. package/test-support/registerDefaultSettingsFields.js +0 -1
  24. package/src/server/UsersWorkspacesServiceProvider.js +0 -44
  25. package/src/server/common/contributors/workspaceActionContextContributor.js +0 -88
  26. package/src/server/common/contributors/workspaceAuthPolicyContextResolver.js +0 -34
  27. package/src/server/common/contributors/workspaceRouteVisibilityResolver.js +0 -78
  28. package/src/server/common/formatters/workspaceFormatter.js +0 -53
  29. package/src/server/common/repositories/workspaceInvitesRepository.js +0 -208
  30. package/src/server/common/repositories/workspaceMembershipsRepository.js +0 -190
  31. package/src/server/common/repositories/workspacesRepository.js +0 -202
  32. package/src/server/common/services/workspaceContextService.js +0 -281
  33. package/src/server/common/support/workspaceRoutePaths.js +0 -17
  34. package/src/server/common/validators/routeParamsValidator.js +0 -62
  35. package/src/server/consoleSettings/bootConsoleSettingsRoutes.js +0 -63
  36. package/src/server/consoleSettings/consoleService.js +0 -36
  37. package/src/server/consoleSettings/consoleSettingsActions.js +0 -55
  38. package/src/server/consoleSettings/consoleSettingsRepository.js +0 -115
  39. package/src/server/consoleSettings/consoleSettingsService.js +0 -40
  40. package/src/server/consoleSettings/registerConsoleSettings.js +0 -56
  41. package/src/server/registerWorkspaceBootstrap.js +0 -27
  42. package/src/server/registerWorkspaceCore.js +0 -73
  43. package/src/server/registerWorkspaceRepositories.js +0 -26
  44. package/src/server/support/resolveWorkspace.js +0 -16
  45. package/src/server/support/workspaceActionSurfaces.js +0 -135
  46. package/src/server/support/workspaceInvitationsPolicy.js +0 -45
  47. package/src/server/support/workspaceRouteInput.js +0 -22
  48. package/src/server/workspaceBootstrapContributor.js +0 -211
  49. package/src/server/workspaceDirectory/bootWorkspaceDirectoryRoutes.js +0 -133
  50. package/src/server/workspaceDirectory/registerWorkspaceDirectory.js +0 -19
  51. package/src/server/workspaceDirectory/workspaceDirectoryActions.js +0 -133
  52. package/src/server/workspaceMembers/bootWorkspaceMembers.js +0 -236
  53. package/src/server/workspaceMembers/registerWorkspaceMembers.js +0 -108
  54. package/src/server/workspaceMembers/workspaceMembersActions.js +0 -186
  55. package/src/server/workspaceMembers/workspaceMembersService.js +0 -222
  56. package/src/server/workspacePendingInvitations/bootWorkspacePendingInvitations.js +0 -62
  57. package/src/server/workspacePendingInvitations/registerWorkspacePendingInvitations.js +0 -119
  58. package/src/server/workspacePendingInvitations/workspacePendingInvitationsActions.js +0 -74
  59. package/src/server/workspacePendingInvitations/workspacePendingInvitationsService.js +0 -138
  60. package/src/server/workspaceSettings/bootWorkspaceSettings.js +0 -76
  61. package/src/server/workspaceSettings/registerWorkspaceSettings.js +0 -62
  62. package/src/server/workspaceSettings/workspaceSettingsActions.js +0 -72
  63. package/src/server/workspaceSettings/workspaceSettingsRepository.js +0 -154
  64. package/src/server/workspaceSettings/workspaceSettingsService.js +0 -66
  65. package/src/shared/resources/consoleSettingsFields.js +0 -54
  66. package/src/shared/resources/consoleSettingsResource.js +0 -119
  67. package/src/shared/resources/workspaceMembersResource.js +0 -354
  68. package/src/shared/resources/workspacePendingInvitationsResource.js +0 -82
  69. package/src/shared/resources/workspaceResource.js +0 -176
  70. package/src/shared/resources/workspaceSettingsFields.js +0 -59
  71. package/src/shared/resources/workspaceSettingsResource.js +0 -169
  72. package/src/shared/roles.js +0 -161
  73. package/src/shared/support/usersApiPaths.js +0 -43
  74. package/src/shared/support/usersVisibility.js +0 -42
  75. package/src/shared/support/workspacePathModel.js +0 -145
  76. package/src/shared/tenancyMode.js +0 -35
  77. package/src/shared/tenancyProfile.js +0 -73
  78. package/templates/migrations/users_core_console_owner.cjs +0 -37
  79. package/templates/packages/main/src/shared/resources/consoleSettingsFields.js +0 -11
  80. package/test/consoleService.test.js +0 -57
  81. package/test/consoleSettingsService.test.js +0 -86
  82. package/test/registerWorkspaceDirectory.test.js +0 -31
  83. package/test/registerWorkspaceSettings.test.js +0 -40
  84. package/test/roles.test.js +0 -159
  85. package/test/tenancyProfile.test.js +0 -67
  86. package/test/usersApiPaths.test.js +0 -49
  87. package/test/usersRouteValidators.test.js +0 -49
  88. package/test/usersVisibility.test.js +0 -27
  89. package/test/workspaceActionContextContributor.test.js +0 -344
  90. package/test/workspaceActionSurfaces.test.js +0 -105
  91. package/test/workspaceAuthPolicyContextResolver.test.js +0 -119
  92. package/test/workspaceBootstrapContributor.test.js +0 -154
  93. package/test/workspaceInvitationsPolicy.test.js +0 -71
  94. package/test/workspaceInvitesRepository.test.js +0 -111
  95. package/test/workspaceMembersService.test.js +0 -398
  96. package/test/workspacePathModel.test.js +0 -93
  97. package/test/workspacePendingInvitationsResource.test.js +0 -38
  98. package/test/workspacePendingInvitationsService.test.js +0 -151
  99. package/test/workspaceRouteVisibilityResolver.test.js +0 -83
  100. package/test/workspaceService.test.js +0 -546
  101. package/test/workspaceSettingsActions.test.js +0 -52
  102. package/test/workspaceSettingsRepository.test.js +0 -202
  103. package/test/workspaceSettingsResource.test.js +0 -169
  104. package/test/workspaceSettingsService.test.js +0 -140
@@ -1,186 +0,0 @@
1
- import { resolveWorkspace } from "../support/resolveWorkspace.js";
2
- import { resolveActionUser } from "../common/support/resolveActionUser.js";
3
- import { workspaceMembersResource } from "../../shared/resources/workspaceMembersResource.js";
4
- import { workspaceSlugParamsValidator } from "../common/validators/routeParamsValidator.js";
5
-
6
- const workspaceMembersActions = Object.freeze([
7
- {
8
- id: "workspace.roles.list",
9
- version: 1,
10
- kind: "query",
11
- channels: ["api", "automation", "internal"],
12
- surfacesFrom: "workspace",
13
- permission: {
14
- require: "all",
15
- permissions: ["workspace.roles.view"]
16
- },
17
- inputValidator: workspaceSlugParamsValidator,
18
- outputValidator: workspaceMembersResource.operations.rolesList.outputValidator,
19
- idempotency: "none",
20
- audit: {
21
- actionName: "workspace.roles.list"
22
- },
23
- observability: {},
24
- async execute(_input, context, deps) {
25
- return deps.workspaceMembersService.listRoles({ context });
26
- }
27
- },
28
- {
29
- id: "workspace.members.list",
30
- version: 1,
31
- kind: "query",
32
- channels: ["api", "automation", "internal"],
33
- surfacesFrom: "workspace",
34
- permission: {
35
- require: "all",
36
- permissions: ["workspace.members.view"]
37
- },
38
- inputValidator: workspaceSlugParamsValidator,
39
- outputValidator: workspaceMembersResource.operations.membersList.outputValidator,
40
- idempotency: "none",
41
- audit: {
42
- actionName: "workspace.members.list"
43
- },
44
- observability: {},
45
- async execute(input, context, deps) {
46
- return deps.workspaceMembersService.listMembers(resolveWorkspace(context, input), {
47
- context
48
- });
49
- }
50
- },
51
- {
52
- id: "workspace.member.role.update",
53
- version: 1,
54
- kind: "command",
55
- channels: ["api", "automation", "internal"],
56
- surfacesFrom: "workspace",
57
- permission: {
58
- require: "all",
59
- permissions: ["workspace.members.manage"]
60
- },
61
- inputValidator: [workspaceSlugParamsValidator, workspaceMembersResource.operations.updateMemberRole.inputValidator],
62
- outputValidator: workspaceMembersResource.operations.updateMemberRole.outputValidator,
63
- idempotency: "optional",
64
- audit: {
65
- actionName: "workspace.member.role.update"
66
- },
67
- observability: {},
68
- async execute(input, context, deps) {
69
- return deps.workspaceMembersService.updateMemberRole(resolveWorkspace(context, input), {
70
- memberUserId: input.memberUserId,
71
- roleSid: input.roleSid
72
- }, {
73
- context
74
- });
75
- }
76
- },
77
- {
78
- id: "workspace.member.remove",
79
- version: 1,
80
- kind: "command",
81
- channels: ["api", "automation", "internal"],
82
- surfacesFrom: "workspace",
83
- permission: {
84
- require: "all",
85
- permissions: ["workspace.members.manage"]
86
- },
87
- inputValidator: [workspaceSlugParamsValidator, workspaceMembersResource.operations.removeMember.inputValidator],
88
- outputValidator: workspaceMembersResource.operations.removeMember.outputValidator,
89
- idempotency: "optional",
90
- audit: {
91
- actionName: "workspace.member.remove"
92
- },
93
- observability: {},
94
- async execute(input, context, deps) {
95
- return deps.workspaceMembersService.removeMember(resolveWorkspace(context, input), {
96
- memberUserId: input.memberUserId
97
- }, {
98
- context
99
- });
100
- }
101
- },
102
- {
103
- id: "workspace.invites.list",
104
- version: 1,
105
- kind: "query",
106
- channels: ["api", "automation", "internal"],
107
- surfacesFrom: "workspace",
108
- permission: {
109
- require: "all",
110
- permissions: ["workspace.members.view"]
111
- },
112
- inputValidator: workspaceSlugParamsValidator,
113
- outputValidator: workspaceMembersResource.operations.invitesList.outputValidator,
114
- idempotency: "none",
115
- audit: {
116
- actionName: "workspace.invites.list"
117
- },
118
- observability: {},
119
- async execute(input, context, deps) {
120
- return deps.workspaceMembersService.listInvites(resolveWorkspace(context, input), {
121
- context
122
- });
123
- }
124
- },
125
- {
126
- id: "workspace.invite.create",
127
- version: 1,
128
- kind: "command",
129
- channels: ["api", "assistant_tool", "automation", "internal"],
130
- surfacesFrom: "workspace",
131
- permission: {
132
- require: "all",
133
- permissions: ["workspace.members.invite"]
134
- },
135
- inputValidator: [workspaceSlugParamsValidator, workspaceMembersResource.operations.createInvite.bodyValidator],
136
- outputValidator: workspaceMembersResource.operations.createInvite.outputValidator,
137
- idempotency: "optional",
138
- audit: {
139
- actionName: "workspace.invite.create"
140
- },
141
- observability: {},
142
- extensions: {
143
- assistant: {
144
- description: "Invite a person to the workspace."
145
- }
146
- },
147
- async execute(input, context, deps) {
148
- return deps.workspaceMembersService.createInvite(
149
- resolveWorkspace(context, input),
150
- resolveActionUser(context, input),
151
- {
152
- email: input.email,
153
- roleSid: input.roleSid
154
- },
155
- {
156
- context
157
- }
158
- );
159
- }
160
- },
161
- {
162
- id: "workspace.invite.revoke",
163
- version: 1,
164
- kind: "command",
165
- channels: ["api", "automation", "internal"],
166
- surfacesFrom: "workspace",
167
- permission: {
168
- require: "all",
169
- permissions: ["workspace.invites.revoke"]
170
- },
171
- inputValidator: [workspaceSlugParamsValidator, workspaceMembersResource.operations.revokeInvite.inputValidator],
172
- outputValidator: workspaceMembersResource.operations.revokeInvite.outputValidator,
173
- idempotency: "optional",
174
- audit: {
175
- actionName: "workspace.invite.revoke"
176
- },
177
- observability: {},
178
- async execute(input, context, deps) {
179
- return deps.workspaceMembersService.revokeInvite(resolveWorkspace(context, input), input.inviteId, {
180
- context
181
- });
182
- }
183
- }
184
- ]);
185
-
186
- export { workspaceMembersActions };
@@ -1,222 +0,0 @@
1
- import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
2
- import { buildInviteToken, hashInviteToken } from "@jskit-ai/auth-core/server/inviteTokens";
3
- import { AppError } from "@jskit-ai/kernel/server/runtime/errors";
4
- import { OWNER_ROLE_ID, createWorkspaceRoleCatalog, cloneWorkspaceRoleCatalog } from "../../shared/roles.js";
5
-
6
- function createService({
7
- workspaceMembershipsRepository,
8
- workspaceInvitesRepository,
9
- inviteExpiresInMs,
10
- roleCatalog = null,
11
- workspaceInvitationsEnabled = true
12
- } = {}) {
13
- if (!workspaceMembershipsRepository || !workspaceInvitesRepository) {
14
- throw new Error("workspaceMembersService requires membership and invite repositories.");
15
- }
16
- const resolvedInviteExpiresInMs = Number(inviteExpiresInMs);
17
- if (!Number.isInteger(resolvedInviteExpiresInMs) || resolvedInviteExpiresInMs < 1) {
18
- throw new Error("workspaceMembersService requires inviteExpiresInMs.");
19
- }
20
-
21
- const resolvedRoleCatalog = roleCatalog && typeof roleCatalog === "object" ? roleCatalog : createWorkspaceRoleCatalog();
22
- const assignableRoleIds = Array.isArray(resolvedRoleCatalog.assignableRoleIds)
23
- ? [...resolvedRoleCatalog.assignableRoleIds]
24
- : [];
25
- const resolvedWorkspaceInvitationsEnabled = workspaceInvitationsEnabled === true;
26
-
27
- function ensureWorkspaceInvitationsEnabled() {
28
- if (resolvedWorkspaceInvitationsEnabled) {
29
- return;
30
- }
31
- throw new AppError(403, "Workspace invitations are disabled.");
32
- }
33
-
34
- function withRoleCatalog(payload = {}) {
35
- return {
36
- ...payload,
37
- roleCatalog: cloneWorkspaceRoleCatalog({
38
- ...resolvedRoleCatalog,
39
- assignableRoleIds
40
- })
41
- };
42
- }
43
-
44
- async function listRoles(options = {}) {
45
- return cloneWorkspaceRoleCatalog({
46
- ...resolvedRoleCatalog,
47
- assignableRoleIds
48
- });
49
- }
50
-
51
- async function listMembersPayload(workspace, options = {}) {
52
- const members = await workspaceMembershipsRepository.listActiveByWorkspaceId(workspace.id, options);
53
-
54
- return withRoleCatalog({
55
- workspace,
56
- members
57
- });
58
- }
59
-
60
- async function listMembers(workspace, options = {}) {
61
- return listMembersPayload(workspace, options);
62
- }
63
-
64
- async function updateMemberRole(workspace, payload = {}, options = {}) {
65
- const memberUserId = normalizeRecordId(payload.memberUserId, { fallback: null });
66
- const roleSid = payload.roleSid;
67
- if (!memberUserId) {
68
- throw new AppError(400, "Validation failed.");
69
- }
70
- if (!assignableRoleIds.includes(roleSid)) {
71
- throw new AppError(400, "Validation failed.", {
72
- details: {
73
- fieldErrors: {
74
- roleSid: "Role is not assignable."
75
- }
76
- }
77
- });
78
- }
79
-
80
- const existingMembership = await workspaceMembershipsRepository.findByWorkspaceIdAndUserId(workspace.id, memberUserId, options);
81
- if (!existingMembership || existingMembership.status !== "active") {
82
- throw new AppError(404, "Member not found.");
83
- }
84
- if (memberUserId === normalizeRecordId(workspace.ownerUserId, { fallback: null }) || existingMembership.roleSid === OWNER_ROLE_ID) {
85
- throw new AppError(409, "Cannot change workspace owner role.");
86
- }
87
-
88
- await workspaceMembershipsRepository.upsertMembership(
89
- workspace.id,
90
- memberUserId,
91
- {
92
- roleSid,
93
- status: "active"
94
- },
95
- options
96
- );
97
-
98
- return listMembersPayload(workspace, options);
99
- }
100
-
101
- async function removeMember(workspace, payload = {}, options = {}) {
102
- const memberUserId = normalizeRecordId(payload.memberUserId, { fallback: null });
103
- if (!memberUserId) {
104
- throw new AppError(400, "Validation failed.");
105
- }
106
-
107
- const existingMembership = await workspaceMembershipsRepository.findByWorkspaceIdAndUserId(workspace.id, memberUserId, options);
108
- if (!existingMembership || existingMembership.status !== "active") {
109
- throw new AppError(404, "Member not found.");
110
- }
111
- if (memberUserId === normalizeRecordId(workspace.ownerUserId, { fallback: null }) || existingMembership.roleSid === OWNER_ROLE_ID) {
112
- throw new AppError(409, "Cannot remove workspace owner.");
113
- }
114
-
115
- await workspaceMembershipsRepository.upsertMembership(
116
- workspace.id,
117
- memberUserId,
118
- {
119
- roleSid: existingMembership.roleSid,
120
- status: "revoked"
121
- },
122
- options
123
- );
124
-
125
- return listMembersPayload(workspace, options);
126
- }
127
-
128
- async function listInvitesPayload(workspace, options = {}) {
129
- ensureWorkspaceInvitationsEnabled();
130
- const invites = await workspaceInvitesRepository.listPendingByWorkspaceIdWithWorkspace(workspace.id, options);
131
-
132
- return withRoleCatalog({
133
- workspace,
134
- invites
135
- });
136
- }
137
-
138
- async function listInvites(workspace, options = {}) {
139
- return listInvitesPayload(workspace, options);
140
- }
141
-
142
- async function createInvite(workspace, user, payload = {}, options = {}) {
143
- const email = payload.email;
144
- const roleSid = payload.roleSid;
145
- if (!assignableRoleIds.includes(roleSid)) {
146
- throw new AppError(400, "Validation failed.", {
147
- details: {
148
- fieldErrors: {
149
- roleSid: "Role is not assignable."
150
- }
151
- }
152
- });
153
- }
154
-
155
- const token = buildInviteToken();
156
- const tokenHash = hashInviteToken(token);
157
- await workspaceInvitesRepository.expirePendingByWorkspaceIdAndEmail(workspace.id, email, options);
158
- const createdInvite = await workspaceInvitesRepository.insert(
159
- {
160
- workspaceId: workspace.id,
161
- email,
162
- roleSid,
163
- status: "pending",
164
- tokenHash,
165
- invitedByUserId: normalizeRecordId(user?.id, { fallback: null }),
166
- expiresAt: new Date(Date.now() + resolvedInviteExpiresInMs).toISOString()
167
- },
168
- options
169
- );
170
- const createdInviteId = normalizeRecordId(createdInvite?.id, { fallback: null });
171
- if (!createdInviteId) {
172
- throw new Error("workspaceMembersService.createInvite expected repository to return created invite id.");
173
- }
174
-
175
- const response = await listInvitesPayload(workspace, options);
176
- return {
177
- ...response,
178
- inviteTokenPreview: token,
179
- createdInviteId
180
- };
181
- }
182
-
183
- async function revokeInvite(workspace, inviteId, options = {}) {
184
- const normalizedInviteId = normalizeRecordId(inviteId, { fallback: null });
185
- if (!normalizedInviteId) {
186
- throw new AppError(400, "Validation failed.");
187
- }
188
-
189
- const invite = await workspaceInvitesRepository.findPendingByIdForWorkspace(
190
- normalizedInviteId,
191
- workspace.id,
192
- options
193
- );
194
- if (!invite) {
195
- throw new AppError(404, "Invite not found.");
196
- }
197
-
198
- await workspaceInvitesRepository.revokeById(normalizedInviteId, options);
199
- const revokedInviteId = normalizeRecordId(invite?.id, { fallback: null });
200
- if (!revokedInviteId) {
201
- throw new Error("workspaceMembersService.revokeInvite expected repository to return pending invite id.");
202
- }
203
-
204
- const response = await listInvitesPayload(workspace, options);
205
- return {
206
- ...response,
207
- revokedInviteId
208
- };
209
- }
210
-
211
- return Object.freeze({
212
- listRoles,
213
- listMembers,
214
- updateMemberRole,
215
- removeMember,
216
- listInvites,
217
- createInvite,
218
- revokeInvite
219
- });
220
- }
221
-
222
- export { createService };
@@ -1,62 +0,0 @@
1
- import { withStandardErrorResponses } from "@jskit-ai/http-runtime/shared/validators/errorResponses";
2
- import { workspaceMembersResource } from "../../shared/resources/workspaceMembersResource.js";
3
- import { workspacePendingInvitationsResource } from "../../shared/resources/workspacePendingInvitationsResource.js";
4
-
5
- function bootWorkspacePendingInvitations(app) {
6
- if (!app || typeof app.make !== "function") {
7
- throw new Error("bootWorkspacePendingInvitations requires application make().");
8
- }
9
-
10
- const router = app.make("jskit.http.router");
11
-
12
- router.register(
13
- "GET",
14
- "/api/workspace/invitations/pending",
15
- {
16
- auth: "required",
17
- meta: {
18
- tags: ["workspace"],
19
- summary: "List pending workspace invitations for authenticated user"
20
- },
21
- responseValidators: withStandardErrorResponses({
22
- 200: workspacePendingInvitationsResource.operations.list.outputValidator
23
- })
24
- },
25
- async function (request, reply) {
26
- const response = await request.executeAction({
27
- actionId: "workspace.invitations.pending.list"
28
- });
29
- reply.code(200).send(response);
30
- }
31
- );
32
-
33
- router.register(
34
- "POST",
35
- "/api/workspace/invitations/redeem",
36
- {
37
- auth: "required",
38
- meta: {
39
- tags: ["workspace"],
40
- summary: "Accept or refuse a workspace invitation using an invite token"
41
- },
42
- bodyValidator: workspaceMembersResource.operations.redeemInvite.bodyValidator,
43
- responseValidators: withStandardErrorResponses(
44
- {
45
- 200: workspaceMembersResource.operations.redeemInvite.outputValidator
46
- },
47
- { includeValidation400: true }
48
- )
49
- },
50
- async function (request, reply) {
51
- const response = await request.executeAction({
52
- actionId: "workspace.invite.redeem",
53
- input: {
54
- payload: request.input.body
55
- }
56
- });
57
- reply.code(200).send(response);
58
- }
59
- );
60
- }
61
-
62
- export { bootWorkspacePendingInvitations };
@@ -1,119 +0,0 @@
1
- import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
2
- import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
3
- import { createService } from "./workspacePendingInvitationsService.js";
4
- import { workspacePendingInvitationsActions } from "./workspacePendingInvitationsActions.js";
5
- import { deepFreeze } from "../common/support/deepFreeze.js";
6
-
7
- function workspaceAudienceFromEntityId({ event } = {}) {
8
- const workspaceId = normalizeRecordId(event?.entityId, { fallback: null });
9
- if (!workspaceId) {
10
- return "none";
11
- }
12
- return {
13
- workspaceId
14
- };
15
- }
16
-
17
- function actorUserEntityId({ options } = {}) {
18
- return normalizeRecordId(options?.context?.actor?.id, { fallback: "" });
19
- }
20
-
21
- function createActorUserEvent({ source, entity, realtimeEvent }) {
22
- return {
23
- type: "entity.changed",
24
- source,
25
- entity,
26
- operation: "updated",
27
- entityId: actorUserEntityId,
28
- realtime: {
29
- event: realtimeEvent,
30
- audience: "actor_user"
31
- }
32
- };
33
- }
34
-
35
- function createWorkspaceAudienceEvent({ entity, realtimeEvent }) {
36
- return {
37
- type: "entity.changed",
38
- source: "workspace",
39
- entity,
40
- operation: "updated",
41
- entityId: ({ result }) => normalizeRecordId(result?.workspaceId, { fallback: "" }),
42
- realtime: {
43
- event: realtimeEvent,
44
- audience: workspaceAudienceFromEntityId
45
- }
46
- };
47
- }
48
-
49
- function createInviteDecisionEvents({ includeDirectoryAndMembers = false } = {}) {
50
- const events = [
51
- createActorUserEvent({
52
- source: "workspace",
53
- entity: "invitation",
54
- realtimeEvent: "workspace.invitations.pending.changed"
55
- }),
56
- createActorUserEvent({
57
- source: "users",
58
- entity: "bootstrap",
59
- realtimeEvent: "users.bootstrap.changed"
60
- })
61
- ];
62
-
63
- if (includeDirectoryAndMembers) {
64
- events.push(
65
- createActorUserEvent({
66
- source: "workspace",
67
- entity: "directory",
68
- realtimeEvent: "workspaces.changed"
69
- }),
70
- createWorkspaceAudienceEvent({
71
- entity: "member",
72
- realtimeEvent: "workspace.members.changed"
73
- })
74
- );
75
- }
76
-
77
- events.push(
78
- createWorkspaceAudienceEvent({
79
- entity: "invite",
80
- realtimeEvent: "workspace.invites.changed"
81
- })
82
- );
83
-
84
- return events;
85
- }
86
-
87
- function registerWorkspacePendingInvitations(app) {
88
- if (!app || typeof app.singleton !== "function" || typeof app.service !== "function" || typeof app.actions !== "function") {
89
- throw new Error("registerWorkspacePendingInvitations requires application singleton()/service()/actions().");
90
- }
91
-
92
- app.service(
93
- "users.workspace.pending-invitations.service",
94
- (scope) =>
95
- createService({
96
- workspaceInvitesRepository: scope.make("workspaceInvitesRepository"),
97
- workspaceMembershipsRepository: scope.make("workspaceMembershipsRepository")
98
- }),
99
- {
100
- events: deepFreeze({
101
- acceptInviteByToken: createInviteDecisionEvents({
102
- includeDirectoryAndMembers: true
103
- }),
104
- refuseInviteByToken: createInviteDecisionEvents()
105
- })
106
- }
107
- );
108
-
109
- app.actions(
110
- withActionDefaults(workspacePendingInvitationsActions, {
111
- domain: "workspace",
112
- dependencies: {
113
- workspacePendingInvitationsService: "users.workspace.pending-invitations.service"
114
- }
115
- })
116
- );
117
- }
118
-
119
- export { registerWorkspacePendingInvitations };
@@ -1,74 +0,0 @@
1
- import {
2
- EMPTY_INPUT_VALIDATOR
3
- } from "@jskit-ai/kernel/shared/actions/actionContributorHelpers";
4
- import { workspaceMembersResource } from "../../shared/resources/workspaceMembersResource.js";
5
- import { workspacePendingInvitationsResource } from "../../shared/resources/workspacePendingInvitationsResource.js";
6
- import { resolveActionUser } from "../common/support/resolveActionUser.js";
7
-
8
- const workspacePendingInvitationsActions = Object.freeze([
9
- {
10
- id: "workspace.invitations.pending.list",
11
- version: 1,
12
- kind: "query",
13
- channels: ["api", "automation", "internal"],
14
- surfacesFrom: "enabled",
15
- permission: {
16
- require: "authenticated"
17
- },
18
- inputValidator: EMPTY_INPUT_VALIDATOR,
19
- outputValidator: workspacePendingInvitationsResource.operations.list.outputValidator,
20
- idempotency: "none",
21
- audit: {
22
- actionName: "workspace.invitations.pending.list"
23
- },
24
- observability: {},
25
- async execute(input, context, deps) {
26
- return {
27
- pendingInvites: await deps.workspacePendingInvitationsService.listPendingInvitesForUser(resolveActionUser(context, input), {
28
- context
29
- })
30
- };
31
- }
32
- },
33
- {
34
- id: "workspace.invite.redeem",
35
- version: 1,
36
- kind: "command",
37
- channels: ["api", "automation", "internal"],
38
- surfacesFrom: "enabled",
39
- permission: {
40
- require: "authenticated"
41
- },
42
- inputValidator: {
43
- payload: workspaceMembersResource.operations.redeemInvite.bodyValidator
44
- },
45
- outputValidator: workspaceMembersResource.operations.redeemInvite.outputValidator,
46
- idempotency: "optional",
47
- audit: {
48
- actionName: "workspace.invite.redeem"
49
- },
50
- observability: {},
51
- async execute(input, context, deps) {
52
- const payload = input.payload || {};
53
- const user = resolveActionUser(context, input);
54
-
55
- if (payload.decision === "accept") {
56
- return deps.workspacePendingInvitationsService.acceptInviteByToken({
57
- user,
58
- token: payload.token
59
- }, {
60
- context
61
- });
62
- }
63
-
64
- return deps.workspacePendingInvitationsService.refuseInviteByToken({
65
- user,
66
- token: payload.token
67
- }, {
68
- context
69
- });
70
- }
71
- }
72
- ]);
73
-
74
- export { workspacePendingInvitationsActions };