@elevasis/core 0.27.0 → 0.28.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.
@@ -1,75 +1,76 @@
1
- import { z, type ZodType } from 'zod'
2
- import { ActionRefSchema } from './actions'
3
- import {
4
- ColorTokenSchema,
5
- DescriptionSchema,
6
- IconNameSchema,
7
- LabelSchema,
8
- ModelIdSchema,
9
- PathSchema,
10
- ReferenceIdsSchema
11
- } from './shared'
1
+ import { z, type ZodType } from 'zod'
2
+ import { ActionRefSchema } from './actions'
3
+ import {
4
+ ColorTokenSchema,
5
+ DescriptionSchema,
6
+ IconNameSchema,
7
+ LabelSchema,
8
+ ModelIdSchema,
9
+ PathSchema,
10
+ ReferenceIdsSchema
11
+ } from './shared'
12
12
  import { OntologyScopeSchema, type OntologyScope } from '../ontology'
13
-
14
- // ---------------------------------------------------------------------------
15
- // Systems domain
16
- // ---------------------------------------------------------------------------
17
- //
18
- // A System is a tenant-defined bounded context. It can carry UI presence,
19
- // hierarchy, and governance metadata; non-UI systems simply omit `ui`.
20
-
21
- export const SystemKindSchema = z
22
- .enum(['product', 'operational', 'platform', 'diagnostic'])
23
- .meta({ label: 'System kind', color: 'blue' })
24
- export const SystemLifecycleSchema = z
25
- .enum(['draft', 'beta', 'active', 'deprecated', 'archived'])
26
- .meta({ label: 'Lifecycle', color: 'teal' })
27
- /** @deprecated Use SystemLifecycleSchema. Accepted for one publish cycle. */
28
- export const SystemStatusSchema = z.enum(['active', 'deprecated', 'archived']).meta({ label: 'Status', color: 'teal' })
29
- export const SystemIdSchema = ModelIdSchema
30
- /**
31
- * Validates a dot-separated system path (e.g. "sales.lead-gen", "sales.crm").
32
- * Each segment is lowercase, starts with a letter or digit, and may contain hyphens.
33
- * This is the canonical form used in `resource.systemPath`.
34
- */
35
- export const SystemPathSchema = z
36
- .string()
37
- .trim()
38
- .min(1)
39
- .regex(
40
- /^[a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)*$/,
41
- 'must be a dotted lowercase path (e.g. "sales.lead-gen" or "sales.crm")'
42
- )
43
- export const UiPositionSchema = z.enum(['sidebar-primary', 'sidebar-bottom']).meta({ label: 'UI position' })
44
- export const NodeIdPathSchema = SystemIdSchema
45
- export const NodeIdStringSchema = z
46
- .string()
47
- .trim()
48
- .min(1)
49
- .max(200)
13
+ import { defineDomainRecord } from '../helpers'
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Systems domain
17
+ // ---------------------------------------------------------------------------
18
+ //
19
+ // A System is a tenant-defined bounded context. It can carry UI presence,
20
+ // hierarchy, and governance metadata; non-UI systems simply omit `ui`.
21
+
22
+ export const SystemKindSchema = z
23
+ .enum(['product', 'operational', 'platform', 'diagnostic'])
24
+ .meta({ label: 'System kind', color: 'blue' })
25
+ export const SystemLifecycleSchema = z
26
+ .enum(['draft', 'beta', 'active', 'deprecated', 'archived'])
27
+ .meta({ label: 'Lifecycle', color: 'teal' })
28
+ /** @deprecated Use SystemLifecycleSchema. Accepted for one publish cycle. */
29
+ export const SystemStatusSchema = z.enum(['active', 'deprecated', 'archived']).meta({ label: 'Status', color: 'teal' })
30
+ export const SystemIdSchema = ModelIdSchema
31
+ /**
32
+ * Validates a dot-separated system path (e.g. "sales.lead-gen", "sales.crm").
33
+ * Each segment is lowercase, starts with a letter or digit, and may contain hyphens.
34
+ * This is the canonical form used in `resource.systemPath`.
35
+ */
36
+ export const SystemPathSchema = z
37
+ .string()
38
+ .trim()
39
+ .min(1)
40
+ .regex(
41
+ /^[a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)*$/,
42
+ 'must be a dotted lowercase path (e.g. "sales.lead-gen" or "sales.crm")'
43
+ )
44
+ export const UiPositionSchema = z.enum(['sidebar-primary', 'sidebar-bottom']).meta({ label: 'UI position' })
45
+ export const NodeIdPathSchema = SystemIdSchema
46
+ export const NodeIdStringSchema = z
47
+ .string()
48
+ .trim()
49
+ .min(1)
50
+ .max(200)
50
51
  // Kind prefix allows hyphens; id allows scoped references such as ontology targets.
