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