@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,138 +0,0 @@
1
- import { resolveInviteTokenHash } from "@jskit-ai/auth-core/server/inviteTokens";
2
- import { AppError } from "@jskit-ai/kernel/server/runtime/errors";
3
- import { normalizeLowerText, normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
4
- import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
5
- import { authenticatedUserValidator } from "../common/validators/authenticatedUserValidator.js";
6
-
7
- function createService({
8
- workspaceInvitesRepository,
9
- workspaceMembershipsRepository
10
- } = {}) {
11
- if (!workspaceInvitesRepository || !workspaceMembershipsRepository) {
12
- throw new Error("workspacePendingInvitationsService requires invite and membership repositories.");
13
- }
14
-
15
- function requireAuthenticatedInviteUser(user) {
16
- const normalizedUser = authenticatedUserValidator.normalize(user);
17
- if (!normalizedUser) {
18
- throw new AppError(401, "Authentication required.");
19
- }
20
-
21
- return normalizedUser;
22
- }
23
-
24
- function requireInviteTokenHash(token) {
25
- const normalizedToken = normalizeText(token);
26
- if (!normalizedToken) {
27
- throw new AppError(400, "Invite token is required.");
28
- }
29
-
30
- const tokenHash = resolveInviteTokenHash(normalizedToken);
31
- if (!tokenHash) {
32
- throw new AppError(400, "Invite token is invalid.");
33
- }
34
-
35
- return tokenHash;
36
- }
37
-
38
- async function requirePendingInviteForUserByToken(user, token, options = {}) {
39
- const normalizedUser = requireAuthenticatedInviteUser(user);
40
- const tokenHash = requireInviteTokenHash(token);
41
-
42
- const invite = await workspaceInvitesRepository.findPendingByTokenHash(tokenHash, options);
43
- if (!invite) {
44
- throw new AppError(404, "Invitation not found or already handled.");
45
- }
46
-
47
- if (normalizeLowerText(invite.email) !== normalizedUser.email) {
48
- throw new AppError(403, "Invitation email does not match authenticated user.");
49
- }
50
-
51
- return {
52
- user: normalizedUser,
53
- invite
54
- };
55
- }
56
-
57
- async function revokeExpiredInviteAndThrow(invite, options = {}) {
58
- if (invite.expiresAt && new Date(invite.expiresAt).getTime() < Date.now()) {
59
- await workspaceInvitesRepository.revokeById(invite.id, options);
60
- throw new AppError(409, "Invitation has expired.");
61
- }
62
- }
63
-
64
- async function listPendingInvitesForUser(user, options = {}) {
65
- const normalizedUser = requireAuthenticatedInviteUser(user);
66
- if (!normalizedUser.email) {
67
- return [];
68
- }
69
-
70
- return workspaceInvitesRepository.listPendingByEmail(normalizedUser.email, options);
71
- }
72
-
73
- function requireWorkspaceIdFromInvite(invite, methodName = "workspacePendingInvitationsService") {
74
- const workspaceId = normalizeRecordId(invite?.workspaceId, { fallback: null });
75
- if (!workspaceId) {
76
- throw new Error(`${methodName} expected invite workspace id.`);
77
- }
78
- return workspaceId;
79
- }
80
-
81
- async function resolveInviteActionInput(user, token, options = {}, methodName = "workspacePendingInvitationsService") {
82
- const resolvedInvite = await requirePendingInviteForUserByToken(user, token, options);
83
- await revokeExpiredInviteAndThrow(resolvedInvite.invite, options);
84
-
85
- return {
86
- resolvedInvite,
87
- workspaceId: requireWorkspaceIdFromInvite(resolvedInvite.invite, methodName)
88
- };
89
- }
90
-
91
- async function acceptInviteByToken({ user, token } = {}, options = {}) {
92
- const { resolvedInvite, workspaceId } = await resolveInviteActionInput(
93
- user,
94
- token,
95
- options,
96
- "workspacePendingInvitationsService.acceptInviteByToken"
97
- );
98
-
99
- await workspaceMembershipsRepository.upsertMembership(
100
- workspaceId,
101
- resolvedInvite.user.id,
102
- {
103
- roleSid: resolvedInvite.invite.roleSid,
104
- status: "active"
105
- },
106
- options
107
- );
108
- await workspaceInvitesRepository.markAcceptedById(resolvedInvite.invite.id, options);
109
-
110
- return {
111
- decision: "accepted",
112
- workspaceId
113
- };
114
- }
115
-
116
- async function refuseInviteByToken({ user, token } = {}, options = {}) {
117
- const { resolvedInvite, workspaceId } = await resolveInviteActionInput(
118
- user,
119
- token,
120
- options,
121
- "workspacePendingInvitationsService.refuseInviteByToken"
122
- );
123
- await workspaceInvitesRepository.revokeById(resolvedInvite.invite.id, options);
124
-
125
- return {
126
- decision: "refused",
127
- workspaceId
128
- };
129
- }
130
-
131
- return Object.freeze({
132
- listPendingInvitesForUser,
133
- acceptInviteByToken,
134
- refuseInviteByToken
135
- });
136
- }
137
-
138
- export { createService };
@@ -1,76 +0,0 @@
1
- import { withStandardErrorResponses } from "@jskit-ai/http-runtime/shared/validators/errorResponses";
2
- import { workspaceSettingsResource } from "../../shared/resources/workspaceSettingsResource.js";
3
- import { resolveWorkspaceRoutePath } from "../common/support/workspaceRoutePaths.js";
4
- import { workspaceSlugParamsValidator } from "../common/validators/routeParamsValidator.js";
5
- import { resolveDefaultWorkspaceRouteSurfaceIdFromAppConfig } from "../support/workspaceActionSurfaces.js";
6
-
7
- function bootWorkspaceSettings(app) {
8
- if (!app || typeof app.make !== "function") {
9
- throw new Error("bootWorkspaceSettings requires application make().");
10
- }
11
-
12
- const router = app.make("jskit.http.router");
13
- const appConfig = typeof app.has === "function" && app.has("appConfig") ? app.make("appConfig") : {};
14
- const workspaceRouteSurfaceId = resolveDefaultWorkspaceRouteSurfaceIdFromAppConfig(appConfig);
15
-
16
- router.register(
17
- "GET",
18
- resolveWorkspaceRoutePath("/settings"),
19
- {
20
- auth: "required",
21
- surface: workspaceRouteSurfaceId,
22
- visibility: "workspace",
23
- meta: {
24
- tags: ["workspace"],
25
- summary: "Get workspace settings and role catalog by workspace slug"
26
- },
27
- paramsValidator: workspaceSlugParamsValidator,
28
- responseValidators: withStandardErrorResponses({
29
- 200: workspaceSettingsResource.operations.view.outputValidator
30
- })
31
- },
32
- async function (request, reply) {
33
- const response = await request.executeAction({
34
- actionId: "workspace.settings.read",
35
- input: {
36
- workspaceSlug: request.input.params.workspaceSlug
37
- }
38
- });
39
- reply.code(200).send(response);
40
- }
41
- );
42
-
43
- router.register(
44
- "PATCH",
45
- resolveWorkspaceRoutePath("/settings"),
46
- {
47
- auth: "required",
48
- surface: workspaceRouteSurfaceId,
49
- visibility: "workspace",
50
- meta: {
51
- tags: ["workspace"],
52
- summary: "Update workspace settings by workspace slug"
53
- },
54
- paramsValidator: workspaceSlugParamsValidator,
55
- bodyValidator: workspaceSettingsResource.operations.patch.bodyValidator,
56
- responseValidators: withStandardErrorResponses(
57
- {
58
- 200: workspaceSettingsResource.operations.patch.outputValidator
59
- },
60
- { includeValidation400: true }
61
- )
62
- },
63
- async function (request, reply) {
64
- const response = await request.executeAction({
65
- actionId: "workspace.settings.update",
66
- input: {
67
- workspaceSlug: request.input.params.workspaceSlug,
68
- patch: request.input.body
69
- }
70
- });
71
- reply.code(200).send(response);
72
- }
73
- );
74
- }
75
-
76
- export { bootWorkspaceSettings };
@@ -1,62 +0,0 @@
1
- import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
2
- import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
3
- import { deepFreeze } from "../common/support/deepFreeze.js";
4
- import { createRepository as createWorkspaceSettingsRepository } from "./workspaceSettingsRepository.js";
5
- import { createService as createWorkspaceSettingsService } from "./workspaceSettingsService.js";
6
- import { workspaceSettingsActions } from "./workspaceSettingsActions.js";
7
- import { createWorkspaceRoleCatalog } from "../../shared/roles.js";
8
- import { createWorkspaceEntityAndBootstrapEvents } from "../common/support/realtimeServiceEvents.js";
9
-
10
- function resolveWorkspaceSettingsDefaultInvitesEnabled(appConfig = {}) {
11
- const defaultInvitesEnabled = appConfig?.workspaceSettings?.defaults?.invitesEnabled;
12
-
13
- if (typeof defaultInvitesEnabled !== "boolean") {
14
- throw new TypeError("users.core requires appConfig.workspaceSettings.defaults.invitesEnabled.");
15
- }
16
-
17
- return defaultInvitesEnabled;
18
- }
19
-
20
- function registerWorkspaceSettings(app) {
21
- if (!app || typeof app.singleton !== "function" || typeof app.actions !== "function" || typeof app.service !== "function") {
22
- throw new Error("registerWorkspaceSettings requires application singleton()/service()/actions().");
23
- }
24
-
25
- app.singleton("workspaceSettingsRepository", (scope) => {
26
- const knex = scope.make("jskit.database.knex");
27
- const appConfig = resolveAppConfig(scope);
28
- return createWorkspaceSettingsRepository(knex, {
29
- defaultInvitesEnabled: resolveWorkspaceSettingsDefaultInvitesEnabled(appConfig)
30
- });
31
- });
32
-
33
- app.service(
34
- "users.workspace.settings.service",
35
- (scope) =>
36
- createWorkspaceSettingsService({
37
- workspaceSettingsRepository: scope.make("workspaceSettingsRepository"),
38
- workspaceInvitationsEnabled: scope.make("users.workspace.invitations.enabled"),
39
- roleCatalog: createWorkspaceRoleCatalog(resolveAppConfig(scope))
40
- }),
41
- {
42
- events: deepFreeze({
43
- updateWorkspaceSettings: createWorkspaceEntityAndBootstrapEvents({
44
- workspaceEntity: "settings",
45
- workspaceOperation: "updated",
46
- workspaceRealtimeEvent: "workspace.settings.changed"
47
- })
48
- })
49
- }
50
- );
51
-
52
- app.actions(
53
- withActionDefaults(workspaceSettingsActions, {
54
- domain: "workspace",
55
- dependencies: {
56
- workspaceSettingsService: "users.workspace.settings.service"
57
- }
58
- })
59
- );
60
- }
61
-
62
- export { registerWorkspaceSettings };
@@ -1,72 +0,0 @@
1
- import { workspaceSettingsResource } from "../../shared/resources/workspaceSettingsResource.js";
2
- import { workspaceSlugParamsValidator } from "../common/validators/routeParamsValidator.js";
3
- import { resolveWorkspace } from "../support/resolveWorkspace.js";
4
-
5
- const workspaceSettingsActions = Object.freeze([
6
- {
7
- id: "workspace.settings.read",
8
- version: 1,
9
- kind: "query",
10
- channels: ["api", "automation", "internal"],
11
- surfacesFrom: "workspace",
12
- permission: {
13
- require: "any",
14
- permissions: ["workspace.settings.view", "workspace.settings.update"]
15
- },
16
- inputValidator: workspaceSlugParamsValidator,
17
- outputValidator: workspaceSettingsResource.operations.view.outputValidator,
18
- idempotency: "none",
19
- audit: {
20
- actionName: "workspace.settings.read"
21
- },
22
- observability: {},
23
- async execute(input, context, deps) {
24
- const response = await deps.workspaceSettingsService.getWorkspaceSettings(resolveWorkspace(context, input), {
25
- context
26
- });
27
-
28
- return response;
29
- }
30
- },
31
- {
32
- id: "workspace.settings.update",
33
- version: 1,
34
- kind: "command",
35
- channels: ["api", "assistant_tool", "automation", "internal"],
36
- surfacesFrom: "workspace",
37
- permission: {
38
- require: "all",
39
- permissions: ["workspace.settings.update"]
40
- },
41
- inputValidator: [
42
- workspaceSlugParamsValidator,
43
- {
44
- patch: workspaceSettingsResource.operations.patch.bodyValidator
45
- }
46
- ],
47
- outputValidator: workspaceSettingsResource.operations.patch.outputValidator,
48
- idempotency: "optional",
49
- audit: {
50
- actionName: "workspace.settings.update"
51
- },
52
- observability: {},
53
- extensions: {
54
- assistant: {
55
- description: "Update workspace settings."
56
- }
57
- },
58
- async execute(input, context, deps) {
59
- const response = await deps.workspaceSettingsService.updateWorkspaceSettings(
60
- resolveWorkspace(context, input),
61
- input.patch,
62
- {
63
- context
64
- }
65
- );
66
-
67
- return response;
68
- }
69
- }
70
- ]);
71
-
72
- export { workspaceSettingsActions };
@@ -1,154 +0,0 @@
1
- import {
2
- normalizeDbRecordId,
3
- normalizeRecordId,
4
- toIsoString,
5
- nowDb,
6
- isDuplicateEntryError,
7
- createWithTransaction
8
- } from "../common/repositories/repositoryUtils.js";
9
- import { normalizeObjectInput } from "@jskit-ai/kernel/shared/validators/inputNormalization";
10
- import { pickOwnProperties } from "@jskit-ai/kernel/shared/support";
11
- import {
12
- workspaceSettingsFields,
13
- resolveWorkspaceSettingsFieldKeys
14
- } from "../../shared/resources/workspaceSettingsFields.js";
15
-
16
- function resolveWorkspaceSettingsSeed(workspace = {}, { defaultInvitesEnabled = true } = {}) {
17
- const source = normalizeObjectInput(workspace);
18
- const seed = {};
19
- for (const field of workspaceSettingsFields) {
20
- const rawValue = Object.hasOwn(source, field.key)
21
- ? source[field.key]
22
- : field.resolveDefault({
23
- workspace: source,
24
- defaultInvitesEnabled
25
- });
26
- seed[field.key] = field.normalizeOutput(rawValue, {
27
- workspace: source,
28
- defaultInvitesEnabled
29
- });
30
- }
31
- return seed;
32
- }
33
-
34
- function createRepository(knex, { defaultInvitesEnabled } = {}) {
35
- if (typeof knex !== "function") {
36
- throw new TypeError("workspaceSettingsRepository requires knex.");
37
- }
38
- const withTransaction = createWithTransaction(knex);
39
-
40
- function mapRow(row) {
41
- if (!row) {
42
- return null;
43
- }
44
-
45
- const settings = {
46
- workspaceId: normalizeDbRecordId(row.workspace_id, { fallback: "" })
47
- };
48
- for (const field of workspaceSettingsFields) {
49
- const rawValue = Object.hasOwn(row, field.dbColumn)
50
- ? row[field.dbColumn]
51
- : field.resolveDefault({
52
- defaultInvitesEnabled
53
- });
54
- settings[field.key] = field.normalizeOutput(rawValue, {
55
- defaultInvitesEnabled
56
- });
57
- }
58
-
59
- settings.createdAt = toIsoString(row.created_at);
60
- settings.updatedAt = toIsoString(row.updated_at);
61
- return settings;
62
- }
63
-
64
- async function findByWorkspaceId(workspaceId, options = {}) {
65
- const client = options?.trx || knex;
66
- const normalizedWorkspaceId = normalizeRecordId(workspaceId, { fallback: null });
67
- if (!normalizedWorkspaceId) {
68
- return null;
69
- }
70
-
71
- const row = await client("workspace_settings").where({ workspace_id: normalizedWorkspaceId }).first();
72
- return mapRow(row);
73
- }
74
-
75
- async function ensureForWorkspaceId(workspaceId, options = {}) {
76
- const client = options?.trx || knex;
77
- const normalizedWorkspaceId = normalizeRecordId(workspaceId, { fallback: null });
78
- if (!normalizedWorkspaceId) {
79
- throw new TypeError("workspaceSettingsRepository.ensureForWorkspaceId requires a valid workspace id.");
80
- }
81
-
82
- const seed = resolveWorkspaceSettingsSeed(options?.workspace, {
83
- defaultInvitesEnabled
84
- });
85
- const existing = await findByWorkspaceId(normalizedWorkspaceId, { trx: client });
86
- if (existing) {
87
- return existing;
88
- }
89
-
90
- try {
91
- const insertPayload = {
92
- workspace_id: normalizedWorkspaceId,
93
- created_at: nowDb(),
94
- updated_at: nowDb()
95
- };
96
- for (const field of workspaceSettingsFields) {
97
- insertPayload[field.dbColumn] = seed[field.key];
98
- }
99
- await client("workspace_settings").insert(insertPayload);
100
- } catch (error) {
101
- if (!isDuplicateEntryError(error)) {
102
- throw error;
103
- }
104
- }
105
-
106
- return findByWorkspaceId(normalizedWorkspaceId, { trx: client });
107
- }
108
-
109
- async function updateSettingsByWorkspaceId(workspaceId, patch = {}, options = {}) {
110
- const client = options?.trx || knex;
111
- const normalizedWorkspaceId = normalizeRecordId(workspaceId, { fallback: null });
112
- if (!normalizedWorkspaceId) {
113
- throw new TypeError("workspaceSettingsRepository.updateSettingsByWorkspaceId requires a valid workspace id.");
114
- }
115
-
116
- const ensured = await ensureForWorkspaceId(normalizedWorkspaceId, {
117
- trx: client,
118
- workspace: options?.workspace
119
- });
120
- const source = normalizeObjectInput(patch);
121
- const settingsPatch = pickOwnProperties(source, resolveWorkspaceSettingsFieldKeys());
122
-
123
- if (Object.keys(settingsPatch).length === 0) {
124
- return ensured;
125
- }
126
-
127
- const dbPatch = {
128
- updated_at: nowDb()
129
- };
130
-
131
- for (const field of workspaceSettingsFields) {
132
- if (!Object.hasOwn(settingsPatch, field.key)) {
133
- continue;
134
- }
135
- dbPatch[field.dbColumn] = field.normalizeInput(settingsPatch[field.key], {
136
- payload: source
137
- });
138
- }
139
-
140
- await client("workspace_settings").where({ workspace_id: normalizedWorkspaceId }).update({
141
- ...dbPatch
142
- });
143
- return findByWorkspaceId(normalizedWorkspaceId, { trx: client });
144
- }
145
-
146
- return Object.freeze({
147
- withTransaction,
148
- findByWorkspaceId,
149
- ensureForWorkspaceId,
150
- updateSettingsByWorkspaceId
151
- });
152
- }
153
-
154
- export { createRepository };
@@ -1,66 +0,0 @@
1
- import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
2
- import { normalizeObjectInput } from "@jskit-ai/kernel/shared/validators/inputNormalization";
3
- import { pickOwnProperties } from "@jskit-ai/kernel/shared/support";
4
- import {
5
- workspaceSettingsFields,
6
- resolveWorkspaceSettingsFieldKeys
7
- } from "../../shared/resources/workspaceSettingsFields.js";
8
- import { createWorkspaceRoleCatalog, cloneWorkspaceRoleCatalog } from "../../shared/roles.js";
9
-
10
- function createService({
11
- workspaceSettingsRepository,
12
- workspaceInvitationsEnabled = true,
13
- roleCatalog = null
14
- } = {}) {
15
- if (!workspaceSettingsRepository) {
16
- throw new Error("workspaceSettingsService requires workspaceSettingsRepository.");
17
- }
18
- const resolvedRoleCatalog = roleCatalog && typeof roleCatalog === "object" ? roleCatalog : createWorkspaceRoleCatalog();
19
- const invitesAvailable = workspaceInvitationsEnabled === true;
20
-
21
- async function getWorkspaceSettings(workspace, options = {}) {
22
- const settingsRecord = await workspaceSettingsRepository.ensureForWorkspaceId(workspace.id, {
23
- ...options,
24
- workspace
25
- });
26
- const settings = {};
27
- for (const field of workspaceSettingsFields) {
28
- settings[field.key] = settingsRecord[field.key];
29
- }
30
- const invitesEnabled = invitesAvailable && settings.invitesEnabled !== false;
31
- settings.invitesEnabled = invitesEnabled;
32
- settings.invitesAvailable = invitesAvailable;
33
- settings.invitesEffective = invitesAvailable && invitesEnabled;
34
-
35
- return {
36
- workspace: {
37
- id: normalizeRecordId(workspace.id, { fallback: "" }),
38
- slug: String(workspace.slug || ""),
39
- ownerUserId: normalizeRecordId(workspace.ownerUserId, { fallback: "" })
40
- },
41
- settings,
42
- roleCatalog: cloneWorkspaceRoleCatalog(resolvedRoleCatalog)
43
- };
44
- }
45
-
46
- async function updateWorkspaceSettings(workspace, payload = {}, options = {}) {
47
- const source = normalizeObjectInput(payload);
48
- const settingsPatch = pickOwnProperties(source, resolveWorkspaceSettingsFieldKeys());
49
-
50
- if (Object.keys(settingsPatch).length > 0) {
51
- await workspaceSettingsRepository.updateSettingsByWorkspaceId(workspace.id, settingsPatch, {
52
- ...options,
53
- workspace
54
- });
55
- }
56
-
57
- return getWorkspaceSettings(workspace, options);
58
- }
59
-
60
- return Object.freeze({
61
- getWorkspaceSettings,
62
- updateWorkspaceSettings
63
- });
64
- }
65
-
66
- export { createService };
@@ -1,54 +0,0 @@
1
- import { normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
2
- import { resolveGlobalArrayRegistry } from "./resolveGlobalArrayRegistry.js";
3
-
4
- const consoleSettingsFields = resolveGlobalArrayRegistry("jskit.users-core.consoleSettingsFields");
5
-
6
- function defineField(field = {}) {
7
- const key = normalizeText(field.key);
8
- if (!key) {
9
- throw new TypeError("consoleSettingsFields.defineField requires field.key.");
10
- }
11
- if (consoleSettingsFields.some((entry) => entry.key === key)) {
12
- throw new Error(`consoleSettingsFields.defineField duplicate key: ${key}`);
13
- }
14
- if (!field.inputSchema || typeof field.inputSchema !== "object") {
15
- throw new TypeError(`consoleSettingsFields.defineField("${key}") requires inputSchema.`);
16
- }
17
- if (!field.outputSchema || typeof field.outputSchema !== "object") {
18
- throw new TypeError(`consoleSettingsFields.defineField("${key}") requires outputSchema.`);
19
- }
20
- const dbColumn = normalizeText(field.dbColumn);
21
- if (!dbColumn) {
22
- throw new TypeError(`consoleSettingsFields.defineField("${key}") requires dbColumn.`);
23
- }
24
- if (typeof field.normalizeInput !== "function") {
25
- throw new TypeError(`consoleSettingsFields.defineField("${key}") requires normalizeInput.`);
26
- }
27
- if (typeof field.normalizeOutput !== "function") {
28
- throw new TypeError(`consoleSettingsFields.defineField("${key}") requires normalizeOutput.`);
29
- }
30
- if (typeof field.resolveDefault !== "function") {
31
- throw new TypeError(`consoleSettingsFields.defineField("${key}") requires resolveDefault.`);
32
- }
33
-
34
- consoleSettingsFields.push({
35
- key,
36
- dbColumn,
37
- required: field.required !== false,
38
- inputSchema: field.inputSchema,
39
- outputSchema: field.outputSchema,
40
- normalizeInput: field.normalizeInput,
41
- normalizeOutput: field.normalizeOutput,
42
- resolveDefault: field.resolveDefault
43
- });
44
- }
45
-
46
- function resetConsoleSettingsFields() {
47
- consoleSettingsFields.splice(0, consoleSettingsFields.length);
48
- }
49
-
50
- export {
51
- defineField,
52
- resetConsoleSettingsFields,
53
- consoleSettingsFields
54
- };