@jskit-ai/workspaces-core 0.1.31 → 0.1.33

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 (62) hide show
  1. package/package.descriptor.mjs +11 -22
  2. package/package.json +11 -9
  3. package/src/server/WorkspacesCoreServiceProvider.js +22 -2
  4. package/src/server/common/repositories/workspaceInvitesRepository.js +233 -78
  5. package/src/server/common/repositories/workspaceMembershipsRepository.js +177 -86
  6. package/src/server/common/repositories/workspacesRepository.js +179 -86
  7. package/src/server/common/services/workspaceContextService.js +28 -26
  8. package/src/server/common/validators/routeParamsValidator.js +36 -53
  9. package/src/server/registerWorkspaceCore.js +9 -10
  10. package/src/server/registerWorkspaceRepositories.js +7 -3
  11. package/src/server/support/workspaceServerScopeSupport.js +1 -1
  12. package/src/server/workspaceBootstrapContributor.js +5 -14
  13. package/src/server/workspaceDirectory/bootWorkspaceDirectoryRoutes.js +54 -27
  14. package/src/server/workspaceDirectory/workspaceDirectoryActions.js +30 -24
  15. package/src/server/workspaceMembers/bootWorkspaceMembers.js +70 -32
  16. package/src/server/workspaceMembers/workspaceMembersActions.js +61 -27
  17. package/src/server/workspaceMembers/workspaceMembersService.js +43 -7
  18. package/src/server/workspacePendingInvitations/bootWorkspacePendingInvitations.js +28 -13
  19. package/src/server/workspacePendingInvitations/workspacePendingInvitationsActions.js +13 -15
  20. package/src/server/workspacePendingInvitations/workspacePendingInvitationsService.js +33 -10
  21. package/src/server/workspaceSettings/bootWorkspaceSettings.js +32 -13
  22. package/src/server/workspaceSettings/registerWorkspaceSettings.js +5 -1
  23. package/src/server/workspaceSettings/workspaceSettingsActions.js +18 -12
  24. package/src/server/workspaceSettings/workspaceSettingsRepository.js +104 -91
  25. package/src/server/workspaceSettings/workspaceSettingsService.js +5 -6
  26. package/src/shared/jsonApiTransports.js +79 -0
  27. package/src/shared/resources/workspaceInvitesResource.js +158 -0
  28. package/src/shared/resources/workspaceMembersResource.js +176 -311
  29. package/src/shared/resources/workspaceMembershipsResource.js +96 -0
  30. package/src/shared/resources/workspacePendingInvitationsResource.js +25 -72
  31. package/src/shared/resources/workspaceResource.js +113 -144
  32. package/src/shared/resources/workspaceRoleCatalogSchema.js +31 -0
  33. package/src/shared/resources/workspaceSettingsResource.js +276 -148
  34. package/test/repositoryContracts.test.js +16 -4
  35. package/test/resourcesCanonical.test.js +39 -16
  36. package/test/routeParamsValidator.test.js +37 -19
  37. package/test/usersRouteResources.test.js +27 -17
  38. package/test/workspaceActionContextContributor.test.js +1 -1
  39. package/test/workspaceInternalCrudResources.test.js +98 -0
  40. package/test/workspaceInvitesRepository.test.js +196 -148
  41. package/test/workspaceMembersResource.test.js +35 -0
  42. package/test/workspaceMembershipsRepository.test.js +155 -115
  43. package/test/workspacePendingInvitationsResource.test.js +18 -23
  44. package/test/workspacePendingInvitationsService.test.js +2 -1
  45. package/test/workspaceServerScopeSupport.test.js +77 -3
  46. package/test/workspaceService.test.js +26 -5
  47. package/test/workspaceSettingsActions.test.js +5 -7
  48. package/test/workspaceSettingsInternalResource.test.js +8 -0
  49. package/test/workspaceSettingsRepository.test.js +158 -123
  50. package/test/workspaceSettingsResource.test.js +51 -62
  51. package/test/workspaceSettingsService.test.js +0 -1
  52. package/test/workspacesRepository.test.js +318 -174
  53. package/test/workspacesRouteRequestInputValidator.test.js +25 -11
  54. package/src/server/common/resources/workspaceInvitesResource.js +0 -207
  55. package/src/server/common/resources/workspaceMembershipsResource.js +0 -154
  56. package/src/server/common/resources/workspacesResource.js +0 -170
  57. package/src/server/common/validators/authenticatedUserValidator.js +0 -43
  58. package/src/shared/resources/resolveGlobalArrayRegistry.js +0 -6
  59. package/src/shared/resources/workspaceSettingsFields.js +0 -65
  60. package/templates/packages/main/src/shared/resources/workspaceSettingsFields.js +0 -197
  61. package/test/settingsFieldRegistriesSingleton.test.js +0 -14
  62. package/test-support/registerDefaultSettingsFields.js +0 -1
