@elevasis/core 0.27.0 → 0.28.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.
@@ -0,0 +1,289 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { z } from 'zod'
3
+ import { defineDomainRecord } from '../helpers'
4
+ import { defineAction, defineActions, ActionSchema } from '../domains/actions'
5
+ import { defineEntity, defineEntities, EntitySchema } from '../domains/entities'
6
+ import { defineSystem, defineSystems, SystemEntrySchema } from '../domains/systems'
7
+ import { definePolicy, definePolicies, PolicySchema } from '../domains/policies'
8
+ import { defineRole, defineRoles, RoleSchema } from '../domains/roles'
9
+ import { defineGoal, defineGoals, ObjectiveSchema } from '../domains/goals'
10
+ import { defineCustomer, defineCustomers, CustomerSegmentSchema } from '../domains/customers'
11
+ import { defineOffering, defineOfferings, ProductSchema } from '../domains/offerings'
12
+ import { defineStatus, defineStatuses, StatusEntrySchema } from '../domains/statuses'
13
+ import { defineKnowledgeNode, defineKnowledgeNodes, OrgKnowledgeNodeSchema } from '../domains/knowledge'
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Minimal valid fixtures for each domain
17
+ // ---------------------------------------------------------------------------
18
+
19
+ const validAction: z.input<typeof ActionSchema> = {
20
+ id: 'test-action',
21
+ order: 10,
22
+ label: 'Test Action',
23
+ invocations: []
24
+ }
25
+
26
+ const validEntity: z.input<typeof EntitySchema> = {
27
+ id: 'test.entity',
28
+ order: 10,
29
+ label: 'Test Entity',
30
+ ownedBySystemId: 'test-system'
31
+ }
32
+
33
+ const validSystem: z.input<typeof SystemEntrySchema> = {
34
+ id: 'test-system',
35
+ order: 10,
36
+ label: 'Test System'
37
+ }
38
+
39
+ const validPolicy: z.input<typeof PolicySchema> = {
40
+ id: 'test-policy',
41
+ order: 10,
42
+ label: 'Test Policy',
43
+ trigger: { kind: 'manual' },
44
+ actions: [{ kind: 'block' }]
45
+ }
46
+
47
+ const validRole: z.input<typeof RoleSchema> = {
48
+ id: 'test-role',
49
+ order: 10,
50
+ title: 'Test Role'
51
+ }
52
+
53
+ const validGoal: z.input<typeof ObjectiveSchema> = {
54
+ id: 'test-goal',
55
+ order: 10,
56
+ description: 'Grow ARR by 2x',
57
+ periodStart: '2026-01-01',
58
+ periodEnd: '2026-12-31'
59
+ }
60
+
61
+ const validCustomer: z.input<typeof CustomerSegmentSchema> = {
62
+ id: 'test-segment',
63
+ order: 10,
64
+ name: 'SMB Agencies'
65
+ }
66
+
67
+ const validOffering: z.input<typeof ProductSchema> = {
68
+ id: 'test-product',
69
+ order: 10,
70
+ name: 'Starter Plan'
71
+ }
72
+
73
+ const validStatus: z.input<typeof StatusEntrySchema> = {
74
+ id: 'test.status',
75
+ order: 10,
76
+ label: 'Test Status',
77
+ semanticClass: 'delivery.task'
78
+ }
79
+
80
+ const validKnowledgeNode: z.input<typeof OrgKnowledgeNodeSchema> = {
81
+ id: 'test-knowledge',
82
+ kind: 'reference',
83
+ title: 'Test Knowledge',
84
+ summary: 'A test knowledge node.',
85
+ body: 'Full body content here.',
86
+ updatedAt: '2026-01-01'
87
+ }
88
+
89
+ // ---------------------------------------------------------------------------
90
+ // Generic defineDomainRecord
91
+ // ---------------------------------------------------------------------------
92
+
93
+ describe('defineDomainRecord', () => {
94
+ it('produces an id-keyed map for valid entries', () => {
95
+ const result = defineDomainRecord(ActionSchema, [validAction])
96
+ expect(result).toHaveProperty('test-action')
97
+ expect(result['test-action'].id).toBe('test-action')
98
+ expect(result['test-action'].label).toBe('Test Action')
99
+ })
100
+
101
+ it('produces a map keyed by each entry id when given multiple entries', () => {
102
+ const second: z.input<typeof ActionSchema> = { ...validAction, id: 'second-action', label: 'Second' }
103
+ const result = defineDomainRecord(ActionSchema, [validAction, second])
104
+ expect(Object.keys(result)).toEqual(['test-action', 'second-action'])
105
+ })
106
+
107
+ it('returns an empty map for an empty entries array', () => {
108
+ const result = defineDomainRecord(ActionSchema, [])
109
+ expect(result).toEqual({})
110
+ })
111
+
112
+ it('throws ZodError when an entry fails schema validation', () => {
113
+ const bad = { id: 'bad', order: 'not-a-number', label: 'Bad' } as unknown as z.input<typeof ActionSchema>
114
+ expect(() => defineDomainRecord(ActionSchema, [bad])).toThrow(z.ZodError)
115
+ })
116
+
117
+ it('applies schema defaults (invocations defaults to [])', () => {
118
+ const minimal = { id: 'minimal-action', order: 10, label: 'Minimal' } as z.input<typeof ActionSchema>
119
+ const result = defineDomainRecord(ActionSchema, [minimal])
120
+ expect(result['minimal-action'].invocations).toEqual([])
121
+ })
122
+ })
123
+
124
+ // ---------------------------------------------------------------------------
125
+ // Parameterized per-domain tests
126
+ // ---------------------------------------------------------------------------
127
+
128
+ describe.each([
129
+ {
130
+ domain: 'actions',
131
+ defineSingle: defineAction,
132
+ defineMultiple: defineActions,
133
+ valid: validAction,
134
+ schema: ActionSchema,
135
+ idField: 'id' as const,
136
+ invalidOverride: { order: 'not-a-number' }
137
+ },
138
+ {
139
+ domain: 'entities',
140
+ defineSingle: defineEntity,
141
+ defineMultiple: defineEntities,
142
+ valid: validEntity,
143
+ schema: EntitySchema,
144
+ idField: 'id' as const,
145
+ invalidOverride: { order: 'not-a-number' }
146
+ },
147
+ {
148
+ domain: 'systems',
149
+ defineSingle: defineSystem,
150
+ defineMultiple: defineSystems,
151
+ valid: validSystem,
152
+ schema: SystemEntrySchema,
153
+ idField: 'id' as const,
154
+ invalidOverride: { order: 'not-a-number' }
155
+ },
156
+ {
157
+ domain: 'policies',
158
+ defineSingle: definePolicy,
159
+ defineMultiple: definePolicies,
160
+ valid: validPolicy,
161
+ schema: PolicySchema,
162
+ idField: 'id' as const,
163
+ invalidOverride: { order: 'not-a-number' }
164
+ },
165
+ {
166
+ domain: 'roles',
167
+ defineSingle: defineRole,
168
+ defineMultiple: defineRoles,
169
+ valid: validRole,
170
+ schema: RoleSchema,
171
+ idField: 'id' as const,
172
+ invalidOverride: { order: 'not-a-number' }
173
+ },
174
+ {
175
+ domain: 'goals',
176
+ defineSingle: defineGoal,
177
+ defineMultiple: defineGoals,
178
+ valid: validGoal,
179
+ schema: ObjectiveSchema,
180
+ idField: 'id' as const,
181
+ invalidOverride: { order: 'not-a-number' }
182
+ },
183
+ {
184
+ domain: 'customers',
185
+ defineSingle: defineCustomer,
186
+ defineMultiple: defineCustomers,
187
+ valid: validCustomer,
188
+ schema: CustomerSegmentSchema,
189
+ idField: 'id' as const,
190
+ invalidOverride: { order: 'not-a-number' }
191
+ },
192
+ {
193
+ domain: 'offerings',
194
+ defineSingle: defineOffering,
195
+ defineMultiple: defineOfferings,
196
+ valid: validOffering,
197
+ schema: ProductSchema,
198
+ idField: 'id' as const,
199
+ invalidOverride: { order: 'not-a-number' }
200
+ },
201
+ {
202
+ domain: 'statuses',
203
+ defineSingle: defineStatus,
204
+ defineMultiple: defineStatuses,
205
+ valid: validStatus,
206
+ schema: StatusEntrySchema,
207
+ idField: 'id' as const,
208
+ invalidOverride: { order: 'not-a-number' }
209
+ },
210
+ {
211
+ domain: 'knowledge',
212
+ defineSingle: defineKnowledgeNode,
213
+ defineMultiple: defineKnowledgeNodes,
214
+ valid: validKnowledgeNode,
215
+ schema: OrgKnowledgeNodeSchema,
216
+ idField: 'id' as const,
217
+ invalidOverride: { kind: 'invalid-kind' }
218
+ }
219
+ ])(
220
+ 'define$domain — $domain',
221
+ ({ domain: _domain, defineSingle, defineMultiple, valid, schema, idField, invalidOverride }) => {
222
+ it('defineX — validates and returns a parsed entry', () => {
223
+ const result = defineSingle(valid as never)
224
+ expect(result).toHaveProperty(idField)
225
+ expect((result as Record<string, unknown>)[idField]).toBe((valid as Record<string, unknown>)[idField])
226
+ })
227
+
228
+ it('defineX — throws ZodError on invalid input', () => {
229
+ const bad = { ...valid, ...invalidOverride } as never
230
+ expect(() => defineSingle(bad)).toThrow(z.ZodError)
231
+ })
232
+
233
+ it('defineXs — produces an id-keyed map', () => {
234
+ const result = defineMultiple([valid] as never[])
235
+ const id = (valid as Record<string, unknown>)[idField] as string
236
+ expect(result).toHaveProperty(id)
237
+ expect((result[id] as Record<string, unknown>)[idField]).toBe(id)
238
+ })
239
+
240
+ it('defineXs — throws ZodError on invalid input', () => {
241
+ const bad = { ...valid, ...invalidOverride } as never
242
+ expect(() => defineMultiple([bad] as never[])).toThrow(z.ZodError)
243
+ })
244
+
245
+ it('defineXs — returns empty map for empty array', () => {
246
+ const result = defineMultiple([] as never[])
247
+ expect(result).toEqual({})
248
+ })
249
+
250
+ it('defineXs — validates through schema (no bypass)', () => {
251
+ // Confirm that defineMultiple uses schema.parse, not a pass-through.
252
+ // Missing required fields should throw even if the object looks similar.
253
+ const missingRequired = { [idField]: (valid as Record<string, unknown>)[idField] } as never
254
+ expect(() => defineMultiple([missingRequired] as never[])).toThrow(z.ZodError)
255
+ })
256
+ }
257
+ )
258
+
259
+ // ---------------------------------------------------------------------------
260
+ // Topology — existing helpers (defineTopology, defineTopologyRelationship)
261
+ // are already tested elsewhere; verified here that they exist and are callable.
262
+ // ---------------------------------------------------------------------------
263
+
264
+ describe('topology — existing helpers', () => {
265
+ it('defineTopologyRelationship is importable from domains/topology', async () => {
266
+ const { defineTopologyRelationship, topologyRef } = await import('../domains/topology')
267
+ const rel = defineTopologyRelationship({
268
+ from: topologyRef.system('sales'),
269
+ kind: 'uses',
270
+ to: topologyRef.resource('lead-gen.company.qualify')
271
+ })
272
+ expect(rel.kind).toBe('uses')
273
+ expect(rel.from.kind).toBe('system')
274
+ expect(rel.to.kind).toBe('resource')
275
+ })
276
+
277
+ it('defineTopology is importable and produces a domain object', async () => {
278
+ const { defineTopology, topologyRef } = await import('../domains/topology')
279
+ const domain = defineTopology({
280
+ 'rel-1': {
281
+ from: topologyRef.system('sales'),
282
+ kind: 'triggers',
283
+ to: topologyRef.resource('lead-gen.company.qualify')
284
+ }
285
+ })
286
+ expect(domain.version).toBe(1)
287
+ expect(domain.relationships).toHaveProperty('rel-1')
288
+ })
289
+ })
@@ -0,0 +1,56 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import { fileURLToPath } from 'node:url'
3
+ import { describe, expect, it } from 'vitest'
4
+ import { ACTION_REGISTRY } from '../domains/prospecting'
5
+ import { getLeadGenStageCatalog } from '../migration-helpers'
6
+
7
+ /**
8
+ * OM Spine doc <-> code contract guard.
9
+ *
10
+ * `scripts/monorepo/meta-verify.mjs` only string-greps the primer for symbol
11
+ * names, so it cannot notice when a documented "constant" has become a
12
+ * type-only export or an empty stub. This drift actually shipped (the Wave-2
13
+ * generic/canonical OM split) and went uncaught for that reason. These
14
+ * assertions tie the primer text to the real code shape so the same class of
15
+ * drift fails the unit suite. Pure file reads + pure imports — CI-safe.
16
+ */
17
+
18
+ const repoRoot = fileURLToPath(new URL('../../../../../', import.meta.url))
19
+ const catalogTypeSrc = fileURLToPath(new URL('../catalogs/lead-gen.ts', import.meta.url))
20
+
21
+ const PRIMER_PATHS = [
22
+ 'apps/docs/content/docs/technical/development/design/om-spine.mdx',
23
+ '.claude/skills/org-os/operations/spine.md'
24
+ ]
25
+
26
+ const read = (p: string) => readFileSync(p, 'utf8')
27
+
28
+ describe('OM Spine doc/code contract', () => {
29
+ it('keeps the generic core action registry an empty stub (bindings live in @repo/elevasis-core)', () => {
30
+ expect(ACTION_REGISTRY).toEqual([])
31
+ })
32
+
33
+ it('exposes the lead-gen catalog only as a reader function, not a hand-authored constant', () => {
34
+ expect(typeof getLeadGenStageCatalog).toBe('function')
35
+
36
+ // catalogs/lead-gen.ts must remain type-only — no LEAD_GEN_STAGE_CATALOG value export.
37
+ const src = read(catalogTypeSrc)
38
+ expect(src).not.toMatch(/export\s+const\s+LEAD_GEN_STAGE_CATALOG\b/)
39
+ expect(src).toMatch(/export\s+interface\s+LeadGenStageCatalogEntry\b/)
40
+ })
41
+
42
+ it.each(PRIMER_PATHS)('primer %s describes the post-split reality, not the retired constant', (relPath) => {
43
+ const text = read(repoRoot + relPath)
44
+
45
+ // Negative: the exact stale claims that shipped before this fix must not return.
46
+ expect(text).not.toMatch(
47
+ /`LEAD_GEN_STAGE_CATALOG`\s+in\s+`packages\/core\/src\/organization-model\/catalogs\/lead-gen\.ts`/
48
+ )
49
+ expect(text).not.toMatch(/LEAD_GEN_STAGE_CATALOG \(vocabulary, catalogs\/lead-gen\.ts\)/)
50
+
51
+ // Positive: each primer must point at the real reader + canonical owners.
52
+ expect(text).toContain('getLeadGenStageCatalog')
53
+ expect(text).toContain('LEAD_GEN_ACTION_ENTRIES')
54
+ expect(text).toContain('@repo/elevasis-core')
55
+ })
56
+ })
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod'
2
2
  import { EntityIdSchema } from './entities'
