@contractspec/lib.identity-rbac 1.57.0 → 1.58.0

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 (82) hide show
  1. package/dist/browser/contracts/index.js +1045 -0
  2. package/dist/browser/contracts/organization.js +655 -0
  3. package/dist/browser/contracts/rbac.js +599 -0
  4. package/dist/browser/contracts/user.js +235 -0
  5. package/dist/browser/entities/index.js +464 -0
  6. package/dist/browser/entities/organization.js +150 -0
  7. package/dist/browser/entities/rbac.js +124 -0
  8. package/dist/browser/entities/user.js +168 -0
  9. package/dist/browser/events.js +374 -0
  10. package/dist/browser/identity-rbac.capability.js +28 -0
  11. package/dist/browser/identity-rbac.feature.js +67 -0
  12. package/dist/browser/index.js +2099 -0
  13. package/dist/browser/policies/engine.js +154 -0
  14. package/dist/browser/policies/index.js +154 -0
  15. package/dist/contracts/index.d.ts +4 -4
  16. package/dist/contracts/index.d.ts.map +1 -0
  17. package/dist/contracts/index.js +1045 -4
  18. package/dist/contracts/organization.d.ts +758 -764
  19. package/dist/contracts/organization.d.ts.map +1 -1
  20. package/dist/contracts/organization.js +653 -602
  21. package/dist/contracts/rbac.d.ts +517 -523
  22. package/dist/contracts/rbac.d.ts.map +1 -1
  23. package/dist/contracts/rbac.js +597 -481
  24. package/dist/contracts/user.d.ts +513 -519
  25. package/dist/contracts/user.d.ts.map +1 -1
  26. package/dist/contracts/user.js +222 -319
  27. package/dist/entities/index.d.ts +164 -169
  28. package/dist/entities/index.d.ts.map +1 -1
  29. package/dist/entities/index.js +462 -33
  30. package/dist/entities/organization.d.ts +58 -63
  31. package/dist/entities/organization.d.ts.map +1 -1
  32. package/dist/entities/organization.js +145 -145
  33. package/dist/entities/rbac.d.ts +62 -67
  34. package/dist/entities/rbac.d.ts.map +1 -1
  35. package/dist/entities/rbac.js +119 -132
  36. package/dist/entities/user.d.ts +66 -71
  37. package/dist/entities/user.d.ts.map +1 -1
  38. package/dist/entities/user.js +164 -189
  39. package/dist/events.d.ts +537 -543
  40. package/dist/events.d.ts.map +1 -1
  41. package/dist/events.js +343 -651
  42. package/dist/identity-rbac.capability.d.ts +2 -7
  43. package/dist/identity-rbac.capability.d.ts.map +1 -1
  44. package/dist/identity-rbac.capability.js +29 -29
  45. package/dist/identity-rbac.feature.d.ts +1 -6
  46. package/dist/identity-rbac.feature.d.ts.map +1 -1
  47. package/dist/identity-rbac.feature.js +66 -193
  48. package/dist/index.d.ts +6 -12
  49. package/dist/index.d.ts.map +1 -0
  50. package/dist/index.js +2100 -14
  51. package/dist/node/contracts/index.js +1045 -0
  52. package/dist/node/contracts/organization.js +655 -0
  53. package/dist/node/contracts/rbac.js +599 -0
  54. package/dist/node/contracts/user.js +235 -0
  55. package/dist/node/entities/index.js +464 -0
  56. package/dist/node/entities/organization.js +150 -0
  57. package/dist/node/entities/rbac.js +124 -0
  58. package/dist/node/entities/user.js +168 -0
  59. package/dist/node/events.js +374 -0
  60. package/dist/node/identity-rbac.capability.js +28 -0
  61. package/dist/node/identity-rbac.feature.js +67 -0
  62. package/dist/node/index.js +2099 -0
  63. package/dist/node/policies/engine.js +154 -0
  64. package/dist/node/policies/index.js +154 -0
  65. package/dist/policies/engine.d.ts +98 -101
  66. package/dist/policies/engine.d.ts.map +1 -1
  67. package/dist/policies/engine.js +151 -164
  68. package/dist/policies/index.d.ts +2 -2
  69. package/dist/policies/index.d.ts.map +1 -0
  70. package/dist/policies/index.js +154 -2
  71. package/package.json +149 -40
  72. package/dist/contracts/organization.js.map +0 -1
  73. package/dist/contracts/rbac.js.map +0 -1
  74. package/dist/contracts/user.js.map +0 -1
  75. package/dist/entities/index.js.map +0 -1
  76. package/dist/entities/organization.js.map +0 -1
  77. package/dist/entities/rbac.js.map +0 -1
  78. package/dist/entities/user.js.map +0 -1
  79. package/dist/events.js.map +0 -1
  80. package/dist/identity-rbac.capability.js.map +0 -1
  81. package/dist/identity-rbac.feature.js.map +0 -1
  82. package/dist/policies/engine.js.map +0 -1