51
- .regex(
52
- /^[a-z][a-z-]*:([a-z0-9-]+)(\.[a-z0-9-]+)*(:[a-z0-9.-]+)*$/,
52
+ .regex(
53
+ /^[a-z][a-z-]*:([a-z0-9-]+)(\.[a-z0-9-]+)*(:[a-z0-9.-]+)*$/,
53
54
  'Node references must use kind:dotted-path (e.g. system:sales.crm or resource:lead-gen.company.qualify)'
54
55
  )
55
-
56
- export const SystemUiSchema = z.object({
57
- path: PathSchema,
58
- surfaces: ReferenceIdsSchema,
59
- icon: IconNameSchema.optional(),
60
- order: z.number().int().optional()
61
- })
62
-
63
- // ---------------------------------------------------------------------------
64
- // Recursive SystemEntry schema.
65
- //
66
- // TypeScript cannot infer the type of a schema that references itself through
67
- // z.lazy(). The standard Zod fix is to:
68
- // 1. Declare the output type as an explicit interface BEFORE the schema.
69
- // 2. Annotate the schema variable with `ZodType<SystemEntry>` so TS uses
70
- // the declared interface instead of trying to infer through the cycle.
71
- // ---------------------------------------------------------------------------
72
-
56
+
57
+ export const SystemUiSchema = z.object({
58
+ path: PathSchema,
59
+ surfaces: ReferenceIdsSchema,
60
+ icon: IconNameSchema.optional(),
61
+ order: z.number().int().optional()
62
+ })
63
+
64
+ // ---------------------------------------------------------------------------
65
+ // Recursive SystemEntry schema.
66
+ //
67
+ // TypeScript cannot infer the type of a schema that references itself through
68
+ // z.lazy(). The standard Zod fix is to:
69
+ // 1. Declare the output type as an explicit interface BEFORE the schema.
70
+ // 2. Annotate the schema variable with `ZodType<SystemEntry>` so TS uses
71
+ // the declared interface instead of trying to infer through the cycle.
72
+ // ---------------------------------------------------------------------------
73
+
73
74
  export type JsonPrimitive = string | number | boolean | null
74
75
  export type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue }
75
76
 
@@ -84,33 +85,30 @@ export const JsonValueSchema: ZodType<JsonValue> = z.lazy(() =>
84
85
  ])
85
86
  )
86
87
 
87
- export const SystemConfigSchema = z
88
- .record(z.string().trim().min(1).max(200), JsonValueSchema)
89
- .default({})
90
- .optional()
88
+ export const SystemConfigSchema = z.record(z.string().trim().min(1).max(200), JsonValueSchema).default({}).optional()
91
89
 
92
90
  /** Explicit interface needed to annotate the recursive SystemEntrySchema. */
