@elevasis/core 0.35.0 → 0.35.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.
@@ -410,6 +410,8 @@ function childSystemsOf(system) {
410
410
  return system.systems ?? system.subsystems ?? {};
411
411
  }
412
412
  function getSystem(model, path) {
413
+ const flatMatch = model.systems[path];
414
+ if (flatMatch !== void 0) return flatMatch;
413
415
  const segments = path.split(".");
414
416
  let current = model.systems;
415
417
  let node;
package/dist/index.js CHANGED
@@ -726,6 +726,8 @@ function childSystemsOf2(system) {
726
726
  return system.systems ?? system.subsystems ?? {};
727
727
  }
728
728
  function getSystem(model, path) {
729
+ const flatMatch = model.systems[path];
730
+ if (flatMatch !== void 0) return flatMatch;
729
731
  const segments = path.split(".");
730
732
  let current = model.systems;
731
733
  let node;
@@ -726,6 +726,8 @@ function childSystemsOf2(system) {
726
726
  return system.systems ?? system.subsystems ?? {};
727
727
  }
728
728
  function getSystem(model, path) {
729
+ const flatMatch = model.systems[path];
730
+ if (flatMatch !== void 0) return flatMatch;
729
731
  const segments = path.split(".");
730
732
  let current = model.systems;
731
733
  let node;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elevasis/core",
3
- "version": "0.35.0",
3
+ "version": "0.35.1",
4
4
  "license": "MIT",
5
5
  "description": "Minimal shared constants across Elevasis monorepo",
6
6
  "sideEffects": false,
@@ -64,6 +64,23 @@ describe('getSystem', () => {
64
64
  expect(sys?.label).toBe('Leaf')
65
65
  })
66
66
 
67
+ it('returns a flat system whose id contains dots', () => {
68
+ const model = resolveOrganizationModel({
69
+ systems: {
70
+ sales: { id: 'sales', order: 10, label: 'Sales', enabled: true, path: '/sales' },
71
+ 'sales.lead-gen': {
72
+ id: 'sales.lead-gen',
73
+ order: 20,
74
+ label: 'Lead Gen',
75
+ enabled: true,
76
+ path: '/lead-gen'
77
+ }
78
+ }
79
+ })
80
+
81
+ expect(getSystem(model, 'sales.lead-gen')?.label).toBe('Lead Gen')
82
+ })
83
+
67
84
  it('returns undefined for unresolved paths', () => {
68
85
  const model = makeNestedModel()
69
86
  expect(getSystem(model, 'nonexistent')).toBeUndefined()
@@ -90,6 +107,23 @@ describe('getSystemAncestors', () => {
90
107
  ])
91
108
  })
92
109
 
110
+ it('returns available ancestors for flat dotted system ids', () => {
111
+ const model = resolveOrganizationModel({
112
+ systems: {
113
+ sales: { id: 'sales', order: 10, label: 'Sales', enabled: true, path: '/sales' },
114
+ 'sales.lead-gen': {
115
+ id: 'sales.lead-gen',
116
+ order: 20,
117
+ label: 'Lead Gen',
118
+ enabled: true,
119
+ path: '/lead-gen'
120
+ }
121
+ }
122
+ })
123
+
124
+ expect(getSystemAncestors(model, 'sales.lead-gen').map((system) => system.label)).toEqual(['Sales', 'Lead Gen'])
125
+ })
126
+
93
127
  it('returns empty arrays for unresolved paths', () => {
94
128
  const model = makeNestedModel()
95
129
  expect(getSystemAncestors(model, 'does.not.exist')).toEqual([])
@@ -149,6 +149,9 @@ export function devOnlyFor(systems: Record<string, OrganizationModelSystemEntry>
149
149
  * Returns `undefined` if any segment is missing.
150
150
  */
151
151
  export function getSystem(model: OrganizationModel, path: string): SystemEntryWithTree | undefined {
152
+ const flatMatch = (model.systems as Record<string, SystemEntryWithTree>)[path]
153
+ if (flatMatch !== undefined) return flatMatch
154
+
152
155
  const segments = path.split('.')
153
156
  // model.systems is typed as Record<string, OrganizationModelSystemEntry>; cast
154
157
  // to SystemEntryWithTree (which extends it with optional content + subsystems).
@@ -171,6 +174,21 @@ export function getSystem(model: OrganizationModel, path: string): SystemEntryWi
171
174
  * Returns an empty array if the path cannot be resolved.
172
175
  */
173
176
  export function getSystemAncestors(model: OrganizationModel, path: string): SystemEntryWithTree[] {
177
+ const flatMatch = (model.systems as Record<string, SystemEntryWithTree>)[path]
178
+ if (flatMatch !== undefined) {
179
+ const ancestorIds = path
180
+ .split('.')
181
+ .map((_, index, segments) => segments.slice(0, index + 1).join('.'))
182
+ .slice(0, -1)
183
+
184
+ return [
185
+ ...ancestorIds
186
+ .map((ancestorId) => (model.systems as Record<string, SystemEntryWithTree>)[ancestorId])
187
+ .filter((system): system is SystemEntryWithTree => system !== undefined),
188
+ flatMatch
189
+ ]
190
+ }
191
+
174
192
  const segments = path.split('.')
175
193
  const result: SystemEntryWithTree[] = []
176
194
  let current: Record<string, SystemEntryWithTree> = model.systems as Record<string, SystemEntryWithTree>
@@ -1,3 +1,3 @@
1
- export const VERSION = {
2
- CURRENT: '1.12.7'
3
- }
1
+ export const VERSION = {
2
+ CURRENT: '1.12.9'
3
+ }
@@ -1800,10 +1800,79 @@ describe('ResourceRegistry', () => {
1800
1800
  })
1801
1801
  })
1802
1802
 
1803
- describe('registerOrganization -- redeploy (register twice)', () => {
1804
- it('second registerOrganization call replaces previous remote resources', () => {
1805
- const remoteWorkflowV1 = createMockWorkflow('remote-wf')
1806
- const remoteWorkflowV2 = createMockWorkflow('remote-wf-v2')
1803
+ describe('registerOrganization -- redeploy (register twice)', () => {
1804
+ it('validates redeploy candidates against the incoming Organization Model snapshot', () => {
1805
+ const registry = new ResourceRegistry({})
1806
+ const remoteConfig = createMockRemoteConfig({ deploymentId: 'deploy-old' })
1807
+ const resourceId = 'remote-wf'
1808
+ const system = {
1809
+ id: 'sales.lead-gen',
1810
+ title: 'Lead Gen',
1811
+ description: 'Lead generation system',
1812
+ kind: 'domain',
1813
+ lifecycle: 'active'
1814
+ } as unknown as SystemEntry
1815
+ const makeResource = (actionId: string): ResourceEntry =>
1816
+ ({
1817
+ id: resourceId,
1818
+ kind: 'workflow',
1819
+ systemPath: system.id,
1820
+ title: 'Remote workflow',
1821
+ description: 'Remote workflow',
1822
+ status: 'active',
1823
+ ontology: {
1824
+ actions: [actionId],
1825
+ primaryAction: actionId
1826
+ }
1827
+ }) as ResourceEntry
1828
+ const makeModel = (resource: ResourceEntry) =>
1829
+ ({
1830
+ systems: { [system.id]: system },
1831
+ resources: { [resource.id]: resource }
1832
+ }) as any
1833
+ const makeDescriptorBackedWorkflow = (resource: ResourceEntry): WorkflowDefinition => {
1834
+ const workflow = createMockWorkflow(resource.id)
1835
+
1836
+ return {
1837
+ ...workflow,
1838
+ config: {
1839
+ ...workflow.config,
1840
+ resource
1841
+ }
1842
+ } as WorkflowDefinition
1843
+ }
1844
+
1845
+ const oldResource = makeResource('sales.lead-gen:action/old-import')
1846
+ const newResource = makeResource('sales.lead-gen:action/new-import')
1847
+
1848
+ registry.registerOrganization(
1849
+ 'test-org',
1850
+ {
1851
+ version: '1.0.0',
1852
+ organizationModel: makeModel(oldResource),
1853
+ workflows: [makeDescriptorBackedWorkflow(oldResource)]
1854
+ },
1855
+ remoteConfig
1856
+ )
1857
+
1858
+ expect(() =>
1859
+ registry.registerOrganization(
1860
+ 'test-org',
1861
+ {
1862
+ version: '1.0.1',
1863
+ organizationModel: makeModel(newResource),
1864
+ workflows: [makeDescriptorBackedWorkflow(newResource)]
1865
+ },
1866
+ createMockRemoteConfig({ deploymentId: 'deploy-new' })
1867
+ )
1868
+ ).not.toThrow()
1869
+
1870
+ expect(registry.getRemoteConfig('test-org', resourceId)?.deploymentId).toBe('deploy-new')
1871
+ })
1872
+
1873
+ it('second registerOrganization call replaces previous remote resources', () => {
1874
+ const remoteWorkflowV1 = createMockWorkflow('remote-wf')
1875
+ const remoteWorkflowV2 = createMockWorkflow('remote-wf-v2')
1807
1876
 
1808
1877
  const registry = new ResourceRegistry({})
1809
1878
 
@@ -240,6 +240,7 @@ export class ResourceRegistry {
240
240
  ? {
241
241
  ...base,
242
242
  version: incoming.version ?? base.version ?? '0.0.0',
243
+ organizationModel: incoming.organizationModel ?? base.organizationModel,
243
244
  workflows: [...(base.workflows ?? []), ...(incoming.workflows ?? [])],
244
245
  agents: [...(base.agents ?? []), ...(incoming.agents ?? [])],
245
246
  triggers: incoming.triggers,
@@ -458,6 +459,7 @@ export class ResourceRegistry {
458
459
  // Merge incoming resources into the org (or create new org entry)
459
460
  const existingOrg = this.registry[orgName]
460
461
  if (existingOrg) {
462
+ existingOrg.organizationModel = org.organizationModel ?? existingOrg.organizationModel
461
463
  existingOrg.workflows = [...(existingOrg.workflows ?? []), ...(org.workflows ?? [])]
462
464
  existingOrg.agents = [...(existingOrg.agents ?? []), ...(org.agents ?? [])]
463
465
  // Deployment-owned metadata: replace entirely (unregister cleared these)