@elevasis/core 0.18.0 → 0.20.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.
- package/dist/index.d.ts +82 -1
- package/dist/index.js +353 -171
- package/dist/knowledge/index.d.ts +44 -1
- package/dist/organization-model/index.d.ts +82 -1
- package/dist/organization-model/index.js +353 -171
- package/dist/test-utils/index.d.ts +41 -12
- package/dist/test-utils/index.js +352 -171
- package/package.json +4 -3
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +89 -69
- package/src/auth/multi-tenancy/organizations/__tests__/api-schemas.test.ts +194 -0
- package/src/auth/multi-tenancy/organizations/api-schemas.ts +136 -128
- package/src/business/acquisition/api-schemas.test.ts +199 -15
- package/src/business/acquisition/api-schemas.ts +116 -51
- package/src/business/acquisition/build-templates.test.ts +212 -0
- package/src/business/acquisition/derive-actions.test.ts +1 -1
- package/src/business/acquisition/types.ts +21 -38
- package/src/business/deals/api-schemas.ts +2 -2
- package/src/execution/engine/index.ts +436 -434
- package/src/execution/engine/tools/integration/server/adapters/google-calendar/google-calendar-adapter.ts +428 -0
- package/src/execution/engine/tools/integration/server/adapters/google-calendar/index.ts +2 -0
- package/src/execution/engine/tools/lead-service-types.ts +51 -9
- package/src/execution/engine/tools/platform/acquisition/company-tools.ts +7 -6
- package/src/execution/engine/tools/platform/acquisition/contact-tools.ts +6 -5
- package/src/execution/engine/tools/platform/acquisition/types.ts +20 -9
- package/src/execution/engine/tools/registry.ts +700 -698
- package/src/execution/engine/tools/tool-maps.ts +10 -0
- package/src/execution/external/__tests__/api-schemas.test.ts +127 -0
- package/src/integrations/oauth/__tests__/provider-registry.test.ts +7 -6
- package/src/integrations/oauth/provider-registry.ts +74 -61
- package/src/integrations/oauth/server/credentials.ts +43 -39
- package/src/knowledge/__tests__/queries.test.ts +89 -0
- package/src/organization-model/__tests__/graph.test.ts +108 -2
- package/src/organization-model/__tests__/icons.test.ts +61 -0
- package/src/organization-model/__tests__/knowledge.test.ts +118 -1
- package/src/organization-model/__tests__/prospecting-ssot.test.ts +91 -0
- package/src/organization-model/__tests__/schema.test.ts +122 -0
- package/src/organization-model/__tests__/surface-projection.test.ts +174 -0
- package/src/organization-model/defaults.ts +8 -0
- package/src/organization-model/domains/knowledge.ts +9 -0
- package/src/organization-model/domains/prospecting.ts +347 -226
- package/src/organization-model/domains/sales.ts +40 -30
- package/src/organization-model/graph/build.ts +74 -0
- package/src/organization-model/graph/schema.ts +1 -0
- package/src/organization-model/graph/types.ts +1 -0
- package/src/organization-model/icons.ts +3 -0
- package/src/organization-model/schema.ts +63 -0
- package/src/organization-model/surface-projection.ts +218 -0
- package/src/organization-model/types.ts +9 -1
- package/src/platform/constants/versions.ts +1 -1
- package/src/platform/utils/__tests__/validation.test.ts +1084 -1083
- package/src/platform/utils/validation.ts +425 -425
- package/src/reference/_generated/contracts.md +89 -69
- package/src/server.ts +6 -0
- package/src/supabase/database.types.ts +6 -12
|
@@ -1,10 +1,45 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
import { UuidSchema, NonEmptyStringSchema } from '../../platform/utils/validation'
|
|
3
|
-
import { LEAD_GEN_STAGE_CATALOG } from '../../organization-model/domains/sales'
|
|
3
|
+
import { CRM_PIPELINE_DEFINITION, LEAD_GEN_STAGE_CATALOG } from '../../organization-model/domains/sales'
|
|
4
|
+
import { CAPABILITY_REGISTRY } from '../../organization-model/domains/prospecting'
|
|
4
5
|
import { isProspectingBuildTemplateId } from './build-templates'
|
|
5
6
|
export { CrmPriorityBucketKeySchema, CrmPriorityBucketOverrideSchema, CrmPriorityOverrideSchema } from './crm-priority'
|
|
6
7
|
export type { CrmPriorityBucketOverride, CrmPriorityOverride, ResolvedCrmPriorityRuleConfig } from './crm-priority'
|
|
7
8
|
|
|
9
|
+
export const ProcessingStageStatusSchema = z.enum(['success', 'no_result', 'skipped', 'error'])
|
|
10
|
+
|
|
11
|
+
export const LeadGenStageKeySchema = z
|
|
12
|
+
.string()
|
|
13
|
+
.refine((value) => Object.prototype.hasOwnProperty.call(LEAD_GEN_STAGE_CATALOG, value), {
|
|
14
|
+
message: 'processing state key must match LEAD_GEN_STAGE_CATALOG'
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
export const LeadGenCapabilityKeySchema = z
|
|
18
|
+
.string()
|
|
19
|
+
.refine((value) => CAPABILITY_REGISTRY.some((c) => c.id === value), {
|
|
20
|
+
message: 'capabilityKey must match CAPABILITY_REGISTRY'
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const crmStageKeys = CRM_PIPELINE_DEFINITION.stages.map((stage) => stage.stageKey) as [string, ...string[]]
|
|
24
|
+
const crmStateKeys = CRM_PIPELINE_DEFINITION.stages.flatMap((stage) => stage.states.map((state) => state.stateKey)) as [
|
|
25
|
+
string,
|
|
26
|
+
...string[]
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
export const CrmStageKeySchema = z.enum(crmStageKeys)
|
|
30
|
+
export const CrmStateKeySchema = z.enum(crmStateKeys)
|
|
31
|
+
|
|
32
|
+
export const ProcessingStateEntrySchema = z
|
|
33
|
+
.object({
|
|
34
|
+
status: ProcessingStageStatusSchema,
|
|
35
|
+
data: z.unknown().optional()
|
|
36
|
+
})
|
|
37
|
+
.passthrough()
|
|
38
|
+
|
|
39
|
+
export const ProcessingStateSchema = z.record(LeadGenStageKeySchema, ProcessingStateEntrySchema)
|
|
40
|
+
export const CompanyProcessingStateSchema = ProcessingStateSchema
|
|
41
|
+
export const ContactProcessingStateSchema = ProcessingStateSchema
|
|
42
|
+
|
|
8
43
|
/**
|
|
9
44
|
* Deal Management API Schemas
|
|
10
45
|
*
|
|
@@ -21,7 +56,7 @@ export type { CrmPriorityBucketOverride, CrmPriorityOverride, ResolvedCrmPriorit
|
|
|
21
56
|
// Enum literals (must match DB CHECK constraints exactly)
|
|
22
57
|
// ---------------------------------------------------------------------------
|
|
23
58
|
|
|
24
|
-
export const DealStageSchema =
|
|
59
|
+
export const DealStageSchema = CrmStageKeySchema
|
|
25
60
|
|
|
26
61
|
export const AcqDealTaskKindSchema = z.enum(['call', 'email', 'meeting', 'other'])
|
|
27
62
|
|
|
@@ -89,7 +124,17 @@ export const TransitionItemRequestSchema = z
|
|
|
89
124
|
.object({
|
|
90
125
|
pipelineKey: z.string().min(1),
|
|
91
126
|
stageKey: z.string().min(1),
|
|
92
|
-
stateKey: z.string().nullable().optional(),
|
|
127
|
+
stateKey: z.string().min(1).nullable().optional(),
|
|
128
|
+
reason: z.string().optional(),
|
|
129
|
+
expectedUpdatedAt: z.string().datetime().optional()
|
|
130
|
+
})
|
|
131
|
+
.strict()
|
|
132
|
+
|
|
133
|
+
export const CrmTransitionItemRequestSchema = z
|
|
134
|
+
.object({
|
|
135
|
+
pipelineKey: z.literal(CRM_PIPELINE_DEFINITION.pipelineKey),
|
|
136
|
+
stageKey: CrmStageKeySchema,
|
|
137
|
+
stateKey: CrmStateKeySchema.nullable().optional(),
|
|
93
138
|
reason: z.string().optional(),
|
|
94
139
|
expectedUpdatedAt: z.string().datetime().optional()
|
|
95
140
|
})
|
|
@@ -97,7 +142,7 @@ export const TransitionItemRequestSchema = z
|
|
|
97
142
|
|
|
98
143
|
export const TransitionDealStateRequestSchema = z
|
|
99
144
|
.object({
|
|
100
|
-
stateKey:
|
|
145
|
+
stateKey: CrmStateKeySchema,
|
|
101
146
|
reason: z.string().optional(),
|
|
102
147
|
expectedUpdatedAt: z.string().datetime().optional()
|
|
103
148
|
})
|
|
@@ -132,7 +177,7 @@ export const DealContactSummarySchema = z.object({
|
|
|
132
177
|
title: z.string().nullable(),
|
|
133
178
|
headline: z.string().nullable(),
|
|
134
179
|
linkedin_url: z.string().nullable(),
|
|
135
|
-
|
|
180
|
+
processing_state: ProcessingStateSchema.nullable(),
|
|
136
181
|
enrichment_data: z.record(z.string(), z.unknown()).nullable(),
|
|
137
182
|
company: z
|
|
138
183
|
.object({
|
|
@@ -305,6 +350,11 @@ export const DealTaskListResponseSchema = z.array(DealTaskResponseSchema)
|
|
|
305
350
|
// ---------------------------------------------------------------------------
|
|
306
351
|
|
|
307
352
|
export const DealSchemas = {
|
|
353
|
+
// Primitives
|
|
354
|
+
CrmStageKey: CrmStageKeySchema,
|
|
355
|
+
CrmStateKey: CrmStateKeySchema,
|
|
356
|
+
DealStage: DealStageSchema,
|
|
357
|
+
|
|
308
358
|
// Params
|
|
309
359
|
DealIdParams: DealIdParamsSchema,
|
|
310
360
|
DealTaskIdParams: DealTaskIdParamsSchema,
|
|
@@ -317,7 +367,7 @@ export const DealSchemas = {
|
|
|
317
367
|
// Request bodies
|
|
318
368
|
CreateDealNoteRequest: CreateDealNoteRequestSchema,
|
|
319
369
|
CreateDealTaskRequest: CreateDealTaskRequestSchema,
|
|
320
|
-
TransitionItemRequest:
|
|
370
|
+
TransitionItemRequest: CrmTransitionItemRequestSchema,
|
|
321
371
|
TransitionDealStateRequest: TransitionDealStateRequestSchema,
|
|
322
372
|
ExecuteActionParams: ExecuteActionParamsSchema,
|
|
323
373
|
ExecuteActionRequest: ExecuteActionRequestSchema,
|
|
@@ -340,6 +390,8 @@ export const DealSchemas = {
|
|
|
340
390
|
// ---------------------------------------------------------------------------
|
|
341
391
|
|
|
342
392
|
export type DealStage = z.infer<typeof DealStageSchema>
|
|
393
|
+
export type CrmStageKey = z.infer<typeof CrmStageKeySchema>
|
|
394
|
+
export type CrmStateKey = z.infer<typeof CrmStateKeySchema>
|
|
343
395
|
export type AcqDealTaskKind = z.infer<typeof AcqDealTaskKindSchema>
|
|
344
396
|
export type DealIdParams = z.infer<typeof DealIdParamsSchema>
|
|
345
397
|
export type DealTaskIdParams = z.infer<typeof DealTaskIdParamsSchema>
|
|
@@ -349,6 +401,7 @@ export type ListDealTasksDueQuery = z.infer<typeof ListDealTasksDueQuerySchema>
|
|
|
349
401
|
export type CreateDealNoteRequest = z.infer<typeof CreateDealNoteRequestSchema>
|
|
350
402
|
export type CreateDealTaskRequest = z.infer<typeof CreateDealTaskRequestSchema>
|
|
351
403
|
export type TransitionItemRequest = z.infer<typeof TransitionItemRequestSchema>
|
|
404
|
+
export type CrmTransitionItemRequest = z.infer<typeof CrmTransitionItemRequestSchema>
|
|
352
405
|
export type TransitionDealStateRequest = z.infer<typeof TransitionDealStateRequestSchema>
|
|
353
406
|
export type ExecuteActionParams = z.infer<typeof ExecuteActionParamsSchema>
|
|
354
407
|
export type ExecuteActionRequest = z.infer<typeof ExecuteActionRequestSchema>
|
|
@@ -442,10 +495,10 @@ export const BuildPlanSnapshotStepSchema = z
|
|
|
442
495
|
description: z.string().trim().min(1).max(2000).optional(),
|
|
443
496
|
primaryEntity: z.enum(['company', 'contact']),
|
|
444
497
|
outputs: z.array(z.enum(['company', 'contact', 'export'])).min(1),
|
|
445
|
-
stageKey:
|
|
498
|
+
stageKey: LeadGenStageKeySchema,
|
|
446
499
|
dependsOn: z.array(z.string().trim().min(1).max(100)).optional(),
|
|
447
500
|
dependencyMode: z.literal('per-record-eligibility'),
|
|
448
|
-
capabilityKey:
|
|
501
|
+
capabilityKey: LeadGenCapabilityKeySchema,
|
|
449
502
|
defaultBatchSize: z.number().int().positive(),
|
|
450
503
|
maxBatchSize: z.number().int().positive()
|
|
451
504
|
})
|
|
@@ -454,11 +507,38 @@ export const BuildPlanSnapshotStepSchema = z
|
|
|
454
507
|
path: ['defaultBatchSize']
|
|
455
508
|
})
|
|
456
509
|
|
|
457
|
-
export const BuildPlanSnapshotSchema = z
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
510
|
+
export const BuildPlanSnapshotSchema = z
|
|
511
|
+
.object({
|
|
512
|
+
templateId: z.string().trim().min(1).max(100),
|
|
513
|
+
templateLabel: z.string().trim().min(1).max(120),
|
|
514
|
+
steps: z.array(BuildPlanSnapshotStepSchema).min(1)
|
|
515
|
+
})
|
|
516
|
+
.superRefine((snapshot, ctx) => {
|
|
517
|
+
const stepIds = new Set<string>()
|
|
518
|
+
|
|
519
|
+
snapshot.steps.forEach((step, index) => {
|
|
520
|
+
if (stepIds.has(step.id)) {
|
|
521
|
+
ctx.addIssue({
|
|
522
|
+
code: z.ZodIssueCode.custom,
|
|
523
|
+
message: `duplicate build-plan step id "${step.id}"`,
|
|
524
|
+
path: ['steps', index, 'id']
|
|
525
|
+
})
|
|
526
|
+
}
|
|
527
|
+
stepIds.add(step.id)
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
snapshot.steps.forEach((step, index) => {
|
|
531
|
+
for (const dependencyId of step.dependsOn ?? []) {
|
|
532
|
+
if (!stepIds.has(dependencyId)) {
|
|
533
|
+
ctx.addIssue({
|
|
534
|
+
code: z.ZodIssueCode.custom,
|
|
535
|
+
message: `dependsOn references unknown build-plan step "${dependencyId}"`,
|
|
536
|
+
path: ['steps', index, 'dependsOn']
|
|
537
|
+
})
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
})
|
|
541
|
+
})
|
|
462
542
|
|
|
463
543
|
export const AcqListMetadataSchema = z
|
|
464
544
|
.object({
|
|
@@ -638,13 +718,6 @@ export const ListTelemetryResponseSchema = ListTelemetrySchema
|
|
|
638
718
|
|
|
639
719
|
export const ListTelemetryListResponseSchema = z.array(ListTelemetrySchema)
|
|
640
720
|
|
|
641
|
-
/**
|
|
642
|
-
* Terminal row-level status for one lead-gen processing stage.
|
|
643
|
-
* Missing key still means not attempted; legacy boolean `true` is normalized
|
|
644
|
-
* to `success` by the API reader during rollout.
|
|
645
|
-
*/
|
|
646
|
-
export const ProcessingStageStatusSchema = z.enum(['success', 'no_result', 'skipped', 'error'])
|
|
647
|
-
|
|
648
721
|
/**
|
|
649
722
|
* Per-stage progress aggregate for a single pipeline stage.
|
|
650
723
|
* `attempted` counts terminal statuses, including success, no-result, skipped,
|
|
@@ -704,29 +777,6 @@ export const AcqCompanyStatusSchema = z.enum(['active', 'invalid'])
|
|
|
704
777
|
export const AcqContactStatusSchema = z.enum(['active', 'invalid'])
|
|
705
778
|
export const AcqEmailValidSchema = z.enum(['VALID', 'INVALID', 'RISKY', 'UNKNOWN'])
|
|
706
779
|
|
|
707
|
-
/**
|
|
708
|
-
* Zod schema mirroring the CompanyPipelineStatus interface from types.ts.
|
|
709
|
-
* Used by operations-layer CompanyRecord schemas to validate pipelineStatus
|
|
710
|
-
* against the canonical shape instead of using open passthrough().
|
|
711
|
-
*/
|
|
712
|
-
export const CompanyPipelineStatusSchema = z
|
|
713
|
-
.object({
|
|
714
|
-
acquired: z.boolean().optional(),
|
|
715
|
-
enrichment: z
|
|
716
|
-
.record(
|
|
717
|
-
z.string(),
|
|
718
|
-
z
|
|
719
|
-
.object({
|
|
720
|
-
status: z.enum(['pending', 'complete', 'failed', 'skipped']),
|
|
721
|
-
completedAt: z.string().optional(),
|
|
722
|
-
error: z.string().optional()
|
|
723
|
-
})
|
|
724
|
-
.passthrough()
|
|
725
|
-
)
|
|
726
|
-
.optional()
|
|
727
|
-
})
|
|
728
|
-
.passthrough()
|
|
729
|
-
|
|
730
780
|
export const CompanyIdParamsSchema = z.object({
|
|
731
781
|
companyId: UuidSchema
|
|
732
782
|
})
|
|
@@ -743,6 +793,7 @@ export const ListCompaniesQuerySchema = z
|
|
|
743
793
|
website: z.string().trim().min(1).max(2048).optional(),
|
|
744
794
|
segment: z.string().trim().min(1).max(255).optional(),
|
|
745
795
|
category: z.string().trim().min(1).max(255).optional(),
|
|
796
|
+
pipelineStatus: z.unknown().optional(),
|
|
746
797
|
batchId: z.string().trim().min(1).max(255).optional(),
|
|
747
798
|
status: AcqCompanyStatusSchema.optional(),
|
|
748
799
|
includeAll: QueryBooleanSchema.optional(),
|
|
@@ -776,6 +827,7 @@ export const CreateCompanyRequestSchema = z
|
|
|
776
827
|
category: z.string().trim().min(1).max(255).optional(),
|
|
777
828
|
source: z.string().trim().min(1).max(255).optional(),
|
|
778
829
|
batchId: z.string().trim().min(1).max(255).optional(),
|
|
830
|
+
pipelineStatus: z.unknown().optional(),
|
|
779
831
|
verticalResearch: z.string().trim().min(1).max(5000).optional()
|
|
780
832
|
})
|
|
781
833
|
.strict()
|
|
@@ -792,7 +844,8 @@ export const UpdateCompanyRequestSchema = z
|
|
|
792
844
|
locationState: z.string().trim().min(1).max(255).optional(),
|
|
793
845
|
category: z.string().trim().min(1).max(255).optional(),
|
|
794
846
|
segment: z.string().trim().min(1).max(255).optional(),
|
|
795
|
-
|
|
847
|
+
processingState: CompanyProcessingStateSchema.optional(),
|
|
848
|
+
pipelineStatus: z.unknown().optional(),
|
|
796
849
|
enrichmentData: z.record(z.string(), z.unknown()).optional(),
|
|
797
850
|
source: z.string().trim().min(1).max(255).optional(),
|
|
798
851
|
batchId: z.string().trim().min(1).max(255).optional(),
|
|
@@ -812,6 +865,7 @@ export const UpdateCompanyRequestSchema = z
|
|
|
812
865
|
data.locationState !== undefined ||
|
|
813
866
|
data.category !== undefined ||
|
|
814
867
|
data.segment !== undefined ||
|
|
868
|
+
data.processingState !== undefined ||
|
|
815
869
|
data.pipelineStatus !== undefined ||
|
|
816
870
|
data.enrichmentData !== undefined ||
|
|
817
871
|
data.source !== undefined ||
|
|
@@ -833,7 +887,8 @@ export const CreateContactRequestSchema = z
|
|
|
833
887
|
title: z.string().trim().min(1).max(255).optional(),
|
|
834
888
|
source: z.string().trim().min(1).max(255).optional(),
|
|
835
889
|
sourceId: z.string().trim().min(1).max(255).optional(),
|
|
836
|
-
batchId: z.string().trim().min(1).max(255).optional()
|
|
890
|
+
batchId: z.string().trim().min(1).max(255).optional(),
|
|
891
|
+
pipelineStatus: z.unknown().optional()
|
|
837
892
|
})
|
|
838
893
|
.strict()
|
|
839
894
|
|
|
@@ -848,7 +903,8 @@ export const UpdateContactRequestSchema = z
|
|
|
848
903
|
headline: z.string().trim().min(1).max(5000).optional(),
|
|
849
904
|
filterReason: z.string().trim().min(1).max(5000).optional(),
|
|
850
905
|
openingLine: z.string().trim().min(1).max(5000).optional(),
|
|
851
|
-
|
|
906
|
+
processingState: ContactProcessingStateSchema.optional(),
|
|
907
|
+
pipelineStatus: z.unknown().optional(),
|
|
852
908
|
enrichmentData: z.record(z.string(), z.unknown()).optional(),
|
|
853
909
|
status: AcqContactStatusSchema.optional()
|
|
854
910
|
})
|
|
@@ -864,6 +920,7 @@ export const UpdateContactRequestSchema = z
|
|
|
864
920
|
data.headline !== undefined ||
|
|
865
921
|
data.filterReason !== undefined ||
|
|
866
922
|
data.openingLine !== undefined ||
|
|
923
|
+
data.processingState !== undefined ||
|
|
867
924
|
data.pipelineStatus !== undefined ||
|
|
868
925
|
data.enrichmentData !== undefined ||
|
|
869
926
|
data.status !== undefined,
|
|
@@ -886,7 +943,8 @@ export const AcqCompanyResponseSchema = z.object({
|
|
|
886
943
|
category: z.string().nullable(),
|
|
887
944
|
categoryPain: z.string().nullable(),
|
|
888
945
|
segment: z.string().nullable(),
|
|
889
|
-
|
|
946
|
+
processingState: CompanyProcessingStateSchema.nullable(),
|
|
947
|
+
pipelineStatus: z.unknown().nullable().optional(),
|
|
890
948
|
enrichmentData: z.record(z.string(), z.unknown()).nullable(),
|
|
891
949
|
source: z.string().nullable(),
|
|
892
950
|
batchId: z.string().nullable(),
|
|
@@ -936,7 +994,8 @@ export const AcqContactResponseSchema = z.object({
|
|
|
936
994
|
openingLine: z.string().nullable(),
|
|
937
995
|
source: z.string().nullable(),
|
|
938
996
|
sourceId: z.string().nullable(),
|
|
939
|
-
|
|
997
|
+
processingState: ContactProcessingStateSchema.nullable(),
|
|
998
|
+
pipelineStatus: z.unknown().nullable().optional(),
|
|
940
999
|
enrichmentData: z.record(z.string(), z.unknown()).nullable(),
|
|
941
1000
|
attioPersonId: z.string().nullable(),
|
|
942
1001
|
batchId: z.string().nullable(),
|
|
@@ -1058,11 +1117,12 @@ export const AcqListCompanyResponseSchema = z.object({
|
|
|
1058
1117
|
})
|
|
1059
1118
|
|
|
1060
1119
|
// ---------------------------------------------------------------------------
|
|
1061
|
-
// Track B: Transition
|
|
1062
|
-
//
|
|
1120
|
+
// Track B: Transition request for list, list-member, and list-company substrate routes.
|
|
1121
|
+
// CRM deals use DealSchemas.TransitionItemRequest, which is catalog-backed.
|
|
1063
1122
|
// ---------------------------------------------------------------------------
|
|
1064
1123
|
|
|
1065
1124
|
export const AcqCompanySchemas = {
|
|
1125
|
+
CompanyProcessingState: CompanyProcessingStateSchema,
|
|
1066
1126
|
CompanyIdParams: CompanyIdParamsSchema,
|
|
1067
1127
|
ListCompaniesQuery: ListCompaniesQuerySchema,
|
|
1068
1128
|
CreateCompanyRequest: CreateCompanyRequestSchema,
|
|
@@ -1073,6 +1133,7 @@ export const AcqCompanySchemas = {
|
|
|
1073
1133
|
}
|
|
1074
1134
|
|
|
1075
1135
|
export const AcqContactSchemas = {
|
|
1136
|
+
ContactProcessingState: ContactProcessingStateSchema,
|
|
1076
1137
|
ContactIdParams: ContactIdParamsSchema,
|
|
1077
1138
|
ListContactsQuery: ListContactsQuerySchema,
|
|
1078
1139
|
CreateContactRequest: CreateContactRequestSchema,
|
|
@@ -1116,7 +1177,10 @@ export const AcqListSchemas = {
|
|
|
1116
1177
|
BuildPlanSnapshot: BuildPlanSnapshotSchema,
|
|
1117
1178
|
BuildPlanSnapshotStep: BuildPlanSnapshotStepSchema,
|
|
1118
1179
|
AcqListMetadata: AcqListMetadataSchema,
|
|
1180
|
+
LeadGenStageKey: LeadGenStageKeySchema,
|
|
1181
|
+
LeadGenCapabilityKey: LeadGenCapabilityKeySchema,
|
|
1119
1182
|
ProcessingStageStatus: ProcessingStageStatusSchema,
|
|
1183
|
+
ProcessingState: ProcessingStateSchema,
|
|
1120
1184
|
ListStageCounts: ListStageCountsSchema,
|
|
1121
1185
|
ListTelemetry: ListTelemetrySchema,
|
|
1122
1186
|
|
|
@@ -1160,7 +1224,7 @@ export const AcqSubstrateSchemas = {
|
|
|
1160
1224
|
ListCompanyIdParams: ListCompanyIdParamsSchema,
|
|
1161
1225
|
AcqListCompanyResponse: AcqListCompanyResponseSchema,
|
|
1162
1226
|
|
|
1163
|
-
// Transition (
|
|
1227
|
+
// Transition (generic stateful substrate)
|
|
1164
1228
|
TransitionItemRequest: TransitionItemRequestSchema
|
|
1165
1229
|
}
|
|
1166
1230
|
|
|
@@ -1195,6 +1259,7 @@ export type PipelineConfig = z.infer<typeof PipelineConfigSchema>
|
|
|
1195
1259
|
export type BuildPlanSnapshotStep = z.infer<typeof BuildPlanSnapshotStepSchema>
|
|
1196
1260
|
export type BuildPlanSnapshot = z.infer<typeof BuildPlanSnapshotSchema>
|
|
1197
1261
|
export type AcqListMetadata = z.infer<typeof AcqListMetadataSchema>
|
|
1262
|
+
export type LeadGenCapabilityKey = z.infer<typeof LeadGenCapabilityKeySchema>
|
|
1198
1263
|
export type ProcessingStageStatus = z.infer<typeof ProcessingStageStatusSchema>
|
|
1199
1264
|
export type ListStageCountsInput = z.infer<typeof ListStageCountsSchema>['stageCounts']
|
|
1200
1265
|
export type ListTelemetryInput = z.infer<typeof ListTelemetrySchema>
|
|
@@ -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 {
|
|
2
|
+
import type { Capability } 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
|
-
|
|
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 = Capability['id']
|
|
65
56
|
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
304
|
+
processing_state: Record<string, unknown> | null
|
|
322
305
|
enrichment_data: Record<string, unknown> | null
|
|
323
306
|
company: {
|
|
324
307
|
id: string
|