@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,176 +0,0 @@
1
- import { Type } from "typebox";
2
- import { normalizeLowerText, normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
3
- import {
4
- normalizeObjectInput,
5
- createCursorListValidator,
6
- recordIdSchema,
7
- recordIdInputSchema
8
- } from "@jskit-ai/kernel/shared/validators";
9
- import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
10
-
11
- function normalizeWorkspaceAvatarUrl(value) {
12
- const avatarUrl = normalizeText(value);
13
- if (!avatarUrl) {
14
- return "";
15
- }
16
- if (!avatarUrl.startsWith("http://") && !avatarUrl.startsWith("https://")) {
17
- return null;
18
- }
19
- try {
20
- return new URL(avatarUrl).toString();
21
- } catch {
22
- return null;
23
- }
24
- }
25
-
26
- function normalizeWorkspaceInput(payload = {}) {
27
- const source = normalizeObjectInput(payload);
28
- const normalized = {};
29
-
30
- if (Object.hasOwn(source, "slug")) {
31
- normalized.slug = normalizeLowerText(source.slug);
32
- }
33
- if (Object.hasOwn(source, "name")) {
34
- normalized.name = normalizeText(source.name);
35
- }
36
- if (Object.hasOwn(source, "ownerUserId")) {
37
- normalized.ownerUserId = normalizeRecordId(source.ownerUserId, { fallback: "" });
38
- }
39
- if (Object.hasOwn(source, "avatarUrl")) {
40
- normalized.avatarUrl = normalizeWorkspaceAvatarUrl(source.avatarUrl);
41
- }
42
- if (Object.hasOwn(source, "isPersonal")) {
43
- normalized.isPersonal = source.isPersonal === true;
44
- }
45
-
46
- return normalized;
47
- }
48
-
49
- function normalizeWorkspaceOutput(payload = {}) {
50
- const source = normalizeObjectInput(payload);
51
-
52
- return {
53
- id: normalizeRecordId(source.id, { fallback: "" }),
54
- slug: normalizeLowerText(source.slug),
55
- name: normalizeText(source.name),
56
- ownerUserId: normalizeRecordId(source.ownerUserId, { fallback: "" }),
57
- avatarUrl: normalizeText(source.avatarUrl)
58
- };
59
- }
60
-
61
- function normalizeWorkspaceListItemOutput(payload = {}) {
62
- const source = normalizeObjectInput(payload);
63
-
64
- return {
65
- id: normalizeRecordId(source.id, { fallback: "" }),
66
- slug: normalizeLowerText(source.slug),
67
- name: normalizeText(source.name),
68
- avatarUrl: normalizeText(source.avatarUrl),
69
- roleSid: normalizeLowerText(source.roleSid || "member") || "member",
70
- isAccessible: source.isAccessible !== false
71
- };
72
- }
73
-
74
- const responseRecordSchema = Type.Object(
75
- {
76
- id: recordIdSchema,
77
- slug: Type.String({ minLength: 1 }),
78
- name: Type.String({ minLength: 1, maxLength: 160 }),
79
- ownerUserId: recordIdSchema,
80
- avatarUrl: Type.String()
81
- },
82
- { additionalProperties: false }
83
- );
84
-
85
- const listItemSchema = Type.Object(
86
- {
87
- id: recordIdSchema,
88
- slug: Type.String({ minLength: 1 }),
89
- name: Type.String({ minLength: 1, maxLength: 160 }),
90
- avatarUrl: Type.String(),
91
- roleSid: Type.String({ minLength: 1 }),
92
- isAccessible: Type.Boolean()
93
- },
94
- { additionalProperties: false }
95
- );
96
-
97
- const createRequestBodySchema = Type.Object(
98
- {
99
- name: Type.String({ minLength: 1, maxLength: 160 }),
100
- slug: Type.Optional(Type.String({ minLength: 1, maxLength: 120 })),
101
- ownerUserId: Type.Optional(recordIdInputSchema)
102
- },
103
- { additionalProperties: false }
104
- );
105
-
106
- const patchRequestBodySchema = Type.Object(
107
- {
108
- name: Type.Optional(Type.String({ minLength: 1, maxLength: 160 })),
109
- avatarUrl: Type.Optional(
110
- Type.String({
111
- pattern: "^(https?://.+)?$",
112
- messages: {
113
- pattern: "Workspace avatar URL must be a valid absolute URL (http:// or https://).",
114
- default: "Workspace avatar URL must be a valid absolute URL (http:// or https://)."
115
- }
116
- })
117
- )
118
- },
119
- { additionalProperties: false }
120
- );
121
-
122
- const responseRecordValidator = Object.freeze({
123
- schema: responseRecordSchema,
124
- normalize: normalizeWorkspaceOutput
125
- });
126
-
127
- const workspaceSummaryOutputValidator = Object.freeze({
128
- schema: listItemSchema,
129
- normalize: normalizeWorkspaceListItemOutput
130
- });
131
-
132
- const resource = {
133
- resource: "workspace",
134
- messages: {
135
- validation: "Fix invalid workspace values and try again.",
136
- saveSuccess: "Workspace updated.",
137
- saveError: "Unable to update workspace.",
138
- apiValidation: "Validation failed."
139
- },
140
- operations: {
141
- view: {
142
- method: "GET",
143
- outputValidator: responseRecordValidator
144
- },
145
- list: {
146
- method: "GET",
147
- outputValidator: createCursorListValidator(workspaceSummaryOutputValidator)
148
- },
149
- create: {
150
- method: "POST",
151
- bodyValidator: {
152
- schema: createRequestBodySchema,
153
- normalize: normalizeWorkspaceInput
154
- },
155
- outputValidator: responseRecordValidator
156
- },
157
- replace: {
158
- method: "PUT",
159
- bodyValidator: {
160
- schema: createRequestBodySchema,
161
- normalize: normalizeWorkspaceInput
162
- },
163
- outputValidator: responseRecordValidator
164
- },
165
- patch: {
166
- method: "PATCH",
167
- bodyValidator: {
168
- schema: patchRequestBodySchema,
169
- normalize: normalizeWorkspaceInput
170
- },
171
- outputValidator: responseRecordValidator
172
- }
173
- }
174
- };
175
-
176
- export { resource as workspaceResource };
@@ -1,59 +0,0 @@
1
- import { normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
2
- import { resolveGlobalArrayRegistry } from "./resolveGlobalArrayRegistry.js";
3
-
4
- const workspaceSettingsFields = resolveGlobalArrayRegistry("jskit.users-core.workspaceSettingsFields");
5
-
6
- function defineField(field = {}) {
7
- const key = normalizeText(field.key);
8
- if (!key) {
9
- throw new TypeError("workspaceSettingsFields.defineField requires field.key.");
10
- }
11
- if (workspaceSettingsFields.some((entry) => entry.key === key)) {
12
- throw new Error(`workspaceSettingsFields.defineField duplicate key: ${key}`);
13
- }
14
- if (!field.inputSchema || typeof field.inputSchema !== "object") {
15
- throw new TypeError(`workspaceSettingsFields.defineField("${key}") requires inputSchema.`);
16
- }
17
- if (!field.outputSchema || typeof field.outputSchema !== "object") {
18
- throw new TypeError(`workspaceSettingsFields.defineField("${key}") requires outputSchema.`);
19
- }
20
- const dbColumn = normalizeText(field.dbColumn);
21
- if (!dbColumn) {
22
- throw new TypeError(`workspaceSettingsFields.defineField("${key}") requires dbColumn.`);
23
- }
24
- if (typeof field.normalizeInput !== "function") {
25
- throw new TypeError(`workspaceSettingsFields.defineField("${key}") requires normalizeInput.`);
26
- }
27
- if (typeof field.normalizeOutput !== "function") {
28
- throw new TypeError(`workspaceSettingsFields.defineField("${key}") requires normalizeOutput.`);
29
- }
30
- if (typeof field.resolveDefault !== "function") {
31
- throw new TypeError(`workspaceSettingsFields.defineField("${key}") requires resolveDefault.`);
32
- }
33
-
34
- workspaceSettingsFields.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 resetWorkspaceSettingsFields() {
47
- workspaceSettingsFields.splice(0, workspaceSettingsFields.length);
48
- }
49
-
50
- function resolveWorkspaceSettingsFieldKeys() {
51
- return workspaceSettingsFields.map((field) => field.key);
52
- }
53
-
54
- export {
55
- defineField,
56
- resetWorkspaceSettingsFields,
57
- resolveWorkspaceSettingsFieldKeys,
58
- workspaceSettingsFields
59
- };
@@ -1,169 +0,0 @@
1
- import { Type } from "typebox";
2
- import { normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
3
- import {
4
- normalizeObjectInput,
5
- createCursorListValidator,
6
- normalizeSettingsFieldInput,
7
- recordIdSchema
8
- } from "@jskit-ai/kernel/shared/validators";
9
- import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
10
- import { workspaceSettingsFields } from "./workspaceSettingsFields.js";
11
- import { createWorkspaceRoleCatalog } from "../roles.js";
12
-
13
- function buildCreateBodySchema() {
14
- const properties = {};
15
- for (const field of workspaceSettingsFields) {
16
- properties[field.key] = field.required === false ? Type.Optional(field.inputSchema) : field.inputSchema;
17
- }
18
-
19
- return Type.Object(properties, {
20
- additionalProperties: false,
21
- messages: {
22
- additionalProperties: "Unexpected field.",
23
- default: "Invalid value."
24
- }
25
- });
26
- }
27
-
28
- function buildSettingsOutputSchema() {
29
- const properties = {};
30
- for (const field of workspaceSettingsFields) {
31
- properties[field.key] = field.outputSchema;
32
- }
33
- properties.invitesAvailable = Type.Boolean();
34
- properties.invitesEffective = Type.Boolean();
35
-
36
- return Type.Object(properties, { additionalProperties: false });
37
- }
38
-
39
- function buildResponseRecordSchema() {
40
- return Type.Object(
41
- {
42
- workspace: Type.Object(
43
- {
44
- id: recordIdSchema,
45
- slug: Type.String({ minLength: 1 }),
46
- ownerUserId: recordIdSchema
47
- },
48
- { additionalProperties: false }
49
- ),
50
- settings: buildSettingsOutputSchema(),
51
- roleCatalog: Type.Object(
52
- {
53
- collaborationEnabled: Type.Boolean(),
54
- defaultInviteRole: Type.String(),
55
- roles: Type.Array(Type.Object({}, { additionalProperties: true })),
56
- assignableRoleIds: Type.Array(Type.String({ minLength: 1 }))
57
- },
58
- { additionalProperties: true }
59
- )
60
- },
61
- { additionalProperties: false }
62
- );
63
- }
64
-
65
- function normalizeInput(payload = {}) {
66
- return normalizeSettingsFieldInput(payload, workspaceSettingsFields);
67
- }
68
-
69
- function normalizeOutput(payload = {}) {
70
- const source = normalizeObjectInput(payload);
71
- const workspace = normalizeObjectInput(source.workspace);
72
- const settings = normalizeObjectInput(source.settings);
73
- const normalizedSettings = {};
74
-
75
- for (const field of workspaceSettingsFields) {
76
- const rawValue = Object.hasOwn(settings, field.key)
77
- ? settings[field.key]
78
- : field.resolveDefault({
79
- workspace,
80
- settings
81
- });
82
- normalizedSettings[field.key] = field.normalizeOutput(rawValue, {
83
- workspace,
84
- settings
85
- });
86
- }
87
-
88
- const invitesEnabled = normalizedSettings.invitesEnabled !== false;
89
- const invitesAvailable = settings.invitesAvailable !== false;
90
- const invitesEffective =
91
- typeof settings.invitesEffective === "boolean" ? settings.invitesEffective : invitesEnabled;
92
- normalizedSettings.invitesEnabled = invitesEnabled;
93
- normalizedSettings.invitesAvailable = invitesAvailable;
94
- normalizedSettings.invitesEffective = invitesEffective;
95
- const roleCatalog = normalizeObjectInput(source.roleCatalog);
96
- const hasRoleCatalog =
97
- Array.isArray(roleCatalog.roles) &&
98
- roleCatalog.roles.length > 0 &&
99
- Array.isArray(roleCatalog.assignableRoleIds);
100
-
101
- return {
102
- workspace: {
103
- id: normalizeRecordId(workspace.id, { fallback: "" }),
104
- slug: normalizeText(workspace.slug),
105
- ownerUserId: normalizeRecordId(workspace.ownerUserId, { fallback: "" })
106
- },
107
- settings: normalizedSettings,
108
- roleCatalog: hasRoleCatalog ? roleCatalog : createWorkspaceRoleCatalog()
109
- };
110
- }
111
-
112
- const responseRecordValidator = Object.freeze({
113
- get schema() {
114
- return buildResponseRecordSchema();
115
- },
116
- normalize: normalizeOutput
117
- });
118
-
119
- const resource = {
120
- resource: "workspaceSettings",
121
- messages: {
122
- validation: "Fix invalid workspace settings values and try again.",
123
- saveSuccess: "Workspace settings updated.",
124
- saveError: "Unable to update workspace settings.",
125
- apiValidation: "Validation failed."
126
- },
127
- operations: {
128
- view: {
129
- method: "GET",
130
- outputValidator: responseRecordValidator
131
- },
132
- list: {
133
- method: "GET",
134
- outputValidator: createCursorListValidator(responseRecordValidator)
135
- },
136
- create: {
137
- method: "POST",
138
- bodyValidator: {
139
- get schema() {
140
- return buildCreateBodySchema();
141
- },
142
- normalize: normalizeInput
143
- },
144
- outputValidator: responseRecordValidator
145
- },
146
- replace: {
147
- method: "PUT",
148
- bodyValidator: {
149
- get schema() {
150
- return buildCreateBodySchema();
151
- },
152
- normalize: normalizeInput
153
- },
154
- outputValidator: responseRecordValidator
155
- },
156
- patch: {
157
- method: "PATCH",
158
- bodyValidator: {
159
- get schema() {
160
- return Type.Partial(buildCreateBodySchema(), { additionalProperties: false });
161
- },
162
- normalize: normalizeInput
163
- },
164
- outputValidator: responseRecordValidator
165
- }
166
- }
167
- };
168
-
169
- export { resource as workspaceSettingsResource };
@@ -1,161 +0,0 @@
1
- import {
2
- hasPermission,
3
- normalizePermissionList
4
- } from "@jskit-ai/kernel/shared/support";
5
-
6
- const OWNER_ROLE_ID = "owner";
7
- const ADMIN_ROLE_ID = "admin";
8
- const MEMBER_ROLE_ID = "member";
9
-
10
- function asRecord(value) {
11
- if (!value || typeof value !== "object" || Array.isArray(value)) {
12
- return {};
13
- }
14
- return value;
15
- }
16
-
17
- function normalizeRoleId(value) {
18
- return String(value || "")
19
- .trim()
20
- .toLowerCase();
21
- }
22
-
23
- function resolveInheritedRolePermissions(roleSid, configuredRoles = {}, seenRoleIds = new Set()) {
24
- if (seenRoleIds.has(roleSid)) {
25
- throw new TypeError(`roleCatalog role "${roleSid}" has circular inheritance.`);
26
- }
27
-
28
- const source = asRecord(configuredRoles[roleSid]);
29
- const inheritedRoleId = normalizeRoleId(source.inherits);
30
- const directPermissions = normalizePermissionList(source.permissions);
31
- if (!inheritedRoleId) {
32
- return directPermissions;
33
- }
34
-
35
- if (!Object.hasOwn(configuredRoles, inheritedRoleId)) {
36
- throw new TypeError(`roleCatalog role "${roleSid}" inherits unknown role "${inheritedRoleId}".`);
37
- }
38
-
39
- const nextSeenRoleIds = new Set(seenRoleIds);
40
- nextSeenRoleIds.add(roleSid);
41
-
42
- return normalizePermissionList([
43
- ...resolveInheritedRolePermissions(inheritedRoleId, configuredRoles, nextSeenRoleIds),
44
- ...directPermissions
45
- ]);
46
- }
47
-
48
- function createRoleDescriptor(roleSid, configuredDefinition, configuredRoles = {}) {
49
- const source = asRecord(configuredDefinition);
50
- const assignable = roleSid === OWNER_ROLE_ID ? false : source.assignable === true;
51
- const permissions = resolveInheritedRolePermissions(roleSid, configuredRoles);
52
-
53
- return Object.freeze({
54
- id: roleSid,
55
- assignable,
56
- permissions: Object.freeze([...permissions])
57
- });
58
- }
59
-
60
- function listConfiguredRoleIds(appConfig = {}) {
61
- const configuredRoles = normalizeConfiguredRoles(appConfig);
62
- return Object.freeze(Object.keys(configuredRoles));
63
- }
64
-
65
- function resolveConfiguredDefaultInviteRole(appConfig = {}) {
66
- return normalizeRoleId(appConfig?.roleCatalog?.workspace?.defaultInviteRole);
67
- }
68
-
69
- function normalizeConfiguredRoles(appConfig = {}) {
70
- const roleCatalog = asRecord(appConfig?.roleCatalog);
71
- const configuredRoles = asRecord(roleCatalog.roles);
72
- const normalizedRoles = {};
73
-
74
- for (const [roleSid, roleDefinition] of Object.entries(configuredRoles)) {
75
- const normalizedRoleId = normalizeRoleId(roleSid);
76
- if (!normalizedRoleId) {
77
- continue;
78
- }
79
- normalizedRoles[normalizedRoleId] = roleDefinition;
80
- }
81
-
82
- return normalizedRoles;
83
- }
84
-
85
- function createWorkspaceRoleCatalog(appConfig = {}) {
86
- const configuredRoles = normalizeConfiguredRoles(appConfig);
87
- const roleIds = listConfiguredRoleIds(appConfig);
88
- const roles = roleIds.map((roleSid) => createRoleDescriptor(roleSid, configuredRoles[roleSid], configuredRoles));
89
- const assignableRoleIds = roles.filter((role) => role.assignable).map((role) => role.id);
90
- const configuredDefaultInviteRole = resolveConfiguredDefaultInviteRole(appConfig);
91
- const defaultInviteRole = assignableRoleIds.includes(configuredDefaultInviteRole)
92
- ? configuredDefaultInviteRole
93
- : assignableRoleIds[0] || "";
94
-
95
- return Object.freeze({
96
- collaborationEnabled: assignableRoleIds.length > 0 && Boolean(defaultInviteRole),
97
- defaultInviteRole,
98
- roles: Object.freeze(
99
- roles.map((role) =>
100
- Object.freeze({
101
- id: role.id,
102
- assignable: role.assignable,
103
- permissions: Object.freeze([...role.permissions])
104
- })
105
- )
106
- ),
107
- assignableRoleIds: Object.freeze([...assignableRoleIds])
108
- });
109
- }
110
-
111
- function cloneWorkspaceRoleCatalog(roleCatalog = null) {
112
- const source = asRecord(roleCatalog);
113
-
114
- return {
115
- collaborationEnabled: source.collaborationEnabled === true,
116
- defaultInviteRole: String(source.defaultInviteRole || ""),
117
- roles: Array.isArray(source.roles)
118
- ? source.roles.map((role) => ({
119
- id: normalizeRoleId(role?.id),
120
- assignable: role?.assignable === true,
121
- permissions: Array.isArray(role?.permissions) ? [...role.permissions] : []
122
- }))
123
- : [],
124
- assignableRoleIds: Array.isArray(source.assignableRoleIds) ? [...source.assignableRoleIds] : []
125
- };
126
- }
127
-
128
- function listRoleDescriptors(appConfig = {}) {
129
- const roleCatalog = createWorkspaceRoleCatalog(appConfig);
130
- return roleCatalog.roles.map((role) => ({
131
- id: role.id,
132
- assignable: role.assignable,
133
- permissions: [...role.permissions]
134
- }));
135
- }
136
-
137
- function resolveRolePermissions(roleSid, appConfig = {}) {
138
- const normalizedRoleId = normalizeRoleId(roleSid);
139
- if (!normalizedRoleId) {
140
- return [];
141
- }
142
-
143
- const roleCatalog = createWorkspaceRoleCatalog(appConfig);
144
- const role = roleCatalog.roles.find((entry) => entry.id === normalizedRoleId);
145
- if (!role) {
146
- return [];
147
- }
148
-
149
- return [...role.permissions];
150
- }
151
-
152
- export {
153
- OWNER_ROLE_ID,
154
- ADMIN_ROLE_ID,
155
- MEMBER_ROLE_ID,
156
- resolveRolePermissions,
157
- listRoleDescriptors,
158
- createWorkspaceRoleCatalog,
159
- cloneWorkspaceRoleCatalog,
160
- hasPermission
161
- };
@@ -1,43 +0,0 @@
1
- import { normalizePathname } from "@jskit-ai/kernel/shared/surface/paths";
2
- import { splitPathQueryAndHash } from "@jskit-ai/kernel/shared/support";
3
-
4
- const USERS_PUBLIC_API_BASE_PATH = "/api";
5
- const USERS_WORKSPACE_API_BASE_PATH = "/api/w/:workspaceSlug";
6
-
7
- function normalizeApiRelativePath(relativePath = "/") {
8
- const { pathname, queryString, hash } = splitPathQueryAndHash(relativePath);
9
- const normalizedPath = normalizePathname(pathname || "/") || "/";
10
- const normalizedQueryString = String(queryString || "").trim().replace(/^\?+/, "");
11
- const normalizedHash = String(hash || "").trim();
12
- const querySuffix = normalizedQueryString ? `?${normalizedQueryString}` : "";
13
- return `${normalizedPath}${querySuffix}${normalizedHash}`;
14
- }
15
-
16
- function normalizeSurfaceWorkspaceRequirement(value = false) {
17
- return value === true;
18
- }
19
-
20
- function resolveApiBasePath({ surfaceRequiresWorkspace = false, relativePath = "/" } = {}) {
21
- const basePath = normalizeSurfaceWorkspaceRequirement(surfaceRequiresWorkspace)
22
- ? USERS_WORKSPACE_API_BASE_PATH
23
- : USERS_PUBLIC_API_BASE_PATH;
24
- const normalizedRelativePath = normalizeApiRelativePath(relativePath);
25
-
26
- if (normalizedRelativePath === "/") {
27
- return basePath;
28
- }
29
-
30
- if (normalizedRelativePath.startsWith("/?") || normalizedRelativePath.startsWith("/#")) {
31
- return `${basePath}${normalizedRelativePath.slice(1)}`;
32
- }
33
-
34
- return `${basePath}${normalizedRelativePath}`;
35
- }
36
-
37
- export {
38
- USERS_PUBLIC_API_BASE_PATH,
39
- USERS_WORKSPACE_API_BASE_PATH,
40
- normalizeApiRelativePath,
41
- normalizeSurfaceWorkspaceRequirement,
42
- resolveApiBasePath
43
- };
@@ -1,42 +0,0 @@
1
- import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
2
-
3
- const USERS_ROUTE_VISIBILITY_PUBLIC = "public";
4
- const USERS_ROUTE_VISIBILITY_USER = "user";
5
- const USERS_ROUTE_VISIBILITY_WORKSPACE = "workspace";
6
- const USERS_ROUTE_VISIBILITY_WORKSPACE_USER = "workspace_user";
7
-
8
- const USERS_ROUTE_VISIBILITY_LEVELS = Object.freeze([
9
- USERS_ROUTE_VISIBILITY_PUBLIC,
10
- USERS_ROUTE_VISIBILITY_USER,
11
- USERS_ROUTE_VISIBILITY_WORKSPACE,
12
- USERS_ROUTE_VISIBILITY_WORKSPACE_USER
13
- ]);
14
- const USERS_ROUTE_VISIBILITY_LEVEL_SET = new Set(USERS_ROUTE_VISIBILITY_LEVELS);
15
-
16
- function checkRouteVisibility(value, { context = "checkRouteVisibility" } = {}) {
17
- const normalized = normalizeText(value).toLowerCase();
18
- if (USERS_ROUTE_VISIBILITY_LEVEL_SET.has(normalized)) {
19
- return normalized;
20
- }
21
-
22
- throw new TypeError(
23
- `${context} must be one of: ${USERS_ROUTE_VISIBILITY_LEVELS.join(", ")}.`
24
- );
25
- }
26
-
27
- function isWorkspaceVisibility(visibility = "") {
28
- const normalized = checkRouteVisibility(visibility, {
29
- context: "isWorkspaceVisibility visibility"
30
- });
31
- return normalized === USERS_ROUTE_VISIBILITY_WORKSPACE || normalized === USERS_ROUTE_VISIBILITY_WORKSPACE_USER;
32
- }
33
-
34
- export {
35
- USERS_ROUTE_VISIBILITY_PUBLIC,
36
- USERS_ROUTE_VISIBILITY_USER,
37
- USERS_ROUTE_VISIBILITY_WORKSPACE,
38
- USERS_ROUTE_VISIBILITY_WORKSPACE_USER,
39
- USERS_ROUTE_VISIBILITY_LEVELS,
40
- checkRouteVisibility,
41
- isWorkspaceVisibility
42
- };