@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.
Files changed (58) hide show
  1. package/dist/index.d.ts +69 -159
  2. package/dist/index.js +324 -613
  3. package/dist/organization-model/index.d.ts +69 -159
  4. package/dist/organization-model/index.js +324 -613
  5. package/dist/test-utils/index.d.ts +192 -45
  6. package/dist/test-utils/index.js +260 -600
  7. package/package.json +1 -1
  8. package/src/__tests__/template-core-compatibility.test.ts +73 -91
  9. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +94 -182
  10. package/src/auth/multi-tenancy/index.ts +20 -17
  11. package/src/auth/multi-tenancy/memberships/api-schemas.ts +142 -126
  12. package/src/auth/multi-tenancy/memberships/index.ts +26 -22
  13. package/src/auth/multi-tenancy/permissions.test.ts +42 -0
  14. package/src/auth/multi-tenancy/permissions.ts +104 -0
  15. package/src/organization-model/README.md +102 -97
  16. package/src/organization-model/__tests__/defaults.test.ts +19 -6
  17. package/src/organization-model/__tests__/domains/resource-mappings.test.ts +24 -93
  18. package/src/organization-model/__tests__/graph.test.ts +82 -894
  19. package/src/organization-model/__tests__/resolve.test.ts +59 -690
  20. package/src/organization-model/__tests__/schema.test.ts +83 -407
  21. package/src/organization-model/contracts.ts +4 -3
  22. package/src/organization-model/defaults.ts +277 -141
  23. package/src/organization-model/domains/features.ts +31 -22
  24. package/src/organization-model/domains/navigation.ts +27 -20
  25. package/src/organization-model/foundation.ts +42 -54
  26. package/src/organization-model/graph/build.ts +42 -217
  27. package/src/organization-model/graph/index.ts +4 -4
  28. package/src/organization-model/graph/link.ts +10 -0
  29. package/src/organization-model/graph/schema.ts +21 -16
  30. package/src/organization-model/graph/types.ts +10 -10
  31. package/src/organization-model/helpers.ts +74 -0
  32. package/src/organization-model/index.ts +7 -7
  33. package/src/organization-model/organization-graph.mdx +89 -272
  34. package/src/organization-model/organization-model.mdx +152 -320
  35. package/src/organization-model/published.ts +20 -19
  36. package/src/organization-model/resolve.ts +8 -33
  37. package/src/organization-model/schema.ts +63 -205
  38. package/src/organization-model/types.ts +12 -11
  39. package/src/platform/constants/versions.ts +3 -3
  40. package/src/platform/registry/__tests__/command-view.test.ts +6 -5
  41. package/src/platform/registry/__tests__/resource-link.test.ts +30 -0
  42. package/src/platform/registry/__tests__/resource-registry.integration.test.ts +15 -15
  43. package/src/platform/registry/command-view.ts +10 -12
  44. package/src/platform/registry/index.ts +93 -93
  45. package/src/platform/registry/resource-link.ts +32 -0
  46. package/src/platform/registry/resource-registry.ts +917 -876
  47. package/src/platform/registry/serialization.ts +56 -73
  48. package/src/platform/registry/serialized-types.ts +17 -12
  49. package/src/platform/registry/types.ts +14 -43
  50. package/src/reference/_generated/contracts.md +94 -182
  51. package/src/reference/glossary.md +71 -105
  52. package/src/scaffold-registry/__tests__/index.test.ts +125 -1
  53. package/src/scaffold-registry/__tests__/schema.test.ts +48 -20
  54. package/src/scaffold-registry/index.ts +236 -188
  55. package/src/scaffold-registry/schema.ts +47 -22
  56. package/src/supabase/database.types.ts +2880 -2719
  57. package/src/test-utils/fixtures/memberships.ts +82 -80
  58. package/src/platform/registry/domains.ts +0 -165
@@ -73,7 +73,7 @@ function ensureResourceNode(
73
73
  })
74
74
  }
75
75
 
76
- type CommandViewResource =
76
+ type CommandViewResource =
77
77
  | CommandViewData['workflows'][number]
78
78
  | CommandViewData['agents'][number]
79
79
  | CommandViewData['triggers'][number]
