@elevasis/core 0.19.0 → 0.21.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 (40) hide show
  1. package/dist/index.d.ts +108 -0
  2. package/dist/index.js +239 -27
  3. package/dist/knowledge/index.d.ts +55 -1
  4. package/dist/organization-model/index.d.ts +108 -0
  5. package/dist/organization-model/index.js +239 -27
  6. package/dist/test-utils/index.d.ts +54 -0
  7. package/dist/test-utils/index.js +238 -27
  8. package/package.json +1 -1
  9. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +17 -5
  10. package/src/business/acquisition/api-schemas.test.ts +125 -14
  11. package/src/business/acquisition/api-schemas.ts +161 -11
  12. package/src/business/acquisition/build-templates.test.ts +28 -0
  13. package/src/business/acquisition/build-templates.ts +20 -8
  14. package/src/business/acquisition/derive-actions.test.ts +1 -1
  15. package/src/business/acquisition/types.ts +7 -2
  16. package/src/business/deals/api-schemas.ts +2 -2
  17. package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.test.ts +55 -0
  18. package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.ts +107 -41
  19. package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.test.ts +48 -0
  20. package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.ts +99 -0
  21. package/src/execution/engine/tools/integration/server/adapters/apollo/index.ts +1 -0
  22. package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.test.ts +18 -0
  23. package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.ts +194 -0
  24. package/src/execution/engine/tools/integration/server/adapters/clickup/index.ts +7 -0
  25. package/src/integrations/credentials/api-schemas.ts +21 -2
  26. package/src/integrations/credentials/schemas.ts +200 -164
  27. package/src/organization-model/__tests__/graph.test.ts +108 -2
  28. package/src/organization-model/__tests__/prospecting-ssot.test.ts +12 -12
  29. package/src/organization-model/__tests__/schema.test.ts +122 -0
  30. package/src/organization-model/__tests__/surface-projection.test.ts +174 -0
  31. package/src/organization-model/domains/prospecting.ts +273 -41
  32. package/src/organization-model/domains/sales.ts +32 -8
  33. package/src/organization-model/graph/build.ts +74 -0
  34. package/src/organization-model/graph/schema.ts +1 -0
  35. package/src/organization-model/graph/types.ts +1 -0
  36. package/src/organization-model/schema.ts +63 -0
  37. package/src/organization-model/surface-projection.ts +218 -0
  38. package/src/platform/constants/versions.ts +1 -1
  39. package/src/reference/_generated/contracts.md +17 -5
  40. package/src/server.ts +2 -0
@@ -14,6 +14,20 @@ function makeFeature(id: string, path = `/${id.replaceAll('.', '/')}`) {
14
14
  }
15
15
  }
16
16
 
