@elevasis/core 0.12.0 → 0.13.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.
@@ -1,18 +1,16 @@
1
1
  import { getSupabaseClient } from '../../../../supabase/server/client'
2
- import { setKek, CURRENT_KEY_ID, LEGACY_KEY_ID } from './encryption'
2
+ import { setKek, CURRENT_KEY_ID } from './encryption'
3
3
 
4
4
  let loaded = false
5
5
 
6
6
  /**
7
7
  * Loads the platform credential KEK from Supabase Vault and registers it under
8
- * `CURRENT_KEY_ID` ('platform-v1'). During the migration window, also registers
9
- * the legacy `SECRETS_ENCRYPTION_KEY` env var under `LEGACY_KEY_ID` so existing
10
- * ciphertext rows (no `keyId` field) can still be decrypted.
8
+ * `CURRENT_KEY_ID` ('platform-v1').
11
9
  *
12
10
  * Idempotent: subsequent calls are no-ops.
13
11
  *
14
12
  * Fails fast on missing / malformed Vault KEK so misconfigured deploys do not
15
- * silently fall through to env-only encryption.
13
+ * silently start without a usable encryption key.
16
14
  */
17
15
  export async function loadCredentialKEKs(): Promise<void> {
18
16
  if (loaded) return
@@ -35,13 +33,5 @@ export async function loadCredentialKEKs(): Promise<void> {
35
33
  }
36
34
  setKek(CURRENT_KEY_ID, vaultKek)
37
35
 
38
- const legacyHex = process.env.SECRETS_ENCRYPTION_KEY
39
- if (legacyHex) {
40
- const legacyKey = Buffer.from(legacyHex, 'hex')
41
- if (legacyKey.length === 32) {
42
- setKek(LEGACY_KEY_ID, legacyKey)
43
- }
44
- }
45
-
46
36
  loaded = true
47
37
  }
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * The DB table `org_rol_permissions` mirrors this constant. Reconciliation
10
10
  * runs at API boot (insert-or-update only — never auto-delete; see the
11
- * deletion runbook in the auth-role-system-redesign doc).
11
+ * deletion runbook in the auth-role-system doc -- review/auth-role-system/).
12
12
  *
13
13
  * Adding a permission:
14
14
  * 1. Add an entry below.
@@ -29,7 +29,8 @@ export const PERMISSIONS = {
29
29
  SECRETS_MANAGE: 'secrets.manage',
30
30
  OPERATIONS_READ: 'operations.read',
31
31
  OPERATIONS_MANAGE: 'operations.manage',
32
- WORK_MANAGE: 'work.manage'
32
+ ACQUISITION_MANAGE: 'acquisition.manage',
33
+ PROJECTS_MANAGE: 'projects.manage'
33
34
  } as const
34
35
 
35
36
  export type PermissionKey = (typeof PERMISSIONS)[keyof typeof PERMISSIONS]
@@ -87,9 +88,15 @@ export const PERMISSION_CATALOG: readonly PermissionDescriptor[] = [
87
88
  isOrgGrantable: true
88
89
  },
89
90
  {
90
- key: 'work.manage',
91
- description: 'Create and edit business-domain records (acq_*, prj_*)',
92
- isOrgGrantable: true
91
+ key: 'acquisition.manage',
92
+ description:
93
+ 'Create, update, and delete acquisition records (acq_companies, acq_contacts, acq_deals, acq_lists*, acq_content*, acquisition storage files)',
94
+ isOrgGrantable: false
95
+ },
96
+ {
97
+ key: 'projects.manage',
98
+ description: 'Create, update, and delete project records (prj_projects, prj_milestones, prj_tasks, prj_notes)',
99
+ isOrgGrantable: false
93
100
  }
94
101
  ] as const
95
102
 
