@elevasis/core 0.17.0 → 0.19.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 (44) hide show
  1. package/dist/index.d.ts +82 -1
  2. package/dist/index.js +291 -171
  3. package/dist/knowledge/index.d.ts +43 -0
  4. package/dist/organization-model/index.d.ts +82 -1
  5. package/dist/organization-model/index.js +291 -171
  6. package/dist/test-utils/index.d.ts +41 -12
  7. package/dist/test-utils/index.js +291 -171
  8. package/package.json +2 -1
  9. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +78 -65
  10. package/src/auth/multi-tenancy/organizations/__tests__/api-schemas.test.ts +194 -0
  11. package/src/auth/multi-tenancy/organizations/api-schemas.ts +136 -128
  12. package/src/business/acquisition/api-schemas.test.ts +100 -2
  13. package/src/business/acquisition/api-schemas.ts +81 -43
  14. package/src/business/acquisition/build-templates.test.ts +212 -0
  15. package/src/business/acquisition/types.ts +21 -38
  16. package/src/execution/engine/index.ts +436 -434
  17. package/src/execution/engine/tools/integration/server/adapters/google-calendar/google-calendar-adapter.ts +428 -0
  18. package/src/execution/engine/tools/integration/server/adapters/google-calendar/index.ts +2 -0
  19. package/src/execution/engine/tools/lead-service-types.ts +51 -9
  20. package/src/execution/engine/tools/platform/acquisition/company-tools.ts +7 -6
  21. package/src/execution/engine/tools/platform/acquisition/contact-tools.ts +6 -5
  22. package/src/execution/engine/tools/platform/acquisition/types.ts +20 -9
  23. package/src/execution/engine/tools/registry.ts +700 -698
  24. package/src/execution/engine/tools/tool-maps.ts +10 -0
  25. package/src/execution/external/__tests__/api-schemas.test.ts +127 -0
  26. package/src/integrations/oauth/__tests__/provider-registry.test.ts +7 -6
  27. package/src/integrations/oauth/provider-registry.ts +74 -61
  28. package/src/integrations/oauth/server/credentials.ts +43 -39
  29. package/src/knowledge/__tests__/queries.test.ts +89 -0
  30. package/src/organization-model/__tests__/icons.test.ts +61 -0
  31. package/src/organization-model/__tests__/knowledge.test.ts +118 -1
  32. package/src/organization-model/__tests__/prospecting-ssot.test.ts +94 -0
  33. package/src/organization-model/defaults.ts +8 -0
  34. package/src/organization-model/domains/knowledge.ts +9 -0
  35. package/src/organization-model/domains/prospecting.ts +272 -226
  36. package/src/organization-model/domains/sales.ts +32 -25
  37. package/src/organization-model/icons.ts +3 -0
  38. package/src/organization-model/types.ts +9 -1
  39. package/src/platform/constants/versions.ts +1 -1
  40. package/src/platform/utils/__tests__/validation.test.ts +1084 -1083
  41. package/src/platform/utils/validation.ts +425 -425
  42. package/src/reference/_generated/contracts.md +78 -65
  43. package/src/server.ts +6 -0
  44. package/src/supabase/database.types.ts +6 -12