@@ -87,7 +87,7 @@ function normalizeCommandViewResourceType(
87
87
  return resourceType === 'human' ? 'human_checkpoint' : resourceType
88
88
  }
89
89
 
90
- function collectCommandViewResources(commandViewData: CommandViewData): CommandViewResource[] {
90
+ function collectCommandViewResources(commandViewData: CommandViewData): CommandViewResource[] {
91
91
  return [
92
92
  ...commandViewData.workflows,
93
93
  ...commandViewData.agents,
@@ -95,8 +95,24 @@ function collectCommandViewResources(commandViewData: CommandViewData): CommandV
95
95
  ...commandViewData.integrations,
96
96
  ...commandViewData.externalResources,
97
97
  ...commandViewData.humanCheckpoints
98
- ]
99
- }
98
+ ]
99
+ }
100
+
101
+ function pushResourceLinks(
102
+ edges: OrganizationGraphEdge[],
103
+ edgeIds: Set<string>,
104
+ resourceNodeId: string,
105
+ links: CommandViewResource['links'] | undefined
106
+ ): void {
107
+ for (const link of links ?? []) {
108
+ pushUniqueEdge(edges, edgeIds, {
109
+ id: edgeId(link.kind, resourceNodeId, link.nodeId),
110
+ kind: link.kind,
111
+ sourceId: resourceNodeId,
112
+ targetId: link.nodeId
113
+ })
114
+ }
115
+ }
100
116
 
101
117
  export function buildOrganizationGraph(input: BuildOrganizationGraphInput): OrganizationGraph {
102
118
  const parsed = BuildOrganizationGraphInputSchema.parse(input)
@@ -116,34 +132,8 @@ export function buildOrganizationGraph(input: BuildOrganizationGraphInput): Orga
116
132
  }
117
133
  pushUniqueNode(nodes, nodeIds, organizationNode)
118
134
 
