@elevasis/core 0.40.0 → 0.42.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/auth/index.d.ts +68 -2
- package/dist/index.d.ts +652 -416
- package/dist/index.js +82 -5
- package/dist/knowledge/index.d.ts +69 -5
- package/dist/organization-model/index.d.ts +652 -416
- package/dist/organization-model/index.js +82 -5
- package/dist/test-utils/index.d.ts +103 -37
- package/dist/test-utils/index.js +69 -2
- package/package.json +1 -1
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +54 -0
- package/src/business/clients/api-schemas.test.ts +63 -29
- package/src/business/clients/api-schemas.ts +41 -29
- package/src/integrations/credentials/index.ts +3 -4
- package/src/organization-model/__tests__/clients.test.ts +146 -0
- package/src/organization-model/contracts.ts +27 -27
- package/src/organization-model/cross-ref.ts +4 -0
- package/src/organization-model/defaults.ts +2 -2
- package/src/organization-model/domains/knowledge.ts +1 -0
- package/src/organization-model/graph/build.ts +23 -6
- package/src/organization-model/graph/schema.ts +4 -3
- package/src/organization-model/graph/types.ts +4 -3
- package/src/organization-model/helpers.ts +15 -0
- package/src/organization-model/published.ts +19 -1
- package/src/organization-model/schema-refinements.ts +2 -2
- package/src/organization-model/schema.ts +97 -0
- package/src/organization-model/snapshot-hash.ts +0 -2
- package/src/organization-model/types.ts +19 -1
- package/src/platform/constants/versions.ts +1 -1
- package/src/reference/_generated/contracts.md +54 -0
- package/src/supabase/database.types.ts +3 -0
- package/src/supabase/index.ts +0 -6
- package/src/business/pdf/server/__tests__/pdfmake-test.ts +0 -219
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
2
|
import {
|
|
3
|
-
ClientDetailResponseSchema,
|
|
4
|
-
ClientListResponseSchema,
|
|
5
|
-
ClientStatusResponseSchema,
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
ClientDetailResponseSchema,
|
|
4
|
+
ClientListResponseSchema,
|
|
5
|
+
ClientStatusResponseSchema,
|
|
6
|
+
CreateClientRequestSchema,
|
|
7
|
+
ListClientsQuerySchema,
|
|
8
|
+
UpdateClientRequestSchema
|
|
9
|
+
} from './api-schemas'
|
|
8
10
|
|
|
9
11
|
const VALID_UUID = '00000000-0000-4000-8000-000000000001'
|
|
10
12
|
const ISO_TS = '2026-05-08T00:00:00.000Z'
|
|
@@ -12,19 +14,21 @@ const ISO_TS = '2026-05-08T00:00:00.000Z'
|
|
|
12
14
|
describe('client API schemas', () => {
|
|
13
15
|
it('coerces list pagination and accepts status/search filters', () => {
|
|
14
16
|
const result = ListClientsQuerySchema.safeParse({
|
|
15
|
-
status: 'active',
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
status: 'active',
|
|
18
|
+
source: 'acquisition',
|
|
19
|
+
search: 'Acme',
|
|
20
|
+
limit: '10',
|
|
21
|
+
offset: '20'
|
|
19
22
|
})
|
|
20
23
|
|
|
21
24
|
expect(result.success).toBe(true)
|
|
22
25
|
if (result.success) {
|
|
23
26
|
expect(result.data).toEqual({
|
|
24
|
-
status: 'active',
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
status: 'active',
|
|
28
|
+
source: 'acquisition',
|
|
29
|
+
search: 'Acme',
|
|
30
|
+
limit: 10,
|
|
31
|
+
offset: 20
|
|
28
32
|
})
|
|
29
33
|
}
|
|
30
34
|
})
|
|
@@ -40,10 +44,11 @@ describe('client API schemas', () => {
|
|
|
40
44
|
{
|
|
41
45
|
id: VALID_UUID,
|
|
42
46
|
organizationId: VALID_UUID,
|
|
43
|
-
name: 'Acme',
|
|
44
|
-
status: 'active',
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
name: 'Acme',
|
|
48
|
+
status: 'active',
|
|
49
|
+
source: 'manual',
|
|
50
|
+
sourceDealId: null,
|
|
51
|
+
primaryCompanyId: null,
|
|
47
52
|
primaryContactId: null,
|
|
48
53
|
convertedAt: null,
|
|
49
54
|
metadata: {},
|
|
@@ -63,13 +68,14 @@ describe('client API schemas', () => {
|
|
|
63
68
|
ClientDetailResponseSchema.safeParse({
|
|
64
69
|
id: VALID_UUID,
|
|
65
70
|
organizationId: VALID_UUID,
|
|
66
|
-
name: 'Acme',
|
|
67
|
-
status: 'onboarding',
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
name: 'Acme',
|
|
72
|
+
status: 'onboarding',
|
|
73
|
+
source: 'acquisition',
|
|
74
|
+
sourceDealId: VALID_UUID,
|
|
75
|
+
primaryCompanyId: VALID_UUID,
|
|
76
|
+
primaryContactId: VALID_UUID,
|
|
77
|
+
convertedAt: ISO_TS,
|
|
78
|
+
metadata: { externalProjectSlug: 'developer-workspace-slug' },
|
|
73
79
|
createdAt: ISO_TS,
|
|
74
80
|
updatedAt: ISO_TS,
|
|
75
81
|
lineage: {
|
|
@@ -96,11 +102,39 @@ describe('client API schemas', () => {
|
|
|
96
102
|
}
|
|
97
103
|
]
|
|
98
104
|
}
|
|
99
|
-
}).success
|
|
100
|
-
).toBe(true)
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
it('accepts client
|
|
105
|
+
}).success
|
|
106
|
+
).toBe(true)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('accepts direct word-of-mouth client create requests with metadata for workspace details', () => {
|
|
110
|
+
expect(
|
|
111
|
+
CreateClientRequestSchema.safeParse({
|
|
112
|
+
name: 'Byron for Irvine',
|
|
113
|
+
status: 'onboarding',
|
|
114
|
+
source: 'word_of_mouth',
|
|
115
|
+
metadata: {
|
|
116
|
+
externalProjectSlug: 'developer-workspace-slug',
|
|
117
|
+
workspacePath: 'client-workspace',
|
|
118
|
+
campaignSlug: 'byron-for-irvine'
|
|
119
|
+
}
|
|
120
|
+
}).success
|
|
121
|
+
).toBe(true)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('rejects invalid client source format', () => {
|
|
125
|
+
expect(
|
|
126
|
+
CreateClientRequestSchema.safeParse({
|
|
127
|
+
name: 'Byron for Irvine',
|
|
128
|
+
source: 'Word of mouth'
|
|
129
|
+
}).success
|
|
130
|
+
).toBe(false)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('accepts source-only client update requests', () => {
|
|
134
|
+
expect(UpdateClientRequestSchema.safeParse({ source: 'acquisition' }).success).toBe(true)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('accepts client portfolio status responses', () => {
|
|
104
138
|
expect(
|
|
105
139
|
ClientStatusResponseSchema.safeParse({
|
|
106
140
|
totalClients: 1,
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
import { UuidSchema } from '../../platform/utils/validation'
|
|
3
3
|
|
|
4
|
-
export const ClientStatusSchema = z.enum(['active', 'onboarding', 'paused', 'completed', 'churned'])
|
|
4
|
+
export const ClientStatusSchema = z.enum(['active', 'onboarding', 'paused', 'completed', 'churned'])
|
|
5
|
+
export const ClientSourceSchema = z
|
|
6
|
+
.string()
|
|
7
|
+
.trim()
|
|
8
|
+
.min(1)
|
|
9
|
+
.max(64)
|
|
10
|
+
.regex(/^[a-z][a-z0-9_]*$/, 'Source must use lowercase letters, numbers, and underscores')
|
|
5
11
|
|
|
6
12
|
export const ClientIdParamsSchema = z
|
|
7
13
|
.object({
|
|
@@ -9,12 +15,13 @@ export const ClientIdParamsSchema = z
|
|
|
9
15
|
})
|
|
10
16
|
.strict()
|
|
11
17
|
|
|
12
|
-
export const ListClientsQuerySchema = z
|
|
13
|
-
.object({
|
|
14
|
-
status: ClientStatusSchema.optional(),
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
export const ListClientsQuerySchema = z
|
|
19
|
+
.object({
|
|
20
|
+
status: ClientStatusSchema.optional(),
|
|
21
|
+
source: ClientSourceSchema.optional(),
|
|
22
|
+
search: z.string().trim().min(1).max(255).optional(),
|
|
23
|
+
limit: z.coerce.number().int().min(1).max(100).default(50),
|
|
24
|
+
offset: z.coerce.number().int().min(0).default(0)
|
|
18
25
|
})
|
|
19
26
|
.strict()
|
|
20
27
|
|
|
@@ -26,12 +33,13 @@ export const ClientRefSchema = z.object({
|
|
|
26
33
|
|
|
27
34
|
export const ClientResponseSchema = z.object({
|
|
28
35
|
id: z.string(),
|
|
29
|
-
organizationId: z.string(),
|
|
30
|
-
name: z.string(),
|
|
31
|
-
status: ClientStatusSchema,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
organizationId: z.string(),
|
|
37
|
+
name: z.string(),
|
|
38
|
+
status: ClientStatusSchema,
|
|
39
|
+
source: ClientSourceSchema,
|
|
40
|
+
sourceDealId: z.string().nullable(),
|
|
41
|
+
primaryCompanyId: z.string().nullable(),
|
|
42
|
+
primaryContactId: z.string().nullable(),
|
|
35
43
|
convertedAt: z.string().nullable(),
|
|
36
44
|
metadata: z.record(z.string(), z.unknown()),
|
|
37
45
|
createdAt: z.string(),
|
|
@@ -100,22 +108,24 @@ export const ClientStatusResponseSchema = z.object({
|
|
|
100
108
|
|
|
101
109
|
export const CreateClientRequestSchema = z
|
|
102
110
|
.object({
|
|
103
|
-
name: z.string().trim().min(1).max(255),
|
|
104
|
-
status: ClientStatusSchema.optional(),
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
111
|
+
name: z.string().trim().min(1).max(255),
|
|
112
|
+
status: ClientStatusSchema.optional(),
|
|
113
|
+
source: ClientSourceSchema.optional(),
|
|
114
|
+
sourceDealId: UuidSchema.nullable().optional(),
|
|
115
|
+
primaryCompanyId: UuidSchema.nullable().optional(),
|
|
116
|
+
primaryContactId: UuidSchema.nullable().optional(),
|
|
108
117
|
metadata: z.record(z.string(), z.unknown()).nullable().optional()
|
|
109
118
|
})
|
|
110
119
|
.strict()
|
|
111
120
|
|
|
112
121
|
export const UpdateClientRequestSchema = z
|
|
113
122
|
.object({
|
|
114
|
-
name: z.string().trim().min(1).max(255).optional(),
|
|
115
|
-
status: ClientStatusSchema.optional(),
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
123
|
+
name: z.string().trim().min(1).max(255).optional(),
|
|
124
|
+
status: ClientStatusSchema.optional(),
|
|
125
|
+
source: ClientSourceSchema.optional(),
|
|
126
|
+
sourceDealId: UuidSchema.nullable().optional(),
|
|
127
|
+
primaryCompanyId: UuidSchema.nullable().optional(),
|
|
128
|
+
primaryContactId: UuidSchema.nullable().optional(),
|
|
119
129
|
metadata: z.record(z.string(), z.unknown()).nullable().optional()
|
|
120
130
|
})
|
|
121
131
|
.strict()
|
|
@@ -123,10 +133,11 @@ export const UpdateClientRequestSchema = z
|
|
|
123
133
|
message: 'At least one field must be provided'
|
|
124
134
|
})
|
|
125
135
|
|
|
126
|
-
export const ClientSchemas = {
|
|
127
|
-
ClientStatus: ClientStatusSchema,
|
|
128
|
-
|
|
129
|
-
|
|
136
|
+
export const ClientSchemas = {
|
|
137
|
+
ClientStatus: ClientStatusSchema,
|
|
138
|
+
ClientSource: ClientSourceSchema,
|
|
139
|
+
ClientIdParams: ClientIdParamsSchema,
|
|
140
|
+
ListClientsQuery: ListClientsQuerySchema,
|
|
130
141
|
ClientRef: ClientRefSchema,
|
|
131
142
|
ClientResponse: ClientResponseSchema,
|
|
132
143
|
ClientDealRef: ClientDealRefSchema,
|
|
@@ -141,8 +152,9 @@ export const ClientSchemas = {
|
|
|
141
152
|
UpdateClientRequest: UpdateClientRequestSchema
|
|
142
153
|
}
|
|
143
154
|
|
|
144
|
-
export type ClientStatus = z.infer<typeof ClientStatusSchema>
|
|
145
|
-
export type
|
|
155
|
+
export type ClientStatus = z.infer<typeof ClientStatusSchema>
|
|
156
|
+
export type ClientSource = z.infer<typeof ClientSourceSchema>
|
|
157
|
+
export type ClientIdParams = z.infer<typeof ClientIdParamsSchema>
|
|
146
158
|
export type ListClientsQuery = z.infer<typeof ListClientsQuerySchema>
|
|
147
159
|
export type ClientRef = z.infer<typeof ClientRefSchema>
|
|
148
160
|
export type ClientResponse = z.infer<typeof ClientResponseSchema>
|
|
@@ -26,7 +26,6 @@ export {
|
|
|
26
26
|
export type { CredentialSchema, CredentialField } from './schemas'
|
|
27
27
|
|
|
28
28
|
export { buildCredentialValue, extractFormValues } from './utils'
|
|
29
|
-
|
|
30
|
-
// API validation schemas
|
|
31
|
-
export * from './api-schemas'
|
|
32
|
-
export { CredentialSchemas } from './api-schemas'
|
|
29
|
+
|
|
30
|
+
// API validation schemas
|
|
31
|
+
export * from './api-schemas'
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { buildOmCrossRefIndex, getClientProfile, getClientProfileBySlug, listClientProfiles } from '..'
|
|
3
|
+
import { buildOrganizationGraph } from '../graph/build'
|
|
4
|
+
import { OrganizationModelSchema } from '../schema'
|
|
5
|
+
import { resolveOrganizationModel } from '../resolve'
|
|
6
|
+
|
|
7
|
+
const CLIENT_ID = '11111111-1111-4111-8111-111111111111'
|
|
8
|
+
|
|
9
|
+
function makeClientProfile(overrides: Record<string, unknown> = {}) {
|
|
10
|
+
return {
|
|
11
|
+
id: CLIENT_ID,
|
|
12
|
+
slug: 'byron-for-irvine',
|
|
13
|
+
name: 'Byron for Irvine',
|
|
14
|
+
status: 'onboarding',
|
|
15
|
+
source: 'word_of_mouth',
|
|
16
|
+
identity: {
|
|
17
|
+
organizationName: 'Byron for Irvine',
|
|
18
|
+
shortName: 'Byron',
|
|
19
|
+
clientBrief: 'Irvine City Council District 5 campaign client.',
|
|
20
|
+
geographicFocus: ['Irvine, CA'],
|
|
21
|
+
timeZone: 'America/Los_Angeles'
|
|
22
|
+
},
|
|
23
|
+
branding: {
|
|
24
|
+
voice: 'Direct and civic-minded',
|
|
25
|
+
tagline: 'Byron for Irvine',
|
|
26
|
+
values: ['Trust', 'Service']
|
|
27
|
+
},
|
|
28
|
+
workspace: {
|
|
29
|
+
kind: 'external-project',
|
|
30
|
+
owner: 'developer',
|
|
31
|
+
projectId: 'developer-owned-project-or-slug'
|
|
32
|
+
},
|
|
33
|
+
links: {
|
|
34
|
+
projectIds: ['22222222-2222-4222-8222-222222222222']
|
|
35
|
+
},
|
|
36
|
+
prompts: {
|
|
37
|
+
defaultContext: 'Campaign context.'
|
|
38
|
+
},
|
|
39
|
+
config: {
|
|
40
|
+
campaignSlug: 'byron-for-irvine'
|
|
41
|
+
},
|
|
42
|
+
customValues: {
|
|
43
|
+
tenantOwned: false
|
|
44
|
+
},
|
|
45
|
+
...overrides
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
describe('client profile domain', () => {
|
|
50
|
+
it('parses top-level clients keyed by canonical public.clients.id', () => {
|
|
51
|
+
const model = resolveOrganizationModel({
|
|
52
|
+
clients: {
|
|
53
|
+
[CLIENT_ID]: makeClientProfile()
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
expect(model.clients[CLIENT_ID]).toMatchObject({
|
|
58
|
+
id: CLIENT_ID,
|
|
59
|
+
slug: 'byron-for-irvine',
|
|
60
|
+
name: 'Byron for Irvine',
|
|
61
|
+
source: 'word_of_mouth'
|
|
62
|
+
})
|
|
63
|
+
expect(model.clients[CLIENT_ID].identity.geographicFocus).toEqual(['Irvine, CA'])
|
|
64
|
+
expect(model.clients[CLIENT_ID].customValues.tenantOwned).toBe(false)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('rejects a client profile whose id does not match its map key', () => {
|
|
68
|
+
const result = OrganizationModelSchema.safeParse({
|
|
69
|
+
clients: {
|
|
70
|
+
[CLIENT_ID]: makeClientProfile({ id: '33333333-3333-4333-8333-333333333333' })
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
expect(result.success).toBe(false)
|
|
75
|
+
if (!result.success) {
|
|
76
|
+
expect(result.error.issues.some((issue) => issue.message.includes('Each client profile id must match'))).toBe(
|
|
77
|
+
true
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('rejects alternate OM link fields on client profiles', () => {
|
|
83
|
+
const result = OrganizationModelSchema.safeParse({
|
|
84
|
+
clients: {
|
|
85
|
+
[CLIENT_ID]: makeClientProfile({ om_client_id: 'byron' })
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
expect(result.success).toBe(false)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('projects helper lookups by id and slug', () => {
|
|
93
|
+
const model = resolveOrganizationModel({
|
|
94
|
+
clients: {
|
|
95
|
+
[CLIENT_ID]: makeClientProfile()
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
expect(getClientProfile(model, CLIENT_ID)?.slug).toBe('byron-for-irvine')
|
|
100
|
+
expect(getClientProfileBySlug(model, 'byron-for-irvine')?.id).toBe(CLIENT_ID)
|
|
101
|
+
expect(listClientProfiles(model).map((client) => client.id)).toEqual([CLIENT_ID])
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('indexes client profiles as knowledge targets', () => {
|
|
105
|
+
const model = resolveOrganizationModel({
|
|
106
|
+
clients: {
|
|
107
|
+
[CLIENT_ID]: makeClientProfile()
|
|
108
|
+
},
|
|
109
|
+
knowledge: {
|
|
110
|
+
'knowledge.byron-client-context': {
|
|
111
|
+
id: 'knowledge.byron-client-context',
|
|
112
|
+
kind: 'reference',
|
|
113
|
+
title: 'Byron Client Context',
|
|
114
|
+
summary: 'Client profile context for Byron for Irvine.',
|
|
115
|
+
body: '## Context\n\nByron campaign client context.',
|
|
116
|
+
links: [{ target: { kind: 'client', id: CLIENT_ID } }],
|
|
117
|
+
ownerIds: [],
|
|
118
|
+
updatedAt: '2026-05-30'
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
const index = buildOmCrossRefIndex(model)
|
|
123
|
+
|
|
124
|
+
expect(index.clientIds.has(CLIENT_ID)).toBe(true)
|
|
125
|
+
expect(model.knowledge['knowledge.byron-client-context'].links[0].nodeId).toBe(`client:${CLIENT_ID}`)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('projects client profiles into the organization graph', () => {
|
|
129
|
+
const model = resolveOrganizationModel({
|
|
130
|
+
clients: {
|
|
131
|
+
[CLIENT_ID]: makeClientProfile()
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
const graph = buildOrganizationGraph({ organizationModel: model })
|
|
135
|
+
|
|
136
|
+
expect(graph.nodes.find((node) => node.id === `client:${CLIENT_ID}`)).toMatchObject({
|
|
137
|
+
kind: 'client',
|
|
138
|
+
sourceId: CLIENT_ID,
|
|
139
|
+
label: 'Byron for Irvine',
|
|
140
|
+
description: 'Irvine City Council District 5 campaign client.'
|
|
141
|
+
})
|
|
142
|
+
expect(
|
|
143
|
+
graph.edges.find((edge) => edge.sourceId === 'organization-model' && edge.targetId === `client:${CLIENT_ID}`)
|
|
144
|
+
).toMatchObject({ kind: 'contains' })
|
|
145
|
+
})
|
|
146
|
+
})
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
export const KNOWLEDGE_FEATURE_ID = 'knowledge' as const
|
|
2
|
-
export const KNOWLEDGE_SYSTEM_ID = 'knowledge' as const
|
|
3
|
-
|
|
4
|
-
export const PROJECTS_SYSTEM_ID = 'projects' as const
|
|
5
|
-
/** @deprecated Use PROJECTS_SYSTEM_ID. Scheduled for removal after one publish cycle. */
|
|
6
|
-
export const PROJECTS_FEATURE_ID = PROJECTS_SYSTEM_ID
|
|
7
|
-
export const PROJECTS_INDEX_SURFACE_ID = 'projects.index' as const
|
|
8
|
-
export const PROJECTS_VIEW_ACTION_ID = 'delivery.projects.view' as const
|
|
9
|
-
|
|
10
|
-
export const SALES_FEATURE_ID = 'crm' as const
|
|
11
|
-
export const PROSPECTING_FEATURE_ID = 'lead-gen' as const
|
|
12
|
-
export const MONITORING_FEATURE_ID = 'monitoring' as const
|
|
13
|
-
export const SETTINGS_FEATURE_ID = 'settings' as const
|
|
14
|
-
export const SEO_FEATURE_ID = 'seo' as const
|
|
15
|
-
export const SALES_SYSTEM_ID = 'sales.crm' as const
|
|
16
|
-
export const PROSPECTING_SYSTEM_ID = 'sales.lead-gen' as const
|
|
17
|
-
export const OPERATIONS_SYSTEM_ID = 'operations' as const
|
|
18
|
-
/** @deprecated Use OPERATIONS_SYSTEM_ID. Scheduled for removal after one publish cycle. */
|
|
19
|
-
export const OPERATIONS_FEATURE_ID = OPERATIONS_SYSTEM_ID
|
|
20
|
-
export const MONITORING_SYSTEM_ID = 'monitoring' as const
|
|
21
|
-
export const SETTINGS_SYSTEM_ID = 'settings' as const
|
|
22
|
-
export const SEO_SYSTEM_ID = 'seo' as const
|
|
23
|
-
|
|
24
|
-
export const SALES_PIPELINE_SURFACE_ID = 'crm.pipeline' as const
|
|
25
|
-
export const PROSPECTING_LISTS_SURFACE_ID = 'lead-gen.lists' as const
|
|
26
|
-
export const OPERATIONS_COMMAND_VIEW_SURFACE_ID = 'knowledge.command-view' as const
|
|
27
|
-
export const SETTINGS_ROLES_SURFACE_ID = 'settings.roles' as const
|
|
1
|
+
export const KNOWLEDGE_FEATURE_ID = 'knowledge' as const
|
|
2
|
+
export const KNOWLEDGE_SYSTEM_ID = 'knowledge' as const
|
|
3
|
+
|
|
4
|
+
export const PROJECTS_SYSTEM_ID = 'platform.projects' as const
|
|
5
|
+
/** @deprecated Use PROJECTS_SYSTEM_ID. Scheduled for removal after one publish cycle. */
|
|
6
|
+
export const PROJECTS_FEATURE_ID = PROJECTS_SYSTEM_ID
|
|
7
|
+
export const PROJECTS_INDEX_SURFACE_ID = 'platform.projects.index' as const
|
|
8
|
+
export const PROJECTS_VIEW_ACTION_ID = 'delivery.projects.view' as const
|
|
9
|
+
|
|
10
|
+
export const SALES_FEATURE_ID = 'crm' as const
|
|
11
|
+
export const PROSPECTING_FEATURE_ID = 'lead-gen' as const
|
|
12
|
+
export const MONITORING_FEATURE_ID = 'monitoring' as const
|
|
13
|
+
export const SETTINGS_FEATURE_ID = 'settings' as const
|
|
14
|
+
export const SEO_FEATURE_ID = 'seo' as const
|
|
15
|
+
export const SALES_SYSTEM_ID = 'sales.crm' as const
|
|
16
|
+
export const PROSPECTING_SYSTEM_ID = 'sales.lead-gen' as const
|
|
17
|
+
export const OPERATIONS_SYSTEM_ID = 'operations' as const
|
|
18
|
+
/** @deprecated Use OPERATIONS_SYSTEM_ID. Scheduled for removal after one publish cycle. */
|
|
19
|
+
export const OPERATIONS_FEATURE_ID = OPERATIONS_SYSTEM_ID
|
|
20
|
+
export const MONITORING_SYSTEM_ID = 'monitoring' as const
|
|
21
|
+
export const SETTINGS_SYSTEM_ID = 'settings' as const
|
|
22
|
+
export const SEO_SYSTEM_ID = 'seo' as const
|
|
23
|
+
|
|
24
|
+
export const SALES_PIPELINE_SURFACE_ID = 'crm.pipeline' as const
|
|
25
|
+
export const PROSPECTING_LISTS_SURFACE_ID = 'lead-gen.lists' as const
|
|
26
|
+
export const OPERATIONS_COMMAND_VIEW_SURFACE_ID = 'knowledge.command-view' as const
|
|
27
|
+
export const SETTINGS_ROLES_SURFACE_ID = 'settings.roles' as const
|
|
@@ -55,6 +55,7 @@ export interface OmCrossRefIndex {
|
|
|
55
55
|
*/
|
|
56
56
|
systemsById: Map<string, unknown>
|
|
57
57
|
resourceIds: Set<string>
|
|
58
|
+
clientIds: Set<string>
|
|
58
59
|
knowledgeIds: Set<string>
|
|
59
60
|
roleIds: Set<string>
|
|
60
61
|
goalIds: Set<string>
|
|
@@ -82,6 +83,7 @@ export function buildOmCrossRefIndex(model: OrganizationModel): OmCrossRefIndex
|
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
const resourceIds = new Set(Object.keys(model.resources ?? {}))
|
|
86
|
+
const clientIds = new Set(Object.keys(model.clients ?? {}))
|
|
85
87
|
const knowledgeIds = new Set(Object.keys(model.knowledge ?? {}))
|
|
86
88
|
const roleIds = new Set(Object.keys(model.roles ?? {}))
|
|
87
89
|
const goalIds = new Set(Object.keys(model.goals ?? {}))
|
|
@@ -121,6 +123,7 @@ export function buildOmCrossRefIndex(model: OrganizationModel): OmCrossRefIndex
|
|
|
121
123
|
return {
|
|
122
124
|
systemsById,
|
|
123
125
|
resourceIds,
|
|
126
|
+
clientIds,
|
|
124
127
|
knowledgeIds,
|
|
125
128
|
roleIds,
|
|
126
129
|
goalIds,
|
|
@@ -147,6 +150,7 @@ export function buildOmCrossRefIndex(model: OrganizationModel): OmCrossRefIndex
|
|
|
147
150
|
*/
|
|
148
151
|
export function knowledgeTargetExists(index: OmCrossRefIndex, kind: string, id: string): boolean {
|
|
149
152
|
if (kind === 'system') return index.systemsById.has(id)
|
|
153
|
+
if (kind === 'client') return index.clientIds.has(id)
|
|
150
154
|
if (kind === 'resource') return index.resourceIds.has(id)
|
|
151
155
|
if (kind === 'knowledge') return index.knowledgeIds.has(id)
|
|
152
156
|
if (kind === 'stage') return index.stageIds.has(id)
|
|
@@ -23,7 +23,7 @@ import { DEFAULT_ORGANIZATION_MODEL_GOALS } from './domains/goals'
|
|
|
23
23
|
import { DEFAULT_ORGANIZATION_MODEL_RESOURCES } from './domains/resources'
|
|
24
24
|
import { DEFAULT_ORGANIZATION_MODEL_TOPOLOGY } from './domains/topology'
|
|
25
25
|
import { DEFAULT_ORGANIZATION_MODEL_POLICIES } from './domains/policies'
|
|
26
|
-
import { DEFAULT_ORGANIZATION_MODEL_DOMAIN_METADATA } from './schema'
|
|
26
|
+
import { DEFAULT_ORGANIZATION_MODEL_CLIENTS, DEFAULT_ORGANIZATION_MODEL_DOMAIN_METADATA } from './schema'
|
|
27
27
|
import { DEFAULT_ONTOLOGY_SCOPE } from './ontology'
|
|
28
28
|
import type { OrganizationModelNavigation } from './types'
|
|
29
29
|
|
|
@@ -32,7 +32,6 @@ import type { OrganizationModelNavigation } from './types'
|
|
|
32
32
|
export const DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE = {} as const
|
|
33
33
|
|
|
34
34
|
export const DEFAULT_ORGANIZATION_MODEL_ENTITIES = {}
|
|
35
|
-
|
|
36
35
|
const DEFAULT_ORGANIZATION_MODEL_NAVIGATION: OrganizationModelNavigation = {
|
|
37
36
|
sidebar: {
|
|
38
37
|
primary: {},
|
|
@@ -47,6 +46,7 @@ export const DEFAULT_ORGANIZATION_MODEL: OrganizationModel = {
|
|
|
47
46
|
branding: DEFAULT_ORGANIZATION_MODEL_BRANDING,
|
|
48
47
|
navigation: DEFAULT_ORGANIZATION_MODEL_NAVIGATION,
|
|
49
48
|
identity: DEFAULT_ORGANIZATION_MODEL_IDENTITY,
|
|
49
|
+
clients: DEFAULT_ORGANIZATION_MODEL_CLIENTS,
|
|
50
50
|
customers: DEFAULT_ORGANIZATION_MODEL_CUSTOMERS,
|
|
51
51
|
offerings: DEFAULT_ORGANIZATION_MODEL_OFFERINGS,
|
|
52
52
|
roles: DEFAULT_ORGANIZATION_MODEL_ROLES,
|
|
@@ -281,16 +281,33 @@ export function buildOrganizationGraph(input: BuildOrganizationGraphInput): Orga
|
|
|
281
281
|
label: 'Organization Model'
|
|
282
282
|
}
|
|
283
283
|
pushUniqueNode(nodes, nodeIds, organizationNode)
|
|
284
|
-
const systemsWithPaths = listAllSystems(organizationModel)
|
|
285
|
-
const systemPathByRef = new Map<string, string>()
|
|
284
|
+
const systemsWithPaths = listAllSystems(organizationModel)
|
|
285
|
+
const systemPathByRef = new Map<string, string>()
|
|
286
286
|
for (const { path, system } of systemsWithPaths) {
|
|
287
287
|
systemPathByRef.set(path, path)
|
|
288
288
|
systemPathByRef.set(system.id, path)
|
|
289
289
|
}
|
|
290
|
-
const validSystemRefs = new Set(systemPathByRef.keys())
|
|
291
|
-
const systemNodeId = (systemRef: string) => nodeId('system', systemPathByRef.get(systemRef) ?? systemRef)
|
|
292
|
-
|
|
293
|
-
|
|
290
|
+
const validSystemRefs = new Set(systemPathByRef.keys())
|
|
291
|
+
const systemNodeId = (systemRef: string) => nodeId('system', systemPathByRef.get(systemRef) ?? systemRef)
|
|
292
|
+
|
|
293
|
+
for (const client of Object.values(organizationModel.clients).sort((a, b) => a.slug.localeCompare(b.slug))) {
|
|
294
|
+
const id = nodeId('client', client.id)
|
|
295
|
+
pushUniqueNode(nodes, nodeIds, {
|
|
296
|
+
id,
|
|
297
|
+
kind: 'client',
|
|
298
|
+
label: client.name,
|
|
299
|
+
sourceId: client.id,
|
|
300
|
+
description: client.identity.clientBrief || undefined
|
|
301
|
+
})
|
|
302
|
+
pushUniqueEdge(edges, edgeIds, {
|
|
303
|
+
id: edgeId('contains', organizationNode.id, id),
|
|
304
|
+
kind: 'contains',
|
|
305
|
+
sourceId: organizationNode.id,
|
|
306
|
+
targetId: id
|
|
307
|
+
})
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function topologyNodeId(ref: OmTopologyNodeRef): string {
|
|
294
311
|
if (ref.kind === 'system') return systemNodeId(ref.id)
|
|
295
312
|
if (ref.kind === 'resource') return nodeId('resource', ref.id)
|
|
296
313
|
if (ref.kind === 'ontology') return ontologyGraphNodeId(ref.id)
|
|
@@ -3,9 +3,10 @@ import { DescriptionSchema, IconNameSchema, LabelSchema } from '../domains/share
|
|
|
3
3
|
import { OrganizationModelSchema } from '../schema'
|
|
4
4
|
|
|
5
5
|
export const OrganizationGraphNodeKindSchema = z.enum([
|
|
6
|
-
'organization',
|
|
7
|
-
'system',
|
|
8
|
-
'
|
|
6
|
+
'organization',
|
|
7
|
+
'system',
|
|
8
|
+
'client',
|
|
9
|
+
'role',
|
|
9
10
|
'action',
|
|
10
11
|
'entity',
|
|
11
12
|
'event',
|
|
@@ -4,9 +4,10 @@ import type { RelationshipType } from '../../platform/registry/command-view'
|
|
|
4
4
|
import type { OrganizationModelIconToken } from '../icons'
|
|
5
5
|
|
|
6
6
|
export type OrganizationGraphNodeKind =
|
|
7
|
-
| 'organization'
|
|
8
|
-
| 'system'
|
|
9
|
-
| '
|
|
7
|
+
| 'organization'
|
|
8
|
+
| 'system'
|
|
9
|
+
| 'client'
|
|
10
|
+
| 'role'
|
|
10
11
|
| 'action'
|
|
11
12
|
| 'entity'
|
|
12
13
|
| 'event'
|
|
@@ -62,6 +62,21 @@ export function listDomain<T extends { id: string; order: number }>(record: Reco
|
|
|
62
62
|
return Object.values(record).sort((a, b) => a.order - b.order)
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
export function listClientProfiles(model: OrganizationModel): OrganizationModel['clients'][string][] {
|
|
66
|
+
return Object.values(model.clients ?? {}).sort((a, b) => a.slug.localeCompare(b.slug))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function getClientProfile(model: OrganizationModel, id: string): OrganizationModel['clients'][string] | undefined {
|
|
70
|
+
return model.clients?.[id]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function getClientProfileBySlug(
|
|
74
|
+
model: OrganizationModel,
|
|
75
|
+
slug: string
|
|
76
|
+
): OrganizationModel['clients'][string] | undefined {
|
|
77
|
+
return Object.values(model.clients ?? {}).find((client) => client.slug === slug)
|
|
78
|
+
}
|
|
79
|
+
|
|
65
80
|
export function findById(
|
|
66
81
|
systems: Record<string, OrganizationModelSystemEntry>,
|
|
67
82
|
id: string
|
|
@@ -24,6 +24,15 @@ export {
|
|
|
24
24
|
} from './ontology'
|
|
25
25
|
export {
|
|
26
26
|
DEFAULT_ORGANIZATION_MODEL_DOMAIN_METADATA,
|
|
27
|
+
ClientProfileBrandingSchema,
|
|
28
|
+
ClientProfileIdentitySchema,
|
|
29
|
+
ClientProfileLinksSchema,
|
|
30
|
+
ClientProfilePromptsSchema,
|
|
31
|
+
ClientProfileSchema,
|
|
32
|
+
ClientProfileSourceSchema,
|
|
33
|
+
ClientProfileStatusSchema,
|
|
34
|
+
ClientProfileWorkspaceSchema,
|
|
35
|
+
ClientProfilesDomainSchema,
|
|
27
36
|
OrganizationModelDomainKeySchema,
|
|
28
37
|
OrganizationModelDomainMetadataByDomainSchema,
|
|
29
38
|
OrganizationModelDomainMetadataSchema,
|
|
@@ -237,7 +246,7 @@ export { defineOrganizationModel, resolveOrganizationModel, resolveOrganizationM
|
|
|
237
246
|
export type { ResolvedSystemEntry, ResolvedOrganizationModel } from './resolve'
|
|
238
247
|
export { createFoundationOrganizationModel } from './foundation'
|
|
239
248
|
export { scaffoldOrganizationModel } from './scaffolders'
|
|
240
|
-
export { defineDomainRecord, listAllSystems } from './helpers'
|
|
249
|
+
export { defineDomainRecord, getClientProfile, getClientProfileBySlug, listAllSystems, listClientProfiles } from './helpers'
|
|
241
250
|
export { projectOrganizationSurfaces, validateOrganizationSurfaceProjection } from './surface-projection'
|
|
242
251
|
export type {
|
|
243
252
|
BaseOmScaffoldSpec,
|
|
@@ -283,11 +292,20 @@ export type {
|
|
|
283
292
|
|
|
284
293
|
export type {
|
|
285
294
|
DeepPartial,
|
|
295
|
+
ClientProfile,
|
|
296
|
+
ClientProfileBranding,
|
|
297
|
+
ClientProfileIdentity,
|
|
298
|
+
ClientProfileLinks,
|
|
299
|
+
ClientProfilePrompts,
|
|
300
|
+
ClientProfileSource,
|
|
301
|
+
ClientProfileStatus,
|
|
302
|
+
ClientProfileWorkspace,
|
|
286
303
|
KnowledgeLink,
|
|
287
304
|
KnowledgeTargetKind,
|
|
288
305
|
KnowledgeTargetRef,
|
|
289
306
|
OrganizationModel,
|
|
290
307
|
OrganizationModelBranding,
|
|
308
|
+
OrganizationModelClients,
|
|
291
309
|
OrganizationModelCustomerFirmographics,
|
|
292
310
|
OrganizationModelCustomers,
|
|
293
311
|
OrganizationModelCustomerSegment,
|