@elevasis/sdk 1.27.1 → 1.28.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/cli.cjs +586 -3
- package/dist/index.d.ts +55 -7
- package/dist/index.js +704 -129
- package/dist/node/index.d.ts +12 -0
- package/dist/test-utils/index.d.ts +12 -0
- package/dist/test-utils/index.js +605 -4
- package/package.json +2 -2
- package/reference/claude-config/sync-notes/2026-05-24-system-interface-readiness.md +43 -0
- package/reference/concepts.mdx +1 -1
- package/reference/resources/patterns.mdx +1 -1
- package/reference/runtime.mdx +8 -9
- package/reference/scaffold/recipes/customize-crm-actions.md +6 -2
- package/reference/scaffold/recipes/extend-crm.md +5 -3
- package/reference/scaffold/recipes/extend-lead-gen.md +9 -6
- package/reference/scaffold/reference/contracts.md +44 -0
- package/reference/troubleshooting.mdx +3 -3
package/dist/index.js
CHANGED
|
@@ -344,6 +344,19 @@ function compileOrganizationOntology(model) {
|
|
|
344
344
|
function childSystemsOf2(system) {
|
|
345
345
|
return system.systems ?? system.subsystems ?? {};
|
|
346
346
|
}
|
|
347
|
+
function getSystem(model, path) {
|
|
348
|
+
const flatMatch = model.systems[path];
|
|
349
|
+
if (flatMatch !== void 0) return flatMatch;
|
|
350
|
+
const segments = path.split(".");
|
|
351
|
+
let current = model.systems;
|
|
352
|
+
let node;
|
|
353
|
+
for (const seg of segments) {
|
|
354
|
+
node = current[seg];
|
|
355
|
+
if (node === void 0) return void 0;
|
|
356
|
+
current = childSystemsOf2(node);
|
|
357
|
+
}
|
|
358
|
+
return node;
|
|
359
|
+
}
|
|
347
360
|
function listAllSystems(model) {
|
|
348
361
|
const results = [];
|
|
349
362
|
function walk(map, prefix) {
|
|
@@ -585,6 +598,26 @@ var SystemUiSchema = z.object({
|
|
|
585
598
|
icon: IconNameSchema.optional(),
|
|
586
599
|
order: z.number().int().optional()
|
|
587
600
|
});
|
|
601
|
+
var SystemInterfaceKeySchema = ModelIdSchema;
|
|
602
|
+
var SystemInterfaceLifecycleSchema = z.enum(["draft", "active", "disabled", "deprecated", "archived"]).meta({ label: "System interface lifecycle", color: "teal" });
|
|
603
|
+
var SystemInterfaceReadinessProfileSchema = z.string().trim().min(1).max(200).regex(
|
|
604
|
+
/^[a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)*(?:\.[a-z0-9][a-z0-9-]*)?$/,
|
|
605
|
+
'Readiness profiles must use dotted lowercase identifiers (e.g. "sales.lead-gen.api")'
|
|
606
|
+
);
|
|
607
|
+
var SystemInterfaceResourceScopeSchema = z.array(ModelIdSchema).default([]);
|
|
608
|
+
var SystemApiInterfaceSchema = z.object({
|
|
609
|
+
lifecycle: SystemInterfaceLifecycleSchema.default("active"),
|
|
610
|
+
readinessProfile: SystemInterfaceReadinessProfileSchema.optional(),
|
|
611
|
+
/**
|
|
612
|
+
* Resource ids that participate in this API interface. This scopes readiness
|
|
613
|
+
* derivation without duplicating authored required/provided contract refs.
|
|
614
|
+
*/
|
|
615
|
+
resourceIds: SystemInterfaceResourceScopeSchema.optional()
|
|
616
|
+
}).strict();
|
|
617
|
+
var SystemInterfaceRefSchema = z.object({
|
|
618
|
+
systemPath: SystemPathSchema,
|
|
619
|
+
interfaceKey: SystemInterfaceKeySchema
|
|
620
|
+
}).strict();
|
|
588
621
|
var JsonValueSchema = z.lazy(
|
|
589
622
|
() => z.union([
|
|
590
623
|
z.string(),
|
|
@@ -623,6 +656,8 @@ var SystemEntrySchema = z.object({
|
|
|
623
656
|
policies: z.array(ModelIdSchema.meta({ ref: "policy" })).default([]).optional(),
|
|
624
657
|
/** Optional goals this system contributes to. */
|
|
625
658
|
drivesGoals: z.array(ModelIdSchema.meta({ ref: "goal" })).default([]).optional(),
|
|
659
|
+
/** Thin API runtime-boundary marker. Readiness is derived from scoped resources and topology. */
|
|
660
|
+
apiInterface: SystemApiInterfaceSchema.optional(),
|
|
626
661
|
/** @deprecated Use lifecycle. Accepted for one publish cycle. */
|
|
627
662
|
status: SystemStatusSchema.optional(),
|
|
628
663
|
/** @deprecated Use ui.path. Kept for one-cycle Feature compatibility. */
|
|
@@ -889,6 +924,15 @@ var OmTopologyMetadataSchema = z.record(z.string().trim().min(1).max(120), JsonV
|
|
|
889
924
|
}
|
|
890
925
|
visit(metadata, []);
|
|
891
926
|
});
|
|
927
|
+
var OmTopologySystemInterfaceGrantSchema = z.object({
|
|
928
|
+
consumer: SystemInterfaceRefSchema,
|
|
929
|
+
provider: SystemInterfaceRefSchema,
|
|
930
|
+
resourceIds: z.array(ResourceIdSchema).default([]),
|
|
931
|
+
ontologyIds: z.array(OntologyIdSchema).default([])
|
|
932
|
+
}).strict();
|
|
933
|
+
z.object({
|
|
934
|
+
systemInterfaceGrant: OmTopologySystemInterfaceGrantSchema
|
|
935
|
+
}).strict();
|
|
892
936
|
var OmTopologyRelationshipSchema = z.object({
|
|
893
937
|
from: OmTopologyNodeRefSchema,
|
|
894
938
|
kind: OmTopologyRelationshipKindSchema,
|
|
@@ -1530,6 +1574,586 @@ function validateModelConfig(config) {
|
|
|
1530
1574
|
}
|
|
1531
1575
|
}
|
|
1532
1576
|
|
|
1577
|
+
// ../core/src/business/acquisition/ontology-validation.ts
|
|
1578
|
+
var LEAD_GEN_API_INTERFACE = {
|
|
1579
|
+
systemPath: "sales.lead-gen",
|
|
1580
|
+
interfaceKey: "api",
|
|
1581
|
+
readinessProfile: "sales.lead-gen.api"
|
|
1582
|
+
};
|
|
1583
|
+
var CRM_API_INTERFACE = {
|
|
1584
|
+
systemPath: "sales.crm",
|
|
1585
|
+
interfaceKey: "api",
|
|
1586
|
+
readinessProfile: "sales.crm.api"
|
|
1587
|
+
};
|
|
1588
|
+
var LEAD_GEN_CRM_HANDOFF_INTERFACE = {
|
|
1589
|
+
systemPath: "sales.lead-gen",
|
|
1590
|
+
interfaceKey: "crm-handoff",
|
|
1591
|
+
readinessProfile: "sales.lead-gen.crm-handoff"
|
|
1592
|
+
};
|
|
1593
|
+
var LEAD_GEN_API_READINESS_LABEL = LEAD_GEN_API_INTERFACE.readinessProfile;
|
|
1594
|
+
var CRM_API_READINESS_LABEL = CRM_API_INTERFACE.readinessProfile;
|
|
1595
|
+
var LEAD_GEN_LIST_OBJECT_ONTOLOGY_ID = formatOntologyId({
|
|
1596
|
+
scope: "sales.lead-gen",
|
|
1597
|
+
kind: "object",
|
|
1598
|
+
localId: "list"
|
|
1599
|
+
});
|
|
1600
|
+
var LEAD_GEN_COMPANY_OBJECT_ONTOLOGY_ID = formatOntologyId({
|
|
1601
|
+
scope: "sales.lead-gen",
|
|
1602
|
+
kind: "object",
|
|
1603
|
+
localId: "company"
|
|
1604
|
+
});
|
|
1605
|
+
var LEAD_GEN_CONTACT_OBJECT_ONTOLOGY_ID = formatOntologyId({
|
|
1606
|
+
scope: "sales.lead-gen",
|
|
1607
|
+
kind: "object",
|
|
1608
|
+
localId: "contact"
|
|
1609
|
+
});
|
|
1610
|
+
var LEAD_GEN_BUILD_TEMPLATE_CATALOG_ONTOLOGY_ID = formatOntologyId({
|
|
1611
|
+
scope: "sales.lead-gen",
|
|
1612
|
+
kind: "catalog",
|
|
1613
|
+
localId: "build-template"
|
|
1614
|
+
});
|
|
1615
|
+
var LEAD_GEN_COMPANY_STAGE_CATALOG_ONTOLOGY_ID = formatOntologyId({
|
|
1616
|
+
scope: "sales.lead-gen",
|
|
1617
|
+
kind: "catalog",
|
|
1618
|
+
localId: "company-stage"
|
|
1619
|
+
});
|
|
1620
|
+
var LEAD_GEN_CONTACT_STAGE_CATALOG_ONTOLOGY_ID = formatOntologyId({
|
|
1621
|
+
scope: "sales.lead-gen",
|
|
1622
|
+
kind: "catalog",
|
|
1623
|
+
localId: "contact-stage"
|
|
1624
|
+
});
|
|
1625
|
+
var CRM_PIPELINE_CATALOG_ONTOLOGY_ID = formatOntologyId({
|
|
1626
|
+
scope: "sales.crm",
|
|
1627
|
+
kind: "catalog",
|
|
1628
|
+
localId: "crm.pipeline"
|
|
1629
|
+
});
|
|
1630
|
+
var LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID = formatOntologyId({
|
|
1631
|
+
scope: "sales.lead-gen",
|
|
1632
|
+
kind: "catalog",
|
|
1633
|
+
localId: "lead-gen.stage-catalog"
|
|
1634
|
+
});
|
|
1635
|
+
var SystemInterfaceReadinessError = class extends Error {
|
|
1636
|
+
code;
|
|
1637
|
+
statusCode = 503;
|
|
1638
|
+
systemPath;
|
|
1639
|
+
interfaceKey;
|
|
1640
|
+
readinessProfile;
|
|
1641
|
+
issues;
|
|
1642
|
+
constructor(result) {
|
|
1643
|
+
const firstIssue = result.issues[0];
|
|
1644
|
+
super(formatInterfaceReadinessFailure(result));
|
|
1645
|
+
this.name = "SystemInterfaceReadinessError";
|
|
1646
|
+
this.code = firstIssue?.family ?? "SYSTEM_INTERFACE_NOT_READY";
|
|
1647
|
+
this.systemPath = result.systemPath;
|
|
1648
|
+
this.interfaceKey = result.interfaceKey;
|
|
1649
|
+
this.readinessProfile = result.readinessProfile;
|
|
1650
|
+
this.issues = result.issues;
|
|
1651
|
+
}
|
|
1652
|
+
};
|
|
1653
|
+
function createLeadGenStageCatalog(model) {
|
|
1654
|
+
return {
|
|
1655
|
+
id: LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID,
|
|
1656
|
+
label: "Lead Gen Processing Stages",
|
|
1657
|
+
ownerSystemId: "sales.lead-gen",
|
|
1658
|
+
kind: "processing-stage-catalog",
|
|
1659
|
+
entries: Object.fromEntries(
|
|
1660
|
+
Object.entries(getLeadGenStageCatalog(model)).map(([key, entry]) => [
|
|
1661
|
+
key,
|
|
1662
|
+
{
|
|
1663
|
+
...entry
|
|
1664
|
+
}
|
|
1665
|
+
])
|
|
1666
|
+
),
|
|
1667
|
+
legacyCatalogKey: "LEAD_GEN_STAGE_CATALOG"
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
function isPlainRecord(value) {
|
|
1671
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1672
|
+
}
|
|
1673
|
+
function addReadinessIssue(issues, family, code, message, details = {}) {
|
|
1674
|
+
issues.push({
|
|
1675
|
+
family,
|
|
1676
|
+
code,
|
|
1677
|
+
message,
|
|
1678
|
+
...details
|
|
1679
|
+
});
|
|
1680
|
+
}
|
|
1681
|
+
function formatInterfaceIdentity(systemPath, interfaceKey) {
|
|
1682
|
+
return `${systemPath}/${interfaceKey}`;
|
|
1683
|
+
}
|
|
1684
|
+
function profileForInterface(systemPath, interfaceKey, readinessProfile) {
|
|
1685
|
+
return readinessProfile ?? `${systemPath}.${interfaceKey}`;
|
|
1686
|
+
}
|
|
1687
|
+
function readinessMarkerPath(context) {
|
|
1688
|
+
return context.interfaceKey === "api" ? `systems.${context.systemPath}.apiInterface` : `systems.${context.systemPath}.derivedCrmHandoffReadiness`;
|
|
1689
|
+
}
|
|
1690
|
+
function formatInterfaceReadinessFailure(result) {
|
|
1691
|
+
const identity = formatInterfaceIdentity(result.systemPath, result.interfaceKey);
|
|
1692
|
+
const issueSummary = result.issues.map((issue) => `${issue.family}:${issue.code}: ${issue.message}`).join("; ");
|
|
1693
|
+
return `${identity} readiness failed${result.readinessProfile ? ` (${result.readinessProfile})` : ""}: ${issueSummary}`;
|
|
1694
|
+
}
|
|
1695
|
+
function throwIfInterfaceNotReady(result) {
|
|
1696
|
+
if (result.ready) return;
|
|
1697
|
+
throw new SystemInterfaceReadinessError(result);
|
|
1698
|
+
}
|
|
1699
|
+
function getActiveScopedResources(model, resourceIds, issues, context) {
|
|
1700
|
+
const resources = [];
|
|
1701
|
+
for (const [index, resourceId] of resourceIds.entries()) {
|
|
1702
|
+
const resource = model.resources?.[resourceId];
|
|
1703
|
+
const path = `${readinessMarkerPath(context)}.resourceIds.${index}`;
|
|
1704
|
+
if (resource === void 0) {
|
|
1705
|
+
addReadinessIssue(
|
|
1706
|
+
issues,
|
|
1707
|
+
"SYSTEM_INTERFACE_NOT_READY",
|
|
1708
|
+
"missing-resource",
|
|
1709
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" scopes missing resource "${resourceId}".`,
|
|
1710
|
+
{ path, ref: resourceId }
|
|
1711
|
+
);
|
|
1712
|
+
continue;
|
|
1713
|
+
}
|
|
1714
|
+
if (resource.systemPath !== context.systemPath) {
|
|
1715
|
+
addReadinessIssue(
|
|
1716
|
+
issues,
|
|
1717
|
+
"SYSTEM_INTERFACE_INVALID",
|
|
1718
|
+
"resource-system-mismatch",
|
|
1719
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" scopes resource "${resourceId}" from system "${resource.systemPath}".`,
|
|
1720
|
+
{ path, ref: resourceId }
|
|
1721
|
+
);
|
|
1722
|
+
continue;
|
|
1723
|
+
}
|
|
1724
|
+
if (resource.status !== "active") {
|
|
1725
|
+
addReadinessIssue(
|
|
1726
|
+
issues,
|
|
1727
|
+
"SYSTEM_INTERFACE_NOT_READY",
|
|
1728
|
+
"inactive-resource",
|
|
1729
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" scopes inactive resource "${resourceId}".`,
|
|
1730
|
+
{ path, ref: resourceId }
|
|
1731
|
+
);
|
|
1732
|
+
continue;
|
|
1733
|
+
}
|
|
1734
|
+
resources.push(resource);
|
|
1735
|
+
}
|
|
1736
|
+
return resources;
|
|
1737
|
+
}
|
|
1738
|
+
function resourceBindingIds(resources, key) {
|
|
1739
|
+
return new Set(resources.flatMap((resource) => resource.ontology?.[key] ?? []));
|
|
1740
|
+
}
|
|
1741
|
+
function requireScopedBinding(issues, boundIds, bindingKey, ontologyId, context) {
|
|
1742
|
+
if (boundIds.has(ontologyId)) return;
|
|
1743
|
+
addReadinessIssue(
|
|
1744
|
+
issues,
|
|
1745
|
+
"SYSTEM_INTERFACE_NOT_READY",
|
|
1746
|
+
"missing-resource-binding",
|
|
1747
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" has no active scoped resource with ontology.${bindingKey} binding "${ontologyId}".`,
|
|
1748
|
+
{ path: `${readinessMarkerPath(context)}.resourceIds`, ref: ontologyId }
|
|
1749
|
+
);
|
|
1750
|
+
}
|
|
1751
|
+
function ontologyOwnerMatches(ontologyId, expectedSystemPath, catalog) {
|
|
1752
|
+
if (catalog?.ownerSystemId !== void 0) return catalog.ownerSystemId === expectedSystemPath;
|
|
1753
|
+
return parseOntologyId(ontologyId).scope === expectedSystemPath;
|
|
1754
|
+
}
|
|
1755
|
+
function requireObjectReadiness(issues, index, objectId, context) {
|
|
1756
|
+
const object = index.ontology.objectTypes[objectId];
|
|
1757
|
+
if (object === void 0) {
|
|
1758
|
+
addReadinessIssue(
|
|
1759
|
+
issues,
|
|
1760
|
+
"SYSTEM_INTERFACE_NOT_READY",
|
|
1761
|
+
"missing-object",
|
|
1762
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" is missing required object type "${objectId}".`,
|
|
1763
|
+
{ ref: objectId }
|
|
1764
|
+
);
|
|
1765
|
+
return;
|
|
1766
|
+
}
|
|
1767
|
+
const ownerSystemId = object.ownerSystemId ?? parseOntologyId(objectId).scope;
|
|
1768
|
+
if (ownerSystemId !== context.systemPath) {
|
|
1769
|
+
addReadinessIssue(
|
|
1770
|
+
issues,
|
|
1771
|
+
"SYSTEM_INTERFACE_INVALID",
|
|
1772
|
+
"foreign-object",
|
|
1773
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" requires object "${objectId}" owned by "${ownerSystemId}".`,
|
|
1774
|
+
{ ref: objectId }
|
|
1775
|
+
);
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
function requireCatalogReadiness(issues, index, catalogId, context) {
|
|
1779
|
+
const catalog = index.ontology.catalogTypes[catalogId];
|
|
1780
|
+
if (catalog === void 0) {
|
|
1781
|
+
addReadinessIssue(
|
|
1782
|
+
issues,
|
|
1783
|
+
"SYSTEM_INTERFACE_NOT_READY",
|
|
1784
|
+
"missing-catalog",
|
|
1785
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" is missing required catalog "${catalogId}".`,
|
|
1786
|
+
{ ref: catalogId }
|
|
1787
|
+
);
|
|
1788
|
+
return void 0;
|
|
1789
|
+
}
|
|
1790
|
+
if (context.allowForeignOwner !== true && !ontologyOwnerMatches(catalogId, context.systemPath, catalog)) {
|
|
1791
|
+
addReadinessIssue(
|
|
1792
|
+
issues,
|
|
1793
|
+
"SYSTEM_INTERFACE_INVALID",
|
|
1794
|
+
"foreign-catalog",
|
|
1795
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" requires catalog "${catalogId}" owned by "${catalog.ownerSystemId ?? parseOntologyId(catalogId).scope}".`,
|
|
1796
|
+
{ ref: catalogId }
|
|
1797
|
+
);
|
|
1798
|
+
}
|
|
1799
|
+
if (Object.keys(getCatalogEntries(catalog)).length === 0) {
|
|
1800
|
+
addReadinessIssue(
|
|
1801
|
+
issues,
|
|
1802
|
+
"SYSTEM_INTERFACE_NOT_READY",
|
|
1803
|
+
"empty-catalog",
|
|
1804
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" requires catalog entries for "${catalogId}".`,
|
|
1805
|
+
{ ref: catalogId }
|
|
1806
|
+
);
|
|
1807
|
+
}
|
|
1808
|
+
return catalog;
|
|
1809
|
+
}
|
|
1810
|
+
function requireLeadGenInterfaceReadiness(issues, index, resources, context) {
|
|
1811
|
+
const reads = resourceBindingIds(resources, "reads");
|
|
1812
|
+
const catalogs = resourceBindingIds(resources, "usesCatalogs");
|
|
1813
|
+
for (const objectId of [
|
|
1814
|
+
LEAD_GEN_LIST_OBJECT_ONTOLOGY_ID,
|
|
1815
|
+
LEAD_GEN_COMPANY_OBJECT_ONTOLOGY_ID,
|
|
1816
|
+
LEAD_GEN_CONTACT_OBJECT_ONTOLOGY_ID
|
|
1817
|
+
]) {
|
|
1818
|
+
requireObjectReadiness(issues, index, objectId, context);
|
|
1819
|
+
requireScopedBinding(issues, reads, "reads", objectId, context);
|
|
1820
|
+
}
|
|
1821
|
+
for (const catalogId of [
|
|
1822
|
+
LEAD_GEN_BUILD_TEMPLATE_CATALOG_ONTOLOGY_ID,
|
|
1823
|
+
LEAD_GEN_COMPANY_STAGE_CATALOG_ONTOLOGY_ID,
|
|
1824
|
+
LEAD_GEN_CONTACT_STAGE_CATALOG_ONTOLOGY_ID
|
|
1825
|
+
]) {
|
|
1826
|
+
requireCatalogReadiness(issues, index, catalogId, context);
|
|
1827
|
+
requireScopedBinding(issues, catalogs, "usesCatalogs", catalogId, context);
|
|
1828
|
+
}
|
|
1829
|
+
const templateStepCatalog = findLeadGenTemplateStepCatalog(index);
|
|
1830
|
+
if (templateStepCatalog === void 0) {
|
|
1831
|
+
addReadinessIssue(
|
|
1832
|
+
issues,
|
|
1833
|
+
"SYSTEM_INTERFACE_NOT_READY",
|
|
1834
|
+
"missing-template-step-catalog",
|
|
1835
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" is missing a lead-gen template-step catalog.`
|
|
1836
|
+
);
|
|
1837
|
+
} else {
|
|
1838
|
+
requireCatalogReadiness(issues, index, templateStepCatalog.id, context);
|
|
1839
|
+
requireScopedBinding(issues, catalogs, "usesCatalogs", templateStepCatalog.id, context);
|
|
1840
|
+
}
|
|
1841
|
+
const leadGenStageCatalog = requireCatalogReadiness(issues, index, LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID, context);
|
|
1842
|
+
return leadGenStageCatalog;
|
|
1843
|
+
}
|
|
1844
|
+
function requireCrmInterfaceReadiness(issues, index, resources, context) {
|
|
1845
|
+
const catalogs = resourceBindingIds(resources, "usesCatalogs");
|
|
1846
|
+
const crmPipelineCatalog = requireCatalogReadiness(issues, index, CRM_PIPELINE_CATALOG_ONTOLOGY_ID, context);
|
|
1847
|
+
requireScopedBinding(issues, catalogs, "usesCatalogs", CRM_PIPELINE_CATALOG_ONTOLOGY_ID, context);
|
|
1848
|
+
return crmPipelineCatalog;
|
|
1849
|
+
}
|
|
1850
|
+
function findSystemInterfaceGrant(model, consumer, provider) {
|
|
1851
|
+
return Object.values(model.topology?.relationships ?? {}).find((relationship) => {
|
|
1852
|
+
const grant = relationship.metadata?.["systemInterfaceGrant"];
|
|
1853
|
+
if (!isPlainRecord(grant) || !isPlainRecord(grant.consumer) || !isPlainRecord(grant.provider)) return false;
|
|
1854
|
+
return grant.consumer.systemPath === consumer.systemPath && grant.consumer.interfaceKey === consumer.interfaceKey && grant.provider.systemPath === provider.systemPath && grant.provider.interfaceKey === provider.interfaceKey;
|
|
1855
|
+
});
|
|
1856
|
+
}
|
|
1857
|
+
function requireHandoffBridgeReadiness(issues, model, context) {
|
|
1858
|
+
const crmResult = computeInterfaceReadiness(model, CRM_API_INTERFACE);
|
|
1859
|
+
if (!crmResult.ready) {
|
|
1860
|
+
for (const issue of crmResult.issues) {
|
|
1861
|
+
addReadinessIssue(
|
|
1862
|
+
issues,
|
|
1863
|
+
"SYSTEM_BRIDGE_NOT_READY",
|
|
1864
|
+
issue.code,
|
|
1865
|
+
`Provider interface ${formatInterfaceIdentity(CRM_API_INTERFACE.systemPath, CRM_API_INTERFACE.interfaceKey)} is not ready: ${issue.message}`,
|
|
1866
|
+
{ path: issue.path, ref: issue.ref }
|
|
1867
|
+
);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
const bridgeGrant = findSystemInterfaceGrant(model, context, CRM_API_INTERFACE);
|
|
1871
|
+
if (bridgeGrant === void 0) {
|
|
1872
|
+
addReadinessIssue(
|
|
1873
|
+
issues,
|
|
1874
|
+
"SYSTEM_BRIDGE_NOT_READY",
|
|
1875
|
+
"missing-topology-grant",
|
|
1876
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" requires a scoped topology grant to "${formatInterfaceIdentity(CRM_API_INTERFACE.systemPath, CRM_API_INTERFACE.interfaceKey)}".`
|
|
1877
|
+
);
|
|
1878
|
+
}
|
|
1879
|
+
return bridgeGrant;
|
|
1880
|
+
}
|
|
1881
|
+
function getLeadGenCrmHandoffResourceIds(model) {
|
|
1882
|
+
return Object.values(model.resources ?? {}).filter(
|
|
1883
|
+
(resource) => resource.systemPath === LEAD_GEN_CRM_HANDOFF_INTERFACE.systemPath && resource.ontology?.usesCatalogs?.includes(CRM_PIPELINE_CATALOG_ONTOLOGY_ID) === true
|
|
1884
|
+
).map((resource) => resource.id);
|
|
1885
|
+
}
|
|
1886
|
+
function getSystemInterfaceReadinessMarker(model, request) {
|
|
1887
|
+
const system = getSystem(model, request.systemPath);
|
|
1888
|
+
if (system === void 0) return void 0;
|
|
1889
|
+
if (request.interfaceKey === LEAD_GEN_API_INTERFACE.interfaceKey) {
|
|
1890
|
+
return system.apiInterface;
|
|
1891
|
+
}
|
|
1892
|
+
if (request.systemPath === LEAD_GEN_CRM_HANDOFF_INTERFACE.systemPath && request.interfaceKey === LEAD_GEN_CRM_HANDOFF_INTERFACE.interfaceKey) {
|
|
1893
|
+
return {
|
|
1894
|
+
lifecycle: "active",
|
|
1895
|
+
readinessProfile: LEAD_GEN_CRM_HANDOFF_INTERFACE.readinessProfile,
|
|
1896
|
+
resourceIds: getLeadGenCrmHandoffResourceIds(model)
|
|
1897
|
+
};
|
|
1898
|
+
}
|
|
1899
|
+
return void 0;
|
|
1900
|
+
}
|
|
1901
|
+
function mergeLeadGenDerivedCatalogs(model) {
|
|
1902
|
+
const baseCatalogTypes = model.ontology?.catalogTypes ?? {};
|
|
1903
|
+
const derivedCatalogTypes = {};
|
|
1904
|
+
if (baseCatalogTypes[LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID] === void 0) {
|
|
1905
|
+
derivedCatalogTypes[LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID] = createLeadGenStageCatalog(model);
|
|
1906
|
+
}
|
|
1907
|
+
if (Object.keys(derivedCatalogTypes).length === 0) return model;
|
|
1908
|
+
return {
|
|
1909
|
+
...model,
|
|
1910
|
+
ontology: {
|
|
1911
|
+
...model.ontology ?? {},
|
|
1912
|
+
catalogTypes: {
|
|
1913
|
+
...baseCatalogTypes,
|
|
1914
|
+
...derivedCatalogTypes
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
function compileBusinessOntology(model, contractId) {
|
|
1920
|
+
const compilation = compileOrganizationOntology(mergeLeadGenDerivedCatalogs(model));
|
|
1921
|
+
if (compilation.diagnostics.length > 0) {
|
|
1922
|
+
const summary = compilation.diagnostics.map((diagnostic) => diagnostic.message).join("; ");
|
|
1923
|
+
throw new Error(`${contractId} ontology validation index failed to compile: ${summary}`);
|
|
1924
|
+
}
|
|
1925
|
+
return {
|
|
1926
|
+
ontology: compilation.ontology,
|
|
1927
|
+
actionTypesByLegacyId: indexActionTypesByLegacyId(compilation.ontology.actionTypes)
|
|
1928
|
+
};
|
|
1929
|
+
}
|
|
1930
|
+
function tryCompileBusinessOntology(model, readinessProfile, issues) {
|
|
1931
|
+
try {
|
|
1932
|
+
return compileBusinessOntology(model, readinessProfile);
|
|
1933
|
+
} catch (error) {
|
|
1934
|
+
addReadinessIssue(
|
|
1935
|
+
issues,
|
|
1936
|
+
"SYSTEM_INTERFACE_INVALID",
|
|
1937
|
+
"ontology-compile-failed",
|
|
1938
|
+
error instanceof Error ? error.message : String(error)
|
|
1939
|
+
);
|
|
1940
|
+
return void 0;
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
function computeInterfaceReadiness(model, request) {
|
|
1944
|
+
const issues = [];
|
|
1945
|
+
const system = getSystem(model, request.systemPath);
|
|
1946
|
+
const systemInterface = getSystemInterfaceReadinessMarker(model, request);
|
|
1947
|
+
const readinessProfile = systemInterface === void 0 ? void 0 : profileForInterface(request.systemPath, request.interfaceKey, systemInterface.readinessProfile);
|
|
1948
|
+
const scopedResourceIds = systemInterface?.resourceIds ?? [];
|
|
1949
|
+
if (system === void 0) {
|
|
1950
|
+
addReadinessIssue(
|
|
1951
|
+
issues,
|
|
1952
|
+
"SYSTEM_INTERFACE_MISSING",
|
|
1953
|
+
"missing-system",
|
|
1954
|
+
`System "${request.systemPath}" is missing.`,
|
|
1955
|
+
{ ref: request.systemPath }
|
|
1956
|
+
);
|
|
1957
|
+
return { ready: false, systemPath: request.systemPath, interfaceKey: request.interfaceKey, scopedResourceIds, issues };
|
|
1958
|
+
}
|
|
1959
|
+
if (systemInterface === void 0) {
|
|
1960
|
+
addReadinessIssue(
|
|
1961
|
+
issues,
|
|
1962
|
+
"SYSTEM_INTERFACE_MISSING",
|
|
1963
|
+
"missing-interface",
|
|
1964
|
+
`System "${request.systemPath}" does not declare interface "${request.interfaceKey}".`,
|
|
1965
|
+
{ path: readinessMarkerPath(request) }
|
|
1966
|
+
);
|
|
1967
|
+
return { ready: false, systemPath: request.systemPath, interfaceKey: request.interfaceKey, scopedResourceIds, issues };
|
|
1968
|
+
}
|
|
1969
|
+
if (systemInterface.lifecycle !== "active") {
|
|
1970
|
+
addReadinessIssue(
|
|
1971
|
+
issues,
|
|
1972
|
+
"SYSTEM_INTERFACE_DISABLED",
|
|
1973
|
+
"inactive-interface",
|
|
1974
|
+
`System Interface "${formatInterfaceIdentity(request.systemPath, request.interfaceKey)}" lifecycle is "${systemInterface.lifecycle}".`,
|
|
1975
|
+
{ path: `${readinessMarkerPath(request)}.lifecycle` }
|
|
1976
|
+
);
|
|
1977
|
+
}
|
|
1978
|
+
const supportedProfiles = [
|
|
1979
|
+
LEAD_GEN_API_INTERFACE.readinessProfile,
|
|
1980
|
+
CRM_API_INTERFACE.readinessProfile,
|
|
1981
|
+
LEAD_GEN_CRM_HANDOFF_INTERFACE.readinessProfile
|
|
1982
|
+
];
|
|
1983
|
+
const supportedProfile = readinessProfile !== void 0 && supportedProfiles.some((profile) => profile === readinessProfile);
|
|
1984
|
+
if (!supportedProfile) {
|
|
1985
|
+
addReadinessIssue(
|
|
1986
|
+
issues,
|
|
1987
|
+
"SYSTEM_INTERFACE_INVALID",
|
|
1988
|
+
"unknown-readiness-profile",
|
|
1989
|
+
`System Interface "${formatInterfaceIdentity(request.systemPath, request.interfaceKey)}" references unknown readiness profile "${readinessProfile}".`,
|
|
1990
|
+
{ path: `${readinessMarkerPath(request)}.readinessProfile`, ref: readinessProfile }
|
|
1991
|
+
);
|
|
1992
|
+
return {
|
|
1993
|
+
ready: false,
|
|
1994
|
+
systemPath: request.systemPath,
|
|
1995
|
+
interfaceKey: request.interfaceKey,
|
|
1996
|
+
readinessProfile,
|
|
1997
|
+
scopedResourceIds,
|
|
1998
|
+
issues
|
|
1999
|
+
};
|
|
2000
|
+
}
|
|
2001
|
+
if (scopedResourceIds.length === 0) {
|
|
2002
|
+
addReadinessIssue(
|
|
2003
|
+
issues,
|
|
2004
|
+
"SYSTEM_INTERFACE_NOT_READY",
|
|
2005
|
+
"missing-scoped-resources",
|
|
2006
|
+
`System Interface "${formatInterfaceIdentity(request.systemPath, request.interfaceKey)}" must scope at least one active resource.`,
|
|
2007
|
+
{ path: `${readinessMarkerPath(request)}.resourceIds` }
|
|
2008
|
+
);
|
|
2009
|
+
}
|
|
2010
|
+
const checkedReadinessProfile = readinessProfile;
|
|
2011
|
+
if (checkedReadinessProfile === void 0) {
|
|
2012
|
+
throw new Error("Supported readiness profile unexpectedly resolved to undefined");
|
|
2013
|
+
}
|
|
2014
|
+
const resources = getActiveScopedResources(model, scopedResourceIds, issues, request);
|
|
2015
|
+
const index = tryCompileBusinessOntology(model, checkedReadinessProfile, issues);
|
|
2016
|
+
if (index !== void 0) {
|
|
2017
|
+
if (checkedReadinessProfile === LEAD_GEN_API_INTERFACE.readinessProfile) {
|
|
2018
|
+
requireLeadGenInterfaceReadiness(issues, index, resources, request);
|
|
2019
|
+
} else if (checkedReadinessProfile === CRM_API_INTERFACE.readinessProfile) {
|
|
2020
|
+
requireCrmInterfaceReadiness(issues, index, resources, request);
|
|
2021
|
+
} else if (checkedReadinessProfile === LEAD_GEN_CRM_HANDOFF_INTERFACE.readinessProfile) {
|
|
2022
|
+
requireLeadGenInterfaceReadiness(issues, index, resources, request);
|
|
2023
|
+
requireCrmInterfaceReadiness(issues, index, resources, { ...request, allowForeignOwner: true });
|
|
2024
|
+
requireHandoffBridgeReadiness(issues, model, request);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
return {
|
|
2028
|
+
ready: issues.length === 0,
|
|
2029
|
+
systemPath: request.systemPath,
|
|
2030
|
+
interfaceKey: request.interfaceKey,
|
|
2031
|
+
readinessProfile,
|
|
2032
|
+
scopedResourceIds,
|
|
2033
|
+
issues
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
function requireObjectType(index, objectId, contractId) {
|
|
2037
|
+
if (index.ontology.objectTypes[objectId] === void 0) {
|
|
2038
|
+
throw new Error(`${contractId} is missing required object type: ${objectId}`);
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
function requireCatalog(index, catalogId, contractId) {
|
|
2042
|
+
const catalog = index.ontology.catalogTypes[catalogId];
|
|
2043
|
+
if (catalog === void 0) {
|
|
2044
|
+
throw new Error(`${contractId} is missing required catalog: ${catalogId}`);
|
|
2045
|
+
}
|
|
2046
|
+
return catalog;
|
|
2047
|
+
}
|
|
2048
|
+
function requireCatalogEntries(catalog, contractId) {
|
|
2049
|
+
if (Object.keys(getCatalogEntries(catalog)).length === 0) {
|
|
2050
|
+
throw new Error(`${contractId} requires catalog entries for ${catalog.id}`);
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
function findLeadGenTemplateStepCatalog(index) {
|
|
2054
|
+
return Object.values(index.ontology.catalogTypes).find(
|
|
2055
|
+
(catalog) => catalog.ownerSystemId === "sales.lead-gen" && catalog.kind === "template-step" && catalog.appliesTo === LEAD_GEN_LIST_OBJECT_ONTOLOGY_ID
|
|
2056
|
+
);
|
|
2057
|
+
}
|
|
2058
|
+
function requireLeadGenApiReadiness(index) {
|
|
2059
|
+
requireObjectType(index, LEAD_GEN_LIST_OBJECT_ONTOLOGY_ID, LEAD_GEN_API_READINESS_LABEL);
|
|
2060
|
+
requireObjectType(index, LEAD_GEN_COMPANY_OBJECT_ONTOLOGY_ID, LEAD_GEN_API_READINESS_LABEL);
|
|
2061
|
+
requireObjectType(index, LEAD_GEN_CONTACT_OBJECT_ONTOLOGY_ID, LEAD_GEN_API_READINESS_LABEL);
|
|
2062
|
+
requireCatalogEntries(
|
|
2063
|
+
requireCatalog(index, LEAD_GEN_BUILD_TEMPLATE_CATALOG_ONTOLOGY_ID, LEAD_GEN_API_READINESS_LABEL),
|
|
2064
|
+
LEAD_GEN_API_READINESS_LABEL
|
|
2065
|
+
);
|
|
2066
|
+
requireCatalogEntries(
|
|
2067
|
+
requireCatalog(index, LEAD_GEN_COMPANY_STAGE_CATALOG_ONTOLOGY_ID, LEAD_GEN_API_READINESS_LABEL),
|
|
2068
|
+
LEAD_GEN_API_READINESS_LABEL
|
|
2069
|
+
);
|
|
2070
|
+
requireCatalogEntries(
|
|
2071
|
+
requireCatalog(index, LEAD_GEN_CONTACT_STAGE_CATALOG_ONTOLOGY_ID, LEAD_GEN_API_READINESS_LABEL),
|
|
2072
|
+
LEAD_GEN_API_READINESS_LABEL
|
|
2073
|
+
);
|
|
2074
|
+
const templateStepCatalog = findLeadGenTemplateStepCatalog(index);
|
|
2075
|
+
if (templateStepCatalog === void 0) {
|
|
2076
|
+
throw new Error(`${LEAD_GEN_API_READINESS_LABEL} is missing a lead-gen template-step catalog`);
|
|
2077
|
+
}
|
|
2078
|
+
requireCatalogEntries(templateStepCatalog, LEAD_GEN_API_READINESS_LABEL);
|
|
2079
|
+
const leadGenStageCatalog = requireCatalog(index, LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID, LEAD_GEN_API_READINESS_LABEL);
|
|
2080
|
+
requireCatalogEntries(leadGenStageCatalog, LEAD_GEN_API_READINESS_LABEL);
|
|
2081
|
+
return leadGenStageCatalog;
|
|
2082
|
+
}
|
|
2083
|
+
function requireCrmApiReadiness(index) {
|
|
2084
|
+
const crmPipelineCatalog = requireCatalog(index, CRM_PIPELINE_CATALOG_ONTOLOGY_ID, CRM_API_READINESS_LABEL);
|
|
2085
|
+
requireCatalogEntries(crmPipelineCatalog, CRM_API_READINESS_LABEL);
|
|
2086
|
+
return crmPipelineCatalog;
|
|
2087
|
+
}
|
|
2088
|
+
function compileBusinessOntologyValidationIndex(model) {
|
|
2089
|
+
throwIfInterfaceNotReady(computeInterfaceReadiness(model, LEAD_GEN_API_INTERFACE));
|
|
2090
|
+
throwIfInterfaceNotReady(computeInterfaceReadiness(model, CRM_API_INTERFACE));
|
|
2091
|
+
const index = compileBusinessOntology(model, "legacy acquisition business ontology");
|
|
2092
|
+
return {
|
|
2093
|
+
...index,
|
|
2094
|
+
leadGenStageCatalog: requireLeadGenApiReadiness(index),
|
|
2095
|
+
crmPipelineCatalog: requireCrmApiReadiness(index)
|
|
2096
|
+
};
|
|
2097
|
+
}
|
|
2098
|
+
function indexActionTypesByLegacyId(actionTypes) {
|
|
2099
|
+
const byLegacyId = {};
|
|
2100
|
+
for (const actionType of Object.values(actionTypes)) {
|
|
2101
|
+
const legacyActionId = actionType["legacyActionId"];
|
|
2102
|
+
if (typeof legacyActionId === "string") {
|
|
2103
|
+
byLegacyId[legacyActionId] = actionType;
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
return byLegacyId;
|
|
2107
|
+
}
|
|
2108
|
+
function getCatalogEntries(catalog) {
|
|
2109
|
+
return isPlainRecord(catalog.entries) ? catalog.entries : {};
|
|
2110
|
+
}
|
|
2111
|
+
function asLeadGenStageEntry(key, value) {
|
|
2112
|
+
const record = isPlainRecord(value) ? value : {};
|
|
2113
|
+
const entity = record.entity === "contact" ? "contact" : "company";
|
|
2114
|
+
const additionalEntities = Array.isArray(record.additionalEntities) ? record.additionalEntities.filter(
|
|
2115
|
+
(item) => item === "company" || item === "contact"
|
|
2116
|
+
) : void 0;
|
|
2117
|
+
const recordEntity = record.recordEntity === "company" || record.recordEntity === "contact" ? record.recordEntity : void 0;
|
|
2118
|
+
const recordStageKey = typeof record.recordStageKey === "string" ? record.recordStageKey : void 0;
|
|
2119
|
+
return {
|
|
2120
|
+
key: typeof record.key === "string" ? record.key : key,
|
|
2121
|
+
label: typeof record.label === "string" ? record.label : key,
|
|
2122
|
+
description: typeof record.description === "string" ? record.description : "",
|
|
2123
|
+
order: typeof record.order === "number" ? record.order : 0,
|
|
2124
|
+
entity,
|
|
2125
|
+
...additionalEntities !== void 0 ? { additionalEntities } : {},
|
|
2126
|
+
...recordEntity !== void 0 ? { recordEntity } : {},
|
|
2127
|
+
...recordStageKey !== void 0 ? { recordStageKey } : {}
|
|
2128
|
+
};
|
|
2129
|
+
}
|
|
2130
|
+
function createLeadGenStageValidators(index) {
|
|
2131
|
+
const getCatalog = () => Object.fromEntries(
|
|
2132
|
+
Object.entries(getCatalogEntries(index.leadGenStageCatalog)).map(([key, value]) => [
|
|
2133
|
+
key,
|
|
2134
|
+
asLeadGenStageEntry(key, value)
|
|
2135
|
+
])
|
|
2136
|
+
);
|
|
2137
|
+
const getEntry = (stageKey) => getCatalog()[stageKey];
|
|
2138
|
+
return {
|
|
2139
|
+
getLeadGenStageCatalog: getCatalog,
|
|
2140
|
+
getLeadGenStageEntry: getEntry,
|
|
2141
|
+
isLeadGenStageKey: (stageKey) => getEntry(stageKey) !== void 0,
|
|
2142
|
+
isLeadGenStageValidForEntity: (stageKey, entity) => {
|
|
2143
|
+
const stage = getEntry(stageKey);
|
|
2144
|
+
return stage !== void 0 && (stage.entity === entity || stage.additionalEntities?.includes(entity) === true);
|
|
2145
|
+
},
|
|
2146
|
+
isLeadGenRecordStageValidForEntity: (stageKey, entity) => {
|
|
2147
|
+
const stage = getEntry(stageKey);
|
|
2148
|
+
return stage !== void 0 && (stage.entity === entity || stage.additionalEntities?.includes(entity) === true || stage.recordEntity === entity);
|
|
2149
|
+
},
|
|
2150
|
+
resolveLeadGenRecordStageKey: (stageKey, entity) => {
|
|
2151
|
+
const stage = getEntry(stageKey);
|
|
2152
|
+
return stage?.recordEntity === entity && stage.recordStageKey ? stage.recordStageKey : stageKey;
|
|
2153
|
+
}
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
|
|
1533
2157
|
// ../core/src/platform/registry/validation.ts
|
|
1534
2158
|
var RegistryValidationError = class extends Error {
|
|
1535
2159
|
constructor(orgName, resourceId, field, message) {
|
|
@@ -1620,7 +2244,7 @@ function ontologyIndexForKind(index, kind) {
|
|
|
1620
2244
|
function sameJson(left, right) {
|
|
1621
2245
|
return JSON.stringify(left ?? null) === JSON.stringify(right ?? null);
|
|
1622
2246
|
}
|
|
1623
|
-
function addOntologyBindingIssues(issues, orgName, resource, ontologyIndex) {
|
|
2247
|
+
function addOntologyBindingIssues(issues, orgName, resource, ontologyIndex, organizationModel) {
|
|
1624
2248
|
const binding = resource.ontology;
|
|
1625
2249
|
if (binding === void 0) return;
|
|
1626
2250
|
const hasOperationalOntologyBinding = binding.primaryAction !== void 0 || (binding.reads?.length ?? 0) > 0 || (binding.writes?.length ?? 0) > 0 || (binding.usesCatalogs?.length ?? 0) > 0 || (binding.emits?.length ?? 0) > 0;
|
|
@@ -1663,6 +2287,45 @@ function addOntologyBindingIssues(issues, orgName, resource, ontologyIndex) {
|
|
|
1663
2287
|
validateRefs("writes", "object", binding.writes);
|
|
1664
2288
|
validateRefs("usesCatalogs", "catalog", binding.usesCatalogs);
|
|
1665
2289
|
validateRefs("emits", "event", binding.emits);
|
|
2290
|
+
addOntologyTopologyGrantIssues(issues, orgName, resource, organizationModel);
|
|
2291
|
+
}
|
|
2292
|
+
function addOntologyTopologyGrantIssues(issues, orgName, resource, organizationModel) {
|
|
2293
|
+
const binding = resource.ontology;
|
|
2294
|
+
if (binding === void 0) return;
|
|
2295
|
+
const refs = [
|
|
2296
|
+
...binding.actions ?? [],
|
|
2297
|
+
...binding.primaryAction !== void 0 ? [binding.primaryAction] : [],
|
|
2298
|
+
...binding.reads ?? [],
|
|
2299
|
+
...binding.writes ?? [],
|
|
2300
|
+
...binding.usesCatalogs ?? [],
|
|
2301
|
+
...binding.emits ?? []
|
|
2302
|
+
];
|
|
2303
|
+
for (const ontologyId of new Set(refs)) {
|
|
2304
|
+
const parsed = parseOntologyId(ontologyId);
|
|
2305
|
+
if (parsed.isGlobal || parsed.scope === resource.systemPath) continue;
|
|
2306
|
+
if (hasScopedTopologyGrant(organizationModel, resource, parsed.scope, ontologyId)) continue;
|
|
2307
|
+
const missingGrant = `systemInterfaceGrant(${resource.systemPath} -> ${parsed.scope}, resourceIds: ["${resource.id}"], ontologyIds: ["${ontologyId}"])`;
|
|
2308
|
+
addGovernanceIssue(
|
|
2309
|
+
issues,
|
|
2310
|
+
"ontology-topology-grant-missing",
|
|
2311
|
+
orgName,
|
|
2312
|
+
resource.id,
|
|
2313
|
+
`[${orgName}] Resource '${resource.id}' in system '${resource.systemPath}' references ontology '${ontologyId}' from system '${parsed.scope}' without scoped topology grant '${missingGrant}'.`
|
|
2314
|
+
);
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
function hasScopedTopologyGrant(organizationModel, resource, targetSystemPath, ontologyId) {
|
|
2318
|
+
return Object.values(organizationModel.topology?.relationships ?? {}).some((relationship) => {
|
|
2319
|
+
if (relationship.kind !== "uses") return false;
|
|
2320
|
+
const grant = relationship.metadata?.["systemInterfaceGrant"];
|
|
2321
|
+
if (!isSystemInterfaceGrantRecord(grant)) return false;
|
|
2322
|
+
return grant.consumer.systemPath === resource.systemPath && grant.provider.systemPath === targetSystemPath && grant.resourceIds.includes(resource.id) && grant.ontologyIds.includes(ontologyId);
|
|
2323
|
+
});
|
|
2324
|
+
}
|
|
2325
|
+
function isSystemInterfaceGrantRecord(value) {
|
|
2326
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2327
|
+
const grant = value;
|
|
2328
|
+
return typeof grant.consumer?.systemPath === "string" && typeof grant.consumer.interfaceKey === "string" && typeof grant.provider?.systemPath === "string" && typeof grant.provider.interfaceKey === "string" && Array.isArray(grant.resourceIds) && grant.resourceIds.every((id) => typeof id === "string") && Array.isArray(grant.ontologyIds) && grant.ontologyIds.every((id) => typeof id === "string");
|
|
1666
2329
|
}
|
|
1667
2330
|
function addTopologyIssues(issues, orgName, deployment, organizationModel, systemsById, omResourcesById, ontologyIndex) {
|
|
1668
2331
|
const relationships = organizationModel.topology?.relationships;
|
|
@@ -1777,7 +2440,7 @@ function validateResourceGovernance(orgName, deployment, organizationModel = dep
|
|
|
1777
2440
|
`[${orgName}] Resource '${resource.id}' ontology descriptor mismatch between code and OM.`
|
|
1778
2441
|
);
|
|
1779
2442
|
}
|
|
1780
|
-
addOntologyBindingIssues(issues, orgName, resource, ontologyIndex);
|
|
2443
|
+
addOntologyBindingIssues(issues, orgName, resource, ontologyIndex, organizationModel);
|
|
1781
2444
|
}
|
|
1782
2445
|
for (const runtimeResource of runtimeResources) {
|
|
1783
2446
|
const omResource = omResourcesById.get(runtimeResource.resourceId);
|
|
@@ -1827,6 +2490,41 @@ function validateResourceGovernance(orgName, deployment, organizationModel = dep
|
|
|
1827
2490
|
issues
|
|
1828
2491
|
};
|
|
1829
2492
|
}
|
|
2493
|
+
function addSystemInterfaceIssue(issues, orgName, systemPath, interfaceKey, issue) {
|
|
2494
|
+
issues.push({
|
|
2495
|
+
type: issue.family,
|
|
2496
|
+
orgName,
|
|
2497
|
+
systemPath,
|
|
2498
|
+
interfaceKey,
|
|
2499
|
+
code: issue.code,
|
|
2500
|
+
path: issue.path,
|
|
2501
|
+
ref: issue.ref,
|
|
2502
|
+
message: `[${orgName}] ${issue.family}:${issue.code}: ${issue.message}`
|
|
2503
|
+
});
|
|
2504
|
+
}
|
|
2505
|
+
function validateDeclaredSystemInterfaceReadiness(orgName, organizationModel) {
|
|
2506
|
+
const issues = [];
|
|
2507
|
+
if (organizationModel === void 0) return { valid: true, issues };
|
|
2508
|
+
const model = organizationModel;
|
|
2509
|
+
for (const { path, system } of listAllSystems(model)) {
|
|
2510
|
+
if (system.apiInterface === void 0) continue;
|
|
2511
|
+
const interfaceKey = "api";
|
|
2512
|
+
const result = computeInterfaceReadiness(model, { systemPath: path, interfaceKey });
|
|
2513
|
+
for (const issue of result.issues) {
|
|
2514
|
+
addSystemInterfaceIssue(issues, orgName, path, interfaceKey, issue);
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
if (issues.length > 0) {
|
|
2518
|
+
const first = issues[0];
|
|
2519
|
+
throw new RegistryValidationError(
|
|
2520
|
+
first.orgName,
|
|
2521
|
+
`${first.systemPath}/${first.interfaceKey}`,
|
|
2522
|
+
first.path ?? "organizationModel.systems.apiInterface",
|
|
2523
|
+
first.message
|
|
2524
|
+
);
|
|
2525
|
+
}
|
|
2526
|
+
return { valid: true, issues };
|
|
2527
|
+
}
|
|
1830
2528
|
function validateDeploymentSpec(orgName, resources) {
|
|
1831
2529
|
const seenIds = /* @__PURE__ */ new Set();
|
|
1832
2530
|
resources.workflows?.forEach((workflow) => {
|
|
@@ -1864,6 +2562,7 @@ function validateDeploymentSpec(orgName, resources) {
|
|
|
1864
2562
|
}
|
|
1865
2563
|
});
|
|
1866
2564
|
validateResourceGovernance(orgName, resources);
|
|
2565
|
+
validateDeclaredSystemInterfaceReadiness(orgName, resources.organizationModel);
|
|
1867
2566
|
}
|
|
1868
2567
|
function validateResourceModelConfig(orgName, resourceId, modelConfig) {
|
|
1869
2568
|
try {
|
|
@@ -4690,6 +5389,7 @@ var ResourceRegistry = class {
|
|
|
4690
5389
|
const candidate = base ? {
|
|
4691
5390
|
...base,
|
|
4692
5391
|
version: incoming.version ?? base.version ?? "0.0.0",
|
|
5392
|
+
organizationModel: incoming.organizationModel ?? base.organizationModel,
|
|
4693
5393
|
workflows: [...base.workflows ?? [], ...incoming.workflows ?? []],
|
|
4694
5394
|
agents: [...base.agents ?? [], ...incoming.agents ?? []],
|
|
4695
5395
|
triggers: incoming.triggers,
|
|
@@ -4851,6 +5551,7 @@ var ResourceRegistry = class {
|
|
|
4851
5551
|
}
|
|
4852
5552
|
const existingOrg = this.registry[orgName];
|
|
4853
5553
|
if (existingOrg) {
|
|
5554
|
+
existingOrg.organizationModel = org.organizationModel ?? existingOrg.organizationModel;
|
|
4854
5555
|
existingOrg.workflows = [...existingOrg.workflows ?? [], ...org.workflows ?? []];
|
|
4855
5556
|
existingOrg.agents = [...existingOrg.agents ?? [], ...org.agents ?? []];
|
|
4856
5557
|
existingOrg.triggers = org.triggers;
|
|
@@ -6119,132 +6820,6 @@ function deriveActions(deal, actions = []) {
|
|
|
6119
6820
|
return actions.filter((a) => a.isAvailableFor(deal)).map(({ key, label, payloadSchema }) => ({ key, label, payloadSchema }));
|
|
6120
6821
|
}
|
|
6121
6822
|
|
|
6122
|
-
// ../core/src/business/acquisition/ontology-validation.ts
|
|
6123
|
-
var CRM_PIPELINE_CATALOG_ONTOLOGY_ID = formatOntologyId({
|
|
6124
|
-
scope: "sales.crm",
|
|
6125
|
-
kind: "catalog",
|
|
6126
|
-
localId: "crm.pipeline"
|
|
6127
|
-
});
|
|
6128
|
-
var LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID = formatOntologyId({
|
|
6129
|
-
scope: "sales.lead-gen",
|
|
6130
|
-
kind: "catalog",
|
|
6131
|
-
localId: "lead-gen.stage-catalog"
|
|
6132
|
-
});
|
|
6133
|
-
function createLeadGenStageCatalog(model) {
|
|
6134
|
-
return {
|
|
6135
|
-
id: LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID,
|
|
6136
|
-
label: "Lead Gen Processing Stages",
|
|
6137
|
-
ownerSystemId: "sales.lead-gen",
|
|
6138
|
-
kind: "processing-stage-catalog",
|
|
6139
|
-
entries: Object.fromEntries(
|
|
6140
|
-
Object.entries(getLeadGenStageCatalog(model)).map(([key, entry]) => [
|
|
6141
|
-
key,
|
|
6142
|
-
{
|
|
6143
|
-
...entry
|
|
6144
|
-
}
|
|
6145
|
-
])
|
|
6146
|
-
),
|
|
6147
|
-
legacyCatalogKey: "LEAD_GEN_STAGE_CATALOG"
|
|
6148
|
-
};
|
|
6149
|
-
}
|
|
6150
|
-
function isPlainRecord(value) {
|
|
6151
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
6152
|
-
}
|
|
6153
|
-
function mergeBridgeCatalogs(model) {
|
|
6154
|
-
const baseCatalogTypes = model.ontology?.catalogTypes ?? {};
|
|
6155
|
-
const bridgeCatalogTypes = {};
|
|
6156
|
-
if (baseCatalogTypes[LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID] === void 0) {
|
|
6157
|
-
bridgeCatalogTypes[LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID] = createLeadGenStageCatalog(model);
|
|
6158
|
-
}
|
|
6159
|
-
if (Object.keys(bridgeCatalogTypes).length === 0) return model;
|
|
6160
|
-
return {
|
|
6161
|
-
...model,
|
|
6162
|
-
ontology: {
|
|
6163
|
-
...model.ontology ?? {},
|
|
6164
|
-
catalogTypes: {
|
|
6165
|
-
...baseCatalogTypes,
|
|
6166
|
-
...bridgeCatalogTypes
|
|
6167
|
-
}
|
|
6168
|
-
}
|
|
6169
|
-
};
|
|
6170
|
-
}
|
|
6171
|
-
function compileBusinessOntologyValidationIndex(model) {
|
|
6172
|
-
const compilation = compileOrganizationOntology(mergeBridgeCatalogs(model));
|
|
6173
|
-
if (compilation.diagnostics.length > 0) {
|
|
6174
|
-
const summary = compilation.diagnostics.map((diagnostic) => diagnostic.message).join("; ");
|
|
6175
|
-
throw new Error(`Business ontology validation index failed to compile: ${summary}`);
|
|
6176
|
-
}
|
|
6177
|
-
const crmPipelineCatalog = compilation.ontology.catalogTypes[CRM_PIPELINE_CATALOG_ONTOLOGY_ID];
|
|
6178
|
-
const leadGenStageCatalog = compilation.ontology.catalogTypes[LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID];
|
|
6179
|
-
if (crmPipelineCatalog === void 0 || leadGenStageCatalog === void 0) {
|
|
6180
|
-
throw new Error("Business ontology validation index is missing CRM or lead-gen catalog bridge records");
|
|
6181
|
-
}
|
|
6182
|
-
return {
|
|
6183
|
-
ontology: compilation.ontology,
|
|
6184
|
-
crmPipelineCatalog,
|
|
6185
|
-
leadGenStageCatalog,
|
|
6186
|
-
actionTypesByLegacyId: indexActionTypesByLegacyId(compilation.ontology.actionTypes)
|
|
6187
|
-
};
|
|
6188
|
-
}
|
|
6189
|
-
function indexActionTypesByLegacyId(actionTypes) {
|
|
6190
|
-
const byLegacyId = {};
|
|
6191
|
-
for (const actionType of Object.values(actionTypes)) {
|
|
6192
|
-
const legacyActionId = actionType["legacyActionId"];
|
|
6193
|
-
if (typeof legacyActionId === "string") {
|
|
6194
|
-
byLegacyId[legacyActionId] = actionType;
|
|
6195
|
-
}
|
|
6196
|
-
}
|
|
6197
|
-
return byLegacyId;
|
|
6198
|
-
}
|
|
6199
|
-
function getCatalogEntries(catalog) {
|
|
6200
|
-
return isPlainRecord(catalog.entries) ? catalog.entries : {};
|
|
6201
|
-
}
|
|
6202
|
-
function asLeadGenStageEntry(key, value) {
|
|
6203
|
-
const record = isPlainRecord(value) ? value : {};
|
|
6204
|
-
const entity = record.entity === "contact" ? "contact" : "company";
|
|
6205
|
-
const additionalEntities = Array.isArray(record.additionalEntities) ? record.additionalEntities.filter(
|
|
6206
|
-
(item) => item === "company" || item === "contact"
|
|
6207
|
-
) : void 0;
|
|
6208
|
-
const recordEntity = record.recordEntity === "company" || record.recordEntity === "contact" ? record.recordEntity : void 0;
|
|
6209
|
-
const recordStageKey = typeof record.recordStageKey === "string" ? record.recordStageKey : void 0;
|
|
6210
|
-
return {
|
|
6211
|
-
key: typeof record.key === "string" ? record.key : key,
|
|
6212
|
-
label: typeof record.label === "string" ? record.label : key,
|
|
6213
|
-
description: typeof record.description === "string" ? record.description : "",
|
|
6214
|
-
order: typeof record.order === "number" ? record.order : 0,
|
|
6215
|
-
entity,
|
|
6216
|
-
...additionalEntities !== void 0 ? { additionalEntities } : {},
|
|
6217
|
-
...recordEntity !== void 0 ? { recordEntity } : {},
|
|
6218
|
-
...recordStageKey !== void 0 ? { recordStageKey } : {}
|
|
6219
|
-
};
|
|
6220
|
-
}
|
|
6221
|
-
function createLeadGenStageValidators(index) {
|
|
6222
|
-
const getCatalog = () => Object.fromEntries(
|
|
6223
|
-
Object.entries(getCatalogEntries(index.leadGenStageCatalog)).map(([key, value]) => [
|
|
6224
|
-
key,
|
|
6225
|
-
asLeadGenStageEntry(key, value)
|
|
6226
|
-
])
|
|
6227
|
-
);
|
|
6228
|
-
const getEntry = (stageKey) => getCatalog()[stageKey];
|
|
6229
|
-
return {
|
|
6230
|
-
getLeadGenStageCatalog: getCatalog,
|
|
6231
|
-
getLeadGenStageEntry: getEntry,
|
|
6232
|
-
isLeadGenStageKey: (stageKey) => getEntry(stageKey) !== void 0,
|
|
6233
|
-
isLeadGenStageValidForEntity: (stageKey, entity) => {
|
|
6234
|
-
const stage = getEntry(stageKey);
|
|
6235
|
-
return stage !== void 0 && (stage.entity === entity || stage.additionalEntities?.includes(entity) === true);
|
|
6236
|
-
},
|
|
6237
|
-
isLeadGenRecordStageValidForEntity: (stageKey, entity) => {
|
|
6238
|
-
const stage = getEntry(stageKey);
|
|
6239
|
-
return stage !== void 0 && (stage.entity === entity || stage.additionalEntities?.includes(entity) === true || stage.recordEntity === entity);
|
|
6240
|
-
},
|
|
6241
|
-
resolveLeadGenRecordStageKey: (stageKey, entity) => {
|
|
6242
|
-
const stage = getEntry(stageKey);
|
|
6243
|
-
return stage?.recordEntity === entity && stage.recordStageKey ? stage.recordStageKey : stageKey;
|
|
6244
|
-
}
|
|
6245
|
-
};
|
|
6246
|
-
}
|
|
6247
|
-
|
|
6248
6823
|
// src/project-deployment-spec.ts
|
|
6249
6824
|
function toSdkResourceDescriptor(resource, getResourceOntologyBinding) {
|
|
6250
6825
|
const ontologyBinding = getResourceOntologyBinding?.(resource.id);
|
|
@@ -6345,4 +6920,4 @@ function projectDeploymentSpec(options) {
|
|
|
6345
6920
|
}
|
|
6346
6921
|
var ListBuilderStageKeySchema = z.string().min(1);
|
|
6347
6922
|
|
|
6348
|
-
export { ActivityEventSchema, BuildPlanSnapshotStepSchema, ProspectingBuildTemplateSchema as BuildTemplateSchema, ContractRefResolutionError, CrmStageKeySchema, CrmStateKeySchema, EmailSchema, ExecutionError, ListBuilderStageKeySchema, ProcessingStageStatusSchema, RegistryValidationError, ResourceRegistry, StepType, ToolingError, bindResourceDescriptor, compileBusinessOntologyValidationIndex, concurrentPool, createLeadGenStageValidators, defineContract, defineResource, defineResourceOntology, defineResources, defineStep, defineTopology, defineTopologyRelationship, defineWorkflow, deriveActions, diagnosticOutput, integrationInput, isZodType, parseTopologyNodeRef, projectDeploymentSpec, projectTopologyRelationships, resolveContractRef, runDiagnostic, splitName, toSdkResourceDescriptor, topologyRef, topologyRelationship, validateResourceGovernance, withPlatformIntegrationResourceDescriptor, withPlatformIntegrationResourceDescriptors, withPlatformResourceDescriptor, withPlatformResourceDescriptors };
|
|
6923
|
+
export { ActivityEventSchema, BuildPlanSnapshotStepSchema, ProspectingBuildTemplateSchema as BuildTemplateSchema, ContractRefResolutionError, CrmStageKeySchema, CrmStateKeySchema, EmailSchema, ExecutionError, ListBuilderStageKeySchema, ProcessingStageStatusSchema, RegistryValidationError, ResourceRegistry, StepType, ToolingError, bindResourceDescriptor, compileBusinessOntologyValidationIndex, concurrentPool, createLeadGenStageValidators, defineContract, defineResource, defineResourceOntology, defineResources, defineStep, defineTopology, defineTopologyRelationship, defineWorkflow, deriveActions, diagnosticOutput, integrationInput, isZodType, parseTopologyNodeRef, projectDeploymentSpec, projectTopologyRelationships, resolveContractRef, runDiagnostic, splitName, toSdkResourceDescriptor, topologyRef, topologyRelationship, validateDeclaredSystemInterfaceReadiness, validateResourceGovernance, withPlatformIntegrationResourceDescriptor, withPlatformIntegrationResourceDescriptors, withPlatformResourceDescriptor, withPlatformResourceDescriptors };
|