@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.
- package/dist/index.d.ts +157 -100
- package/dist/index.js +116 -46
- package/dist/knowledge/index.d.ts +22 -22
- package/dist/organization-model/index.d.ts +157 -100
- package/dist/organization-model/index.js +116 -46
- package/dist/test-utils/index.d.ts +18 -18
- package/dist/test-utils/index.js +22 -20
- package/package.json +3 -3
- 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/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/published.ts +27 -2
- package/src/platform/constants/versions.ts +1 -1
- package/src/reference/_generated/contracts.md +24 -24
|
@@ -1,144 +1,157 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import { DescriptionSchema, LabelSchema, ModelIdSchema } from './shared'
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
order:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
{ toEntity: 'leadgen.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
{ toEntity: 'leadgen.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
{ toEntity: 'leadgen.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
{ toEntity: 'delivery.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
{ toEntity: 'delivery.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
{ toEntity: 'delivery.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
export
|
|
143
|
-
|
|
144
|
-
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { DescriptionSchema, LabelSchema, ModelIdSchema } from './shared'
|
|
3
|
+
import { defineDomainRecord } from '../helpers'
|
|
4
|
+
|
|
5
|
+
export const EntityIdSchema = ModelIdSchema
|
|
6
|
+
|
|
7
|
+
export const EntityLinkKindSchema = z
|
|
8
|
+
.enum(['belongs-to', 'has-many', 'has-one', 'many-to-many'])
|
|
9
|
+
.meta({ label: 'Link kind' })
|
|
10
|
+
|
|
11
|
+
export const EntityLinkSchema = z.object({
|
|
12
|
+
toEntity: EntityIdSchema.meta({ ref: 'entity' }),
|
|
13
|
+
kind: EntityLinkKindSchema,
|
|
14
|
+
via: z.string().trim().min(1).max(255).optional(),
|
|
15
|
+
label: LabelSchema.optional()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
export const EntitySchema = z.object({
|
|
19
|
+
id: EntityIdSchema,
|
|
20
|
+
/** Domain-map iteration order. Convention: multiples of 10 (10, 20, 30, ...) to allow easy insertion. */
|
|
21
|
+
order: z.number(),
|
|
22
|
+
label: LabelSchema,
|
|
23
|
+
description: DescriptionSchema.optional(),
|
|
24
|
+
ownedBySystemId: ModelIdSchema.meta({ ref: 'system' }),
|
|
25
|
+
table: z.string().trim().min(1).max(255).optional(),
|
|
26
|
+
rowSchema: ModelIdSchema.optional(),
|
|
27
|
+
stateCatalogId: ModelIdSchema.optional(),
|
|
28
|
+
links: z.array(EntityLinkSchema).optional()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
export const EntitiesDomainSchema = z
|
|
32
|
+
.record(z.string(), EntitySchema)
|
|
33
|
+
.refine((record) => Object.entries(record).every(([key, entry]) => entry.id === key), {
|
|
34
|
+
message: 'Each entity entry id must match its map key'
|
|
35
|
+
})
|
|
36
|
+
.default({})
|
|
37
|
+
|
|
38
|
+
const ENTITY_ENTRY_INPUTS: z.input<typeof EntitySchema>[] = [
|
|
39
|
+
{
|
|
40
|
+
id: 'crm.deal',
|
|
41
|
+
order: 10,
|
|
42
|
+
label: 'Deal',
|
|
43
|
+
description: 'A CRM opportunity or sales pipeline record.',
|
|
44
|
+
ownedBySystemId: 'sales.crm',
|
|
45
|
+
table: 'crm_deals',
|
|
46
|
+
stateCatalogId: 'crm.pipeline',
|
|
47
|
+
links: [{ toEntity: 'crm.contact', kind: 'has-many', via: 'deal_contacts', label: 'contacts' }]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 'crm.contact',
|
|
51
|
+
order: 20,
|
|
52
|
+
label: 'CRM Contact',
|
|
53
|
+
description: 'A person associated with a CRM relationship or deal.',
|
|
54
|
+
ownedBySystemId: 'sales.crm',
|
|
55
|
+
table: 'crm_contacts'
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: 'leadgen.list',
|
|
59
|
+
order: 30,
|
|
60
|
+
label: 'Lead List',
|
|
61
|
+
description: 'A prospecting list that groups companies and contacts for acquisition workflows.',
|
|
62
|
+
ownedBySystemId: 'sales.lead-gen',
|
|
63
|
+
table: 'acq_lists',
|
|
64
|
+
links: [
|
|
65
|
+
{ toEntity: 'leadgen.company', kind: 'has-many', via: 'acq_list_companies', label: 'companies' },
|
|
66
|
+
{ toEntity: 'leadgen.contact', kind: 'has-many', via: 'acq_list_members', label: 'contacts' }
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: 'leadgen.company',
|
|
71
|
+
order: 40,
|
|
72
|
+
label: 'Lead Company',
|
|
73
|
+
description: 'A company record sourced, enriched, and qualified during prospecting.',
|
|
74
|
+
ownedBySystemId: 'sales.lead-gen',
|
|
75
|
+
table: 'acq_list_companies',
|
|
76
|
+
stateCatalogId: 'lead-gen.company',
|
|
77
|
+
links: [
|
|
78
|
+
{ toEntity: 'leadgen.list', kind: 'belongs-to', via: 'list_id', label: 'list' },
|
|
79
|
+
{ toEntity: 'leadgen.contact', kind: 'has-many', via: 'company_id', label: 'contacts' }
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: 'leadgen.contact',
|
|
84
|
+
order: 50,
|
|
85
|
+
label: 'Lead Contact',
|
|
86
|
+
description: 'A prospect contact discovered or enriched during lead generation.',
|
|
87
|
+
ownedBySystemId: 'sales.lead-gen',
|
|
88
|
+
table: 'acq_list_members',
|
|
89
|
+
stateCatalogId: 'lead-gen.contact',
|
|
90
|
+
links: [
|
|
91
|
+
{ toEntity: 'leadgen.list', kind: 'belongs-to', via: 'list_id', label: 'list' },
|
|
92
|
+
{ toEntity: 'leadgen.company', kind: 'belongs-to', via: 'company_id', label: 'company' }
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: 'delivery.project',
|
|
97
|
+
order: 60,
|
|
98
|
+
label: 'Project',
|
|
99
|
+
description: 'A client delivery project.',
|
|
100
|
+
ownedBySystemId: 'projects',
|
|
101
|
+
table: 'projects',
|
|
102
|
+
links: [
|
|
103
|
+
{ toEntity: 'delivery.milestone', kind: 'has-many', via: 'project_id', label: 'milestones' },
|
|
104
|
+
{ toEntity: 'delivery.task', kind: 'has-many', via: 'project_id', label: 'tasks' }
|
|
105
|
+
]
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: 'delivery.milestone',
|
|
109
|
+
order: 70,
|
|
110
|
+
label: 'Milestone',
|
|
111
|
+
description: 'A delivery checkpoint within a project.',
|
|
112
|
+
ownedBySystemId: 'projects',
|
|
113
|
+
table: 'project_milestones',
|
|
114
|
+
links: [
|
|
115
|
+
{ toEntity: 'delivery.project', kind: 'belongs-to', via: 'project_id', label: 'project' },
|
|
116
|
+
{ toEntity: 'delivery.task', kind: 'has-many', via: 'milestone_id', label: 'tasks' }
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: 'delivery.task',
|
|
121
|
+
order: 80,
|
|
122
|
+
label: 'Task',
|
|
123
|
+
description: 'A delivery task that can move through the task status catalog.',
|
|
124
|
+
ownedBySystemId: 'projects',
|
|
125
|
+
table: 'project_tasks',
|
|
126
|
+
stateCatalogId: 'delivery.task',
|
|
127
|
+
links: [
|
|
128
|
+
{ toEntity: 'delivery.project', kind: 'belongs-to', via: 'project_id', label: 'project' },
|
|
129
|
+
{ toEntity: 'delivery.milestone', kind: 'belongs-to', via: 'milestone_id', label: 'milestone' }
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
export const DEFAULT_ORGANIZATION_MODEL_ENTITIES: z.infer<typeof EntitiesDomainSchema> = Object.fromEntries(
|
|
135
|
+
ENTITY_ENTRY_INPUTS.map((entity) => {
|
|
136
|
+
const parsed = EntitySchema.parse(entity)
|
|
137
|
+
return [parsed.id, parsed]
|
|
138
|
+
})
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
/** Validate and return a single entity entry. */
|
|
142
|
+
export function defineEntity(entry: z.input<typeof EntitySchema>): z.infer<typeof EntitySchema> {
|
|
143
|
+
return EntitySchema.parse(entry)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Validate and return an id-keyed map of entity entries. */
|
|
147
|
+
export function defineEntities(
|
|
148
|
+
entries: readonly z.input<typeof EntitySchema>[]
|
|
149
|
+
): Record<string, z.infer<typeof EntitySchema>> {
|
|
150
|
+
return defineDomainRecord(EntitySchema, entries)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export type EntityId = z.infer<typeof EntityIdSchema>
|
|
154
|
+
export type EntityLinkKind = z.infer<typeof EntityLinkKindSchema>
|
|
155
|
+
export type EntityLink = z.infer<typeof EntityLinkSchema>
|
|
156
|
+
export type Entity = z.infer<typeof EntitySchema>
|
|
157
|
+
export type EntitiesDomain = z.infer<typeof EntitiesDomainSchema>
|
|
@@ -1,83 +1,100 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
description
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
* "
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
order:
|
|
48
|
-
|
|
49
|
-
description
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
*
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
*
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { defineDomainRecord } from '../helpers'
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Measurable outcome schema — one trackable result that supports a goal.
|
|
6
|
+
// The field name `keyResults` is used for compatibility with OKR tooling;
|
|
7
|
+
// user-facing surfaces and documentation MUST say "measurable outcomes",
|
|
8
|
+
// never "key results" or "OKR".
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
export const KeyResultSchema = z.object({
|
|
12
|
+
/** Stable unique identifier for the measurable outcome (e.g. "kr-revenue-q1"). */
|
|
13
|
+
id: z.string().trim().min(1).max(100),
|
|
14
|
+
/** Plain-language description of this measurable outcome (e.g. "Increase trial-to-paid conversion"). */
|
|
15
|
+
description: z.string().trim().min(1).max(500),
|
|
16
|
+
/**
|
|
17
|
+
* What is being measured — the metric name (e.g. "monthly revenue", "NPS score",
|
|
18
|
+
* "trial-to-paid conversion rate"). Free-form string.
|
|
19
|
+
*/
|
|
20
|
+
targetMetric: z.string().trim().min(1).max(200),
|
|
21
|
+
/** Current measured value. Defaults to 0 when not yet tracked. */
|
|
22
|
+
currentValue: z.number().default(0),
|
|
23
|
+
/**
|
|
24
|
+
* Target value to reach for this measurable outcome to be considered achieved.
|
|
25
|
+
* Optional — omit if the outcome is directional (e.g. "reduce churn") without
|
|
26
|
+
* a hard numeric target.
|
|
27
|
+
*/
|
|
28
|
+
targetValue: z.number().optional()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Objective schema — one goal the organization is working toward.
|
|
33
|
+
// User-facing label is "goal"; this schema type is named Objective for
|
|
34
|
+
// structural continuity with OKR tooling. Do NOT expose "OKR", "objective",
|
|
35
|
+
// or "key result" in any user-facing label or documentation.
|
|
36
|
+
//
|
|
37
|
+
// Period fields use ISO 8601 date strings (YYYY-MM-DD). Cross-schema
|
|
38
|
+
// validation (periodEnd > periodStart) is enforced in
|
|
39
|
+
// `OrganizationModelSchema.superRefine()`.
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/
|
|
43
|
+
|
|
44
|
+
export const ObjectiveSchema = z.object({
|
|
45
|
+
/** Stable unique identifier for the goal (e.g. "goal-grow-arr-q1-2026"). */
|
|
46
|
+
id: z.string().trim().min(1).max(100),
|
|
47
|
+
/** Domain-map iteration order. Convention: multiples of 10 (10, 20, 30, ...) to allow easy insertion. */
|
|
48
|
+
order: z.number(),
|
|
49
|
+
/** Plain-language description of what the organization wants to achieve. */
|
|
50
|
+
description: z.string().trim().min(1).max(1000),
|
|
51
|
+
/**
|
|
52
|
+
* Start of the period this goal is active for — ISO 8601 date string (YYYY-MM-DD).
|
|
53
|
+
* Must be strictly before `periodEnd`.
|
|
54
|
+
*/
|
|
55
|
+
periodStart: z.string().regex(ISO_DATE_REGEX, 'periodStart must be an ISO date string (YYYY-MM-DD)'),
|
|
56
|
+
/**
|
|
57
|
+
* End of the period this goal is active for — ISO 8601 date string (YYYY-MM-DD).
|
|
58
|
+
* Must be strictly after `periodStart`.
|
|
59
|
+
* Enforced via `OrganizationModelSchema.superRefine()`.
|
|
60
|
+
*/
|
|
61
|
+
periodEnd: z.string().regex(ISO_DATE_REGEX, 'periodEnd must be an ISO date string (YYYY-MM-DD)'),
|
|
62
|
+
/**
|
|
63
|
+
* List of measurable outcomes that define success for this goal.
|
|
64
|
+
* Defaults to empty array so goals can be declared before outcomes are defined.
|
|
65
|
+
*/
|
|
66
|
+
keyResults: z.array(KeyResultSchema).default([])
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Goals domain schema — a collection of goals the organization is pursuing.
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
export const GoalsDomainSchema = z
|
|
74
|
+
.record(z.string(), ObjectiveSchema)
|
|
75
|
+
.refine((record) => Object.entries(record).every(([key, entry]) => entry.id === key), {
|
|
76
|
+
message: 'Each objective entry id must match its map key'
|
|
77
|
+
})
|
|
78
|
+
.default({})
|
|
79
|
+
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// Seed — empty by default; adapters populate with real goal definitions.
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
export const DEFAULT_ORGANIZATION_MODEL_GOALS: z.infer<typeof GoalsDomainSchema> = {}
|
|
85
|
+
|
|
86
|
+
/** Validate and return a single goal (objective) entry. */
|
|
87
|
+
export function defineGoal(entry: z.input<typeof ObjectiveSchema>): z.infer<typeof ObjectiveSchema> {
|
|
88
|
+
return ObjectiveSchema.parse(entry)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Validate and return an id-keyed map of goal entries. */
|
|
92
|
+
export function defineGoals(
|
|
93
|
+
entries: readonly z.input<typeof ObjectiveSchema>[]
|
|
94
|
+
): Record<string, z.infer<typeof ObjectiveSchema>> {
|
|
95
|
+
return defineDomainRecord(ObjectiveSchema, entries)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export type KeyResult = z.infer<typeof KeyResultSchema>
|
|
99
|
+
export type Objective = z.infer<typeof ObjectiveSchema>
|
|
100
|
+
export type GoalsDomain = z.infer<typeof GoalsDomainSchema>
|