@elevasis/core 0.28.0 → 0.30.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 (36) hide show
  1. package/dist/auth/index.d.ts +5289 -0
  2. package/dist/auth/index.js +595 -0
  3. package/dist/index.d.ts +11 -11
  4. package/dist/knowledge/index.d.ts +1 -1
  5. package/dist/organization-model/index.d.ts +11 -11
  6. package/dist/test-utils/index.d.ts +24 -1
  7. package/package.json +7 -3
  8. package/src/__tests__/publish.test.ts +8 -7
  9. package/src/auth/__tests__/access-key-coverage.test.ts +42 -0
  10. package/src/auth/__tests__/access-key-scan.ts +117 -0
  11. package/src/auth/__tests__/access-keys.test.ts +81 -0
  12. package/src/auth/__tests__/access-model.test.ts +257 -0
  13. package/src/auth/__tests__/access-test-fixtures.ts +50 -0
  14. package/src/auth/__tests__/key-catalog-drift.test.ts +33 -0
  15. package/src/auth/__tests__/platform-admin-bypass-parity.test.ts +67 -0
  16. package/src/auth/access-keys.ts +173 -0
  17. package/src/auth/access-model.ts +185 -0
  18. package/src/auth/index.ts +6 -2
  19. package/src/auth/multi-tenancy/memberships/membership.ts +2 -4
  20. package/src/auth/multi-tenancy/permissions.ts +1 -1
  21. package/src/auth/multi-tenancy/types.ts +3 -12
  22. package/src/business/acquisition/api-schemas.test.ts +59 -8
  23. package/src/business/acquisition/api-schemas.ts +10 -5
  24. package/src/business/acquisition/build-templates.test.ts +187 -240
  25. package/src/business/acquisition/build-templates.ts +87 -98
  26. package/src/business/acquisition/types.ts +390 -389
  27. package/src/execution/engine/index.ts +6 -4
  28. package/src/execution/engine/tools/lead-service-types.ts +63 -34
  29. package/src/execution/engine/tools/platform/acquisition/types.ts +7 -8
  30. package/src/execution/engine/tools/registry.ts +6 -4
  31. package/src/execution/engine/tools/tool-maps.ts +23 -1
  32. package/src/organization-model/domains/prospecting.ts +2 -327
  33. package/src/organization-model/migration-helpers.ts +16 -12
  34. package/src/reference/_generated/contracts.md +352 -328
  35. package/src/reference/glossary.md +8 -6
  36. package/src/supabase/database.types.ts +13 -0
