@jskit-ai/workspaces-core 0.1.137 → 0.1.138

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 (49) hide show
  1. package/package.json +38 -238
  2. package/patterns/workspace-server/PATTERN.md +60 -0
  3. package/src/server/WorkspacesFeature.js +226 -0
  4. package/src/server/WorkspacesIntegrationsProvider.js +74 -0
  5. package/src/server/common/contributors/workspaceActionContextContributor.js +18 -6
  6. package/src/server/common/contributors/workspaceAuthPolicyContextResolver.js +25 -4
  7. package/src/server/common/contributors/workspaceRouteVisibilityResolver.js +22 -4
  8. package/src/server/common/services/workspaceContextService.js +7 -2
  9. package/src/server/common/support/realtimeServiceEvents.js +102 -61
  10. package/src/server/support/workspaceActionSurfaces.js +18 -13
  11. package/src/server/workspaceDirectory/bootWorkspaceDirectoryRoutes.js +5 -11
  12. package/src/server/workspaceDirectory/workspaceDirectoryActions.js +24 -14
  13. package/src/server/workspaceMembers/bootWorkspaceMembers.js +5 -12
  14. package/src/server/workspaceMembers/workspaceMembersActions.js +63 -23
  15. package/src/server/workspacePendingInvitations/bootWorkspacePendingInvitations.js +4 -6
  16. package/src/server/workspacePendingInvitations/workspacePendingInvitationsActions.js +31 -12
  17. package/src/server/workspaceSettings/bootWorkspaceSettings.js +5 -8
  18. package/src/server/workspaceSettings/workspaceSettingsActions.js +26 -8
  19. package/src/server/workspaceSettings/workspaceSettingsRepository.js +4 -4
  20. package/test/exportsContract.test.js +2 -1
  21. package/test/featureRuntime.test.js +116 -0
  22. package/test/packageMetadata.test.js +20 -54
  23. package/test/workspaceActionContextContributor.test.js +49 -8
  24. package/test/workspaceActionSurfaces.test.js +36 -1
  25. package/test/workspaceAuthPolicyContextResolver.test.js +61 -0
  26. package/test/workspaceRouteVisibilityResolver.test.js +31 -0
  27. package/test/workspaceService.test.js +62 -0
  28. package/test/workspaceSettingsActions.test.js +102 -16
  29. package/test/workspacesRouteRequestInputValidator.test.js +14 -65
  30. package/src/server/WorkspacesCoreServiceProvider.js +0 -64
  31. package/src/server/registerWorkspaceBootstrap.js +0 -27
  32. package/src/server/registerWorkspaceCore.js +0 -101
  33. package/src/server/registerWorkspaceRepositories.js +0 -30
  34. package/src/server/support/workspaceServerScopeSupport.js +0 -18
  35. package/src/server/workspaceDirectory/registerWorkspaceDirectory.js +0 -19
  36. package/src/server/workspaceMembers/registerWorkspaceMembers.js +0 -137
  37. package/src/server/workspacePendingInvitations/registerWorkspacePendingInvitations.js +0 -130
  38. package/src/server/workspaceSettings/registerWorkspaceSettings.js +0 -66
  39. package/test/registerServiceRealtimeEvents.test.js +0 -116
  40. package/test/registerWorkspaceBootstrap.test.js +0 -86
  41. package/test/registerWorkspaceDirectory.test.js +0 -31
  42. package/test/registerWorkspaceMembers.test.js +0 -140
  43. package/test/registerWorkspaceSettings.test.js +0 -40
  44. package/test/workspaceServerScopeSupport.test.js +0 -131
  45. /package/{templates/migrations → migrations}/workspaces_core_initial.cjs +0 -0
  46. /package/{templates/migrations → migrations}/workspaces_core_workspace_settings_single_name_source.cjs +0 -0
  47. /package/{templates/migrations → migrations}/workspaces_core_workspaces_drop_color.cjs +0 -0
  48. /package/{templates → patterns/workspace-server/example}/config/roles.js +0 -0
  49. /package/{templates → patterns/workspace-server/example}/packages/main/src/server/email/workspaceInviteEmail.js +0 -0