@@ -1,147 +1,160 @@
1
1
  import {
2
- normalizeDbRecordId,
3
2
  normalizeRecordId,
4
- toIsoString,
5
3
  nowDb,
6
4
  isDuplicateEntryError,
7
5
  createWithTransaction
8
6
  } 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
7
  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
- });
8
+ createJsonApiInputRecord,
9
+ createJsonRestContext,
10
+ simplifyJsonApiDocument
11
+ } from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
12
+ import { resolveWorkspaceThemePalettes } from "../../shared/settings.js";
13
+
14
+ const RESOURCE_TYPE = "workspaceSettings";
15
+ const WORKSPACE_SETTINGS_PATCH_FIELDS = Object.freeze([
16
+ "lightPrimaryColor",
17
+ "lightSecondaryColor",
18
+ "lightSurfaceColor",
19
+ "lightSurfaceVariantColor",
20
+ "darkPrimaryColor",
21
+ "darkSecondaryColor",
22
+ "darkSurfaceColor",
23
+ "darkSurfaceVariantColor",
24
+ "invitesEnabled"
25
+ ]);
26
+
27
+ function pickPatchFields(source = {}) {
28
+ const patch = {};
29
+
30
+ for (const fieldName of WORKSPACE_SETTINGS_PATCH_FIELDS) {
31
+ if (Object.hasOwn(source, fieldName)) {
32
+ patch[fieldName] = source[fieldName];
33
+ }
30
34
  }
31
- return seed;
35
+
36
+ return patch;
37
+ }
38
+
39
+ function createDefaultWorkspaceSettingsCreatePayload(workspaceId) {
40
+ const palettes = resolveWorkspaceThemePalettes({});
41
+
42
+ return {
43
+ id: workspaceId,
44
+ lightPrimaryColor: palettes.light.color,
45
+ lightSecondaryColor: palettes.light.secondaryColor,
46
+ lightSurfaceColor: palettes.light.surfaceColor,
47
+ lightSurfaceVariantColor: palettes.light.surfaceVariantColor,
48
+ darkPrimaryColor: palettes.dark.color,
49
+ darkSecondaryColor: palettes.dark.secondaryColor,
50
+ darkSurfaceColor: palettes.dark.surfaceColor,
51
+ darkSurfaceVariantColor: palettes.dark.surfaceVariantColor,
52
+ invitesEnabled: true
53
+ };
32
54
  }
33
55
 
