@elevasis/core 0.37.0 → 0.38.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 +5 -1
- package/dist/index.d.ts +26 -3
- package/dist/index.js +21 -4
- package/dist/knowledge/index.d.ts +5 -1
- package/dist/organization-model/index.d.ts +26 -3
- package/dist/organization-model/index.js +21 -4
- package/dist/test-utils/index.d.ts +5 -1
- package/dist/test-utils/index.js +20 -3
- package/package.json +1 -1
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +356 -338
- package/src/_gen/__tests__/__snapshots__/system-interface-capabilities.md.snap +47 -0
- package/src/_gen/__tests__/scaffold-contracts.test.ts +47 -8
- package/src/business/acquisition/api-schemas.test.ts +13 -1
- package/src/business/acquisition/ontology-validation.ts +13 -22
- package/src/organization-model/__tests__/domains/systems.test.ts +34 -1
- package/src/organization-model/__tests__/schema.test.ts +47 -0
- package/src/organization-model/domains/systems.ts +22 -9
- package/src/organization-model/published.ts +2 -0
- package/src/platform/constants/versions.ts +1 -1
- package/src/platform/registry/__tests__/validation.test.ts +1404 -1318
- package/src/platform/registry/index.ts +90 -88
- package/src/platform/registry/types.ts +443 -425
- package/src/platform/registry/validation.ts +60 -1
- package/src/reference/_generated/contracts.md +356 -338
- package/src/reference/_generated/system-interface-capabilities.md +47 -0
- package/src/reference/glossary.md +14 -2
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<!-- @generated by scripts/monorepo/generate-scaffold-contracts.js -- DO NOT EDIT -->
|
|
2
|
+
<!-- Regenerate: pnpm scaffold:sync -->
|
|
3
|
+
|
|
4
|
+
# System Interface Capabilities
|
|
5
|
+
|
|
6
|
+
This catalog is generated from `SYSTEM_INTERFACE_PROFILES` and the derived-readiness checks in `packages/core/src/business/acquisition/ontology-validation.ts`.
|
|
7
|
+
|
|
8
|
+
System Interface profiles are a closed platform adoption handshake. Tenant custom Systems should extend behavior through workflows/operations plus ontology, resources, and topology instead of declaring custom `apiInterface.readinessProfile` values.
|
|
9
|
+
|
|
10
|
+
| Profile | Required System path | Interface key |
|
|
11
|
+
| --- | --- | --- |
|
|
12
|
+
| `sales.lead-gen.api` | `sales.lead-gen` | `api` |
|
|
13
|
+
| `sales.crm.api` | `sales.crm` | `api` |
|
|
14
|
+
| `sales.lead-gen.crm-handoff` | `sales.lead-gen` | `crm-handoff` |
|
|
15
|
+
|
|
16
|
+
## `sales.lead-gen.api`
|
|
17
|
+
|
|
18
|
+
- Required System path: `sales.lead-gen`
|
|
19
|
+
- Interface key: `api`
|
|
20
|
+
- Derived-readiness requirements:
|
|
21
|
+
- System Interface marker must be active and scope at least one active `sales.lead-gen` Resource.
|
|
22
|
+
- Object types: `sales.lead-gen:object/list`, `sales.lead-gen:object/company`, `sales.lead-gen:object/contact`.
|
|
23
|
+
- Scoped Resource `ontology.reads` bindings for each required object type.
|
|
24
|
+
- Catalog types with entries: `sales.lead-gen:catalog/build-template`, `sales.lead-gen:catalog/company-stage`, `sales.lead-gen:catalog/contact-stage`, and derived `sales.lead-gen:catalog/lead-gen.stage-catalog`.
|
|
25
|
+
- Scoped Resource `ontology.usesCatalogs` bindings for each required catalog.
|
|
26
|
+
- A lead-gen template-step catalog owned by `sales.lead-gen` whose `appliesTo` value is `sales.lead-gen:object/list`, plus a scoped `ontology.usesCatalogs` binding for it.
|
|
27
|
+
|
|
28
|
+
## `sales.crm.api`
|
|
29
|
+
|
|
30
|
+
- Required System path: `sales.crm`
|
|
31
|
+
- Interface key: `api`
|
|
32
|
+
- Derived-readiness requirements:
|
|
33
|
+
- System Interface marker must be active and scope at least one active `sales.crm` Resource.
|
|
34
|
+
- Catalog type with entries: `sales.crm:catalog/crm.pipeline`.
|
|
35
|
+
- Scoped Resource `ontology.usesCatalogs` binding for `sales.crm:catalog/crm.pipeline`.
|
|
36
|
+
|
|
37
|
+
## `sales.lead-gen.crm-handoff`
|
|
38
|
+
|
|
39
|
+
- Required System path: `sales.lead-gen`
|
|
40
|
+
- Interface key: `crm-handoff`
|
|
41
|
+
- Derived-readiness requirements:
|
|
42
|
+
- Derived handoff readiness is evaluated for `sales.lead-gen/crm-handoff`; it is not authored as a separate custom tenant API surface.
|
|
43
|
+
- Lead-gen API readiness requirements must pass for `sales.lead-gen/api`.
|
|
44
|
+
- CRM pipeline catalog must exist with entries: `sales.crm:catalog/crm.pipeline`. Foreign ownership is allowed because the provider is `sales.crm/api`.
|
|
45
|
+
- Derived scoped resources are active `sales.lead-gen` Resources whose `ontology.usesCatalogs` includes `sales.crm:catalog/crm.pipeline`.
|
|
46
|
+
- Provider readiness must pass for `sales.crm/api`.
|
|
47
|
+
- Topology must include a scoped `systemInterfaceGrant` relationship from consumer `sales.lead-gen/crm-handoff` to provider `sales.crm/api`.
|
|
@@ -16,8 +16,17 @@ import { describe, it, expect } from 'vitest'
|
|
|
16
16
|
/** Monorepo root relative to packages/core/src/_gen/__tests__/ */
|
|
17
17
|
const ROOT = resolve(import.meta.dirname, '..', '..', '..', '..', '..')
|
|
18
18
|
|
|
19
|
-
const OUTPUT_PATH = resolve(ROOT, 'packages/core/src/reference/_generated/contracts.md')
|
|
20
|
-
const SNAPSHOT_PATH = resolve(import.meta.dirname, '__snapshots__', 'contracts.md.snap')
|
|
19
|
+
const OUTPUT_PATH = resolve(ROOT, 'packages/core/src/reference/_generated/contracts.md')
|
|
20
|
+
const SNAPSHOT_PATH = resolve(import.meta.dirname, '__snapshots__', 'contracts.md.snap')
|
|
21
|
+
const CAPABILITY_CATALOG_OUTPUT_PATH = resolve(
|
|
22
|
+
ROOT,
|
|
23
|
+
'packages/core/src/reference/_generated/system-interface-capabilities.md'
|
|
24
|
+
)
|
|
25
|
+
const CAPABILITY_CATALOG_SNAPSHOT_PATH = resolve(
|
|
26
|
+
import.meta.dirname,
|
|
27
|
+
'__snapshots__',
|
|
28
|
+
'system-interface-capabilities.md.snap'
|
|
29
|
+
)
|
|
21
30
|
|
|
22
31
|
function normalizeSnapshotContent(content: string) {
|
|
23
32
|
return content
|
|
@@ -29,7 +38,7 @@ function normalizeSnapshotContent(content: string) {
|
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
describe('scaffold-contracts generator', () => {
|
|
32
|
-
it('output file exists and has content', () => {
|
|
41
|
+
it('output file exists and has content', () => {
|
|
33
42
|
// The generator must have been run (either manually or by CI gen step).
|
|
34
43
|
// This test validates the committed artifact — it does NOT re-run the generator
|
|
35
44
|
// so the test suite stays fast and deterministic.
|
|
@@ -43,8 +52,22 @@ describe('scaffold-contracts generator', () => {
|
|
|
43
52
|
)
|
|
44
53
|
}
|
|
45
54
|
|
|
46
|
-
expect(content.length).toBeGreaterThan(0)
|
|
47
|
-
})
|
|
55
|
+
expect(content.length).toBeGreaterThan(0)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('capability catalog output file exists and has content', () => {
|
|
59
|
+
let content: string
|
|
60
|
+
try {
|
|
61
|
+
content = readFileSync(CAPABILITY_CATALOG_OUTPUT_PATH, 'utf8')
|
|
62
|
+
} catch {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Generated file not found: ${CAPABILITY_CATALOG_OUTPUT_PATH}\n` +
|
|
65
|
+
`Run "pnpm scaffold:generate" or "node scripts/monorepo/generate-scaffold-contracts.js" first.`
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
expect(content.length).toBeGreaterThan(0)
|
|
70
|
+
})
|
|
48
71
|
|
|
49
72
|
it('output file matches stored snapshot', () => {
|
|
50
73
|
let content: string
|
|
@@ -59,6 +82,22 @@ describe('scaffold-contracts generator', () => {
|
|
|
59
82
|
|
|
60
83
|
const snapshot = readFileSync(SNAPSHOT_PATH, 'utf8')
|
|
61
84
|
|
|
62
|
-
expect(normalizeSnapshotContent(content)).toBe(normalizeSnapshotContent(snapshot))
|
|
63
|
-
})
|
|
64
|
-
|
|
85
|
+
expect(normalizeSnapshotContent(content)).toBe(normalizeSnapshotContent(snapshot))
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('capability catalog output file matches stored snapshot', () => {
|
|
89
|
+
let content: string
|
|
90
|
+
try {
|
|
91
|
+
content = readFileSync(CAPABILITY_CATALOG_OUTPUT_PATH, 'utf8')
|
|
92
|
+
} catch {
|
|
93
|
+
throw new Error(
|
|
94
|
+
`Generated file not found: ${CAPABILITY_CATALOG_OUTPUT_PATH}\n` +
|
|
95
|
+
`Run "pnpm scaffold:generate" first to produce the artifact before snapshotting.`
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const snapshot = readFileSync(CAPABILITY_CATALOG_SNAPSHOT_PATH, 'utf8')
|
|
100
|
+
|
|
101
|
+
expect(normalizeSnapshotContent(content)).toBe(normalizeSnapshotContent(snapshot))
|
|
102
|
+
})
|
|
103
|
+
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
2
|
import { type CrmPriorityRuleConfig, type StatefulPipelineDefinition } from '../../organization-model/domains/sales'
|
|
3
|
+
import { SYSTEM_INTERFACE_PROFILES } from '../../organization-model/domains/systems'
|
|
3
4
|
|
|
4
5
|
// Inline fixture for lead-gen pipeline stage/state validation tests.
|
|
5
6
|
// The canonical constants live in @repo/elevasis-core; @repo/core cannot depend on it.
|
|
@@ -503,6 +504,14 @@ describe('DealStageSchema', () => {
|
|
|
503
504
|
// ---------------------------------------------------------------------------
|
|
504
505
|
|
|
505
506
|
describe('business ontology validation contracts', () => {
|
|
507
|
+
it('derives compatibility interface constants from the canonical profile registry', () => {
|
|
508
|
+
expect(SYSTEM_INTERFACE_PROFILES).toEqual([
|
|
509
|
+
LEAD_GEN_API_INTERFACE,
|
|
510
|
+
CRM_API_INTERFACE,
|
|
511
|
+
LEAD_GEN_CRM_HANDOFF_INTERFACE
|
|
512
|
+
])
|
|
513
|
+
})
|
|
514
|
+
|
|
506
515
|
it('computes structured readiness for a ready lead-gen API interface without CRM', () => {
|
|
507
516
|
const model = buildMinimalLeadGenModel()
|
|
508
517
|
const readiness = computeInterfaceReadiness(model, LEAD_GEN_API_INTERFACE)
|
|
@@ -655,12 +664,15 @@ describe('business ontology validation contracts', () => {
|
|
|
655
664
|
const model = buildMinimalLeadGenModel()
|
|
656
665
|
const apiInterface = model.systems.sales?.systems?.['lead-gen']?.apiInterface
|
|
657
666
|
if (apiInterface === undefined) throw new Error('Minimal fixture is missing lead-gen API interface')
|
|
658
|
-
apiInterface.readinessProfile = 'sales.lead-gen.unknown'
|
|
667
|
+
;(apiInterface as { readinessProfile?: string }).readinessProfile = 'sales.lead-gen.unknown'
|
|
659
668
|
|
|
660
669
|
const readiness = computeInterfaceReadiness(model, LEAD_GEN_API_INTERFACE)
|
|
661
670
|
|
|
662
671
|
expect(readiness.ready).toBe(false)
|
|
663
672
|
expect(readiness.issues).toMatchObject([{ family: 'SYSTEM_INTERFACE_INVALID', code: 'unknown-readiness-profile' }])
|
|
673
|
+
expect(readiness.issues[0]?.message).toContain('Supported profiles: "sales.lead-gen.api", "sales.crm.api", "sales.lead-gen.crm-handoff"')
|
|
674
|
+
expect(readiness.issues[0]?.message).toContain('Custom Systems should not declare apiInterface')
|
|
675
|
+
expect(readiness.issues[0]?.message).toContain('workflows/operations plus ontology, resources, and topology')
|
|
664
676
|
})
|
|
665
677
|
|
|
666
678
|
it('lead-gen-to-CRM handoff validation fails when the scoped topology grant is absent', () => {
|
|
@@ -10,6 +10,10 @@ import {
|
|
|
10
10
|
} from '../../organization-model/ontology'
|
|
11
11
|
import type { OrganizationModel } from '../../organization-model/types'
|
|
12
12
|
import type { ResourceEntry } from '../../organization-model/domains/resources'
|
|
13
|
+
import {
|
|
14
|
+
SYSTEM_INTERFACE_PROFILES,
|
|
15
|
+
SYSTEM_INTERFACE_READINESS_PROFILES
|
|
16
|
+
} from '../../organization-model/domains/systems'
|
|
13
17
|
import type { OmTopologyRelationship } from '../../organization-model/domains/topology'
|
|
14
18
|
import {
|
|
15
19
|
type LeadGenStageCatalogEntry,
|
|
@@ -18,23 +22,11 @@ import {
|
|
|
18
22
|
import { getSystem } from '../../organization-model/helpers'
|
|
19
23
|
import { getLeadGenStageCatalog } from '../../organization-model/migration-helpers'
|
|
20
24
|
|
|
21
|
-
export const LEAD_GEN_API_INTERFACE =
|
|
22
|
-
systemPath: 'sales.lead-gen',
|
|
23
|
-
interfaceKey: 'api',
|
|
24
|
-
readinessProfile: 'sales.lead-gen.api'
|
|
25
|
-
} as const
|
|
25
|
+
export const LEAD_GEN_API_INTERFACE = SYSTEM_INTERFACE_PROFILES[0]
|
|
26
26
|
|
|
27
|
-
export const CRM_API_INTERFACE =
|
|
28
|
-
systemPath: 'sales.crm',
|
|
29
|
-
interfaceKey: 'api',
|
|
30
|
-
readinessProfile: 'sales.crm.api'
|
|
31
|
-
} as const
|
|
27
|
+
export const CRM_API_INTERFACE = SYSTEM_INTERFACE_PROFILES[1]
|
|
32
28
|
|
|
33
|
-
export const LEAD_GEN_CRM_HANDOFF_INTERFACE =
|
|
34
|
-
systemPath: 'sales.lead-gen',
|
|
35
|
-
interfaceKey: 'crm-handoff',
|
|
36
|
-
readinessProfile: 'sales.lead-gen.crm-handoff'
|
|
37
|
-
} as const
|
|
29
|
+
export const LEAD_GEN_CRM_HANDOFF_INTERFACE = SYSTEM_INTERFACE_PROFILES[2]
|
|
38
30
|
|
|
39
31
|
const LEAD_GEN_API_READINESS_LABEL = LEAD_GEN_API_INTERFACE.readinessProfile
|
|
40
32
|
const CRM_API_READINESS_LABEL = CRM_API_INTERFACE.readinessProfile
|
|
@@ -215,6 +207,10 @@ function formatInterfaceReadinessFailure(result: SystemInterfaceReadinessResult)
|
|
|
215
207
|
return `${identity} readiness failed${result.readinessProfile ? ` (${result.readinessProfile})` : ''}: ${issueSummary}`
|
|
216
208
|
}
|
|
217
209
|
|
|
210
|
+
function formatSupportedReadinessProfiles(): string {
|
|
211
|
+
return SYSTEM_INTERFACE_READINESS_PROFILES.map((profile) => `"${profile}"`).join(', ')
|
|
212
|
+
}
|
|
213
|
+
|
|
218
214
|
function throwIfInterfaceNotReady(result: SystemInterfaceReadinessResult): void {
|
|
219
215
|
if (result.ready) return
|
|
220
216
|
throw new SystemInterfaceReadinessError(result)
|
|
@@ -607,20 +603,15 @@ export function computeInterfaceReadiness(
|
|
|
607
603
|
)
|
|
608
604
|
}
|
|
609
605
|
|
|
610
|
-
const supportedProfiles = [
|
|
611
|
-
LEAD_GEN_API_INTERFACE.readinessProfile,
|
|
612
|
-
CRM_API_INTERFACE.readinessProfile,
|
|
613
|
-
LEAD_GEN_CRM_HANDOFF_INTERFACE.readinessProfile
|
|
614
|
-
] as const
|
|
615
606
|
const supportedProfile =
|
|
616
|
-
readinessProfile !== undefined &&
|
|
607
|
+
readinessProfile !== undefined && SYSTEM_INTERFACE_PROFILES.some((profile) => profile.readinessProfile === readinessProfile)
|
|
617
608
|
|
|
618
609
|
if (!supportedProfile) {
|
|
619
610
|
addReadinessIssue(
|
|
620
611
|
issues,
|
|
621
612
|
'SYSTEM_INTERFACE_INVALID',
|
|
622
613
|
'unknown-readiness-profile',
|
|
623
|
-
`System Interface "${formatInterfaceIdentity(request.systemPath, request.interfaceKey)}" references unknown readiness profile "${readinessProfile}".`,
|
|
614
|
+
`System Interface "${formatInterfaceIdentity(request.systemPath, request.interfaceKey)}" references unknown readiness profile "${readinessProfile}". Supported profiles: ${formatSupportedReadinessProfiles()}. Custom Systems should not declare apiInterface; route custom behavior through workflows/operations plus ontology, resources, and topology.`,
|
|
624
615
|
{ path: `${readinessMarkerPath(request)}.readinessProfile`, ref: readinessProfile }
|
|
625
616
|
)
|
|
626
617
|
return {
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from 'vitest'
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_ORGANIZATION_MODEL_SYSTEMS,
|
|
4
|
+
SYSTEM_INTERFACE_PROFILES,
|
|
4
5
|
SystemEntrySchema,
|
|
5
6
|
SystemApiInterfaceSchema,
|
|
7
|
+
SystemInterfaceReadinessProfileSchema,
|
|
6
8
|
SystemInterfaceRefSchema,
|
|
7
9
|
SystemKindSchema,
|
|
8
10
|
SystemStatusSchema,
|
|
9
11
|
SystemsDomainSchema
|
|
10
12
|
} from '../../domains/systems'
|
|
11
|
-
import { resolveOrganizationModel } from '../../resolve'
|
|
13
|
+
import { resolveOrganizationModel } from '../../resolve'
|
|
14
|
+
import type {
|
|
15
|
+
OrganizationModelSystemApiInterface,
|
|
16
|
+
OrganizationModelSystemInterfaceReadinessProfile
|
|
17
|
+
} from '../../types'
|
|
12
18
|
|
|
13
19
|
const VALID_SYSTEM = {
|
|
14
20
|
id: 'sys.lead-gen',
|
|
@@ -60,6 +66,32 @@ describe('SystemEntrySchema - positive parse', () => {
|
|
|
60
66
|
}
|
|
61
67
|
})
|
|
62
68
|
|
|
69
|
+
it('accepts only registry-backed readiness profiles', () => {
|
|
70
|
+
const profileValues = SYSTEM_INTERFACE_PROFILES.map((profile) => profile.readinessProfile)
|
|
71
|
+
|
|
72
|
+
for (const readinessProfile of profileValues) {
|
|
73
|
+
expect(SystemInterfaceReadinessProfileSchema.safeParse(readinessProfile).success).toBe(true)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
expect(SystemInterfaceReadinessProfileSchema.safeParse('sales.lead-gen.unknown').success).toBe(false)
|
|
77
|
+
expect(SystemApiInterfaceSchema.safeParse({ readinessProfile: 'sales.lead-gen.unknown' }).success).toBe(false)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('publishes a narrowed System Interface readiness profile type', () => {
|
|
81
|
+
const acceptedReadinessProfile: OrganizationModelSystemInterfaceReadinessProfile = 'sales.lead-gen.api'
|
|
82
|
+
const acceptedInterface: OrganizationModelSystemApiInterface = {
|
|
83
|
+
lifecycle: 'active',
|
|
84
|
+
readinessProfile: acceptedReadinessProfile,
|
|
85
|
+
resourceIds: []
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// @ts-expect-error unsupported profiles are not part of the published readiness profile union.
|
|
89
|
+
const unsupportedReadinessProfile: OrganizationModelSystemInterfaceReadinessProfile = 'sales.lead-gen.unknown'
|
|
90
|
+
|
|
91
|
+
expect(acceptedInterface.readinessProfile).toBe('sales.lead-gen.api')
|
|
92
|
+
expect(unsupportedReadinessProfile).toBe('sales.lead-gen.unknown')
|
|
93
|
+
})
|
|
94
|
+
|
|
63
95
|
it('represents missing and disabled System Interfaces distinctly', () => {
|
|
64
96
|
const missing = SystemEntrySchema.parse(VALID_SYSTEM)
|
|
65
97
|
const disabled = SystemEntrySchema.parse({
|
|
@@ -131,6 +163,7 @@ describe('SystemEntrySchema - negative parse', () => {
|
|
|
131
163
|
expect(SystemApiInterfaceSchema.safeParse({ kind: 'api', lifecycle: 'active' }).success).toBe(false)
|
|
132
164
|
expect(SystemApiInterfaceSchema.safeParse({ lifecycle: 'paused' }).success).toBe(false)
|
|
133
165
|
expect(SystemApiInterfaceSchema.safeParse({ readinessProfile: 'Sales Lead Gen API' }).success).toBe(false)
|
|
166
|
+
expect(SystemApiInterfaceSchema.safeParse({ readinessProfile: 'sales.lead-gen.unknown' }).success).toBe(false)
|
|
134
167
|
expect(
|
|
135
168
|
SystemInterfaceRefSchema.safeParse({ systemPath: 'sales.lead-gen', interfaceKey: 'api' }).success
|
|
136
169
|
).toBe(true)
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
listResolvedOntologyRecords,
|
|
8
8
|
parseOntologyId
|
|
9
9
|
} from '../ontology'
|
|
10
|
+
import { SYSTEM_INTERFACE_PROFILES } from '../domains/systems'
|
|
10
11
|
import { resolveOrganizationModelWithResources } from '../resolve'
|
|
11
12
|
import { OrganizationModelSchema } from '../schema'
|
|
12
13
|
|
|
@@ -236,6 +237,52 @@ describe('retired contract authoring surfaces', () => {
|
|
|
236
237
|
})
|
|
237
238
|
|
|
238
239
|
describe('System Interface schema integration', () => {
|
|
240
|
+
it('accepts every registry-backed System Interface readiness profile', () => {
|
|
241
|
+
for (const profile of SYSTEM_INTERFACE_PROFILES) {
|
|
242
|
+
const result = OrganizationModelSchema.safeParse({
|
|
243
|
+
...makeMinimalModel({
|
|
244
|
+
sales: {
|
|
245
|
+
id: 'sales',
|
|
246
|
+
order: 10,
|
|
247
|
+
label: 'Sales',
|
|
248
|
+
lifecycle: 'active'
|
|
249
|
+
},
|
|
250
|
+
[profile.systemPath]: {
|
|
251
|
+
...makeSystem(profile.systemPath, `/${profile.systemPath.replaceAll('.', '/')}`),
|
|
252
|
+
apiInterface: {
|
|
253
|
+
lifecycle: 'active',
|
|
254
|
+
readinessProfile: profile.readinessProfile
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
})
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
expect(result.success).toBe(true)
|
|
261
|
+
}
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
it('rejects unsupported System Interface readiness profiles', () => {
|
|
265
|
+
const messages = getIssueMessages(
|
|
266
|
+
makeMinimalModel({
|
|
267
|
+
sales: {
|
|
268
|
+
id: 'sales',
|
|
269
|
+
order: 10,
|
|
270
|
+
label: 'Sales',
|
|
271
|
+
lifecycle: 'active'
|
|
272
|
+
},
|
|
273
|
+
'sales.lead-gen': {
|
|
274
|
+
...makeSystem('sales.lead-gen', '/lead-gen/lists'),
|
|
275
|
+
apiInterface: {
|
|
276
|
+
lifecycle: 'active',
|
|
277
|
+
readinessProfile: 'sales.lead-gen.unknown'
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
})
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
expect(messages.some((message) => message.includes('Invalid option'))).toBe(true)
|
|
284
|
+
})
|
|
285
|
+
|
|
239
286
|
it('accepts a System API Interface scoped to resources from the same System', () => {
|
|
240
287
|
const result = OrganizationModelSchema.safeParse({
|
|
241
288
|
...makeMinimalModel({
|
|
@@ -65,15 +65,28 @@ export const SystemInterfaceKeySchema = ModelIdSchema
|
|
|
65
65
|
export const SystemInterfaceLifecycleSchema = z
|
|
66
66
|
.enum(['draft', 'active', 'disabled', 'deprecated', 'archived'])
|
|
67
67
|
.meta({ label: 'System interface lifecycle', color: 'teal' })
|
|
68
|
-
export const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
'
|
|
76
|
-
|
|
68
|
+
export const SYSTEM_INTERFACE_PROFILES = [
|
|
69
|
+
{
|
|
70
|
+
systemPath: 'sales.lead-gen',
|
|
71
|
+
interfaceKey: 'api',
|
|
72
|
+
readinessProfile: 'sales.lead-gen.api'
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
systemPath: 'sales.crm',
|
|
76
|
+
interfaceKey: 'api',
|
|
77
|
+
readinessProfile: 'sales.crm.api'
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
systemPath: 'sales.lead-gen',
|
|
81
|
+
interfaceKey: 'crm-handoff',
|
|
82
|
+
readinessProfile: 'sales.lead-gen.crm-handoff'
|
|
83
|
+
}
|
|
84
|
+
] as const
|
|
85
|
+
type SystemInterfaceReadinessProfileValue = (typeof SYSTEM_INTERFACE_PROFILES)[number]['readinessProfile']
|
|
86
|
+
export const SYSTEM_INTERFACE_READINESS_PROFILES = SYSTEM_INTERFACE_PROFILES.map(
|
|
87
|
+
(profile) => profile.readinessProfile
|
|
88
|
+
) as [SystemInterfaceReadinessProfileValue, ...SystemInterfaceReadinessProfileValue[]]
|
|
89
|
+
export const SystemInterfaceReadinessProfileSchema = z.enum(SYSTEM_INTERFACE_READINESS_PROFILES)
|
|
77
90
|
export const SystemInterfaceResourceScopeSchema = z
|
|
78
91
|
.array(ModelIdSchema)
|
|
79
92
|
.default([])
|