@elevasis/core 0.21.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 +2518 -2169
- package/dist/index.js +2495 -1095
- package/dist/knowledge/index.d.ts +706 -1044
- package/dist/knowledge/index.js +9 -9
- package/dist/organization-model/index.d.ts +2518 -2169
- package/dist/organization-model/index.js +2495 -1095
- package/dist/test-utils/index.d.ts +826 -1014
- package/dist/test-utils/index.js +1894 -1032
- package/package.json +3 -3
- package/src/__tests__/template-core-compatibility.test.ts +11 -79
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +852 -397
- package/src/auth/multi-tenancy/permissions.ts +20 -8
- package/src/business/README.md +2 -2
- package/src/business/acquisition/api-schemas.test.ts +175 -2
- package/src/business/acquisition/api-schemas.ts +132 -16
- 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/index.ts +12 -0
- package/src/business/acquisition/types.ts +7 -3
- package/src/business/clients/api-schemas.test.ts +115 -0
- package/src/business/clients/api-schemas.ts +158 -0
- package/src/business/clients/index.ts +1 -0
- package/src/business/deals/api-schemas.ts +8 -0
- package/src/business/index.ts +5 -2
- package/src/business/projects/types.ts +19 -0
- package/src/execution/engine/__tests__/fixtures/test-agents.ts +10 -8
- package/src/execution/engine/agent/core/__tests__/agent.test.ts +16 -12
- package/src/execution/engine/agent/core/__tests__/error-passthrough.test.ts +4 -3
- package/src/execution/engine/agent/core/types.ts +25 -15
- package/src/execution/engine/agent/index.ts +6 -4
- package/src/execution/engine/agent/reasoning/__tests__/request-builder.test.ts +24 -18
- package/src/execution/engine/index.ts +3 -0
- package/src/execution/engine/workflow/types.ts +9 -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 +73 -26
- package/src/organization-model/__tests__/content-kinds-registry.test.ts +210 -0
- package/src/organization-model/__tests__/defaults.test.ts +76 -96
- 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 +310 -0
- package/src/organization-model/__tests__/domains/roles.test.ts +463 -347
- package/src/organization-model/__tests__/domains/statuses.test.ts +246 -243
- package/src/organization-model/__tests__/domains/systems.test.ts +209 -0
- 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 +209 -49
- 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 +499 -86
- 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 +76 -17
- 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 +145 -0
- package/src/organization-model/domains/roles.ts +96 -55
- 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 +203 -0
- 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/icons.ts +1 -0
- package/src/organization-model/index.ts +118 -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 +111 -44
- package/src/organization-model/published.ts +172 -19
- package/src/organization-model/resolve.ts +117 -54
- package/src/organization-model/schema.ts +654 -112
- package/src/organization-model/surface-projection.ts +116 -122
- package/src/organization-model/types.ts +146 -20
- package/src/platform/api/types.ts +38 -35
- 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 +2053 -2005
- package/src/platform/registry/__tests__/validation.test.ts +1347 -1086
- package/src/platform/registry/index.ts +14 -0
- package/src/platform/registry/resource-registry.ts +52 -2
- package/src/platform/registry/serialization.ts +241 -202
- package/src/platform/registry/serialized-types.ts +1 -0
- package/src/platform/registry/types.ts +411 -361
- package/src/platform/registry/validation.ts +745 -513
- package/src/projects/api-schemas.ts +290 -267
- package/src/reference/_generated/contracts.md +853 -397
- package/src/reference/glossary.md +23 -18
- package/src/supabase/database.types.ts +181 -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,279 +1,280 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import {
|
|
3
|
-
BusinessHoursDaySchema,
|
|
4
|
-
BusinessHoursSchema,
|
|
5
|
-
DEFAULT_ORGANIZATION_MODEL_IDENTITY,
|
|
6
|
-
IdentityDomainSchema
|
|
7
|
-
} from '../../domains/identity'
|
|
8
|
-
import { resolveOrganizationModel } from '../../resolve'
|
|
9
|
-
|
|
10
|
-
// ---------------------------------------------------------------------------
|
|
11
|
-
// Group 1: IdentityDomainSchema — positive parse
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
|
-
|
|
14
|
-
describe('IdentityDomainSchema — positive parse', () => {
|
|
15
|
-
it('accepts a fully-populated identity object', () => {
|
|
16
|
-
const result = IdentityDomainSchema.safeParse({
|
|
17
|
-
mission: 'Help businesses automate complex work.',
|
|
18
|
-
vision: 'A world where every team has an AI co-pilot.',
|
|
19
|
-
legalName: 'Acme Corp LLC',
|
|
20
|
-
entityType: 'LLC',
|
|
21
|
-
jurisdiction: 'United States – Delaware',
|
|
22
|
-
industryCategory: 'Software / SaaS',
|
|
23
|
-
geographicFocus: 'North America',
|
|
24
|
-
timeZone: 'America/Los_Angeles',
|
|
25
|
-
businessHours: {
|
|
26
|
-
monday: { open: '09:00', close: '17:00' },
|
|
27
|
-
friday: { open: '09:00', close: '15:00' }
|
|
28
|
-
}
|
|
29
|
-
})
|
|
30
|
-
expect(result.success).toBe(true)
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('accepts a minimal input (empty object) and applies all defaults', () => {
|
|
34
|
-
const result = IdentityDomainSchema.safeParse({})
|
|
35
|
-
expect(result.success).toBe(true)
|
|
36
|
-
if (result.success) {
|
|
37
|
-
expect(result.data.mission).toBe('')
|
|
38
|
-
expect(result.data.vision).toBe('')
|
|
39
|
-
expect(result.data.legalName).toBe('')
|
|
40
|
-
expect(result.data.entityType).toBe('')
|
|
41
|
-
expect(result.data.jurisdiction).toBe('')
|
|
42
|
-
expect(result.data.industryCategory).toBe('')
|
|
43
|
-
expect(result.data.geographicFocus).toBe('')
|
|
44
|
-
expect(result.data.timeZone).toBe('UTC')
|
|
45
|
-
expect(result.data.businessHours).toEqual({})
|
|
46
|
-
}
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('accepts a partial object — only mission and timeZone provided', () => {
|
|
50
|
-
const result = IdentityDomainSchema.safeParse({
|
|
51
|
-
mission: 'Ship great software.',
|
|
52
|
-
timeZone: 'Europe/London'
|
|
53
|
-
})
|
|
54
|
-
expect(result.success).toBe(true)
|
|
55
|
-
if (result.success) {
|
|
56
|
-
expect(result.data.mission).toBe('Ship great software.')
|
|
57
|
-
expect(result.data.timeZone).toBe('Europe/London')
|
|
58
|
-
expect(result.data.vision).toBe('')
|
|
59
|
-
}
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('trims leading/trailing whitespace from string fields', () => {
|
|
63
|
-
const result = IdentityDomainSchema.safeParse({
|
|
64
|
-
mission: ' Trimmed mission. ',
|
|
65
|
-
legalName: ' Trimmed Name '
|
|
66
|
-
})
|
|
67
|
-
expect(result.success).toBe(true)
|
|
68
|
-
if (result.success) {
|
|
69
|
-
expect(result.data.mission).toBe('Trimmed mission.')
|
|
70
|
-
expect(result.data.legalName).toBe('Trimmed Name')
|
|
71
|
-
}
|
|
72
|
-
})
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
// ---------------------------------------------------------------------------
|
|
76
|
-
// Group 2: Default values
|
|
77
|
-
// ---------------------------------------------------------------------------
|
|
78
|
-
|
|
79
|
-
describe('IdentityDomainSchema — default values', () => {
|
|
80
|
-
it('timeZone defaults to "UTC"', () => {
|
|
81
|
-
const result = IdentityDomainSchema.safeParse({})
|
|
82
|
-
expect(result.success).toBe(true)
|
|
83
|
-
if (result.success) {
|
|
84
|
-
expect(result.data.timeZone).toBe('UTC')
|
|
85
|
-
}
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
it('businessHours defaults to empty object', () => {
|
|
89
|
-
const result = IdentityDomainSchema.safeParse({})
|
|
90
|
-
expect(result.success).toBe(true)
|
|
91
|
-
if (result.success) {
|
|
92
|
-
expect(result.data.businessHours).toEqual({})
|
|
93
|
-
}
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
it('all string fields default to empty string', () => {
|
|
97
|
-
const result = IdentityDomainSchema.safeParse({})
|
|
98
|
-
expect(result.success).toBe(true)
|
|
99
|
-
if (result.success) {
|
|
100
|
-
const stringDefaults: (keyof typeof result.data)[] = [
|
|
101
|
-
'mission',
|
|
102
|
-
'vision',
|
|
103
|
-
'legalName',
|
|
104
|
-
'entityType',
|
|
105
|
-
'jurisdiction',
|
|
106
|
-
'industryCategory',
|
|
107
|
-
'geographicFocus'
|
|
108
|
-
]
|
|
109
|
-
for (const field of stringDefaults) {
|
|
110
|
-
expect(result.data[field], `field "${field}" should default to ""`).toBe('')
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
it('DEFAULT_ORGANIZATION_MODEL_IDENTITY constant matches schema parse of empty object', () => {
|
|
116
|
-
const result = IdentityDomainSchema.safeParse({})
|
|
117
|
-
expect(result.success).toBe(true)
|
|
118
|
-
if (result.success) {
|
|
119
|
-
expect(result.data).toEqual(DEFAULT_ORGANIZATION_MODEL_IDENTITY)
|
|
120
|
-
}
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
// ---------------------------------------------------------------------------
|
|
125
|
-
// Group 3: BusinessHoursSchema — valid shapes
|
|
126
|
-
// ---------------------------------------------------------------------------
|
|
127
|
-
|
|
128
|
-
describe('BusinessHoursSchema — valid shapes', () => {
|
|
129
|
-
it('accepts all seven days populated', () => {
|
|
130
|
-
const result = BusinessHoursSchema.safeParse({
|
|
131
|
-
monday: { open: '08:00', close: '17:00' },
|
|
132
|
-
tuesday: { open: '08:00', close: '17:00' },
|
|
133
|
-
wednesday: { open: '08:00', close: '17:00' },
|
|
134
|
-
thursday: { open: '08:00', close: '17:00' },
|
|
135
|
-
friday: { open: '08:00', close: '17:00' },
|
|
136
|
-
saturday: { open: '10:00', close: '14:00' },
|
|
137
|
-
sunday: { open: '10:00', close: '14:00' }
|
|
138
|
-
})
|
|
139
|
-
expect(result.success).toBe(true)
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
it('accepts an object with only some days present (others are optional)', () => {
|
|
143
|
-
const result = BusinessHoursSchema.safeParse({
|
|
144
|
-
monday: { open: '09:00', close: '18:00' }
|
|
145
|
-
})
|
|
146
|
-
expect(result.success).toBe(true)
|
|
147
|
-
if (result.success) {
|
|
148
|
-
expect(result.data.tuesday).toBeUndefined()
|
|
149
|
-
expect(result.data.sunday).toBeUndefined()
|
|
150
|
-
}
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
it('defaults to empty object when input is undefined (schema .default({}))', () => {
|
|
154
|
-
const result = BusinessHoursSchema.safeParse(undefined)
|
|
155
|
-
expect(result.success).toBe(true)
|
|
156
|
-
if (result.success) {
|
|
157
|
-
expect(result.data).toEqual({})
|
|
158
|
-
}
|
|
159
|
-
})
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
// ---------------------------------------------------------------------------
|
|
163
|
-
// Group 4: BusinessHoursDaySchema — HH:MM format validation
|
|
164
|
-
// ---------------------------------------------------------------------------
|
|
165
|
-
|
|
166
|
-
describe('BusinessHoursDaySchema — HH:MM regex', () => {
|
|
167
|
-
it('accepts valid 24-hour HH:MM values', () => {
|
|
168
|
-
const result = BusinessHoursDaySchema.safeParse({ open: '00:00', close: '23:59' })
|
|
169
|
-
expect(result.success).toBe(true)
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
it('rejects time without leading zero (9:00 instead of 09:00)', () => {
|
|
173
|
-
const result = BusinessHoursDaySchema.safeParse({ open: '9:00', close: '17:00' })
|
|
174
|
-
expect(result.success).toBe(false)
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
it('rejects time with seconds appended (HH:MM:SS)', () => {
|
|
178
|
-
const result = BusinessHoursDaySchema.safeParse({ open: '09:00:00', close: '17:00:00' })
|
|
179
|
-
expect(result.success).toBe(false)
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
it('rejects time with AM/PM suffix', () => {
|
|
183
|
-
const result = BusinessHoursDaySchema.safeParse({ open: '09:00AM', close: '05:00PM' })
|
|
184
|
-
expect(result.success).toBe(false)
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
it('rejects missing close field', () => {
|
|
188
|
-
const result = BusinessHoursDaySchema.safeParse({ open: '09:00' })
|
|
189
|
-
expect(result.success).toBe(false)
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
it('rejects non-string open value (number)', () => {
|
|
193
|
-
const result = BusinessHoursDaySchema.safeParse({ open: 900, close: '17:00' })
|
|
194
|
-
expect(result.success).toBe(false)
|
|
195
|
-
})
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
// ---------------------------------------------------------------------------
|
|
199
|
-
// Group 5: IdentityDomainSchema — negative parse (wrong types / over-max)
|
|
200
|
-
// ---------------------------------------------------------------------------
|
|
201
|
-
|
|
202
|
-
describe('IdentityDomainSchema — negative parse', () => {
|
|
203
|
-
it('rejects mission as a number', () => {
|
|
204
|
-
const result = IdentityDomainSchema.safeParse({ mission: 42 })
|
|
205
|
-
expect(result.success).toBe(false)
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
it('rejects timeZone as a number', () => {
|
|
209
|
-
const result = IdentityDomainSchema.safeParse({ timeZone: 0 })
|
|
210
|
-
expect(result.success).toBe(false)
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
it('rejects mission exceeding 1000 characters', () => {
|
|
214
|
-
const result = IdentityDomainSchema.safeParse({ mission: 'x'.repeat(1001) })
|
|
215
|
-
expect(result.success).toBe(false)
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
it('rejects legalName exceeding 200 characters', () => {
|
|
219
|
-
const result = IdentityDomainSchema.safeParse({ legalName: 'x'.repeat(201) })
|
|
220
|
-
expect(result.success).toBe(false)
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
it('rejects entityType exceeding 100 characters', () => {
|
|
224
|
-
const result = IdentityDomainSchema.safeParse({ entityType: 'x'.repeat(101) })
|
|
225
|
-
expect(result.success).toBe(false)
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
it('rejects timeZone exceeding 100 characters', () => {
|
|
229
|
-
const result = IdentityDomainSchema.safeParse({ timeZone: 'x'.repeat(101) })
|
|
230
|
-
expect(result.success).toBe(false)
|
|
231
|
-
})
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
// ---------------------------------------------------------------------------
|
|
235
|
-
// Group 6: Integration — resolveOrganizationModel
|
|
236
|
-
// ---------------------------------------------------------------------------
|
|
237
|
-
|
|
238
|
-
describe('resolveOrganizationModel — identity domain integration', () => {
|
|
239
|
-
it('merges partial identity override and preserves unset fields as defaults', () => {
|
|
240
|
-
const model = resolveOrganizationModel({
|
|
241
|
-
identity: { mission: 'Build the future.' }
|
|
242
|
-
})
|
|
243
|
-
expect(model.identity.mission).toBe('Build the future.')
|
|
244
|
-
expect(model.identity.timeZone).toBe('UTC')
|
|
245
|
-
expect(model.identity.vision).toBe('')
|
|
246
|
-
expect(model.identity.businessHours).toEqual({})
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
it('fully-specified identity override round-trips through resolveOrganizationModel', () => {
|
|
250
|
-
const override = {
|
|
251
|
-
identity: {
|
|
252
|
-
mission: 'Automate everything.',
|
|
253
|
-
vision: 'Zero manual work.',
|
|
254
|
-
legalName: 'Test Corp Inc',
|
|
255
|
-
entityType: 'Corporation',
|
|
256
|
-
jurisdiction: 'United States – California',
|
|
257
|
-
industryCategory: 'Software / SaaS',
|
|
258
|
-
geographicFocus: 'Global',
|
|
259
|
-
timeZone: 'America/Chicago',
|
|
260
|
-
clientBrief: '',
|
|
261
|
-
businessHours: {
|
|
262
|
-
monday: { open: '09:00', close: '17:00' }
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
const model = resolveOrganizationModel(override)
|
|
267
|
-
expect(model.identity).toEqual(override.identity)
|
|
268
|
-
})
|
|
269
|
-
|
|
270
|
-
it('does not bleed identity changes into other top-level domains', () => {
|
|
271
|
-
const model = resolveOrganizationModel({
|
|
272
|
-
identity: { mission: 'Isolated change.' }
|
|
273
|
-
})
|
|
274
|
-
//
|
|
275
|
-
|
|
276
|
-
expect(model.
|
|
277
|
-
expect(model.
|
|
278
|
-
|
|
279
|
-
})
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
BusinessHoursDaySchema,
|
|
4
|
+
BusinessHoursSchema,
|
|
5
|
+
DEFAULT_ORGANIZATION_MODEL_IDENTITY,
|
|
6
|
+
IdentityDomainSchema
|
|
7
|
+
} from '../../domains/identity'
|
|
8
|
+
import { resolveOrganizationModel } from '../../resolve'
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Group 1: IdentityDomainSchema — positive parse
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
describe('IdentityDomainSchema — positive parse', () => {
|
|
15
|
+
it('accepts a fully-populated identity object', () => {
|
|
16
|
+
const result = IdentityDomainSchema.safeParse({
|
|
17
|
+
mission: 'Help businesses automate complex work.',
|
|
18
|
+
vision: 'A world where every team has an AI co-pilot.',
|
|
19
|
+
legalName: 'Acme Corp LLC',
|
|
20
|
+
entityType: 'LLC',
|
|
21
|
+
jurisdiction: 'United States – Delaware',
|
|
22
|
+
industryCategory: 'Software / SaaS',
|
|
23
|
+
geographicFocus: 'North America',
|
|
24
|
+
timeZone: 'America/Los_Angeles',
|
|
25
|
+
businessHours: {
|
|
26
|
+
monday: { open: '09:00', close: '17:00' },
|
|
27
|
+
friday: { open: '09:00', close: '15:00' }
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
expect(result.success).toBe(true)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('accepts a minimal input (empty object) and applies all defaults', () => {
|
|
34
|
+
const result = IdentityDomainSchema.safeParse({})
|
|
35
|
+
expect(result.success).toBe(true)
|
|
36
|
+
if (result.success) {
|
|
37
|
+
expect(result.data.mission).toBe('')
|
|
38
|
+
expect(result.data.vision).toBe('')
|
|
39
|
+
expect(result.data.legalName).toBe('')
|
|
40
|
+
expect(result.data.entityType).toBe('')
|
|
41
|
+
expect(result.data.jurisdiction).toBe('')
|
|
42
|
+
expect(result.data.industryCategory).toBe('')
|
|
43
|
+
expect(result.data.geographicFocus).toBe('')
|
|
44
|
+
expect(result.data.timeZone).toBe('UTC')
|
|
45
|
+
expect(result.data.businessHours).toEqual({})
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('accepts a partial object — only mission and timeZone provided', () => {
|
|
50
|
+
const result = IdentityDomainSchema.safeParse({
|
|
51
|
+
mission: 'Ship great software.',
|
|
52
|
+
timeZone: 'Europe/London'
|
|
53
|
+
})
|
|
54
|
+
expect(result.success).toBe(true)
|
|
55
|
+
if (result.success) {
|
|
56
|
+
expect(result.data.mission).toBe('Ship great software.')
|
|
57
|
+
expect(result.data.timeZone).toBe('Europe/London')
|
|
58
|
+
expect(result.data.vision).toBe('')
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('trims leading/trailing whitespace from string fields', () => {
|
|
63
|
+
const result = IdentityDomainSchema.safeParse({
|
|
64
|
+
mission: ' Trimmed mission. ',
|
|
65
|
+
legalName: ' Trimmed Name '
|
|
66
|
+
})
|
|
67
|
+
expect(result.success).toBe(true)
|
|
68
|
+
if (result.success) {
|
|
69
|
+
expect(result.data.mission).toBe('Trimmed mission.')
|
|
70
|
+
expect(result.data.legalName).toBe('Trimmed Name')
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// Group 2: Default values
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
describe('IdentityDomainSchema — default values', () => {
|
|
80
|
+
it('timeZone defaults to "UTC"', () => {
|
|
81
|
+
const result = IdentityDomainSchema.safeParse({})
|
|
82
|
+
expect(result.success).toBe(true)
|
|
83
|
+
if (result.success) {
|
|
84
|
+
expect(result.data.timeZone).toBe('UTC')
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('businessHours defaults to empty object', () => {
|
|
89
|
+
const result = IdentityDomainSchema.safeParse({})
|
|
90
|
+
expect(result.success).toBe(true)
|
|
91
|
+
if (result.success) {
|
|
92
|
+
expect(result.data.businessHours).toEqual({})
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('all string fields default to empty string', () => {
|
|
97
|
+
const result = IdentityDomainSchema.safeParse({})
|
|
98
|
+
expect(result.success).toBe(true)
|
|
99
|
+
if (result.success) {
|
|
100
|
+
const stringDefaults: (keyof typeof result.data)[] = [
|
|
101
|
+
'mission',
|
|
102
|
+
'vision',
|
|
103
|
+
'legalName',
|
|
104
|
+
'entityType',
|
|
105
|
+
'jurisdiction',
|
|
106
|
+
'industryCategory',
|
|
107
|
+
'geographicFocus'
|
|
108
|
+
]
|
|
109
|
+
for (const field of stringDefaults) {
|
|
110
|
+
expect(result.data[field], `field "${field}" should default to ""`).toBe('')
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('DEFAULT_ORGANIZATION_MODEL_IDENTITY constant matches schema parse of empty object', () => {
|
|
116
|
+
const result = IdentityDomainSchema.safeParse({})
|
|
117
|
+
expect(result.success).toBe(true)
|
|
118
|
+
if (result.success) {
|
|
119
|
+
expect(result.data).toEqual(DEFAULT_ORGANIZATION_MODEL_IDENTITY)
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// Group 3: BusinessHoursSchema — valid shapes
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
describe('BusinessHoursSchema — valid shapes', () => {
|
|
129
|
+
it('accepts all seven days populated', () => {
|
|
130
|
+
const result = BusinessHoursSchema.safeParse({
|
|
131
|
+
monday: { open: '08:00', close: '17:00' },
|
|
132
|
+
tuesday: { open: '08:00', close: '17:00' },
|
|
133
|
+
wednesday: { open: '08:00', close: '17:00' },
|
|
134
|
+
thursday: { open: '08:00', close: '17:00' },
|
|
135
|
+
friday: { open: '08:00', close: '17:00' },
|
|
136
|
+
saturday: { open: '10:00', close: '14:00' },
|
|
137
|
+
sunday: { open: '10:00', close: '14:00' }
|
|
138
|
+
})
|
|
139
|
+
expect(result.success).toBe(true)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('accepts an object with only some days present (others are optional)', () => {
|
|
143
|
+
const result = BusinessHoursSchema.safeParse({
|
|
144
|
+
monday: { open: '09:00', close: '18:00' }
|
|
145
|
+
})
|
|
146
|
+
expect(result.success).toBe(true)
|
|
147
|
+
if (result.success) {
|
|
148
|
+
expect(result.data.tuesday).toBeUndefined()
|
|
149
|
+
expect(result.data.sunday).toBeUndefined()
|
|
150
|
+
}
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('defaults to empty object when input is undefined (schema .default({}))', () => {
|
|
154
|
+
const result = BusinessHoursSchema.safeParse(undefined)
|
|
155
|
+
expect(result.success).toBe(true)
|
|
156
|
+
if (result.success) {
|
|
157
|
+
expect(result.data).toEqual({})
|
|
158
|
+
}
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
// Group 4: BusinessHoursDaySchema — HH:MM format validation
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
|
|
166
|
+
describe('BusinessHoursDaySchema — HH:MM regex', () => {
|
|
167
|
+
it('accepts valid 24-hour HH:MM values', () => {
|
|
168
|
+
const result = BusinessHoursDaySchema.safeParse({ open: '00:00', close: '23:59' })
|
|
169
|
+
expect(result.success).toBe(true)
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('rejects time without leading zero (9:00 instead of 09:00)', () => {
|
|
173
|
+
const result = BusinessHoursDaySchema.safeParse({ open: '9:00', close: '17:00' })
|
|
174
|
+
expect(result.success).toBe(false)
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('rejects time with seconds appended (HH:MM:SS)', () => {
|
|
178
|
+
const result = BusinessHoursDaySchema.safeParse({ open: '09:00:00', close: '17:00:00' })
|
|
179
|
+
expect(result.success).toBe(false)
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it('rejects time with AM/PM suffix', () => {
|
|
183
|
+
const result = BusinessHoursDaySchema.safeParse({ open: '09:00AM', close: '05:00PM' })
|
|
184
|
+
expect(result.success).toBe(false)
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('rejects missing close field', () => {
|
|
188
|
+
const result = BusinessHoursDaySchema.safeParse({ open: '09:00' })
|
|
189
|
+
expect(result.success).toBe(false)
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
it('rejects non-string open value (number)', () => {
|
|
193
|
+
const result = BusinessHoursDaySchema.safeParse({ open: 900, close: '17:00' })
|
|
194
|
+
expect(result.success).toBe(false)
|
|
195
|
+
})
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
// Group 5: IdentityDomainSchema — negative parse (wrong types / over-max)
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
|
|
202
|
+
describe('IdentityDomainSchema — negative parse', () => {
|
|
203
|
+
it('rejects mission as a number', () => {
|
|
204
|
+
const result = IdentityDomainSchema.safeParse({ mission: 42 })
|
|
205
|
+
expect(result.success).toBe(false)
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('rejects timeZone as a number', () => {
|
|
209
|
+
const result = IdentityDomainSchema.safeParse({ timeZone: 0 })
|
|
210
|
+
expect(result.success).toBe(false)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('rejects mission exceeding 1000 characters', () => {
|
|
214
|
+
const result = IdentityDomainSchema.safeParse({ mission: 'x'.repeat(1001) })
|
|
215
|
+
expect(result.success).toBe(false)
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('rejects legalName exceeding 200 characters', () => {
|
|
219
|
+
const result = IdentityDomainSchema.safeParse({ legalName: 'x'.repeat(201) })
|
|
220
|
+
expect(result.success).toBe(false)
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('rejects entityType exceeding 100 characters', () => {
|
|
224
|
+
const result = IdentityDomainSchema.safeParse({ entityType: 'x'.repeat(101) })
|
|
225
|
+
expect(result.success).toBe(false)
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it('rejects timeZone exceeding 100 characters', () => {
|
|
229
|
+
const result = IdentityDomainSchema.safeParse({ timeZone: 'x'.repeat(101) })
|
|
230
|
+
expect(result.success).toBe(false)
|
|
231
|
+
})
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
// Group 6: Integration — resolveOrganizationModel
|
|
236
|
+
// ---------------------------------------------------------------------------
|
|
237
|
+
|
|
238
|
+
describe('resolveOrganizationModel — identity domain integration', () => {
|
|
239
|
+
it('merges partial identity override and preserves unset fields as defaults', () => {
|
|
240
|
+
const model = resolveOrganizationModel({
|
|
241
|
+
identity: { mission: 'Build the future.' }
|
|
242
|
+
})
|
|
243
|
+
expect(model.identity.mission).toBe('Build the future.')
|
|
244
|
+
expect(model.identity.timeZone).toBe('UTC')
|
|
245
|
+
expect(model.identity.vision).toBe('')
|
|
246
|
+
expect(model.identity.businessHours).toEqual({})
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
it('fully-specified identity override round-trips through resolveOrganizationModel', () => {
|
|
250
|
+
const override = {
|
|
251
|
+
identity: {
|
|
252
|
+
mission: 'Automate everything.',
|
|
253
|
+
vision: 'Zero manual work.',
|
|
254
|
+
legalName: 'Test Corp Inc',
|
|
255
|
+
entityType: 'Corporation',
|
|
256
|
+
jurisdiction: 'United States – California',
|
|
257
|
+
industryCategory: 'Software / SaaS',
|
|
258
|
+
geographicFocus: 'Global',
|
|
259
|
+
timeZone: 'America/Chicago',
|
|
260
|
+
clientBrief: '',
|
|
261
|
+
businessHours: {
|
|
262
|
+
monday: { open: '09:00', close: '17:00' }
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const model = resolveOrganizationModel(override)
|
|
267
|
+
expect(model.identity).toEqual(override.identity)
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
it('does not bleed identity changes into other top-level domains', () => {
|
|
271
|
+
const model = resolveOrganizationModel({
|
|
272
|
+
identity: { mission: 'Isolated change.' }
|
|
273
|
+
})
|
|
274
|
+
// Phase 4 (D1, D8): model.navigation and model.statuses removed from top-level OM.
|
|
275
|
+
// Verify other sibling domains still present instead.
|
|
276
|
+
expect(model.systems).toBeDefined()
|
|
277
|
+
expect(model.knowledge).toBeDefined()
|
|
278
|
+
expect(model.navigation).toBeDefined()
|
|
279
|
+
})
|
|
280
|
+
})
|