@jskit-ai/users-core 0.1.48 → 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 (87) hide show
  1. package/package.descriptor.mjs +7 -7
  2. package/package.json +7 -17
  3. package/src/server/common/services/authProfileSyncService.js +28 -7
  4. package/src/server/common/support/realtimeServiceEvents.js +1 -59
  5. package/src/server/profileSyncLifecycleContributorRegistry.js +56 -0
  6. package/src/server/registerUsersBootstrap.js +0 -1
  7. package/src/server/registerUsersCore.js +2 -14
  8. package/src/server/usersBootstrapContributor.js +2 -64
  9. package/src/shared/index.js +2 -99
  10. package/src/shared/settings.js +1 -119
  11. package/test/authProfileSyncService.test.js +19 -10
  12. package/test/registerServiceRealtimeEvents.test.js +0 -86
  13. package/test/registerUsersCore.test.js +6 -15
  14. package/test/repositoryContracts.test.js +1 -9
  15. package/test/resourcesCanonical.test.js +0 -16
  16. package/test/settingsFieldRegistriesSingleton.test.js +0 -5
  17. package/test/usersBootstrapContributor.test.js +2 -26
  18. package/test/usersRouteResources.test.js +0 -16
  19. package/src/server/UsersWorkspacesServiceProvider.js +0 -44
  20. package/src/server/common/contributors/workspaceActionContextContributor.js +0 -88
  21. package/src/server/common/contributors/workspaceAuthPolicyContextResolver.js +0 -34
  22. package/src/server/common/contributors/workspaceRouteVisibilityResolver.js +0 -78
  23. package/src/server/common/formatters/workspaceFormatter.js +0 -53
  24. package/src/server/common/repositories/workspaceInvitesRepository.js +0 -208
  25. package/src/server/common/repositories/workspaceMembershipsRepository.js +0 -190
  26. package/src/server/common/repositories/workspacesRepository.js +0 -202
  27. package/src/server/common/services/workspaceContextService.js +0 -281
  28. package/src/server/common/support/workspaceRoutePaths.js +0 -17
  29. package/src/server/common/validators/routeParamsValidator.js +0 -62
  30. package/src/server/registerWorkspaceBootstrap.js +0 -27
  31. package/src/server/registerWorkspaceCore.js +0 -73
  32. package/src/server/registerWorkspaceRepositories.js +0 -26
  33. package/src/server/support/resolveWorkspace.js +0 -16
  34. package/src/server/support/workspaceActionSurfaces.js +0 -118
  35. package/src/server/support/workspaceInvitationsPolicy.js +0 -45
  36. package/src/server/support/workspaceRouteInput.js +0 -22
  37. package/src/server/workspaceBootstrapContributor.js +0 -212
  38. package/src/server/workspaceDirectory/bootWorkspaceDirectoryRoutes.js +0 -133
  39. package/src/server/workspaceDirectory/registerWorkspaceDirectory.js +0 -19
  40. package/src/server/workspaceDirectory/workspaceDirectoryActions.js +0 -133
  41. package/src/server/workspaceMembers/bootWorkspaceMembers.js +0 -236
  42. package/src/server/workspaceMembers/registerWorkspaceMembers.js +0 -108
  43. package/src/server/workspaceMembers/workspaceMembersActions.js +0 -186
  44. package/src/server/workspaceMembers/workspaceMembersService.js +0 -222
  45. package/src/server/workspacePendingInvitations/bootWorkspacePendingInvitations.js +0 -62
  46. package/src/server/workspacePendingInvitations/registerWorkspacePendingInvitations.js +0 -119
  47. package/src/server/workspacePendingInvitations/workspacePendingInvitationsActions.js +0 -74
  48. package/src/server/workspacePendingInvitations/workspacePendingInvitationsService.js +0 -138
  49. package/src/server/workspaceSettings/bootWorkspaceSettings.js +0 -76
  50. package/src/server/workspaceSettings/registerWorkspaceSettings.js +0 -62
  51. package/src/server/workspaceSettings/workspaceSettingsActions.js +0 -72
  52. package/src/server/workspaceSettings/workspaceSettingsRepository.js +0 -154
  53. package/src/server/workspaceSettings/workspaceSettingsService.js +0 -66
  54. package/src/shared/resources/workspaceMembersResource.js +0 -354
  55. package/src/shared/resources/workspacePendingInvitationsResource.js +0 -82
  56. package/src/shared/resources/workspaceResource.js +0 -176
  57. package/src/shared/resources/workspaceSettingsFields.js +0 -59
  58. package/src/shared/resources/workspaceSettingsResource.js +0 -169
  59. package/src/shared/roles.js +0 -161
  60. package/src/shared/support/usersApiPaths.js +0 -43
  61. package/src/shared/support/usersVisibility.js +0 -42
  62. package/src/shared/support/workspacePathModel.js +0 -145
  63. package/src/shared/tenancyMode.js +0 -35
  64. package/src/shared/tenancyProfile.js +0 -73
  65. package/test/registerWorkspaceDirectory.test.js +0 -31
  66. package/test/registerWorkspaceSettings.test.js +0 -40
  67. package/test/roles.test.js +0 -159
  68. package/test/tenancyProfile.test.js +0 -67
  69. package/test/usersApiPaths.test.js +0 -49
  70. package/test/usersRouteValidators.test.js +0 -49
  71. package/test/usersVisibility.test.js +0 -27
  72. package/test/workspaceActionContextContributor.test.js +0 -344
  73. package/test/workspaceActionSurfaces.test.js +0 -85
  74. package/test/workspaceAuthPolicyContextResolver.test.js +0 -119
  75. package/test/workspaceBootstrapContributor.test.js +0 -154
  76. package/test/workspaceInvitationsPolicy.test.js +0 -71
  77. package/test/workspaceInvitesRepository.test.js +0 -111
  78. package/test/workspaceMembersService.test.js +0 -398
  79. package/test/workspacePathModel.test.js +0 -93
  80. package/test/workspacePendingInvitationsResource.test.js +0 -38
  81. package/test/workspacePendingInvitationsService.test.js +0 -151
  82. package/test/workspaceRouteVisibilityResolver.test.js +0 -83
  83. package/test/workspaceService.test.js +0 -546
  84. package/test/workspaceSettingsActions.test.js +0 -52
  85. package/test/workspaceSettingsRepository.test.js +0 -202
  86. package/test/workspaceSettingsResource.test.js +0 -169
  87. package/test/workspaceSettingsService.test.js +0 -140