@@ -0,0 +1,212 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import {
3
+ DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID,
4
+ PROSPECTING_BUILD_TEMPLATE_OPTIONS,
5
+ createBuildPlanSnapshotFromTemplateId,
6
+ isProspectingBuildTemplateId
7
+ } from './build-templates'
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // isProspectingBuildTemplateId
11
+ // ---------------------------------------------------------------------------
12
+
13
+ describe('isProspectingBuildTemplateId', () => {
14
+ it('returns true for every id in PROSPECTING_BUILD_TEMPLATE_OPTIONS', () => {
15
+ for (const option of PROSPECTING_BUILD_TEMPLATE_OPTIONS) {
16
+ expect(isProspectingBuildTemplateId(option.id)).toBe(true)
17
+ }
18
+ })
19
+
20
+ it('returns true for the known "local-services" template id', () => {
21
+ expect(isProspectingBuildTemplateId('local-services')).toBe(true)
22
+ })
23
+
24
+ it('returns true for the known "dtc-subscription-apollo-clickup" template id', () => {
25
+ expect(isProspectingBuildTemplateId('dtc-subscription-apollo-clickup')).toBe(true)
26
+ })
27
+
28
+ it('returns false for an unknown string', () => {
29
+ expect(isProspectingBuildTemplateId('not-a-template')).toBe(false)
30
+ })
31
+
32
+ it('returns false for an empty string', () => {
33
+ expect(isProspectingBuildTemplateId('')).toBe(false)
34
+ })
35
+
36
+ it('returns false for a partial id match', () => {
37
+ expect(isProspectingBuildTemplateId('local')).toBe(false)
38
+ expect(isProspectingBuildTemplateId('dtc-subscription')).toBe(false)
39
+ })
40
+
41
+ it('returns false for a whitespace-padded id', () => {
42
+ expect(isProspectingBuildTemplateId(' local-services ')).toBe(false)
43
+ })
44
+
45
+ it('verifies PROSPECTING_BUILD_TEMPLATE_OPTIONS contains at least two entries', () => {
46
+ expect(PROSPECTING_BUILD_TEMPLATE_OPTIONS.length).toBeGreaterThanOrEqual(2)
47
+ })
48
+
49
+ it('verifies DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID is itself a valid template id', () => {
50
+ expect(isProspectingBuildTemplateId(DEFAULT_PROSPECTING_BUILD_TEMPLATE_ID)).toBe(true)
51
+ })
52
+ })
53
+
54
+ // ---------------------------------------------------------------------------
55
+ // createBuildPlanSnapshotFromTemplateId — unknown id
56
+ // ---------------------------------------------------------------------------
57
+
58
+ describe('createBuildPlanSnapshotFromTemplateId — unknown id', () => {
59
+ it('returns null for an unknown template id', () => {
60
+ expect(createBuildPlanSnapshotFromTemplateId('not-a-template')).toBeNull()
61
+ })
62
+
63
+ it('returns null for an empty string', () => {
64
+ expect(createBuildPlanSnapshotFromTemplateId('')).toBeNull()
65
+ })
66
+ })
67
+
68
+ // ---------------------------------------------------------------------------
69
+ // createBuildPlanSnapshotFromTemplateId — "local-services"
70
+ // ---------------------------------------------------------------------------
71
+
72
+ describe('createBuildPlanSnapshotFromTemplateId — "local-services"', () => {
73
+ const snapshot = createBuildPlanSnapshotFromTemplateId('local-services')
74
+
75
+ it('returns a non-null snapshot', () => {
76
+ expect(snapshot).not.toBeNull()
77
+ })
78
+
79
+ it('preserves the template id in the snapshot', () => {
80
+ expect(snapshot?.templateId).toBe('local-services')
81
+ })
82
+
83
+ it('includes a non-empty templateLabel', () => {
84
+ expect(snapshot?.templateLabel).toBeTruthy()
85
+ })
86
+
87
+ it('produces a steps array with length matching the catalog (7 steps)', () => {
88
+ expect(snapshot?.steps).toHaveLength(7)
89
+ })
90
+
91
+ it('every step has required fields: id, label, primaryEntity, outputs, stageKey, dependencyMode, capabilityKey', () => {
92
+ for (const step of snapshot?.steps ?? []) {
93
+ expect(step.id).toBeTruthy()
94
+ expect(step.label).toBeTruthy()
95
+ expect(['company', 'contact']).toContain(step.primaryEntity)
96
+ expect(step.outputs.length).toBeGreaterThanOrEqual(1)
97
+ expect(step.stageKey).toBeTruthy()
98
+ expect(step.dependencyMode).toBe('per-record-eligibility')
99
+ expect(step.capabilityKey).toBeTruthy()
100
+ }
101
+ })
102
+
103
+ it('every step has positive defaultBatchSize and maxBatchSize', () => {
104
+ for (const step of snapshot?.steps ?? []) {
105
+ expect(step.defaultBatchSize).toBeGreaterThan(0)
106
+ expect(step.maxBatchSize).toBeGreaterThanOrEqual(step.defaultBatchSize)
107
+ }
108
+ })
109
+
110
+ it('steps with declared dependsOn in the catalog have dependsOn in the snapshot', () => {
111
+ const withDeps = snapshot?.steps.filter((step) => step.dependsOn !== undefined) ?? []
112
+ expect(withDeps.length).toBeGreaterThan(0)
113
+ for (const step of withDeps) {
114
+ expect(Array.isArray(step.dependsOn)).toBe(true)
115
+ expect((step.dependsOn ?? []).length).toBeGreaterThan(0)
116
+ }
117
+ })
118
+
119
+ it('the first step (source-companies) has no dependsOn', () => {
120
+ const first = snapshot?.steps[0]
121
+ expect(first?.id).toBe('source-companies')
122
+ expect(first?.dependsOn).toBeUndefined()
123
+ })
124
+
125
+ it('outputs arrays are copies (mutation does not affect subsequent calls)', () => {
126
+ const snapshotA = createBuildPlanSnapshotFromTemplateId('local-services')
127
+ const snapshotB = createBuildPlanSnapshotFromTemplateId('local-services')
128
+ snapshotA?.steps[0]?.outputs.push('export' as never)
129
+ expect(snapshotB?.steps[0]?.outputs).not.toContain('export')
130
+ })
131
+ })
132
+
133
+ // ---------------------------------------------------------------------------
134
+ // createBuildPlanSnapshotFromTemplateId — "dtc-subscription-apollo-clickup"
135
+ // ---------------------------------------------------------------------------
136
+
137
+ describe('createBuildPlanSnapshotFromTemplateId — "dtc-subscription-apollo-clickup"', () => {
138
+ const snapshot = createBuildPlanSnapshotFromTemplateId('dtc-subscription-apollo-clickup')
139
+
140
+ it('returns a non-null snapshot', () => {
141
+ expect(snapshot).not.toBeNull()
142
+ })
143
+
144
+ it('preserves the template id in the snapshot', () => {
145
+ expect(snapshot?.templateId).toBe('dtc-subscription-apollo-clickup')
146
+ })
147
+
148
+ it('produces a steps array with length matching the catalog (6 steps)', () => {
149
+ expect(snapshot?.steps).toHaveLength(6)
150
+ })
151
+
152
+ it('every step has required fields: id, label, primaryEntity, outputs, stageKey, dependencyMode, capabilityKey', () => {
153
+ for (const step of snapshot?.steps ?? []) {
154
+ expect(step.id).toBeTruthy()
155
+ expect(step.label).toBeTruthy()
156
+ expect(['company', 'contact']).toContain(step.primaryEntity)
157
+ expect(step.outputs.length).toBeGreaterThanOrEqual(1)
158
+ expect(step.stageKey).toBeTruthy()
159
+ expect(step.dependencyMode).toBe('per-record-eligibility')
160
+ expect(step.capabilityKey).toBeTruthy()
161
+ }
162
+ })
163
+
164
+ it('every step has positive defaultBatchSize and maxBatchSize', () => {
165
+ for (const step of snapshot?.steps ?? []) {
166
+ expect(step.defaultBatchSize).toBeGreaterThan(0)
167
+ expect(step.maxBatchSize).toBeGreaterThanOrEqual(step.defaultBatchSize)
168
+ }
169
+ })
170
+
171
+ it('the first step (import-apollo-search) has no dependsOn', () => {
172
+ const first = snapshot?.steps[0]
173
+ expect(first?.id).toBe('import-apollo-search')
174
+ expect(first?.dependsOn).toBeUndefined()
175
+ })
176
+
177
+ it('the first step outputs both company and contact', () => {
178
+ const first = snapshot?.steps[0]
179
+ expect(first?.outputs).toContain('company')
180
+ expect(first?.outputs).toContain('contact')
181
+ })
182
+
183
+ it('the final step (review-and-export) outputs export', () => {
184
+ const last = snapshot?.steps[snapshot.steps.length - 1]
185
+ expect(last?.id).toBe('review-and-export')
186
+ expect(last?.outputs).toContain('export')
187
+ })
188
+
189
+ it('steps with descriptions in the catalog include description in the snapshot', () => {
190
+ const withDesc = snapshot?.steps.filter((step) => step.description !== undefined) ?? []
191
+ expect(withDesc.length).toBeGreaterThan(0)
192
+ for (const step of withDesc) {
193
+ expect(typeof step.description).toBe('string')
194
+ expect(step.description!.length).toBeGreaterThan(0)
195
+ }
196
+ })
197
+ })
198
+
199
+ // ---------------------------------------------------------------------------
200
+ // Snapshot shape contract — applies to every valid template id
201
+ // ---------------------------------------------------------------------------
202
+
203
+ describe('createBuildPlanSnapshotFromTemplateId — shape contract for all templates', () => {
204
+ it('returns a snapshot for every id that isProspectingBuildTemplateId reports valid', () => {
205
+ for (const option of PROSPECTING_BUILD_TEMPLATE_OPTIONS) {
206
+ const snapshot = createBuildPlanSnapshotFromTemplateId(option.id)
207
+ expect(snapshot).not.toBeNull()
208
+ expect(snapshot?.templateId).toBe(option.id)
209
+ expect(snapshot?.steps.length).toBeGreaterThan(0)
210
+ }
211
+ })
212
+ })
@@ -1,5 +1,7 @@
1
1
  import type { Database } from '../../supabase/database.types'