17
+ function makeSurface(id: string, featureId?: string, featureIds: string[] = []) {
18
+ return {
19
+ id,
20
+ label: id,
21
+ path: `/${id.replaceAll('.', '/')}`,
22
+ surfaceType: 'page' as const,
23
+ featureId,
24
+ featureIds,
25
+ entityIds: [],
26
+ resourceIds: [],
27
+ capabilityIds: []
28
+ }
29
+ }
30
+
17
31
  function makeMinimalModel(features: unknown[] = []) {
18
32
  return {
19
33
  version: 1 as const,
@@ -48,6 +62,22 @@ describe('flat feature tree validation', () => {
48
62
  expect(messages.some((message) => message.includes('Feature id "sales" must be unique'))).toBe(true)
49
63
  })
50
64
 
65
+ it('rejects duplicate effective feature paths when explicit and default paths collide', () => {
66
+ const messages = getIssueMessages(
67
+ makeMinimalModel([
68
+ { id: 'sales', label: 'Sales', enabled: true },
69
+ makeFeature('sales.crm', '/sales/lead-gen'),
70
+ { id: 'sales.lead-gen', label: 'Lead Gen', enabled: true }
71
+ ])
72
+ )
73
+
74
+ expect(
75
+ messages.some((message) =>
76
+ message.includes('Feature "sales.lead-gen" effective path "/sales/lead-gen" duplicates feature "sales.crm"')
77
+ )
78
+ ).toBe(true)
79
+ })
80
+
51
81
  it('rejects a dotted child when its immediate parent is missing', () => {
52
82
  const messages = getIssueMessages(makeMinimalModel([makeFeature('sales.crm.pipeline')]))
53
83
 
@@ -80,4 +110,96 @@ describe('flat feature tree validation', () => {
80
110
  expect(model.navigation.groups).toEqual([])
81
111
  expect('resourceMappings' in model).toBe(false)
82
112
  })
113
+
114
+ it('rejects an unknown navigation default surface when surfaces are explicitly empty', () => {
115
+ const messages = getIssueMessages({
116
+ ...makeMinimalModel([makeFeature('dashboard', '/')]),
117
+ navigation: {
118
+ defaultSurfaceId: 'missing.surface',
119
+ surfaces: [],
120
+ groups: []
121
+ }
122
+ })
123
+
124
+ expect(
125
+ messages.some((message) =>
126
+ message.includes('Navigation defaultSurfaceId references unknown surface "missing.surface"')
127
+ )
128
+ ).toBe(true)
129
+ })
130
+
131
+ it('rejects navigation group surfaceIds that reference missing surfaces', () => {
132
+ const messages = getIssueMessages({
133
+ ...makeMinimalModel([makeFeature('dashboard', '/')]),
134
+ navigation: {
135
+ surfaces: [makeSurface('dashboard.home', 'dashboard')],
136
+ groups: [
137
+ {
138
+ id: 'primary',
139
+ label: 'Primary',
140
+ placement: 'primary',
141
+ surfaceIds: ['missing.surface']
142
+ }
143
+ ]
144
+ }
145
+ })
146
+
147
+ expect(
148
+ messages.some((message) =>
149
+ message.includes('Navigation group "primary" references unknown surface "missing.surface"')
150
+ )
151
+ ).toBe(true)
152
+ })
153
+
154
+ it('rejects navigation surface featureId references to missing features', () => {
155
+ const messages = getIssueMessages({
156
+ ...makeMinimalModel([makeFeature('dashboard', '/')]),
157
+ navigation: {
158
+ surfaces: [makeSurface('dashboard.home', 'missing.feature')],
159
+ groups: []
160
+ }
161
+ })
162
+
163
+ expect(
164
+ messages.some((message) =>
165
+ message.includes('Navigation surface "dashboard.home" references unknown feature "missing.feature"')
166
+ )
167
+ ).toBe(true)
168
+ })
169
+
170
+ it('rejects navigation surface featureIds references to missing features', () => {
171
+ const messages = getIssueMessages({
172
+ ...makeMinimalModel([makeFeature('dashboard', '/')]),
173
+ navigation: {
174
+ surfaces: [makeSurface('dashboard.home', 'dashboard', ['dashboard', 'missing.feature'])],
175
+ groups: []
176
+ }
177
+ })
178
+
179
+ expect(
180
+ messages.some((message) =>
181
+ message.includes('Navigation surface "dashboard.home" references unknown feature "missing.feature"')
182
+ )
183
+ ).toBe(true)
184
+ })
185
+
186
+ it('allows legacy navigation feature aliases when canonical features exist', () => {
187
+ const result = OrganizationModelSchema.safeParse({
188
+ ...makeMinimalModel([
189
+ { id: 'sales', label: 'Sales', enabled: true },
190
+ makeFeature('sales.crm', '/sales/crm/pipeline'),
191
+ makeFeature('sales.lead-gen', '/lead-gen/lists')
192
+ ]),
193
+ navigation: {
194
+ defaultSurfaceId: 'crm.pipeline',
195
+ surfaces: [
196
+ makeSurface('crm.pipeline', 'crm', ['crm']),
197
+ makeSurface('lead-gen.lists', 'lead-gen', ['lead-gen'])
198
+ ],
199
+ groups: []
200
+ }
201
+ })
202
+
203
+ expect(result.success).toBe(true)
204
+ })
83
205
  })
@@ -0,0 +1,174 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { DEFAULT_ORGANIZATION_MODEL } from '../defaults'
3
+ import {
4
+ projectOrganizationSurfaces,
5
+ validateOrganizationSurfaceProjection
6
+ } from '../surface-projection'
7
+ import type { OrganizationModel, OrganizationModelFeature, OrganizationModelSurface } from '../types'
8
+
9
+ function makeFeature(
10
+ id: string,
11
+ overrides: Partial<OrganizationModelFeature> = {}
12
+ ): OrganizationModelFeature {
13
+ return {
14
+ id,
15
+ label: id,
16
+ enabled: true,
17
+ ...overrides
18
+ }
19
+ }
20
+
21
+ function makeSurface(
22
+ id: string,
23
+ overrides: Partial<OrganizationModelSurface> = {}
24
+ ): OrganizationModelSurface {
25
+ return {
26
+ id,
27
+ label: id,
28
+ path: `/${id.replaceAll('.', '/')}`,
29
+ surfaceType: 'page',
30
+ enabled: true,
31
+ featureIds: [],
32
+ entityIds: [],
33
+ resourceIds: [],
34
+ capabilityIds: [],
35
+ ...overrides
36
+ }
37
+ }
38
+
39
+ function makeModel(
40
+ features: OrganizationModelFeature[],
41
+ surfaces: OrganizationModelSurface[],
42
+ navigation: Partial<OrganizationModel['navigation']> = {}
43
+ ): OrganizationModel {
44
+ return {
45
+ ...DEFAULT_ORGANIZATION_MODEL,
46
+ features,
47
+ navigation: {
48
+ surfaces,
49
+ groups: [],
50
+ ...navigation
51
+ }
52
+ }
53
+ }
54
+
55
+ describe('projectOrganizationSurfaces', () => {
56
+ it('projects navigation surfaces into data-only DTOs with inherited flags', () => {
57
+ const model = makeModel(
58
+ [
59
+ makeFeature('sales', { requiresAdmin: true }),
60
+ makeFeature('sales.crm', { devOnly: true })
61
+ ],
62
+ [
63
+ makeSurface('crm.pipeline', {
64
+ label: 'Pipeline',
65
+ path: '/crm/pipeline',
66
+ surfaceType: 'graph',
67
+ featureId: 'sales.crm',
68
+ featureIds: ['sales.crm'],
69
+ entityIds: ['crm.deal'],
70
+ resourceIds: ['workflow.crm-sync'],
71
+ capabilityIds: ['crm.pipeline.manage']
72
+ })
73
+ ]
74
+ )
75
+
76
+ expect(projectOrganizationSurfaces(model)).toEqual([
77
+ {
78
+ id: 'crm.pipeline',
79
+ label: 'Pipeline',
80
+ path: '/crm/pipeline',
81
+ surfaceType: 'graph',
82
+ featureId: 'sales.crm',
83
+ featureIds: ['sales.crm'],
84
+ entityIds: ['crm.deal'],
85
+ resourceIds: ['workflow.crm-sync'],
86
+ capabilityIds: ['crm.pipeline.manage'],
87
+ enabled: true,
88
+ devOnly: true,
89
+ requiresAdmin: true
90
+ }
91
+ ])
92
+ })
93
+
94
+ it('canonicalizes legacy feature aliases', () => {
95
+ const model = makeModel(
96
+ [
97
+ makeFeature('sales'),
98
+ makeFeature('sales.crm'),
99
+ makeFeature('sales.lead-gen'),
100
+ makeFeature('monitoring'),
101
+ makeFeature('monitoring.submitted-requests')
102
+ ],
103
+ [
104
+ makeSurface('legacy.sales', {
105
+ featureId: 'crm',
106
+ featureIds: ['crm', 'lead-gen', 'submitted-requests']
107
+ })
108
+ ]
109
+ )
110
+
111
+ expect(projectOrganizationSurfaces(model)[0]).toMatchObject({
112
+ featureId: 'sales.crm',
113
+ featureIds: ['sales.crm', 'sales.lead-gen', 'monitoring.submitted-requests']
114
+ })
115
+ })
116
+
117
+ it('disables projected surfaces when feature lineage is disabled', () => {
118
+ const model = makeModel(
119
+ [
120
+ makeFeature('sales', { enabled: false }),
121
+ makeFeature('sales.crm')
122
+ ],
123
+ [
124
+ makeSurface('crm.pipeline', {
125
+ enabled: true,
126
+ featureId: 'sales.crm',
127
+ featureIds: ['sales.crm']
128
+ })
129
+ ]
130
+ )
131
+
132
+ expect(projectOrganizationSurfaces(model)[0].enabled).toBe(false)
133
+ })
134
+ })
135
+
136
+ describe('validateOrganizationSurfaceProjection', () => {
137
+ it('returns issue codes for duplicate and unknown surface references', () => {
138
+ const model = makeModel(
139
+ [makeFeature('sales'), makeFeature('sales.crm')],
140
+ [
141
+ makeSurface('crm.pipeline', {
142
+ path: '/crm/pipeline',
143
+ featureId: 'sales.crm',
144
+ featureIds: ['sales.crm']
145
+ }),
146
+ makeSurface('crm.pipeline', {
147
+ path: '/crm/pipeline/',
148
+ featureId: 'missing.primary',
149
+ featureIds: ['missing.related']
150
+ })
151
+ ],
152
+ {
153
+ defaultSurfaceId: 'missing.default',
154
+ groups: [
155
+ {
156
+ id: 'primary',
157
+ label: 'Primary',
158
+ placement: 'primary',
159
+ surfaceIds: ['missing.group']
160
+ }
161
+ ]
162
+ }
163
+ )
164
+
165
+ expect(validateOrganizationSurfaceProjection(model).map((issue) => issue.code)).toEqual([
166
+ 'duplicate-surface-id',
167
+ 'duplicate-surface-path',
168
+ 'unknown-surface-feature',
169
+ 'unknown-surface-feature-reference',
170
+ 'unknown-default-surface',
171
+ 'unknown-group-surface'
172
+ ])
173
+ })
174
+ })