@@ -0,0 +1,595 @@
1
+ import { z } from 'zod';
2
+
3
+ // src/auth/multi-tenancy/theme-presets.ts
4
+ var THEME_PRESETS = [
5
+ "default",
6
+ "tactical",
7
+ "regal",
8
+ "cyber-volt",
9
+ "aurora",
10
+ "rose-gold",
11
+ "midnight",
12
+ "titanium",
13
+ "canopy",
14
+ "slate",
15
+ "cyber-strike",
16
+ "cyber-chrome",
17
+ "cyber-void",
18
+ "nirvana",
19
+ "wave",
20
+ "synapse",
21
+ "cortex",
22
+ "helios",
23
+ "graphite",
24
+ "quarry"
25
+ ];
26
+ var ThemePresetEnum = z.enum(THEME_PRESETS);
27
+
28
+ // src/auth/multi-tenancy/permissions.ts
29
+ var PERMISSIONS = {
30
+ ORG_READ: "org.read",
31
+ ORG_MANAGE: "org.manage",
32
+ ORG_DELETE: "org.delete",
33
+ MEMBERS_MANAGE: "members.manage",
34
+ ROLES_MANAGE: "roles.manage",
35
+ SECRETS_MANAGE: "secrets.manage",
36
+ OPERATIONS_READ: "operations.read",
37
+ OPERATIONS_MANAGE: "operations.manage",
38
+ ACQUISITION_MANAGE: "acquisition.manage",
39
+ PROJECTS_MANAGE: "projects.manage",
40
+ CLIENTS_MANAGE: "clients.manage"
41
+ };
42
+ var PERMISSION_CATALOG = [
43
+ {
44
+ key: "org.read",
45
+ description: "Read organization profile and listings",
46
+ isOrgGrantable: true
47
+ },
48
+ {
49
+ key: "org.manage",
50
+ description: "Update organization settings",
51
+ isOrgGrantable: false
52
+ },
53
+ {
54
+ key: "org.delete",
55
+ description: "Delete the organization (owner-only)",
56
+ isOrgGrantable: false
57
+ },
58
+ {
59
+ key: "members.manage",
60
+ description: "Invite, remove, and reassign roles for members",
61
+ isOrgGrantable: false
62
+ },
63
+ {
64
+ key: "roles.manage",
65
+ description: "Grant or revoke privileged system roles (owner, admin) within the organization",
66
+ isOrgGrantable: false
67
+ },
68
+ {
69
+ key: "secrets.manage",
70
+ description: "Create, update, and delete API keys and credentials",
71
+ isOrgGrantable: false
72
+ },
73
+ {
74
+ key: "operations.read",
75
+ description: "View executions, sessions, schedules, and command queue",
76
+ isOrgGrantable: true
77
+ },
78
+ {
79
+ key: "operations.manage",
80
+ description: "Run and modify executions, sessions, schedules, queue",
81
+ isOrgGrantable: true
82
+ },
83
+ {
84
+ key: "acquisition.manage",
85
+ description: "Create, update, and delete acquisition records (acq_companies, acq_contacts, acq_deals, acq_lists*, acq_content*, acquisition storage files)",
86
+ isOrgGrantable: false
87
+ },
88
+ {
89
+ key: "projects.manage",
90
+ description: "Create, update, and delete project records (prj_projects, prj_milestones, prj_tasks, prj_notes)",
91
+ isOrgGrantable: false
92
+ },
93
+ {
94
+ key: "clients.manage",
95
+ description: "Create, update, and delete client hub records (clients, clt_* satellites)",
96
+ isOrgGrantable: false
97
+ }
98
+ ];
99
+ var PERMISSION_KEY_SET = new Set(PERMISSION_CATALOG.map((p) => p.key));
100
+ function isPermissionKey(value) {
101
+ return typeof value === "string" && PERMISSION_KEY_SET.has(value);
102
+ }
103
+ var PermissionKeySchema = z.string().min(1).max(100);
104
+ var OrgRoleParamsSchema = z.object({
105
+ orgId: z.string().uuid(),
106
+ roleId: z.string().uuid()
107
+ }).strict();
108
+ var OrgRolesParamsSchema = z.object({
109
+ orgId: z.string().uuid()
110
+ }).strict();
111
+ var MembershipRoleParamsSchema = z.object({
112
+ membershipId: z.string().min(1),
113
+ roleId: z.string().uuid()
114
+ }).strict();
115
+ var MembershipParamsSchema = z.object({
116
+ membershipId: z.string().min(1)
117
+ }).strict();
118
+ var CreateOrgRoleRequestSchema = z.object({
119
+ name: z.string().min(1).max(100).trim(),
120
+ slug: z.string().min(1).max(100).regex(/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/, "Role slug must be lowercase letters, numbers, dashes, or underscores"),
121
+ description: z.string().max(500).trim().optional(),
122
+ permissionKeys: z.array(PermissionKeySchema).default([])
123
+ }).strict();
124
+ var UpdateOrgRoleRequestSchema = z.object({
125
+ name: z.string().min(1).max(100).trim().optional(),
126
+ slug: z.string().min(1).max(100).regex(/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/, "Role slug must be lowercase letters, numbers, dashes, or underscores").optional(),
127
+ description: z.string().max(500).trim().nullable().optional(),
128
+ permissionKeys: z.array(PermissionKeySchema).optional()
129
+ }).strict().refine(
130
+ (data) => data.name !== void 0 || data.slug !== void 0 || data.description !== void 0 || data.permissionKeys !== void 0,
131
+ { message: "At least one field must be provided" }
132
+ );
133
+ var AssignMembershipRoleRequestSchema = z.object({
134
+ roleId: z.string().uuid()
135
+ }).strict();
136
+ var UuidSchema = z.string().uuid();
137
+ z.string().trim().min(1).max(1e3);
138
+ z.enum(["agent", "workflow"]);
139
+ z.enum(["agent", "workflow", "scheduler", "api"]);
140
+ z.string().trim().toLowerCase().min(1, "Credential name required").max(100, "Credential name too long (max 100 chars)").regex(
141
+ /^[a-z0-9]+(-[a-z0-9]+)+$/,
142
+ "Credential name must be lowercase letters, numbers, and hyphens in format: service-environment (e.g., gmail-prod, attio-dev)"
143
+ );
144
+ z.enum(["google-sheets", "google-calendar", "dropbox"]);
145
+ z.string().min(10, "Authorization code too short").max(1e3, "Authorization code too long");
146
+ z.string().min(10, "State parameter too short").max(2048, "State parameter too long");
147
+ z.string().trim().transform((str) => str.replace(/[<>'"]/g, ""));
148
+ var EmailSchema = z.string().email();
149
+ z.string().url();
150
+ z.object({
151
+ limit: z.coerce.number().int().min(1).max(100).default(20),
152
+ offset: z.coerce.number().int().min(0).default(0)
153
+ });
154
+ z.string().datetime();
155
+ z.object({
156
+ startDate: z.string().datetime(),
157
+ endDate: z.string().datetime()
158
+ });
159
+ function createStringSchema(minLength, maxLength, fieldName) {
160
+ const schema = z.string().trim().min(minLength).max(maxLength);
161
+ return fieldName ? schema.describe(`${fieldName} (${minLength}-${maxLength} characters)`) : schema;
162
+ }
163
+
164
+ // src/auth/multi-tenancy/organizations/api-schemas.ts
165
+ var OrganizationNameSchema = z.string().min(2, "Organization name must be at least 2 characters").max(100, "Organization name must be at most 100 characters").trim().regex(
166
+ /^[a-zA-Z0-9\s\-_]+$/,
167
+ "Organization name must contain only letters, numbers, spaces, hyphens, and underscores"
168
+ );
169
+ var OrganizationIdSchema = z.union([
170
+ UuidSchema,
171
+ z.string().regex(/^org_[a-zA-Z0-9]+$/, "Invalid WorkOS organization ID")
172
+ ]);
173
+ var OrganizationDomainSchema = z.object({
174
+ domain: z.string().min(3).max(255),
175
+ state: z.enum(["verified", "pending", "failed"]).optional()
176
+ });
177
+ var OrganizationIdParamSchema = z.object({
178
+ id: OrganizationIdSchema
179
+ }).strict();
180
+ var CreateOrganizationSchema = z.object({
181
+ name: OrganizationNameSchema,
182
+ domainData: z.array(OrganizationDomainSchema).max(10).optional(),
183
+ metadata: z.record(z.string(), z.unknown()).refine((val) => JSON.stringify(val).length <= 10240, "Metadata must be under 10KB").optional()
184
+ }).strict();
185
+ var UpdateOrganizationSchema = CreateOrganizationSchema.partial().strict().refine((data) => Object.keys(data).length > 0, {
186
+ message: "At least one field (name, domainData, or metadata) must be provided"
187
+ });
188
+ var ListOrganizationsQuerySchema = z.object({
189
+ limit: z.coerce.number().int().min(1).max(100).default(20),
190
+ before: z.string().optional(),
191
+ // WorkOS pagination cursor
192
+ after: z.string().optional()
193
+ // WorkOS pagination cursor
194
+ });
195
+ var UserIdParamSchema = z.object({
196
+ id: z.string().min(1)
197
+ // WorkOS user IDs can be UUID or 'user_' prefixed strings
198
+ }).strict();
199
+ var ExternalIdParamSchema = z.object({
200
+ externalId: z.string().min(1)
201
+ }).strict();
202
+ var UpdateUserSchema = z.object({
203
+ email: EmailSchema.optional(),
204
+ firstName: createStringSchema(1, 100, "First name").optional(),
205
+ lastName: createStringSchema(1, 100, "Last name").optional()
206
+ }).strict();
207
+ var UpdateMyProfileSchema = z.object({
208
+ firstName: createStringSchema(1, 100, "First name").optional(),
209
+ lastName: createStringSchema(1, 100, "Last name").optional(),
210
+ profilePictureUrl: z.string().url().refine((url) => url.startsWith("https://"), { message: "Only HTTPS URLs are allowed" }).optional(),
211
+ lastVisitedOrg: z.string().uuid().optional(),
212
+ config: z.object({
213
+ theme: z.object({
214
+ // `.catch('default')` makes the write path tolerant of stale/unknown preset strings.
215
+ // Any value that fails enum validation silently coerces to 'default' instead of 400ing.
216
+ // This protects against preset renames (e.g. cyber-punk → cyber-chrome) and drift between
217
+ // the enum and legacy DB values. Paired with a read-path sync-back in main.tsx that
218
+ // persists the corrected value back to DB on next profile load.
219
+ // ThemePresetEnum is derived from THEME_PRESETS — the single source of truth in
220
+ // packages/core/src/auth/multi-tenancy/theme-presets.ts.
221
+ preset: ThemePresetEnum.catch("default").optional(),
222
+ colorScheme: z.enum(["light", "dark", "auto"]).catch("auto").optional()
223
+ }).strict().optional(),
224
+ onboarding: z.object({
225
+ completed: z.boolean().optional(),
226
+ completedAt: z.string().datetime().nullable().optional(),
227
+ role: z.string().max(100).nullable().optional(),
228
+ primaryUseCase: z.array(z.string().max(100)).max(10).nullable().optional(),
229
+ experienceLevel: z.string().max(100).nullable().optional(),
230
+ guides: z.object({
231
+ completedIds: z.array(z.string().max(100)).max(20).optional(),
232
+ dismissed: z.boolean().optional(),
233
+ completedAt: z.string().datetime().nullable().optional()
234
+ }).strict().optional()
235
+ }).strict().optional()
236
+ }).strict().optional()
237
+ }).strict();
238
+ var ListUsersQuerySchema = z.object({
239
+ email: EmailSchema.optional(),
240
+ organizationId: z.string().optional(),
241
+ // WorkOS org IDs can be UUID or 'org_' prefixed
242
+ limit: z.coerce.number().int().min(1).max(100).optional(),
243
+ before: z.string().optional(),
244
+ // WorkOS pagination cursor
245
+ after: z.string().optional()
246
+ // WorkOS pagination cursor
247
+ }).strict();
248
+
249
+ // src/auth/multi-tenancy/memberships/membership.ts
250
+ function transformMembershipToTableRow(membership) {
251
+ return {
252
+ id: membership.id,
253
+ userId: membership.userId,
254
+ organizationId: membership.organizationId,
255
+ userEmail: membership.user?.email || "Unknown",
256
+ userFullName: membership.user?.firstName && membership.user?.lastName ? `${membership.user.firstName} ${membership.user.lastName}` : membership.user?.email || "Unknown User",
257
+ organizationName: membership.organization?.name || "Unknown Organization",
258
+ role: membership.role.slug,
259
+ status: membership.status,
260
+ statusBadge: membership.status,
261
+ joinedAt: new Date(membership.createdAt),
262
+ updatedAt: new Date(membership.updatedAt),
263
+ canEdit: membership.status === "active",
264
+ canRemove: true
265
+ };
266
+ }
267
+ var MEMBERSHIP_STATUS_COLORS = {
268
+ active: "green",
269
+ inactive: "gray"
270
+ };
271
+
272
+ // src/auth/multi-tenancy/memberships/supabase.ts
273
+ function mapSupabaseStatus(status) {
274
+ if (status === "active") return "active";
275
+ return "inactive";
276
+ }
277
+ function transformSupabaseToMembership(supabaseData) {
278
+ return {
279
+ object: "organization_membership",
280
+ id: supabaseData.workos_membership_id ?? supabaseData.id,
281
+ userId: supabaseData.user_id,
282
+ // Always use Supabase user_id
283
+ organizationId: supabaseData.organization_id,
284
+ // Always use Supabase organization_id
285
+ role: { slug: supabaseData.role_slug || "member" },
286
+ status: mapSupabaseStatus(supabaseData.membership_status),
287
+ createdAt: supabaseData.created_at || (/* @__PURE__ */ new Date()).toISOString(),
288
+ updatedAt: supabaseData.updated_at || (/* @__PURE__ */ new Date()).toISOString()
289
+ };
290
+ }
291
+ var MembershipRoleSchema = z.string().min(1).max(64);
292
+ var MembershipStatusSchema = z.enum(["active", "inactive"]);
293
+ var MembershipIdParamSchema = z.object({
294
+ id: z.string().min(1)
295
+ // WorkOS membership IDs can be various formats
296
+ }).strict();
297
+ var OrgIdParamSchema = z.object({
298
+ orgId: z.string().uuid()
299
+ }).strict();
300
+ var MyOrgPermissionsResponseSchema = z.object({
301
+ permissions: z.array(z.string())
302
+ });
303
+ var CreateMembershipSchema = z.object({
304
+ userId: z.string().min(1),
305
+ organizationId: z.string().min(1),
306
+ roleSlug: MembershipRoleSchema.default("member")
307
+ }).strict();
308
+ var UpdateMembershipSchema = z.object({
309
+ roleSlug: MembershipRoleSchema
310
+ }).strict();
311
+ var ListMembershipsQuerySchema = z.object({
312
+ userId: z.string().optional(),
313
+ organizationId: z.string().optional(),
314
+ limit: z.coerce.number().int().min(1).max(100).optional(),
315
+ before: z.string().optional(),
316
+ // WorkOS pagination cursor
317
+ after: z.string().optional()
318
+ // WorkOS pagination cursor
319
+ }).strict().refine((data) => data.userId || data.organizationId, {
320
+ message: "Either userId or organizationId must be provided"
321
+ });
322
+
323
+ // src/auth/multi-tenancy/invitations/supabase.ts
324
+ function transformSupabaseToInvitation(supabaseInvitation) {
325
+ return {
326
+ id: supabaseInvitation.id,
327
+ workosInvitationId: supabaseInvitation.workos_invitation_id,
328
+ email: supabaseInvitation.email,
329
+ organizationId: supabaseInvitation.organization_id,
330
+ inviterUserId: supabaseInvitation.inviter_user_id,
331
+ invitationState: supabaseInvitation.invitation_state,
332
+ roleSlug: supabaseInvitation.role_slug,
333
+ invitationToken: supabaseInvitation.invitation_token,
334
+ acceptInvitationUrl: supabaseInvitation.accept_invitation_url,
335
+ acceptedAt: supabaseInvitation.accepted_at,
336
+ revokedAt: supabaseInvitation.revoked_at,
337
+ expiresAt: supabaseInvitation.expires_at,
338
+ createdAt: supabaseInvitation.created_at,
339
+ updatedAt: supabaseInvitation.updated_at
340
+ };
341
+ }
342
+ var InvitationIdParamSchema = z.object({
343
+ id: z.string().min(1)
344
+ // WorkOS invitation IDs
345
+ }).strict();
346
+ var SendInvitationSchema = z.object({
347
+ email: EmailSchema,
348
+ organizationId: z.string().optional(),
349
+ // For WorkOS API - but typically from JWT
350
+ roleSlug: MembershipRoleSchema.default("member"),
351
+ expiresInDays: z.number().int().min(1).max(90).default(7)
352
+ }).strict();
353
+ var AcceptInvitationSchema = z.object({
354
+ invitation_token: z.string().min(1, "Invitation token is required")
355
+ }).strict();
356
+ var ListInvitationsQuerySchema = z.object({
357
+ organizationId: z.string().optional(),
358
+ userId: z.string().optional(),
359
+ email: EmailSchema.optional(),
360
+ limit: z.coerce.number().int().min(1).max(100).optional(),
361
+ before: z.string().optional(),
362
+ // WorkOS pagination cursor
363
+ after: z.string().optional()
364
+ // WorkOS pagination cursor
365
+ }).strict().refine((data) => data.organizationId || data.userId, { message: "Either organizationId or userId must be provided" });
366
+
367
+ // src/organization-model/helpers.ts
368
+ function childSystemsOf(system) {
369
+ return system.systems ?? system.subsystems ?? {};
370
+ }
371
+ function getSystem(model, path) {
372
+ const segments = path.split(".");
373
+ let current = model.systems;
374
+ let node;
375
+ for (const seg of segments) {
376
+ node = current[seg];
377
+ if (node === void 0) return void 0;
378
+ current = childSystemsOf(node);
379
+ }
380
+ return node;
381
+ }
382
+ function listAllSystems(model) {
383
+ const results = [];
384
+ function walk(map, prefix) {
385
+ for (const [localId, system] of Object.entries(map)) {
386
+ const fullPath = prefix ? `${prefix}.${localId}` : localId;
387
+ results.push({ path: fullPath, system });
388
+ const childSystems = childSystemsOf(system);
389
+ if (Object.keys(childSystems).length > 0) {
390
+ walk(childSystems, fullPath);
391
+ }
392
+ }
393
+ }
394
+ walk(model.systems, "");
395
+ return results;
396
+ }
397
+
398
+ // src/auth/access-keys.ts
399
+ var DEFAULT_ACCESS_ACTION = "view";
400
+ var PLATFORM_ADMIN_ACCESS_KEY = "platform.admin";
401
+ var PLATFORM_ADMIN_ACCESS_KEY_SHORTHAND = "platformAdmin";
402
+ var AccessActionSchema = z.enum(["view", "manage"]);
403
+ var AccessKeyObjectSchema = z.object({
404
+ systemPath: z.string().trim().min(1),
405
+ action: AccessActionSchema.default(DEFAULT_ACCESS_ACTION)
406
+ }).strict();
407
+ var AccessKeyInputSchema = z.union([z.string().trim().min(1), AccessKeyObjectSchema]);
408
+ var NormalizedAccessKeySchema = AccessKeyObjectSchema;
409
+ var AccessKeySchema = AccessKeyInputSchema;
410
+ var DIAGNOSTIC_VIEW_ACCESS_KEYS = [
411
+ "diagnostic.operations.overview",
412
+ "diagnostic.operations.recent-executions",
413
+ "diagnostic.monitoring.execution-logs",
414
+ "diagnostic.monitoring.notifications"
415
+ ];
416
+ var AccessKeys = {
417
+ platformAdmin: { systemPath: PLATFORM_ADMIN_ACCESS_KEY, action: DEFAULT_ACCESS_ACTION },
418
+ organizationManage: { systemPath: "permission.org", action: "manage" },
419
+ membersManage: { systemPath: "permission.members", action: "manage" },
420
+ rolesManage: { systemPath: "permission.roles", action: "manage" },
421
+ secretsManage: { systemPath: "permission.secrets", action: "manage" },
422
+ operationsRead: { systemPath: "permission.operations", action: DEFAULT_ACCESS_ACTION },
423
+ operationsManage: { systemPath: "permission.operations", action: "manage" },
424
+ acquisitionManage: { systemPath: "permission.acquisition", action: "manage" },
425
+ projectsManage: { systemPath: "permission.projects", action: "manage" },
426
+ clientsManage: { systemPath: "permission.clients", action: "manage" },
427
+ operationsOverview: { systemPath: "diagnostic.operations.overview", action: DEFAULT_ACCESS_ACTION },
428
+ operationsRecentExecutions: { systemPath: "diagnostic.operations.recent-executions", action: DEFAULT_ACCESS_ACTION },
429
+ monitoringExecutionLogs: { systemPath: "diagnostic.monitoring.execution-logs", action: DEFAULT_ACCESS_ACTION },
430
+ monitoringNotifications: { systemPath: "diagnostic.monitoring.notifications", action: DEFAULT_ACCESS_ACTION }
431
+ };
432
+ var PERMISSION_ACCESS_KEY_DEFINITIONS = [
433
+ { key: AccessKeys.organizationManage, rolePermission: PERMISSIONS.ORG_MANAGE },
434
+ { key: AccessKeys.membersManage, rolePermission: PERMISSIONS.MEMBERS_MANAGE },
435
+ { key: AccessKeys.rolesManage, rolePermission: PERMISSIONS.ROLES_MANAGE },
436
+ { key: AccessKeys.secretsManage, rolePermission: PERMISSIONS.SECRETS_MANAGE },
437
+ { key: AccessKeys.operationsRead, rolePermission: PERMISSIONS.OPERATIONS_READ },
438
+ { key: AccessKeys.operationsManage, rolePermission: PERMISSIONS.OPERATIONS_MANAGE },
439
+ { key: AccessKeys.acquisitionManage, rolePermission: PERMISSIONS.ACQUISITION_MANAGE },
440
+ { key: AccessKeys.projectsManage, rolePermission: PERMISSIONS.PROJECTS_MANAGE },
441
+ { key: AccessKeys.clientsManage, rolePermission: PERMISSIONS.CLIENTS_MANAGE }
442
+ ];
443
+ function normalizeAccessKey(input) {
444
+ const parsed = AccessKeyInputSchema.parse(input);
445
+ const normalized = typeof parsed === "string" ? {
446
+ systemPath: parsed === PLATFORM_ADMIN_ACCESS_KEY_SHORTHAND ? PLATFORM_ADMIN_ACCESS_KEY : parsed,
447
+ action: DEFAULT_ACCESS_ACTION
448
+ } : parsed;
449
+ return NormalizedAccessKeySchema.parse(normalized);
450
+ }
451
+ function accessKeyToString(input) {
452
+ const key = normalizeAccessKey(input);
453
+ return `${key.systemPath}:${key.action}`;
454
+ }
455
+ function rolePermissionForAccessKey(key) {
456
+ if (key.action === DEFAULT_ACCESS_ACTION) return void 0;
457
+ return `${key.systemPath}.${key.action}`;
458
+ }
459
+ function groupCatalogEntries(entries) {
460
+ const grouped = /* @__PURE__ */ new Map();
461
+ for (const entry of entries) {
462
+ const existing = grouped.get(entry.key.systemPath) ?? [];
463
+ existing.push(entry);
464
+ grouped.set(entry.key.systemPath, existing);
465
+ }
466
+ return grouped;
467
+ }
468
+ function buildCatalogEntry(systemPath, action, source) {
469
+ const key = normalizeAccessKey({ systemPath, action });
470
+ return {
471
+ key,
472
+ source,
473
+ rolePermission: rolePermissionForAccessKey(key)
474
+ };
475
+ }
476
+ function deriveAccessKeyCatalog(organizationModel, options = {}) {
477
+ const { diagnosticKeys = DIAGNOSTIC_VIEW_ACCESS_KEYS, includeManageActions = true } = options;
478
+ const entries = [
479
+ buildCatalogEntry(PLATFORM_ADMIN_ACCESS_KEY, DEFAULT_ACCESS_ACTION, "platform")
480
+ ];
481
+ for (const { path } of listAllSystems(organizationModel)) {
482
+ entries.push(buildCatalogEntry(path, DEFAULT_ACCESS_ACTION, "om-system"));
483
+ if (includeManageActions) {
484
+ entries.push(buildCatalogEntry(path, "manage", "om-system"));
485
+ }
486
+ }
487
+ for (const { key, rolePermission } of PERMISSION_ACCESS_KEY_DEFINITIONS) {
488
+ entries.push({
489
+ key,
490
+ source: "permission",
491
+ rolePermission
492
+ });
493
+ }
494
+ for (const systemPath of diagnosticKeys) {
495
+ entries.push(buildCatalogEntry(systemPath, DEFAULT_ACCESS_ACTION, "diagnostic"));
496
+ }
497
+ return {
498
+ bySystemPath: groupCatalogEntries(entries),
499
+ entries
500
+ };
501
+ }
502
+ function findAccessCatalogEntry(catalog, key) {
503
+ return catalog.bySystemPath.get(key.systemPath)?.find((entry) => entry.key.action === key.action);
504
+ }
505
+ function listAccessKeys(catalog) {
506
+ return catalog.entries.map((entry) => entry.key);
507
+ }
508
+
509
+ // src/auth/access-model.ts
510
+ var ALLOWED = { allowed: true, restrictedBy: null, reason: "allowed" };
511
+ var PLATFORM_ADMIN_BYPASS = {
512
+ allowed: true,
513
+ restrictedBy: null,
514
+ reason: "platform-admin-bypass"
515
+ };
516
+ function deny(restrictedBy, reason) {
517
+ return { allowed: false, restrictedBy, reason };
518
+ }
519
+ function isPlatformAdmin(profile) {
520
+ return profile?.isPlatformAdmin === true || profile?.is_platform_admin === true;
521
+ }
522
+ function diagnosticAllowlistHas(allowlist, systemPath) {
523
+ if (allowlist === void 0) return false;
524
+ return "has" in allowlist ? allowlist.has(systemPath) : allowlist.includes(systemPath);
525
+ }
526
+ function lifecycleAllowsAccess(lifecycle, ctx) {
527
+ if (lifecycle === "active") return true;
528
+ if (lifecycle === "beta") return ctx.betaAccessEnabled === true || ctx.isDevelopment === true;
529
+ return false;
530
+ }
531
+ function getPermissionSource(ctx) {
532
+ return ctx.permissions ?? ctx.membership?.effectivePermissions;
533
+ }
534
+ function hasRequiredPermission(key, rolePermission, ctx) {
535
+ const permissionSource = getPermissionSource(ctx);
536
+ if (permissionSource === void 0 || permissionSource === null) return true;
537
+ const requiredPermission = rolePermission ?? `${key.systemPath}.${key.action}`;
538
+ return permissionSource.includes(requiredPermission);
539
+ }
540
+ function hasExplicitRequiredPermission(rolePermission, ctx) {
541
+ const permissionSource = getPermissionSource(ctx);
542
+ return rolePermission !== void 0 && permissionSource !== void 0 && permissionSource !== null ? permissionSource.includes(rolePermission) : false;
543
+ }
544
+ function checkAccess(input, ctx) {
545
+ if (isPlatformAdmin(ctx.profile)) return PLATFORM_ADMIN_BYPASS;
546
+ const parsed = (() => {
547
+ try {
548
+ return normalizeAccessKey(input);
549
+ } catch {
550
+ return null;
551
+ }
552
+ })();
553
+ if (parsed === null) return deny("catalog", "invalid-access-key");
554
+ const catalog = ctx.accessCatalog ?? deriveAccessKeyCatalog(ctx.organizationModel);
555
+ const catalogEntry = findAccessCatalogEntry(catalog, parsed);
556
+ if (catalogEntry === void 0) return deny("catalog", "unknown-access-key");
557
+ const membership = ctx.membership;
558
+ if (membership === void 0 || membership === null) return deny("membership", "missing-membership");
559
+ if (membership.organizationId !== ctx.organizationId) return deny("membership", "organization-mismatch");
560
+ if (parsed.systemPath === PLATFORM_ADMIN_ACCESS_KEY) {
561
+ return deny("role-permission", "role-permission-denied");
562
+ }
563
+ if (catalogEntry.source === "diagnostic") {
564
+ if (!diagnosticAllowlistHas(ctx.diagnosticAllowlist, parsed.systemPath)) {
565
+ return deny("diagnostic-allowlist", "diagnostic-key-not-allowed");
566
+ }
567
+ if (parsed.action !== DEFAULT_ACCESS_ACTION && !hasRequiredPermission(parsed, catalogEntry.rolePermission, ctx)) {
568
+ return deny("role-permission", "role-permission-denied");
569
+ }
570
+ return ALLOWED;
571
+ }
572
+ if (catalogEntry.source === "permission") {
573
+ if (!hasExplicitRequiredPermission(catalogEntry.rolePermission, ctx)) {
574
+ return deny("role-permission", "role-permission-denied");
575
+ }
576
+ return ALLOWED;
577
+ }
578
+ const system = getSystem(ctx.organizationModel, parsed.systemPath);
579
+ if (system === void 0 || !lifecycleAllowsAccess(system.lifecycle, ctx)) {
580
+ return deny("system-lifecycle", "system-not-active");
581
+ }
582
+ if (parsed.action !== DEFAULT_ACCESS_ACTION && !hasRequiredPermission(parsed, catalogEntry.rolePermission, ctx)) {
583
+ return deny("role-permission", "role-permission-denied");
584
+ }
585
+ return ALLOWED;
586
+ }
587
+ function createAccessModel(organizationModel) {
588
+ const catalog = deriveAccessKeyCatalog(organizationModel);
589
+ return {
590
+ catalog,
591
+ checkAccess: (key, ctx) => checkAccess(key, { ...ctx, organizationModel, accessCatalog: catalog })
592
+ };
593
+ }
594
+
595
+ export { AcceptInvitationSchema, AccessActionSchema, AccessKeyInputSchema, AccessKeyObjectSchema, AccessKeySchema, AccessKeys, AssignMembershipRoleRequestSchema, CreateMembershipSchema, CreateOrgRoleRequestSchema, CreateOrganizationSchema, DEFAULT_ACCESS_ACTION, DIAGNOSTIC_VIEW_ACCESS_KEYS, ExternalIdParamSchema, InvitationIdParamSchema, ListInvitationsQuerySchema, ListMembershipsQuerySchema, ListOrganizationsQuerySchema, ListUsersQuerySchema, MEMBERSHIP_STATUS_COLORS, MembershipIdParamSchema, MembershipParamsSchema, MembershipRoleParamsSchema, MembershipRoleSchema, MembershipStatusSchema, MyOrgPermissionsResponseSchema, NormalizedAccessKeySchema, OrgIdParamSchema, OrgRoleParamsSchema, OrgRolesParamsSchema, OrganizationDomainSchema, OrganizationIdParamSchema, OrganizationNameSchema, PERMISSIONS, PERMISSION_CATALOG, PLATFORM_ADMIN_ACCESS_KEY, PLATFORM_ADMIN_ACCESS_KEY_SHORTHAND, SendInvitationSchema, THEME_PRESETS, ThemePresetEnum, UpdateMembershipSchema, UpdateMyProfileSchema, UpdateOrgRoleRequestSchema, UpdateOrganizationSchema, UpdateUserSchema, UserIdParamSchema, accessKeyToString, checkAccess, createAccessModel, deriveAccessKeyCatalog, findAccessCatalogEntry, isPermissionKey, listAccessKeys, normalizeAccessKey, rolePermissionForAccessKey, transformMembershipToTableRow, transformSupabaseToInvitation, transformSupabaseToMembership };