@@ -0,0 +1,142 @@
1
+ import { z } from 'zod'
2
+
3
+ // ---------------------------------------------------------------------------
4
+ // Individual event member schemas
5
+ // ---------------------------------------------------------------------------
6
+
7
+ const StageChangeEventSchema = z.object({
8
+ type: z.literal('stage_change'),
9
+ timestamp: z.string().datetime(),
10
+ stageBefore: z.string(),
11
+ stageAfter: z.string(),
12
+ reason: z.string().optional()
13
+ })
14
+
15
+ const StateChangeEventSchema = z.object({
16
+ type: z.literal('state_change'),
17
+ timestamp: z.string().datetime(),
18
+ stateBefore: z.string().nullable(),
19
+ stateAfter: z.string().nullable(),
20
+ reason: z.string().optional()
21
+ })
22
+
23
+ const ActionTakenEventSchema = z.object({
24
+ type: z.literal('action_taken'),
25
+ timestamp: z.string().datetime(),
26
+ actionKey: z.string(),
27
+ payload: z.record(z.string(), z.unknown()).optional()
28
+ })
29
+
30
+ const ApprovalCreatedEventSchema = z.object({
31
+ type: z.literal('approval_created'),
32
+ timestamp: z.string().datetime(),
33
+ commandId: z.string(),
34
+ dealStageBefore: z.string().optional(),
35
+ dealStageAfter: z.string().optional()
36
+ })
37
+
38
+ const ApprovalResolvedEventSchema = z.object({
39
+ type: z.literal('approval_resolved'),
40
+ timestamp: z.string().datetime(),
41
+ commandId: z.string(),
42
+ resolution: z.enum(['superseded']),
43
+ originResourceType: z.string().optional()
44
+ })
45
+
46
+ const ApprovalStaleEventSchema = z.object({
47
+ type: z.literal('approval_stale'),
48
+ timestamp: z.string().datetime(),
49
+ commandId: z.string(),
50
+ dealStageBefore: z.string().optional(),
51
+ dealStageAfter: z.string().optional()
52
+ })
53
+
54
+ const TaskCreatedEventSchema = z.object({
55
+ type: z.literal('task_created'),
56
+ timestamp: z.string().datetime(),
57
+ taskId: z.string()
58
+ })
59
+
60
+ const DealCreatedEventSchema = z.object({
61
+ type: z.literal('deal_created'),
62
+ timestamp: z.string().datetime()
63
+ })
64
+
65
+ const ReplyReceivedEventSchema = z.object({
66
+ type: z.literal('reply_received'),
67
+ timestamp: z.string().datetime(),
68
+ messageId: z.string().optional(),
69
+ source: z.string().optional()
70
+ })
71
+
72
+ const ReplySentToLeadEventSchema = z.object({
73
+ type: z.literal('reply_sent_to_lead'),
74
+ timestamp: z.string().datetime(),
75
+ messageId: z.string().optional(),
76
+ source: z.string().optional()
77
+ })
78
+
79
+ const BookingNudgeSentEventSchema = z.object({
80
+ type: z.literal('booking_nudge_sent'),
81
+ timestamp: z.string().datetime(),
82
+ followupDay: z.number()
83
+ })
84
+
85
+ const ReminderSentEventSchema = z.object({
86
+ type: z.literal('reminder_sent'),
87
+ timestamp: z.string().datetime(),
88
+ followupDay: z.number().optional()
89
+ })
90
+
91
+ const BookingCancelledEventSchema = z.object({
92
+ type: z.literal('booking_cancelled'),
93
+ timestamp: z.string().datetime(),
94
+ reason: z.string().optional()
95
+ })
96
+
97
+ const DiscoverySubmittedEventSchema = z.object({
98
+ type: z.literal('discovery_submitted'),
99
+ timestamp: z.string().datetime()
100
+ })
101
+
102
+ const MovedToNurturingEventSchema = z.object({
103
+ type: z.literal('moved_to_nurturing'),
104
+ timestamp: z.string().datetime()
105
+ })
106
+
107
+ const NoShowEventSchema = z.object({
108
+ type: z.literal('no_show'),
109
+ timestamp: z.string().datetime()
110
+ })
111
+
112
+ const FollowupEmailSentEventSchema = z.object({
113
+ type: z.literal('followup_email_sent'),
114
+ timestamp: z.string().datetime(),
115
+ followupDay: z.number().optional()
116
+ })
117
+
118
+ // ---------------------------------------------------------------------------
119
+ // Union
120
+ // ---------------------------------------------------------------------------
121
+
122
+ export const ActivityEventSchema = z.discriminatedUnion('type', [
123
+ StageChangeEventSchema,
124
+ StateChangeEventSchema,
125
+ ActionTakenEventSchema,
126
+ ApprovalCreatedEventSchema,
127
+ ApprovalResolvedEventSchema,
128
+ ApprovalStaleEventSchema,
129
+ TaskCreatedEventSchema,
130
+ DealCreatedEventSchema,
131
+ ReplyReceivedEventSchema,
132
+ ReplySentToLeadEventSchema,
133
+ BookingNudgeSentEventSchema,
134
+ ReminderSentEventSchema,
135
+ BookingCancelledEventSchema,
136
+ DiscoverySubmittedEventSchema,
137
+ MovedToNurturingEventSchema,
138
+ NoShowEventSchema,
139
+ FollowupEmailSentEventSchema
140
+ ])
141
+
142
+ export type ActivityEvent = z.infer<typeof ActivityEventSchema>