@@ -0,0 +1,2099 @@
1
+ // src/contracts/user.ts
2
+ import { SchemaModel, ScalarTypeEnum } from "@contractspec/lib.schema";
3
+ import { defineCommand, defineQuery } from "@contractspec/lib.contracts";
4
+ var OWNERS = ["platform.identity-rbac"];
5
+ var UserProfileModel = new SchemaModel({
6
+ name: "UserProfile",
7
+ description: "User profile information",
8
+ fields: {
9
+ id: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
10
+ email: { type: ScalarTypeEnum.EmailAddress(), isOptional: false },
11
+ emailVerified: { type: ScalarTypeEnum.Boolean(), isOptional: false },
12
+ name: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
13
+ firstName: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
14
+ lastName: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
15
+ locale: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
16
+ timezone: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
17
+ imageUrl: { type: ScalarTypeEnum.URL(), isOptional: true },
18
+ role: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
19
+ onboardingCompleted: { type: ScalarTypeEnum.Boolean(), isOptional: false },
20
+ createdAt: { type: ScalarTypeEnum.DateTime(), isOptional: false }
21
+ }
22
+ });
23
+ var CreateUserInputModel = new SchemaModel({
24
+ name: "CreateUserInput",
25
+ description: "Input for creating a new user",
26
+ fields: {
27
+ email: { type: ScalarTypeEnum.EmailAddress(), isOptional: false },
28
+ name: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
29
+ firstName: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
30
+ lastName: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
31
+ password: { type: ScalarTypeEnum.String_unsecure(), isOptional: true }
32
+ }
33
+ });
34
+ var UpdateUserInputModel = new SchemaModel({
35
+ name: "UpdateUserInput",
36
+ description: "Input for updating a user profile",
37
+ fields: {
38
+ name: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
39
+ firstName: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
40
+ lastName: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
41
+ locale: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
42
+ timezone: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
43
+ imageUrl: { type: ScalarTypeEnum.URL(), isOptional: true }
44
+ }
45
+ });
46
+ var DeleteUserInputModel = new SchemaModel({
47
+ name: "DeleteUserInput",
48
+ description: "Input for deleting a user",
49
+ fields: {
50
+ confirmEmail: { type: ScalarTypeEnum.EmailAddress(), isOptional: false }
51
+ }
52
+ });
53
+ var SuccessResultModel = new SchemaModel({
54
+ name: "SuccessResult",
55
+ description: "Simple success result",
56
+ fields: {
57
+ success: { type: ScalarTypeEnum.Boolean(), isOptional: false }
58
+ }
59
+ });
60
+ var UserDeletedPayloadModel = new SchemaModel({
61
+ name: "UserDeletedPayload",
62
+ description: "Payload for user deleted event",
63
+ fields: {
64
+ userId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false }
65
+ }
66
+ });
67
+ var ListUsersInputModel = new SchemaModel({
68
+ name: "ListUsersInput",
69
+ description: "Input for listing users",
70
+ fields: {
71
+ limit: { type: ScalarTypeEnum.Int_unsecure(), isOptional: true },
72
+ offset: { type: ScalarTypeEnum.Int_unsecure(), isOptional: true },
73
+ search: { type: ScalarTypeEnum.String_unsecure(), isOptional: true }
74
+ }
75
+ });
76
+ var ListUsersOutputModel = new SchemaModel({
77
+ name: "ListUsersOutput",
78
+ description: "Output for listing users",
79
+ fields: {
80
+ users: { type: UserProfileModel, isOptional: false, isArray: true },
81
+ total: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false }
82
+ }
83
+ });
84
+ var CreateUserContract = defineCommand({
85
+ meta: {
86
+ key: "identity.user.create",
87
+ version: "1.0.0",
88
+ stability: "stable",
89
+ owners: [...OWNERS],
90
+ tags: ["identity", "user", "create"],
91
+ description: "Create a new user account.",
92
+ goal: "Register a new user in the system.",
93
+ context: "Used during signup flows. May trigger email verification."
94
+ },
95
+ io: {
96
+ input: CreateUserInputModel,
97
+ output: UserProfileModel,
98
+ errors: {
99
+ EMAIL_EXISTS: {
100
+ description: "A user with this email already exists",
101
+ http: 409,
102
+ gqlCode: "EMAIL_EXISTS",
103
+ when: "Email is already registered"
104
+ }
105
+ }
106
+ },
107
+ policy: {
108
+ auth: "anonymous"
109
+ },
110
+ sideEffects: {
111
+ emits: [
112
+ {
113
+ key: "user.created",
114
+ version: "1.0.0",
115
+ when: "User is successfully created",
116
+ payload: UserProfileModel
117
+ }
118
+ ],
119
+ audit: ["user.created"]
120
+ }
121
+ });
122
+ var GetCurrentUserContract = defineQuery({
123
+ meta: {
124
+ key: "identity.user.me",
125
+ version: "1.0.0",
126
+ stability: "stable",
127
+ owners: [...OWNERS],
128
+ tags: ["identity", "user", "profile"],
129
+ description: "Get the current authenticated user profile.",
130
+ goal: "Retrieve user profile for the authenticated session.",
131
+ context: "Called on app load and after profile updates."
132
+ },
133
+ io: {
134
+ input: null,
135
+ output: UserProfileModel
136
+ },
137
+ policy: {
138
+ auth: "user"
139
+ }
140
+ });
141
+ var UpdateUserContract = defineCommand({
142
+ meta: {
143
+ key: "identity.user.update",
144
+ version: "1.0.0",
145
+ stability: "stable",
146
+ owners: [...OWNERS],
147
+ tags: ["identity", "user", "update"],
148
+ description: "Update user profile information.",
149
+ goal: "Allow users to update their profile.",
150
+ context: "Self-service profile updates."
151
+ },
152
+ io: {
153
+ input: UpdateUserInputModel,
154
+ output: UserProfileModel
155
+ },
156
+ policy: {
157
+ auth: "user"
158
+ },
159
+ sideEffects: {
160
+ emits: [
161
+ {
162
+ key: "user.updated",
163
+ version: "1.0.0",
164
+ when: "User profile is updated",
165
+ payload: UserProfileModel
166
+ }
167
+ ],
168
+ audit: ["user.updated"]
169
+ }
170
+ });
171
+ var DeleteUserContract = defineCommand({
172
+ meta: {
173
+ key: "identity.user.delete",
174
+ version: "1.0.0",
175
+ stability: "stable",
176
+ owners: [...OWNERS],
177
+ tags: ["identity", "user", "delete"],
178
+ description: "Delete user account and all associated data.",
179
+ goal: "Allow users to delete their account (GDPR compliance).",
180
+ context: "Self-service account deletion. Cascades to memberships, sessions, etc."
181
+ },
182
+ io: {
183
+ input: DeleteUserInputModel,
184
+ output: SuccessResultModel
185
+ },
186
+ policy: {
187
+ auth: "user",
188
+ escalate: "human_review"
189
+ },
190
+ sideEffects: {
191
+ emits: [
192
+ {
193
+ key: "user.deleted",
194
+ version: "1.0.0",
195
+ when: "User account is deleted",
196
+ payload: UserDeletedPayloadModel
197
+ }
198
+ ],
199
+ audit: ["user.deleted"]
200
+ }
201
+ });
202
+ var ListUsersContract = defineQuery({
203
+ meta: {
204
+ key: "identity.user.list",
205
+ version: "1.0.0",
206
+ stability: "stable",
207
+ owners: [...OWNERS],
208
+ tags: ["identity", "user", "admin", "list"],
209
+ description: "List all users (admin only).",
210
+ goal: "Allow admins to browse and manage users.",
211
+ context: "Admin dashboard user management."
212
+ },
213
+ io: {
214
+ input: ListUsersInputModel,
215
+ output: ListUsersOutputModel
216
+ },
217
+ policy: {
218
+ auth: "admin"
219
+ }
220
+ });
221
+
222
+ // src/contracts/organization.ts
223
+ import { ScalarTypeEnum as ScalarTypeEnum2, SchemaModel as SchemaModel2 } from "@contractspec/lib.schema";
224
+ import { defineCommand as defineCommand2, defineQuery as defineQuery2 } from "@contractspec/lib.contracts";
225
+ var OWNERS2 = ["platform.identity-rbac"];
226
+ var OrganizationModel = new SchemaModel2({
227
+ name: "Organization",
228
+ description: "Organization details",
229
+ fields: {
230
+ id: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
231
+ name: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
232
+ slug: { type: ScalarTypeEnum2.String_unsecure(), isOptional: true },
233
+ logo: { type: ScalarTypeEnum2.URL(), isOptional: true },
234
+ description: { type: ScalarTypeEnum2.String_unsecure(), isOptional: true },
235
+ type: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
236
+ onboardingCompleted: { type: ScalarTypeEnum2.Boolean(), isOptional: false },
237
+ createdAt: { type: ScalarTypeEnum2.DateTime(), isOptional: false }
238
+ }
239
+ });
240
+ var MemberUserModel = new SchemaModel2({
241
+ name: "MemberUser",
242
+ description: "Basic user info within a member",
243
+ fields: {
244
+ id: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
245
+ email: { type: ScalarTypeEnum2.EmailAddress(), isOptional: false },
246
+ name: { type: ScalarTypeEnum2.String_unsecure(), isOptional: true }
247
+ }
248
+ });
249
+ var MemberModel = new SchemaModel2({
250
+ name: "Member",
251
+ description: "Organization member",
252
+ fields: {
253
+ id: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
254
+ userId: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
255
+ organizationId: {
256
+ type: ScalarTypeEnum2.String_unsecure(),
257
+ isOptional: false
258
+ },
259
+ role: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
260
+ createdAt: { type: ScalarTypeEnum2.DateTime(), isOptional: false },
261
+ user: { type: MemberUserModel, isOptional: false }
262
+ }
263
+ });
264
+ var InvitationModel = new SchemaModel2({
265
+ name: "Invitation",
266
+ description: "Organization invitation",
267
+ fields: {
268
+ id: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
269
+ email: { type: ScalarTypeEnum2.EmailAddress(), isOptional: false },
270
+ role: { type: ScalarTypeEnum2.String_unsecure(), isOptional: true },
271
+ status: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
272
+ expiresAt: { type: ScalarTypeEnum2.DateTime(), isOptional: true },
273
+ createdAt: { type: ScalarTypeEnum2.DateTime(), isOptional: false }
274
+ }
275
+ });
276
+ var CreateOrgInputModel = new SchemaModel2({
277
+ name: "CreateOrgInput",
278
+ description: "Input for creating an organization",
279
+ fields: {
280
+ name: { type: ScalarTypeEnum2.NonEmptyString(), isOptional: false },
281
+ slug: { type: ScalarTypeEnum2.String_unsecure(), isOptional: true },
282
+ description: { type: ScalarTypeEnum2.String_unsecure(), isOptional: true },
283
+ type: { type: ScalarTypeEnum2.String_unsecure(), isOptional: true }
284
+ }
285
+ });
286
+ var GetOrgInputModel = new SchemaModel2({
287
+ name: "GetOrgInput",
288
+ description: "Input for getting an organization",
289
+ fields: {
290
+ orgId: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false }
291
+ }
292
+ });
293
+ var UpdateOrgInputModel = new SchemaModel2({
294
+ name: "UpdateOrgInput",
295
+ description: "Input for updating an organization",
296
+ fields: {
297
+ orgId: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
298
+ name: { type: ScalarTypeEnum2.String_unsecure(), isOptional: true },
299
+ slug: { type: ScalarTypeEnum2.String_unsecure(), isOptional: true },
300
+ logo: { type: ScalarTypeEnum2.URL(), isOptional: true },
301
+ description: { type: ScalarTypeEnum2.String_unsecure(), isOptional: true }
302
+ }
303
+ });
304
+ var InviteMemberInputModel = new SchemaModel2({
305
+ name: "InviteMemberInput",
306
+ description: "Input for inviting a member",
307
+ fields: {
308
+ orgId: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
309
+ email: { type: ScalarTypeEnum2.EmailAddress(), isOptional: false },
310
+ role: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
311
+ teamId: { type: ScalarTypeEnum2.String_unsecure(), isOptional: true }
312
+ }
313
+ });
314
+ var AcceptInviteInputModel = new SchemaModel2({
315
+ name: "AcceptInviteInput",
316
+ description: "Input for accepting an invitation",
317
+ fields: {
318
+ invitationId: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false }
319
+ }
320
+ });
321
+ var RemoveMemberInputModel = new SchemaModel2({
322
+ name: "RemoveMemberInput",
323
+ description: "Input for removing a member",
324
+ fields: {
325
+ orgId: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
326
+ userId: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false }
327
+ }
328
+ });
329
+ var MemberRemovedPayloadModel = new SchemaModel2({
330
+ name: "MemberRemovedPayload",
331
+ description: "Payload for member removed event",
332
+ fields: {
333
+ orgId: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
334
+ userId: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false }
335
+ }
336
+ });
337
+ var ListMembersInputModel = new SchemaModel2({
338
+ name: "ListMembersInput",
339
+ description: "Input for listing members",
340
+ fields: {
341
+ orgId: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
342
+ limit: { type: ScalarTypeEnum2.Int_unsecure(), isOptional: true },
343
+ offset: { type: ScalarTypeEnum2.Int_unsecure(), isOptional: true }
344
+ }
345
+ });
346
+ var ListMembersOutputModel = new SchemaModel2({
347
+ name: "ListMembersOutput",
348
+ description: "Output for listing members",
349
+ fields: {
350
+ members: { type: MemberModel, isOptional: false, isArray: true },
351
+ total: { type: ScalarTypeEnum2.Int_unsecure(), isOptional: false }
352
+ }
353
+ });
354
+ var OrganizationWithRoleModel = new SchemaModel2({
355
+ name: "OrganizationWithRole",
356
+ description: "Organization with user role",
357
+ fields: {
358
+ id: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
359
+ name: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
360
+ slug: { type: ScalarTypeEnum2.String_unsecure(), isOptional: true },
361
+ logo: { type: ScalarTypeEnum2.URL(), isOptional: true },
362
+ description: { type: ScalarTypeEnum2.String_unsecure(), isOptional: true },
363
+ type: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
364
+ onboardingCompleted: { type: ScalarTypeEnum2.Boolean(), isOptional: false },
365
+ createdAt: { type: ScalarTypeEnum2.DateTime(), isOptional: false },
366
+ role: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false }
367
+ }
368
+ });
369
+ var ListUserOrgsOutputModel = new SchemaModel2({
370
+ name: "ListUserOrgsOutput",
371
+ description: "Output for listing user organizations",
372
+ fields: {
373
+ organizations: {
374
+ type: OrganizationWithRoleModel,
375
+ isOptional: false,
376
+ isArray: true
377
+ }
378
+ }
379
+ });
380
+ var CreateOrgContract = defineCommand2({
381
+ meta: {
382
+ key: "identity.org.create",
383
+ version: "1.0.0",
384
+ stability: "stable",
385
+ owners: [...OWNERS2],
386
+ tags: ["identity", "org", "create"],
387
+ description: "Create a new organization.",
388
+ goal: "Allow users to create new organizations/workspaces.",
389
+ context: "Called during onboarding or when creating additional workspaces."
390
+ },
391
+ io: {
392
+ input: CreateOrgInputModel,
393
+ output: OrganizationModel,
394
+ errors: {
395
+ SLUG_EXISTS: {
396
+ description: "An organization with this slug already exists",
397
+ http: 409,
398
+ gqlCode: "SLUG_EXISTS",
399
+ when: "Slug is already taken"
400
+ }
401
+ }
402
+ },
403
+ policy: {
404
+ auth: "user"
405
+ },
406
+ sideEffects: {
407
+ emits: [
408
+ {
409
+ key: "org.created",
410
+ version: "1.0.0",
411
+ when: "Organization is created",
412
+ payload: OrganizationModel
413
+ }
414
+ ],
415
+ audit: ["org.created"]
416
+ }
417
+ });
418
+ var GetOrgContract = defineQuery2({
419
+ meta: {
420
+ key: "identity.org.get",
421
+ version: "1.0.0",
422
+ stability: "stable",
423
+ owners: [...OWNERS2],
424
+ tags: ["identity", "org", "get"],
425
+ description: "Get organization details.",
426
+ goal: "Retrieve organization information.",
427
+ context: "Called when viewing organization settings or dashboard."
428
+ },
429
+ io: {
430
+ input: GetOrgInputModel,
431
+ output: OrganizationModel
432
+ },
433
+ policy: {
434
+ auth: "user"
435
+ }
436
+ });
437
+ var UpdateOrgContract = defineCommand2({
438
+ meta: {
439
+ key: "identity.org.update",
440
+ version: "1.0.0",
441
+ stability: "stable",
442
+ owners: [...OWNERS2],
443
+ tags: ["identity", "org", "update"],
444
+ description: "Update organization details.",
445
+ goal: "Allow org admins to update organization settings.",
446
+ context: "Organization settings page."
447
+ },
448
+ io: {
449
+ input: UpdateOrgInputModel,
450
+ output: OrganizationModel
451
+ },
452
+ policy: {
453
+ auth: "user"
454
+ },
455
+ sideEffects: {
456
+ emits: [
457
+ {
458
+ key: "org.updated",
459
+ version: "1.0.0",
460
+ when: "Organization is updated",
461
+ payload: OrganizationModel
462
+ }
463
+ ],
464
+ audit: ["org.updated"]
465
+ }
466
+ });
467
+ var InviteMemberContract = defineCommand2({
468
+ meta: {
469
+ key: "identity.org.invite",
470
+ version: "1.0.0",
471
+ stability: "stable",
472
+ owners: [...OWNERS2],
473
+ tags: ["identity", "org", "invite", "member"],
474
+ description: "Invite a user to join the organization.",
475
+ goal: "Allow org admins to invite new members.",
476
+ context: "Team management. Sends invitation email."
477
+ },
478
+ io: {
479
+ input: InviteMemberInputModel,
480
+ output: InvitationModel,
481
+ errors: {
482
+ ALREADY_MEMBER: {
483
+ description: "User is already a member of this organization",
484
+ http: 409,
485
+ gqlCode: "ALREADY_MEMBER",
486
+ when: "Invitee is already a member"
487
+ },
488
+ INVITE_PENDING: {
489
+ description: "An invitation for this email is already pending",
490
+ http: 409,
491
+ gqlCode: "INVITE_PENDING",
492
+ when: "Active invitation exists"
493
+ }
494
+ }
495
+ },
496
+ policy: {
497
+ auth: "user"
498
+ },
499
+ sideEffects: {
500
+ emits: [
501
+ {
502
+ key: "org.invite.sent",
503
+ version: "1.0.0",
504
+ when: "Invitation is sent",
505
+ payload: InvitationModel
506
+ }
507
+ ],
508
+ audit: ["org.invite.sent"]
509
+ }
510
+ });
511
+ var AcceptInviteContract = defineCommand2({
512
+ meta: {
513
+ key: "identity.org.invite.accept",
514
+ version: "1.0.0",
515
+ stability: "stable",
516
+ owners: [...OWNERS2],
517
+ tags: ["identity", "org", "invite", "accept"],
518
+ description: "Accept an organization invitation.",
519
+ goal: "Allow users to join organizations via invitation.",
520
+ context: "Called from invitation email link."
521
+ },
522
+ io: {
523
+ input: AcceptInviteInputModel,
524
+ output: MemberModel,
525
+ errors: {
526
+ INVITE_EXPIRED: {
527
+ description: "The invitation has expired",
528
+ http: 410,
529
+ gqlCode: "INVITE_EXPIRED",
530
+ when: "Invitation is past expiry date"
531
+ },
532
+ INVITE_USED: {
533
+ description: "The invitation has already been used",
534
+ http: 409,
535
+ gqlCode: "INVITE_USED",
536
+ when: "Invitation was already accepted"
537
+ }
538
+ }
539
+ },
540
+ policy: {
541
+ auth: "user"
542
+ },
543
+ sideEffects: {
544
+ emits: [
545
+ {
546
+ key: "org.member.added",
547
+ version: "1.0.0",
548
+ when: "Member joins org",
549
+ payload: MemberModel
550
+ }
551
+ ],
552
+ audit: ["org.member.added"]
553
+ }
554
+ });
555
+ var RemoveMemberContract = defineCommand2({
556
+ meta: {
557
+ key: "identity.org.member.remove",
558
+ version: "1.0.0",
559
+ stability: "stable",
560
+ owners: [...OWNERS2],
561
+ tags: ["identity", "org", "member", "remove"],
562
+ description: "Remove a member from the organization.",
563
+ goal: "Allow org admins to remove members.",
564
+ context: "Team management."
565
+ },
566
+ io: {
567
+ input: RemoveMemberInputModel,
568
+ output: SuccessResultModel,
569
+ errors: {
570
+ CANNOT_REMOVE_OWNER: {
571
+ description: "Cannot remove the organization owner",
572
+ http: 403,
573
+ gqlCode: "CANNOT_REMOVE_OWNER",
574
+ when: "Target is the org owner"
575
+ }
576
+ }
577
+ },
578
+ policy: {
579
+ auth: "user"
580
+ },
581
+ sideEffects: {
582
+ emits: [
583
+ {
584
+ key: "org.member.removed",
585
+ version: "1.0.0",
586
+ when: "Member is removed",
587
+ payload: MemberRemovedPayloadModel
588
+ }
589
+ ],
590
+ audit: ["org.member.removed"]
591
+ }
592
+ });
593
+ var ListMembersContract = defineQuery2({
594
+ meta: {
595
+ key: "identity.org.members.list",
596
+ version: "1.0.0",
597
+ stability: "stable",
598
+ owners: [...OWNERS2],
599
+ tags: ["identity", "org", "member", "list"],
600
+ description: "List organization members.",
601
+ goal: "View all members of an organization.",
602
+ context: "Team management page."
603
+ },
604
+ io: {
605
+ input: ListMembersInputModel,
606
+ output: ListMembersOutputModel
607
+ },
608
+ policy: {
609
+ auth: "user"
610
+ }
611
+ });
612
+ var ListUserOrgsContract = defineQuery2({
613
+ meta: {
614
+ key: "identity.org.list",
615
+ version: "1.0.0",
616
+ stability: "stable",
617
+ owners: [...OWNERS2],
618
+ tags: ["identity", "org", "list"],
619
+ description: "List organizations the current user belongs to.",
620
+ goal: "Show user their organizations for workspace switching.",
621
+ context: "Workspace switcher, org selection."
622
+ },
623
+ io: {
624
+ input: null,
625
+ output: ListUserOrgsOutputModel
626
+ },
627
+ policy: {
628
+ auth: "user"
629
+ }
630
+ });
631
+
632
+ // src/contracts/rbac.ts
633
+ import { SchemaModel as SchemaModel3, ScalarTypeEnum as ScalarTypeEnum3 } from "@contractspec/lib.schema";
634
+ import { defineCommand as defineCommand3, defineQuery as defineQuery3 } from "@contractspec/lib.contracts";
635
+ var RoleModel = new SchemaModel3({
636
+ name: "Role",
637
+ description: "RBAC role definition",
638
+ fields: {
639
+ id: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
640
+ name: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
641
+ description: { type: ScalarTypeEnum3.String_unsecure(), isOptional: true },
642
+ permissions: {
643
+ type: ScalarTypeEnum3.String_unsecure(),
644
+ isOptional: false,
645
+ isArray: true
646
+ },
647
+ createdAt: { type: ScalarTypeEnum3.DateTime(), isOptional: false }
648
+ }
649
+ });
650
+ var PolicyBindingModel = new SchemaModel3({
651
+ name: "PolicyBinding",
652
+ description: "Role assignment to a target",
653
+ fields: {
654
+ id: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
655
+ roleId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
656
+ targetType: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
657
+ targetId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
658
+ expiresAt: { type: ScalarTypeEnum3.DateTime(), isOptional: true },
659
+ createdAt: { type: ScalarTypeEnum3.DateTime(), isOptional: false },
660
+ role: { type: RoleModel, isOptional: false }
661
+ }
662
+ });
663
+ var PermissionCheckResultModel = new SchemaModel3({
664
+ name: "PermissionCheckResult",
665
+ description: "Result of a permission check",
666
+ fields: {
667
+ allowed: { type: ScalarTypeEnum3.Boolean(), isOptional: false },
668
+ reason: { type: ScalarTypeEnum3.String_unsecure(), isOptional: true },
669
+ matchedRole: { type: ScalarTypeEnum3.String_unsecure(), isOptional: true }
670
+ }
671
+ });
672
+ var CreateRoleInputModel = new SchemaModel3({
673
+ name: "CreateRoleInput",
674
+ description: "Input for creating a role",
675
+ fields: {
676
+ name: { type: ScalarTypeEnum3.NonEmptyString(), isOptional: false },
677
+ description: { type: ScalarTypeEnum3.String_unsecure(), isOptional: true },
678
+ permissions: {
679
+ type: ScalarTypeEnum3.String_unsecure(),
680
+ isOptional: false,
681
+ isArray: true
682
+ }
683
+ }
684
+ });
685
+ var UpdateRoleInputModel = new SchemaModel3({
686
+ name: "UpdateRoleInput",
687
+ description: "Input for updating a role",
688
+ fields: {
689
+ roleId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
690
+ name: { type: ScalarTypeEnum3.String_unsecure(), isOptional: true },
691
+ description: { type: ScalarTypeEnum3.String_unsecure(), isOptional: true },
692
+ permissions: {
693
+ type: ScalarTypeEnum3.String_unsecure(),
694
+ isOptional: true,
695
+ isArray: true
696
+ }
697
+ }
698
+ });
699
+ var DeleteRoleInputModel = new SchemaModel3({
700
+ name: "DeleteRoleInput",
701
+ description: "Input for deleting a role",
702
+ fields: {
703
+ roleId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false }
704
+ }
705
+ });
706
+ var ListRolesOutputModel = new SchemaModel3({
707
+ name: "ListRolesOutput",
708
+ description: "Output for listing roles",
709
+ fields: {
710
+ roles: { type: RoleModel, isOptional: false, isArray: true }
711
+ }
712
+ });
713
+ var AssignRoleInputModel = new SchemaModel3({
714
+ name: "AssignRoleInput",
715
+ description: "Input for assigning a role",
716
+ fields: {
717
+ roleId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
718
+ targetType: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
719
+ targetId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
720
+ expiresAt: { type: ScalarTypeEnum3.DateTime(), isOptional: true }
721
+ }
722
+ });
723
+ var RevokeRoleInputModel = new SchemaModel3({
724
+ name: "RevokeRoleInput",
725
+ description: "Input for revoking a role",
726
+ fields: {
727
+ bindingId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false }
728
+ }
729
+ });
730
+ var BindingIdPayloadModel = new SchemaModel3({
731
+ name: "BindingIdPayload",
732
+ description: "Payload with binding ID",
733
+ fields: {
734
+ bindingId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false }
735
+ }
736
+ });
737
+ var CheckPermissionInputModel = new SchemaModel3({
738
+ name: "CheckPermissionInput",
739
+ description: "Input for checking a permission",
740
+ fields: {
741
+ userId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
742
+ orgId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: true },
743
+ permission: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false }
744
+ }
745
+ });
746
+ var ListUserPermissionsInputModel = new SchemaModel3({
747
+ name: "ListUserPermissionsInput",
748
+ description: "Input for listing user permissions",
749
+ fields: {
750
+ userId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
751
+ orgId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: true }
752
+ }
753
+ });
754
+ var ListUserPermissionsOutputModel = new SchemaModel3({
755
+ name: "ListUserPermissionsOutput",
756
+ description: "Output for listing user permissions",
757
+ fields: {
758
+ permissions: {
759
+ type: ScalarTypeEnum3.String_unsecure(),
760
+ isOptional: false,
761
+ isArray: true
762
+ },
763
+ roles: { type: RoleModel, isOptional: false, isArray: true }
764
+ }
765
+ });
766
+ var CreateRoleContract = defineCommand3({
767
+ meta: {
768
+ key: "identity.rbac.role.create",
769
+ version: "1.0.0",
770
+ stability: "stable",
771
+ owners: ["@platform.identity-rbac"],
772
+ tags: ["identity", "rbac", "role", "create"],
773
+ description: "Create a new role with permissions.",
774
+ goal: "Allow admins to define custom roles.",
775
+ context: "Role management in admin settings."
776
+ },
777
+ io: {
778
+ input: CreateRoleInputModel,
779
+ output: RoleModel,
780
+ errors: {
781
+ ROLE_EXISTS: {
782
+ description: "A role with this name already exists",
783
+ http: 409,
784
+ gqlCode: "ROLE_EXISTS",
785
+ when: "Role name is taken"
786
+ }
787
+ }
788
+ },
789
+ policy: {
790
+ auth: "admin"
791
+ },
792
+ sideEffects: {
793
+ audit: ["role.created"]
794
+ }
795
+ });
796
+ var UpdateRoleContract = defineCommand3({
797
+ meta: {
798
+ key: "identity.rbac.role.update",
799
+ version: "1.0.0",
800
+ stability: "stable",
801
+ owners: ["@platform.identity-rbac"],
802
+ tags: ["identity", "rbac", "role", "update"],
803
+ description: "Update an existing role.",
804
+ goal: "Allow admins to modify role permissions.",
805
+ context: "Role management in admin settings."
806
+ },
807
+ io: {
808
+ input: UpdateRoleInputModel,
809
+ output: RoleModel
810
+ },
811
+ policy: {
812
+ auth: "admin"
813
+ },
814
+ sideEffects: {
815
+ audit: ["role.updated"]
816
+ }
817
+ });
818
+ var DeleteRoleContract = defineCommand3({
819
+ meta: {
820
+ key: "identity.rbac.role.delete",
821
+ version: "1.0.0",
822
+ stability: "stable",
823
+ owners: ["@platform.identity-rbac"],
824
+ tags: ["identity", "rbac", "role", "delete"],
825
+ description: "Delete an existing role.",
826
+ goal: "Allow admins to remove unused roles.",
827
+ context: "Role management. Removes all policy bindings using this role."
828
+ },
829
+ io: {
830
+ input: DeleteRoleInputModel,
831
+ output: SuccessResultModel,
832
+ errors: {
833
+ ROLE_IN_USE: {
834
+ description: "Role is still assigned to users or organizations",
835
+ http: 409,
836
+ gqlCode: "ROLE_IN_USE",
837
+ when: "Role has active bindings"
838
+ }
839
+ }
840
+ },
841
+ policy: {
842
+ auth: "admin"
843
+ },
844
+ sideEffects: {
845
+ audit: ["role.deleted"]
846
+ }
847
+ });
848
+ var ListRolesContract = defineQuery3({
849
+ meta: {
850
+ key: "identity.rbac.role.list",
851
+ version: "1.0.0",
852
+ stability: "stable",
853
+ owners: ["@platform.identity-rbac"],
854
+ tags: ["identity", "rbac", "role", "list"],
855
+ description: "List all available roles.",
856
+ goal: "Show available roles for assignment.",
857
+ context: "Role assignment UI."
858
+ },
859
+ io: {
860
+ input: null,
861
+ output: ListRolesOutputModel
862
+ },
863
+ policy: {
864
+ auth: "user"
865
+ }
866
+ });
867
+ var AssignRoleContract = defineCommand3({
868
+ meta: {
869
+ key: "identity.rbac.assign",
870
+ version: "1.0.0",
871
+ stability: "stable",
872
+ owners: ["@platform.identity-rbac"],
873
+ tags: ["identity", "rbac", "assign"],
874
+ description: "Assign a role to a user or organization.",
875
+ goal: "Grant permissions via role assignment.",
876
+ context: "User/org permission management."
877
+ },
878
+ io: {
879
+ input: AssignRoleInputModel,
880
+ output: PolicyBindingModel,
881
+ errors: {
882
+ ROLE_NOT_FOUND: {
883
+ description: "The specified role does not exist",
884
+ http: 404,
885
+ gqlCode: "ROLE_NOT_FOUND",
886
+ when: "Role ID is invalid"
887
+ },
888
+ ALREADY_ASSIGNED: {
889
+ description: "This role is already assigned to the target",
890
+ http: 409,
891
+ gqlCode: "ALREADY_ASSIGNED",
892
+ when: "Binding already exists"
893
+ }
894
+ }
895
+ },
896
+ policy: {
897
+ auth: "admin"
898
+ },
899
+ sideEffects: {
900
+ emits: [
901
+ {
902
+ key: "role.assigned",
903
+ version: "1.0.0",
904
+ when: "Role is assigned",
905
+ payload: PolicyBindingModel
906
+ }
907
+ ],
908
+ audit: ["role.assigned"]
909
+ }
910
+ });
911
+ var RevokeRoleContract = defineCommand3({
912
+ meta: {
913
+ key: "identity.rbac.revoke",
914
+ version: "1.0.0",
915
+ stability: "stable",
916
+ owners: ["@platform.identity-rbac"],
917
+ tags: ["identity", "rbac", "revoke"],
918
+ description: "Revoke a role from a user or organization.",
919
+ goal: "Remove permissions via role revocation.",
920
+ context: "User/org permission management."
921
+ },
922
+ io: {
923
+ input: RevokeRoleInputModel,
924
+ output: SuccessResultModel,
925
+ errors: {
926
+ BINDING_NOT_FOUND: {
927
+ description: "The policy binding does not exist",
928
+ http: 404,
929
+ gqlCode: "BINDING_NOT_FOUND",
930
+ when: "Binding ID is invalid"
931
+ }
932
+ }
933
+ },
934
+ policy: {
935
+ auth: "admin"
936
+ },
937
+ sideEffects: {
938
+ emits: [
939
+ {
940
+ key: "role.revoked",
941
+ version: "1.0.0",
942
+ when: "Role is revoked",
943
+ payload: BindingIdPayloadModel
944
+ }
945
+ ],
946
+ audit: ["role.revoked"]
947
+ }
948
+ });
949
+ var CheckPermissionContract = defineQuery3({
950
+ meta: {
951
+ key: "identity.rbac.check",
952
+ version: "1.0.0",
953
+ stability: "stable",
954
+ owners: ["@platform.identity-rbac"],
955
+ tags: ["identity", "rbac", "check", "permission"],
956
+ description: "Check if a user has a specific permission.",
957
+ goal: "Authorization check before sensitive operations.",
958
+ context: "Called by other services to verify permissions."
959
+ },
960
+ io: {
961
+ input: CheckPermissionInputModel,
962
+ output: PermissionCheckResultModel
963
+ },
964
+ policy: {
965
+ auth: "user"
966
+ }
967
+ });
968
+ var ListUserPermissionsContract = defineQuery3({
969
+ meta: {
970
+ key: "identity.rbac.permissions",
971
+ version: "1.0.0",
972
+ stability: "stable",
973
+ owners: ["@platform.identity-rbac"],
974
+ tags: ["identity", "rbac", "permissions", "user"],
975
+ description: "List all permissions for a user in a context.",
976
+ goal: "Show what a user can do in an org.",
977
+ context: "UI permission display, debugging."
978
+ },
979
+ io: {
980
+ input: ListUserPermissionsInputModel,
981
+ output: ListUserPermissionsOutputModel
982
+ },
983
+ policy: {
984
+ auth: "user"
985
+ }
986
+ });
987
+ // src/entities/user.ts
988
+ import { defineEntity, field, index } from "@contractspec/lib.schema";
989
+ var UserEntity = defineEntity({
990
+ name: "User",
991
+ description: "A user of the platform. Users hold core profile information and authenticate via Account records.",
992
+ schema: "lssm_sigil",
993
+ map: "user",
994
+ fields: {
995
+ id: field.id({ description: "Unique user identifier" }),
996
+ email: field.email({ isUnique: true, description: "User email address" }),
997
+ emailVerified: field.boolean({
998
+ default: false,
999
+ description: "Whether email has been verified"
1000
+ }),
1001
+ name: field.string({ isOptional: true, description: "Display name" }),
1002
+ firstName: field.string({ isOptional: true, description: "First name" }),
1003
+ lastName: field.string({ isOptional: true, description: "Last name" }),
1004
+ locale: field.string({
1005
+ isOptional: true,
1006
+ description: 'User locale (e.g., "en-US")'
1007
+ }),
1008
+ timezone: field.string({
1009
+ isOptional: true,
1010
+ description: 'Olson timezone (e.g., "Europe/Paris")'
1011
+ }),
1012
+ imageUrl: field.url({
1013
+ isOptional: true,
1014
+ description: "URL of avatar or profile picture"
1015
+ }),
1016
+ image: field.string({
1017
+ isOptional: true,
1018
+ description: "Legacy image field"
1019
+ }),
1020
+ metadata: field.json({
1021
+ isOptional: true,
1022
+ description: "Arbitrary user metadata"
1023
+ }),
1024
+ onboardingCompleted: field.boolean({
1025
+ default: false,
1026
+ description: "Whether onboarding is complete"
1027
+ }),
1028
+ onboardingStep: field.string({
1029
+ isOptional: true,
1030
+ description: "Current onboarding step"
1031
+ }),
1032
+ whitelistedAt: field.dateTime({
1033
+ isOptional: true,
1034
+ description: "When user was whitelisted"
1035
+ }),
1036
+ role: field.string({
1037
+ isOptional: true,
1038
+ default: '"user"',
1039
+ description: "User role (user, admin)"
1040
+ }),
1041
+ banned: field.boolean({
1042
+ default: false,
1043
+ description: "Whether user is banned"
1044
+ }),
1045
+ banReason: field.string({
1046
+ isOptional: true,
1047
+ description: "Reason for ban"
1048
+ }),
1049
+ banExpires: field.dateTime({
1050
+ isOptional: true,
1051
+ description: "When ban expires"
1052
+ }),
1053
+ phoneNumber: field.string({
1054
+ isOptional: true,
1055
+ isUnique: true,
1056
+ description: "Phone number"
1057
+ }),
1058
+ phoneNumberVerified: field.boolean({
1059
+ default: false,
1060
+ description: "Whether phone is verified"
1061
+ }),
1062
+ createdAt: field.createdAt(),
1063
+ updatedAt: field.updatedAt(),
1064
+ sessions: field.hasMany("Session"),
1065
+ accounts: field.hasMany("Account"),
1066
+ memberships: field.hasMany("Member"),
1067
+ invitations: field.hasMany("Invitation"),
1068
+ teamMemberships: field.hasMany("TeamMember"),
1069
+ policyBindings: field.hasMany("PolicyBinding"),
1070
+ apiKeys: field.hasMany("ApiKey"),
1071
+ passkeys: field.hasMany("Passkey")
1072
+ }
1073
+ });
1074
+ var SessionEntity = defineEntity({
1075
+ name: "Session",
1076
+ description: "Represents a login session (e.g., web session or API token).",
1077
+ schema: "lssm_sigil",
1078
+ map: "session",
1079
+ fields: {
1080
+ id: field.id(),
1081
+ userId: field.foreignKey(),
1082
+ expiresAt: field.dateTime({ description: "Session expiration time" }),
1083
+ token: field.string({ isUnique: true, description: "Session token" }),
1084
+ ipAddress: field.string({
1085
+ isOptional: true,
1086
+ description: "Client IP address"
1087
+ }),
1088
+ userAgent: field.string({
1089
+ isOptional: true,
1090
+ description: "Client user agent"
1091
+ }),
1092
+ impersonatedBy: field.string({
1093
+ isOptional: true,
1094
+ description: "Admin impersonating this session"
1095
+ }),
1096
+ activeOrganizationId: field.string({
1097
+ isOptional: true,
1098
+ description: "Active org context"
1099
+ }),
1100
+ activeTeamId: field.string({
1101
+ isOptional: true,
1102
+ description: "Active team context"
1103
+ }),
1104
+ createdAt: field.createdAt(),
1105
+ updatedAt: field.updatedAt(),
1106
+ user: field.belongsTo("User", ["userId"], ["id"], { onDelete: "Cascade" })
1107
+ }
1108
+ });
1109
+ var AccountEntity = defineEntity({
1110
+ name: "Account",
1111
+ description: "External authentication accounts (OAuth, password, etc.).",
1112
+ schema: "lssm_sigil",
1113
+ map: "account",
1114
+ fields: {
1115
+ id: field.id(),
1116
+ accountId: field.string({ description: "Account ID from provider" }),
1117
+ providerId: field.string({ description: "Provider identifier" }),
1118
+ userId: field.foreignKey(),
1119
+ accessToken: field.string({ isOptional: true }),
1120
+ refreshToken: field.string({ isOptional: true }),
1121
+ idToken: field.string({ isOptional: true }),
1122
+ accessTokenExpiresAt: field.dateTime({ isOptional: true }),
1123
+ refreshTokenExpiresAt: field.dateTime({ isOptional: true }),
1124
+ scope: field.string({ isOptional: true }),
1125
+ password: field.string({
1126
+ isOptional: true,
1127
+ description: "Hashed password for password providers"
1128
+ }),
1129
+ createdAt: field.createdAt(),
1130
+ updatedAt: field.updatedAt(),
1131
+ user: field.belongsTo("User", ["userId"], ["id"], { onDelete: "Cascade" })
1132
+ },
1133
+ indexes: [index.unique(["accountId", "providerId"])]
1134
+ });
1135
+ var VerificationEntity = defineEntity({
1136
+ name: "Verification",
1137
+ description: "Verification tokens for email/phone confirmation.",
1138
+ schema: "lssm_sigil",
1139
+ map: "verification",
1140
+ fields: {
1141
+ id: field.uuid(),
1142
+ identifier: field.string({ description: "Email or phone being verified" }),
1143
+ value: field.string({ description: "Verification code/token" }),
1144
+ expiresAt: field.dateTime({ description: "Token expiration" }),
1145
+ createdAt: field.createdAt(),
1146
+ updatedAt: field.updatedAt()
1147
+ }
1148
+ });
1149
+
1150
+ // src/entities/organization.ts
1151
+ import {
1152
+ defineEntity as defineEntity2,
1153
+ defineEntityEnum,
1154
+ field as field2,
1155
+ index as index2
1156
+ } from "@contractspec/lib.schema";
1157
+ var OrganizationTypeEnum = defineEntityEnum({
1158
+ name: "OrganizationType",
1159
+ values: ["PLATFORM_ADMIN", "CONTRACT_SPEC_CUSTOMER"],
1160
+ schema: "lssm_sigil",
1161
+ description: "Type of organization in the platform."
1162
+ });
1163
+ var OrganizationEntity = defineEntity2({
1164
+ name: "Organization",
1165
+ description: "An organization is a tenant boundary grouping users.",
1166
+ schema: "lssm_sigil",
1167
+ map: "organization",
1168
+ fields: {
1169
+ id: field2.id({ description: "Unique organization identifier" }),
1170
+ name: field2.string({ description: "Organization display name" }),
1171
+ slug: field2.string({
1172
+ isOptional: true,
1173
+ isUnique: true,
1174
+ description: "URL-friendly identifier"
1175
+ }),
1176
+ logo: field2.url({ isOptional: true, description: "Organization logo URL" }),
1177
+ description: field2.string({
1178
+ isOptional: true,
1179
+ description: "Organization description"
1180
+ }),
1181
+ metadata: field2.json({
1182
+ isOptional: true,
1183
+ description: "Arbitrary organization metadata"
1184
+ }),
1185
+ type: field2.enum("OrganizationType", { description: "Organization type" }),
1186
+ onboardingCompleted: field2.boolean({ default: false }),
1187
+ onboardingStep: field2.string({ isOptional: true }),
1188
+ referralCode: field2.string({
1189
+ isOptional: true,
1190
+ isUnique: true,
1191
+ description: "Unique referral code"
1192
+ }),
1193
+ referredBy: field2.string({
1194
+ isOptional: true,
1195
+ description: "ID of referring user"
1196
+ }),
1197
+ createdAt: field2.createdAt(),
1198
+ updatedAt: field2.updatedAt(),
1199
+ members: field2.hasMany("Member"),
1200
+ invitations: field2.hasMany("Invitation"),
1201
+ teams: field2.hasMany("Team"),
1202
+ policyBindings: field2.hasMany("PolicyBinding")
1203
+ },
1204
+ enums: [OrganizationTypeEnum]
1205
+ });
1206
+ var MemberEntity = defineEntity2({
1207
+ name: "Member",
1208
+ description: "Membership of a user in an organization with a role.",
1209
+ schema: "lssm_sigil",
1210
+ map: "member",
1211
+ fields: {
1212
+ id: field2.id(),
1213
+ userId: field2.foreignKey(),
1214
+ organizationId: field2.foreignKey(),
1215
+ role: field2.string({
1216
+ description: "Role in organization (owner, admin, member)"
1217
+ }),
1218
+ createdAt: field2.createdAt(),
1219
+ user: field2.belongsTo("User", ["userId"], ["id"], { onDelete: "Cascade" }),
1220
+ organization: field2.belongsTo("Organization", ["organizationId"], ["id"], {
1221
+ onDelete: "Cascade"
1222
+ })
1223
+ },
1224
+ indexes: [index2.unique(["userId", "organizationId"])]
1225
+ });
1226
+ var InvitationEntity = defineEntity2({
1227
+ name: "Invitation",
1228
+ description: "An invitation to join an organization.",
1229
+ schema: "lssm_sigil",
1230
+ map: "invitation",
1231
+ fields: {
1232
+ id: field2.id(),
1233
+ organizationId: field2.foreignKey(),
1234
+ email: field2.email({ description: "Invited email address" }),
1235
+ role: field2.string({
1236
+ isOptional: true,
1237
+ description: "Role to assign on acceptance"
1238
+ }),
1239
+ status: field2.string({
1240
+ default: '"pending"',
1241
+ description: "Invitation status"
1242
+ }),
1243
+ acceptedAt: field2.dateTime({ isOptional: true }),
1244
+ expiresAt: field2.dateTime({ isOptional: true }),
1245
+ inviterId: field2.foreignKey({
1246
+ description: "User who sent the invitation"
1247
+ }),
1248
+ teamId: field2.string({ isOptional: true }),
1249
+ createdAt: field2.createdAt(),
1250
+ updatedAt: field2.updatedAt(),
1251
+ organization: field2.belongsTo("Organization", ["organizationId"], ["id"], {
1252
+ onDelete: "Cascade"
1253
+ }),
1254
+ inviter: field2.belongsTo("User", ["inviterId"], ["id"], {
1255
+ onDelete: "Cascade"
1256
+ }),
1257
+ team: field2.belongsTo("Team", ["teamId"], ["id"], { onDelete: "Cascade" })
1258
+ }
1259
+ });
1260
+ var TeamEntity = defineEntity2({
1261
+ name: "Team",
1262
+ description: "Team within an organization.",
1263
+ schema: "lssm_sigil",
1264
+ map: "team",
1265
+ fields: {
1266
+ id: field2.id(),
1267
+ name: field2.string({ description: "Team name" }),
1268
+ organizationId: field2.foreignKey(),
1269
+ createdAt: field2.createdAt(),
1270
+ updatedAt: field2.updatedAt(),
1271
+ organization: field2.belongsTo("Organization", ["organizationId"], ["id"], {
1272
+ onDelete: "Cascade"
1273
+ }),
1274
+ members: field2.hasMany("TeamMember"),
1275
+ invitations: field2.hasMany("Invitation")
1276
+ }
1277
+ });
1278
+ var TeamMemberEntity = defineEntity2({
1279
+ name: "TeamMember",
1280
+ description: "Team membership for a user.",
1281
+ schema: "lssm_sigil",
1282
+ map: "team_member",
1283
+ fields: {
1284
+ id: field2.id(),
1285
+ teamId: field2.foreignKey(),
1286
+ userId: field2.foreignKey(),
1287
+ createdAt: field2.createdAt(),
1288
+ team: field2.belongsTo("Team", ["teamId"], ["id"], { onDelete: "Cascade" }),
1289
+ user: field2.belongsTo("User", ["userId"], ["id"], { onDelete: "Cascade" })
1290
+ }
1291
+ });
1292
+
1293
+ // src/entities/rbac.ts
1294
+ import { defineEntity as defineEntity3, field as field3, index as index3 } from "@contractspec/lib.schema";
1295
+ var RoleEntity = defineEntity3({
1296
+ name: "Role",
1297
+ description: "A role defines a named set of permissions.",
1298
+ schema: "lssm_sigil",
1299
+ map: "role",
1300
+ fields: {
1301
+ id: field3.id(),
1302
+ name: field3.string({ isUnique: true, description: "Unique role name" }),
1303
+ description: field3.string({
1304
+ isOptional: true,
1305
+ description: "Role description"
1306
+ }),
1307
+ permissions: field3.string({
1308
+ isArray: true,
1309
+ description: "Array of permission names"
1310
+ }),
1311
+ createdAt: field3.createdAt(),
1312
+ updatedAt: field3.updatedAt(),
1313
+ policyBindings: field3.hasMany("PolicyBinding")
1314
+ }
1315
+ });
1316
+ var PermissionEntity = defineEntity3({
1317
+ name: "Permission",
1318
+ description: "A permission represents an atomic access right.",
1319
+ schema: "lssm_sigil",
1320
+ map: "permission",
1321
+ fields: {
1322
+ id: field3.id(),
1323
+ name: field3.string({
1324
+ isUnique: true,
1325
+ description: "Unique permission name"
1326
+ }),
1327
+ description: field3.string({
1328
+ isOptional: true,
1329
+ description: "Permission description"
1330
+ }),
1331
+ createdAt: field3.createdAt(),
1332
+ updatedAt: field3.updatedAt()
1333
+ }
1334
+ });
1335
+ var PolicyBindingEntity = defineEntity3({
1336
+ name: "PolicyBinding",
1337
+ description: "Binds roles to principals (users or organizations).",
1338
+ schema: "lssm_sigil",
1339
+ map: "policy_binding",
1340
+ fields: {
1341
+ id: field3.id(),
1342
+ roleId: field3.foreignKey(),
1343
+ targetType: field3.string({ description: '"user" or "organization"' }),
1344
+ targetId: field3.string({ description: "ID of User or Organization" }),
1345
+ expiresAt: field3.dateTime({
1346
+ isOptional: true,
1347
+ description: "When binding expires"
1348
+ }),
1349
+ createdAt: field3.createdAt(),
1350
+ userId: field3.string({ isOptional: true }),
1351
+ organizationId: field3.string({ isOptional: true }),
1352
+ role: field3.belongsTo("Role", ["roleId"], ["id"], { onDelete: "Cascade" }),
1353
+ user: field3.belongsTo("User", ["userId"], ["id"]),
1354
+ organization: field3.belongsTo("Organization", ["organizationId"], ["id"])
1355
+ },
1356
+ indexes: [index3.on(["targetType", "targetId"])]
1357
+ });
1358
+ var ApiKeyEntity = defineEntity3({
1359
+ name: "ApiKey",
1360
+ description: "API keys for programmatic access.",
1361
+ schema: "lssm_sigil",
1362
+ map: "api_key",
1363
+ fields: {
1364
+ id: field3.id(),
1365
+ name: field3.string({ description: "API key name" }),
1366
+ start: field3.string({
1367
+ description: "Starting characters for identification"
1368
+ }),
1369
+ prefix: field3.string({ description: "API key prefix" }),
1370
+ key: field3.string({ description: "Hashed API key" }),
1371
+ userId: field3.foreignKey(),
1372
+ refillInterval: field3.int({ description: "Refill interval in ms" }),
1373
+ refillAmount: field3.int({ description: "Amount to refill" }),
1374
+ lastRefillAt: field3.dateTime(),
1375
+ remaining: field3.int({ description: "Remaining requests" }),
1376
+ requestCount: field3.int({ description: "Total requests made" }),
1377
+ lastRequest: field3.dateTime(),
1378
+ enabled: field3.boolean({ default: true }),
1379
+ rateLimitEnabled: field3.boolean({ default: true }),
1380
+ rateLimitTimeWindow: field3.int({ description: "Rate limit window in ms" }),
1381
+ rateLimitMax: field3.int({ description: "Max requests in window" }),
1382
+ expiresAt: field3.dateTime(),
1383
+ permissions: field3.string({ isArray: true }),
1384
+ metadata: field3.json({ isOptional: true }),
1385
+ createdAt: field3.createdAt(),
1386
+ updatedAt: field3.updatedAt(),
1387
+ user: field3.belongsTo("User", ["userId"], ["id"], { onDelete: "Cascade" })
1388
+ }
1389
+ });
1390
+ var PasskeyEntity = defineEntity3({
1391
+ name: "Passkey",
1392
+ description: "WebAuthn passkeys for passwordless authentication.",
1393
+ schema: "lssm_sigil",
1394
+ map: "passkey",
1395
+ fields: {
1396
+ id: field3.id(),
1397
+ name: field3.string({ description: "Passkey name" }),
1398
+ publicKey: field3.string({ description: "Public key" }),
1399
+ userId: field3.foreignKey(),
1400
+ credentialID: field3.string({ description: "Credential ID" }),
1401
+ counter: field3.int({ description: "Counter" }),
1402
+ deviceType: field3.string({ description: "Device type" }),
1403
+ backedUp: field3.boolean({ description: "Whether passkey is backed up" }),
1404
+ transports: field3.string({ description: "Transports" }),
1405
+ aaguid: field3.string({ description: "Authenticator GUID" }),
1406
+ createdAt: field3.createdAt(),
1407
+ user: field3.belongsTo("User", ["userId"], ["id"], { onDelete: "Cascade" })
1408
+ }
1409
+ });
1410
+ // src/entities/index.ts
1411
+ var identityRbacEntities = [
1412
+ UserEntity,
1413
+ SessionEntity,
1414
+ AccountEntity,
1415
+ VerificationEntity,
1416
+ OrganizationEntity,
1417
+ MemberEntity,
1418
+ InvitationEntity,
1419
+ TeamEntity,
1420
+ TeamMemberEntity,
1421
+ RoleEntity,
1422
+ PermissionEntity,
1423
+ PolicyBindingEntity,
1424
+ ApiKeyEntity,
1425
+ PasskeyEntity
1426
+ ];
1427
+ var identityRbacSchemaContribution = {
1428
+ moduleId: "@contractspec/lib.identity-rbac",
1429
+ entities: identityRbacEntities,
1430
+ enums: [OrganizationTypeEnum]
1431
+ };
1432
+
1433
+ // src/events.ts
1434
+ import { SchemaModel as SchemaModel4, ScalarTypeEnum as ScalarTypeEnum4 } from "@contractspec/lib.schema";
1435
+ import { defineEvent } from "@contractspec/lib.contracts";
1436
+ var UserCreatedPayload = new SchemaModel4({
1437
+ name: "UserCreatedPayload",
1438
+ description: "Payload for user created event",
1439
+ fields: {
1440
+ userId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1441
+ email: { type: ScalarTypeEnum4.EmailAddress(), isOptional: false },
1442
+ name: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
1443
+ createdAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1444
+ }
1445
+ });
1446
+ var UserUpdatedPayload = new SchemaModel4({
1447
+ name: "UserUpdatedPayload",
1448
+ description: "Payload for user updated event",
1449
+ fields: {
1450
+ userId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1451
+ updatedFields: {
1452
+ type: ScalarTypeEnum4.String_unsecure(),
1453
+ isOptional: false,
1454
+ isArray: true
1455
+ },
1456
+ updatedAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1457
+ }
1458
+ });
1459
+ var UserDeletedPayload = new SchemaModel4({
1460
+ name: "UserDeletedPayload",
1461
+ description: "Payload for user deleted event",
1462
+ fields: {
1463
+ userId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1464
+ email: { type: ScalarTypeEnum4.EmailAddress(), isOptional: false },
1465
+ deletedAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1466
+ }
1467
+ });
1468
+ var UserEmailVerifiedPayload = new SchemaModel4({
1469
+ name: "UserEmailVerifiedPayload",
1470
+ description: "Payload for user email verified event",
1471
+ fields: {
1472
+ userId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1473
+ email: { type: ScalarTypeEnum4.EmailAddress(), isOptional: false },
1474
+ verifiedAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1475
+ }
1476
+ });
1477
+ var OrgCreatedPayload = new SchemaModel4({
1478
+ name: "OrgCreatedPayload",
1479
+ description: "Payload for org created event",
1480
+ fields: {
1481
+ orgId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1482
+ name: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1483
+ slug: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
1484
+ createdBy: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1485
+ createdAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1486
+ }
1487
+ });
1488
+ var OrgUpdatedPayload = new SchemaModel4({
1489
+ name: "OrgUpdatedPayload",
1490
+ description: "Payload for org updated event",
1491
+ fields: {
1492
+ orgId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1493
+ updatedFields: {
1494
+ type: ScalarTypeEnum4.String_unsecure(),
1495
+ isOptional: false,
1496
+ isArray: true
1497
+ },
1498
+ updatedBy: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1499
+ updatedAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1500
+ }
1501
+ });
1502
+ var OrgDeletedPayload = new SchemaModel4({
1503
+ name: "OrgDeletedPayload",
1504
+ description: "Payload for org deleted event",
1505
+ fields: {
1506
+ orgId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1507
+ name: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1508
+ deletedBy: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1509
+ deletedAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1510
+ }
1511
+ });
1512
+ var OrgMemberAddedPayload = new SchemaModel4({
1513
+ name: "OrgMemberAddedPayload",
1514
+ description: "Payload for member added event",
1515
+ fields: {
1516
+ orgId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1517
+ userId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1518
+ role: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1519
+ invitedBy: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
1520
+ joinedAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1521
+ }
1522
+ });
1523
+ var OrgMemberRemovedPayload = new SchemaModel4({
1524
+ name: "OrgMemberRemovedPayload",
1525
+ description: "Payload for member removed event",
1526
+ fields: {
1527
+ orgId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1528
+ userId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1529
+ removedBy: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
1530
+ reason: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
1531
+ removedAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1532
+ }
1533
+ });
1534
+ var OrgMemberRoleChangedPayload = new SchemaModel4({
1535
+ name: "OrgMemberRoleChangedPayload",
1536
+ description: "Payload for member role changed event",
1537
+ fields: {
1538
+ orgId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1539
+ userId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1540
+ previousRole: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1541
+ newRole: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1542
+ changedBy: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1543
+ changedAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1544
+ }
1545
+ });
1546
+ var OrgInviteSentPayload = new SchemaModel4({
1547
+ name: "OrgInviteSentPayload",
1548
+ description: "Payload for invite sent event",
1549
+ fields: {
1550
+ invitationId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1551
+ orgId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1552
+ email: { type: ScalarTypeEnum4.EmailAddress(), isOptional: false },
1553
+ role: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1554
+ invitedBy: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1555
+ expiresAt: { type: ScalarTypeEnum4.DateTime(), isOptional: true },
1556
+ sentAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1557
+ }
1558
+ });
1559
+ var OrgInviteAcceptedPayload = new SchemaModel4({
1560
+ name: "OrgInviteAcceptedPayload",
1561
+ description: "Payload for invite accepted event",
1562
+ fields: {
1563
+ invitationId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1564
+ orgId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1565
+ userId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1566
+ acceptedAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1567
+ }
1568
+ });
1569
+ var OrgInviteDeclinedPayload = new SchemaModel4({
1570
+ name: "OrgInviteDeclinedPayload",
1571
+ description: "Payload for invite declined event",
1572
+ fields: {
1573
+ invitationId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1574
+ orgId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1575
+ declinedAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1576
+ }
1577
+ });
1578
+ var RoleAssignedPayload = new SchemaModel4({
1579
+ name: "RoleAssignedPayload",
1580
+ description: "Payload for role assigned event",
1581
+ fields: {
1582
+ bindingId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1583
+ roleId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1584
+ roleName: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1585
+ targetType: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1586
+ targetId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1587
+ assignedBy: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1588
+ expiresAt: { type: ScalarTypeEnum4.DateTime(), isOptional: true },
1589
+ assignedAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1590
+ }
1591
+ });
1592
+ var RoleRevokedPayload = new SchemaModel4({
1593
+ name: "RoleRevokedPayload",
1594
+ description: "Payload for role revoked event",
1595
+ fields: {
1596
+ bindingId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1597
+ roleId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1598
+ roleName: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1599
+ targetType: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1600
+ targetId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1601
+ revokedBy: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1602
+ revokedAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1603
+ }
1604
+ });
1605
+ var UserCreatedEvent = defineEvent({
1606
+ meta: {
1607
+ key: "user.created",
1608
+ version: "1.0.0",
1609
+ description: "A new user has been created.",
1610
+ stability: "stable",
1611
+ owners: ["@platform.identity-rbac"],
1612
+ tags: ["user", "created", "identity"]
1613
+ },
1614
+ payload: UserCreatedPayload
1615
+ });
1616
+ var UserUpdatedEvent = defineEvent({
1617
+ meta: {
1618
+ key: "user.updated",
1619
+ version: "1.0.0",
1620
+ description: "A user profile has been updated.",
1621
+ stability: "stable",
1622
+ owners: ["@platform.identity-rbac"],
1623
+ tags: ["user", "updated", "identity"]
1624
+ },
1625
+ payload: UserUpdatedPayload
1626
+ });
1627
+ var UserDeletedEvent = defineEvent({
1628
+ meta: {
1629
+ key: "user.deleted",
1630
+ version: "1.0.0",
1631
+ description: "A user account has been deleted.",
1632
+ stability: "stable",
1633
+ owners: ["@platform.identity-rbac"],
1634
+ tags: ["user", "deleted", "identity"]
1635
+ },
1636
+ pii: ["email"],
1637
+ payload: UserDeletedPayload
1638
+ });
1639
+ var UserEmailVerifiedEvent = defineEvent({
1640
+ meta: {
1641
+ key: "user.email_verified",
1642
+ version: "1.0.0",
1643
+ description: "A user has verified their email address.",
1644
+ stability: "stable",
1645
+ owners: ["@platform.identity-rbac"],
1646
+ tags: ["user", "verified", "identity"]
1647
+ },
1648
+ payload: UserEmailVerifiedPayload
1649
+ });
1650
+ var OrgCreatedEvent = defineEvent({
1651
+ meta: {
1652
+ key: "org.created",
1653
+ version: "1.0.0",
1654
+ description: "A new organization has been created.",
1655
+ stability: "stable",
1656
+ owners: ["@platform.identity-rbac"],
1657
+ tags: ["org", "created", "identity"]
1658
+ },
1659
+ payload: OrgCreatedPayload
1660
+ });
1661
+ var OrgUpdatedEvent = defineEvent({
1662
+ meta: {
1663
+ key: "org.updated",
1664
+ version: "1.0.0",
1665
+ description: "An organization has been updated.",
1666
+ stability: "stable",
1667
+ owners: ["@platform.identity-rbac"],
1668
+ tags: ["org", "updated", "identity"]
1669
+ },
1670
+ payload: OrgUpdatedPayload
1671
+ });
1672
+ var OrgDeletedEvent = defineEvent({
1673
+ meta: {
1674
+ key: "org.deleted",
1675
+ version: "1.0.0",
1676
+ description: "An organization has been deleted.",
1677
+ stability: "stable",
1678
+ owners: ["@platform.identity-rbac"],
1679
+ tags: ["org", "deleted", "identity"]
1680
+ },
1681
+ payload: OrgDeletedPayload
1682
+ });
1683
+ var OrgMemberAddedEvent = defineEvent({
1684
+ meta: {
1685
+ key: "org.member.added",
1686
+ version: "1.0.0",
1687
+ description: "A user has joined an organization.",
1688
+ stability: "stable",
1689
+ owners: ["@platform.identity-rbac"],
1690
+ tags: ["org", "member", "added", "identity"]
1691
+ },
1692
+ payload: OrgMemberAddedPayload
1693
+ });
1694
+ var OrgMemberRemovedEvent = defineEvent({
1695
+ meta: {
1696
+ key: "org.member.removed",
1697
+ version: "1.0.0",
1698
+ description: "A user has left or been removed from an organization.",
1699
+ stability: "stable",
1700
+ owners: ["@platform.identity-rbac"],
1701
+ tags: ["org", "member", "removed", "identity"]
1702
+ },
1703
+ payload: OrgMemberRemovedPayload
1704
+ });
1705
+ var OrgMemberRoleChangedEvent = defineEvent({
1706
+ meta: {
1707
+ key: "org.member.role_changed",
1708
+ version: "1.0.0",
1709
+ description: "A member's role in an organization has changed.",
1710
+ stability: "stable",
1711
+ owners: ["@platform.identity-rbac"],
1712
+ tags: ["org", "member", "role", "changed", "identity"]
1713
+ },
1714
+ payload: OrgMemberRoleChangedPayload
1715
+ });
1716
+ var OrgInviteSentEvent = defineEvent({
1717
+ meta: {
1718
+ key: "org.invite.sent",
1719
+ version: "1.0.0",
1720
+ description: "An invitation to join an organization has been sent.",
1721
+ stability: "stable",
1722
+ owners: ["@platform.identity-rbac"],
1723
+ tags: ["org", "invite", "sent", "identity"]
1724
+ },
1725
+ pii: ["email"],
1726
+ payload: OrgInviteSentPayload
1727
+ });
1728
+ var OrgInviteAcceptedEvent = defineEvent({
1729
+ meta: {
1730
+ key: "org.invite.accepted",
1731
+ version: "1.0.0",
1732
+ description: "An invitation has been accepted.",
1733
+ stability: "stable",
1734
+ owners: ["@platform.identity-rbac"],
1735
+ tags: ["org", "invite", "accepted", "identity"]
1736
+ },
1737
+ payload: OrgInviteAcceptedPayload
1738
+ });
1739
+ var OrgInviteDeclinedEvent = defineEvent({
1740
+ meta: {
1741
+ key: "org.invite.declined",
1742
+ version: "1.0.0",
1743
+ description: "An invitation has been declined.",
1744
+ stability: "stable",
1745
+ owners: ["@platform.identity-rbac"],
1746
+ tags: ["org", "invite", "declined", "identity"]
1747
+ },
1748
+ payload: OrgInviteDeclinedPayload
1749
+ });
1750
+ var RoleAssignedEvent = defineEvent({
1751
+ meta: {
1752
+ key: "role.assigned",
1753
+ version: "1.0.0",
1754
+ description: "A role has been assigned.",
1755
+ stability: "stable",
1756
+ owners: ["@platform.identity-rbac"],
1757
+ tags: ["role", "assigned", "identity"]
1758
+ },
1759
+ payload: RoleAssignedPayload
1760
+ });
1761
+ var RoleRevokedEvent = defineEvent({
1762
+ meta: {
1763
+ key: "role.revoked",
1764
+ version: "1.0.0",
1765
+ description: "A role has been revoked.",
1766
+ stability: "stable",
1767
+ owners: ["@platform.identity-rbac"],
1768
+ tags: ["role", "revoked", "identity"]
1769
+ },
1770
+ payload: RoleRevokedPayload
1771
+ });
1772
+ var IdentityRbacEvents = {
1773
+ UserCreatedEvent,
1774
+ UserUpdatedEvent,
1775
+ UserDeletedEvent,
1776
+ UserEmailVerifiedEvent,
1777
+ OrgCreatedEvent,
1778
+ OrgUpdatedEvent,
1779
+ OrgDeletedEvent,
1780
+ OrgMemberAddedEvent,
1781
+ OrgMemberRemovedEvent,
1782
+ OrgMemberRoleChangedEvent,
1783
+ OrgInviteSentEvent,
1784
+ OrgInviteAcceptedEvent,
1785
+ OrgInviteDeclinedEvent,
1786
+ RoleAssignedEvent,
1787
+ RoleRevokedEvent
1788
+ };
1789
+
1790
+ // src/identity-rbac.feature.ts
1791
+ import { defineFeature } from "@contractspec/lib.contracts";
1792
+ var IdentityRbacFeature = defineFeature({
1793
+ meta: {
1794
+ key: "identity-rbac",
1795
+ version: "1.0.0",
1796
+ title: "Identity & RBAC",
1797
+ description: "User identity, organization management, and role-based access control",
1798
+ domain: "platform",
1799
+ owners: ["@platform.identity-rbac"],
1800
+ tags: ["identity", "rbac", "users", "organizations", "permissions"],
1801
+ stability: "stable"
1802
+ },
1803
+ operations: [
1804
+ { key: "identity.user.create", version: "1.0.0" },
1805
+ { key: "identity.user.update", version: "1.0.0" },
1806
+ { key: "identity.user.delete", version: "1.0.0" },
1807
+ { key: "identity.user.me", version: "1.0.0" },
1808
+ { key: "identity.user.list", version: "1.0.0" },
1809
+ { key: "identity.org.create", version: "1.0.0" },
1810
+ { key: "identity.org.update", version: "1.0.0" },
1811
+ { key: "identity.org.get", version: "1.0.0" },
1812
+ { key: "identity.org.list", version: "1.0.0" },
1813
+ { key: "identity.org.invite", version: "1.0.0" },
1814
+ { key: "identity.org.invite.accept", version: "1.0.0" },
1815
+ { key: "identity.org.member.remove", version: "1.0.0" },
1816
+ { key: "identity.org.members.list", version: "1.0.0" },
1817
+ { key: "identity.rbac.role.create", version: "1.0.0" },
1818
+ { key: "identity.rbac.role.update", version: "1.0.0" },
1819
+ { key: "identity.rbac.role.delete", version: "1.0.0" },
1820
+ { key: "identity.rbac.role.list", version: "1.0.0" },
1821
+ { key: "identity.rbac.assign", version: "1.0.0" },
1822
+ { key: "identity.rbac.revoke", version: "1.0.0" },
1823
+ { key: "identity.rbac.check", version: "1.0.0" },
1824
+ { key: "identity.rbac.permissions", version: "1.0.0" }
1825
+ ],
1826
+ events: [
1827
+ { key: "user.created", version: "1.0.0" },
1828
+ { key: "user.updated", version: "1.0.0" },
1829
+ { key: "user.deleted", version: "1.0.0" },
1830
+ { key: "user.email_verified", version: "1.0.0" },
1831
+ { key: "org.created", version: "1.0.0" },
1832
+ { key: "org.updated", version: "1.0.0" },
1833
+ { key: "org.deleted", version: "1.0.0" },
1834
+ { key: "org.member.added", version: "1.0.0" },
1835
+ { key: "org.member.removed", version: "1.0.0" },
1836
+ { key: "org.member.role_changed", version: "1.0.0" },
1837
+ { key: "org.invite.sent", version: "1.0.0" },
1838
+ { key: "org.invite.accepted", version: "1.0.0" },
1839
+ { key: "org.invite.declined", version: "1.0.0" },
1840
+ { key: "role.assigned", version: "1.0.0" },
1841
+ { key: "role.revoked", version: "1.0.0" }
1842
+ ],
1843
+ presentations: [],
1844
+ opToPresentation: [],
1845
+ presentationsTargets: [],
1846
+ capabilities: {
1847
+ provides: [
1848
+ { key: "identity", version: "1.0.0" },
1849
+ { key: "rbac", version: "1.0.0" }
1850
+ ],
1851
+ requires: []
1852
+ }
1853
+ });
1854
+
1855
+ // src/policies/engine.ts
1856
+ var Permission = {
1857
+ USER_CREATE: "user.create",
1858
+ USER_READ: "user.read",
1859
+ USER_UPDATE: "user.update",
1860
+ USER_DELETE: "user.delete",
1861
+ USER_LIST: "user.list",
1862
+ USER_MANAGE: "user.manage",
1863
+ ORG_CREATE: "org.create",
1864
+ ORG_READ: "org.read",
1865
+ ORG_UPDATE: "org.update",
1866
+ ORG_DELETE: "org.delete",
1867
+ ORG_LIST: "org.list",
1868
+ MEMBER_INVITE: "member.invite",
1869
+ MEMBER_REMOVE: "member.remove",
1870
+ MEMBER_UPDATE_ROLE: "member.update_role",
1871
+ MEMBER_LIST: "member.list",
1872
+ MANAGE_MEMBERS: "org.manage_members",
1873
+ TEAM_CREATE: "team.create",
1874
+ TEAM_UPDATE: "team.update",
1875
+ TEAM_DELETE: "team.delete",
1876
+ TEAM_MANAGE: "team.manage",
1877
+ ROLE_CREATE: "role.create",
1878
+ ROLE_UPDATE: "role.update",
1879
+ ROLE_DELETE: "role.delete",
1880
+ ROLE_ASSIGN: "role.assign",
1881
+ ROLE_REVOKE: "role.revoke",
1882
+ BILLING_VIEW: "billing.view",
1883
+ BILLING_MANAGE: "billing.manage",
1884
+ PROJECT_CREATE: "project.create",
1885
+ PROJECT_READ: "project.read",
1886
+ PROJECT_UPDATE: "project.update",
1887
+ PROJECT_DELETE: "project.delete",
1888
+ PROJECT_MANAGE: "project.manage",
1889
+ ADMIN_ACCESS: "admin.access",
1890
+ ADMIN_IMPERSONATE: "admin.impersonate"
1891
+ };
1892
+ var StandardRole = {
1893
+ OWNER: {
1894
+ name: "owner",
1895
+ description: "Organization owner with full access",
1896
+ permissions: Object.values(Permission)
1897
+ },
1898
+ ADMIN: {
1899
+ name: "admin",
1900
+ description: "Administrator with most permissions",
1901
+ permissions: [
1902
+ Permission.USER_READ,
1903
+ Permission.USER_LIST,
1904
+ Permission.ORG_READ,
1905
+ Permission.ORG_UPDATE,
1906
+ Permission.MEMBER_INVITE,
1907
+ Permission.MEMBER_REMOVE,
1908
+ Permission.MEMBER_UPDATE_ROLE,
1909
+ Permission.MEMBER_LIST,
1910
+ Permission.MANAGE_MEMBERS,
1911
+ Permission.TEAM_CREATE,
1912
+ Permission.TEAM_UPDATE,
1913
+ Permission.TEAM_DELETE,
1914
+ Permission.TEAM_MANAGE,
1915
+ Permission.PROJECT_CREATE,
1916
+ Permission.PROJECT_READ,
1917
+ Permission.PROJECT_UPDATE,
1918
+ Permission.PROJECT_DELETE,
1919
+ Permission.PROJECT_MANAGE,
1920
+ Permission.BILLING_VIEW
1921
+ ]
1922
+ },
1923
+ MEMBER: {
1924
+ name: "member",
1925
+ description: "Regular organization member",
1926
+ permissions: [
1927
+ Permission.USER_READ,
1928
+ Permission.ORG_READ,
1929
+ Permission.MEMBER_LIST,
1930
+ Permission.PROJECT_READ,
1931
+ Permission.PROJECT_CREATE
1932
+ ]
1933
+ },
1934
+ VIEWER: {
1935
+ name: "viewer",
1936
+ description: "Read-only access",
1937
+ permissions: [
1938
+ Permission.USER_READ,
1939
+ Permission.ORG_READ,
1940
+ Permission.MEMBER_LIST,
1941
+ Permission.PROJECT_READ
1942
+ ]
1943
+ }
1944
+ };
1945
+
1946
+ class RBACPolicyEngine {
1947
+ roleCache = new Map;
1948
+ bindingCache = new Map;
1949
+ async checkPermission(input, bindings) {
1950
+ const { userId, orgId, permission } = input;
1951
+ const now = new Date;
1952
+ const userBindings = bindings.filter((b) => b.targetType === "user" && b.targetId === userId);
1953
+ const orgBindings = orgId ? bindings.filter((b) => b.targetType === "organization" && b.targetId === orgId) : [];
1954
+ const allBindings = [...userBindings, ...orgBindings];
1955
+ const activeBindings = allBindings.filter((b) => !b.expiresAt || b.expiresAt > now);
1956
+ if (activeBindings.length === 0) {
1957
+ return {
1958
+ allowed: false,
1959
+ reason: "No active role bindings found"
1960
+ };
1961
+ }
1962
+ for (const binding of activeBindings) {
1963
+ if (binding.role.permissions.includes(permission)) {
1964
+ return {
1965
+ allowed: true,
1966
+ matchedRole: binding.role.name
1967
+ };
1968
+ }
1969
+ }
1970
+ return {
1971
+ allowed: false,
1972
+ reason: `No role grants the "${permission}" permission`
1973
+ };
1974
+ }
1975
+ async getPermissions(userId, orgId, bindings) {
1976
+ const now = new Date;
1977
+ const userBindings = bindings.filter((b) => b.targetType === "user" && b.targetId === userId);
1978
+ const orgBindings = orgId ? bindings.filter((b) => b.targetType === "organization" && b.targetId === orgId) : [];
1979
+ const allBindings = [...userBindings, ...orgBindings];
1980
+ const activeBindings = allBindings.filter((b) => !b.expiresAt || b.expiresAt > now);
1981
+ const permissions = new Set;
1982
+ const roles = [];
1983
+ for (const binding of activeBindings) {
1984
+ roles.push(binding.role);
1985
+ for (const perm of binding.role.permissions) {
1986
+ permissions.add(perm);
1987
+ }
1988
+ }
1989
+ return { permissions, roles };
1990
+ }
1991
+ async hasAnyPermission(userId, orgId, permissions, bindings) {
1992
+ const { permissions: userPerms } = await this.getPermissions(userId, orgId, bindings);
1993
+ return permissions.some((p) => userPerms.has(p));
1994
+ }
1995
+ async hasAllPermissions(userId, orgId, permissions, bindings) {
1996
+ const { permissions: userPerms } = await this.getPermissions(userId, orgId, bindings);
1997
+ return permissions.every((p) => userPerms.has(p));
1998
+ }
1999
+ }
2000
+ function createRBACEngine() {
2001
+ return new RBACPolicyEngine;
2002
+ }
2003
+ export {
2004
+ identityRbacSchemaContribution,
2005
+ identityRbacEntities,
2006
+ createRBACEngine,
2007
+ VerificationEntity,
2008
+ UserUpdatedEvent,
2009
+ UserProfileModel,
2010
+ UserEntity,
2011
+ UserEmailVerifiedEvent,
2012
+ UserDeletedPayloadModel,
2013
+ UserDeletedEvent,
2014
+ UserCreatedEvent,
2015
+ UpdateUserInputModel,
2016
+ UpdateUserContract,
2017
+ UpdateRoleInputModel,
2018
+ UpdateRoleContract,
2019
+ UpdateOrgInputModel,
2020
+ UpdateOrgContract,
2021
+ TeamMemberEntity,
2022
+ TeamEntity,
2023
+ SuccessResultModel,
2024
+ StandardRole,
2025
+ SessionEntity,
2026
+ RoleRevokedEvent,
2027
+ RoleModel,
2028
+ RoleEntity,
2029
+ RoleAssignedEvent,
2030
+ RevokeRoleInputModel,
2031
+ RevokeRoleContract,
2032
+ RemoveMemberInputModel,
2033
+ RemoveMemberContract,
2034
+ RBACPolicyEngine,
2035
+ PolicyBindingModel,
2036
+ PolicyBindingEntity,
2037
+ PermissionEntity,
2038
+ PermissionCheckResultModel,
2039
+ Permission,
2040
+ PasskeyEntity,
2041
+ OrganizationWithRoleModel,
2042
+ OrganizationTypeEnum,
2043
+ OrganizationModel,
2044
+ OrganizationEntity,
2045
+ OrgUpdatedEvent,
2046
+ OrgMemberRoleChangedEvent,
2047
+ OrgMemberRemovedEvent,
2048
+ OrgMemberAddedEvent,
2049
+ OrgInviteSentEvent,
2050
+ OrgInviteDeclinedEvent,
2051
+ OrgInviteAcceptedEvent,
2052
+ OrgDeletedEvent,
2053
+ OrgCreatedEvent,
2054
+ MemberUserModel,
2055
+ MemberRemovedPayloadModel,
2056
+ MemberModel,
2057
+ MemberEntity,
2058
+ ListUsersOutputModel,
2059
+ ListUsersInputModel,
2060
+ ListUsersContract,
2061
+ ListUserPermissionsOutputModel,
2062
+ ListUserPermissionsInputModel,
2063
+ ListUserPermissionsContract,
2064
+ ListUserOrgsOutputModel,
2065
+ ListUserOrgsContract,
2066
+ ListRolesOutputModel,
2067
+ ListRolesContract,
2068
+ ListMembersOutputModel,
2069
+ ListMembersInputModel,
2070
+ ListMembersContract,
2071
+ InviteMemberInputModel,
2072
+ InviteMemberContract,
2073
+ InvitationModel,
2074
+ InvitationEntity,
2075
+ IdentityRbacFeature,
2076
+ IdentityRbacEvents,
2077
+ GetOrgInputModel,
2078
+ GetOrgContract,
2079
+ GetCurrentUserContract,
2080
+ DeleteUserInputModel,
2081
+ DeleteUserContract,
2082
+ DeleteRoleInputModel,
2083
+ DeleteRoleContract,
2084
+ CreateUserInputModel,
2085
+ CreateUserContract,
2086
+ CreateRoleInputModel,
2087
+ CreateRoleContract,
2088
+ CreateOrgInputModel,
2089
+ CreateOrgContract,
2090
+ CheckPermissionInputModel,
2091
+ CheckPermissionContract,
2092
+ BindingIdPayloadModel,
2093
+ AssignRoleInputModel,
2094
+ AssignRoleContract,
2095
+ ApiKeyEntity,
2096
+ AccountEntity,
2097
+ AcceptInviteInputModel,
2098
+ AcceptInviteContract
2099
+ };