@elevasis/core 0.10.0 → 0.11.1
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 +69 -159
- package/dist/index.js +324 -613
- package/dist/organization-model/index.d.ts +69 -159
- package/dist/organization-model/index.js +324 -613
- package/dist/test-utils/index.d.ts +192 -45
- package/dist/test-utils/index.js +260 -600
- package/package.json +1 -1
- package/src/__tests__/template-core-compatibility.test.ts +73 -91
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +94 -182
- package/src/auth/multi-tenancy/index.ts +20 -17
- package/src/auth/multi-tenancy/memberships/api-schemas.ts +142 -126
- package/src/auth/multi-tenancy/memberships/index.ts +26 -22
- package/src/auth/multi-tenancy/permissions.test.ts +42 -0
- package/src/auth/multi-tenancy/permissions.ts +104 -0
- package/src/organization-model/README.md +102 -97
- package/src/organization-model/__tests__/defaults.test.ts +19 -6
- package/src/organization-model/__tests__/domains/resource-mappings.test.ts +24 -93
- package/src/organization-model/__tests__/graph.test.ts +82 -894
- package/src/organization-model/__tests__/resolve.test.ts +59 -690
- package/src/organization-model/__tests__/schema.test.ts +83 -407
- package/src/organization-model/contracts.ts +4 -3
- package/src/organization-model/defaults.ts +277 -141
- package/src/organization-model/domains/features.ts +31 -22
- package/src/organization-model/domains/navigation.ts +27 -20
- package/src/organization-model/foundation.ts +42 -54
- package/src/organization-model/graph/build.ts +42 -217
- package/src/organization-model/graph/index.ts +4 -4
- package/src/organization-model/graph/link.ts +10 -0
- package/src/organization-model/graph/schema.ts +21 -16
- package/src/organization-model/graph/types.ts +10 -10
- package/src/organization-model/helpers.ts +74 -0
- package/src/organization-model/index.ts +7 -7
- package/src/organization-model/organization-graph.mdx +89 -272
- package/src/organization-model/organization-model.mdx +152 -320
- package/src/organization-model/published.ts +20 -19
- package/src/organization-model/resolve.ts +8 -33
- package/src/organization-model/schema.ts +63 -205
- package/src/organization-model/types.ts +12 -11
- package/src/platform/constants/versions.ts +3 -3
- package/src/platform/registry/__tests__/command-view.test.ts +6 -5
- package/src/platform/registry/__tests__/resource-link.test.ts +30 -0
- package/src/platform/registry/__tests__/resource-registry.integration.test.ts +15 -15
- package/src/platform/registry/command-view.ts +10 -12
- package/src/platform/registry/index.ts +93 -93
- package/src/platform/registry/resource-link.ts +32 -0
- package/src/platform/registry/resource-registry.ts +917 -876
- package/src/platform/registry/serialization.ts +56 -73
- package/src/platform/registry/serialized-types.ts +17 -12
- package/src/platform/registry/types.ts +14 -43
- package/src/reference/_generated/contracts.md +94 -182
- package/src/reference/glossary.md +71 -105
- package/src/scaffold-registry/__tests__/index.test.ts +125 -1
- package/src/scaffold-registry/__tests__/schema.test.ts +48 -20
- package/src/scaffold-registry/index.ts +236 -188
- package/src/scaffold-registry/schema.ts +47 -22
- package/src/supabase/database.types.ts +2880 -2719
- package/src/test-utils/fixtures/memberships.ts +82 -80
- package/src/platform/registry/domains.ts +0 -165
|
@@ -1,36 +1,34 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
|
-
import { OrganizationModelBrandingSchema } from './domains/branding'
|
|
3
|
-
import { OrganizationModelSalesSchema } from './domains/sales'
|
|
4
|
-
import { OrganizationModelProjectsSchema } from './domains/projects'
|
|
5
|
-
import { FeatureSchema } from './domains/features'
|
|
6
|
-
import { OrganizationModelProspectingSchema } from './domains/prospecting'
|
|
7
|
-
import { OrganizationModelNavigationSchema } from './domains/navigation'
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { OfferingsDomainSchema, DEFAULT_ORGANIZATION_MODEL_OFFERINGS } from './domains/offerings'
|
|
2
|
+
import { OrganizationModelBrandingSchema } from './domains/branding'
|
|
3
|
+
import { OrganizationModelSalesSchema } from './domains/sales'
|
|
4
|
+
import { OrganizationModelProjectsSchema } from './domains/projects'
|
|
5
|
+
import { FeatureSchema } from './domains/features'
|
|
6
|
+
import { OrganizationModelProspectingSchema } from './domains/prospecting'
|
|
7
|
+
import { OrganizationModelNavigationSchema } from './domains/navigation'
|
|
8
|
+
import { IdentityDomainSchema, DEFAULT_ORGANIZATION_MODEL_IDENTITY } from './domains/identity'
|
|
9
|
+
import { CustomersDomainSchema, DEFAULT_ORGANIZATION_MODEL_CUSTOMERS } from './domains/customers'
|
|
10
|
+
import { OfferingsDomainSchema, DEFAULT_ORGANIZATION_MODEL_OFFERINGS } from './domains/offerings'
|
|
12
11
|
import { RolesDomainSchema, DEFAULT_ORGANIZATION_MODEL_ROLES } from './domains/roles'
|
|
13
12
|
import { GoalsDomainSchema, DEFAULT_ORGANIZATION_MODEL_GOALS } from './domains/goals'
|
|
14
13
|
import { OperationsDomainSchema } from './domains/operations'
|
|
15
14
|
import { StatusesDomainSchema } from './domains/statuses'
|
|
16
15
|
|
|
17
16
|
const OrganizationModelSchemaBase = z.object({
|
|
18
|
-
version: z.literal(1).default(1),
|
|
19
|
-
features: z.array(FeatureSchema).default([]),
|
|
20
|
-
branding: OrganizationModelBrandingSchema,
|
|
21
|
-
navigation: OrganizationModelNavigationSchema,
|
|
22
|
-
sales: OrganizationModelSalesSchema,
|
|
23
|
-
prospecting: OrganizationModelProspectingSchema,
|
|
24
|
-
projects: OrganizationModelProjectsSchema,
|
|
17
|
+
version: z.literal(1).default(1),
|
|
18
|
+
features: z.array(FeatureSchema).default([]),
|
|
19
|
+
branding: OrganizationModelBrandingSchema,
|
|
20
|
+
navigation: OrganizationModelNavigationSchema.default({ surfaces: [], groups: [] }),
|
|
21
|
+
sales: OrganizationModelSalesSchema,
|
|
22
|
+
prospecting: OrganizationModelProspectingSchema,
|
|
23
|
+
projects: OrganizationModelProjectsSchema,
|
|
25
24
|
identity: IdentityDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_IDENTITY),
|
|
26
25
|
customers: CustomersDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_CUSTOMERS),
|
|
27
26
|
offerings: OfferingsDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_OFFERINGS),
|
|
28
27
|
roles: RolesDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_ROLES),
|
|
29
|
-
goals: GoalsDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_GOALS),
|
|
30
|
-
statuses: StatusesDomainSchema.default({ entries: [] }),
|
|
31
|
-
operations: OperationsDomainSchema.default({ entries: [] })
|
|
32
|
-
|
|
33
|
-
})
|
|
28
|
+
goals: GoalsDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_GOALS),
|
|
29
|
+
statuses: StatusesDomainSchema.default({ entries: [] }),
|
|
30
|
+
operations: OperationsDomainSchema.default({ entries: [] })
|
|
31
|
+
})
|
|
34
32
|
|
|
35
33
|
function addIssue(ctx: z.RefinementCtx, path: Array<string | number>, message: string): void {
|
|
36
34
|
ctx.addIssue({
|
|
@@ -40,7 +38,7 @@ function addIssue(ctx: z.RefinementCtx, path: Array<string | number>, message: s
|
|
|
40
38
|
})
|
|
41
39
|
}
|
|
42
40
|
|
|
43
|
-
function collectIds<T extends { id: string }>(
|
|
41
|
+
function collectIds<T extends { id: string }>(
|
|
44
42
|
items: T[],
|
|
45
43
|
ctx: z.RefinementCtx,
|
|
46
44
|
collectionPath: Array<string | number>,
|
|
@@ -57,144 +55,46 @@ function collectIds<T extends { id: string }>(
|
|
|
57
55
|
itemsById.set(item.id, item)
|
|
58
56
|
})
|
|
59
57
|
|
|
60
|
-
return itemsById
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
})
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
// Feature -> Surface bidirectional validation
|
|
104
|
-
model.features.forEach((feature, featureIndex) => {
|
|
105
|
-
feature.surfaceIds.forEach((surfaceId, surfaceIndex) => {
|
|
106
|
-
const surface = surfacesById.get(surfaceId)
|
|
107
|
-
if (!surface) {
|
|
108
|
-
addIssue(
|
|
109
|
-
ctx,
|
|
110
|
-
['features', featureIndex, 'surfaceIds', surfaceIndex],
|
|
111
|
-
`Feature "${feature.id}" references unknown surface "${surfaceId}"`
|
|
112
|
-
)
|
|
113
|
-
return
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (!surface.featureIds.includes(feature.id)) {
|
|
117
|
-
addIssue(
|
|
118
|
-
ctx,
|
|
119
|
-
['features', featureIndex, 'surfaceIds', surfaceIndex],
|
|
120
|
-
`Feature "${feature.id}" references surface "${surfaceId}" but that surface does not include feature "${feature.id}"`
|
|
121
|
-
)
|
|
122
|
-
}
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
// Feature -> Resource bidirectional validation
|
|
126
|
-
feature.resourceIds.forEach((resourceId, resourceIndex) => {
|
|
127
|
-
const resourceMapping = resourceMappingsByResourceId.get(resourceId)
|
|
128
|
-
if (!resourceMapping) {
|
|
129
|
-
addIssue(
|
|
130
|
-
ctx,
|
|
131
|
-
['features', featureIndex, 'resourceIds', resourceIndex],
|
|
132
|
-
`Feature "${feature.id}" references unknown resource "${resourceId}"`
|
|
133
|
-
)
|
|
134
|
-
return
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (!resourceMapping.featureIds.includes(feature.id)) {
|
|
138
|
-
addIssue(
|
|
139
|
-
ctx,
|
|
140
|
-
['features', featureIndex, 'resourceIds', resourceIndex],
|
|
141
|
-
`Feature "${feature.id}" references resource "${resourceId}" but that resource mapping does not include feature "${feature.id}"`
|
|
142
|
-
)
|
|
143
|
-
}
|
|
144
|
-
})
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
// Surface -> Feature bidirectional validation and other surface refs
|
|
148
|
-
model.navigation.surfaces.forEach((surface, surfaceIndex) => {
|
|
149
|
-
if (surface.parentId && !surfacesById.has(surface.parentId)) {
|
|
150
|
-
addIssue(
|
|
151
|
-
ctx,
|
|
152
|
-
['navigation', 'surfaces', surfaceIndex, 'parentId'],
|
|
153
|
-
`Surface "${surface.id}" references unknown parent surface "${surface.parentId}"`
|
|
154
|
-
)
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
surface.featureIds.forEach((featureId, featureIndex) => {
|
|
158
|
-
const feature = featuresById.get(featureId)
|
|
159
|
-
if (!feature) {
|
|
160
|
-
addIssue(
|
|
161
|
-
ctx,
|
|
162
|
-
['navigation', 'surfaces', surfaceIndex, 'featureIds', featureIndex],
|
|
163
|
-
`Surface "${surface.id}" references unknown feature "${featureId}"`
|
|
164
|
-
)
|
|
165
|
-
return
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (!feature.surfaceIds.includes(surface.id)) {
|
|
169
|
-
addIssue(
|
|
170
|
-
ctx,
|
|
171
|
-
['navigation', 'surfaces', surfaceIndex, 'featureIds', featureIndex],
|
|
172
|
-
`Surface "${surface.id}" references feature "${featureId}" but that feature does not include surface "${surface.id}"`
|
|
173
|
-
)
|
|
174
|
-
}
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
// Surface -> Resource bidirectional validation
|
|
178
|
-
surface.resourceIds.forEach((resourceId, resourceIndex) => {
|
|
179
|
-
const resourceMapping = resourceMappingsByResourceId.get(resourceId)
|
|
180
|
-
if (!resourceMapping) {
|
|
181
|
-
addIssue(
|
|
182
|
-
ctx,
|
|
183
|
-
['navigation', 'surfaces', surfaceIndex, 'resourceIds', resourceIndex],
|
|
184
|
-
`Surface "${surface.id}" references unknown resource "${resourceId}"`
|
|
185
|
-
)
|
|
186
|
-
return
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (!resourceMapping.surfaceIds.includes(surface.id)) {
|
|
190
|
-
addIssue(
|
|
191
|
-
ctx,
|
|
192
|
-
['navigation', 'surfaces', surfaceIndex, 'resourceIds', resourceIndex],
|
|
193
|
-
`Surface "${surface.id}" references resource "${resourceId}" but that surface does not include resource "${surface.id}"`
|
|
194
|
-
)
|
|
195
|
-
}
|
|
196
|
-
})
|
|
197
|
-
})
|
|
58
|
+
return itemsById
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const LEGACY_FEATURE_ALIASES = new Map<string, string>([
|
|
62
|
+
['crm', 'sales.crm'],
|
|
63
|
+
['lead-gen', 'sales.lead-gen'],
|
|
64
|
+
['submitted-requests', 'monitoring.submitted-requests']
|
|
65
|
+
])
|
|
66
|
+
|
|
67
|
+
function hasFeature(featuresById: Map<string, unknown>, featureId: string): boolean {
|
|
68
|
+
return featuresById.has(featureId) || featuresById.has(LEGACY_FEATURE_ALIASES.get(featureId) ?? '')
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const OrganizationModelSchema = OrganizationModelSchemaBase.superRefine((model, ctx) => {
|
|
72
|
+
const featuresById = collectIds(model.features, ctx, ['features'], 'Feature')
|
|
73
|
+
model.features.forEach((feature, featureIndex) => {
|
|
74
|
+
const segments = feature.id.split('.')
|
|
75
|
+
if (segments.length > 1) {
|
|
76
|
+
const parentId = segments.slice(0, -1).join('.')
|
|
77
|
+
if (!featuresById.has(parentId)) {
|
|
78
|
+
addIssue(ctx, ['features', featureIndex, 'id'], `Feature "${feature.id}" references unknown parent "${parentId}"`)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const hasChildren = model.features.some(
|
|
83
|
+
(candidate) => candidate.id.startsWith(`${feature.id}.`) && candidate.id !== feature.id
|
|
84
|
+
)
|
|
85
|
+
if (hasChildren && feature.enabled) {
|
|
86
|
+
const hasEnabledDescendant = model.features.some(
|
|
87
|
+
(candidate) => candidate.id.startsWith(`${feature.id}.`) && candidate.enabled
|
|
88
|
+
)
|
|
89
|
+
if (!hasEnabledDescendant) {
|
|
90
|
+
addIssue(
|
|
91
|
+
ctx,
|
|
92
|
+
['features', featureIndex, 'enabled'],
|
|
93
|
+
`Feature "${feature.id}" is enabled but has no enabled descendants`
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
})
|
|
198
98
|
|
|
199
99
|
// Offerings -> CustomerSegment cross-ref: targetSegmentIds must resolve
|
|
200
100
|
const segmentsById = new Map(model.customers.segments.map((seg) => [seg.id, seg]))
|
|
@@ -210,7 +110,7 @@ export const OrganizationModelSchema = OrganizationModelSchemaBase.superRefine((
|
|
|
210
110
|
})
|
|
211
111
|
|
|
212
112
|
// Offerings -> Feature cross-ref: deliveryFeatureId must resolve (when present)
|
|
213
|
-
if (product.deliveryFeatureId !== undefined && !featuresById
|
|
113
|
+
if (product.deliveryFeatureId !== undefined && !hasFeature(featuresById, product.deliveryFeatureId)) {
|
|
214
114
|
addIssue(
|
|
215
115
|
ctx,
|
|
216
116
|
['offerings', 'products', productIndex, 'deliveryFeatureId'],
|
|
@@ -242,46 +142,4 @@ export const OrganizationModelSchema = OrganizationModelSchemaBase.superRefine((
|
|
|
242
142
|
}
|
|
243
143
|
})
|
|
244
144
|
|
|
245
|
-
|
|
246
|
-
model.resourceMappings.forEach((resourceMapping, resourceIndex) => {
|
|
247
|
-
resourceMapping.featureIds.forEach((featureId, featureIndex) => {
|
|
248
|
-
const feature = featuresById.get(featureId)
|
|
249
|
-
if (!feature) {
|
|
250
|
-
addIssue(
|
|
251
|
-
ctx,
|
|
252
|
-
['resourceMappings', resourceIndex, 'featureIds', featureIndex],
|
|
253
|
-
`Resource mapping "${resourceMapping.id}" references unknown feature "${featureId}"`
|
|
254
|
-
)
|
|
255
|
-
return
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (!feature.resourceIds.includes(resourceMapping.resourceId)) {
|
|
259
|
-
addIssue(
|
|
260
|
-
ctx,
|
|
261
|
-
['resourceMappings', resourceIndex, 'featureIds', featureIndex],
|
|
262
|
-
`Resource mapping "${resourceMapping.id}" references feature "${featureId}" but that feature does not include resource "${resourceMapping.resourceId}"`
|
|
263
|
-
)
|
|
264
|
-
}
|
|
265
|
-
})
|
|
266
|
-
|
|
267
|
-
resourceMapping.surfaceIds.forEach((surfaceId, surfaceIndex) => {
|
|
268
|
-
const surface = surfacesById.get(surfaceId)
|
|
269
|
-
if (!surface) {
|
|
270
|
-
addIssue(
|
|
271
|
-
ctx,
|
|
272
|
-
['resourceMappings', resourceIndex, 'surfaceIds', surfaceIndex],
|
|
273
|
-
`Resource mapping "${resourceMapping.id}" references unknown surface "${surfaceId}"`
|
|
274
|
-
)
|
|
275
|
-
return
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
if (!surface.resourceIds.includes(resourceMapping.resourceId)) {
|
|
279
|
-
addIssue(
|
|
280
|
-
ctx,
|
|
281
|
-
['resourceMappings', resourceIndex, 'surfaceIds', surfaceIndex],
|
|
282
|
-
`Resource mapping "${resourceMapping.id}" references surface "${surfaceId}" but that surface does not include resource "${resourceMapping.resourceId}"`
|
|
283
|
-
)
|
|
284
|
-
}
|
|
285
|
-
})
|
|
286
|
-
})
|
|
287
|
-
})
|
|
145
|
+
})
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { z } from 'zod'
|
|
2
2
|
import { OrganizationModelBrandingSchema } from './domains/branding'
|
|
3
3
|
import { OrganizationModelSalesSchema } from './domains/sales'
|
|
4
|
-
import { OrganizationModelProjectsSchema } from './domains/projects'
|
|
5
|
-
import { FeatureSchema } from './domains/features'
|
|
6
|
-
import { OrganizationModelProspectingSchema } from './domains/prospecting'
|
|
7
|
-
import { OrganizationModelNavigationSchema, SurfaceDefinitionSchema } from './domains/navigation'
|
|
8
|
-
import {
|
|
4
|
+
import { OrganizationModelProjectsSchema } from './domains/projects'
|
|
5
|
+
import { FeatureSchema, NodeIdPathSchema, NodeIdStringSchema } from './domains/features'
|
|
6
|
+
import { OrganizationModelProspectingSchema } from './domains/prospecting'
|
|
7
|
+
import { OrganizationModelNavigationSchema, SurfaceDefinitionSchema } from './domains/navigation'
|
|
8
|
+
import { TechStackEntrySchema } from './domains/shared'
|
|
9
9
|
import { OperationsDomainSchema, OperationEntrySchema, OperationSemanticClassSchema } from './domains/operations'
|
|
10
10
|
import { StatusesDomainSchema, StatusEntrySchema, StatusSemanticClassSchema } from './domains/statuses'
|
|
11
11
|
import { CustomersDomainSchema, CustomerSegmentSchema, FirmographicsSchema } from './domains/customers'
|
|
@@ -18,12 +18,13 @@ export type OrganizationModel = z.infer<typeof OrganizationModelSchema>
|
|
|
18
18
|
export type OrganizationModelBranding = z.infer<typeof OrganizationModelBrandingSchema>
|
|
19
19
|
export type OrganizationModelSales = z.infer<typeof OrganizationModelSalesSchema>
|
|
20
20
|
export type OrganizationModelProspecting = z.infer<typeof OrganizationModelProspectingSchema>
|
|
21
|
-
export type OrganizationModelProjects = z.infer<typeof OrganizationModelProjectsSchema>
|
|
22
|
-
export type OrganizationModelFeature = z.infer<typeof FeatureSchema>
|
|
23
|
-
export type
|
|
24
|
-
export type
|
|
25
|
-
export type
|
|
26
|
-
export type
|
|
21
|
+
export type OrganizationModelProjects = z.infer<typeof OrganizationModelProjectsSchema>
|
|
22
|
+
export type OrganizationModelFeature = z.infer<typeof FeatureSchema>
|
|
23
|
+
export type NodeIdPath = z.infer<typeof NodeIdPathSchema>
|
|
24
|
+
export type NodeIdString = z.infer<typeof NodeIdStringSchema>
|
|
25
|
+
export type OrganizationModelNavigation = z.infer<typeof OrganizationModelNavigationSchema>
|
|
26
|
+
export type OrganizationModelSurface = z.infer<typeof SurfaceDefinitionSchema>
|
|
27
|
+
export type OrganizationModelTechStackEntry = z.infer<typeof TechStackEntrySchema>
|
|
27
28
|
export type OrganizationModelStatuses = z.infer<typeof StatusesDomainSchema>
|
|
28
29
|
export type OrganizationModelStatusEntry = z.infer<typeof StatusEntrySchema>
|
|
29
30
|
export type OrganizationModelStatusSemanticClass = z.infer<typeof StatusSemanticClassSchema>
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export const VERSION = {
|
|
2
|
-
CURRENT: '1.6.
|
|
3
|
-
}
|
|
1
|
+
export const VERSION = {
|
|
2
|
+
CURRENT: '1.6.10'
|
|
3
|
+
}
|
|
@@ -37,10 +37,11 @@ const mockAgent: CommandViewAgent = {
|
|
|
37
37
|
status: 'dev',
|
|
38
38
|
modelProvider: 'anthropic',
|
|
39
39
|
modelId: 'claude-sonnet-4-20250514',
|
|
40
|
-
toolCount: 5,
|
|
41
|
-
hasKnowledgeMap: true,
|
|
42
|
-
hasMemory: false,
|
|
43
|
-
|
|
40
|
+
toolCount: 5,
|
|
41
|
+
hasKnowledgeMap: true,
|
|
42
|
+
hasMemory: false,
|
|
43
|
+
links: [{ nodeId: 'feature:sales.crm', kind: 'operates-on' }],
|
|
44
|
+
category: 'production'
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
const mockWorkflow: CommandViewWorkflow = {
|
|
@@ -52,7 +53,7 @@ const mockWorkflow: CommandViewWorkflow = {
|
|
|
52
53
|
status: 'prod',
|
|
53
54
|
stepCount: 3,
|
|
54
55
|
entryPoint: 'start',
|
|
55
|
-
|
|
56
|
+
links: [{ nodeId: 'feature:monitoring.submitted-requests', kind: 'operates-on' }]
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
const mockTrigger: TriggerDefinition = {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { ResourceCategorySchema, ResourceLinkSchema, kindPrefix, validateNodeId } from '../resource-link'
|
|
3
|
+
import type { OrganizationGraph } from '../../../organization-model/graph/types'
|
|
4
|
+
|
|
5
|
+
describe('resource-link', () => {
|
|
6
|
+
it('parses resource links and categories', () => {
|
|
7
|
+
expect(ResourceLinkSchema.parse({ nodeId: 'feature:sales.crm', kind: 'operates-on' })).toEqual({
|
|
8
|
+
nodeId: 'feature:sales.crm',
|
|
9
|
+
kind: 'operates-on'
|
|
10
|
+
})
|
|
11
|
+
expect(ResourceCategorySchema.parse('diagnostic')).toBe('diagnostic')
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('rejects node ids without a kind prefix', () => {
|
|
15
|
+
expect(() => ResourceLinkSchema.parse({ nodeId: 'sales.crm', kind: 'operates-on' })).toThrow()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('builds and validates graph node ids', () => {
|
|
19
|
+
const graph: OrganizationGraph = {
|
|
20
|
+
version: 1,
|
|
21
|
+
organizationModelVersion: 1,
|
|
22
|
+
nodes: [{ id: 'feature:sales.crm', kind: 'feature', label: 'CRM', sourceId: 'sales.crm' }],
|
|
23
|
+
edges: []
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
expect(kindPrefix('feature', 'sales.crm')).toBe('feature:sales.crm')
|
|
27
|
+
expect(() => validateNodeId('feature:sales.crm', graph)).not.toThrow()
|
|
28
|
+
expect(() => validateNodeId('feature:missing', graph)).toThrow(/Unknown organization graph node/)
|
|
29
|
+
})
|
|
30
|
+
})
|
|
@@ -35,16 +35,16 @@ describe('ResourceRegistry Integration', () => {
|
|
|
35
35
|
const createMockWorkflow = (
|
|
36
36
|
resourceId: string,
|
|
37
37
|
status: 'dev' | 'prod' = 'dev',
|
|
38
|
-
|
|
38
|
+
links?: Array<{ nodeId: string; kind: 'operates-on' }>
|
|
39
39
|
): WorkflowDefinition => ({
|
|
40
40
|
config: {
|
|
41
41
|
resourceId,
|
|
42
42
|
name: `Workflow ${resourceId}`,
|
|
43
43
|
description: `Test workflow ${resourceId}`,
|
|
44
|
-
version: '1.0.0',
|
|
45
|
-
type: 'workflow',
|
|
46
|
-
status,
|
|
47
|
-
|
|
44
|
+
version: '1.0.0',
|
|
45
|
+
type: 'workflow',
|
|
46
|
+
status,
|
|
47
|
+
links
|
|
48
48
|
},
|
|
49
49
|
contract: {
|
|
50
50
|
inputSchema: z.object({ data: z.string() }),
|
|
@@ -67,17 +67,17 @@ describe('ResourceRegistry Integration', () => {
|
|
|
67
67
|
const createMockAgent = (
|
|
68
68
|
resourceId: string,
|
|
69
69
|
status: 'dev' | 'prod' = 'dev',
|
|
70
|
-
|
|
70
|
+
links?: Array<{ nodeId: string; kind: 'operates-on' }>
|
|
71
71
|
): AgentDefinition => ({
|
|
72
72
|
config: {
|
|
73
73
|
resourceId,
|
|
74
74
|
name: `Agent ${resourceId}`,
|
|
75
75
|
description: `Test agent ${resourceId}`,
|
|
76
76
|
version: '1.0.0',
|
|
77
|
-
type: 'agent',
|
|
78
|
-
status,
|
|
79
|
-
systemPrompt: 'You are a test agent',
|
|
80
|
-
|
|
77
|
+
type: 'agent',
|
|
78
|
+
status,
|
|
79
|
+
systemPrompt: 'You are a test agent',
|
|
80
|
+
links
|
|
81
81
|
},
|
|
82
82
|
contract: {
|
|
83
83
|
inputSchema: z.object({ query: z.string() }),
|
|
@@ -116,7 +116,7 @@ describe('ResourceRegistry Integration', () => {
|
|
|
116
116
|
description: 'Webhook from Shopify on new orders',
|
|
117
117
|
status: 'prod',
|
|
118
118
|
webhookPath: '/webhooks/shopify/orders',
|
|
119
|
-
|
|
119
|
+
links: [{ nodeId: 'feature:sales.crm', kind: 'operates-on' }]
|
|
120
120
|
},
|
|
121
121
|
{
|
|
122
122
|
resourceId: 'trigger-schedule-support',
|
|
@@ -127,7 +127,7 @@ describe('ResourceRegistry Integration', () => {
|
|
|
127
127
|
description: 'Check for new support tickets every hour',
|
|
128
128
|
status: 'dev',
|
|
129
129
|
schedule: '0 * * * *',
|
|
130
|
-
|
|
130
|
+
links: [{ nodeId: 'feature:monitoring.submitted-requests', kind: 'operates-on' }]
|
|
131
131
|
},
|
|
132
132
|
{
|
|
133
133
|
resourceId: 'trigger-manual-test',
|
|
@@ -150,7 +150,7 @@ describe('ResourceRegistry Integration', () => {
|
|
|
150
150
|
credentialName: 'shopify-prod',
|
|
151
151
|
name: 'Shopify Production',
|
|
152
152
|
description: 'E-commerce platform',
|
|
153
|
-
|
|
153
|
+
links: [{ nodeId: 'feature:sales.crm', kind: 'operates-on' }]
|
|
154
154
|
},
|
|
155
155
|
{
|
|
156
156
|
resourceId: 'integration-zendesk',
|
|
@@ -161,7 +161,7 @@ describe('ResourceRegistry Integration', () => {
|
|
|
161
161
|
credentialName: 'zendesk-dev',
|
|
162
162
|
name: 'Zendesk Support',
|
|
163
163
|
description: 'Support ticket system',
|
|
164
|
-
|
|
164
|
+
links: [{ nodeId: 'feature:monitoring.submitted-requests', kind: 'operates-on' }]
|
|
165
165
|
}
|
|
166
166
|
]
|
|
167
167
|
|
|
@@ -202,7 +202,7 @@ describe('ResourceRegistry Integration', () => {
|
|
|
202
202
|
status: 'prod',
|
|
203
203
|
requestedBy: { agents: ['order-agent'] },
|
|
204
204
|
routesTo: { workflows: ['order-workflow'] },
|
|
205
|
-
|
|
205
|
+
links: [{ nodeId: 'feature:sales.crm', kind: 'operates-on' }]
|
|
206
206
|
},
|
|
207
207
|
{
|
|
208
208
|
resourceId: 'approval-escalation',
|
|
@@ -10,12 +10,11 @@
|
|
|
10
10
|
|
|
11
11
|
import type {
|
|
12
12
|
ResourceDefinition,
|
|
13
|
-
TriggerDefinition,
|
|
14
|
-
IntegrationDefinition,
|
|
15
|
-
ExternalResourceDefinition,
|
|
16
|
-
HumanCheckpointDefinition
|
|
17
|
-
|
|
18
|
-
} from './types'
|
|
13
|
+
TriggerDefinition,
|
|
14
|
+
IntegrationDefinition,
|
|
15
|
+
ExternalResourceDefinition,
|
|
16
|
+
HumanCheckpointDefinition
|
|
17
|
+
} from './types'
|
|
19
18
|
|
|
20
19
|
// ============================================================================
|
|
21
20
|
// Node Types - Resources that appear in the graph
|
|
@@ -102,12 +101,11 @@ export interface CommandViewData {
|
|
|
102
101
|
workflows: CommandViewWorkflow[]
|
|
103
102
|
agents: CommandViewAgent[]
|
|
104
103
|
triggers: TriggerDefinition[]
|
|
105
|
-
integrations: IntegrationDefinition[]
|
|
106
|
-
externalResources: ExternalResourceDefinition[]
|
|
107
|
-
humanCheckpoints: HumanCheckpointDefinition[]
|
|
108
|
-
edges: CommandViewEdge[]
|
|
109
|
-
|
|
110
|
-
}
|
|
104
|
+
integrations: IntegrationDefinition[]
|
|
105
|
+
externalResources: ExternalResourceDefinition[]
|
|
106
|
+
humanCheckpoints: HumanCheckpointDefinition[]
|
|
107
|
+
edges: CommandViewEdge[]
|
|
108
|
+
}
|
|
111
109
|
|
|
112
110
|
// ============================================================================
|
|
113
111
|
// Type Guards
|