93
91
  export interface SystemEntry {
94
- id: string
95
- label?: string
96
- title?: string
97
- description?: string
98
- kind?: 'product' | 'operational' | 'platform' | 'diagnostic'
99
- parentSystemId?: string
100
- ui?: { path: string; surfaces: string[]; icon?: string; order?: number }
101
- lifecycle?: 'draft' | 'beta' | 'active' | 'deprecated' | 'archived'
102
- responsibleRoleId?: string
103
- governedByKnowledge?: string[]
104
- actions?: { actionId: string; intent: 'exposes' | 'consumes'; invocation?: unknown }[]
105
- policies?: string[]
106
- drivesGoals?: string[]
107
- /** @deprecated Use lifecycle. Accepted for one publish cycle. */
108
- status?: 'active' | 'deprecated' | 'archived'
109
- path?: string
110
- icon?: string
111
- color?: string
112
- uiPosition?: 'sidebar-primary' | 'sidebar-bottom'
113
- enabled?: boolean
92
+ id: string
93
+ label?: string
94
+ title?: string
95
+ description?: string
96
+ kind?: 'product' | 'operational' | 'platform' | 'diagnostic'
97
+ parentSystemId?: string
98
+ ui?: { path: string; surfaces: string[]; icon?: string; order?: number }
99
+ lifecycle?: 'draft' | 'beta' | 'active' | 'deprecated' | 'archived'
100
+ responsibleRoleId?: string
101
+ governedByKnowledge?: string[]
102
+ actions?: { actionId: string; intent: 'exposes' | 'consumes'; invocation?: unknown }[]
103
+ policies?: string[]
104
+ drivesGoals?: string[]
105
+ /** @deprecated Use lifecycle. Accepted for one publish cycle. */
106
+ status?: 'active' | 'deprecated' | 'archived'
107
+ path?: string
108
+ icon?: string
109
+ color?: string
110
+ uiPosition?: 'sidebar-primary' | 'sidebar-bottom'
111
+ enabled?: boolean
114
112
  devOnly?: boolean
115
113
  requiresAdmin?: boolean
116
114
  order: number
@@ -119,58 +117,58 @@ export interface SystemEntry {
119
117
  systems?: Record<string, SystemEntry>
120
118
  subsystems?: Record<string, SystemEntry>
121
119
  }
122
-
120
+
123
121
  export const SystemEntrySchema: ZodType<SystemEntry> = z
124
122
  .object({
125
- /** Stable tenant-defined system id (e.g. "sys.lead-gen" or "sales.crm"). */
126
- id: SystemIdSchema,
127
- /** Human-readable system label shown in UI, governance, and operations surfaces. */
128
- label: LabelSchema.optional(),
129
- /** @deprecated Use label. Accepted for pre-consolidation System declarations. */
130
- title: LabelSchema.optional(),
131
- /** One-paragraph purpose statement for the bounded context. */
132
- description: DescriptionSchema.optional(),
133
- /** Closed system shape enum; catalog values remain tenant-defined. */
134
- kind: SystemKindSchema.optional(),
135
- /** Optional self-reference for System hierarchy. */
136
- parentSystemId: SystemIdSchema.optional(),
137
- /** Optional UI presence. Systems without UI omit this. */
138
- ui: SystemUiSchema.optional(),
139
- /** Canonical lifecycle state. Replaces Feature.enabled/devOnly and System.status. */
140
- lifecycle: SystemLifecycleSchema.optional(),
141
- /** Optional role responsible for this system. */
142
- responsibleRoleId: ModelIdSchema.meta({ ref: 'role' }).optional(),
143
- /** Optional knowledge nodes that govern this system. */
144
- governedByKnowledge: z
145
- .array(ModelIdSchema.meta({ ref: 'knowledge' }))
146
- .default([])
147
- .optional(),
148
- /** Optional actions this system exposes or consumes. */
149
- actions: z.array(ActionRefSchema).optional(),
150
- /** Optional operational policies that apply to this system. */
151
- policies: z
152
- .array(ModelIdSchema.meta({ ref: 'policy' }))
153
- .default([])
154
- .optional(),
155
- /** Optional goals this system contributes to. */
156
- drivesGoals: z
157
- .array(ModelIdSchema.meta({ ref: 'goal' }))
158
- .default([])
159
- .optional(),
160
- /** @deprecated Use lifecycle. Accepted for one publish cycle. */
161
- status: SystemStatusSchema.optional(),
162
- /** @deprecated Use ui.path. Kept for one-cycle Feature compatibility. */
163
- path: PathSchema.optional(),
164
- /** @deprecated Use ui.icon. Kept for one-cycle Feature compatibility. */
165
- icon: IconNameSchema.optional(),
166
- /** @deprecated Feature color token, retained for one-cycle compatibility. */
167
- color: ColorTokenSchema.optional(),
168
- /** @deprecated UI placement hint, retained for one-cycle compatibility. */
169
- uiPosition: UiPositionSchema.optional(),
170
- /** @deprecated Use lifecycle. */
171
- enabled: z.boolean().optional(),
172
- /** @deprecated Use lifecycle: "beta". */
173
- devOnly: z.boolean().optional(),
123
+ /** Stable tenant-defined system id (e.g. "sys.lead-gen" or "sales.crm"). */
124
+ id: SystemIdSchema,
125
+ /** Human-readable system label shown in UI, governance, and operations surfaces. */
126
+ label: LabelSchema.optional(),
127
+ /** @deprecated Use label. Accepted for pre-consolidation System declarations. */
128
+ title: LabelSchema.optional(),
129
+ /** One-paragraph purpose statement for the bounded context. */
130
+ description: DescriptionSchema.optional(),
131
+ /** Closed system shape enum; catalog values remain tenant-defined. */
132
+ kind: SystemKindSchema.optional(),
133
+ /** Optional self-reference for System hierarchy. */
134
+ parentSystemId: SystemIdSchema.optional(),
135
+ /** Optional UI presence. Systems without UI omit this. */
136
+ ui: SystemUiSchema.optional(),
137
+ /** Canonical lifecycle state. Replaces Feature.enabled/devOnly and System.status. */
138
+ lifecycle: SystemLifecycleSchema.optional(),
139
+ /** Optional role responsible for this system. */
140
+ responsibleRoleId: ModelIdSchema.meta({ ref: 'role' }).optional(),
141
+ /** Optional knowledge nodes that govern this system. */
142
+ governedByKnowledge: z
143
+ .array(ModelIdSchema.meta({ ref: 'knowledge' }))
144
+ .default([])
145
+ .optional(),
146
+ /** Optional actions this system exposes or consumes. */
147
+ actions: z.array(ActionRefSchema).optional(),
148
+ /** Optional operational policies that apply to this system. */
149
+ policies: z
150
+ .array(ModelIdSchema.meta({ ref: 'policy' }))
151
+ .default([])
152
+ .optional(),
153
+ /** Optional goals this system contributes to. */
154
+ drivesGoals: z
155
+ .array(ModelIdSchema.meta({ ref: 'goal' }))
156
+ .default([])
157
+ .optional(),
158
+ /** @deprecated Use lifecycle. Accepted for one publish cycle. */
159
+ status: SystemStatusSchema.optional(),
160
+ /** @deprecated Use ui.path. Kept for one-cycle Feature compatibility. */
161
+ path: PathSchema.optional(),
162
+ /** @deprecated Use ui.icon. Kept for one-cycle Feature compatibility. */
163
+ icon: IconNameSchema.optional(),
164
+ /** @deprecated Feature color token, retained for one-cycle compatibility. */
165
+ color: ColorTokenSchema.optional(),
166
+ /** @deprecated UI placement hint, retained for one-cycle compatibility. */
167
+ uiPosition: UiPositionSchema.optional(),
168
+ /** @deprecated Use lifecycle. */
169
+ enabled: z.boolean().optional(),
170
+ /** @deprecated Use lifecycle: "beta". */
171
+ devOnly: z.boolean().optional(),
174
172
  requiresAdmin: z.boolean().optional(),
175
173
  /** Domain-map iteration order. Convention: multiples of 10 (10, 20, 30, ...) to allow easy insertion. */
176
174
  order: z.number(),
@@ -187,23 +185,25 @@ export const SystemEntrySchema: ZodType<SystemEntry> = z
187
185
  ontology: OntologyScopeSchema.optional(),
188
186
  /**
189
187
  * Recursive child systems, authored via nesting (per L11).
190
- * The key is the local system id; the full path is computed by joining
191
- * ancestor keys with `.` (e.g. parent key `'sales'` + child key `'crm'` → `'sales.crm'`).
192
- * Per Phase 4: `id` and `parentSystemId` fields will be removed in favour of
193
- * position-derived paths. Both still exist on this schema for backward compat.
194
- */
188
+ * The key is the local system id; the full path is computed by joining
189
+ * ancestor keys with `.` (e.g. parent key `'sales'` + child key `'crm'` → `'sales.crm'`).
190
+ * Per Phase 4: `id` and `parentSystemId` fields will be removed in favour of
191
+ * position-derived paths. Both still exist on this schema for backward compat.
192
+ */
195
193
  systems: z.lazy(() => z.record(z.string().trim().min(1).max(100), SystemEntrySchema)).optional(),
196
194
  /** @deprecated Use systems. Accepted as a compatibility alias during the ontology bridge. */
197
195
  subsystems: z.lazy(() => z.record(z.string().trim().min(1).max(100), SystemEntrySchema)).optional()
198
196
  })
199
197
  .strict()
200
198
  .refine((system: SystemEntry) => system.label !== undefined || system.title !== undefined, {
201
- path: ['label'],
202
- message: 'System must provide label or title'
203
- })
199
+ path: ['label'],
200
+ message: 'System must provide label or title'
201
+ })
204
202
  .transform((system: SystemEntry) => {
205
203
  const normalizedSystem =
206
- system.systems !== undefined && system.subsystems === undefined ? { ...system, subsystems: system.systems } : system
204
+ system.systems !== undefined && system.subsystems === undefined
205
+ ? { ...system, subsystems: system.systems }
206
+ : system
207
207
 
208
208
  if (normalizedSystem.status === undefined) return normalizedSystem
209
209
 
@@ -212,22 +212,34 @@ export const SystemEntrySchema: ZodType<SystemEntry> = z
212
212
  ? { ...normalizedSystem, lifecycle: normalizedSystem.status }
213
213
  : normalizedSystem
214
214
  })
