@jskit-ai/users-core 0.1.32 → 0.1.35

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 (48) hide show
  1. package/package.descriptor.mjs +16 -245
  2. package/package.json +7 -7
  3. package/src/server/UsersCoreServiceProvider.js +4 -28
  4. package/src/server/UsersWorkspacesServiceProvider.js +44 -0
  5. package/src/server/accountNotifications/accountNotificationsService.js +3 -3
  6. package/src/server/accountNotifications/registerAccountNotifications.js +1 -1
  7. package/src/server/accountPreferences/accountPreferencesService.js +3 -3
  8. package/src/server/accountPreferences/registerAccountPreferences.js +1 -1
  9. package/src/server/accountProfile/accountProfileActions.js +8 -2
  10. package/src/server/accountProfile/accountProfileService.js +10 -10
  11. package/src/server/accountProfile/avatarService.js +9 -9
  12. package/src/server/accountProfile/bootAccountProfileRoutes.js +5 -3
  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/registerCommonRepositories.js +3 -22
  18. package/src/server/common/repositories/userSettingsRepository.js +1 -12
  19. package/src/server/common/repositories/{userProfilesRepository.js → usersRepository.js} +1 -1
  20. package/src/server/common/services/accountContextService.js +4 -4
  21. package/src/server/common/services/authProfileSyncService.js +10 -10
  22. package/src/server/registerUsersBootstrap.js +22 -0
  23. package/src/server/registerUsersCore.js +30 -0
  24. package/src/server/registerWorkspaceBootstrap.js +3 -6
  25. package/src/server/registerWorkspaceCore.js +5 -17
  26. package/src/server/registerWorkspaceRepositories.js +26 -0
  27. package/src/server/usersBootstrapContributor.js +248 -0
  28. package/src/server/workspaceBootstrapContributor.js +65 -259
  29. package/src/shared/roles.js +31 -6
  30. package/src/shared/settings.js +1 -2
  31. package/templates/migrations/users_core_generic_initial.cjs +69 -0
  32. package/test/authProfileSyncService.test.js +3 -3
  33. package/test/avatarService.test.js +2 -2
  34. package/test/registerUsersCore.test.js +42 -0
  35. package/test/roles.test.js +90 -5
  36. package/test/usersBootstrapContributor.test.js +172 -0
  37. package/test/usersRouteRequestInputValidator.test.js +7 -390
  38. package/test/workspaceActionContextContributor.test.js +98 -5
  39. package/test/workspaceBootstrapContributor.test.js +34 -346
  40. package/test/workspaceMembersService.test.js +4 -2
  41. package/test/workspaceService.test.js +12 -8
  42. package/test/workspaceSettingsResource.test.js +4 -2
  43. package/test-support/registerDefaultSettingsFields.js +1 -1
  44. package/templates/config/workspaceRoles.js +0 -30
  45. package/templates/migrations/users_core_initial.cjs +0 -123
  46. package/templates/migrations/users_core_workspace_settings_single_name_source.cjs +0 -71
  47. package/templates/migrations/users_core_workspaces_drop_color.cjs +0 -85
  48. package/templates/packages/main/src/shared/resources/workspaceSettingsFields.js +0 -197
@@ -21,9 +21,9 @@ async function readAvatarBuffer(stream, { maxBytes = DEFAULT_AVATAR_POLICY.maxUp
21
21
  });
22
22
  }
23
23
 