34
- function createRepository(knex, { defaultInvitesEnabled } = {}) {
56
+ function createRepository({ api, knex } = {}) {
57
+ if (!api?.resources?.workspaceSettings) {
58
+ throw new TypeError("workspaceSettingsRepository requires json-rest-api workspaceSettings resource.");
59
+ }
35
60
  if (typeof knex !== "function") {
36
61
  throw new TypeError("workspaceSettingsRepository requires knex.");
37
62
  }
38
63
  const withTransaction = createWithTransaction(knex);
39
64
 
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 column = field.repository.column;
50
- const rawValue = Object.hasOwn(row, column)
51
- ? row[column]
52
- : field.resolveDefault({
53
- defaultInvitesEnabled
54
- });
55
- settings[field.key] = field.normalizeOutput(rawValue, {
56
- defaultInvitesEnabled
57
- });
58
- }
59
-
60
- settings.createdAt = toIsoString(row.created_at);
61
- settings.updatedAt = toIsoString(row.updated_at);
62
- return settings;
65
+ async function queryFirst(filters = {}, options = {}) {
66
+ const result = await api.resources.workspaceSettings.query(
67
+ {
68
+ queryParams: {
69
+ filters
70
+ },
71
+ transaction: options?.trx || null,
72
+ simplified: false
73
+ },
74
+ createJsonRestContext(options?.context || null)
75
+ );
76
+
77
+ const rows = simplifyJsonApiDocument(result);
78
+ return Array.isArray(rows) ? rows[0] || null : null;
63
79
  }
64
80
 
65
81
  async function findByWorkspaceId(workspaceId, options = {}) {
66
- const client = options?.trx || knex;
67
82
  const normalizedWorkspaceId = normalizeRecordId(workspaceId, { fallback: null });
68
83
  if (!normalizedWorkspaceId) {
69
84
  return null;
70
85
  }
71
86
 
72
- const row = await client("workspace_settings").where({ workspace_id: normalizedWorkspaceId }).first();
73
- return mapRow(row);
87
+ return queryFirst({ id: normalizedWorkspaceId }, options);
74
88
  }
75
89
 
76
90
  async function ensureForWorkspaceId(workspaceId, options = {}) {
77
- const client = options?.trx || knex;
78
91
  const normalizedWorkspaceId = normalizeRecordId(workspaceId, { fallback: null });
79
92
  if (!normalizedWorkspaceId) {
80
93
  throw new TypeError("workspaceSettingsRepository.ensureForWorkspaceId requires a valid workspace id.");
81
94
  }
82
95
 
83
- const seed = resolveWorkspaceSettingsSeed(options?.workspace, {
84
- defaultInvitesEnabled
85
- });
86
- const existing = await findByWorkspaceId(normalizedWorkspaceId, { trx: client });
96
+ const existing = await findByWorkspaceId(normalizedWorkspaceId, options);
87
97
  if (existing) {
88
98
  return existing;
89
99
  }
90
100
 
91
101
  try {
92
- const insertPayload = {
93
- workspace_id: normalizedWorkspaceId,
94
- created_at: nowDb(),
95
- updated_at: nowDb()
96
- };
97
- for (const field of workspaceSettingsFields) {
98
- insertPayload[field.repository.column] = seed[field.key];
99
- }
100
- await client("workspace_settings").insert(insertPayload);
102
+ await api.resources.workspaceSettings.post(
103
+ {
104
+ inputRecord: createJsonApiInputRecord(
105
+ RESOURCE_TYPE,
106
+ createDefaultWorkspaceSettingsCreatePayload(normalizedWorkspaceId),
107
+ {
108
+ id: normalizedWorkspaceId
109
+ }
110
+ ),
111
+ transaction: options?.trx || null,
112
+ simplified: false
113
+ },
114
+ createJsonRestContext(options?.context || null)
115
+ );
101
116
  } catch (error) {
102
117
  if (!isDuplicateEntryError(error)) {
103
118
  throw error;
104
119
  }
105
120
  }
106
121
 
107
- return findByWorkspaceId(normalizedWorkspaceId, { trx: client });
122
+ return findByWorkspaceId(normalizedWorkspaceId, options);
108
123
  }
109
124
 
110
125
  async function updateSettingsByWorkspaceId(workspaceId, patch = {}, options = {}) {
111
- const client = options?.trx || knex;
112
126
  const normalizedWorkspaceId = normalizeRecordId(workspaceId, { fallback: null });
113
127
  if (!normalizedWorkspaceId) {
114
128
  throw new TypeError("workspaceSettingsRepository.updateSettingsByWorkspaceId requires a valid workspace id.");
115
129
  }
116
130
 
117
- const ensured = await ensureForWorkspaceId(normalizedWorkspaceId, {
118
- trx: client,
119
- workspace: options?.workspace
120
- });
121
- const source = normalizeObjectInput(patch);
122
- const settingsPatch = pickOwnProperties(source, resolveWorkspaceSettingsFieldKeys());
131
+ await ensureForWorkspaceId(normalizedWorkspaceId, options);
132
+ const source = patch && typeof patch === "object" && !Array.isArray(patch) ? patch : {};
133
+ const updatePayload = pickPatchFields(source);
123
134
 
124
- if (Object.keys(settingsPatch).length === 0) {
125
- return ensured;
126
- }
127
-
128
- const dbPatch = {
129
- updated_at: nowDb()
130
- };
131
-
132
- for (const field of workspaceSettingsFields) {
133
- if (!Object.hasOwn(settingsPatch, field.key)) {
134
- continue;
135
- }
136
- dbPatch[field.repository.column] = field.normalizeInput(settingsPatch[field.key], {
137
- payload: source
138
- });
135
+ if (Object.keys(updatePayload).length < 1) {
136
+ return findByWorkspaceId(normalizedWorkspaceId, options);
139
137
  }
140
138
 
141
- await client("workspace_settings").where({ workspace_id: normalizedWorkspaceId }).update({
142
- ...dbPatch
143
- });
144
- return findByWorkspaceId(normalizedWorkspaceId, { trx: client });
139
+ await api.resources.workspaceSettings.patch(
140
+ {
141
+ inputRecord: createJsonApiInputRecord(
142
+ RESOURCE_TYPE,
143
+ {
144
+ ...updatePayload,
145
+ updatedAt: nowDb()
146
+ },
147
+ {
148
+ id: normalizedWorkspaceId
149
+ }
150
+ ),
151
+ transaction: options?.trx || null,
152
+ simplified: false
153
+ },
154
+ createJsonRestContext(options?.context || null)
155
+ );
156
+
157
+ return findByWorkspaceId(normalizedWorkspaceId, options);
145
158
  }
146
159
 
147
160
  return Object.freeze({
@@ -2,9 +2,8 @@ import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
2
2
  import { normalizeObjectInput } from "@jskit-ai/kernel/shared/validators/inputNormalization";
3
3
  import { pickOwnProperties } from "@jskit-ai/kernel/shared/support";
4
4
  import {
5
- workspaceSettingsFields,
6
- resolveWorkspaceSettingsFieldKeys
7
- } from "../../shared/resources/workspaceSettingsFields.js";
5
+ WORKSPACE_SETTINGS_FIELD_KEYS
6
+ } from "../../shared/resources/workspaceSettingsResource.js";
8
7
  import { createWorkspaceRoleCatalog, cloneWorkspaceRoleCatalog } from "../../shared/roles.js";
9
8
 
10
9
  function createService({
@@ -24,8 +23,8 @@ function createService({
24
23
  workspace
25
24
  });
26
25
  const settings = {};
27
- for (const field of workspaceSettingsFields) {
28
- settings[field.key] = settingsRecord[field.key];
26
+ for (const fieldKey of WORKSPACE_SETTINGS_FIELD_KEYS) {
27
+ settings[fieldKey] = settingsRecord[fieldKey];
29
28
  }
30
29
  const invitesEnabled = invitesAvailable && settings.invitesEnabled !== false;
31
30
  settings.invitesEnabled = invitesEnabled;
@@ -45,7 +44,7 @@ function createService({
45
44
 
46
45
  async function updateWorkspaceSettings(workspace, payload = {}, options = {}) {
47
46
  const source = normalizeObjectInput(payload);
48
- const settingsPatch = pickOwnProperties(source, resolveWorkspaceSettingsFieldKeys());
47
+ const settingsPatch = pickOwnProperties(source, WORKSPACE_SETTINGS_FIELD_KEYS);
49
48
 
50
49
  if (Object.keys(settingsPatch).length > 0) {
51
50
  await workspaceSettingsRepository.updateSettingsByWorkspaceId(workspace.id, settingsPatch, {
@@ -0,0 +1,79 @@
1
+ import { deepFreeze } from "@jskit-ai/kernel/shared/support/deepFreeze";
2
+
3
+ const WORKSPACES_TRANSPORT = deepFreeze({
4
+ kind: "jsonapi-resource",
5
+ requestType: "workspaces",
6
+ responseType: "workspaces",
7
+ responseKind: "record"
8
+ });
9
+
10
+ const WORKSPACES_COLLECTION_TRANSPORT = deepFreeze({
11
+ kind: "jsonapi-resource",
12
+ responseType: "workspaces",
13
+ responseKind: "collection"
14
+ });
15
+
16
+ const WORKSPACE_SETTINGS_TRANSPORT = deepFreeze({
17
+ kind: "jsonapi-resource",
18
+ requestType: "workspace-settings",
19
+ responseType: "workspace-settings",
20
+ responseKind: "record"
21
+ });
22
+
23
+ const WORKSPACE_ROLE_CATALOG_TRANSPORT = deepFreeze({
24
+ kind: "jsonapi-resource",
25
+ responseType: "workspace-role-catalogs",
26
+ responseKind: "record"
27
+ });
28
+
29
+ const WORKSPACE_MEMBERS_TRANSPORT = deepFreeze({
30
+ kind: "jsonapi-resource",
31
+ responseType: "workspace-members",
32
+ responseKind: "record"
33
+ });
34
+
35
+ const WORKSPACE_MEMBER_ROLE_UPDATE_TRANSPORT = deepFreeze({
36
+ kind: "jsonapi-resource",
37
+ requestType: "workspace-member-role-updates",
38
+ responseType: "workspace-members",
39
+ responseKind: "record"
40
+ });
41
+
42
+ const WORKSPACE_INVITES_TRANSPORT = deepFreeze({
43
+ kind: "jsonapi-resource",
44
+ responseType: "workspace-invites",
45
+ responseKind: "record"
46
+ });
47
+
48
+ const WORKSPACE_INVITE_CREATE_TRANSPORT = deepFreeze({
49
+ kind: "jsonapi-resource",
50
+ requestType: "workspace-invite-creations",
51
+ responseType: "workspace-invites",
52
+ responseKind: "record"
53
+ });
54
+
55
+ const WORKSPACE_INVITE_REDEEM_TRANSPORT = deepFreeze({
56
+ kind: "jsonapi-resource",
57
+ requestType: "workspace-invitation-decisions",
58
+ responseType: "workspace-invitation-decisions",
59
+ responseKind: "record"
60
+ });
61
+
62
+ const WORKSPACE_PENDING_INVITATIONS_TRANSPORT = deepFreeze({
63
+ kind: "jsonapi-resource",
64
+ responseType: "workspace-pending-invitations",
65
+ responseKind: "record"
66
+ });
67
+
68
+ export {
69
+ WORKSPACES_TRANSPORT,
70
+ WORKSPACES_COLLECTION_TRANSPORT,
71
+ WORKSPACE_SETTINGS_TRANSPORT,
72
+ WORKSPACE_ROLE_CATALOG_TRANSPORT,
73
+ WORKSPACE_MEMBERS_TRANSPORT,
74
+ WORKSPACE_MEMBER_ROLE_UPDATE_TRANSPORT,
75
+ WORKSPACE_INVITES_TRANSPORT,
76
+ WORKSPACE_INVITE_CREATE_TRANSPORT,
77
+ WORKSPACE_INVITE_REDEEM_TRANSPORT,
78
+ WORKSPACE_PENDING_INVITATIONS_TRANSPORT
79
+ };
@@ -0,0 +1,158 @@
1
+ import { normalizeLowerText, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
2
+ import { defineCrudResource } from "@jskit-ai/resource-crud-core/shared/crudResource";
3
+
4
+ const workspaceInvitesResource = defineCrudResource({
5
+ namespace: "workspaceInvites",
6
+ tableName: "workspace_invites",
7
+ searchSchema: {
8
+ id: { type: "id", actualField: "id" }
9
+ },
10
+ schema: {
11
+ workspaceId: {
12
+ type: "id",
13
+ required: true,
14
+ search: true,
15
+ belongsTo: "workspaces",
16
+ as: "workspace",
17
+ operations: {
18
+ output: { required: true },
19
+ create: { required: true }
20
+ }
21
+ },
22
+ email: {
23
+ type: "string",
24
+ required: true,
25
+ max: 255,
26
+ search: true,
27
+ setter: (value) => normalizeLowerText(value),
28
+ operations: {
29
+ output: { required: true },
30
+ create: { required: true }
31
+ }
32
+ },
33
+ roleSid: {
34
+ type: "string",
35
+ required: true,
36
+ max: 64,
37
+ defaultTo: "member",
38
+ search: true,
39
+ storage: { column: "role_sid" },
40
+ setter: (value) => normalizeLowerText(value),
41
+ operations: {
42
+ output: { required: true },
43
+ create: { required: true },
44
+ patch: { required: false }
45
+ }
46
+ },
47
+ status: {
48
+ type: "string",
49
+ required: true,
50
+ max: 64,
51
+ defaultTo: "pending",
52
+ search: true,
53
+ setter: (value) => normalizeLowerText(value),
54
+ operations: {
55
+ output: { required: true },
56
+ create: { required: true },
57
+ patch: { required: false }
58
+ }
59
+ },
60
+ tokenHash: {
61
+ type: "string",
62
+ required: true,
63
+ max: 255,
64
+ search: true,
65
+ storage: { column: "token_hash" },
66
+ setter: (value) => normalizeText(value),
67
+ operations: {
68
+ output: { required: true },
69
+ create: { required: true }
70
+ }
71
+ },
72
+ invitedByUserId: {
73
+ type: "id",
74
+ nullable: true,
75
+ belongsTo: "userProfiles",
76
+ as: "invitedByUser",
77
+ storage: { column: "invited_by_user_id" },
78
+ operations: {
79
+ output: { required: false },
80
+ create: { required: false }
81
+ }
82
+ },
83
+ expiresAt: {
84
+ type: "dateTime",
85
+ nullable: true,
86
+ storage: {
87
+ column: "expires_at",
88
+ writeSerializer: "datetime-utc"
89
+ },
90
+ operations: {
91
+ output: { required: true },
92
+ create: { required: false },
93
+ patch: { required: false }
94
+ }
95
+ },
96
+ acceptedAt: {
97
+ type: "dateTime",
98
+ nullable: true,
99
+ storage: {
100
+ column: "accepted_at",
101
+ writeSerializer: "datetime-utc"
102
+ },
103
+ operations: {
104
+ output: { required: true },
105
+ create: { required: false },
106
+ patch: { required: false }
107
+ }
108
+ },
109
+ revokedAt: {
110
+ type: "dateTime",
111
+ nullable: true,
112
+ storage: {
113
+ column: "revoked_at",
114
+ writeSerializer: "datetime-utc"
115
+ },
116
+ operations: {
117
+ output: { required: true },
118
+ create: { required: false },
119
+ patch: { required: false }
120
+ }
121
+ },
122
+ createdAt: {
123
+ type: "dateTime",
124
+ default: "now()",
125
+ storage: {
126
+ column: "created_at",
127
+ writeSerializer: "datetime-utc"
128
+ },
129
+ operations: {
130
+ output: { required: true },
131
+ create: { required: false }
132
+ }
133
+ },
134
+ updatedAt: {
135
+ type: "dateTime",
136
+ default: "now()",
137
+ storage: {
138
+ column: "updated_at",
139
+ writeSerializer: "datetime-utc"
140
+ },
141
+ operations: {
142
+ output: { required: true },
143
+ create: { required: false },
144
+ patch: { required: false }
145
+ }
146
+ }
147
+ },
148
+ messages: {
149
+ validation: "Fix invalid values and try again.",
150
+ saveSuccess: "Record saved.",
151
+ saveError: "Unable to save record.",
152
+ deleteSuccess: "Record deleted.",
153
+ deleteError: "Unable to delete record."
154
+ },
155
+ crudOperations: ["list", "view", "create", "patch"]
156
+ });
157
+
158
+ export { workspaceInvitesResource };