@jskit-ai/workspaces-core 0.1.31 → 0.1.32

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 (61) 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 +26 -24
  8. package/src/server/common/validators/routeParamsValidator.js +36 -53
  9. package/src/server/registerWorkspaceCore.js +6 -7
  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 +21 -3
  46. package/test/workspaceSettingsActions.test.js +5 -7
  47. package/test/workspaceSettingsInternalResource.test.js +8 -0
  48. package/test/workspaceSettingsRepository.test.js +158 -123
  49. package/test/workspaceSettingsResource.test.js +51 -62
  50. package/test/workspaceSettingsService.test.js +0 -1
  51. package/test/workspacesRepository.test.js +318 -174
  52. package/test/workspacesRouteRequestInputValidator.test.js +25 -11
  53. package/src/server/common/resources/workspaceInvitesResource.js +0 -207
  54. package/src/server/common/resources/workspaceMembershipsResource.js +0 -154
  55. package/src/server/common/resources/workspacesResource.js +0 -170
  56. package/src/server/common/validators/authenticatedUserValidator.js +0 -43
  57. package/src/shared/resources/resolveGlobalArrayRegistry.js +0 -6
  58. package/src/shared/resources/workspaceSettingsFields.js +0 -65
  59. package/templates/packages/main/src/shared/resources/workspaceSettingsFields.js +0 -197
  60. package/test/settingsFieldRegistriesSingleton.test.js +0 -14
  61. package/test-support/registerDefaultSettingsFields.js +0 -1