119
- // Build feature map for lookups
120
- const featureMap = new Map<string, (typeof organizationModel.features)[number]>()
121
- for (const feature of organizationModel.features) {
122
- featureMap.set(feature.id, feature)
123
- }
124
-
125
- const surfaceMap = new Map<string, (typeof organizationModel.navigation.surfaces)[number]>()
126
- for (const surface of organizationModel.navigation.surfaces) {
127
- surfaceMap.set(surface.id, surface)
128
- }
129
-
130
- const entityIds = new Set<string>()
131
- const capabilityIds = new Set<string>()
132
- const resourceMappings = [...organizationModel.resourceMappings].sort((a, b) =>
133
- a.resourceId.localeCompare(b.resourceId)
134
- )
135
- for (const resourceMapping of resourceMappings) {
136
- for (const entityId of resourceMapping.entityIds) {
137
- entityIds.add(entityId)
138
- }
139
- for (const capabilityId of resourceMapping.capabilityIds) {
140
- capabilityIds.add(capabilityId)
141
- }
142
- }
143
-
144
- // Unified feature loop: creates feature nodes with semantic data (replaces old feature + domain loops)
145
- for (const feature of [...organizationModel.features].sort((a, b) => a.id.localeCompare(b.id))) {
146
- const id = nodeId('feature', feature.id)
135
+ for (const feature of [...organizationModel.features].sort((a, b) => a.id.localeCompare(b.id))) {
136
+ const id = nodeId('feature', feature.id)
147
137
  pushUniqueNode(nodes, nodeIds, {
148
138
  id,
149
139
  kind: 'feature',
@@ -157,173 +147,18 @@ export function buildOrganizationGraph(input: BuildOrganizationGraphInput): Orga
157
147
  id: edgeId('contains', organizationNode.id, id),
158
148
  kind: 'contains',
159
149
  sourceId: organizationNode.id,
160
- targetId: id
161
- })
162
-
163
- for (const entityId of feature.entityIds) {
164
- entityIds.add(entityId)
165
- }
166
- for (const capabilityId of feature.capabilityIds) {
167
- capabilityIds.add(capabilityId)
168
- }
169
- for (const surfaceId of feature.surfaceIds) {
170
- const surface = surfaceMap.get(surfaceId)
171
- if (surface) {
172
- pushUniqueEdge(edges, edgeIds, {
173
- id: edgeId('references', id, nodeId('surface', surface.id)),
174
- kind: 'references',
175
- sourceId: id,
176
- targetId: nodeId('surface', surface.id)
177
- })
178
- }
179
- }
180
- }
181
-
182
- for (const surface of [...organizationModel.navigation.surfaces].sort((a, b) => a.id.localeCompare(b.id))) {
183
- const id = nodeId('surface', surface.id)
184
- pushUniqueNode(nodes, nodeIds, {
185
- id,
186
- kind: 'surface',
187
- label: surface.label,
188
- sourceId: surface.id,
189
- description: surface.description,
190
- surfaceType: surface.surfaceType
191
- })
192
- pushUniqueEdge(edges, edgeIds, {
193
- id: edgeId('contains', organizationNode.id, id),
194
- kind: 'contains',
195
- sourceId: organizationNode.id,
196
- targetId: id
197
- })
198
-
199
- if (surface.featureId) {
200
- pushUniqueEdge(edges, edgeIds, {
201
- id: edgeId('exposes', nodeId('feature', surface.featureId), id),
202
- kind: 'exposes',
203
- sourceId: nodeId('feature', surface.featureId),
204
- targetId: id
205
- })
206
- }
207
-
208
- for (const featureId of surface.featureIds) {
209
- if (featureMap.has(featureId)) {
210
- pushUniqueEdge(edges, edgeIds, {
211
- id: edgeId('references', id, nodeId('feature', featureId)),
212
- kind: 'references',
213
- sourceId: id,
214
- targetId: nodeId('feature', featureId)
215
- })
216
- }
217
- }
218
-
219
- for (const entityId of surface.entityIds) {
220
- entityIds.add(entityId)
221
- pushUniqueEdge(edges, edgeIds, {
222
- id: edgeId('references', id, nodeId('entity', entityId)),
223
- kind: 'references',
224
- sourceId: id,
225
- targetId: nodeId('entity', entityId)
226
- })
227
- }
228
-
229
- for (const capabilityId of surface.capabilityIds) {
230
- capabilityIds.add(capabilityId)
231
- pushUniqueEdge(edges, edgeIds, {
232
- id: edgeId('references', id, nodeId('capability', capabilityId)),
233
- kind: 'references',
234
- sourceId: id,
235
- targetId: nodeId('capability', capabilityId)
236
- })
237
- }
238
- }
239
-
240
- for (const entityId of [...entityIds].sort()) {
241
- pushUniqueNode(nodes, nodeIds, {
242
- id: nodeId('entity', entityId),
243
- kind: 'entity',
244
- label: entityId,
245
- sourceId: entityId
246
- })
247
- pushUniqueEdge(edges, edgeIds, {
248
- id: edgeId('contains', organizationNode.id, nodeId('entity', entityId)),
249
- kind: 'contains',
250
- sourceId: organizationNode.id,
251
- targetId: nodeId('entity', entityId)
252
- })
253
- }
254
-
255
- for (const capabilityId of [...capabilityIds].sort()) {
256
- pushUniqueNode(nodes, nodeIds, {
257
- id: nodeId('capability', capabilityId),
258
- kind: 'capability',
259
- label: capabilityId,
260
- sourceId: capabilityId
261
- })
262
- pushUniqueEdge(edges, edgeIds, {
263
- id: edgeId('contains', organizationNode.id, nodeId('capability', capabilityId)),
264
- kind: 'contains',
265
- sourceId: organizationNode.id,
266
- targetId: nodeId('capability', capabilityId)
267
- })
268
- }
269
-
270
- for (const resourceMapping of resourceMappings) {
271
- const id = nodeId('resource', resourceMapping.resourceId)
272
- upsertResourceNode(nodes, nodeIds, resourceNodesById, {
273
- id,
274
- kind: 'resource',
275
- label: resourceMapping.label,
276
- sourceId: resourceMapping.resourceId,
277
- description: resourceMapping.description,
278
- resourceType: resourceMapping.resourceType
279
- })
280
- pushUniqueEdge(edges, edgeIds, {
281
- id: edgeId('contains', organizationNode.id, id),
282
- kind: 'contains',
283
- sourceId: organizationNode.id,
284
- targetId: id
285
- })
286
-
287
- for (const featureId of resourceMapping.featureIds) {
288
- if (featureMap.has(featureId)) {
289
- pushUniqueEdge(edges, edgeIds, {
290
- id: edgeId('maps_to', id, nodeId('feature', featureId)),
291
- kind: 'maps_to',
292
- sourceId: id,
293
- targetId: nodeId('feature', featureId)
294
- })
295
- }
296
- }
297
-
298
- for (const entityId of resourceMapping.entityIds) {
299
- pushUniqueEdge(edges, edgeIds, {
300
- id: edgeId('maps_to', id, nodeId('entity', entityId)),
301
- kind: 'maps_to',
302
- sourceId: id,
303
- targetId: nodeId('entity', entityId)
304
- })
305
- }
306
-
307
- for (const surfaceId of resourceMapping.surfaceIds) {
308
- if (surfaceMap.has(surfaceId)) {
309
- pushUniqueEdge(edges, edgeIds, {
310
- id: edgeId('maps_to', id, nodeId('surface', surfaceId)),
311
- kind: 'maps_to',
312
- sourceId: id,
313
- targetId: nodeId('surface', surfaceId)
314
- })
315
- }
316
- }
317
-
318
- for (const capabilityId of resourceMapping.capabilityIds) {
319
- pushUniqueEdge(edges, edgeIds, {
320
- id: edgeId('maps_to', id, nodeId('capability', capabilityId)),
321
- kind: 'maps_to',
322
- sourceId: id,
323
- targetId: nodeId('capability', capabilityId)
324
- })
325
- }
326
- }
150
+ targetId: id
151
+ })
152
+ const parentId = feature.id.includes('.') ? feature.id.slice(0, feature.id.lastIndexOf('.')) : undefined
153
+ if (parentId) {
154
+ pushUniqueEdge(edges, edgeIds, {
155
+ id: edgeId('contains', nodeId('feature', parentId), id),
156
+ kind: 'contains',
157
+ sourceId: nodeId('feature', parentId),
158
+ targetId: id
159
+ })
160
+ }
161
+ }
327
162
 
