@elevasis/core 0.30.0 → 0.32.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 (30) hide show
  1. package/dist/auth/index.d.ts +58 -5
  2. package/dist/index.d.ts +16 -5
  3. package/dist/index.js +73 -109
  4. package/dist/knowledge/index.d.ts +10 -2
  5. package/dist/organization-model/index.d.ts +16 -5
  6. package/dist/organization-model/index.js +73 -109
  7. package/dist/test-utils/index.d.ts +55 -2
  8. package/dist/test-utils/index.js +72 -108
  9. package/package.json +1 -1
  10. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +376 -446
  11. package/src/business/acquisition/api-schemas.test.ts +69 -5
  12. package/src/business/acquisition/crm-state-actions.test.ts +24 -6
  13. package/src/business/pdf/sections/__tests__/proposal-document.test.ts +146 -0
  14. package/src/business/pdf/sections/acceptance.ts +114 -112
  15. package/src/business/pdf/sections/proposal-document.ts +206 -200
  16. package/src/execution/engine/index.ts +440 -439
  17. package/src/execution/engine/tools/integration/types/clickup.ts +57 -0
  18. package/src/execution/engine/tools/integration/types/index.ts +20 -19
  19. package/src/execution/engine/tools/tool-maps.ts +16 -0
  20. package/src/organization-model/__tests__/domains/entities.test.ts +35 -56
  21. package/src/organization-model/__tests__/domains/passthrough-extensibility.test.ts +199 -0
  22. package/src/organization-model/domains/branding.ts +58 -16
  23. package/src/organization-model/domains/entities.ts +0 -103
  24. package/src/organization-model/domains/identity.ts +122 -94
  25. package/src/organization-model/domains/sales.test.ts +35 -28
  26. package/src/organization-model/domains/sales.ts +0 -85
  27. package/src/organization-model/published.ts +0 -1
  28. package/src/organization-model/schema.ts +2 -2
  29. package/src/reference/_generated/contracts.md +0 -94
  30. package/src/supabase/database.types.ts +45 -0
@@ -1,9 +1,73 @@
1
1
  import { describe, expect, it } from 'vitest'
2
- import {
3
- LEAD_GEN_PIPELINE_DEFINITIONS,
4
- type CrmPriorityRuleConfig,
5
- type StatefulPipelineDefinition
6
- } from '../../organization-model/domains/sales'
2
+ import { type CrmPriorityRuleConfig, type StatefulPipelineDefinition } from '../../organization-model/domains/sales'
3
+
4
+ // Inline fixture for lead-gen pipeline stage/state validation tests.
5
+ // The canonical constants live in @repo/elevasis-core; @repo/core cannot depend on it.
6
+ const LEAD_GEN_PIPELINE_DEFINITIONS: Record<string, StatefulPipelineDefinition[]> = {
7
+ 'acq.list-member': [
8
+ {
9
+ pipelineKey: 'lead-gen',
10
+ label: 'Lead Generation',
11
+ entityKey: 'acq.list-member',
12
+ stages: [
13
+ {
14
+ stageKey: 'outreach',
15
+ label: 'Outreach',
16
+ states: [
17
+ { stateKey: 'pending', label: 'Pending' },
18
+ { stateKey: 'personalized', label: 'Personalized' },
19
+ { stateKey: 'uploaded', label: 'Uploaded' },
20
+ { stateKey: 'interested', label: 'Interested' }
21
+ ]
22
+ },
23
+ {
24
+ stageKey: 'prospecting',
25
+ label: 'Prospecting',
26
+ states: [
27
+ { stateKey: 'pending', label: 'Pending' },
28
+ { stateKey: 'discovered', label: 'Discovered' },
29
+ { stateKey: 'verified', label: 'Verified' }
30
+ ]
31
+ },
32
+ { stageKey: 'qualification', label: 'Qualification', states: [{ stateKey: 'pending', label: 'Pending' }] }
33
+ ]
34
+ }
35
+ ],
36
+ 'acq.list-company': [
37
+ {
38
+ pipelineKey: 'lead-gen',
39
+ label: 'Lead Generation',
40
+ entityKey: 'acq.list-company',
41
+ stages: [
42
+ {
43
+ stageKey: 'outreach',
44
+ label: 'Outreach',
45
+ states: [
46
+ { stateKey: 'pending', label: 'Pending' },
47
+ { stateKey: 'uploaded', label: 'Uploaded' }
48
+ ]
49
+ },
50
+ {
51
+ stageKey: 'prospecting',
52
+ label: 'Prospecting',
53
+ states: [
54
+ { stateKey: 'pending', label: 'Pending' },
55
+ { stateKey: 'populated', label: 'Populated' },
56
+ { stateKey: 'extracted', label: 'Extracted' }
57
+ ]
58
+ },
59
+ {
60
+ stageKey: 'qualification',
61
+ label: 'Qualification',
62
+ states: [
63
+ { stateKey: 'pending', label: 'Pending' },
64
+ { stateKey: 'qualified', label: 'Qualified' }
65
+ ]
66
+ }
67
+ ]
68
+ }
69
+ ]
70
+ }
7
71
  import { DEFAULT_ORGANIZATION_MODEL } from '../../organization-model/defaults'