@@ -1,354 +1,219 @@
1
- import { Type } from "@fastify/type-provider-typebox";
2
- import { normalizeLowerText, normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
3
- import {
4
- normalizeObjectInput,
5
- recordIdSchema,
6
- recordIdInputSchema,
7
- nullableRecordIdSchema
8
- } from "@jskit-ai/kernel/shared/validators";
9
- import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
1
+ import { RECORD_ID_PATTERN } from "@jskit-ai/kernel/shared/validators";
2
+ import { createSchema } from "json-rest-schema";
3
+ import { defineResource } from "@jskit-ai/resource-core/shared/resource";
10
4
  import { createOperationMessages } from "../operationMessages.js";
11
- import { createWorkspaceRoleCatalog, OWNER_ROLE_ID } from "../roles.js";
5
+ import { workspaceRoleCatalogSchema } from "./workspaceRoleCatalogSchema.js";
6
+
7
+ const workspaceSummaryOutputSchema = createSchema({
8
+ id: { type: "string", required: true, minLength: 1, pattern: RECORD_ID_PATTERN },
9
+ slug: { type: "string", required: true, minLength: 1, maxLength: 120 },
10
+ name: { type: "string", required: true, minLength: 1, maxLength: 160 },
11
+ ownerUserId: { type: "string", required: true, minLength: 1, pattern: RECORD_ID_PATTERN },
12
+ avatarUrl: { type: "string", required: true }
13
+ });
12
14
 
13
- const workspaceSummaryOutputSchema = Type.Object(
14
- {
15
- id: recordIdSchema,
16
- slug: Type.String({ minLength: 1 }),
17
- name: Type.String({ minLength: 1 }),
18
- ownerUserId: recordIdSchema,
19
- avatarUrl: Type.String()
20
- },
21
- { additionalProperties: false }
22
- );
15
+ const memberSummaryOutputSchema = createSchema({
16
+ userId: { type: "string", required: true, minLength: 1, pattern: RECORD_ID_PATTERN },
17
+ roleSid: { type: "string", required: true, minLength: 1, maxLength: 64 },
18
+ status: { type: "string", required: true, minLength: 1, maxLength: 64 },
19
+ displayName: { type: "string", required: true },
20
+ email: { type: "string", required: true, minLength: 1, maxLength: 255 },
21
+ isOwner: { type: "boolean", required: true }
22
+ });
23
23
 
24
- const memberSummaryOutputSchema = Type.Object(
25
- {
26
- userId: recordIdSchema,
27
- roleSid: Type.String({ minLength: 1 }),
28
- status: Type.String({ minLength: 1 }),
29
- displayName: Type.String(),
30
- email: Type.String({ minLength: 1 }),
31
- isOwner: Type.Boolean()
32
- },
33
- { additionalProperties: false }
34
- );
24
+ const inviteSummaryOutputSchema = createSchema({
25
+ id: { type: "string", required: true, minLength: 1, pattern: RECORD_ID_PATTERN },
26
+ email: { type: "string", required: true, minLength: 3, maxLength: 255 },
27
+ roleSid: { type: "string", required: true, minLength: 1, maxLength: 64 },
28
+ status: { type: "string", required: true, minLength: 1, maxLength: 64 },
29
+ expiresAt: { type: "string", required: true, minLength: 1 },
30
+ invitedByUserId: { type: "string", required: false, nullable: true, minLength: 1, pattern: RECORD_ID_PATTERN }
31
+ });
35
32
 
36
- const inviteSummaryOutputSchema = Type.Object(
37
- {
38
- id: recordIdSchema,
39
- email: Type.String({ minLength: 3, format: "email" }),
40
- roleSid: Type.String({ minLength: 1 }),
41
- status: Type.String({ minLength: 1 }),
42
- expiresAt: Type.String({ minLength: 1 }),
43
- invitedByUserId: nullableRecordIdSchema
33
+ const workspaceMembersOutputSchema = createSchema({
34
+ workspace: {
35
+ type: "object",
36
+ required: true,
37
+ schema: workspaceSummaryOutputSchema
44
38
  },
45
- { additionalProperties: false }
46
- );
47
-
48
- function normalizeWorkspaceAdminSummary(workspace) {
49
- const source = normalizeObjectInput(workspace);
50
-
51
- return {
52
- id: normalizeRecordId(source.id, { fallback: "" }),
53
- slug: normalizeText(source.slug),
54
- name: normalizeText(source.name),
55
- ownerUserId: normalizeRecordId(source.ownerUserId, { fallback: "" }),
56
- avatarUrl: normalizeText(source.avatarUrl)
57
- };
58
- }
59
-
60
- function normalizeMemberSummary(member, workspace) {
61
- const source = normalizeObjectInput(member);
62
- const userId = normalizeRecordId(source.userId, { fallback: "" });
63
-
64
- return {
65
- userId,
66
- roleSid: normalizeLowerText(source.roleSid || "member") || "member",
67
- status: normalizeLowerText(source.status || "active") || "active",
68
- displayName: normalizeText(source.displayName),
69
- email: normalizeLowerText(source.email),
70
- isOwner: userId === workspace.ownerUserId || normalizeLowerText(source.roleSid) === OWNER_ROLE_ID
71
- };
72
- }
73
-
74
- function normalizeInviteSummary(invite) {
75
- const source = normalizeObjectInput(invite);
76
-
77
- return {
78
- id: normalizeRecordId(source.id, { fallback: "" }),
79
- email: normalizeLowerText(source.email),
80
- roleSid: normalizeLowerText(source.roleSid || "member") || "member",
81
- status: normalizeLowerText(source.status || "pending") || "pending",
82
- expiresAt: source.expiresAt,
83
- invitedByUserId: source.invitedByUserId == null ? null : normalizeRecordId(source.invitedByUserId, { fallback: null })
84
- };
85
- }
86
-
87
- function normalizeWorkspaceOutputEnvelope(
88
- payload = {},
89
- { itemsKey, normalizeItem, includeInviteTokenPreview = false } = {}
90
- ) {
91
- const source = normalizeObjectInput(payload);
92
- const workspace = normalizeWorkspaceAdminSummary(source.workspace);
93
- const items = Array.isArray(source[itemsKey]) ? source[itemsKey] : [];
94
- const roleCatalog = normalizeObjectInput(source.roleCatalog);
95
- const hasRoleCatalog =
96
- Array.isArray(roleCatalog.roles) &&
97
- roleCatalog.roles.length > 0 &&
98
- Array.isArray(roleCatalog.assignableRoleIds);
99
- const normalized = {
100
- workspace,
101
- [itemsKey]: items.map((item) => normalizeItem(item, workspace)),
102
- roleCatalog: hasRoleCatalog ? roleCatalog : createWorkspaceRoleCatalog()
103
- };
104
-
105
- if (includeInviteTokenPreview && Object.hasOwn(source, "inviteTokenPreview")) {
106
- normalized.inviteTokenPreview = normalizeText(source.inviteTokenPreview);
39
+ members: {
40
+ type: "array",
41
+ required: true,
42
+ items: memberSummaryOutputSchema
43
+ },
44
+ roleCatalog: {
45
+ type: "object",
46
+ required: true,
47
+ schema: workspaceRoleCatalogSchema
107
48
  }
108
-
109
- return normalized;
110
- }
111
-
112
- function normalizeWorkspaceMembersOutput(payload = {}) {
113
- return normalizeWorkspaceOutputEnvelope(payload, {
114
- itemsKey: "members",
115
- normalizeItem: normalizeMemberSummary
116
- });
117
- }
118
-
119
- function normalizeWorkspaceInvitesOutput(payload = {}) {
120
- return normalizeWorkspaceOutputEnvelope(payload, {
121
- itemsKey: "invites",
122
- normalizeItem: normalizeInviteSummary,
123
- includeInviteTokenPreview: true
124
- });
125
- }
126
-
127
- const workspaceRoleCatalogOutputValidator = Object.freeze({
128
- schema: Type.Object(
129
- {
130
- collaborationEnabled: Type.Boolean(),
131
- defaultInviteRole: Type.String(),
132
- roles: Type.Array(Type.Object({}, { additionalProperties: true })),
133
- assignableRoleIds: Type.Array(Type.String({ minLength: 1 }))
134
- },
135
- { additionalProperties: true }
136
- )
137
- });
138
-
139
- const workspaceMembersOutputValidator = Object.freeze({
140
- schema: Type.Object(
141
- {
142
- workspace: workspaceSummaryOutputSchema,
143
- members: Type.Array(memberSummaryOutputSchema),
144
- roleCatalog: workspaceRoleCatalogOutputValidator.schema
145
- },
146
- { additionalProperties: false }
147
- ),
148
- normalize: normalizeWorkspaceMembersOutput
149
49
  });
150
50
 
151
- const workspaceInvitesOutputValidator = Object.freeze({
152
- schema: Type.Object(
153
- {
154
- workspace: workspaceSummaryOutputSchema,
155
- invites: Type.Array(inviteSummaryOutputSchema),
156
- roleCatalog: workspaceRoleCatalogOutputValidator.schema,
157
- inviteTokenPreview: Type.Optional(Type.String({ minLength: 1 }))
158
- },
159
- { additionalProperties: false }
160
- ),
161
- normalize: normalizeWorkspaceInvitesOutput
51
+ const workspaceInvitesListOutputSchema = createSchema({
52
+ workspace: {
53
+ type: "object",
54
+ required: true,
55
+ schema: workspaceSummaryOutputSchema
56
+ },
57
+ invites: {
58
+ type: "array",
59
+ required: true,
60
+ items: inviteSummaryOutputSchema
61
+ },
62
+ roleCatalog: {
63
+ type: "object",
64
+ required: true,
65
+ schema: workspaceRoleCatalogSchema
66
+ }
162
67
  });
163
68
 
164
- const updateMemberRoleBodyValidator = Object.freeze({
165
- schema: Type.Object(
166
- {
167
- roleSid: Type.String({ minLength: 1 })
168
- },
169
- { additionalProperties: false }
170
- ),
171
- normalize(payload = {}) {
172
- const source = normalizeObjectInput(payload);
173
-
174
- return {
175
- roleSid: normalizeLowerText(source.roleSid)
176
- };
69
+ const workspaceInviteCreateOutputSchema = createSchema({
70
+ workspace: {
71
+ type: "object",
72
+ required: true,
73
+ schema: workspaceSummaryOutputSchema
74
+ },
75
+ invites: {
76
+ type: "array",
77
+ required: true,
78
+ items: inviteSummaryOutputSchema
79
+ },
80
+ roleCatalog: {
81
+ type: "object",
82
+ required: true,
83
+ schema: workspaceRoleCatalogSchema
84
+ },
85
+ inviteTokenPreview: {
86
+ type: "string",
87
+ required: true,
88
+ minLength: 1
89
+ },
90
+ createdInviteId: {
91
+ type: "string",
92
+ required: true,
93
+ minLength: 1,
94
+ pattern: RECORD_ID_PATTERN
177
95
  }
178
96
  });
179
97
 
180
- const updateMemberRoleInputValidator = Object.freeze({
181
- schema: Type.Object(
182
- {
183
- memberUserId: recordIdInputSchema,
184
- roleSid: Type.String({ minLength: 1 })
185
- },
186
- { additionalProperties: false }
187
- ),
188
- normalize(payload = {}) {
189
- const source = normalizeObjectInput(payload);
190
-
191
- return {
192
- memberUserId: normalizeRecordId(source.memberUserId, { fallback: "" }),
193
- roleSid: normalizeLowerText(source.roleSid)
194
- };
98
+ const workspaceInviteRevokeOutputSchema = createSchema({
99
+ workspace: {
100
+ type: "object",
101
+ required: true,
102
+ schema: workspaceSummaryOutputSchema
103
+ },
104
+ invites: {
105
+ type: "array",
106
+ required: true,
107
+ items: inviteSummaryOutputSchema
108
+ },
109
+ roleCatalog: {
110
+ type: "object",
111
+ required: true,
112
+ schema: workspaceRoleCatalogSchema
113
+ },
114
+ revokedInviteId: {
115
+ type: "string",
116
+ required: true,
117
+ minLength: 1,
118
+ pattern: RECORD_ID_PATTERN
195
119
  }
196
120
  });
197
121
 
198
- const removeMemberInputValidator = Object.freeze({
199
- schema: Type.Object(
200
- {
201
- memberUserId: recordIdInputSchema
202
- },
203
- { additionalProperties: false }
204
- ),
205
- normalize(payload = {}) {
206
- const source = normalizeObjectInput(payload);
207
-
208
- return {
209
- memberUserId: normalizeRecordId(source.memberUserId, { fallback: "" })
210
- };
211
- }
122
+ const updateMemberRoleBodySchema = createSchema({
123
+ roleSid: { type: "string", required: true, minLength: 1, lowercase: true }
212
124
  });
213
125
 
214
- const createInviteBodyValidator = Object.freeze({
215
- schema: Type.Object(
216
- {
217
- email: Type.String({ minLength: 3, format: "email" }),
218
- roleSid: Type.String({ minLength: 1 })
219
- },
220
- { additionalProperties: false }
221
- ),
222
- normalize(payload = {}) {
223
- const source = normalizeObjectInput(payload);
126
+ const updateMemberRoleInputSchema = createSchema({
127
+ memberUserId: { type: "id", required: true },
128
+ roleSid: { type: "string", required: true, minLength: 1, lowercase: true }
129
+ });
224
130
 
225
- return {
226
- email: normalizeLowerText(source.email),
227
- roleSid: normalizeLowerText(source.roleSid || "member") || "member"
228
- };
229
- }
131
+ const removeMemberInputSchema = createSchema({
132
+ memberUserId: { type: "id", required: true }
230
133
  });
231
134
 
232
- const revokeInviteInputValidator = Object.freeze({
233
- schema: Type.Object(
234
- {
235
- inviteId: recordIdInputSchema
236
- },
237
- { additionalProperties: false }
238
- ),
239
- normalize(payload = {}) {
240
- const source = normalizeObjectInput(payload);
135
+ const createInviteBodySchema = createSchema({
136
+ email: { type: "string", required: true, minLength: 3, lowercase: true },
137
+ roleSid: { type: "string", required: true, minLength: 1, lowercase: true }
138
+ });
241
139
 
242
- return {
243
- inviteId: normalizeRecordId(source.inviteId, { fallback: "" })
244
- };
245
- }
140
+ const revokeInviteInputSchema = createSchema({
141
+ inviteId: { type: "id", required: true }
246
142
  });
247
143
 
248
- const redeemInviteBodyValidator = Object.freeze({
249
- schema: Type.Object(
250
- {
251
- token: Type.String({
252
- minLength: 1,
253
- messages: {
254
- required: "Invite token is required.",
255
- minLength: "Invite token is required.",
256
- default: "Invite token is invalid."
257
- }
258
- }),
259
- decision: Type.Union([Type.Literal("accept"), Type.Literal("refuse")], {
260
- messages: {
261
- required: "Decision is required.",
262
- default: "Decision must be accept or refuse."
263
- }
264
- })
265
- },
266
- {
267
- additionalProperties: false,
268
- messages: {
269
- additionalProperties: "Unexpected field."
270
- }
144
+ const redeemInviteBodySchema = createSchema({
145
+ token: {
146
+ type: "string",
147
+ required: true,
148
+ minLength: 1,
149
+ messages: {
150
+ required: "Invite token is required.",
151
+ minLength: "Invite token is required.",
152
+ default: "Invite token is invalid."
153
+ }
154
+ },
155
+ decision: {
156
+ type: "string",
157
+ required: true,
158
+ enum: ["accept", "refuse"],
159
+ messages: {
160
+ required: "Decision is required.",
161
+ default: "Decision must be accept or refuse."
271
162
  }
272
- ),
273
- normalize(payload = {}) {
274
- const source = normalizeObjectInput(payload);
275
-
276
- return {
277
- token: normalizeText(source.token),
278
- decision: normalizeLowerText(source.decision)
279
- };
280
163
  }
281
164
  });
282
165
 
283
- const redeemInviteOutputValidator = Object.freeze({
284
- schema: Type.Object(
285
- {
286
- decision: Type.Union([Type.Literal("accepted"), Type.Literal("refused")])
287
- },
288
- { additionalProperties: false }
289
- ),
290
- normalize(payload = {}) {
291
- const source = normalizeObjectInput(payload);
292
-
293
- return {
294
- decision: normalizeLowerText(source.decision)
295
- };
166
+ const redeemInviteOutputSchema = createSchema({
167
+ decision: {
168
+ type: "string",
169
+ required: true,
170
+ enum: ["accepted", "refused"]
296
171
  }
297
172
  });
298
173
 
299
- const WORKSPACE_MEMBERS_MESSAGES = createOperationMessages();
300
-
301
- const workspaceMembersResource = Object.freeze({
174
+ const workspaceMembersResource = defineResource({
302
175
  namespace: "workspaceMembers",
303
- messages: WORKSPACE_MEMBERS_MESSAGES,
304
- operations: Object.freeze({
305
- rolesList: Object.freeze({
176
+ messages: createOperationMessages(),
177
+ operations: {
178
+ rolesList: {
306
179
  method: "GET",
307
- messages: WORKSPACE_MEMBERS_MESSAGES,
308
- outputValidator: workspaceRoleCatalogOutputValidator
309
- }),
310
- membersList: Object.freeze({
180
+ output: workspaceRoleCatalogSchema
181
+ },
182
+ membersList: {
311
183
  method: "GET",
312
- messages: WORKSPACE_MEMBERS_MESSAGES,
313
- outputValidator: workspaceMembersOutputValidator
314
- }),
315
- updateMemberRole: Object.freeze({
184
+ output: workspaceMembersOutputSchema
185
+ },
186
+ updateMemberRole: {
316
187
  method: "PATCH",
317
- messages: WORKSPACE_MEMBERS_MESSAGES,
318
- bodyValidator: updateMemberRoleBodyValidator,
319
- inputValidator: updateMemberRoleInputValidator,
320
- outputValidator: workspaceMembersOutputValidator
321
- }),
322
- removeMember: Object.freeze({
188
+ body: updateMemberRoleBodySchema,
189
+ input: updateMemberRoleInputSchema,
190
+ output: workspaceMembersOutputSchema
191
+ },
192
+ removeMember: {
323
193
  method: "DELETE",
324
- messages: WORKSPACE_MEMBERS_MESSAGES,
325
- inputValidator: removeMemberInputValidator,
326
- outputValidator: workspaceMembersOutputValidator
327
- }),
328
- invitesList: Object.freeze({
194
+ input: removeMemberInputSchema,
195
+ output: workspaceMembersOutputSchema
196
+ },
197
+ invitesList: {
329
198
  method: "GET",
330
- messages: WORKSPACE_MEMBERS_MESSAGES,
331
- outputValidator: workspaceInvitesOutputValidator
332
- }),
333
- createInvite: Object.freeze({
199
+ output: workspaceInvitesListOutputSchema
200
+ },
201
+ createInvite: {
334
202
  method: "POST",
335
- messages: WORKSPACE_MEMBERS_MESSAGES,
336
- bodyValidator: createInviteBodyValidator,
337
- outputValidator: workspaceInvitesOutputValidator
338
- }),
339
- revokeInvite: Object.freeze({
203
+ body: createInviteBodySchema,
204
+ output: workspaceInviteCreateOutputSchema
205
+ },
206
+ revokeInvite: {
340
207
  method: "DELETE",
341
- messages: WORKSPACE_MEMBERS_MESSAGES,
342
- inputValidator: revokeInviteInputValidator,
343
- outputValidator: workspaceInvitesOutputValidator
344
- }),
345
- redeemInvite: Object.freeze({
208
+ input: revokeInviteInputSchema,
209
+ output: workspaceInviteRevokeOutputSchema
210
+ },
211
+ redeemInvite: {
346
212
  method: "POST",
347
- messages: WORKSPACE_MEMBERS_MESSAGES,
348
- bodyValidator: redeemInviteBodyValidator,
349
- outputValidator: redeemInviteOutputValidator
350
- })
351
- })
213
+ body: redeemInviteBodySchema,
214
+ output: redeemInviteOutputSchema
215
+ }
216
+ }
352
217
  });
353
218
 
354
219
  export { workspaceMembersResource };
@@ -0,0 +1,96 @@
1
+ import { normalizeLowerText } from "@jskit-ai/kernel/shared/support/normalize";
2
+ import { defineCrudResource } from "@jskit-ai/resource-crud-core/shared/crudResource";
3
+
4
+ const workspaceMembershipsResource = defineCrudResource({
5
+ namespace: "workspaceMemberships",
6
+ tableName: "workspace_memberships",
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
+ userId: {
23
+ type: "id",
24
+ required: true,
25
+ search: true,
26
+ belongsTo: "userProfiles",
27
+ as: "user",
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: 32,
51
+ defaultTo: "active",
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
+ createdAt: {
61
+ type: "dateTime",
62
+ default: "now()",
63
+ storage: {
64
+ column: "created_at",
65
+ writeSerializer: "datetime-utc"
66
+ },
67
+ operations: {
68
+ output: { required: true },
69
+ create: { required: false }
70
+ }
71
+ },
72
+ updatedAt: {
73
+ type: "dateTime",
74
+ default: "now()",
75
+ storage: {
76
+ column: "updated_at",
77
+ writeSerializer: "datetime-utc"
78
+ },
79
+ operations: {
80
+ output: { required: true },
81
+ create: { required: false },
82
+ patch: { required: false }
83
+ }
84
+ }
85
+ },
86
+ messages: {
87
+ validation: "Fix invalid values and try again.",
88
+ saveSuccess: "Record saved.",
89
+ saveError: "Unable to save record.",
90
+ deleteSuccess: "Record deleted.",
91
+ deleteError: "Unable to delete record."
92
+ },
93
+ crudOperations: ["list", "view", "create", "patch"]
94
+ });
95
+
96
+ export { workspaceMembershipsResource };