@elevasis/core 0.10.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +69 -159
- package/dist/index.js +324 -613
- package/dist/organization-model/index.d.ts +69 -159
- package/dist/organization-model/index.js +324 -613
- package/dist/test-utils/index.d.ts +192 -45
- package/dist/test-utils/index.js +260 -600
- package/package.json +1 -1
- package/src/__tests__/template-core-compatibility.test.ts +73 -91
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +94 -182
- package/src/auth/multi-tenancy/index.ts +20 -17
- package/src/auth/multi-tenancy/memberships/api-schemas.ts +142 -126
- package/src/auth/multi-tenancy/memberships/index.ts +26 -22
- package/src/auth/multi-tenancy/permissions.test.ts +42 -0
- package/src/auth/multi-tenancy/permissions.ts +104 -0
- package/src/organization-model/README.md +102 -97
- package/src/organization-model/__tests__/defaults.test.ts +19 -6
- package/src/organization-model/__tests__/domains/resource-mappings.test.ts +24 -93
- package/src/organization-model/__tests__/graph.test.ts +82 -894
- package/src/organization-model/__tests__/resolve.test.ts +59 -690
- package/src/organization-model/__tests__/schema.test.ts +83 -407
- package/src/organization-model/contracts.ts +4 -3
- package/src/organization-model/defaults.ts +277 -141
- package/src/organization-model/domains/features.ts +31 -22
- package/src/organization-model/domains/navigation.ts +27 -20
- package/src/organization-model/foundation.ts +42 -54
- package/src/organization-model/graph/build.ts +42 -217
- package/src/organization-model/graph/index.ts +4 -4
- package/src/organization-model/graph/link.ts +10 -0
- package/src/organization-model/graph/schema.ts +21 -16
- package/src/organization-model/graph/types.ts +10 -10
- package/src/organization-model/helpers.ts +74 -0
- package/src/organization-model/index.ts +7 -7
- package/src/organization-model/organization-graph.mdx +89 -272
- package/src/organization-model/organization-model.mdx +152 -320
- package/src/organization-model/published.ts +20 -19
- package/src/organization-model/resolve.ts +8 -33
- package/src/organization-model/schema.ts +63 -205
- package/src/organization-model/types.ts +12 -11
- package/src/platform/constants/versions.ts +3 -3
- package/src/platform/registry/__tests__/command-view.test.ts +6 -5
- package/src/platform/registry/__tests__/resource-link.test.ts +30 -0
- package/src/platform/registry/__tests__/resource-registry.integration.test.ts +15 -15
- package/src/platform/registry/command-view.ts +10 -12
- package/src/platform/registry/index.ts +93 -93
- package/src/platform/registry/resource-link.ts +32 -0
- package/src/platform/registry/resource-registry.ts +917 -876
- package/src/platform/registry/serialization.ts +56 -73
- package/src/platform/registry/serialized-types.ts +17 -12
- package/src/platform/registry/types.ts +14 -43
- package/src/reference/_generated/contracts.md +94 -182
- package/src/reference/glossary.md +71 -105
- package/src/scaffold-registry/__tests__/index.test.ts +125 -1
- package/src/scaffold-registry/__tests__/schema.test.ts +48 -20
- package/src/scaffold-registry/index.ts +236 -188
- package/src/scaffold-registry/schema.ts +47 -22
- package/src/supabase/database.types.ts +2880 -2719
- package/src/test-utils/fixtures/memberships.ts +82 -80
- package/src/platform/registry/domains.ts +0 -165
|
@@ -1,97 +1,102 @@
|
|
|
1
|
-
# Organization Model
|
|
2
|
-
|
|
3
|
-
The organization model is the published semantic contract that maps a tenant's product shape to
|
|
4
|
-
|
|
5
|
-
Use this module when
|
|
6
|
-
|
|
7
|
-
## Published Exports
|
|
8
|
-
|
|
9
|
-
The public entry point exposes:
|
|
10
|
-
|
|
11
|
-
- `OrganizationModelSchema`
|
|
12
|
-
- `
|
|
13
|
-
- `
|
|
14
|
-
- `
|
|
15
|
-
- `
|
|
16
|
-
- `
|
|
17
|
-
-
|
|
18
|
-
- `OrganizationModel` and
|
|
19
|
-
|
|
20
|
-
Import it from the published subpath:
|
|
21
|
-
|
|
22
|
-
```ts
|
|
23
|
-
import {
|
|
24
|
-
DEFAULT_ORGANIZATION_MODEL,
|
|
25
|
-
|
|
26
|
-
defineOrganizationModel,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- `
|
|
41
|
-
- `
|
|
42
|
-
- `
|
|
43
|
-
- `
|
|
44
|
-
- `
|
|
45
|
-
- `
|
|
46
|
-
- `
|
|
47
|
-
- `
|
|
48
|
-
- `
|
|
49
|
-
- `
|
|
50
|
-
- `
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
- `
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
- `
|
|
78
|
-
- `
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
|
|
97
|
-
|
|
1
|
+
# Organization Model
|
|
2
|
+
|
|
3
|
+
The organization model is the published semantic contract that maps a tenant's product shape to flat feature hierarchy, shell navigation, business semantics, and graph bindings.
|
|
4
|
+
|
|
5
|
+
Use this module when resolving or validating an organization's contract before wiring UI shells, routing, feature gates, or domain-specific capability checks.
|
|
6
|
+
|
|
7
|
+
## Published Exports
|
|
8
|
+
|
|
9
|
+
The public entry point exposes:
|
|
10
|
+
|
|
11
|
+
- `OrganizationModelSchema`
|
|
12
|
+
- `FeatureSchema`
|
|
13
|
+
- `DEFAULT_ORGANIZATION_MODEL`
|
|
14
|
+
- `defineOrganizationModel`
|
|
15
|
+
- `resolveOrganizationModel`
|
|
16
|
+
- `createFoundationOrganizationModel`
|
|
17
|
+
- node ID and feature helper types
|
|
18
|
+
- `OrganizationModel` and supporting domain types
|
|
19
|
+
|
|
20
|
+
Import it from the published subpath:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import {
|
|
24
|
+
DEFAULT_ORGANIZATION_MODEL,
|
|
25
|
+
createFoundationOrganizationModel,
|
|
26
|
+
defineOrganizationModel,
|
|
27
|
+
resolveOrganizationModel,
|
|
28
|
+
type OrganizationModel
|
|
29
|
+
} from '@elevasis/core/organization-model'
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Contract Shape
|
|
33
|
+
|
|
34
|
+
The model is versioned and currently validates against `version: 1`.
|
|
35
|
+
|
|
36
|
+
Top-level fields:
|
|
37
|
+
|
|
38
|
+
- `version`
|
|
39
|
+
- `branding`
|
|
40
|
+
- `features`
|
|
41
|
+
- `navigation`
|
|
42
|
+
- `sales`
|
|
43
|
+
- `prospecting`
|
|
44
|
+
- `projects`
|
|
45
|
+
- `identity`
|
|
46
|
+
- `customers`
|
|
47
|
+
- `offerings`
|
|
48
|
+
- `roles`
|
|
49
|
+
- `goals`
|
|
50
|
+
- `statuses`
|
|
51
|
+
- `operations`
|
|
52
|
+
|
|
53
|
+
Resources bind to the graph from deployment metadata through `links` and `category`.
|
|
54
|
+
|
|
55
|
+
## Feature Set
|
|
56
|
+
|
|
57
|
+
Feature hierarchy is authored as a flat array. Dotted IDs define parent/child relationships:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
features: [
|
|
61
|
+
{ id: 'dashboard', label: 'Dashboard', enabled: true, path: '/', uiPosition: 'sidebar-primary' },
|
|
62
|
+
{ id: 'sales', label: 'Sales', enabled: true, uiPosition: 'sidebar-primary' },
|
|
63
|
+
{ id: 'sales.crm', label: 'CRM', enabled: true, path: '/crm' },
|
|
64
|
+
{ id: 'operations.resources', label: 'Resources', enabled: true, path: '/operations/resources' }
|
|
65
|
+
]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Containers omit `path`; leaves provide `path`. `uiPosition`, `requiresAdmin`, and `devOnly` inherit from ancestors.
|
|
69
|
+
Development-only features remain in the contract with `enabled: true` and `devOnly: true`; shell consumers hide them and guard their paths outside development mode.
|
|
70
|
+
|
|
71
|
+
## Graph IDs
|
|
72
|
+
|
|
73
|
+
Cross-collection links use kind-prefixed IDs:
|
|
74
|
+
|
|
75
|
+
- `feature:sales.crm`
|
|
76
|
+
- `integration:instantly`
|
|
77
|
+
- `resource:lead-import`
|
|
78
|
+
- `capability:operations.queue.review`
|
|
79
|
+
|
|
80
|
+
## Resolution Semantics
|
|
81
|
+
|
|
82
|
+
- `defineOrganizationModel()` is a typed helper.
|
|
83
|
+
- `resolveOrganizationModel()` deep-merges a partial override into the default model, then validates it.
|
|
84
|
+
- `createFoundationOrganizationModel()` resolves the canonical model and returns UI-facing helper outputs.
|
|
85
|
+
- Plain objects merge recursively.
|
|
86
|
+
- Arrays replace the default value.
|
|
87
|
+
- Missing fields fall back to `DEFAULT_ORGANIZATION_MODEL`.
|
|
88
|
+
|
|
89
|
+
## Referential Integrity
|
|
90
|
+
|
|
91
|
+
- Feature IDs must be unique.
|
|
92
|
+
- Child feature IDs require ancestor feature nodes.
|
|
93
|
+
- Container features omit `path`.
|
|
94
|
+
- Leaf features provide `path`.
|
|
95
|
+
- Resource links are validated against graph node IDs during registry registration.
|
|
96
|
+
|
|
97
|
+
## Practical Guidance
|
|
98
|
+
|
|
99
|
+
- Use `resolveOrganizationModel()` when you need a runtime-safe model.
|
|
100
|
+
- Use `defineOrganizationModel()` when authoring static overrides.
|
|
101
|
+
- Keep feature IDs stable because shell routing, gating, breadcrumbs, and docs depend on them.
|
|
102
|
+
- Put semantic resource relationships on resource metadata, not on feature nodes.
|
|
@@ -69,12 +69,25 @@ describe('organization-model defaults', () => {
|
|
|
69
69
|
expect(() => OrganizationModelSchema.parse(result)).not.toThrow()
|
|
70
70
|
})
|
|
71
71
|
|
|
72
|
-
it('resolveOrganizationModel with no override equals resolveOrganizationModel with DEFAULT_ORGANIZATION_MODEL', () => {
|
|
73
|
-
const fromUndefined = resolveOrganizationModel(undefined)
|
|
74
|
-
const fromDefault = resolveOrganizationModel(DEFAULT_ORGANIZATION_MODEL)
|
|
75
|
-
expect(fromDefault).toEqual(fromUndefined)
|
|
76
|
-
})
|
|
77
|
-
|
|
72
|
+
it('resolveOrganizationModel with no override equals resolveOrganizationModel with DEFAULT_ORGANIZATION_MODEL', () => {
|
|
73
|
+
const fromUndefined = resolveOrganizationModel(undefined)
|
|
74
|
+
const fromDefault = resolveOrganizationModel(DEFAULT_ORGANIZATION_MODEL)
|
|
75
|
+
expect(fromDefault).toEqual(fromUndefined)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('keeps Command View enabled but development-only in the default Operations navigation', () => {
|
|
79
|
+
const result = resolveOrganizationModel(undefined)
|
|
80
|
+
const commandViewFeature = result.features.find((feature) => feature.id === 'operations.command-view')
|
|
81
|
+
const commandViewSurface = DEFAULT_ORGANIZATION_MODEL_NAVIGATION.surfaces.find(
|
|
82
|
+
(surface) => surface.id === 'operations.command-view'
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
expect(commandViewFeature?.enabled).toBe(true)
|
|
86
|
+
expect(commandViewFeature?.devOnly).toBe(true)
|
|
87
|
+
expect(commandViewSurface?.enabled).toBe(true)
|
|
88
|
+
expect(commandViewSurface?.devOnly).toBe(true)
|
|
89
|
+
})
|
|
90
|
+
})
|
|
78
91
|
|
|
79
92
|
describe.each(domainCases)('$name (domain sub-constant)', ({ name: _name, constant, schema }) => {
|
|
80
93
|
it('passes its domain schema parse without throwing', () => {
|
|
@@ -267,96 +267,27 @@ describe('ResourceMappingSchema — mixed techStack presence across entries', ()
|
|
|
267
267
|
// Group 6: Integration via resolveOrganizationModel
|
|
268
268
|
// ---------------------------------------------------------------------------
|
|
269
269
|
|
|
270
|
-
describe('resolveOrganizationModel
|
|
271
|
-
it('
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
expect(model
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
it('resolves a model with a resourceMapping that has techStack', () => {
|
|
296
|
-
const model = resolveOrganizationModel({
|
|
297
|
-
resourceMappings: [
|
|
298
|
-
{
|
|
299
|
-
id: 'rm-crm-sync',
|
|
300
|
-
label: 'CRM Sync',
|
|
301
|
-
resourceId: 'crm-sync-workflow',
|
|
302
|
-
resourceType: 'workflow',
|
|
303
|
-
featureIds: [],
|
|
304
|
-
entityIds: [],
|
|
305
|
-
surfaceIds: [],
|
|
306
|
-
capabilityIds: [],
|
|
307
|
-
techStack: {
|
|
308
|
-
platform: 'Salesforce',
|
|
309
|
-
purpose: 'Enterprise CRM integration',
|
|
310
|
-
credentialStatus: 'configured',
|
|
311
|
-
isSystemOfRecord: true
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
]
|
|
315
|
-
})
|
|
316
|
-
expect(model.resourceMappings).toHaveLength(1)
|
|
317
|
-
const mapping = model.resourceMappings[0]
|
|
318
|
-
expect(mapping.techStack).toBeDefined()
|
|
319
|
-
expect(mapping.techStack?.platform).toBe('Salesforce')
|
|
320
|
-
expect(mapping.techStack?.isSystemOfRecord).toBe(true)
|
|
321
|
-
})
|
|
322
|
-
|
|
323
|
-
it('resolves multiple mappings with mixed techStack presence', () => {
|
|
324
|
-
const model = resolveOrganizationModel({
|
|
325
|
-
resourceMappings: [
|
|
326
|
-
{
|
|
327
|
-
id: 'rm-a',
|
|
328
|
-
label: 'Mapping A',
|
|
329
|
-
resourceId: 'workflow-a',
|
|
330
|
-
resourceType: 'workflow',
|
|
331
|
-
featureIds: [],
|
|
332
|
-
entityIds: [],
|
|
333
|
-
surfaceIds: [],
|
|
334
|
-
capabilityIds: [],
|
|
335
|
-
techStack: {
|
|
336
|
-
platform: 'HubSpot',
|
|
337
|
-
purpose: 'Contacts',
|
|
338
|
-
credentialStatus: 'pending'
|
|
339
|
-
}
|
|
340
|
-
},
|
|
341
|
-
{
|
|
342
|
-
id: 'rm-b',
|
|
343
|
-
label: 'Mapping B',
|
|
344
|
-
resourceId: 'workflow-b',
|
|
345
|
-
resourceType: 'agent',
|
|
346
|
-
featureIds: [],
|
|
347
|
-
entityIds: [],
|
|
348
|
-
surfaceIds: [],
|
|
349
|
-
capabilityIds: []
|
|
350
|
-
}
|
|
351
|
-
]
|
|
352
|
-
})
|
|
353
|
-
expect(model.resourceMappings).toHaveLength(2)
|
|
354
|
-
expect(model.resourceMappings[0].techStack?.platform).toBe('HubSpot')
|
|
355
|
-
expect(model.resourceMappings[0].techStack?.isSystemOfRecord).toBe(false)
|
|
356
|
-
expect(model.resourceMappings[1].techStack).toBeUndefined()
|
|
357
|
-
})
|
|
358
|
-
|
|
359
|
-
it('resolved model still validates without errors when resourceMappings omitted', () => {
|
|
360
|
-
expect(() => resolveOrganizationModel({})).not.toThrow()
|
|
361
|
-
})
|
|
362
|
-
})
|
|
270
|
+
describe('resolveOrganizationModel resource mapping removal', () => {
|
|
271
|
+
it('resolved model validates without resourceMappings', () => {
|
|
272
|
+
expect(() => resolveOrganizationModel({})).not.toThrow()
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
it('drops resourceMappings overrides because graph links now live on resources', () => {
|
|
276
|
+
const model = resolveOrganizationModel({
|
|
277
|
+
resourceMappings: [
|
|
278
|
+
{
|
|
279
|
+
id: 'rm-legacy',
|
|
280
|
+
label: 'Legacy mapping',
|
|
281
|
+
resourceId: 'legacy-workflow',
|
|
282
|
+
resourceType: 'workflow',
|
|
283
|
+
featureIds: [],
|
|
284
|
+
entityIds: [],
|
|
285
|
+
surfaceIds: [],
|
|
286
|
+
capabilityIds: []
|
|
287
|
+
}
|
|
288
|
+
]
|
|
289
|
+
} as Parameters<typeof resolveOrganizationModel>[0])
|
|
290
|
+
|
|
291
|
+
expect('resourceMappings' in model).toBe(false)
|
|
292
|
+
})
|
|
293
|
+
})
|