24
- function createService({ userProfilesRepository, avatarStorageService, avatarPolicy } = {}) {
25
- if (!userProfilesRepository) {
26
- throw new TypeError("avatarService requires userProfilesRepository.");
24
+ function createService({ usersRepository, avatarStorageService, avatarPolicy } = {}) {
25
+ if (!usersRepository) {
26
+ throw new TypeError("avatarService requires usersRepository.");
27
27
  }
28
28
  if (!avatarStorageService) {
29
29
  throw new TypeError("avatarService requires avatarStorageService.");
@@ -32,21 +32,21 @@ function createService({ userProfilesRepository, avatarStorageService, avatarPol
32
32
  const resolvedAvatarPolicy = resolveAvatarPolicy(avatarPolicy);
33
33
 
34
34
  async function resolveProfile(user) {
35
- const profile = await resolveUserProfile(userProfilesRepository, user);
35
+ const profile = await resolveUserProfile(usersRepository, user);
36
36
  if (!profile) {
37
37
  throw new AppError(404, "User profile was not found.");
38
38
  }
39
39
  return profile;
40
40
  }
41
41
 
42
- async function uploadForUser(user, payload = {}) {
42
+ async function uploadForUser(user, avatarUpload = {}) {
43
43
  const profile = await resolveProfile(user);
44
- validateUploadMimeType(payload?.mimeType, resolvedAvatarPolicy, {
44
+ validateUploadMimeType(avatarUpload?.mimeType, resolvedAvatarPolicy, {
45
45
  fieldName: "avatar",
46
46
  label: "Avatar"
47
47
  });
48
48
 
49
- const buffer = await readAvatarBuffer(payload.stream, {
49
+ const buffer = await readAvatarBuffer(avatarUpload?.stream, {
50
50
  maxBytes: resolvedAvatarPolicy.maxUploadBytes
51
51
  });
52
52
 
@@ -57,7 +57,7 @@ function createService({ userProfilesRepository, avatarStorageService, avatarPol
57
57
  buffer
58
58
  });
59
59
 
60
- const updatedProfile = await userProfilesRepository.updateAvatarById(profile.id, {
60
+ const updatedProfile = await usersRepository.updateAvatarById(profile.id, {
61
61
  avatarStorageKey: savedAvatar.storageKey,
62
62
  avatarVersion,
63
63
  avatarUpdatedAt: new Date(avatarVersionMs)
@@ -73,7 +73,7 @@ function createService({ userProfilesRepository, avatarStorageService, avatarPol
73
73
  if (profile.avatarStorageKey) {
74
74
  await avatarStorageService.deleteAvatar(profile.avatarStorageKey);
75
75
  }
76
- return userProfilesRepository.clearAvatarById(profile.id);
76
+ return usersRepository.clearAvatarById(profile.id);
77
77
  }
78
78
 
79
79
  async function readForUser(user) {
@@ -1,4 +1,5 @@
1
1
  import { withStandardErrorResponses } from "@jskit-ai/http-runtime/shared/validators/errorResponses";
2
+ import { DEFAULT_IMAGE_UPLOAD_MAX_BYTES } from "@jskit-ai/uploads-runtime/shared";
2
3
  import { readSingleMultipartFile } from "@jskit-ai/uploads-runtime/server/multipart/readSingleMultipartFile";
3
4
  import { userSettingsResource } from "../../shared/resources/userSettingsResource.js";
4
5
  import { userProfileResource } from "../../shared/resources/userProfileResource.js";
@@ -77,7 +78,7 @@ function bootAccountProfileRoutes(app) {
77
78
  }
78
79
  },
79
80
  async function (request, reply) {
80
- const avatar = await accountProfileService.readAvatar(request, request.user, {}, {
81
+ const avatar = await accountProfileService.readAvatar(request, request.user, {
81
82
  context: {
82
83
  actor: request.user
83
84
  }
@@ -114,10 +115,11 @@ function bootAccountProfileRoutes(app) {
114
115
  },
115
116
  async function (request, reply) {
116
117
  const filePart = await readSingleMultipartFile(request, {
117
- fieldNames: ["avatar"],
118
+ fieldName: "avatar",
118
119
  required: true,
119
120
  fieldErrorKey: "avatar",
120
- label: "Avatar"
121
+ label: "Avatar",
122
+ maxBytes: DEFAULT_IMAGE_UPLOAD_MAX_BYTES
121
123
  });
122
124
 
123
125
  const uploadDimension = filePart.fields?.uploadDimension?.value;
@@ -20,7 +20,7 @@ function registerAccountProfile(app) {
20
20
 
21
21
  app.singleton("users.avatar.service", (scope) =>
22
22
  createAvatarService({
23
- userProfilesRepository: scope.make("userProfilesRepository"),
23
+ usersRepository: scope.make("usersRepository"),
24
24
  avatarStorageService: scope.make("users.avatar.storage.service")
25
25
  })
26
26
  );
@@ -30,7 +30,7 @@ function registerAccountProfile(app) {
30
30
  (scope) =>
31
31
  createAccountProfileService({
32
32
  userSettingsRepository: scope.make("userSettingsRepository"),
33
- userProfilesRepository: scope.make("userProfilesRepository"),
33
+ usersRepository: scope.make("usersRepository"),
34
34
  authService: scope.make("authService"),
35
35
  avatarService: scope.make("users.avatar.service")
36
36
  }),
@@ -10,10 +10,10 @@ import {
10
10
 
11
11
  function createService({
12
12
  userSettingsRepository,
13
- userProfilesRepository,
13
+ usersRepository,
14
14
  authService
15
15
  } = {}) {
16
- if (!userSettingsRepository || !userProfilesRepository) {
16
+ if (!userSettingsRepository || !usersRepository) {
17
17
  throw new Error("accountSecurityService requires repositories.");
18
18
  }
19
19
 
@@ -40,7 +40,7 @@ function createService({
40
40
  throw new AppError(501, "Password method toggle is not available.");
41
41
  }
42
42
 
43
- const profile = await resolveUserProfile(userProfilesRepository, user);
43
+ const profile = await resolveUserProfile(usersRepository, user);
44
44
  if (!profile) {
45
45
  throw new AppError(404, "User profile was not found.");
46
46
  }
@@ -11,7 +11,7 @@ function registerAccountSecurity(app) {
11
11
  const authService = scope.has("authService") ? scope.make("authService") : null;
12
12
  return createAccountSecurityService({
13
13
  userSettingsRepository: scope.make("userSettingsRepository"),
14
- userProfilesRepository: scope.make("userProfilesRepository"),
14
+ usersRepository: scope.make("usersRepository"),
15
15
  authService
16
16
  });
17
17
  });
@@ -2,6 +2,7 @@ import {
2
2
  normalizeObject,
3
3
  requireServiceMethod
4
4
  } from "@jskit-ai/kernel/shared/actions/actionContributorHelpers";
5
+ import { normalizeSurfaceId } from "@jskit-ai/kernel/shared/surface/registry";
5
6
  import {
6
7
  checkRouteVisibility,
7
8
  USERS_ROUTE_VISIBILITY_PUBLIC,
@@ -9,45 +10,51 @@ import {
9
10
  USERS_ROUTE_VISIBILITY_WORKSPACE_USER
10
11
  } from "../../../shared/support/usersVisibility.js";
11
12
  import { resolveActionUser } from "../support/resolveActionUser.js";
12
-
13
- const WORKSPACE_CONTEXT_ACTION_IDS = Object.freeze([
14
- "workspace.roles.list",
15
- "workspace.settings.read",
16
- "workspace.settings.update",
17
- "workspace.members.list",
18
- "workspace.member.role.update",
19
- "workspace.member.remove",
20
- "workspace.invites.list",
21
- "workspace.invite.create",
22
- "workspace.invite.revoke"
23
- ]);
24
13
  const WORKSPACE_VISIBILITY_ACTION_CONTEXT_SET = new Set([
25
14
  USERS_ROUTE_VISIBILITY_WORKSPACE,
26
15
  USERS_ROUTE_VISIBILITY_WORKSPACE_USER
27
16
  ]);
28
17
 
29
- function createWorkspaceActionContextContributor({ workspaceService } = {}) {
18
+ function normalizeWorkspaceSurfaceIds(surfaceIds = []) {
19
+ const source = Array.isArray(surfaceIds) ? surfaceIds : [];
20
+ const normalized = new Set();
21
+
22
+ for (const entry of source) {
23
+ const surfaceId = normalizeSurfaceId(entry);
24
+ if (!surfaceId) {
25
+ continue;
26
+ }
27
+ normalized.add(surfaceId);
28
+ }
29
+
30
+ return normalized;
31
+ }
32
+
33
+ function createWorkspaceActionContextContributor({ workspaceService, workspaceSurfaceIds = [] } = {}) {
30
34
  const contributorId = "users.workspace.context";
35
+ const workspaceSurfaceIdSet = normalizeWorkspaceSurfaceIds(workspaceSurfaceIds);
31
36
 
32
37
  requireServiceMethod(workspaceService, "resolveWorkspaceContextForUserBySlug", contributorId);
33
38
 
34
39
  return Object.freeze({
35
40
  contributorId,
36
- async contribute({ actionId, input, context, request } = {}) {
41
+ async contribute({ definition = null, input, context, request } = {}) {
37
42
  const payload = normalizeObject(input);
38
43
  if (!Object.hasOwn(payload, "workspaceSlug")) {
39
44
  return {};
40
45
  }
41
46
 
42
- const actionName = String(actionId || "").trim();
43
- const hasLegacyWorkspaceActionId = WORKSPACE_CONTEXT_ACTION_IDS.includes(actionName);
47
+ const actionSurfaces = Array.isArray(definition?.surfaces) ? definition.surfaces : [];
48
+ const hasWorkspaceActionSurface = actionSurfaces.some((surfaceId) => workspaceSurfaceIdSet.has(surfaceId));
49
+ const routeSurfaceId = normalizeSurfaceId(request?.routeOptions?.config?.surface);
50
+ const hasWorkspaceSurface = workspaceSurfaceIdSet.has(routeSurfaceId);
44
51
  const routeVisibilityInput =
45
52
  request && request.routeOptions && request.routeOptions.config
46
53
  ? request.routeOptions.config.visibility
47
54
  : USERS_ROUTE_VISIBILITY_PUBLIC;
48
55
  const routeVisibility = checkRouteVisibility(routeVisibilityInput);
49
56
  const hasWorkspaceRouteVisibility = WORKSPACE_VISIBILITY_ACTION_CONTEXT_SET.has(routeVisibility);
50
- if (!hasLegacyWorkspaceActionId && !hasWorkspaceRouteVisibility) {
57
+ if (!hasWorkspaceActionSurface && !hasWorkspaceRouteVisibility && !hasWorkspaceSurface) {
51
58
  return {};
52
59
  }
53
60
 
@@ -1,8 +1,5 @@
1
- import { createRepository as createUserProfilesRepository } from "./repositories/userProfilesRepository.js";
1
+ import { createRepository as createUsersRepository } from "./repositories/usersRepository.js";
2
2
  import { createRepository as createUserSettingsRepository } from "./repositories/userSettingsRepository.js";
3
- import { createRepository as createWorkspacesRepository } from "./repositories/workspacesRepository.js";
4
- import { createRepository as createWorkspaceMembershipsRepository } from "./repositories/workspaceMembershipsRepository.js";
5
- import { createRepository as createWorkspaceInvitesRepository } from "./repositories/workspaceInvitesRepository.js";
6
3
  import { createRepository as createConsoleSettingsRepository } from "../consoleSettings/consoleSettingsRepository.js";
7
4
 
8
5
  function registerCommonRepositories(app) {
@@ -10,31 +7,15 @@ function registerCommonRepositories(app) {
10
7
  throw new Error("registerCommonRepositories requires application singleton().");
11
8
  }
12
9
 
13
- app.singleton("userProfilesRepository", (scope) => {
10
+ app.singleton("usersRepository", (scope) => {
14
11
  const knex = scope.make("jskit.database.knex");
15
- return createUserProfilesRepository(knex);
12
+ return createUsersRepository(knex);
16
13
  });
17
14
 
18
15
  app.singleton("userSettingsRepository", (scope) => {
19
16
  const knex = scope.make("jskit.database.knex");
20
17
  return createUserSettingsRepository(knex);
21
18
  });
22
-
23
- app.singleton("workspacesRepository", (scope) => {
24
- const knex = scope.make("jskit.database.knex");
25
- return createWorkspacesRepository(knex);
26
- });
27
-
28
- app.singleton("workspaceMembershipsRepository", (scope) => {
29
- const knex = scope.make("jskit.database.knex");
30
- return createWorkspaceMembershipsRepository(knex);
31
- });
32
-
33
- app.singleton("workspaceInvitesRepository", (scope) => {
34
- const knex = scope.make("jskit.database.knex");
35
- return createWorkspaceInvitesRepository(knex);
36
- });
37
-
38
19
  app.singleton("consoleSettingsRepository", (scope) => {
39
20
  const knex = scope.make("jskit.database.knex");
40
21
  return createConsoleSettingsRepository(knex);
@@ -15,7 +15,6 @@ function mapRow(row) {
15
15
 
16
16
  const mapped = {
17
17
  userId: Number(row.user_id),
18
- lastActiveWorkspaceId: row.last_active_workspace_id == null ? null : Number(row.last_active_workspace_id),
19
18
  passwordSignInEnabled: row.password_sign_in_enabled == null ? true : Boolean(row.password_sign_in_enabled),
20
19
  passwordSetupRequired: row.password_setup_required == null ? false : Boolean(row.password_setup_required),
21
20
  createdAt: toIsoString(row.created_at),
@@ -48,7 +47,6 @@ function normalizeBoolean(value, fallback = false) {
48
47
  function createInsertPayload(userId) {
49
48
  const payload = {
50
49
  user_id: Number(userId),
51
- last_active_workspace_id: null,
52
50
  password_sign_in_enabled: DEFAULT_USER_SETTINGS.passwordSignInEnabled,
53
51
  password_setup_required: DEFAULT_USER_SETTINGS.passwordSetupRequired,
54
52
  created_at: nowDb(),
@@ -127,10 +125,6 @@ function createRepository(knex) {
127
125
  if (Object.hasOwn(source, "passwordSetupRequired")) {
128
126
  dbPatch.password_setup_required = normalizeBoolean(source.passwordSetupRequired, ensured.passwordSetupRequired);
129
127
  }
130
- if (Object.hasOwn(source, "lastActiveWorkspaceId")) {
131
- dbPatch.last_active_workspace_id = source.lastActiveWorkspaceId == null ? null : Number(source.lastActiveWorkspaceId);
132
- }
133
-
134
128
  await client("user_settings").where({ user_id: Number(userId) }).update(dbPatch);
135
129
  return findByUserId(userId, { trx: client });
136
130
  }
@@ -160,10 +154,6 @@ function createRepository(knex) {
160
154
  return patchUserSettings(userId, { passwordSetupRequired: required }, options);
161
155
  }
162
156
 
163
- async function updateLastActiveWorkspaceId(userId, workspaceId, options = {}) {
164
- return patchUserSettings(userId, { lastActiveWorkspaceId: workspaceId }, options);
165
- }
166
-
167
157
  return Object.freeze({
168
158
  findByUserId,
169
159
  ensureForUserId,
@@ -171,8 +161,7 @@ function createRepository(knex) {
171
161
  updatePreferences,
172
162
  updateNotifications,
173
163
  updatePasswordSignInEnabled,
174
- updatePasswordSetupRequired,
175
- updateLastActiveWorkspaceId
164
+ updatePasswordSetupRequired
176
165
  });
177
166
  }
178
167
 
@@ -104,7 +104,7 @@ async function resolveUniqueUsername(client, baseUsername, { excludeUserId = 0 }
104
104
 
105
105
  function createRepository(knex) {
106
106
  if (typeof knex !== "function") {
107
- throw new TypeError("userProfilesRepository requires knex.");
107
+ throw new TypeError("usersRepository requires knex.");
108
108
  }
109
109
 
110
110
  async function findById(userId, options = {}) {
@@ -1,9 +1,9 @@
1
- import { normalizeIdentity } from "../repositories/userProfilesRepository.js";
1
+ import { normalizeIdentity } from "../repositories/usersRepository.js";
2
2
 
3
- async function resolveUserProfile(userProfilesRepository, user) {
3
+ async function resolveUserProfile(usersRepository, user) {
4
4
  const identity = normalizeIdentity(user);
5
5
  if (identity) {
6
- const profile = await userProfilesRepository.findByIdentity(identity);
6
+ const profile = await usersRepository.findByIdentity(identity);
7
7
  if (profile) {
8
8
  return profile;
9
9
  }
@@ -11,7 +11,7 @@ async function resolveUserProfile(userProfilesRepository, user) {
11
11
 
12
12
  const userId = Number(user?.id);
13
13
  if (Number.isInteger(userId) && userId > 0) {
14
- const profileById = await userProfilesRepository.findById(userId);
14
+ const profileById = await usersRepository.findById(userId);
15
15
  if (profileById) {
16
16
  return profileById;
17
17
  }
@@ -1,5 +1,5 @@
1
1
  import { normalizeLowerText, normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
2
- import { normalizeIdentity } from "../repositories/userProfilesRepository.js";
2
+ import { normalizeIdentity } from "../repositories/usersRepository.js";
3
3
 
4
4
  function buildNormalizedIdentityKey(identityLike) {
5
5
  const identity = normalizeIdentity(identityLike);
@@ -53,12 +53,12 @@ function requireSynchronizedProfile(profile) {
53
53
  throw new Error("Profile synchronization failed.");
54
54
  }
55
55
 
56
- function createService({ userProfilesRepository, workspaceProvisioningService = null, userSettingsRepository = null } = {}) {
57
- if (!userProfilesRepository || typeof userProfilesRepository.findByIdentity !== "function") {
58
- throw new Error("authProfileSyncService requires userProfilesRepository.findByIdentity().");
56
+ function createService({ usersRepository, workspaceProvisioningService = null, userSettingsRepository = null } = {}) {
57
+ if (!usersRepository || typeof usersRepository.findByIdentity !== "function") {
58
+ throw new Error("authProfileSyncService requires usersRepository.findByIdentity().");
59
59
  }
60
- if (typeof userProfilesRepository.upsert !== "function") {
61
- throw new Error("authProfileSyncService requires userProfilesRepository.upsert().");
60
+ if (typeof usersRepository.upsert !== "function") {
61
+ throw new Error("authProfileSyncService requires usersRepository.upsert().");
62
62
  }
63
63
  if (!userSettingsRepository || typeof userSettingsRepository.ensureForUserId !== "function") {
64
64
  throw new Error("authProfileSyncService requires userSettingsRepository.ensureForUserId().");
@@ -66,7 +66,7 @@ function createService({ userProfilesRepository, workspaceProvisioningService =
66
66
 
67
67
  async function findByIdentity(identityLike, options = {}) {
68
68
  const normalized = buildNormalizedIdentityKey(identityLike);
69
- return userProfilesRepository.findByIdentity(
69
+ return usersRepository.findByIdentity(
70
70
  {
71
71
  provider: normalized.authProvider,
72
72
  providerUserId: normalized.authProviderUserSid
@@ -77,7 +77,7 @@ function createService({ userProfilesRepository, workspaceProvisioningService =
77
77
 
78
78
  async function upsertByIdentity(profileLike, options = {}) {
79
79
  const normalized = buildNormalizedIdentityProfile(profileLike);
80
- return userProfilesRepository.upsert(
80
+ return usersRepository.upsert(
81
81
  {
82
82
  authProvider: normalized.authProvider,
83
83
  authProviderUserSid: normalized.authProviderUserSid,
@@ -118,8 +118,8 @@ function createService({ userProfilesRepository, workspaceProvisioningService =
118
118
  if (options?.trx) {
119
119
  return runSync(options.trx);
120
120
  }
121
- if (typeof userProfilesRepository.withTransaction === "function") {
122
- return userProfilesRepository.withTransaction((trx) => runSync(trx));
121
+ if (typeof usersRepository.withTransaction === "function") {
122
+ return usersRepository.withTransaction((trx) => runSync(trx));
123
123
  }
124
124
  return runSync();
125
125
  }
@@ -0,0 +1,22 @@
1
+ import { registerBootstrapPayloadContributor } from "@jskit-ai/kernel/server/runtime";
2
+ import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
3
+ import { createUsersBootstrapContributor } from "./usersBootstrapContributor.js";
4
+
5
+ function registerUsersBootstrap(app) {
6
+ if (!app || typeof app.singleton !== "function") {
7
+ throw new Error("registerUsersBootstrap requires application singleton().");
8
+ }
9
+
10
+ registerBootstrapPayloadContributor(app, "users.core.bootstrap.payloadContributor", (scope) => {
11
+ return createUsersBootstrapContributor({
12
+ usersRepository: scope.make("usersRepository"),
13
+ userSettingsRepository: scope.make("userSettingsRepository"),
14
+ appConfig: resolveAppConfig(scope),
15
+ tenancyProfile: scope.make("users.tenancy.profile"),
16
+ authService: scope.make("authService"),
17
+ consoleService: scope.has("consoleService") ? scope.make("consoleService") : null
18
+ });
19
+ });
20
+ }
21
+
22
+ export { registerUsersBootstrap };
@@ -0,0 +1,30 @@
1
+ import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
2
+ import { resolveTenancyProfile } from "../shared/tenancyProfile.js";
3
+ import { createService as createAuthProfileSyncService } from "./common/services/authProfileSyncService.js";
4
+ import { registerUsersCoreActionSurfaceSources } from "./support/workspaceActionSurfaces.js";
5
+
6
+ function registerUsersCore(app) {
7
+ if (!app || typeof app.singleton !== "function") {
8
+ throw new Error("registerUsersCore requires application singleton().");
9
+ }
10
+
11
+ registerUsersCoreActionSurfaceSources(app);
12
+
13
+ app.singleton("users.profile.sync.service", (scope) => {
14
+ return createAuthProfileSyncService({
15
+ usersRepository: scope.make("usersRepository"),
16
+ userSettingsRepository: scope.make("userSettingsRepository"),
17
+ workspaceProvisioningService:
18
+ typeof scope.has === "function" && scope.has("users.workspace.service")
19
+ ? scope.make("users.workspace.service")
20
+ : null
21
+ });
22
+ });
23
+
24
+ app.singleton("users.tenancy.profile", (scope) => {
25
+ const appConfig = resolveAppConfig(scope);
26
+ return resolveTenancyProfile(appConfig);
27
+ });
28
+ }
29
+
30
+ export { registerUsersCore };
@@ -8,7 +8,7 @@ function registerWorkspaceBootstrap(app) {
8
8
  throw new Error("registerWorkspaceBootstrap requires application singleton().");
9
9
  }
10
10
 
11
- registerBootstrapPayloadContributor(app, "users.core.workspace.bootstrap.payloadContributor", (scope) => {
11
+ registerBootstrapPayloadContributor(app, "workspaces.core.bootstrap.payloadContributor", (scope) => {
12
12
  const workspaceInvitationsEnabled = scope.make("users.workspace.invitations.enabled");
13
13
 
14
14
  return createWorkspaceBootstrapContributor({
@@ -17,12 +17,9 @@ function registerWorkspaceBootstrap(app) {
17
17
  ? scope.make("users.workspace.pending-invitations.service")
18
18
  : null,
19
19
  workspaceInvitationsEnabled,
20
- userProfilesRepository: scope.make("userProfilesRepository"),
21
- userSettingsRepository: scope.make("userSettingsRepository"),
20
+ usersRepository: scope.make("usersRepository"),
22
21
  appConfig: resolveAppConfig(scope),
23
- tenancyProfile: scope.make("users.tenancy.profile"),
24
- authService: scope.make("authService"),
25
- consoleService: scope.has("consoleService") ? scope.make("consoleService") : null
22
+ tenancyProfile: scope.make("users.tenancy.profile")
26
23
  });
27
24
  });
28
25
  }
@@ -3,13 +3,13 @@ import {
3
3
  } from "@jskit-ai/kernel/server/actions";
4
4
  import { registerRouteVisibilityResolver } from "@jskit-ai/kernel/server/http";
5
5
  import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
6
- import { TENANCY_MODE_WORKSPACES, resolveTenancyProfile } from "../shared/tenancyProfile.js";
7
6
  import { createService as createWorkspaceService } from "./common/services/workspaceContextService.js";
8
- import { createService as createAuthProfileSyncService } from "./common/services/authProfileSyncService.js";
9
7
  import { createWorkspaceActionContextContributor } from "./common/contributors/workspaceActionContextContributor.js";
10
8
  import { createWorkspaceRouteVisibilityResolver } from "./common/contributors/workspaceRouteVisibilityResolver.js";
11
9
  import { createWorkspaceAuthPolicyContextResolver } from "./common/contributors/workspaceAuthPolicyContextResolver.js";
10
+ import { TENANCY_MODE_WORKSPACES } from "../shared/tenancyProfile.js";
12
11
  import { resolveWorkspaceInvitationsPolicy } from "./support/workspaceInvitationsPolicy.js";
12
+ import { resolveWorkspaceSurfaceIdsFromAppConfig } from "./support/workspaceActionSurfaces.js";
13
13
 
14
14
 
15
15
  function registerWorkspaceCore(app) {
@@ -26,20 +26,6 @@ function registerWorkspaceCore(app) {
26
26
  workspaceSettingsRepository: scope.make("workspaceSettingsRepository")
27
27
  });
28
28
  });
29
-
30
- app.singleton("users.profile.sync.service", (scope) => {
31
- return createAuthProfileSyncService({
32
- userProfilesRepository: scope.make("userProfilesRepository"),
33
- userSettingsRepository: scope.make("userSettingsRepository"),
34
- workspaceProvisioningService: scope.make("users.workspace.service")
35
- });
36
- });
37
-
38
- app.singleton("users.tenancy.profile", (scope) => {
39
- const appConfig = resolveAppConfig(scope);
40
- return resolveTenancyProfile(appConfig);
41
- });
42
-
43
29
  app.singleton("users.workspace.enabled", (scope) => {
44
30
  return scope.make("users.tenancy.profile").workspace.enabled === true;
45
31
  });
@@ -62,8 +48,10 @@ function registerWorkspaceCore(app) {
62
48
  });
63
49
 
64
50
  registerActionContextContributor(app, "users.core.workspace.actionContextContributor", (scope) => {
51
+ const appConfig = resolveAppConfig(scope);
65
52
  return createWorkspaceActionContextContributor({
66
- workspaceService: scope.make("users.workspace.service")
53
+ workspaceService: scope.make("users.workspace.service"),
54
+ workspaceSurfaceIds: resolveWorkspaceSurfaceIdsFromAppConfig(appConfig)
67
55
  });
68
56
  });
69
57
 
@@ -0,0 +1,26 @@
1
+ import { createRepository as createWorkspacesRepository } from "./common/repositories/workspacesRepository.js";
2
+ import { createRepository as createWorkspaceMembershipsRepository } from "./common/repositories/workspaceMembershipsRepository.js";
3
+ import { createRepository as createWorkspaceInvitesRepository } from "./common/repositories/workspaceInvitesRepository.js";
4
+
5
+ function registerWorkspaceRepositories(app) {
6
+ if (!app || typeof app.singleton !== "function") {
7
+ throw new Error("registerWorkspaceRepositories requires application singleton().");
8
+ }
9
+
10
+ app.singleton("workspacesRepository", (scope) => {
11
+ const knex = scope.make("jskit.database.knex");
12
+ return createWorkspacesRepository(knex);
13
+ });
14
+
15
+ app.singleton("workspaceMembershipsRepository", (scope) => {
16
+ const knex = scope.make("jskit.database.knex");
17
+ return createWorkspaceMembershipsRepository(knex);
18
+ });
19
+
20
+ app.singleton("workspaceInvitesRepository", (scope) => {
21
+ const knex = scope.make("jskit.database.knex");
22
+ return createWorkspaceInvitesRepository(knex);
23
+ });
24
+ }
25
+
26
+ export { registerWorkspaceRepositories };