@@ -1,281 +0,0 @@
1
- import { createHash } from "node:crypto";
2
- import { AppError } from "@jskit-ai/kernel/server/runtime/errors";
3
- import {
4
- TENANCY_MODE_NONE,
5
- resolveTenancyProfile
6
- } from "../../../shared/tenancyProfile.js";
7
- import {
8
- resolveRolePermissions
9
- } from "../../../shared/roles.js";
10
- import {
11
- mapWorkspaceSummary
12
- } from "../formatters/workspaceFormatter.js";
13
- import { normalizeLowerText, normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
14
- import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
15
- import { authenticatedUserValidator } from "../validators/authenticatedUserValidator.js";
16
-
17
- function toSlugPart(value) {
18
- const normalized = normalizeLowerText(value)
19
- .replace(/[^a-z0-9]+/g, "-")
20
- .replace(/^-+|-+$/g, "")
21
- .slice(0, 48);
22
- return normalized || "workspace";
23
- }
24
-
25
- function buildWorkspaceBaseSlug(user = {}) {
26
- const username = normalizeLowerText(user.username);
27
- if (username) {
28
- return toSlugPart(username);
29
- }
30
- const displayName = normalizeText(user.displayName);
31
- if (displayName) {
32
- return toSlugPart(displayName);
33
- }
34
- const email = normalizeLowerText(user.email);
35
- if (email.includes("@")) {
36
- return toSlugPart(email.split("@")[0]);
37
- }
38
- return "workspace";
39
- }
40
-
41
- function buildWorkspaceName(user = {}) {
42
- const displayName = normalizeText(user.displayName);
43
- if (displayName) {
44
- return `${displayName}'s Workspace`;
45
- }
46
- const email = normalizeLowerText(user.email);
47
- if (email) {
48
- return `${email}'s Workspace`;
49
- }
50
- return "Workspace";
51
- }
52
-
53
- function buildPermissionsFromMembership(membership, appConfig = {}) {
54
- const roleSid = normalizeLowerText(membership?.roleSid || "member");
55
- return resolveRolePermissions(roleSid, appConfig);
56
- }
57
-
58
- function hashInviteToken(token) {
59
- return createHash("sha256").update(normalizeText(token)).digest("hex");
60
- }
61
-
62
- function normalizeWorkspaceCreationInput(payload = {}) {
63
- const source = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
64
- return {
65
- name: normalizeText(source.name),
66
- requestedSlug: normalizeLowerText(source.slug)
67
- };
68
- }
69
-
70
- function createService({
71
- appConfig = {},
72
- workspacesRepository,
73
- workspaceMembershipsRepository,
74
- workspaceSettingsRepository
75
- } = {}) {
76
- if (
77
- !workspacesRepository ||
78
- !workspaceMembershipsRepository ||
79
- !workspaceSettingsRepository
80
- ) {
81
- throw new Error("workspaceService requires repositories.");
82
- }
83
-
84
- const resolvedTenancyProfile = resolveTenancyProfile(appConfig);
85
- const resolvedTenancyMode = resolvedTenancyProfile.mode;
86
- const workspacePolicy = resolvedTenancyProfile.workspace;
87
- async function ensureUniqueWorkspaceSlug(baseSlug, options = {}) {
88
- let suffix = 0;
89
- while (suffix < 1000) {
90
- suffix += 1;
91
- const candidate = suffix === 1 ? toSlugPart(baseSlug) : `${toSlugPart(baseSlug)}-${suffix}`;
92
- const existing = await workspacesRepository.findBySlug(candidate, options);
93
- if (!existing) {
94
- return candidate;
95
- }
96
- }
97
- throw new AppError(500, "Unable to generate unique workspace slug.");
98
- }
99
-
100
- async function ensureWorkspaceSettingsForWorkspace(workspace, options = {}) {
101
- return workspaceSettingsRepository.ensureForWorkspaceId(workspace?.id, {
102
- ...options,
103
- workspace
104
- });
105
- }
106
-
107
- async function ensurePersonalWorkspaceForUser(user, options = {}) {
108
- const normalizedUser = authenticatedUserValidator.normalize(user);
109
- if (!normalizedUser) {
110
- throw new AppError(400, "Invalid authenticated user payload.");
111
- }
112
-
113
- const existing = await workspacesRepository.findPersonalByOwnerUserId(normalizedUser.id, options);
114
- if (existing) {
115
- await workspaceMembershipsRepository.ensureOwnerMembership(existing.id, normalizedUser.id, options);
116
- await ensureWorkspaceSettingsForWorkspace(existing, options);
117
- return existing;
118
- }
119
-
120
- const slug = await ensureUniqueWorkspaceSlug(buildWorkspaceBaseSlug(normalizedUser), options);
121
- const inserted = await workspacesRepository.insert(
122
- {
123
- slug,
124
- name: buildWorkspaceName(normalizedUser),
125
- ownerUserId: normalizedUser.id,
126
- isPersonal: true,
127
- avatarUrl: ""
128
- },
129
- options
130
- );
131
-
132
- await workspaceMembershipsRepository.ensureOwnerMembership(inserted.id, normalizedUser.id, options);
133
- await ensureWorkspaceSettingsForWorkspace(inserted, options);
134
- return inserted;
135
- }
136
-
137
- async function listWorkspacesForUser(user, options = {}) {
138
- const normalizedUser = authenticatedUserValidator.normalize(user);
139
- if (!normalizedUser) {
140
- return [];
141
- }
142
-
143
- if (resolvedTenancyMode === TENANCY_MODE_NONE) {
144
- return [];
145
- }
146
-
147
- const list = await workspacesRepository.listForUserId(normalizedUser.id, options);
148
- const accessible = list
149
- .map((entry) => mapWorkspaceSummary(entry, { roleSid: entry.roleSid, status: entry.membershipStatus }))
150
- .filter((entry) => entry.isAccessible);
151
-
152
- return accessible;
153
- }
154
-
155
- async function listWorkspacesForAuthenticatedUser(user, options = {}) {
156
- return listWorkspacesForUser(user, options);
157
- }
158
-
159
- async function provisionWorkspaceForNewUser(user, options = {}) {
160
- const normalizedUser = authenticatedUserValidator.normalize(user);
161
- if (!normalizedUser) {
162
- throw new AppError(400, "Invalid authenticated user payload.");
163
- }
164
-
165
- if (workspacePolicy.autoProvision !== true) {
166
- return null;
167
- }
168
-
169
- return ensurePersonalWorkspaceForUser(normalizedUser, options);
170
- }
171
-
172
- async function createWorkspaceForAuthenticatedUser(user, payload = {}, options = {}) {
173
- const normalizedUser = authenticatedUserValidator.normalize(user);
174
- if (!normalizedUser) {
175
- throw new AppError(401, "Authentication required.");
176
- }
177
-
178
- if (workspacePolicy.allowSelfCreate !== true) {
179
- throw new AppError(403, "Workspace creation is disabled for this tenancy mode.");
180
- }
181
-
182
- const createInput = normalizeWorkspaceCreationInput(payload);
183
- if (!createInput.name) {
184
- throw new AppError(400, "Workspace name is required.");
185
- }
186
-
187
- const slugBase = createInput.requestedSlug || toSlugPart(createInput.name);
188
- const slug = await ensureUniqueWorkspaceSlug(slugBase, options);
189
- const inserted = await workspacesRepository.insert(
190
- {
191
- slug,
192
- name: createInput.name,
193
- ownerUserId: normalizedUser.id,
194
- isPersonal: false,
195
- avatarUrl: ""
196
- },
197
- options
198
- );
199
-
200
- await workspaceMembershipsRepository.ensureOwnerMembership(inserted.id, normalizedUser.id, options);
201
- await ensureWorkspaceSettingsForWorkspace(inserted, options);
202
- return inserted;
203
- }
204
-
205
- async function getWorkspaceForAuthenticatedUser(user, workspaceSlug, options = {}) {
206
- const workspaceContext = await resolveWorkspaceContextForUserBySlug(user, workspaceSlug, options);
207
- return workspaceContext.workspace;
208
- }
209
-
210
- async function updateWorkspaceForAuthenticatedUser(user, workspaceSlug, patch = {}, options = {}) {
211
- const workspaceContext = await resolveWorkspaceContextForUserBySlug(user, workspaceSlug, options);
212
- return workspacesRepository.updateById(workspaceContext.workspace.id, patch, options);
213
- }
214
-
215
- async function resolveWorkspaceContextForUserBySlug(user, workspaceSlug, options = {}) {
216
- const normalizedUser = authenticatedUserValidator.normalize(user);
217
- if (!normalizedUser) {
218
- throw new AppError(401, "Authentication required.");
219
- }
220
-
221
- if (resolvedTenancyMode === TENANCY_MODE_NONE) {
222
- throw new AppError(403, "Workspace context is disabled.");
223
- }
224
-
225
- const normalizedWorkspaceSlug = normalizeLowerText(workspaceSlug);
226
- if (!normalizedWorkspaceSlug) {
227
- throw new AppError(400, "workspaceSlug is required.");
228
- }
229
-
230
- const workspace = await workspacesRepository.findBySlug(normalizedWorkspaceSlug, options);
231
-
232
- if (!workspace) {
233
- throw new AppError(404, "Workspace not found.");
234
- }
235
-
236
- let membership = await workspaceMembershipsRepository.findByWorkspaceIdAndUserId(
237
- workspace.id,
238
- normalizedUser.id,
239
- options
240
- );
241
- const actorOwnsWorkspace =
242
- normalizeRecordId(workspace.ownerUserId, { fallback: null }) ===
243
- normalizeRecordId(normalizedUser.id, { fallback: null });
244
- const membershipIsActive = normalizeLowerText(membership?.status) === "active";
245
-
246
- if (!membershipIsActive && actorOwnsWorkspace) {
247
- membership = await workspaceMembershipsRepository.ensureOwnerMembership(workspace.id, normalizedUser.id, options);
248
- }
249
-
250
- if (!membership || normalizeLowerText(membership.status) !== "active") {
251
- throw new AppError(403, "You do not have access to this workspace.");
252
- }
253
-
254
- const workspaceSettings = await ensureWorkspaceSettingsForWorkspace(workspace, options);
255
- const permissions = buildPermissionsFromMembership(membership, appConfig);
256
-
257
- return {
258
- workspace,
259
- membership,
260
- permissions,
261
- workspaceSettings
262
- };
263
- }
264
-
265
- return Object.freeze({
266
- toSlugPart,
267
- buildWorkspaceName,
268
- buildWorkspaceBaseSlug,
269
- hashInviteToken,
270
- ensurePersonalWorkspaceForUser,
271
- provisionWorkspaceForNewUser,
272
- createWorkspaceForAuthenticatedUser,
273
- getWorkspaceForAuthenticatedUser,
274
- updateWorkspaceForAuthenticatedUser,
275
- listWorkspacesForUser,
276
- listWorkspacesForAuthenticatedUser,
277
- resolveWorkspaceContextForUserBySlug
278
- });
279
- }
280
-
281
- export { createService };
@@ -1,17 +0,0 @@
1
- import {
2
- USERS_WORKSPACE_API_BASE_PATH,
3
- normalizeApiRelativePath
4
- } from "../../../shared/support/usersApiPaths.js";
5
-
6
- const USERS_WORKSPACE_ROUTE_BASE_PATH = USERS_WORKSPACE_API_BASE_PATH;
7
-
8
- function resolveWorkspaceRoutePath(relativePath = "/") {
9
- const normalizedRelativePath = normalizeApiRelativePath(relativePath);
10
- if (normalizedRelativePath === "/") {
11
- return USERS_WORKSPACE_ROUTE_BASE_PATH;
12
- }
13
-
14
- return `${USERS_WORKSPACE_ROUTE_BASE_PATH}${normalizedRelativePath}`;
15
- }
16
-
17
- export { USERS_WORKSPACE_ROUTE_BASE_PATH, resolveWorkspaceRoutePath };
@@ -1,62 +0,0 @@
1
- import { Type } from "@fastify/type-provider-typebox";
2
- import { normalizeObjectInput } from "@jskit-ai/kernel/shared/validators/inputNormalization";
3
- import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
4
-
5
- function normalizeRouteParams(input = {}) {
6
- const source = normalizeObjectInput(input);
7
- const normalized = {};
8
-
9
- if (Object.hasOwn(source, "workspaceSlug")) {
10
- normalized.workspaceSlug = normalizeText(source.workspaceSlug).toLowerCase();
11
- }
12
-
13
- if (Object.hasOwn(source, "memberUserId")) {
14
- normalized.memberUserId = normalizeText(source.memberUserId);
15
- }
16
-
17
- if (Object.hasOwn(source, "inviteId")) {
18
- normalized.inviteId = normalizeText(source.inviteId);
19
- }
20
-
21
- if (Object.hasOwn(source, "provider")) {
22
- normalized.provider = normalizeText(source.provider);
23
- }
24
-
25
- return normalized;
26
- }
27
-
28
- function normalizeWorkspaceSlugParams(input = {}) {
29
- const source = normalizeObjectInput(input);
30
- const normalized = {};
31
-
32
- if (Object.hasOwn(source, "workspaceSlug")) {
33
- normalized.workspaceSlug = normalizeText(source.workspaceSlug).toLowerCase();
34
- }
35
-
36
- return normalized;
37
- }
38
-
39
- const routeParamsValidator = Object.freeze({
40
- schema: Type.Object(
41
- {
42
- workspaceSlug: Type.Optional(Type.String({ minLength: 1 })),
43
- memberUserId: Type.Optional(Type.String({ minLength: 1 })),
44
- inviteId: Type.Optional(Type.String({ minLength: 1 })),
45
- provider: Type.Optional(Type.String({ minLength: 1 }))
46
- },
47
- { additionalProperties: false }
48
- ),
49
- normalize: normalizeRouteParams
50
- });
51
-
52
- const workspaceSlugParamsValidator = Object.freeze({
53
- schema: Type.Object(
54
- {
55
- workspaceSlug: Type.Optional(Type.String({ minLength: 1 }))
56
- },
57
- { additionalProperties: false }
58
- ),
59
- normalize: normalizeWorkspaceSlugParams
60
- });
61
-
62
- export { routeParamsValidator, workspaceSlugParamsValidator };
@@ -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("users.workspace.invitations.enabled");
13
-
14
- return createWorkspaceBootstrapContributor({
15
- workspaceService: scope.make("users.workspace.service"),
16
- workspacePendingInvitationsService: workspaceInvitationsEnabled
17
- ? scope.make("users.workspace.pending-invitations.service")
18
- : null,
19
- workspaceInvitationsEnabled,
20
- usersRepository: scope.make("usersRepository"),
21
- appConfig: resolveAppConfig(scope),
22
- tenancyProfile: scope.make("users.tenancy.profile")
23
- });
24
- });
25
- }
26
-
27
- export { registerWorkspaceBootstrap };
@@ -1,73 +0,0 @@
1
- import {
2
- registerActionContextContributor
3
- } from "@jskit-ai/kernel/server/actions";
4
- import { registerRouteVisibilityResolver } from "@jskit-ai/kernel/server/http";
5
- import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
6
- import { createService as createWorkspaceService } from "./common/services/workspaceContextService.js";
7
- import { createWorkspaceActionContextContributor } from "./common/contributors/workspaceActionContextContributor.js";
8
- import { createWorkspaceRouteVisibilityResolver } from "./common/contributors/workspaceRouteVisibilityResolver.js";
9
- import { createWorkspaceAuthPolicyContextResolver } from "./common/contributors/workspaceAuthPolicyContextResolver.js";
10
- import { TENANCY_MODE_WORKSPACES } from "../shared/tenancyProfile.js";
11
- import { resolveWorkspaceInvitationsPolicy } from "./support/workspaceInvitationsPolicy.js";
12
- import { resolveWorkspaceSurfaceIdsFromAppConfig } from "./support/workspaceActionSurfaces.js";
13
-
14
-
15
- function registerWorkspaceCore(app) {
16
- if (!app || typeof app.singleton !== "function") {
17
- throw new Error("registerWorkspaceCore requires application singleton().");
18
- }
19
-
20
- app.singleton("users.workspace.service", (scope) => {
21
- const appConfig = resolveAppConfig(scope);
22
- return createWorkspaceService({
23
- appConfig,
24
- workspacesRepository: scope.make("workspacesRepository"),
25
- workspaceMembershipsRepository: scope.make("workspaceMembershipsRepository"),
26
- workspaceSettingsRepository: scope.make("workspaceSettingsRepository")
27
- });
28
- });
29
- app.singleton("users.workspace.enabled", (scope) => {
30
- return scope.make("users.tenancy.profile").workspace.enabled === true;
31
- });
32
-
33
- app.singleton("users.workspace.self-create.enabled", (scope) => {
34
- return scope.make("users.tenancy.profile").workspace.allowSelfCreate === true;
35
- });
36
-
37
- app.singleton("users.workspace.tenancy.enabled", (scope) => {
38
- return scope.make("users.tenancy.profile").mode === TENANCY_MODE_WORKSPACES;
39
- });
40
-
41
- app.singleton("users.workspace.invitations.enabled", (scope) => {
42
- const appConfig = resolveAppConfig(scope);
43
- const tenancyProfile = scope.make("users.tenancy.profile");
44
- return resolveWorkspaceInvitationsPolicy({
45
- appConfig,
46
- tenancyProfile
47
- }).enabled;
48
- });
49
-
50
- registerActionContextContributor(app, "users.core.workspace.actionContextContributor", (scope) => {
51
- const appConfig = resolveAppConfig(scope);
52
- return createWorkspaceActionContextContributor({
53
- workspaceService: scope.make("users.workspace.service"),
54
- workspaceSurfaceIds: resolveWorkspaceSurfaceIdsFromAppConfig(appConfig)
55
- });
56
- });
57
-
58
- if (typeof app.has !== "function" || !app.has("auth.policy.contextResolver")) {
59
- app.singleton("auth.policy.contextResolver", (scope) =>
60
- createWorkspaceAuthPolicyContextResolver({
61
- workspaceService: scope.make("users.workspace.service")
62
- })
63
- );
64
- }
65
-
66
- registerRouteVisibilityResolver(app, "users.core.workspace.routeVisibilityResolver", (scope) =>
67
- createWorkspaceRouteVisibilityResolver({
68
- workspaceService: scope.make("users.workspace.service")
69
- })
70
- );
71
- }
72
-
73
- export { registerWorkspaceCore };
@@ -1,26 +0,0 @@
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 };
@@ -1,16 +0,0 @@
1
- import { normalizeObject } from "@jskit-ai/kernel/shared/support/normalize";
2
-
3
- function resolveRequest(context = {}) {
4
- const requestMeta = normalizeObject(context?.requestMeta);
5
- return normalizeObject(requestMeta.request);
6
- }
7
-
8
- function resolveWorkspace(context = {}, input = {}) {
9
- const payload = normalizeObject(input);
10
- const requestMeta = normalizeObject(context?.requestMeta);
11
- const resolvedWorkspaceContext = normalizeObject(requestMeta.resolvedWorkspaceContext);
12
-
13
- return payload.workspace || resolvedWorkspaceContext.workspace || context?.workspace || resolveRequest(context)?.workspace || null;
14
- }
15
-
16
- export { resolveWorkspace };
@@ -1,118 +0,0 @@
1
- import { normalizeSurfaceId } from "@jskit-ai/kernel/shared/surface/registry";
2
- import { isRecord } from "@jskit-ai/kernel/shared/support/normalize";
3
- import { resolveDefaultWorkspaceSurfaceId } from "../../shared/support/workspacePathModel.js";
4
-
5
- function normalizeSurfaceIds(surfaceIds = []) {
6
- const source = Array.isArray(surfaceIds) ? surfaceIds : [];
7
- const seen = new Set();
8
- const normalized = [];
9
-
10
- for (const candidate of source) {
11
- const surfaceId = normalizeSurfaceId(candidate);
12
- if (!surfaceId || seen.has(surfaceId)) {
13
- continue;
14
- }
15
- seen.add(surfaceId);
16
- normalized.push(surfaceId);
17
- }
18
-
19
- return normalized;
20
- }
21
-
22
- function resolveWorkspaceSurfaceIdsFromAppConfig(appConfig = {}) {
23
- return resolveSurfaceIdsFromAppConfig(appConfig, (definition) => definition.requiresWorkspace === true);
24
- }
25
-
26
- function resolveSurfaceIdsFromAppConfig(appConfig = {}, predicate) {
27
- const source = isRecord(appConfig?.surfaceDefinitions) ? appConfig.surfaceDefinitions : {};
28
- const resolved = [];
29
-
30
- for (const [key, value] of Object.entries(source)) {
31
- const definition = isRecord(value) ? value : {};
32
- const surfaceId = normalizeSurfaceId(definition.id || key);
33
- if (!surfaceId) {
34
- continue;
35
- }
36
- if (definition.enabled === false) {
37
- continue;
38
- }
39
- if (typeof predicate === "function" && predicate(definition) === true) {
40
- resolved.push(surfaceId);
41
- }
42
- }
43
-
44
- return normalizeSurfaceIds(resolved);
45
- }
46
-
47
- function materializeWorkspaceActionSurfaces(actions = [], { workspaceSurfaceIds = [] } = {}) {
48
- const sourceActions = Array.isArray(actions) ? actions : [];
49
- const resolvedWorkspaceSurfaceIds = normalizeSurfaceIds(workspaceSurfaceIds);
50
- const materialized = [];
51
-
52
- for (const entry of sourceActions) {
53
- const action = isRecord(entry) ? entry : {};
54
- const surfacesFrom = String(action.surfacesFrom || "")
55
- .trim()
56
- .toLowerCase();
57
- if (surfacesFrom !== "workspace") {
58
- materialized.push(action);
59
- continue;
60
- }
61
-
62
- if (resolvedWorkspaceSurfaceIds.length < 1) {
63
- continue;
64
- }
65
-
66
- const { surfacesFrom: _ignored, ...rest } = action;
67
- materialized.push({
68
- ...rest,
69
- surfaces: [...resolvedWorkspaceSurfaceIds]
70
- });
71
- }
72
-
73
- return Object.freeze(materialized.map((entry) => Object.freeze({ ...entry })));
74
- }
75
-
76
- function registerUsersCoreActionSurfaceSources(app) {
77
- if (!app || typeof app.actionSurfaceSource !== "function") {
78
- return;
79
- }
80
-
81
- app.actionSurfaceSource("workspace", ({ scope }) => {
82
- const appConfig = scope?.has?.("appConfig") ? scope.make("appConfig") : {};
83
- return resolveWorkspaceSurfaceIdsFromAppConfig(appConfig);
84
- });
85
- }
86
-
87
- function materializeWorkspaceActionSurfacesFromAppConfig(actions = [], { appConfig = {} } = {}) {
88
- const workspaceSurfaceIds = resolveWorkspaceSurfaceIdsFromAppConfig(appConfig);
89
- return materializeWorkspaceActionSurfaces(actions, { workspaceSurfaceIds });
90
- }
91
-
92
- function resolveDefaultWorkspaceRouteSurfaceIdFromAppConfig(appConfig = {}) {
93
- const workspaceSurfaceIds = resolveWorkspaceSurfaceIdsFromAppConfig(appConfig);
94
- const workspaceSurfaceSet = new Set(workspaceSurfaceIds);
95
-
96
- const resolvedSurfaceId = resolveDefaultWorkspaceSurfaceId({
97
- defaultSurfaceId: appConfig?.surfaceDefaultId,
98
- workspaceSurfaceIds,
99
- surfaceRequiresWorkspace(surfaceId) {
100
- return workspaceSurfaceSet.has(surfaceId);
101
- }
102
- });
103
-
104
- return (
105
- normalizeSurfaceId(resolvedSurfaceId) ||
106
- normalizeSurfaceId(workspaceSurfaceIds[0]) ||
107
- normalizeSurfaceId(appConfig?.surfaceDefaultId) ||
108
- ""
109
- );
110
- }
111
-
112
- export {
113
- resolveWorkspaceSurfaceIdsFromAppConfig,
114
- resolveDefaultWorkspaceRouteSurfaceIdFromAppConfig,
115
- materializeWorkspaceActionSurfaces,
116
- materializeWorkspaceActionSurfacesFromAppConfig,
117
- registerUsersCoreActionSurfaceSources
118
- };
@@ -1,45 +0,0 @@
1
- import {
2
- TENANCY_MODE_NONE,
3
- TENANCY_MODE_PERSONAL,
4
- normalizeTenancyMode
5
- } from "../../shared/tenancyMode.js";
6
-
7
- function normalizeWorkspaceInvitationsConfig(appConfig = {}) {
8
- const source = appConfig && typeof appConfig === "object" && !Array.isArray(appConfig)
9
- ? appConfig.workspaceInvitations
10
- : null;
11
- const normalizedSource = source && typeof source === "object" && !Array.isArray(source)
12
- ? source
13
- : {};
14
-
15
- return Object.freeze({
16
- enabled: normalizedSource.enabled !== false,
17
- allowInPersonalMode: normalizedSource.allowInPersonalMode !== false
18
- });
19
- }
20
-
21
- function resolveWorkspaceInvitationsPolicy({
22
- appConfig = {},
23
- tenancyProfile = null
24
- } = {}) {
25
- const config = normalizeWorkspaceInvitationsConfig(appConfig);
26
- const normalizedTenancyProfile = tenancyProfile && typeof tenancyProfile === "object"
27
- ? tenancyProfile
28
- : {};
29
- const tenancyMode = normalizeTenancyMode(normalizedTenancyProfile.mode || appConfig?.tenancyMode);
30
- const workspaceEnabled = normalizedTenancyProfile?.workspace?.enabled === true || tenancyMode !== TENANCY_MODE_NONE;
31
- const enabledForTenancyMode = tenancyMode !== TENANCY_MODE_PERSONAL || config.allowInPersonalMode === true;
32
- const enabled = config.enabled === true && workspaceEnabled && enabledForTenancyMode;
33
-
34
- return Object.freeze({
35
- enabled,
36
- workspaceEnabled,
37
- allowInPersonalMode: config.allowInPersonalMode,
38
- tenancyMode
39
- });
40
- }
41
-
42
- export {
43
- normalizeWorkspaceInvitationsConfig,
44
- resolveWorkspaceInvitationsPolicy
45
- };
@@ -1,22 +0,0 @@
1
- import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
2
-
3
- function readWorkspaceSlugFromRouteParams(params = {}) {
4
- const workspaceSlug = normalizeText(params?.workspaceSlug).toLowerCase();
5
- return workspaceSlug || "";
6
- }
7
-
8
- function buildWorkspaceInputFromRouteParams(params = {}) {
9
- const workspaceSlug = readWorkspaceSlugFromRouteParams(params);
10
- if (!workspaceSlug) {
11
- return {};
12
- }
13
-
14
- return {
15
- workspaceSlug
16
- };
17
- }
18
-
19
- export {
20
- readWorkspaceSlugFromRouteParams,
21
- buildWorkspaceInputFromRouteParams
22
- };