8
72
  import type { OrganizationModel } from '../../organization-model/types'
9
73
  import { CrmPriorityOverrideSchema, evaluateCrmDealPriority, resolveCrmPriorityRuleConfig } from './crm-priority'
@@ -1,8 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest'
2
- import {
3
- LEAD_GEN_PIPELINE_DEFINITIONS,
4
- type StatefulPipelineDefinition
5
- } from '../../organization-model/domains/sales'
2
+ import { type StatefulPipelineDefinition } from '../../organization-model/domains/sales'
6
3
  import { ActivityEventSchema } from './activity-events'
7
4
  import { DealStageSchema, TransitionItemRequestSchema } from './api-schemas'
8
5
  import { deriveActions } from './derive-actions'
@@ -188,8 +185,29 @@ describe('CRM stage and transition vocabulary contracts', () => {
188
185
  }
189
186
  })
190
187
 
191
- it('accepts canonical lead-gen stage/state pairs in transition requests', () => {
192
- for (const pipelineDefinitions of Object.values(LEAD_GEN_PIPELINE_DEFINITIONS)) {
188
+ it('accepts caller-supplied lead-gen stage/state pairs in transition requests', () => {
189
+ const leadGenPipelines: Record<string, StatefulPipelineDefinition[]> = {
190
+ 'acq.list-member': [
191
+ {
192
+ pipelineKey: 'lead-gen',
193
+ label: 'Lead Gen',
194
+ entityKey: 'leadgen.contact',
195
+ stages: [
196
+ {
197
+ stageKey: 'outreach',
198
+ label: 'Outreach',
199
+ color: 'blue',
200
+ states: [
201
+ { stateKey: 'personalized', label: 'Personalized' },
202
+ { stateKey: 'contacted', label: 'Contacted' }
203
+ ]
204
+ }
205
+ ]
206
+ }
207
+ ]
208
+ }
209
+
210
+ for (const pipelineDefinitions of Object.values(leadGenPipelines)) {
193
211
  for (const pipeline of pipelineDefinitions) {
194
212
  for (const stage of pipeline.stages) {
195
213
  for (const state of stage.states) {
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Tests for the shared PDF proposal document builder.
3
+ *
4
+ * Verifies that provider branding is fully caller-supplied and no
5
+ * hardcoded "Elevasis" strings leak into the shared @repo/core builder.
6
+ */
7
+
8
+ import { describe, it, expect } from 'vitest'
9
+ import { buildProposalDocument } from '../proposal-document'
10
+ import { acceptanceSection } from '../acceptance'
11
+ import type { AutomationConfig } from '../types'
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // Fixtures
15
+ // ---------------------------------------------------------------------------
16
+
17
+ const sampleAutomation: AutomationConfig = {
18
+ title: 'Test Automation',
19
+ howItWorks: 'Does stuff automatically.',
20
+ workflow: ['Step 1', 'Step 2'],
21
+ impact: ['Saves time'],
22
+ hoursPerWeek: 5,
23
+ annualSavings: 10000
24
+ }
25
+
26
+ const baseConfig = {
27
+ company: 'Acme Corp',
28
+ contact: 'Jane Doe',
29
+ vertical: 'Tech',
30
+ solutions: 'Automation',
31
+ executiveSummary: 'Summary text.',
32
+ automations: [sampleAutomation],
33
+ tier: 'tier2' as const,
34
+ initialFee: 5000,
35
+ monthlyFee: 2500
36
+ }
37
+
38
+ // ---------------------------------------------------------------------------
39
+ // buildProposalDocument
40
+ // ---------------------------------------------------------------------------
41
+
42
+ describe('buildProposalDocument', () => {
43
+ it('sets metadata.author from providerName', () => {
44
+ const doc = buildProposalDocument({ ...baseConfig, providerName: 'Elevasis' })
45
+ expect(doc.metadata.author).toBe('Elevasis')
46
+ })
47
+
48
+ it('sets metadata.author to a different caller-supplied value', () => {
49
+ const doc = buildProposalDocument({ ...baseConfig, providerName: 'Acme Solutions' })
50
+ expect(doc.metadata.author).toBe('Acme Solutions')
51
+ })
52
+
53
+ it('does not hardcode "Elevasis" in metadata when a different providerName is given', () => {
54
+ const doc = buildProposalDocument({ ...baseConfig, providerName: 'OtherProvider' })
55
+ expect(doc.metadata.author).not.toBe('Elevasis')
56
+ })
57
+
58
+ it('includes the providerName in the acceptance page text', () => {
59
+ const doc = buildProposalDocument({ ...baseConfig, providerName: 'MyAgency' })
60
+ // The acceptance page is the last page
61
+ const lastPage = doc.pages[doc.pages.length - 1]
62
+ const allText = JSON.stringify(lastPage)
63
+ expect(allText).toContain('MyAgency')
64
+ expect(allText).not.toContain('Elevasis')
65
+ })
66
+
67
+ it('includes correct metadata title', () => {
68
+ const doc = buildProposalDocument({ ...baseConfig, providerName: 'Elevasis' })
69
+ expect(doc.metadata.title).toBe('Proposal for Acme Corp')
70
+ })
71
+
72
+ it('builds the correct number of pages (cover + summary + automations + summary-investment + acceptance)', () => {
73
+ const doc = buildProposalDocument({ ...baseConfig, providerName: 'Elevasis' })
74
+ // 1 cover + 1 summary + 1 automation + 1 summary-investment + 1 acceptance = 5
75
+ expect(doc.pages).toHaveLength(5)
76
+ })
77
+ })
78
+
79
+ // ---------------------------------------------------------------------------
80
+ // acceptanceSection
81
+ // ---------------------------------------------------------------------------
82
+
83
+ describe('acceptanceSection', () => {
84
+ it('uses providerName in the acceptance statement', () => {
85
+ const page = acceptanceSection({
86
+ companyName: 'Acme Corp',
87
+ contactName: 'Jane Doe',
88
+ tier: 'tier2',
89
+ initialFee: 5000,
90
+ providerName: 'Elevasis'
91
+ })
92
+ const text = JSON.stringify(page)
93
+ expect(text).toContain('authorizes Elevasis to proceed')
94
+ })
95
+
96
+ it('uses a different providerName when supplied', () => {
97
+ const page = acceptanceSection({
98
+ companyName: 'Acme Corp',
99
+ contactName: 'Jane Doe',
100
+ tier: 'tier1',
101
+ initialFee: 3000,
102
+ providerName: 'AnotherAgency'
103
+ })
104
+ const text = JSON.stringify(page)
105
+ expect(text).toContain('authorizes AnotherAgency to proceed')
106
+ expect(text).not.toContain('Elevasis')
107
+ })
108
+
109
+ it('includes companyName and contactName in the acceptance statement', () => {
110
+ const page = acceptanceSection({
111
+ companyName: 'TestCo',
112
+ contactName: 'Bob Smith',
113
+ tier: 'tier2',
114
+ initialFee: 4000,
115
+ providerName: 'MyProvider'
116
+ })
117
+ const text = JSON.stringify(page)
118
+ expect(text).toContain('Bob Smith')
119
+ expect(text).toContain('TestCo')
120
+ })
121
+
122
+ it('omits monthly fee term when monthlyFee is not provided', () => {
123
+ const page = acceptanceSection({
124
+ companyName: 'Acme',
125
+ contactName: 'Alice',
126
+ tier: 'tier1',
127
+ initialFee: 2000,
128
+ providerName: 'Elevasis'
129
+ })
130
+ const text = JSON.stringify(page)
131
+ expect(text).not.toContain('Monthly fee')
132
+ })
133
+
134
+ it('includes monthly fee term when monthlyFee is provided', () => {
135
+ const page = acceptanceSection({
136
+ companyName: 'Acme',
137
+ contactName: 'Alice',
138
+ tier: 'tier2',
139
+ initialFee: 5000,
140
+ monthlyFee: 1500,
141
+ providerName: 'Elevasis'
142
+ })
143
+ const text = JSON.stringify(page)
144
+ expect(text).toContain('Monthly fee')
145
+ })
146
+ })
@@ -1,112 +1,114 @@
1
- /**
2
- * Acceptance Section Builder
3
- *
4
- * Creates a dedicated signature/acceptance page for proposals.
5
- * This page contains minimal terms and signature fields placement area.
6
- *
7
- * Pattern: Content-only configuration, styling handled by theme system.
8
- *
9
- * @example
10
- * ```typescript
11
- * import { acceptanceSection } from '@repo/core/pdf/sections'
12
- *
13
- * const page = acceptanceSection({
14
- * companyName: 'Acme Corp',
15
- * contactName: 'John Smith',
16
- * tier: 'tier2',
17
- * initialFee: 5000,
18
- * monthlyFee: 2500 // optional
19
- * })
20
- * ```
21
- */
22
-
23
- import type { Page } from '../types'
24
-
25
- /**
26
- * Configuration for acceptance section
27
- */
28
- export interface AcceptanceSectionConfig {
29
- /** Company name for personalization */
30
- companyName: string
31
- /** Contact name for personalization */
32
- contactName: string
33
- /** Service tier being proposed */
34
- tier: 'tier1' | 'tier2' | 'tier3' | 'custom'
35
- /** Initial fee amount in dollars (one-time implementation fee) */
36
- initialFee: number
37
- /** Monthly fee amount in dollars (optional, for ongoing support) */
38
- monthlyFee?: number
39
- }
40
-
41
- /**
42
- * Build acceptance page with simplified terms and signature area
43
- *
44
- * Creates a dedicated page at the end of the proposal with:
45
- * - Pricing summary
46
- * - Key terms (audit timeline, training, support, guarantee)
47
- * - Acceptance statement
48
- * - Space for electronic signature fields (added by SignatureAPI)
49
- *
50
- * @param config - Acceptance section configuration
51
- * @returns Page structure for terms and acceptance
52
- */
53
- export function acceptanceSection(config: AcceptanceSectionConfig): Page {
54
- const { companyName, contactName, tier, initialFee, monthlyFee } = config
55
-
56
- const tierDescriptions = {
57
- tier1: 'Standard Implementation',
58
- tier2: 'Professional Implementation',
59
- tier3: 'Enterprise Implementation',
60
- custom: 'Custom Implementation'
61
- }
62
-
63
- // Build simplified terms list
64
- const terms: string[] = [
65
- `Initial fee: $${initialFee.toLocaleString()} (${tierDescriptions[tier]})`,
66
- ...(monthlyFee ? [`Monthly fee: $${monthlyFee.toLocaleString()}/month`] : []),
67
- 'Detailed audit begins within 5 business days of payment',
68
- 'Training materials provided',
69
- ...(monthlyFee ? ['Ongoing maintenance, support, and optimization included'] : []),
70
- 'Payment link provided via email after signing',
71
- '60-day money-back guarantee'
72
- ]
73
-
74
- return {
75
- header: { title: 'Agreement & Terms', variant: 'accent' },
76
- footer: true,
77
- sections: [
78
- {
79
- type: 'card',
80
- content: [
81
- {
82
- type: 'list',
83
- ordered: false,
84
- items: terms
85
- }
86
- ]
87
- },
88
- {
89
- type: 'text',
90
- content: `By signing below, ${contactName} of ${companyName} authorizes Elevasis to proceed.`,
91
- variant: 'body'
92
- },
93
- {
94
- type: 'card',
95
- title: 'Signature',
96
- variant: 'light',
97
- content: [
98
- {
99
- type: 'text',
100
- content: 'Electronic signature will be captured below via SignatureAPI',
101
- variant: 'caption'
102
- },
103
- {
104
- type: 'text',
105
- content: '\n\n\n\n\n',
106
- variant: 'body'
107
- }
108
- ]
109
- }
110
- ]
111
- }
112
- }
1
+ /**
2
+ * Acceptance Section Builder
3
+ *
4
+ * Creates a dedicated signature/acceptance page for proposals.
5
+ * This page contains minimal terms and signature fields placement area.
6
+ *
7
+ * Pattern: Content-only configuration, styling handled by theme system.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { acceptanceSection } from '@repo/core/pdf/sections'
12
+ *
13
+ * const page = acceptanceSection({
14
+ * companyName: 'Acme Corp',
15
+ * contactName: 'John Smith',
16
+ * tier: 'tier2',
17
+ * initialFee: 5000,
18
+ * monthlyFee: 2500 // optional
19
+ * })
20
+ * ```
21
+ */
22
+
23
+ import type { Page } from '../types'
24
+
25
+ /**
26
+ * Configuration for acceptance section
27
+ */
28
+ export interface AcceptanceSectionConfig {
29
+ /** Company name for personalization */
30
+ companyName: string
31
+ /** Contact name for personalization */
32
+ contactName: string
33
+ /** Service tier being proposed */
34
+ tier: 'tier1' | 'tier2' | 'tier3' | 'custom'
35
+ /** Initial fee amount in dollars (one-time implementation fee) */
36
+ initialFee: number
37
+ /** Monthly fee amount in dollars (optional, for ongoing support) */
38
+ monthlyFee?: number
39
+ /** Name of the service provider issuing the proposal (e.g. "Elevasis") */
40
+ providerName: string
41
+ }
42
+
43
+ /**
44
+ * Build acceptance page with simplified terms and signature area
45
+ *
46
+ * Creates a dedicated page at the end of the proposal with:
47
+ * - Pricing summary
48
+ * - Key terms (audit timeline, training, support, guarantee)
49
+ * - Acceptance statement
50
+ * - Space for electronic signature fields (added by SignatureAPI)
51
+ *
52
+ * @param config - Acceptance section configuration
53
+ * @returns Page structure for terms and acceptance
54
+ */
55
+ export function acceptanceSection(config: AcceptanceSectionConfig): Page {
56
+ const { companyName, contactName, tier, initialFee, monthlyFee, providerName } = config
57
+
58
+ const tierDescriptions = {
59
+ tier1: 'Standard Implementation',
60
+ tier2: 'Professional Implementation',
61
+ tier3: 'Enterprise Implementation',
62
+ custom: 'Custom Implementation'
63
+ }
64
+
65
+ // Build simplified terms list
66
+ const terms: string[] = [
67
+ `Initial fee: $${initialFee.toLocaleString()} (${tierDescriptions[tier]})`,
68
+ ...(monthlyFee ? [`Monthly fee: $${monthlyFee.toLocaleString()}/month`] : []),
69
+ 'Detailed audit begins within 5 business days of payment',
70
+ 'Training materials provided',
71
+ ...(monthlyFee ? ['Ongoing maintenance, support, and optimization included'] : []),
72
+ 'Payment link provided via email after signing',
73
+ '60-day money-back guarantee'
74
+ ]
75
+
76
+ return {
77
+ header: { title: 'Agreement & Terms', variant: 'accent' },
78
+ footer: true,
79
+ sections: [
80
+ {
81
+ type: 'card',
82
+ content: [
83
+ {
84
+ type: 'list',
85
+ ordered: false,
86
+ items: terms
87
+ }
88
+ ]
89
+ },
90
+ {
91
+ type: 'text',
92
+ content: `By signing below, ${contactName} of ${companyName} authorizes ${providerName} to proceed.`,
93
+ variant: 'body'
94
+ },
95
+ {
96
+ type: 'card',
97
+ title: 'Signature',
98
+ variant: 'light',
99
+ content: [
100
+ {
101
+ type: 'text',
102
+ content: 'Electronic signature will be captured below via SignatureAPI',
103
+ variant: 'caption'
104
+ },
105
+ {
106
+ type: 'text',
107
+ content: '\n\n\n\n\n',
108
+ variant: 'body'
109
+ }
110
+ ]
111
+ }
112
+ ]
113
+ }
114
+ }