2
- import type { PipelineStage } from './api-schemas'
2
+ import type { CAPABILITY_REGISTRY } from '../../organization-model/domains/prospecting'
3
+ import type { LEAD_GEN_STAGE_CATALOG } from '../../organization-model/domains/sales'
4
+ import type { PipelineStage, ProcessingStageStatus } from './api-schemas'
3
5
 
4
6
  // =============================================================================
5
7
  // Supabase-Generated Types (Re-exported from database.types)
@@ -49,43 +51,20 @@ export interface WebPost {
49
51
  aiInsight?: string
50
52
  }
51
53
 
52
- /**
53
- * Tracks pipeline status for a company across all processing stages.
54
- */
55
- export interface CompanyPipelineStatus {
56
- acquired: boolean
57
- enrichment: {
58
- [source: string]: {
59
- status: 'pending' | 'complete' | 'failed' | 'skipped'
60
- completedAt?: string
61
- error?: string
62
- }
63
- }
64
- }
54
+ export type LeadGenStageKey = (typeof LEAD_GEN_STAGE_CATALOG)[keyof typeof LEAD_GEN_STAGE_CATALOG]['key']
55
+ export type LeadGenCapabilityKey = keyof typeof CAPABILITY_REGISTRY
65
56
 
66
- /**
67
- * Tracks pipeline status for a contact across all processing stages.
68
- */
69
- export interface ContactPipelineStatus {
70
- enrichment: {
71
- [source: string]: {
72
- status: 'pending' | 'complete' | 'failed' | 'skipped'
73
- completedAt?: string
74
- error?: string
75
- }
76
- }
77
- personalization: {
78
- status: 'pending' | 'complete' | 'failed' | 'skipped'
79
- completedAt?: string
80
- }
81
- outreach: {
82
- status: 'pending' | 'sent' | 'replied' | 'bounced' | 'opted-out'
83
- sentAt?: string
84
- channel?: string
85
- campaignId?: string
86
- }
57
+ export interface ProcessingStateEntry {
58
+ status: ProcessingStageStatus
59
+ data?: unknown
87
60
  }
88
61
 
62
+ export type ProcessingState = Partial<Record<LeadGenStageKey, ProcessingStateEntry>>
63
+ export type CompanyProcessingState = ProcessingState
64
+ export type ContactProcessingState = ProcessingState
65
+ /** @deprecated Use `processingState`. Retained only as a compile-time/read-shape bridge for external tenants. */
66
+ export type LegacyPipelineStatus = unknown
67
+
89
68
  /**
90
69
  * Enrichment data collected for a company from various sources.
91
70
  */
@@ -241,7 +220,9 @@ export interface AcqCompany {
241
220
  category: string | null
242
221
  categoryPain: string | null
243
222
  segment: string | null
244
- pipelineStatus: CompanyPipelineStatus | null
223
+ processingState: CompanyProcessingState | null
224
+ /** @deprecated Use `processingState`. This legacy DB column has been removed; responses return null. */
225
+ pipelineStatus?: LegacyPipelineStatus | null
245
226
  enrichmentData: CompanyEnrichmentData | null
246
227
  source: string | null
247
228
  batchId: string | null
@@ -277,7 +258,9 @@ export interface AcqContact {
277
258
  openingLine: string | null
278
259
  source: string | null
279
260
  sourceId: string | null
280
- pipelineStatus: ContactPipelineStatus | null
261
+ processingState: ContactProcessingState | null
262
+ /** @deprecated Use `processingState`. This legacy DB column has been removed; responses return null. */
263
+ pipelineStatus?: LegacyPipelineStatus | null
281
264
  enrichmentData: ContactEnrichmentData | null
282
265
  /** Attio Person record ID - set when contact responds and is added to CRM */
283
266
  attioPersonId: string | null
@@ -318,7 +301,7 @@ export interface DealContact {
318
301
  title: string | null
319
302
  headline: string | null
320
303
  linkedin_url: string | null
321
- pipeline_status: Record<string, unknown> | null
304
+ processing_state: Record<string, unknown> | null
322
305
  enrichment_data: Record<string, unknown> | null
323
306
  company: {
324
307
  id: string