@elevasis/core 0.27.0 → 0.29.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 +146 -89
- package/dist/index.js +116 -46
- package/dist/knowledge/index.d.ts +21 -21
- package/dist/organization-model/index.d.ts +146 -89
- package/dist/organization-model/index.js +116 -46
- package/dist/test-utils/index.d.ts +20 -17
- package/dist/test-utils/index.js +22 -20
- package/package.json +1 -1
- package/src/business/acquisition/api-schemas.test.ts +59 -8
- package/src/business/acquisition/api-schemas.ts +10 -5
- package/src/business/acquisition/build-templates.test.ts +187 -240
- package/src/business/acquisition/build-templates.ts +87 -98
- package/src/business/acquisition/types.ts +390 -389
- package/src/execution/engine/index.ts +6 -4
- package/src/execution/engine/tools/lead-service-types.ts +63 -34
- package/src/execution/engine/tools/platform/acquisition/types.ts +7 -8
- package/src/execution/engine/tools/registry.ts +6 -4
- package/src/execution/engine/tools/tool-maps.ts +23 -1
- package/src/organization-model/__tests__/define-domain-record.test.ts +289 -0
- package/src/organization-model/__tests__/om-spine-doc-contract.test.ts +56 -0
- package/src/organization-model/domains/actions.ts +13 -0
- package/src/organization-model/domains/customers.ts +95 -78
- package/src/organization-model/domains/entities.ts +157 -144
- package/src/organization-model/domains/goals.ts +100 -83
- package/src/organization-model/domains/knowledge.ts +106 -93
- package/src/organization-model/domains/offerings.ts +88 -71
- package/src/organization-model/domains/policies.ts +115 -102
- package/src/organization-model/domains/prospecting.ts +2 -327
- package/src/organization-model/domains/roles.ts +109 -96
- package/src/organization-model/domains/statuses.ts +351 -339
- package/src/organization-model/domains/systems.ts +176 -164
- package/src/organization-model/helpers.ts +331 -306
- package/src/organization-model/index.ts +42 -0
- package/src/organization-model/migration-helpers.ts +16 -12
- package/src/organization-model/published.ts +27 -2
- package/src/platform/constants/versions.ts +1 -1
- package/src/reference/_generated/contracts.md +376 -352
- package/src/supabase/database.types.ts +3 -0
|
@@ -326,10 +326,12 @@ export {
|
|
|
326
326
|
type TransitionDealParams,
|
|
327
327
|
type LoadDealParams,
|
|
328
328
|
// List-oriented lead gen params (Step 3 of list-oriented migration)
|
|
329
|
-
type UpdateListConfigParams,
|
|
330
|
-
type UpdateCompanyStageParams,
|
|
331
|
-
type UpdateContactStageParams,
|
|
332
|
-
type
|
|
329
|
+
type UpdateListConfigParams,
|
|
330
|
+
type UpdateCompanyStageParams,
|
|
331
|
+
type UpdateContactStageParams,
|
|
332
|
+
type ClearCompanyStagesParams,
|
|
333
|
+
type ClearContactStagesParams,
|
|
334
|
+
type ListPendingCompanyIdsParams,
|
|
333
335
|
type ListPendingContactIdsParams,
|
|
334
336
|
type AddCompaniesToListParams,
|
|
335
337
|
type AddCompaniesToListResult,
|
|
@@ -529,23 +529,39 @@ export interface UpdateListConfigParams {
|
|
|
529
529
|
pipelineConfig?: PipelineConfig
|
|
530
530
|
}
|
|
531
531
|
|
|
532
|
-
export interface UpdateCompanyStageParams {
|
|
533
|
-
organizationId: string
|
|
534
|
-
listId: string
|
|
535
|
-
companyId: string
|
|
536
|
-
stage: string
|
|
537
|
-
status?: ProcessingStageStatus
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
532
|
+
export interface UpdateCompanyStageParams {
|
|
533
|
+
organizationId: string
|
|
534
|
+
listId: string
|
|
535
|
+
companyId: string
|
|
536
|
+
stage: string
|
|
537
|
+
status?: ProcessingStageStatus
|
|
538
|
+
data?: unknown
|
|
539
|
+
executionId?: string
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
export interface UpdateContactStageParams {
|
|
543
|
+
organizationId: string
|
|
544
|
+
listId: string
|
|
545
|
+
contactId: string
|
|
546
|
+
stage: string
|
|
547
|
+
status?: ProcessingStageStatus
|
|
548
|
+
data?: unknown
|
|
549
|
+
executionId?: string
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
export interface ClearCompanyStagesParams {
|
|
553
|
+
organizationId: string
|
|
554
|
+
listId: string
|
|
555
|
+
companyId: string
|
|
556
|
+
stages: string[]
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
export interface ClearContactStagesParams {
|
|
560
|
+
organizationId: string
|
|
561
|
+
listId: string
|
|
562
|
+
contactId: string
|
|
563
|
+
stages: string[]
|
|
564
|
+
}
|
|
549
565
|
|
|
550
566
|
export interface ListPendingCompanyIdsParams {
|
|
551
567
|
organizationId: string
|
|
@@ -589,14 +605,15 @@ export interface RecordListExecutionParams {
|
|
|
589
605
|
configSnapshot?: Record<string, unknown>
|
|
590
606
|
}
|
|
591
607
|
|
|
592
|
-
export interface ListExecutionSummary {
|
|
593
|
-
executionId: string
|
|
594
|
-
resourceId: string
|
|
595
|
-
status: string
|
|
596
|
-
createdAt: string
|
|
597
|
-
completedAt: string | null
|
|
598
|
-
durationMs: number | null
|
|
599
|
-
|
|
608
|
+
export interface ListExecutionSummary {
|
|
609
|
+
executionId: string
|
|
610
|
+
resourceId: string
|
|
611
|
+
status: string
|
|
612
|
+
createdAt: string
|
|
613
|
+
completedAt: string | null
|
|
614
|
+
durationMs: number | null
|
|
615
|
+
input?: unknown
|
|
616
|
+
}
|
|
600
617
|
|
|
601
618
|
// Bulk import (contacts)
|
|
602
619
|
export interface BulkImportParams {
|
|
@@ -712,15 +729,27 @@ export interface ILeadService {
|
|
|
712
729
|
*/
|
|
713
730
|
getListProgress(listId: string, organizationId: string): Promise<ListProgress | null>
|
|
714
731
|
|
|
715
|
-
/**
|
|
716
|
-
* Advance a company row within a list's explicit stage journey.
|
|
717
|
-
*/
|
|
718
|
-
updateCompanyStage(params: UpdateCompanyStageParams): Promise<void>
|
|
719
|
-
|
|
720
|
-
/**
|
|
721
|
-
* Advance a contact row within a list's explicit stage journey.
|
|
722
|
-
*/
|
|
723
|
-
updateContactStage(params: UpdateContactStageParams): Promise<void>
|
|
732
|
+
/**
|
|
733
|
+
* Advance a company row within a list's explicit stage journey.
|
|
734
|
+
*/
|
|
735
|
+
updateCompanyStage(params: UpdateCompanyStageParams): Promise<void>
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Advance a contact row within a list's explicit stage journey.
|
|
739
|
+
*/
|
|
740
|
+
updateContactStage(params: UpdateContactStageParams): Promise<void>
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Clear company stage entries by deleting keys from processing_state.
|
|
744
|
+
* Missing stage keys represent "not attempted"; never persist null markers.
|
|
745
|
+
*/
|
|
746
|
+
clearCompanyStages(params: ClearCompanyStagesParams): Promise<void>
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Clear contact stage entries by deleting keys from processing_state.
|
|
750
|
+
* Missing stage keys represent "not attempted"; never persist null markers.
|
|
751
|
+
*/
|
|
752
|
+
clearContactStages(params: ClearContactStagesParams): Promise<void>
|
|
724
753
|
|
|
725
754
|
/**
|
|
726
755
|
* Return the company_ids in acq_list_companies for the given list where
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import {
|
|
4
3
|
CompanyProcessingStateSchema,
|
|
5
4
|
ContactProcessingStateSchema
|
|
6
5
|
} from '../../../../../business/acquisition/api-schemas'
|
|
@@ -10,11 +9,11 @@ import { EmailSchema } from '../../../../../platform/utils/validation'
|
|
|
10
9
|
// LIST SCHEMAS
|
|
11
10
|
// ============================================
|
|
12
11
|
|
|
13
|
-
export const CreateListInputSchema = z.object({
|
|
14
|
-
name: z.string().min(1),
|
|
15
|
-
description: z.string().optional(),
|
|
16
|
-
buildTemplateId: z.string().
|
|
17
|
-
})
|
|
12
|
+
export const CreateListInputSchema = z.object({
|
|
13
|
+
name: z.string().min(1),
|
|
14
|
+
description: z.string().optional(),
|
|
15
|
+
buildTemplateId: z.string().trim().min(1).max(100).optional()
|
|
16
|
+
})
|
|
18
17
|
|
|
19
18
|
export const UpdateListInputSchema = z.object({
|
|
20
19
|
id: z.string().uuid(),
|
|
@@ -587,10 +587,12 @@ export {
|
|
|
587
587
|
type GetDealByIdParams,
|
|
588
588
|
type GetContactByIdParams,
|
|
589
589
|
type GetCompanyByIdParams,
|
|
590
|
-
type UpdateListConfigParams,
|
|
591
|
-
type UpdateCompanyStageParams,
|
|
592
|
-
type UpdateContactStageParams,
|
|
593
|
-
type
|
|
590
|
+
type UpdateListConfigParams,
|
|
591
|
+
type UpdateCompanyStageParams,
|
|
592
|
+
type UpdateContactStageParams,
|
|
593
|
+
type ClearCompanyStagesParams,
|
|
594
|
+
type ClearContactStagesParams,
|
|
595
|
+
type ListPendingCompanyIdsParams,
|
|
594
596
|
type ListPendingContactIdsParams,
|
|
595
597
|
type AddCompaniesToListParams,
|
|
596
598
|
type AddCompaniesToListResult,
|
|
@@ -258,6 +258,8 @@ import type {
|
|
|
258
258
|
RecordListExecutionParams,
|
|
259
259
|
UpdateCompanyStageParams,
|
|
260
260
|
UpdateContactStageParams,
|
|
261
|
+
ClearCompanyStagesParams,
|
|
262
|
+
ClearContactStagesParams,
|
|
261
263
|
ListPendingCompanyIdsParams,
|
|
262
264
|
ListPendingContactIdsParams,
|
|
263
265
|
UpsertSocialPostParams,
|
|
@@ -589,6 +591,14 @@ export type LeadToolMap = {
|
|
|
589
591
|
params: Omit<UpdateContactStageParams, 'organizationId'>
|
|
590
592
|
result: void
|
|
591
593
|
}
|
|
594
|
+
clearCompanyStages: {
|
|
595
|
+
params: Omit<ClearCompanyStagesParams, 'organizationId'>
|
|
596
|
+
result: void
|
|
597
|
+
}
|
|
598
|
+
clearContactStages: {
|
|
599
|
+
params: Omit<ClearContactStagesParams, 'organizationId'>
|
|
600
|
+
result: void
|
|
601
|
+
}
|
|
592
602
|
// Company operations
|
|
593
603
|
createCompany: { params: Omit<CreateCompanyParams, 'organizationId'>; result: AcqCompany }
|
|
594
604
|
upsertCompany: { params: Omit<UpsertCompanyParams, 'organizationId'>; result: AcqCompany }
|
|
@@ -713,7 +723,11 @@ export type LeadToolMap = {
|
|
|
713
723
|
export type ListToolMap = {
|
|
714
724
|
getConfig: {
|
|
715
725
|
params: { listId: string }
|
|
716
|
-
result: {
|
|
726
|
+
result: {
|
|
727
|
+
scrapingConfig: ScrapingConfig
|
|
728
|
+
icp: IcpRubric
|
|
729
|
+
pipelineConfig: PipelineConfig & { dataMode: 'mock' | 'live' }
|
|
730
|
+
}
|
|
717
731
|
}
|
|
718
732
|
recordExecution: {
|
|
719
733
|
params: Omit<RecordListExecutionParams, 'organizationId'>
|
|
@@ -727,6 +741,14 @@ export type ListToolMap = {
|
|
|
727
741
|
params: Omit<UpdateContactStageParams, 'organizationId'>
|
|
728
742
|
result: void
|
|
729
743
|
}
|
|
744
|
+
clearCompanyStages: {
|
|
745
|
+
params: Omit<ClearCompanyStagesParams, 'organizationId'>
|
|
746
|
+
result: void
|
|
747
|
+
}
|
|
748
|
+
clearContactStages: {
|
|
749
|
+
params: Omit<ClearContactStagesParams, 'organizationId'>
|
|
750
|
+
result: void
|
|
751
|
+
}
|
|
730
752
|
listPendingCompanyIds: {
|
|
731
753
|
params: Omit<ListPendingCompanyIdsParams, 'organizationId'>
|
|
732
754
|
result: string[]
|
|
@@ -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>
|