328
163
  if (commandViewData) {
329
164
  const commandViewResources = collectCommandViewResources(commandViewData).sort((a, b) =>
@@ -341,24 +176,14 @@ export function buildOrganizationGraph(input: BuildOrganizationGraphInput): Orga
341
176
  resourceType: normalizeCommandViewResourceType(resource.type)
342
177
  })
343
178
 
344
- for (const featureId of resource.domains ?? []) {
345
- if (featureMap.has(featureId)) {
346
- pushUniqueEdge(edges, edgeIds, {
347
- id: edgeId('references', resourceNode.id, nodeId('feature', featureId), 'feature'),
348
- kind: 'references',
349
- sourceId: resourceNode.id,
350
- targetId: nodeId('feature', featureId)
351
- })
352
- }
353
- }
354
-
355
- pushUniqueEdge(edges, edgeIds, {
356
- id: edgeId('contains', organizationNode.id, resourceNode.id),
357
- kind: 'contains',
358
- sourceId: organizationNode.id,
359
- targetId: resourceNode.id
360
- })
361
- }
179
+ pushUniqueEdge(edges, edgeIds, {
180
+ id: edgeId('contains', organizationNode.id, resourceNode.id),
181
+ kind: 'contains',
182
+ sourceId: organizationNode.id,
183
+ targetId: resourceNode.id
184
+ })
185
+ pushResourceLinks(edges, edgeIds, resourceNode.id, resource.links)
186
+ }
362
187
 
