@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,235 @@
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
+ export {
222
+ UserProfileModel,
223
+ UserDeletedPayloadModel,
224
+ UpdateUserInputModel,
225
+ UpdateUserContract,
226
+ SuccessResultModel,
227
+ ListUsersOutputModel,
228
+ ListUsersInputModel,
229
+ ListUsersContract,
230
+ GetCurrentUserContract,
231
+ DeleteUserInputModel,
232
+ DeleteUserContract,
233
+ CreateUserInputModel,
234
+ CreateUserContract
235
+ };
@@ -0,0 +1,464 @@
1
+ // src/entities/user.ts
2
+ import { defineEntity, field, index } from "@contractspec/lib.schema";
3
+ var UserEntity = defineEntity({
4
+ name: "User",
5
+ description: "A user of the platform. Users hold core profile information and authenticate via Account records.",
6
+ schema: "lssm_sigil",
7
+ map: "user",
8
+ fields: {
9
+ id: field.id({ description: "Unique user identifier" }),
10
+ email: field.email({ isUnique: true, description: "User email address" }),
11
+ emailVerified: field.boolean({
12
+ default: false,
13
+ description: "Whether email has been verified"
14
+ }),
15
+ name: field.string({ isOptional: true, description: "Display name" }),
16
+ firstName: field.string({ isOptional: true, description: "First name" }),
17
+ lastName: field.string({ isOptional: true, description: "Last name" }),
18
+ locale: field.string({
19
+ isOptional: true,
20
+ description: 'User locale (e.g., "en-US")'
21
+ }),
22
+ timezone: field.string({
23
+ isOptional: true,
24
+ description: 'Olson timezone (e.g., "Europe/Paris")'
25
+ }),
26
+ imageUrl: field.url({
27
+ isOptional: true,
28
+ description: "URL of avatar or profile picture"
29
+ }),
30
+ image: field.string({
31
+ isOptional: true,
32
+ description: "Legacy image field"
33
+ }),
34
+ metadata: field.json({
35
+ isOptional: true,
36
+ description: "Arbitrary user metadata"
37
+ }),
38
+ onboardingCompleted: field.boolean({
39
+ default: false,
40
+ description: "Whether onboarding is complete"
41
+ }),
42
+ onboardingStep: field.string({
43
+ isOptional: true,
44
+ description: "Current onboarding step"
45
+ }),
46
+ whitelistedAt: field.dateTime({
47
+ isOptional: true,
48
+ description: "When user was whitelisted"
49
+ }),
50
+ role: field.string({
51
+ isOptional: true,
52
+ default: '"user"',
53
+ description: "User role (user, admin)"
54
+ }),
55
+ banned: field.boolean({
56
+ default: false,
57
+ description: "Whether user is banned"
58
+ }),
59
+ banReason: field.string({
60
+ isOptional: true,
61
+ description: "Reason for ban"
62
+ }),
63
+ banExpires: field.dateTime({
64
+ isOptional: true,
65
+ description: "When ban expires"
66
+ }),
67
+ phoneNumber: field.string({
68
+ isOptional: true,
69
+ isUnique: true,
70
+ description: "Phone number"
71
+ }),
72
+ phoneNumberVerified: field.boolean({
73
+ default: false,
74
+ description: "Whether phone is verified"
75
+ }),
76
+ createdAt: field.createdAt(),
77
+ updatedAt: field.updatedAt(),
78
+ sessions: field.hasMany("Session"),
79
+ accounts: field.hasMany("Account"),
80
+ memberships: field.hasMany("Member"),
81
+ invitations: field.hasMany("Invitation"),
82
+ teamMemberships: field.hasMany("TeamMember"),
83
+ policyBindings: field.hasMany("PolicyBinding"),
84
+ apiKeys: field.hasMany("ApiKey"),
85
+ passkeys: field.hasMany("Passkey")
86
+ }
87
+ });
88
+ var SessionEntity = defineEntity({
89
+ name: "Session",
90
+ description: "Represents a login session (e.g., web session or API token).",
91
+ schema: "lssm_sigil",
92
+ map: "session",
93
+ fields: {
94
+ id: field.id(),
95
+ userId: field.foreignKey(),
96
+ expiresAt: field.dateTime({ description: "Session expiration time" }),
97
+ token: field.string({ isUnique: true, description: "Session token" }),
98
+ ipAddress: field.string({
99
+ isOptional: true,
100
+ description: "Client IP address"
101
+ }),
102
+ userAgent: field.string({
103
+ isOptional: true,
104
+ description: "Client user agent"
105
+ }),
106
+ impersonatedBy: field.string({
107
+ isOptional: true,
108
+ description: "Admin impersonating this session"
109
+ }),
110
+ activeOrganizationId: field.string({
111
+ isOptional: true,
112
+ description: "Active org context"
113
+ }),
114
+ activeTeamId: field.string({
115
+ isOptional: true,
116
+ description: "Active team context"
117
+ }),
118
+ createdAt: field.createdAt(),
119
+ updatedAt: field.updatedAt(),
120
+ user: field.belongsTo("User", ["userId"], ["id"], { onDelete: "Cascade" })
121
+ }
122
+ });
123
+ var AccountEntity = defineEntity({
124
+ name: "Account",
125
+ description: "External authentication accounts (OAuth, password, etc.).",
126
+ schema: "lssm_sigil",
127
+ map: "account",
128
+ fields: {
129
+ id: field.id(),
130
+ accountId: field.string({ description: "Account ID from provider" }),
131
+ providerId: field.string({ description: "Provider identifier" }),
132
+ userId: field.foreignKey(),
133
+ accessToken: field.string({ isOptional: true }),
134
+ refreshToken: field.string({ isOptional: true }),
135
+ idToken: field.string({ isOptional: true }),
136
+ accessTokenExpiresAt: field.dateTime({ isOptional: true }),
137
+ refreshTokenExpiresAt: field.dateTime({ isOptional: true }),
138
+ scope: field.string({ isOptional: true }),
139
+ password: field.string({
140
+ isOptional: true,
141
+ description: "Hashed password for password providers"
142
+ }),
143
+ createdAt: field.createdAt(),
144
+ updatedAt: field.updatedAt(),
145
+ user: field.belongsTo("User", ["userId"], ["id"], { onDelete: "Cascade" })
146
+ },
147
+ indexes: [index.unique(["accountId", "providerId"])]
148
+ });
149
+ var VerificationEntity = defineEntity({
150
+ name: "Verification",
151
+ description: "Verification tokens for email/phone confirmation.",
152
+ schema: "lssm_sigil",
153
+ map: "verification",
154
+ fields: {
155
+ id: field.uuid(),
156
+ identifier: field.string({ description: "Email or phone being verified" }),
157
+ value: field.string({ description: "Verification code/token" }),
158
+ expiresAt: field.dateTime({ description: "Token expiration" }),
159
+ createdAt: field.createdAt(),
160
+ updatedAt: field.updatedAt()
161
+ }
162
+ });
163
+
164
+ // src/entities/organization.ts
165
+ import {
166
+ defineEntity as defineEntity2,
167
+ defineEntityEnum,
168
+ field as field2,
169
+ index as index2
170
+ } from "@contractspec/lib.schema";
171
+ var OrganizationTypeEnum = defineEntityEnum({
172
+ name: "OrganizationType",
173
+ values: ["PLATFORM_ADMIN", "CONTRACT_SPEC_CUSTOMER"],
174
+ schema: "lssm_sigil",
175
+ description: "Type of organization in the platform."
176
+ });
177
+ var OrganizationEntity = defineEntity2({
178
+ name: "Organization",
179
+ description: "An organization is a tenant boundary grouping users.",
180
+ schema: "lssm_sigil",
181
+ map: "organization",
182
+ fields: {
183
+ id: field2.id({ description: "Unique organization identifier" }),
184
+ name: field2.string({ description: "Organization display name" }),
185
+ slug: field2.string({
186
+ isOptional: true,
187
+ isUnique: true,
188
+ description: "URL-friendly identifier"
189
+ }),
190
+ logo: field2.url({ isOptional: true, description: "Organization logo URL" }),
191
+ description: field2.string({
192
+ isOptional: true,
193
+ description: "Organization description"
194
+ }),
195
+ metadata: field2.json({
196
+ isOptional: true,
197
+ description: "Arbitrary organization metadata"
198
+ }),
199
+ type: field2.enum("OrganizationType", { description: "Organization type" }),
200
+ onboardingCompleted: field2.boolean({ default: false }),
201
+ onboardingStep: field2.string({ isOptional: true }),
202
+ referralCode: field2.string({
203
+ isOptional: true,
204
+ isUnique: true,
205
+ description: "Unique referral code"
206
+ }),
207
+ referredBy: field2.string({
208
+ isOptional: true,
209
+ description: "ID of referring user"
210
+ }),
211
+ createdAt: field2.createdAt(),
212
+ updatedAt: field2.updatedAt(),
213
+ members: field2.hasMany("Member"),
214
+ invitations: field2.hasMany("Invitation"),
215
+ teams: field2.hasMany("Team"),
216
+ policyBindings: field2.hasMany("PolicyBinding")
217
+ },
218
+ enums: [OrganizationTypeEnum]
219
+ });
220
+ var MemberEntity = defineEntity2({
221
+ name: "Member",
222
+ description: "Membership of a user in an organization with a role.",
223
+ schema: "lssm_sigil",
224
+ map: "member",
225
+ fields: {
226
+ id: field2.id(),
227
+ userId: field2.foreignKey(),
228
+ organizationId: field2.foreignKey(),
229
+ role: field2.string({
230
+ description: "Role in organization (owner, admin, member)"
231
+ }),
232
+ createdAt: field2.createdAt(),
233
+ user: field2.belongsTo("User", ["userId"], ["id"], { onDelete: "Cascade" }),
234
+ organization: field2.belongsTo("Organization", ["organizationId"], ["id"], {
235
+ onDelete: "Cascade"
236
+ })
237
+ },
238
+ indexes: [index2.unique(["userId", "organizationId"])]
239
+ });
240
+ var InvitationEntity = defineEntity2({
241
+ name: "Invitation",
242
+ description: "An invitation to join an organization.",
243
+ schema: "lssm_sigil",
244
+ map: "invitation",
245
+ fields: {
246
+ id: field2.id(),
247
+ organizationId: field2.foreignKey(),
248
+ email: field2.email({ description: "Invited email address" }),
249
+ role: field2.string({
250
+ isOptional: true,
251
+ description: "Role to assign on acceptance"
252
+ }),
253
+ status: field2.string({
254
+ default: '"pending"',
255
+ description: "Invitation status"
256
+ }),
257
+ acceptedAt: field2.dateTime({ isOptional: true }),
258
+ expiresAt: field2.dateTime({ isOptional: true }),
259
+ inviterId: field2.foreignKey({
260
+ description: "User who sent the invitation"
261
+ }),
262
+ teamId: field2.string({ isOptional: true }),
263
+ createdAt: field2.createdAt(),
264
+ updatedAt: field2.updatedAt(),
265
+ organization: field2.belongsTo("Organization", ["organizationId"], ["id"], {
266
+ onDelete: "Cascade"
267
+ }),
268
+ inviter: field2.belongsTo("User", ["inviterId"], ["id"], {
269
+ onDelete: "Cascade"
270
+ }),
271
+ team: field2.belongsTo("Team", ["teamId"], ["id"], { onDelete: "Cascade" })
272
+ }
273
+ });
274
+ var TeamEntity = defineEntity2({
275
+ name: "Team",
276
+ description: "Team within an organization.",
277
+ schema: "lssm_sigil",
278
+ map: "team",
279
+ fields: {
280
+ id: field2.id(),
281
+ name: field2.string({ description: "Team name" }),
282
+ organizationId: field2.foreignKey(),
283
+ createdAt: field2.createdAt(),
284
+ updatedAt: field2.updatedAt(),
285
+ organization: field2.belongsTo("Organization", ["organizationId"], ["id"], {
286
+ onDelete: "Cascade"
287
+ }),
288
+ members: field2.hasMany("TeamMember"),
289
+ invitations: field2.hasMany("Invitation")
290
+ }
291
+ });
292
+ var TeamMemberEntity = defineEntity2({
293
+ name: "TeamMember",
294
+ description: "Team membership for a user.",
295
+ schema: "lssm_sigil",
296
+ map: "team_member",
297
+ fields: {
298
+ id: field2.id(),
299
+ teamId: field2.foreignKey(),
300
+ userId: field2.foreignKey(),
301
+ createdAt: field2.createdAt(),
302
+ team: field2.belongsTo("Team", ["teamId"], ["id"], { onDelete: "Cascade" }),
303
+ user: field2.belongsTo("User", ["userId"], ["id"], { onDelete: "Cascade" })
304
+ }
305
+ });
306
+
307
+ // src/entities/rbac.ts
308
+ import { defineEntity as defineEntity3, field as field3, index as index3 } from "@contractspec/lib.schema";
309
+ var RoleEntity = defineEntity3({
310
+ name: "Role",
311
+ description: "A role defines a named set of permissions.",
312
+ schema: "lssm_sigil",
313
+ map: "role",
314
+ fields: {
315
+ id: field3.id(),
316
+ name: field3.string({ isUnique: true, description: "Unique role name" }),
317
+ description: field3.string({
318
+ isOptional: true,
319
+ description: "Role description"
320
+ }),
321
+ permissions: field3.string({
322
+ isArray: true,
323
+ description: "Array of permission names"
324
+ }),
325
+ createdAt: field3.createdAt(),
326
+ updatedAt: field3.updatedAt(),
327
+ policyBindings: field3.hasMany("PolicyBinding")
328
+ }
329
+ });
330
+ var PermissionEntity = defineEntity3({
331
+ name: "Permission",
332
+ description: "A permission represents an atomic access right.",
333
+ schema: "lssm_sigil",
334
+ map: "permission",
335
+ fields: {
336
+ id: field3.id(),
337
+ name: field3.string({
338
+ isUnique: true,
339
+ description: "Unique permission name"
340
+ }),
341
+ description: field3.string({
342
+ isOptional: true,
343
+ description: "Permission description"
344
+ }),
345
+ createdAt: field3.createdAt(),
346
+ updatedAt: field3.updatedAt()
347
+ }
348
+ });
349
+ var PolicyBindingEntity = defineEntity3({
350
+ name: "PolicyBinding",
351
+ description: "Binds roles to principals (users or organizations).",
352
+ schema: "lssm_sigil",
353
+ map: "policy_binding",
354
+ fields: {
355
+ id: field3.id(),
356
+ roleId: field3.foreignKey(),
357
+ targetType: field3.string({ description: '"user" or "organization"' }),
358
+ targetId: field3.string({ description: "ID of User or Organization" }),
359
+ expiresAt: field3.dateTime({
360
+ isOptional: true,
361
+ description: "When binding expires"
362
+ }),
363
+ createdAt: field3.createdAt(),
364
+ userId: field3.string({ isOptional: true }),
365
+ organizationId: field3.string({ isOptional: true }),
366
+ role: field3.belongsTo("Role", ["roleId"], ["id"], { onDelete: "Cascade" }),
367
+ user: field3.belongsTo("User", ["userId"], ["id"]),
368
+ organization: field3.belongsTo("Organization", ["organizationId"], ["id"])
369
+ },
370
+ indexes: [index3.on(["targetType", "targetId"])]
371
+ });
372
+ var ApiKeyEntity = defineEntity3({
373
+ name: "ApiKey",
374
+ description: "API keys for programmatic access.",
375
+ schema: "lssm_sigil",
376
+ map: "api_key",
377
+ fields: {
378
+ id: field3.id(),
379
+ name: field3.string({ description: "API key name" }),
380
+ start: field3.string({
381
+ description: "Starting characters for identification"
382
+ }),
383
+ prefix: field3.string({ description: "API key prefix" }),
384
+ key: field3.string({ description: "Hashed API key" }),
385
+ userId: field3.foreignKey(),
386
+ refillInterval: field3.int({ description: "Refill interval in ms" }),
387
+ refillAmount: field3.int({ description: "Amount to refill" }),
388
+ lastRefillAt: field3.dateTime(),
389
+ remaining: field3.int({ description: "Remaining requests" }),
390
+ requestCount: field3.int({ description: "Total requests made" }),
391
+ lastRequest: field3.dateTime(),
392
+ enabled: field3.boolean({ default: true }),
393
+ rateLimitEnabled: field3.boolean({ default: true }),
394
+ rateLimitTimeWindow: field3.int({ description: "Rate limit window in ms" }),
395
+ rateLimitMax: field3.int({ description: "Max requests in window" }),
396
+ expiresAt: field3.dateTime(),
397
+ permissions: field3.string({ isArray: true }),
398
+ metadata: field3.json({ isOptional: true }),
399
+ createdAt: field3.createdAt(),
400
+ updatedAt: field3.updatedAt(),
401
+ user: field3.belongsTo("User", ["userId"], ["id"], { onDelete: "Cascade" })
402
+ }
403
+ });
404
+ var PasskeyEntity = defineEntity3({
405
+ name: "Passkey",
406
+ description: "WebAuthn passkeys for passwordless authentication.",
407
+ schema: "lssm_sigil",
408
+ map: "passkey",
409
+ fields: {
410
+ id: field3.id(),
411
+ name: field3.string({ description: "Passkey name" }),
412
+ publicKey: field3.string({ description: "Public key" }),
413
+ userId: field3.foreignKey(),
414
+ credentialID: field3.string({ description: "Credential ID" }),
415
+ counter: field3.int({ description: "Counter" }),
416
+ deviceType: field3.string({ description: "Device type" }),
417
+ backedUp: field3.boolean({ description: "Whether passkey is backed up" }),
418
+ transports: field3.string({ description: "Transports" }),
419
+ aaguid: field3.string({ description: "Authenticator GUID" }),
420
+ createdAt: field3.createdAt(),
421
+ user: field3.belongsTo("User", ["userId"], ["id"], { onDelete: "Cascade" })
422
+ }
423
+ });
424
+ // src/entities/index.ts
425
+ var identityRbacEntities = [
426
+ UserEntity,
427
+ SessionEntity,
428
+ AccountEntity,
429
+ VerificationEntity,
430
+ OrganizationEntity,
431
+ MemberEntity,
432
+ InvitationEntity,
433
+ TeamEntity,
434
+ TeamMemberEntity,
435
+ RoleEntity,
436
+ PermissionEntity,
437
+ PolicyBindingEntity,
438
+ ApiKeyEntity,
439
+ PasskeyEntity
440
+ ];
441
+ var identityRbacSchemaContribution = {
442
+ moduleId: "@contractspec/lib.identity-rbac",
443
+ entities: identityRbacEntities,
444
+ enums: [OrganizationTypeEnum]
445
+ };
446
+ export {
447
+ identityRbacSchemaContribution,
448
+ identityRbacEntities,
449
+ VerificationEntity,
450
+ UserEntity,
451
+ TeamMemberEntity,
452
+ TeamEntity,
453
+ SessionEntity,
454
+ RoleEntity,
455
+ PolicyBindingEntity,
456
+ PermissionEntity,
457
+ PasskeyEntity,
458
+ OrganizationTypeEnum,
459
+ OrganizationEntity,
460
+ MemberEntity,
461
+ InvitationEntity,
462
+ ApiKeyEntity,
463
+ AccountEntity
464
+ };