@@ -1,64 +0,0 @@
1
- import {
2
- INTERNAL_JSON_REST_API,
3
- addResourceIfMissing,
4
- createJsonRestResourceScopeOptions
5
- } from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
6
- import { toDatabaseDateTimeUtc } from "@jskit-ai/database-runtime/shared";
7
- import { bootWorkspaceDirectoryRoutes } from "./workspaceDirectory/bootWorkspaceDirectoryRoutes.js";
8
- import { registerWorkspaceDirectory } from "./workspaceDirectory/registerWorkspaceDirectory.js";
9
- import {
10
- registerWorkspacePendingInvitations
11
- } from "./workspacePendingInvitations/registerWorkspacePendingInvitations.js";
12
- import { bootWorkspacePendingInvitations } from "./workspacePendingInvitations/bootWorkspacePendingInvitations.js";
13
- import { registerWorkspaceMembers } from "./workspaceMembers/registerWorkspaceMembers.js";
14
- import { bootWorkspaceMembers } from "./workspaceMembers/bootWorkspaceMembers.js";
15
- import { registerWorkspaceSettings } from "./workspaceSettings/registerWorkspaceSettings.js";
16
- import { bootWorkspaceSettings } from "./workspaceSettings/bootWorkspaceSettings.js";
17
- import { registerWorkspaceRepositories } from "./registerWorkspaceRepositories.js";
18
- import { registerWorkspaceCore } from "./registerWorkspaceCore.js";
19
- import { registerWorkspaceBootstrap } from "./registerWorkspaceBootstrap.js";
20
- import { workspaceResource } from "../shared/resources/workspaceResource.js";
21
- import { workspaceMembershipsResource } from "../shared/resources/workspaceMembershipsResource.js";
22
- import { workspaceInvitesResource } from "../shared/resources/workspaceInvitesResource.js";
23
- import { workspaceSettingsResource } from "../shared/resources/workspaceSettingsResource.js";
24
-
25
- class WorkspacesCoreServiceProvider {
26
- static id = "workspaces.core";
27
-
28
- static startsAfter = ["runtime.actions", "users.core"];
29
-
30
- async register(app) {
31
- registerWorkspaceRepositories(app);
32
- registerWorkspaceCore(app);
33
- registerWorkspaceBootstrap(app);
34
- registerWorkspaceDirectory(app);
35
- registerWorkspaceMembers(app);
36
- registerWorkspaceSettings(app);
37
- registerWorkspacePendingInvitations(app);
38
- }
39
-
40
- async boot(app) {
41
- const api = app.make(INTERNAL_JSON_REST_API);
42
- const scopeOptions = {
43
- writeSerializers: {
44
- "datetime-utc": toDatabaseDateTimeUtc
45
- }
46
- };
47
- await addResourceIfMissing(api, "workspaces", createJsonRestResourceScopeOptions(workspaceResource, scopeOptions));
48
- await addResourceIfMissing(api, "workspaceMemberships", createJsonRestResourceScopeOptions(workspaceMembershipsResource, scopeOptions));
49
- await addResourceIfMissing(api, "workspaceInvites", createJsonRestResourceScopeOptions(workspaceInvitesResource, scopeOptions));
50
- await addResourceIfMissing(api, "workspaceSettings", createJsonRestResourceScopeOptions(workspaceSettingsResource, scopeOptions));
51
- if (app.make("workspaces.enabled") !== true) {
52
- return;
53
- }
54
-
55
- bootWorkspaceDirectoryRoutes(app);
56
- if (app.make("workspaces.invitations.enabled") === true) {
57
- bootWorkspacePendingInvitations(app);
58
- }
59
- bootWorkspaceSettings(app);
60
- bootWorkspaceMembers(app);
61
- }
62
- }
63
-
64
- export { WorkspacesCoreServiceProvider };
@@ -1,27 +0,0 @@
1
- import { registerBootstrapPayloadContributor } from "@jskit-ai/kernel/server/runtime";
2
- import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
3
- import { createWorkspaceBootstrapContributor } from "./workspaceBootstrapContributor.js";
4
-
5
-
6
- function registerWorkspaceBootstrap(app) {
7
- if (!app || typeof app.singleton !== "function") {
8
- throw new Error("registerWorkspaceBootstrap requires application singleton().");
9
- }
10
-
11
- registerBootstrapPayloadContributor(app, "workspaces.core.bootstrap.payloadContributor", (scope) => {
12
- const workspaceInvitationsEnabled = scope.make("workspaces.invitations.enabled");
13
-
14
- return createWorkspaceBootstrapContributor({
15
- workspaceService: scope.make("workspaces.service"),
16
- workspacePendingInvitationsService: workspaceInvitationsEnabled
17
- ? scope.make("workspaces.pending-invitations.service")
18
- : null,
19
- workspaceInvitationsEnabled,
20
- userProfilesRepository: scope.make("internal.repository.user-profiles"),
21
- appConfig: resolveAppConfig(scope),
22
- tenancyProfile: scope.make("workspaces.tenancy.profile")
23
- });
24
- });
25
- }
26
-
27
- export { registerWorkspaceBootstrap };
@@ -1,101 +0,0 @@
1
- import {
2
- registerActionContextContributor
3
- } from "@jskit-ai/kernel/server/actions";
4
- import { registerRouteVisibilityResolver } from "@jskit-ai/kernel/server/http";
5
- import { registerAuthPolicyContextResolver } from "@jskit-ai/auth-core/server/authPolicyContextResolverRegistry";
6
- import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
7
- import { registerProfileSyncLifecycleContributor } from "@jskit-ai/users-core/server/profileSyncLifecycleContributorRegistry";
8
- import { createService as createWorkspaceService } from "./common/services/workspaceContextService.js";
9
- import { createWorkspaceActionContextContributor } from "./common/contributors/workspaceActionContextContributor.js";
10
- import { createWorkspaceRouteVisibilityResolver } from "./common/contributors/workspaceRouteVisibilityResolver.js";
11
- import { createWorkspaceAuthPolicyContextResolver } from "./common/contributors/workspaceAuthPolicyContextResolver.js";
12
- import { TENANCY_MODE_WORKSPACES, resolveTenancyProfile } from "../shared/tenancyProfile.js";
13
- import { resolveWorkspaceInvitationsPolicy } from "./support/workspaceInvitationsPolicy.js";
14
- import {
15
- registerWorkspaceActionSurfaceSources,
16
- resolveWorkspaceSurfaceIdsFromAppConfig
17
- } from "./support/workspaceActionSurfaces.js";
18
- import { createWorkspaceServerScopeSupport } from "./support/workspaceServerScopeSupport.js";
19
-
20
-
21
- function registerWorkspaceCore(app) {
22
- if (!app || typeof app.singleton !== "function") {
23
- throw new Error("registerWorkspaceCore requires application singleton().");
24
- }
25
-
26
- registerWorkspaceActionSurfaceSources(app);
27
-
28
- app.singleton("workspaces.tenancy.profile", (scope) => {
29
- const appConfig = resolveAppConfig(scope);
30
- return resolveTenancyProfile(appConfig);
31
- });
32
-
33
- app.singleton("workspaces.service", (scope) => {
34
- const appConfig = resolveAppConfig(scope);
35
- return createWorkspaceService({
36
- appConfig,
37
- workspacesRepository: scope.make("internal.repository.workspaces"),
38
- workspaceMembershipsRepository: scope.make("internal.repository.workspace-memberships"),
39
- workspaceSettingsRepository: scope.make("internal.repository.workspace-settings")
40
- });
41
- });
42
- app.singleton("workspaces.enabled", (scope) => {
43
- return scope.make("workspaces.tenancy.profile").workspace.enabled === true;
44
- });
45
-
46
- app.singleton("workspaces.self-create.enabled", (scope) => {
47
- return scope.make("workspaces.tenancy.profile").workspace.allowSelfCreate === true;
48
- });
49
-
50
- app.singleton("workspaces.tenancy.enabled", (scope) => {
51
- return scope.make("workspaces.tenancy.profile").mode === TENANCY_MODE_WORKSPACES;
52
- });
53
-
54
- app.singleton("workspaces.invitations.enabled", (scope) => {
55
- const appConfig = resolveAppConfig(scope);
56
- const tenancyProfile = scope.make("workspaces.tenancy.profile");
57
- return resolveWorkspaceInvitationsPolicy({
58
- appConfig,
59
- tenancyProfile
60
- }).enabled;
61
- });
62
- app.singleton("workspaces.server.scope-support", () => createWorkspaceServerScopeSupport());
63
-
64
- registerProfileSyncLifecycleContributor(app, "workspaces.core.profileSyncLifecycleContributor", (scope) => {
65
- const workspaceService = scope.make("workspaces.service");
66
-
67
- return Object.freeze({
68
- contributorId: "workspaces.core.profileSync",
69
- order: 100,
70
- async afterIdentityProfileSynced({ profile, options } = {}) {
71
- if (!profile || typeof workspaceService?.ensureProvisionedWorkspaceForAuthenticatedUser !== "function") {
72
- return;
73
- }
74
-
75
- await workspaceService.ensureProvisionedWorkspaceForAuthenticatedUser(profile, options);
76
- }
77
- });
78
- });
79
-
80
- registerActionContextContributor(app, "users.core.workspace.actionContextContributor", (scope) => {
81
- const appConfig = resolveAppConfig(scope);
82
- return createWorkspaceActionContextContributor({
83
- workspaceService: scope.make("workspaces.service"),
84
- workspaceSurfaceIds: resolveWorkspaceSurfaceIdsFromAppConfig(appConfig)
85
- });
86
- });
87
-
88
- registerAuthPolicyContextResolver(app, "workspaces.core.authPolicyContextResolver", (scope) =>
89
- createWorkspaceAuthPolicyContextResolver({
90
- workspaceService: scope.make("workspaces.service")
91
- })
92
- );
93
-
94
- registerRouteVisibilityResolver(app, "users.core.workspace.routeVisibilityResolver", (scope) =>
95
- createWorkspaceRouteVisibilityResolver({
96
- workspaceService: scope.make("workspaces.service")
97
- })
98
- );
99
- }
100
-
101
- export { registerWorkspaceCore };
@@ -1,30 +0,0 @@
1
- import { INTERNAL_JSON_REST_API } from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
2
- import { createRepository as createWorkspacesRepository } from "./common/repositories/workspacesRepository.js";
3
- import { createRepository as createWorkspaceMembershipsRepository } from "./common/repositories/workspaceMembershipsRepository.js";
4
- import { createRepository as createWorkspaceInvitesRepository } from "./common/repositories/workspaceInvitesRepository.js";
5
-
6
- function registerWorkspaceRepositories(app) {
7
- if (!app || typeof app.singleton !== "function") {
8
- throw new Error("registerWorkspaceRepositories requires application singleton().");
9
- }
10
-
11
- app.singleton("internal.repository.workspaces", (scope) => {
12
- const api = scope.make(INTERNAL_JSON_REST_API);
13
- const knex = scope.make("jskit.database.knex");
14
- return createWorkspacesRepository({ api, knex });
15
- });
16
-
17
- app.singleton("internal.repository.workspace-memberships", (scope) => {
18
- const api = scope.make(INTERNAL_JSON_REST_API);
19
- const knex = scope.make("jskit.database.knex");
20
- return createWorkspaceMembershipsRepository({ api, knex });
21
- });
22
-
23
- app.singleton("internal.repository.workspace-invites", (scope) => {
24
- const api = scope.make(INTERNAL_JSON_REST_API);
25
- const knex = scope.make("jskit.database.knex");
26
- return createWorkspaceInvitesRepository({ api, knex });
27
- });
28
- }
29
-
30
- export { registerWorkspaceRepositories };
@@ -1,18 +0,0 @@
1
- import { workspaceSlugParamsValidator } from "../common/validators/routeParamsValidator.js";
2
- import { buildWorkspaceInputFromRouteParams } from "./workspaceRouteInput.js";
3
- import { resolveWorkspace } from "./resolveWorkspace.js";
4
-
5
- function createWorkspaceServerScopeSupport() {
6
- return Object.freeze({
7
- available: true,
8
- params: workspaceSlugParamsValidator,
9
- buildInputFromRouteParams(params = {}) {
10
- return buildWorkspaceInputFromRouteParams(params);
11
- },
12
- resolveWorkspace(context = {}, input = {}) {
13
- return resolveWorkspace(context, input);
14
- }
15
- });
16
- }
17
-
18
- export { createWorkspaceServerScopeSupport };
@@ -1,19 +0,0 @@
1
- import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
2
- import { workspaceDirectoryActions } from "./workspaceDirectoryActions.js";
3
-
4
- function registerWorkspaceDirectory(app) {
5
- if (!app || typeof app.singleton !== "function" || typeof app.actions !== "function") {
6
- throw new Error("registerWorkspaceDirectory requires application singleton()/actions().");
7
- }
8
-
9
- app.actions(
10
- withActionDefaults(workspaceDirectoryActions, {
11
- domain: "workspace",
12
- dependencies: {
13
- workspaceService: "workspaces.service"
14
- }
15
- })
16
- );
17
- }
18
-
19
- export { registerWorkspaceDirectory };
@@ -1,137 +0,0 @@
1
- import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
2
- import { normalizeDbRecordId } from "@jskit-ai/database-runtime/shared";
3
- import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
4
- import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
5
- import { deepFreeze } from "../common/support/deepFreeze.js";
6
- import { createService as createWorkspaceMembersService } from "./workspaceMembersService.js";
7
- import { workspaceMembersActions } from "./workspaceMembersActions.js";
8
- import { createWorkspaceRoleCatalog } from "../../shared/roles.js";
9
- import { createWorkspaceEntityAndBootstrapEvents } from "../common/support/realtimeServiceEvents.js";
10
- import { renderDefaultWorkspaceInviteEmail } from "./defaultWorkspaceInviteEmail.js";
11
- import { createWorkspaceInviteUrlBuilder } from "./workspaceInviteUrls.js";
12
-
13
-
14
- function resolveWorkspaceMembersInviteExpiresInMs(appConfig = {}) {
15
- const inviteExpiresInMs = Number(appConfig?.workspaceMembers?.defaults?.inviteExpiresInMs);
16
- if (!Number.isInteger(inviteExpiresInMs) || inviteExpiresInMs < 1) {
17
- throw new TypeError("users.core requires appConfig.workspaceMembers.defaults.inviteExpiresInMs.");
18
- }
19
-
20
- return inviteExpiresInMs;
21
- }
22
-
23
- function resolveWorkspaceInviteEmailTemplate(scope, appConfig = {}) {
24
- if (typeof scope.has === "function" && scope.has("workspaces.invite.emailTemplate")) {
25
- return scope.make("workspaces.invite.emailTemplate");
26
- }
27
-
28
- const configuredTemplate =
29
- appConfig.workspaceInviteEmailTemplate ||
30
- appConfig.workspaceInviteEmailRenderer ||
31
- appConfig.workspaceInvitations?.emailTemplate ||
32
- appConfig.workspaceInvitations?.emailRenderer;
33
- if (typeof configuredTemplate === "function") {
34
- return configuredTemplate;
35
- }
36
-
37
- return renderDefaultWorkspaceInviteEmail;
38
- }
39
-
40
- const INVITE_RECIPIENT_BOOTSTRAP_AUDIENCE = Object.freeze({
41
- preset: "event_scope",
42
- async userQuery({ knex, event } = {}) {
43
- if (typeof knex !== "function") {
44
- return [];
45
- }
46
-
47
- const inviteId = normalizeRecordId(event?.entityId, { fallback: null });
48
- if (!inviteId) {
49
- return [];
50
- }
51
-
52
- const row = await knex("workspace_invites as wi")
53
- .join("users as up", "up.email", "wi.email")
54
- .where("wi.id", inviteId)
55
- .first("up.id as user_id");
56
-
57
- const userId = normalizeDbRecordId(row?.user_id, { fallback: null });
58
- if (!userId) {
59
- return [];
60
- }
61
-
62
- return [
63
- {
64
- userId
65
- }
66
- ];
67
- }
68
- });
69
-
70
- function registerWorkspaceMembers(app) {
71
- if (!app || typeof app.singleton !== "function" || typeof app.service !== "function" || typeof app.actions !== "function") {
72
- throw new Error("registerWorkspaceMembers requires application singleton()/service()/actions().");
73
- }
74
-
75
- app.service(
76
- "workspaces.members.service",
77
- (scope) => {
78
- const appConfig = resolveAppConfig(scope);
79
- const env = typeof scope.has === "function" && scope.has("jskit.env") ? scope.make("jskit.env") : process.env;
80
- return createWorkspaceMembersService({
81
- workspaceMembershipsRepository: scope.make("internal.repository.workspace-memberships"),
82
- workspaceInvitesRepository: scope.make("internal.repository.workspace-invites"),
83
- inviteExpiresInMs: resolveWorkspaceMembersInviteExpiresInMs(appConfig),
84
- roleCatalog: createWorkspaceRoleCatalog(appConfig),
85
- workspaceInvitationsEnabled: scope.make("workspaces.invitations.enabled") === true,
86
- inviteUrlBuilder: createWorkspaceInviteUrlBuilder({
87
- appConfig,
88
- env
89
- }),
90
- workspaceInviteMailer:
91
- typeof scope.has === "function" && scope.has("workspaces.invite.mailer")
92
- ? scope.make("workspaces.invite.mailer")
93
- : null,
94
- workspaceInviteEmailTemplate: resolveWorkspaceInviteEmailTemplate(scope, appConfig)
95
- });
96
- },
97
- {
98
- events: deepFreeze({
99
- updateMemberRole: createWorkspaceEntityAndBootstrapEvents({
100
- workspaceEntity: "member",
101
- workspaceOperation: "updated",
102
- workspaceRealtimeEvent: "workspace.members.changed"
103
- }),
104
- removeMember: createWorkspaceEntityAndBootstrapEvents({
105
- workspaceEntity: "member",
106
- workspaceOperation: "updated",
107
- workspaceRealtimeEvent: "workspace.members.changed"
108
- }),
109
- createInvite: createWorkspaceEntityAndBootstrapEvents({
110
- workspaceEntity: "invite",
111
- workspaceOperation: "created",
112
- workspaceRealtimeEvent: "workspace.invites.changed",
113
- bootstrapEntityId: ({ result }) => result?.createdInviteId,
114
- bootstrapAudience: INVITE_RECIPIENT_BOOTSTRAP_AUDIENCE
115
- }),
116
- revokeInvite: createWorkspaceEntityAndBootstrapEvents({
117
- workspaceEntity: "invite",
118
- workspaceOperation: "updated",
119
- workspaceRealtimeEvent: "workspace.invites.changed",
120
- bootstrapEntityId: ({ result }) => result?.revokedInviteId,
121
- bootstrapAudience: INVITE_RECIPIENT_BOOTSTRAP_AUDIENCE
122
- })
123
- })
124
- }
125
- );
126
-
127
- app.actions(
128
- withActionDefaults(workspaceMembersActions, {
129
- domain: "workspace",
130
- dependencies: {
131
- workspaceMembersService: "workspaces.members.service"
132
- }
133
- })
134
- );
135
- }
136
-
137
- export { registerWorkspaceMembers };
@@ -1,130 +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
- "workspaces.pending-invitations.service",
94
- (scope) =>
95
- createService({
96
- workspaceInvitesRepository: scope.make("internal.repository.workspace-invites"),
97
- workspaceMembershipsRepository: scope.make("internal.repository.workspace-memberships")
98
- }),
99
- {
100
- events: deepFreeze({
101
- acceptInviteByToken: createInviteDecisionEvents({
102
- includeDirectoryAndMembers: true
103
- }),
104
- refuseInviteByToken: createInviteDecisionEvents()
105
- })
106
- }
107
- );
108
-
109
- if (typeof app.has === "function" && !app.has("auth.invitationContextResolver")) {
110
- app.singleton("auth.invitationContextResolver", (scope) => {
111
- const service = scope.make("workspaces.pending-invitations.service");
112
- return {
113
- async resolveInvitationContext(invitation, options = {}) {
114
- return service.resolveInviteContextForAuth(invitation, options);
115
- }
116
- };
117
- });
118
- }
119
-
120
- app.actions(
121
- withActionDefaults(workspacePendingInvitationsActions, {
122
- domain: "workspace",
123
- dependencies: {
124
- workspacePendingInvitationsService: "workspaces.pending-invitations.service"
125
- }
126
- })
127
- );
128
- }
129
-
130
- export { registerWorkspacePendingInvitations };
@@ -1,66 +0,0 @@
1
- import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
2
- import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
3
- import { INTERNAL_JSON_REST_API } from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
4
- import { deepFreeze } from "../common/support/deepFreeze.js";
5
- import { createRepository as createWorkspaceSettingsRepository } from "./workspaceSettingsRepository.js";
6
- import { createService as createWorkspaceSettingsService } from "./workspaceSettingsService.js";
7
- import { workspaceSettingsActions } from "./workspaceSettingsActions.js";
8
- import { createWorkspaceRoleCatalog } from "../../shared/roles.js";
9
- import { createWorkspaceEntityAndBootstrapEvents } from "../common/support/realtimeServiceEvents.js";
10
-
11
- function resolveWorkspaceSettingsDefaultInvitesEnabled(appConfig = {}) {
12
- const defaultInvitesEnabled = appConfig?.workspaceSettings?.defaults?.invitesEnabled;
13
-
14
- if (typeof defaultInvitesEnabled !== "boolean") {
15
- throw new TypeError("users.core requires appConfig.workspaceSettings.defaults.invitesEnabled.");
16
- }
17
-
18
- return defaultInvitesEnabled;
19
- }
20
-
21
- function registerWorkspaceSettings(app) {
22
- if (!app || typeof app.singleton !== "function" || typeof app.actions !== "function" || typeof app.service !== "function") {
23
- throw new Error("registerWorkspaceSettings requires application singleton()/service()/actions().");
24
- }
25
-
26
- app.singleton("internal.repository.workspace-settings", (scope) => {
27
- const api = scope.make(INTERNAL_JSON_REST_API);
28
- const knex = scope.make("jskit.database.knex");
29
- const appConfig = resolveAppConfig(scope);
30
- return createWorkspaceSettingsRepository({
31
- api,
32
- knex,
33
- defaultInvitesEnabled: resolveWorkspaceSettingsDefaultInvitesEnabled(appConfig)
34
- });
35
- });
36
-
37
- app.service(
38
- "workspaces.settings.service",
39
- (scope) =>
40
- createWorkspaceSettingsService({
41
- workspaceSettingsRepository: scope.make("internal.repository.workspace-settings"),
42
- workspaceInvitationsEnabled: scope.make("workspaces.invitations.enabled"),
43
- roleCatalog: createWorkspaceRoleCatalog(resolveAppConfig(scope))
44
- }),
45
- {
46
- events: deepFreeze({
47
- updateWorkspaceSettings: createWorkspaceEntityAndBootstrapEvents({
48
- workspaceEntity: "settings",
49
- workspaceOperation: "updated",
50
- workspaceRealtimeEvent: "workspace.settings.changed"
51
- })
52
- })
53
- }
54
- );
55
-
56
- app.actions(
57
- withActionDefaults(workspaceSettingsActions, {
58
- domain: "workspace",
59
- dependencies: {
60
- workspaceSettingsService: "workspaces.settings.service"
61
- }
62
- })
63
- );
64
- }
65
-
66
- export { registerWorkspaceSettings };