3
3
  import { DescriptionSchema, LabelSchema, ModelIdSchema } from './shared'
4
+ import { defineDomainRecord } from '../helpers'
4
5
 
5
6
  export const ActionResourceIdSchema = z
6
7
  .string()
@@ -107,6 +108,18 @@ export function findOrganizationActionById(
107
108
  return actions[id]
108
109
  }
109
110
 
111
+ /** Validate and return a single action entry. */
112
+ export function defineAction(entry: z.input<typeof ActionSchema>): z.infer<typeof ActionSchema> {
113
+ return ActionSchema.parse(entry)
114
+ }
115
+
116
+ /** Validate and return an id-keyed map of action entries. */
117
+ export function defineActions(
118
+ entries: readonly z.input<typeof ActionSchema>[]
119
+ ): Record<string, z.infer<typeof ActionSchema>> {
120
+ return defineDomainRecord(ActionSchema, entries)
121
+ }
122
+
110
123
  export type ActionId = z.infer<typeof ActionIdSchema>
111
124
  export type ActionScope = z.infer<typeof ActionScopeSchema>
112
125
  export type ActionRef = z.infer<typeof ActionRefSchema>
@@ -1,78 +1,95 @@
1
- import { z } from 'zod'
2
-
3
- // ---------------------------------------------------------------------------
4
- // Firmographics — optional demographic/firmographic filters that describe the
5
- // target customer segment's organizational profile. All fields are optional so
6
- // a segment can declare only the axes relevant to targeting.
7
- // ---------------------------------------------------------------------------
8
-
9
- export const FirmographicsSchema = z.object({
10
- /** Industry vertical (e.g. "Marketing Agency", "Legal", "Real Estate"). */
11
- industry: z.string().trim().max(200).optional(),
12
- /**
13
- * Company headcount band (e.g. "1–10", "11–50", "51–200", "200+").
14
- * Free-form string to accommodate any band notation.
15
- */
16
- companySize: z.string().trim().max(100).optional(),
17
- /**
18
- * Primary geographic region the segment operates in or is targeted from
19
- * (e.g. "North America", "Europe", "Global").
20
- */
21
- region: z.string().trim().max(200).optional()
22
- })
23
-
24
- // ---------------------------------------------------------------------------
25
- // Customer segment schema — one entry per distinct buyer archetype.
26
- // Modeled after Value Proposition Canvas (BMC / VPC) and Business Model Canvas
27
- // customer-segment language. Fields use plain English throughout.
28
- // ---------------------------------------------------------------------------
29
-
30
- export const CustomerSegmentSchema = z.object({
31
- /** Stable unique identifier for the segment (e.g. "segment-smb-agencies"). */
32
- id: z.string().trim().min(1).max(100),
33
- /** Domain-map iteration order. Convention: multiples of 10 (10, 20, 30, ...) to allow easy insertion. */
34
- order: z.number(),
35
- /** Human-readable name shown to agents and in UI (e.g. "SMB Marketing Agencies"). */
36
- name: z.string().trim().max(200).default(''),
37
- /** One or two sentences describing who this segment is. */
38
- description: z.string().trim().max(2000).default(''),
39
- /**
40
- * The primary job(s) this segment is trying to get done — the goal they hire
41
- * a product/service to accomplish. Plain-language narrative or bullet list.
42
- */
43
- jobsToBeDone: z.string().trim().max(2000).default(''),
44
- /**
45
- * Pains — frustrations, obstacles, and risks the segment experiences
46
- * when trying to accomplish their jobs-to-be-done.
47
- */
48
- pains: z.array(z.string().trim().max(500)).default([]),
49
- /**
50
- * Gains — outcomes and benefits the segment desires; positive motivators
51
- * beyond merely resolving pains.
52
- */
53
- gains: z.array(z.string().trim().max(500)).default([]),
54
- /** Firmographic profile for targeting and filtering. */
55
- firmographics: FirmographicsSchema.default({}),
56
- /**
57
- * Value proposition — one or two sentences stating why this organization's
58
- * offering is uniquely suited for this segment's needs.
59
- */
60
- valueProp: z.string().trim().max(2000).default('')
61
- })
62
-
63
- // ---------------------------------------------------------------------------
64
- // Customers domain schema — id-keyed map of customer segments.
65
- // ---------------------------------------------------------------------------
66
-
67
- export const CustomersDomainSchema = z
68
- .record(z.string(), CustomerSegmentSchema)
69
- .refine((record) => Object.entries(record).every(([key, entry]) => entry.id === key), {
70
- message: 'Each segment entry id must match its map key'
71
- })
72
- .default({})
73
-
74
- // ---------------------------------------------------------------------------
75
- // Seed — empty by default; adapters populate with real segment definitions.
76
- // ---------------------------------------------------------------------------
77
-
78
- export const DEFAULT_ORGANIZATION_MODEL_CUSTOMERS: z.infer<typeof CustomersDomainSchema> = {}
1
+ import { z } from 'zod'
2
+ import { defineDomainRecord } from '../helpers'
3
+
4
+ // ---------------------------------------------------------------------------
5
+ // Firmographics optional demographic/firmographic filters that describe the
6
+ // target customer segment's organizational profile. All fields are optional so
7
+ // a segment can declare only the axes relevant to targeting.
8
+ // ---------------------------------------------------------------------------
9
+
10
+ export const FirmographicsSchema = z.object({
11
+ /** Industry vertical (e.g. "Marketing Agency", "Legal", "Real Estate"). */
12
+ industry: z.string().trim().max(200).optional(),
13
+ /**
14
+ * Company headcount band (e.g. "1–10", "11–50", "51–200", "200+").
15
+ * Free-form string to accommodate any band notation.
16
+ */
17
+ companySize: z.string().trim().max(100).optional(),
18
+ /**
19
+ * Primary geographic region the segment operates in or is targeted from
20
+ * (e.g. "North America", "Europe", "Global").
21
+ */
22
+ region: z.string().trim().max(200).optional()
23
+ })
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Customer segment schema one entry per distinct buyer archetype.
27
+ // Modeled after Value Proposition Canvas (BMC / VPC) and Business Model Canvas
28
+ // customer-segment language. Fields use plain English throughout.
29
+ // ---------------------------------------------------------------------------
30
+
31
+ export const CustomerSegmentSchema = z.object({
32
+ /** Stable unique identifier for the segment (e.g. "segment-smb-agencies"). */
33
+ id: z.string().trim().min(1).max(100),
34
+ /** Domain-map iteration order. Convention: multiples of 10 (10, 20, 30, ...) to allow easy insertion. */
35
+ order: z.number(),
36
+ /** Human-readable name shown to agents and in UI (e.g. "SMB Marketing Agencies"). */
37
+ name: z.string().trim().max(200).default(''),
38
+ /** One or two sentences describing who this segment is. */
39
+ description: z.string().trim().max(2000).default(''),
40
+ /**
41
+ * The primary job(s) this segment is trying to get done the goal they hire
42
+ * a product/service to accomplish. Plain-language narrative or bullet list.
43
+ */
44
+ jobsToBeDone: z.string().trim().max(2000).default(''),
45
+ /**
46
+ * Pains frustrations, obstacles, and risks the segment experiences
47
+ * when trying to accomplish their jobs-to-be-done.
48
+ */
49
+ pains: z.array(z.string().trim().max(500)).default([]),
50
+ /**
51
+ * Gains outcomes and benefits the segment desires; positive motivators
52
+ * beyond merely resolving pains.
53
+ */
54
+ gains: z.array(z.string().trim().max(500)).default([]),
55
+ /** Firmographic profile for targeting and filtering. */
56
+ firmographics: FirmographicsSchema.default({}),
57
+ /**
58
+ * Value proposition one or two sentences stating why this organization's
59
+ * offering is uniquely suited for this segment's needs.
60
+ */
61
+ valueProp: z.string().trim().max(2000).default('')
62
+ })
63
+
64
+ // ---------------------------------------------------------------------------
65
+ // Customers domain schema — id-keyed map of customer segments.
66
+ // ---------------------------------------------------------------------------
67
+
68
+ export const CustomersDomainSchema = z
69
+ .record(z.string(), CustomerSegmentSchema)
70
+ .refine((record) => Object.entries(record).every(([key, entry]) => entry.id === key), {
71
+ message: 'Each segment entry id must match its map key'
72
+ })
73
+ .default({})
74
+
75
+ // ---------------------------------------------------------------------------
76
+ // Seed — empty by default; adapters populate with real segment definitions.
77
+ // ---------------------------------------------------------------------------
78
+
79
+ export const DEFAULT_ORGANIZATION_MODEL_CUSTOMERS: z.infer<typeof CustomersDomainSchema> = {}
80
+
81
+ /** Validate and return a single customer segment entry. */
82
+ export function defineCustomer(entry: z.input<typeof CustomerSegmentSchema>): z.infer<typeof CustomerSegmentSchema> {
83
+ return CustomerSegmentSchema.parse(entry)
84
+ }
85
+
86
+ /** Validate and return an id-keyed map of customer segment entries. */
87
+ export function defineCustomers(
88
+ entries: readonly z.input<typeof CustomerSegmentSchema>[]
89
+ ): Record<string, z.infer<typeof CustomerSegmentSchema>> {
90
+ return defineDomainRecord(CustomerSegmentSchema, entries)
91
+ }
92
+
93
+ export type CustomerSegment = z.infer<typeof CustomerSegmentSchema>
94
+ export type CustomersDomain = z.infer<typeof CustomersDomainSchema>
95
+ export type Firmographics = z.infer<typeof FirmographicsSchema>