215
-
216
- export const SystemsDomainSchema = z
217
- .record(z.string(), SystemEntrySchema)
218
- .refine((record) => Object.entries(record).every(([key, entry]) => entry.id === key), {
219
- message: 'Each system entry id must match its map key'
220
- })
221
- .default({})
222
-
223
- export const DEFAULT_ORGANIZATION_MODEL_SYSTEMS: z.infer<typeof SystemsDomainSchema> = {}
224
-
225
- export type SystemId = z.infer<typeof SystemIdSchema>
215
+
216
+ export const SystemsDomainSchema = z
217
+ .record(z.string(), SystemEntrySchema)
218
+ .refine((record) => Object.entries(record).every(([key, entry]) => entry.id === key), {
219
+ message: 'Each system entry id must match its map key'
220
+ })
221
+ .default({})
222
+
223
+ export const DEFAULT_ORGANIZATION_MODEL_SYSTEMS: z.infer<typeof SystemsDomainSchema> = {}
224
+
225
+ /** Validate and return a single system entry. */
226
+ export function defineSystem(entry: z.input<typeof SystemEntrySchema>): z.infer<typeof SystemEntrySchema> {
227
+ return SystemEntrySchema.parse(entry)
228
+ }
229
+
230
+ /** Validate and return an id-keyed map of system entries. */
231
+ export function defineSystems(
232
+ entries: readonly z.input<typeof SystemEntrySchema>[]
233
+ ): Record<string, z.infer<typeof SystemEntrySchema>> {
234
+ return defineDomainRecord(SystemEntrySchema, entries)
235
+ }
236
+
237
+ export type SystemId = z.infer<typeof SystemIdSchema>
226
238
  export type SystemKind = z.infer<typeof SystemKindSchema>
227
239
  export type SystemLifecycle = z.infer<typeof SystemLifecycleSchema>
228
240
  /** @deprecated Use SystemLifecycle. Accepted for one publish cycle. */
229
241
  export type SystemStatus = z.infer<typeof SystemStatusSchema>
230
242
  export type SystemLocalConfig = z.infer<typeof SystemConfigSchema>
231
243
  // SystemEntry is declared as an explicit interface above the schema (required for
232
- // recursive z.lazy() type inference). Re-export omitted — the interface IS the type.
233
- export type SystemsDomain = z.infer<typeof SystemsDomainSchema>
244
+ // recursive z.lazy() type inference). Re-export omitted — the interface IS the type.
245
+ export type SystemsDomain = z.infer<typeof SystemsDomainSchema>