@elevasis/core 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2330 -2391
- package/dist/index.js +2322 -1147
- package/dist/knowledge/index.d.ts +702 -1136
- package/dist/knowledge/index.js +9 -9
- package/dist/organization-model/index.d.ts +2330 -2391
- package/dist/organization-model/index.js +2322 -1147
- package/dist/test-utils/index.d.ts +703 -1106
- package/dist/test-utils/index.js +1735 -1089
- package/package.json +1 -1
- package/src/__tests__/template-core-compatibility.test.ts +11 -79
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +360 -98
- package/src/business/acquisition/api-schemas.test.ts +2 -2
- package/src/business/acquisition/api-schemas.ts +7 -9
- package/src/business/acquisition/build-templates.test.ts +4 -4
- package/src/business/acquisition/build-templates.ts +72 -30
- package/src/business/acquisition/crm-state-actions.test.ts +13 -11
- package/src/business/acquisition/types.ts +7 -3
- package/src/execution/engine/agent/core/types.ts +1 -1
- package/src/execution/engine/workflow/types.ts +2 -2
- package/src/knowledge/README.md +8 -7
- package/src/knowledge/__tests__/queries.test.ts +74 -73
- package/src/knowledge/format.ts +10 -9
- package/src/knowledge/index.ts +1 -1
- package/src/knowledge/published.ts +1 -1
- package/src/knowledge/queries.ts +26 -25
- package/src/organization-model/README.md +66 -26
- package/src/organization-model/__tests__/content-kinds-registry.test.ts +210 -0
- package/src/organization-model/__tests__/defaults.test.ts +72 -98
- package/src/organization-model/__tests__/domains/actions.test.ts +56 -0
- package/src/organization-model/__tests__/domains/customers.test.ts +299 -295
- package/src/organization-model/__tests__/domains/entities.test.ts +56 -0
- package/src/organization-model/__tests__/domains/goals.test.ts +493 -479
- package/src/organization-model/__tests__/domains/identity.test.ts +280 -279
- package/src/organization-model/__tests__/domains/navigation.test.ts +268 -212
- package/src/organization-model/__tests__/domains/offerings.test.ts +414 -419
- package/src/organization-model/__tests__/domains/policies.test.ts +323 -0
- package/src/organization-model/__tests__/domains/resource-mappings.test.ts +271 -271
- package/src/organization-model/__tests__/domains/resources.test.ts +159 -37
- package/src/organization-model/__tests__/domains/roles.test.ts +147 -86
- package/src/organization-model/__tests__/domains/statuses.test.ts +246 -243
- package/src/organization-model/__tests__/domains/systems.test.ts +67 -51
- package/src/organization-model/__tests__/flatten-additive-merge.test.ts +361 -0
- package/src/organization-model/__tests__/foundation.test.ts +74 -102
- package/src/organization-model/__tests__/get-resources-for-system.test.ts +144 -0
- package/src/organization-model/__tests__/graph.test.ts +899 -71
- package/src/organization-model/__tests__/knowledge.test.ts +173 -52
- package/src/organization-model/__tests__/lookup-helpers.test.ts +438 -0
- package/src/organization-model/__tests__/migration-helpers.test.ts +591 -0
- package/src/organization-model/__tests__/prospecting-ssot.test.ts +36 -27
- package/src/organization-model/__tests__/recursive-system-schema.test.ts +520 -0
- package/src/organization-model/__tests__/resolve.test.ts +174 -23
- package/src/organization-model/__tests__/schema.test.ts +291 -114
- package/src/organization-model/__tests__/surface-projection.test.ts +207 -97
- package/src/organization-model/catalogs/lead-gen.ts +144 -0
- package/src/organization-model/content-kinds/config.ts +36 -0
- package/src/organization-model/content-kinds/index.ts +74 -0
- package/src/organization-model/content-kinds/pipeline.ts +68 -0
- package/src/organization-model/content-kinds/registry.ts +44 -0
- package/src/organization-model/content-kinds/status.ts +71 -0
- package/src/organization-model/content-kinds/template.ts +83 -0
- package/src/organization-model/content-kinds/types.ts +117 -0
- package/src/organization-model/contracts.ts +13 -3
- package/src/organization-model/defaults.ts +488 -96
- package/src/organization-model/domains/actions.ts +239 -0
- package/src/organization-model/domains/customers.ts +78 -75
- package/src/organization-model/domains/entities.ts +144 -0
- package/src/organization-model/domains/goals.ts +83 -80
- package/src/organization-model/domains/knowledge.ts +74 -16
- package/src/organization-model/domains/navigation.ts +107 -384
- package/src/organization-model/domains/offerings.ts +71 -66
- package/src/organization-model/domains/policies.ts +102 -0
- package/src/organization-model/domains/projects.ts +14 -48
- package/src/organization-model/domains/prospecting.ts +62 -181
- package/src/organization-model/domains/resources.ts +81 -24
- package/src/organization-model/domains/roles.ts +13 -10
- package/src/organization-model/domains/sales.ts +10 -219
- package/src/organization-model/domains/shared.ts +57 -57
- package/src/organization-model/domains/statuses.ts +339 -130
- package/src/organization-model/domains/systems.ts +186 -29
- package/src/organization-model/foundation.ts +54 -67
- package/src/organization-model/graph/build.ts +682 -54
- package/src/organization-model/graph/link.ts +1 -1
- package/src/organization-model/graph/schema.ts +24 -9
- package/src/organization-model/graph/types.ts +20 -7
- package/src/organization-model/helpers.ts +231 -26
- package/src/organization-model/index.ts +116 -5
- package/src/organization-model/migration-helpers.ts +249 -0
- package/src/organization-model/organization-graph.mdx +16 -15
- package/src/organization-model/organization-model.mdx +89 -41
- package/src/organization-model/published.ts +120 -18
- package/src/organization-model/resolve.ts +117 -54
- package/src/organization-model/schema.ts +561 -140
- package/src/organization-model/surface-projection.ts +116 -122
- package/src/organization-model/types.ts +102 -21
- package/src/platform/constants/versions.ts +1 -1
- package/src/platform/registry/__tests__/command-view.test.ts +6 -8
- package/src/platform/registry/__tests__/resource-link.test.ts +13 -8
- package/src/platform/registry/__tests__/resource-registry.integration.test.ts +16 -31
- package/src/platform/registry/__tests__/resource-registry.nested-systems.test.ts +245 -0
- package/src/platform/registry/__tests__/resource-registry.test.ts +9 -7
- package/src/platform/registry/__tests__/validation.test.ts +15 -11
- package/src/platform/registry/resource-registry.ts +20 -8
- package/src/platform/registry/serialization.ts +7 -7
- package/src/platform/registry/types.ts +3 -3
- package/src/platform/registry/validation.ts +17 -15
- package/src/reference/_generated/contracts.md +362 -99
- package/src/reference/glossary.md +18 -18
- package/src/supabase/database.types.ts +60 -0
- package/src/test-utils/test-utils.test.ts +1 -6
- package/src/organization-model/__tests__/domains/operations.test.ts +0 -203
- package/src/organization-model/domains/features.ts +0 -31
- package/src/organization-model/domains/operations.ts +0 -85
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
# Organization Model
|
|
2
2
|
|
|
3
|
-
The organization model is the published semantic contract that maps a tenant's
|
|
3
|
+
The organization model is the published semantic contract that maps a tenant's System hierarchy, shell navigation, business semantics, resource governance, and graph bindings.
|
|
4
4
|
|
|
5
|
-
Use this module when resolving or validating an organization's contract before wiring UI shells, routing,
|
|
5
|
+
Use this module when resolving or validating an organization's contract before wiring UI shells, routing, system gates, or domain-specific capability checks.
|
|
6
6
|
|
|
7
7
|
## Published Exports
|
|
8
8
|
|
|
9
9
|
The public entry point exposes:
|
|
10
10
|
|
|
11
11
|
- `OrganizationModelSchema`
|
|
12
|
-
- `FeatureSchema`
|
|
13
12
|
- `DEFAULT_ORGANIZATION_MODEL`
|
|
14
13
|
- `defineOrganizationModel`
|
|
15
14
|
- `resolveOrganizationModel`
|
|
16
15
|
- `createFoundationOrganizationModel`
|
|
17
|
-
- node ID and
|
|
16
|
+
- node ID and System helper types
|
|
18
17
|
- `OrganizationModel` and supporting domain types
|
|
19
18
|
|
|
20
19
|
Import it from the published subpath:
|
|
@@ -36,8 +35,8 @@ The model is versioned and currently validates against `version: 1`.
|
|
|
36
35
|
Top-level fields:
|
|
37
36
|
|
|
38
37
|
- `version`
|
|
38
|
+
- `domainMetadata`
|
|
39
39
|
- `branding`
|
|
40
|
-
- `features`
|
|
41
40
|
- `navigation`
|
|
42
41
|
- `sales`
|
|
43
42
|
- `prospecting`
|
|
@@ -49,40 +48,81 @@ Top-level fields:
|
|
|
49
48
|
- `goals`
|
|
50
49
|
- `systems`
|
|
51
50
|
- `resources`
|
|
51
|
+
- `capabilities`
|
|
52
|
+
- `policies`
|
|
52
53
|
- `statuses`
|
|
53
|
-
- `operations`
|
|
54
54
|
- `knowledge`
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
The pure collection domains are id-keyed maps: `systems`, `roles`, `goals`, `customers`, `offerings`, `resources`, `capabilities`, `policies`, and `statuses`. The map key must match the entry `id`. Entries carry `order` for deterministic ordered views; use `listDomain(record)` when order matters.
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
Resource identity is authored in `resources`. Runtime workflows, agents, integrations, and scripts import those descriptors, derive `resourceId` and kind from them, and attach executable behavior in operations code.
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
## System Set
|
|
61
|
+
|
|
62
|
+
System hierarchy is authored as an id-keyed `systems` map. Dotted IDs and `parentSystemId` define parent/child relationships:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
systems: {
|
|
66
|
+
dashboard: { id: 'dashboard', order: 10, label: 'Dashboard', lifecycle: 'active' },
|
|
67
|
+
sales: { id: 'sales', order: 20, label: 'Sales', lifecycle: 'active' },
|
|
68
|
+
clients: { id: 'clients', order: 30, label: 'Clients', lifecycle: 'active' },
|
|
69
|
+
projects: { id: 'projects', order: 40, label: 'Projects', lifecycle: 'active' }
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Systems describe semantic ownership, hierarchy, lifecycle, access, and governance. Sidebar presentation is authored separately under `navigation.sidebar`; UI-backed Systems may still provide `ui.path` for route matching during the migration, but they do not author composed surface lists. Lifecycle values such as `active`, `beta`, `deprecated`, and `archived` replace the old feature enabled/dev-only split.
|
|
74
|
+
|
|
75
|
+
## Sidebar Navigation
|
|
76
|
+
|
|
77
|
+
Shell navigation is authored as a recursive sidebar tree:
|
|
61
78
|
|
|
62
79
|
```ts
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
80
|
+
navigation: {
|
|
81
|
+
sidebar: {
|
|
82
|
+
primary: {
|
|
83
|
+
dashboard: {
|
|
84
|
+
type: 'surface',
|
|
85
|
+
order: 10,
|
|
86
|
+
label: 'Dashboard',
|
|
87
|
+
path: '/',
|
|
88
|
+
surfaceType: 'dashboard',
|
|
89
|
+
targets: { systems: ['dashboard'] }
|
|
90
|
+
},
|
|
91
|
+
business: {
|
|
92
|
+
type: 'group',
|
|
93
|
+
order: 20,
|
|
94
|
+
label: 'Business',
|
|
95
|
+
children: {
|
|
96
|
+
clients: {
|
|
97
|
+
type: 'surface',
|
|
98
|
+
order: 20,
|
|
99
|
+
label: 'Clients',
|
|
100
|
+
path: '/clients',
|
|
101
|
+
surfaceType: 'list',
|
|
102
|
+
targets: { systems: ['clients'] }
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
bottom: {}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
69
110
|
```
|
|
70
111
|
|
|
71
|
-
|
|
72
|
-
Development-only features remain in the contract with `enabled: true` and `devOnly: true`; shell consumers hide them and guard their paths outside development mode.
|
|
112
|
+
Routeable sidebar leaves are projected into flat semantic surface DTOs by `projectOrganizationSurfaces(model)`. Do not author top-level `surfaces` or `navigationGroups` fields on `OrganizationModel`.
|
|
73
113
|
|
|
74
114
|
## Graph IDs
|
|
75
115
|
|
|
76
116
|
Cross-collection links use kind-prefixed IDs:
|
|
77
117
|
|
|
78
|
-
- `
|
|
118
|
+
- `system:sales.crm`
|
|
79
119
|
- `integration:instantly`
|
|
80
120
|
- `resource:lead-import`
|
|
81
|
-
- `
|
|
121
|
+
- `action:operations.queue.review`
|
|
82
122
|
|
|
83
123
|
## Resource Descriptors
|
|
84
124
|
|
|
85
|
-
The OM Resources domain is governance-only. Descriptors declare canonical `id`, required `
|
|
125
|
+
The OM Resources domain is governance-only. Descriptors declare canonical `id`, required `systemPath`, governance `status`, and optional role ownership. `DeploymentSpec` remains the runtime/deploy assembly around those descriptors, not a second resource identity catalog.
|
|
86
126
|
|
|
87
127
|
## Resolution Semantics
|
|
88
128
|
|
|
@@ -90,20 +130,20 @@ The OM Resources domain is governance-only. Descriptors declare canonical `id`,
|
|
|
90
130
|
- `resolveOrganizationModel()` deep-merges a partial override into the default model, then validates it.
|
|
91
131
|
- `createFoundationOrganizationModel()` resolves the canonical model and returns UI-facing helper outputs.
|
|
92
132
|
- Plain objects merge recursively.
|
|
133
|
+
- Id-keyed domain maps merge additively by key.
|
|
93
134
|
- Arrays replace the default value.
|
|
94
135
|
- Missing fields fall back to `DEFAULT_ORGANIZATION_MODEL`.
|
|
95
136
|
|
|
96
137
|
## Referential Integrity
|
|
97
138
|
|
|
98
|
-
-
|
|
99
|
-
- Child
|
|
100
|
-
-
|
|
101
|
-
- Leaf features provide `path`.
|
|
139
|
+
- System IDs must be unique.
|
|
140
|
+
- Child System IDs require valid ancestors or `parentSystemId` links.
|
|
141
|
+
- Systems with UI provide `ui.path`.
|
|
102
142
|
- Systems, resources, roles, knowledge nodes, and goals must resolve their declared cross-references.
|
|
103
143
|
|
|
104
144
|
## Practical Guidance
|
|
105
145
|
|
|
106
146
|
- Use `resolveOrganizationModel()` when you need a runtime-safe model.
|
|
107
147
|
- Use `defineOrganizationModel()` when authoring static overrides.
|
|
108
|
-
- Keep
|
|
109
|
-
- Put resource identity and governance in `resources
|
|
148
|
+
- Keep System IDs stable because shell routing, gating, breadcrumbs, and docs depend on them.
|
|
149
|
+
- Put resource identity and governance in `resources`; attach executable behavior in operations.
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
CONTENT_KIND_REGISTRY,
|
|
4
|
+
defineContentType,
|
|
5
|
+
lookupContentType,
|
|
6
|
+
pipelineKind,
|
|
7
|
+
stageKind,
|
|
8
|
+
templateKind,
|
|
9
|
+
templateStepKind,
|
|
10
|
+
statusFlowKind,
|
|
11
|
+
statusKind,
|
|
12
|
+
configKvKind
|
|
13
|
+
} from '../content-kinds/index'
|
|
14
|
+
import type { ContentTypeDefinition, ContentTypeKey } from '../content-kinds/types'
|
|
15
|
+
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// CONTENT_KIND_REGISTRY shape (D8: static object literal)
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
describe('CONTENT_KIND_REGISTRY', () => {
|
|
21
|
+
it('contains all 7 expected keys', () => {
|
|
22
|
+
const expectedKeys: ContentTypeKey[] = [
|
|
23
|
+
'schema:pipeline',
|
|
24
|
+
'schema:stage',
|
|
25
|
+
'schema:template',
|
|
26
|
+
'schema:template-step',
|
|
27
|
+
'schema:status-flow',
|
|
28
|
+
'schema:status',
|
|
29
|
+
'config:kv'
|
|
30
|
+
]
|
|
31
|
+
for (const key of expectedKeys) {
|
|
32
|
+
expect(CONTENT_KIND_REGISTRY).toHaveProperty(key)
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('maps schema:pipeline to the pipelineKind definition', () => {
|
|
37
|
+
expect(CONTENT_KIND_REGISTRY['schema:pipeline']).toBe(pipelineKind)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('maps schema:stage to the stageKind definition', () => {
|
|
41
|
+
expect(CONTENT_KIND_REGISTRY['schema:stage']).toBe(stageKind)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('maps schema:template to the templateKind definition', () => {
|
|
45
|
+
expect(CONTENT_KIND_REGISTRY['schema:template']).toBe(templateKind)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('maps schema:template-step to the templateStepKind definition', () => {
|
|
49
|
+
expect(CONTENT_KIND_REGISTRY['schema:template-step']).toBe(templateStepKind)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('maps schema:status-flow to the statusFlowKind definition', () => {
|
|
53
|
+
expect(CONTENT_KIND_REGISTRY['schema:status-flow']).toBe(statusFlowKind)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('maps schema:status to the statusKind definition', () => {
|
|
57
|
+
expect(CONTENT_KIND_REGISTRY['schema:status']).toBe(statusKind)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('maps config:kv to the configKvKind definition', () => {
|
|
61
|
+
expect(CONTENT_KIND_REGISTRY['config:kv']).toBe(configKvKind)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('has exactly 7 keys (no extra unintended entries)', () => {
|
|
65
|
+
expect(Object.keys(CONTENT_KIND_REGISTRY)).toHaveLength(7)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('every entry has kind, type, payloadSchema, label, and description fields', () => {
|
|
69
|
+
for (const [key, def] of Object.entries(CONTENT_KIND_REGISTRY)) {
|
|
70
|
+
expect(def).toHaveProperty('kind')
|
|
71
|
+
expect(def).toHaveProperty('type')
|
|
72
|
+
expect(def).toHaveProperty('payloadSchema')
|
|
73
|
+
expect(typeof def.kind).toBe('string')
|
|
74
|
+
expect(typeof def.type).toBe('string')
|
|
75
|
+
expect(def.label).toBeTruthy()
|
|
76
|
+
expect(def.description).toBeTruthy()
|
|
77
|
+
// key must equal kind:type
|
|
78
|
+
expect(key).toBe(`${def.kind}:${def.type}`)
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// parentTypes constraint
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
describe('parentTypes constraints', () => {
|
|
88
|
+
it('schema:pipeline has empty parentTypes (top-level, no parent required)', () => {
|
|
89
|
+
expect(pipelineKind.parentTypes).toEqual([])
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('schema:stage parentTypes is ["schema:pipeline"]', () => {
|
|
93
|
+
expect(stageKind.parentTypes).toEqual(['schema:pipeline'])
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('schema:template has empty parentTypes', () => {
|
|
97
|
+
expect(templateKind.parentTypes).toEqual([])
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('schema:template-step parentTypes is ["schema:template"]', () => {
|
|
101
|
+
expect(templateStepKind.parentTypes).toEqual(['schema:template'])
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('schema:status-flow has empty parentTypes', () => {
|
|
105
|
+
expect(statusFlowKind.parentTypes).toEqual([])
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('schema:status parentTypes is ["schema:status-flow"]', () => {
|
|
109
|
+
expect(statusKind.parentTypes).toEqual(['schema:status-flow'])
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('config:kv has empty parentTypes', () => {
|
|
113
|
+
expect(configKvKind.parentTypes).toEqual([])
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// lookupContentType
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
|
|
121
|
+
describe('lookupContentType', () => {
|
|
122
|
+
it('returns the correct definition for schema:pipeline', () => {
|
|
123
|
+
const def = lookupContentType('schema', 'pipeline')
|
|
124
|
+
expect(def).toBe(pipelineKind)
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('returns the correct definition for schema:stage', () => {
|
|
128
|
+
const def = lookupContentType('schema', 'stage')
|
|
129
|
+
expect(def).toBe(stageKind)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('returns the correct definition for config:kv', () => {
|
|
133
|
+
const def = lookupContentType('config', 'kv')
|
|
134
|
+
expect(def).toBe(configKvKind)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('returns undefined for an unregistered kind (D2: not an error)', () => {
|
|
138
|
+
const def = lookupContentType('tenant', 'custom-thing')
|
|
139
|
+
expect(def).toBeUndefined()
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('returns undefined for unknown kind with valid type', () => {
|
|
143
|
+
const def = lookupContentType('unknown', 'pipeline')
|
|
144
|
+
expect(def).toBeUndefined()
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it('returns undefined for valid kind with unknown type', () => {
|
|
148
|
+
const def = lookupContentType('schema', 'not-a-real-type')
|
|
149
|
+
expect(def).toBeUndefined()
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it('returns undefined for empty kind string', () => {
|
|
153
|
+
const def = lookupContentType('', 'pipeline')
|
|
154
|
+
expect(def).toBeUndefined()
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('returns undefined for empty type string', () => {
|
|
158
|
+
const def = lookupContentType('schema', '')
|
|
159
|
+
expect(def).toBeUndefined()
|
|
160
|
+
})
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
// defineContentType (identity factory, L16)
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
|
|
167
|
+
describe('defineContentType', () => {
|
|
168
|
+
it('is an identity function — returns the same object reference', () => {
|
|
169
|
+
const def: ContentTypeDefinition<{ name: string }> = {
|
|
170
|
+
kind: 'tenant',
|
|
171
|
+
type: 'custom',
|
|
172
|
+
payloadSchema: { safeParse: () => ({ success: true, data: { name: 'x' } }) } as unknown as ContentTypeDefinition<{
|
|
173
|
+
name: string
|
|
174
|
+
}>['payloadSchema'],
|
|
175
|
+
label: 'Custom',
|
|
176
|
+
description: 'A tenant-defined type.',
|
|
177
|
+
parentTypes: []
|
|
178
|
+
}
|
|
179
|
+
const result = defineContentType(def)
|
|
180
|
+
expect(result).toBe(def)
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
it('does NOT mutate the input definition', () => {
|
|
184
|
+
const def: ContentTypeDefinition<unknown> = {
|
|
185
|
+
kind: 'schema',
|
|
186
|
+
type: 'pipeline',
|
|
187
|
+
payloadSchema: {
|
|
188
|
+
safeParse: () => ({ success: true, data: {} })
|
|
189
|
+
} as unknown as ContentTypeDefinition<unknown>['payloadSchema'],
|
|
190
|
+
parentTypes: []
|
|
191
|
+
}
|
|
192
|
+
const original = { ...def }
|
|
193
|
+
defineContentType(def)
|
|
194
|
+
expect(def).toEqual(original)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('does NOT register the definition globally (no side-effects on CONTENT_KIND_REGISTRY)', () => {
|
|
198
|
+
const tenantKey = 'tenant:my-new-type'
|
|
199
|
+
defineContentType({
|
|
200
|
+
kind: 'tenant',
|
|
201
|
+
type: 'my-new-type',
|
|
202
|
+
payloadSchema: {
|
|
203
|
+
safeParse: () => ({ success: true, data: {} })
|
|
204
|
+
} as unknown as ContentTypeDefinition<unknown>['payloadSchema'],
|
|
205
|
+
parentTypes: []
|
|
206
|
+
})
|
|
207
|
+
// After calling defineContentType, the registry must remain unchanged (D8: static)
|
|
208
|
+
expect(CONTENT_KIND_REGISTRY).not.toHaveProperty(tenantKey)
|
|
209
|
+
})
|
|
210
|
+
})
|
|
@@ -1,26 +1,22 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
2
|
import { DEFAULT_ORGANIZATION_MODEL } from '../defaults'
|
|
3
|
-
import { DEFAULT_ORGANIZATION_MODEL_BRANDING } from '../domains/branding'
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { OrganizationModelSalesSchema } from '../domains/sales'
|
|
7
|
-
import { DEFAULT_ORGANIZATION_MODEL_PROJECTS } from '../domains/projects'
|
|
8
|
-
import { OrganizationModelProjectsSchema } from '../domains/projects'
|
|
9
|
-
import { DEFAULT_ORGANIZATION_MODEL_PROSPECTING } from '../domains/prospecting'
|
|
10
|
-
import { OrganizationModelProspectingSchema } from '../domains/prospecting'
|
|
11
|
-
import { DEFAULT_ORGANIZATION_MODEL_NAVIGATION } from '../domains/navigation'
|
|
12
|
-
import { OrganizationModelNavigationSchema } from '../domains/navigation'
|
|
13
|
-
import { DEFAULT_ORGANIZATION_MODEL_OPERATIONS } from '../domains/operations'
|
|
14
|
-
import { OperationsDomainSchema } from '../domains/operations'
|
|
15
|
-
import { DEFAULT_ORGANIZATION_MODEL_STATUSES } from '../domains/statuses'
|
|
16
|
-
import { StatusesDomainSchema } from '../domains/statuses'
|
|
3
|
+
import { DEFAULT_ORGANIZATION_MODEL_BRANDING, OrganizationModelBrandingSchema } from '../domains/branding'
|
|
4
|
+
import { DEFAULT_ORGANIZATION_MODEL_ENTITIES, EntitiesDomainSchema } from '../domains/entities'
|
|
5
|
+
import { DEFAULT_ORGANIZATION_MODEL_STATUSES, StatusesDomainSchema } from '../domains/statuses'
|
|
17
6
|
import { DEFAULT_ORGANIZATION_MODEL_SYSTEMS, SystemsDomainSchema } from '../domains/systems'
|
|
18
7
|
import { resolveOrganizationModel } from '../resolve'
|
|
19
8
|
import { OrganizationModelSchema } from '../schema'
|
|
9
|
+
import { DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE } from '../defaults'
|
|
20
10
|
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
11
|
+
// Phase 4 (D8): sales, prospecting, projects, navigation top-level OM fields removed.
|
|
12
|
+
// DEFAULT_ORGANIZATION_MODEL_SALES / OrganizationModelSalesSchema,
|
|
13
|
+
// DEFAULT_ORGANIZATION_MODEL_PROJECTS / OrganizationModelProjectsSchema,
|
|
14
|
+
// DEFAULT_ORGANIZATION_MODEL_PROSPECTING / OrganizationModelProspectingSchema,
|
|
15
|
+
// DEFAULT_ORGANIZATION_MODEL_NAVIGATION / OrganizationModelNavigationSchema
|
|
16
|
+
// are no longer exported from their respective domain files.
|
|
17
|
+
// Tests that depended on these exports are skipped with explicit reason comments.
|
|
18
|
+
|
|
19
|
+
// Only the domain constants and schemas that still exist are covered here.
|
|
24
20
|
const domainCases = [
|
|
25
21
|
{
|
|
26
22
|
name: 'DEFAULT_ORGANIZATION_MODEL_BRANDING',
|
|
@@ -28,35 +24,15 @@ const domainCases = [
|
|
|
28
24
|
schema: OrganizationModelBrandingSchema
|
|
29
25
|
},
|
|
30
26
|
{
|
|
31
|
-
name: '
|
|
32
|
-
constant:
|
|
33
|
-
schema:
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
name: 'DEFAULT_ORGANIZATION_MODEL_PROJECTS',
|
|
37
|
-
constant: DEFAULT_ORGANIZATION_MODEL_PROJECTS,
|
|
38
|
-
schema: OrganizationModelProjectsSchema
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
name: 'DEFAULT_ORGANIZATION_MODEL_PROSPECTING',
|
|
42
|
-
constant: DEFAULT_ORGANIZATION_MODEL_PROSPECTING,
|
|
43
|
-
schema: OrganizationModelProspectingSchema
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
name: 'DEFAULT_ORGANIZATION_MODEL_NAVIGATION',
|
|
47
|
-
constant: DEFAULT_ORGANIZATION_MODEL_NAVIGATION,
|
|
48
|
-
schema: OrganizationModelNavigationSchema
|
|
27
|
+
name: 'DEFAULT_ORGANIZATION_MODEL_ENTITIES',
|
|
28
|
+
constant: DEFAULT_ORGANIZATION_MODEL_ENTITIES,
|
|
29
|
+
schema: EntitiesDomainSchema
|
|
49
30
|
},
|
|
50
31
|
{
|
|
51
32
|
name: 'DEFAULT_ORGANIZATION_MODEL_STATUSES',
|
|
52
33
|
constant: DEFAULT_ORGANIZATION_MODEL_STATUSES,
|
|
53
34
|
schema: StatusesDomainSchema
|
|
54
35
|
},
|
|
55
|
-
{
|
|
56
|
-
name: 'DEFAULT_ORGANIZATION_MODEL_OPERATIONS',
|
|
57
|
-
constant: DEFAULT_ORGANIZATION_MODEL_OPERATIONS,
|
|
58
|
-
schema: OperationsDomainSchema
|
|
59
|
-
},
|
|
60
36
|
{
|
|
61
37
|
name: 'DEFAULT_ORGANIZATION_MODEL_SYSTEMS',
|
|
62
38
|
constant: DEFAULT_ORGANIZATION_MODEL_SYSTEMS,
|
|
@@ -81,17 +57,14 @@ describe('organization-model defaults', () => {
|
|
|
81
57
|
expect(fromDefault).toEqual(fromUndefined)
|
|
82
58
|
})
|
|
83
59
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
expect(commandViewFeature?.devOnly).toBe(true)
|
|
93
|
-
expect(commandViewSurface?.enabled).toBe(true)
|
|
94
|
-
expect(commandViewSurface?.devOnly).toBe(true)
|
|
60
|
+
// Phase 4 (D8): model.navigation removed. The knowledge.command-view surface tracking
|
|
61
|
+
// previously used DEFAULT_ORGANIZATION_MODEL_NAVIGATION.surfaces — skipped since that
|
|
62
|
+
// constant is deleted. The system itself still exists; only the navigation surface
|
|
63
|
+
// reference is gone from the top-level schema.
|
|
64
|
+
it.skip('keeps Command View enabled but development-only in the default Knowledge navigation (deferred — Phase 4: navigation domain removed)', () => {
|
|
65
|
+
// Previously checked DEFAULT_ORGANIZATION_MODEL_NAVIGATION.surfaces.
|
|
66
|
+
// The command-view system still exists in DEFAULT_ORGANIZATION_MODEL.systems.
|
|
67
|
+
// Re-enable with a top-level surfaces Record fixture if that assertion is needed.
|
|
95
68
|
})
|
|
96
69
|
})
|
|
97
70
|
|
|
@@ -101,84 +74,71 @@ describe('organization-model defaults', () => {
|
|
|
101
74
|
})
|
|
102
75
|
})
|
|
103
76
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const result = resolveOrganizationModel({ branding: DEFAULT_ORGANIZATION_MODEL_BRANDING })
|
|
107
|
-
expect(() => OrganizationModelSchema.parse(result)).not.toThrow()
|
|
108
|
-
})
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
describe('DEFAULT_ORGANIZATION_MODEL_SALES via resolveOrganizationModel', () => {
|
|
77
|
+
// Phase 4 (D8): OrganizationModelSalesSchema, DEFAULT_ORGANIZATION_MODEL_SALES removed.
|
|
78
|
+
describe.skip('DEFAULT_ORGANIZATION_MODEL_SALES via resolveOrganizationModel (deferred — Phase 4: sales domain removed)', () => {
|
|
112
79
|
it('resolving with sales override produces a schema-valid model', () => {
|
|
113
|
-
|
|
114
|
-
|
|
80
|
+
// Previously: resolveOrganizationModel({ sales: DEFAULT_ORGANIZATION_MODEL_SALES })
|
|
81
|
+
// Sales data now lives in system.content. Use getAllPipelines(model) from migration-helpers.
|
|
115
82
|
})
|
|
116
83
|
})
|
|
117
84
|
|
|
118
|
-
|
|
85
|
+
// Phase 4 (D8): OrganizationModelProjectsSchema, DEFAULT_ORGANIZATION_MODEL_PROJECTS removed.
|
|
86
|
+
describe.skip('DEFAULT_ORGANIZATION_MODEL_PROJECTS via resolveOrganizationModel (deferred — Phase 4: projects domain removed)', () => {
|
|
119
87
|
it('resolving with projects override produces a schema-valid model', () => {
|
|
120
|
-
|
|
121
|
-
|
|
88
|
+
// Previously: resolveOrganizationModel({ projects: DEFAULT_ORGANIZATION_MODEL_PROJECTS })
|
|
89
|
+
// Project statuses now live in system.content. Use getAllProjectStatuses(model) from migration-helpers.
|
|
122
90
|
})
|
|
123
91
|
})
|
|
124
92
|
|
|
125
|
-
|
|
93
|
+
// Phase 4 (D8): OrganizationModelProspectingSchema, DEFAULT_ORGANIZATION_MODEL_PROSPECTING removed.
|
|
94
|
+
describe.skip('DEFAULT_ORGANIZATION_MODEL_PROSPECTING via resolveOrganizationModel (deferred — Phase 4: prospecting domain removed)', () => {
|
|
126
95
|
it('resolving with prospecting override produces a schema-valid model', () => {
|
|
127
|
-
|
|
128
|
-
|
|
96
|
+
// Previously: resolveOrganizationModel({ prospecting: DEFAULT_ORGANIZATION_MODEL_PROSPECTING })
|
|
97
|
+
// Prospecting stages/templates now live in system.content. Use getAllProspectingStages() / getAllBuildTemplates().
|
|
129
98
|
})
|
|
130
99
|
})
|
|
131
100
|
|
|
132
|
-
|
|
101
|
+
// Phase 4 (D8): OrganizationModelNavigationSchema, DEFAULT_ORGANIZATION_MODEL_NAVIGATION removed.
|
|
102
|
+
describe.skip('DEFAULT_ORGANIZATION_MODEL_NAVIGATION via resolveOrganizationModel (deferred — Phase 4: navigation domain removed)', () => {
|
|
133
103
|
it('resolving with navigation override produces a schema-valid model', () => {
|
|
134
|
-
|
|
135
|
-
|
|
104
|
+
// Previously: resolveOrganizationModel({ navigation: DEFAULT_ORGANIZATION_MODEL_NAVIGATION })
|
|
105
|
+
// Surfaces and navigation groups are now top-level Records on OrganizationModelSchemaBase.
|
|
136
106
|
})
|
|
137
107
|
})
|
|
138
108
|
|
|
139
|
-
describe('
|
|
140
|
-
it('resolving with
|
|
141
|
-
const result = resolveOrganizationModel({
|
|
109
|
+
describe('DEFAULT_ORGANIZATION_MODEL_BRANDING via resolveOrganizationModel', () => {
|
|
110
|
+
it('resolving with branding override produces a schema-valid model', () => {
|
|
111
|
+
const result = resolveOrganizationModel({ branding: DEFAULT_ORGANIZATION_MODEL_BRANDING })
|
|
142
112
|
expect(() => OrganizationModelSchema.parse(result)).not.toThrow()
|
|
143
113
|
})
|
|
144
|
-
|
|
145
|
-
it('default operations seed covers all 5 entity categories', () => {
|
|
146
|
-
expect(DEFAULT_ORGANIZATION_MODEL_OPERATIONS.entries).toHaveLength(5)
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
it('every entry has a non-empty id, label, and semanticClass', () => {
|
|
150
|
-
for (const entry of DEFAULT_ORGANIZATION_MODEL_OPERATIONS.entries) {
|
|
151
|
-
expect(entry.id.length).toBeGreaterThan(0)
|
|
152
|
-
expect(entry.label.length).toBeGreaterThan(0)
|
|
153
|
-
expect(entry.semanticClass.length).toBeGreaterThan(0)
|
|
154
|
-
}
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
it('all entry ids are unique', () => {
|
|
158
|
-
const ids = DEFAULT_ORGANIZATION_MODEL_OPERATIONS.entries.map((e) => e.id)
|
|
159
|
-
const uniqueIds = new Set(ids)
|
|
160
|
-
expect(uniqueIds.size).toBe(ids.length)
|
|
161
|
-
})
|
|
162
114
|
})
|
|
163
115
|
|
|
164
116
|
describe('DEFAULT_ORGANIZATION_MODEL_STATUSES via resolveOrganizationModel', () => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
117
|
+
// Phase 4 (D1): statuses top-level field removed from OM schema.
|
|
118
|
+
// DEFAULT_ORGANIZATION_MODEL_STATUSES is still exported from domains/statuses.ts
|
|
119
|
+
// (it's used by the UI statuses domain as a semantic registry, not OM-schema-level data).
|
|
120
|
+
// The resolveOrganizationModel override with { statuses: ... } was silently accepted
|
|
121
|
+
// before Phase 4 because statuses was an extra field; now statuses is gone from OM entirely.
|
|
122
|
+
// We keep the data-integrity checks below; the resolveOrganizationModel override test is skipped.
|
|
123
|
+
it.skip('resolving with statuses override produces a schema-valid model (deferred — Phase 4: statuses field removed from OM schema)', () => {
|
|
124
|
+
// Previously: resolveOrganizationModel({ statuses: DEFAULT_ORGANIZATION_MODEL_STATUSES })
|
|
125
|
+
// Status data now lives in system.content via (schema:status-flow) + (schema:status) content nodes.
|
|
168
126
|
})
|
|
169
127
|
|
|
170
128
|
it('default statuses seed covers all 9 delivery.task values', () => {
|
|
171
|
-
const taskEntries = DEFAULT_ORGANIZATION_MODEL_STATUSES.
|
|
129
|
+
const taskEntries = Object.values(DEFAULT_ORGANIZATION_MODEL_STATUSES).filter(
|
|
130
|
+
(e) => e.semanticClass === 'delivery.task'
|
|
131
|
+
)
|
|
172
132
|
expect(taskEntries).toHaveLength(9)
|
|
173
133
|
})
|
|
174
134
|
|
|
175
135
|
it('default statuses seed covers all 5 queue values', () => {
|
|
176
|
-
const queueEntries = DEFAULT_ORGANIZATION_MODEL_STATUSES.
|
|
136
|
+
const queueEntries = Object.values(DEFAULT_ORGANIZATION_MODEL_STATUSES).filter((e) => e.semanticClass === 'queue')
|
|
177
137
|
expect(queueEntries).toHaveLength(5)
|
|
178
138
|
})
|
|
179
139
|
|
|
180
140
|
it('every entry has a non-empty id, label, and semanticClass', () => {
|
|
181
|
-
for (const entry of DEFAULT_ORGANIZATION_MODEL_STATUSES
|
|
141
|
+
for (const entry of Object.values(DEFAULT_ORGANIZATION_MODEL_STATUSES)) {
|
|
182
142
|
expect(entry.id.length).toBeGreaterThan(0)
|
|
183
143
|
expect(entry.label.length).toBeGreaterThan(0)
|
|
184
144
|
expect(entry.semanticClass.length).toBeGreaterThan(0)
|
|
@@ -186,9 +146,23 @@ describe('organization-model defaults', () => {
|
|
|
186
146
|
})
|
|
187
147
|
|
|
188
148
|
it('all entry ids are unique', () => {
|
|
189
|
-
const ids = DEFAULT_ORGANIZATION_MODEL_STATUSES.
|
|
149
|
+
const ids = Object.values(DEFAULT_ORGANIZATION_MODEL_STATUSES).map((e) => e.id)
|
|
190
150
|
const uniqueIds = new Set(ids)
|
|
191
151
|
expect(uniqueIds.size).toBe(ids.length)
|
|
192
152
|
})
|
|
193
153
|
})
|
|
154
|
+
|
|
155
|
+
describe('DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE — flat Record (Phase 4 D3)', () => {
|
|
156
|
+
it('is an empty object (no pre-seeded nodes in generic base)', () => {
|
|
157
|
+
expect(DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE).toEqual({})
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('does not have a .nodes array (old wrapper shape removed)', () => {
|
|
161
|
+
expect(DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE).not.toHaveProperty('nodes')
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('does not have a .version field (moved to domainMetadata.knowledge)', () => {
|
|
165
|
+
expect(DEFAULT_ORGANIZATION_MODEL_KNOWLEDGE).not.toHaveProperty('version')
|
|
166
|
+
})
|
|
167
|
+
})
|
|
194
168
|
})
|