363
188
  for (const relationship of [...commandViewData.edges].sort((a, b) => a.id.localeCompare(b.id))) {
364
189
  const sourceNode = ensureResourceNode(nodes, nodeIds, resourceNodesById, relationship.source)
@@ -1,4 +1,4 @@
1
- export * from './types'
2
- export * from './schema'
3
- export * from './build'
4
-
1
+ export * from './types'
2
+ export * from './schema'
3
+ export * from './build'
4
+ export * from './link'
@@ -0,0 +1,10 @@
1
+ import { z } from 'zod'
2
+ import { NodeIdStringSchema } from '../domains/features'
3
+ import { OrganizationGraphEdgeKindSchema } from './schema'
4
+
5
+ export const LinkSchema = z.object({
6
+ nodeId: NodeIdStringSchema,
7
+ kind: OrganizationGraphEdgeKindSchema
8
+ })
9
+
10
+ export type Link = z.infer<typeof LinkSchema>
@@ -1,7 +1,6 @@
1
- import { z } from 'zod'
2
- import { DescriptionSchema, LabelSchema, ModelIdSchema } from '../domains/shared'
3
- import { SurfaceTypeSchema } from '../domains/navigation'
4
- import { OrganizationModelSchema } from '../schema'
1
+ import { z } from 'zod'
2
+ import { DescriptionSchema, LabelSchema } from '../domains/shared'
3
+ import { OrganizationModelSchema } from '../schema'
5
4
 
6
5
  export const OrganizationGraphNodeKindSchema = z.enum([
7
6
  'organization',
@@ -12,19 +11,25 @@ export const OrganizationGraphNodeKindSchema = z.enum([
12
11
  'resource'
13
12
  ])
14
13
 
15
- export const OrganizationGraphEdgeKindSchema = z.enum(['contains', 'references', 'exposes', 'maps_to'])
14
+ export const OrganizationGraphEdgeKindSchema = z.enum([
15
+ 'contains',
16
+ 'references',
17
+ 'exposes',
18
+ 'maps_to',
19
+ 'operates-on',
20
+ 'uses'
21
+ ])
16
22
 
17
- export const OrganizationGraphNodeSchema = z.object({
18
- id: z.string().trim().min(1).max(200),
19
- kind: OrganizationGraphNodeKindSchema,
20
- label: LabelSchema,
21
- sourceId: z.string().trim().min(1).max(255).optional(),
22
- description: DescriptionSchema.optional(),
23
- enabled: z.boolean().optional(),
24
- featureId: ModelIdSchema.optional(),
25
- surfaceType: SurfaceTypeSchema.optional(),
26
- resourceType: z.enum(['workflow', 'agent', 'trigger', 'integration', 'external', 'human_checkpoint']).optional()
27
- })
23
+ export const OrganizationGraphNodeSchema = z.object({
24
+ id: z.string().trim().min(1).max(200),
25
+ kind: OrganizationGraphNodeKindSchema,
26
+ label: LabelSchema,
27
+ sourceId: z.string().trim().min(1).max(255).optional(),
28
+ description: DescriptionSchema.optional(),
29
+ enabled: z.boolean().optional(),
30
+ featureId: z.string().trim().min(1).max(100).optional(),
31
+ resourceType: z.enum(['workflow', 'agent', 'trigger', 'integration', 'external', 'human_checkpoint']).optional()
32
+ })
28
33
 
29
34
  export const OrganizationGraphEdgeSchema = z.object({
30
35
  id: z.string().trim().min(1).max(250),
@@ -1,22 +1,22 @@
1
- import type { CommandViewData } from '../../platform/registry/command-view'
2
- import type { OrganizationModel, OrganizationModelResourceMapping, OrganizationModelSurface } from '../types'
3
- import type { RelationshipType } from '../../platform/registry/command-view'
1
+ import type { CommandViewData } from '../../platform/registry/command-view'
2
+ import type { OrganizationModel } from '../types'
3
+ import type { RelationshipType } from '../../platform/registry/command-view'
4
4
 
5
5
  export type OrganizationGraphNodeKind = 'organization' | 'feature' | 'surface' | 'entity' | 'capability' | 'resource'
6
6
 
7
- export type OrganizationGraphEdgeKind = 'contains' | 'references' | 'exposes' | 'maps_to'
7
+ export type OrganizationGraphEdgeKind = 'contains' | 'references' | 'exposes' | 'maps_to' | 'operates-on' | 'uses'
8
+ export type { Link } from './link'
8
9
 
9
10
  export interface OrganizationGraphNode {
10
11
  id: string
11
12
  kind: OrganizationGraphNodeKind
12
13
  label: string
13
14
  sourceId?: string
14
- description?: string
15
- enabled?: boolean
16
- featureId?: string
17
- surfaceType?: OrganizationModelSurface['surfaceType']
18
- resourceType?: OrganizationModelResourceMapping['resourceType']
19
- }
15
+ description?: string
16
+ enabled?: boolean
17
+ featureId?: string
18
+ resourceType?: 'workflow' | 'agent' | 'trigger' | 'integration' | 'external' | 'human_checkpoint'
19
+ }
20
20
 
21
21
  export interface OrganizationGraphEdge {
22
22
  id: string
@@ -0,0 +1,74 @@
1
+ import type { OrganizationModelFeature } from './types'
2
+
3
+ export function defaultPathFor(id: string): string {
4
+ return `/${id.replaceAll('.', '/')}`
5
+ }
6
+
7
+ export function parentIdOf(id: string): string | undefined {
8
+ const index = id.lastIndexOf('.')
9
+ return index === -1 ? undefined : id.slice(0, index)
10
+ }
11
+
12
+ export function findById(features: readonly OrganizationModelFeature[], id: string): OrganizationModelFeature | undefined {
13
+ return features.find((feature) => feature.id === id)
14
+ }
15
+
16
+ export function findByPath(
17
+ features: readonly OrganizationModelFeature[],
18
+ path: string
19
+ ): OrganizationModelFeature | undefined {
20
+ return features.find((feature) => (feature.path ?? defaultPathFor(feature.id)) === path)
21
+ }
22
+
23
+ export function childrenOf(
24
+ features: readonly OrganizationModelFeature[],
25
+ id: string
26
+ ): OrganizationModelFeature[] {
27
+ const prefix = `${id}.`
28
+ return features.filter((feature) => feature.id.startsWith(prefix) && !feature.id.slice(prefix.length).includes('.'))
29
+ }
30
+
31
+ export function topLevel(features: readonly OrganizationModelFeature[]): OrganizationModelFeature[] {
32
+ return features.filter((feature) => !feature.id.includes('.'))
33
+ }
34
+
35
+ export function ancestorsOf(
36
+ features: readonly OrganizationModelFeature[],
37
+ id: string
38
+ ): OrganizationModelFeature[] {
39
+ const segments = id.split('.')
40
+ const ids = segments.map((_, index) => segments.slice(0, index + 1).join('.'))
41
+ return ids.map((ancestorId) => findById(features, ancestorId)).filter((feature): feature is OrganizationModelFeature => Boolean(feature))
42
+ }
43
+
44
+ export function parentOf(
45
+ features: readonly OrganizationModelFeature[],
46
+ id: string
47
+ ): OrganizationModelFeature | undefined {
48
+ const parentId = parentIdOf(id)
49
+ return parentId ? findById(features, parentId) : undefined
50
+ }
51
+
52
+ function inheritedValue<T>(
53
+ features: readonly OrganizationModelFeature[],
54
+ id: string,
55
+ getValue: (feature: OrganizationModelFeature) => T | undefined
56
+ ): T | undefined {
57
+ return ancestorsOf(features, id)
58
+ .slice()
59
+ .reverse()
60
+ .map(getValue)
61
+ .find((value): value is T => value !== undefined)
62
+ }
63
+
64
+ export function uiPositionFor(features: readonly OrganizationModelFeature[], id: string) {
65
+ return inheritedValue(features, id, (feature) => feature.uiPosition)
66
+ }
67
+
68
+ export function requiresAdminFor(features: readonly OrganizationModelFeature[], id: string): boolean {
69
+ return inheritedValue(features, id, (feature) => feature.requiresAdmin) ?? false
70
+ }
71
+
72
+ export function devOnlyFor(features: readonly OrganizationModelFeature[], id: string): boolean {
73
+ return inheritedValue(features, id, (feature) => feature.devOnly) ?? false
74
+ }
@@ -1,13 +1,13 @@
1
1
  export * from './schema'
2
2
  export * from './types'
3
3
  export * from './contracts'
4
- export * from './defaults'
5
- export * from './resolve'
6
- export * from './foundation'
7
- export * from './graph'
4
+ export * from './defaults'
5
+ export * from './resolve'
6
+ export * from './foundation'
7
+ export * from './helpers'
8
+ export * from './graph'
8
9
  export * from './domains/branding'
9
10
  export * from './domains/sales'
10
11
  export * from './domains/projects'
11
- export * from './domains/features'
12
- export * from './domains/prospecting'
13
- export * from './domains/navigation'
12
+ export * from './domains/features'
13
+ export * from './domains/prospecting'