@elevasis/core 0.3.0 → 0.5.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.
@@ -168,6 +168,15 @@ var DEFAULT_ORGANIZATION_MODEL_LEAD_GEN = {
168
168
  var PROJECTS_FEATURE_ID = "projects";
169
169
  var PROJECTS_INDEX_SURFACE_ID = "projects.index";
170
170
  var DELIVERY_PROJECTS_VIEW_CAPABILITY_ID = "delivery.projects.view";
171
+ var CRM_FEATURE_ID = "crm";
172
+ var LEAD_GEN_FEATURE_ID = "lead-gen";
173
+ var OPERATIONS_FEATURE_ID = "operations";
174
+ var MONITORING_FEATURE_ID = "monitoring";
175
+ var SETTINGS_FEATURE_ID = "settings";
176
+ var SEO_FEATURE_ID = "seo";
177
+ var CRM_PIPELINE_SURFACE_ID = "crm.pipeline";
178
+ var LEAD_GEN_LISTS_SURFACE_ID = "lead-gen.lists";
179
+ var OPERATIONS_ORGANIZATION_GRAPH_SURFACE_ID = "operations.organization-graph";
171
180
 
172
181
  // src/organization-model/domains/navigation.ts
173
182
  var SurfaceTypeSchema = z.enum(["page", "dashboard", "graph", "detail", "list", "settings"]);
@@ -177,6 +186,7 @@ var SurfaceDefinitionSchema = z.object({
177
186
  path: PathSchema,
178
187
  surfaceType: SurfaceTypeSchema,
179
188
  description: DescriptionSchema.optional(),
189
+ enabled: z.boolean().default(true),
180
190
  icon: IconNameSchema.optional(),
181
191
  featureId: ModelIdSchema.optional(),
182
192
  featureIds: ReferenceIdsSchema,
@@ -204,6 +214,7 @@ var DEFAULT_ORGANIZATION_MODEL_NAVIGATION = {
204
214
  label: "Pipeline",
205
215
  path: "/crm/pipeline",
206
216
  surfaceType: "graph",
217
+ enabled: true,
207
218
  featureId: "crm",
208
219
  featureIds: ["crm"],
209
220
  entityIds: ["crm.deal"],
@@ -215,6 +226,7 @@ var DEFAULT_ORGANIZATION_MODEL_NAVIGATION = {
215
226
  label: "Lists",
216
227
  path: "/lead-gen/lists",
217
228
  surfaceType: "list",
229
+ enabled: true,
218
230
  featureId: "lead-gen",
219
231
  featureIds: ["lead-gen"],
220
232
  entityIds: ["leadgen.list"],
@@ -226,6 +238,7 @@ var DEFAULT_ORGANIZATION_MODEL_NAVIGATION = {
226
238
  label: "Projects",
227
239
  path: "/projects",
228
240
  surfaceType: "list",
241
+ enabled: true,
229
242
  featureId: PROJECTS_FEATURE_ID,
230
243
  featureIds: [PROJECTS_FEATURE_ID],
231
244
  entityIds: ["delivery.project"],
@@ -237,6 +250,7 @@ var DEFAULT_ORGANIZATION_MODEL_NAVIGATION = {
237
250
  label: "Organization Graph",
238
251
  path: "/operations/organization-graph",
239
252
  surfaceType: "graph",
253
+ enabled: true,
240
254
  featureId: "operations",
241
255
  featureIds: ["operations"],
242
256
  entityIds: [],
@@ -248,11 +262,216 @@ var DEFAULT_ORGANIZATION_MODEL_NAVIGATION = {
248
262
  label: "Command View",
249
263
  path: "/operations/command-view",
250
264
  surfaceType: "graph",
265
+ enabled: false,
251
266
  featureId: "operations",
252
267
  featureIds: ["operations"],
253
268
  entityIds: [],
254
269
  resourceIds: [],
255
270
  capabilityIds: ["operations.command-view"]
271
+ },
272
+ {
273
+ id: "operations.overview",
274
+ label: "Overview",
275
+ path: "/operations",
276
+ surfaceType: "dashboard",
277
+ enabled: true,
278
+ featureId: "operations",
279
+ featureIds: ["operations"],
280
+ entityIds: [],
281
+ resourceIds: [],
282
+ capabilityIds: []
283
+ },
284
+ {
285
+ id: "operations.resources",
286
+ label: "Resources",
287
+ path: "/operations/resources",
288
+ surfaceType: "list",
289
+ enabled: true,
290
+ featureId: "operations",
291
+ featureIds: ["operations"],
292
+ entityIds: [],
293
+ resourceIds: [],
294
+ capabilityIds: []
295
+ },
296
+ {
297
+ id: "operations.command-queue",
298
+ label: "Command Queue",
299
+ path: "/operations/command-queue",
300
+ surfaceType: "list",
301
+ enabled: true,
302
+ featureId: "operations",
303
+ featureIds: ["operations"],
304
+ entityIds: [],
305
+ resourceIds: [],
306
+ capabilityIds: []
307
+ },
308
+ {
309
+ id: "operations.sessions",
310
+ label: "Sessions",
311
+ path: "/operations/sessions",
312
+ surfaceType: "page",
313
+ enabled: false,
314
+ featureId: "operations",
315
+ featureIds: ["operations"],
316
+ entityIds: [],
317
+ resourceIds: [],
318
+ capabilityIds: []
319
+ },
320
+ {
321
+ id: "operations.task-scheduler",
322
+ label: "Task Scheduler",
323
+ path: "/operations/task-scheduler",
324
+ surfaceType: "page",
325
+ enabled: true,
326
+ featureId: "operations",
327
+ featureIds: ["operations"],
328
+ entityIds: [],
329
+ resourceIds: [],
330
+ capabilityIds: []
331
+ },
332
+ {
333
+ id: "monitoring.activity-log",
334
+ label: "Activity Log",
335
+ path: "/monitoring/activity-log",
336
+ surfaceType: "list",
337
+ enabled: true,
338
+ featureId: "monitoring",
339
+ featureIds: ["monitoring"],
340
+ entityIds: [],
341
+ resourceIds: [],
342
+ capabilityIds: []
343
+ },
344
+ {
345
+ id: "monitoring.execution-logs",
346
+ label: "Execution Logs",
347
+ path: "/monitoring/execution-logs",
348
+ surfaceType: "list",
349
+ enabled: true,
350
+ featureId: "monitoring",
351
+ featureIds: ["monitoring"],
352
+ entityIds: [],
353
+ resourceIds: [],
354
+ capabilityIds: []
355
+ },
356
+ {
357
+ id: "monitoring.execution-health",
358
+ label: "Execution Health",
359
+ path: "/monitoring/execution-health",
360
+ surfaceType: "dashboard",
361
+ enabled: true,
362
+ featureId: "monitoring",
363
+ featureIds: ["monitoring"],
364
+ entityIds: [],
365
+ resourceIds: [],
366
+ capabilityIds: []
367
+ },
368
+ {
369
+ id: "monitoring.cost-analytics",
370
+ label: "Cost Analytics",
371
+ path: "/monitoring/cost-analytics",
372
+ surfaceType: "dashboard",
373
+ enabled: false,
374
+ featureId: "monitoring",
375
+ featureIds: ["monitoring"],
376
+ entityIds: [],
377
+ resourceIds: [],
378
+ capabilityIds: []
379
+ },
380
+ {
381
+ id: "monitoring.notifications",
382
+ label: "Notifications",
383
+ path: "/monitoring/notifications",
384
+ surfaceType: "page",
385
+ enabled: true,
386
+ featureId: "monitoring",
387
+ featureIds: ["monitoring"],
388
+ entityIds: [],
389
+ resourceIds: [],
390
+ capabilityIds: []
391
+ },
392
+ {
393
+ id: "settings.account",
394
+ label: "Account",
395
+ path: "/settings/account",
396
+ surfaceType: "settings",
397
+ enabled: true,
398
+ featureId: "settings",
399
+ featureIds: ["settings"],
400
+ entityIds: [],
401
+ resourceIds: [],
402
+ capabilityIds: []
403
+ },
404
+ {
405
+ id: "settings.appearance",
406
+ label: "Appearance",
407
+ path: "/settings/appearance",
408
+ surfaceType: "settings",
409
+ enabled: true,
410
+ featureId: "settings",
411
+ featureIds: ["settings"],
412
+ entityIds: [],
413
+ resourceIds: [],
414
+ capabilityIds: []
415
+ },
416
+ {
417
+ id: "settings.organization",
418
+ label: "Organization",
419
+ path: "/settings/organization",
420
+ surfaceType: "settings",
421
+ enabled: true,
422
+ featureId: "settings",
423
+ featureIds: ["settings"],
424
+ entityIds: [],
425
+ resourceIds: [],
426
+ capabilityIds: []
427
+ },
428
+ {
429
+ id: "settings.credentials",
430
+ label: "Credentials",
431
+ path: "/settings/credentials",
432
+ surfaceType: "settings",
433
+ enabled: true,
434
+ featureId: "settings",
435
+ featureIds: ["settings"],
436
+ entityIds: [],
437
+ resourceIds: [],
438
+ capabilityIds: []
439
+ },
440
+ {
441
+ id: "settings.api-keys",
442
+ label: "API Keys",
443
+ path: "/settings/api-keys",
444
+ surfaceType: "settings",
445
+ enabled: true,
446
+ featureId: "settings",
447
+ featureIds: ["settings"],
448
+ entityIds: [],
449
+ resourceIds: [],
450
+ capabilityIds: []
451
+ },
452
+ {
453
+ id: "settings.webhooks",
454
+ label: "Webhooks",
455
+ path: "/settings/webhooks",
456
+ surfaceType: "settings",
457
+ enabled: true,
458
+ featureId: "settings",
459
+ featureIds: ["settings"],
460
+ entityIds: [],
461
+ resourceIds: [],
462
+ capabilityIds: []
463
+ },
464
+ {
465
+ id: "settings.deployments",
466
+ label: "Deployments",
467
+ path: "/settings/deployments",
468
+ surfaceType: "settings",
469
+ enabled: true,
470
+ featureId: "settings",
471
+ featureIds: ["settings"],
472
+ entityIds: [],
473
+ resourceIds: [],
474
+ capabilityIds: []
256
475
  }
257
476
  ],
258
477
  groups: [
@@ -266,7 +485,41 @@ var DEFAULT_ORGANIZATION_MODEL_NAVIGATION = {
266
485
  id: "primary-operations",
267
486
  label: "Operations",
268
487
  placement: "primary",
269
- surfaceIds: ["operations.organization-graph", "operations.command-view"]
488
+ surfaceIds: [
489
+ "operations.organization-graph",
490
+ "operations.command-view",
491
+ "operations.overview",
492
+ "operations.resources",
493
+ "operations.command-queue",
494
+ "operations.sessions",
495
+ "operations.task-scheduler"
496
+ ]
497
+ },
498
+ {
499
+ id: "primary-monitoring",
500
+ label: "Monitoring",
501
+ placement: "primary",
502
+ surfaceIds: [
503
+ "monitoring.activity-log",
504
+ "monitoring.execution-logs",
505
+ "monitoring.execution-health",
506
+ "monitoring.cost-analytics",
507
+ "monitoring.notifications"
508
+ ]
509
+ },
510
+ {
511
+ id: "primary-settings",
512
+ label: "Settings",
513
+ placement: "bottom",
514
+ surfaceIds: [
515
+ "settings.account",
516
+ "settings.appearance",
517
+ "settings.organization",
518
+ "settings.credentials",
519
+ "settings.api-keys",
520
+ "settings.webhooks",
521
+ "settings.deployments"
522
+ ]
270
523
  }
271
524
  ]
272
525
  };
@@ -463,24 +716,24 @@ var DEFAULT_ORGANIZATION_MODEL = {
463
716
  version: 1,
464
717
  features: [
465
718
  {
466
- id: "crm",
719
+ id: CRM_FEATURE_ID,
467
720
  label: "CRM",
468
721
  description: "Relationship pipeline and deal management",
469
722
  enabled: true,
470
723
  color: "blue",
471
724
  entityIds: ["crm.deal"],
472
- surfaceIds: ["crm.pipeline"],
725
+ surfaceIds: [CRM_PIPELINE_SURFACE_ID],
473
726
  resourceIds: [],
474
727
  capabilityIds: ["crm.pipeline.manage"]
475
728
  },
476
729
  {
477
- id: "lead-gen",
730
+ id: LEAD_GEN_FEATURE_ID,
478
731
  label: "Lead Gen",
479
732
  description: "Prospecting, qualification, and outreach preparation",
480
733
  enabled: true,
481
734
  color: "cyan",
482
735
  entityIds: ["leadgen.list", "leadgen.company", "leadgen.contact"],
483
- surfaceIds: ["lead-gen.lists"],
736
+ surfaceIds: [LEAD_GEN_LISTS_SURFACE_ID],
484
737
  resourceIds: [],
485
738
  capabilityIds: ["leadgen.lists.manage"]
486
739
  },
@@ -496,36 +749,58 @@ var DEFAULT_ORGANIZATION_MODEL = {
496
749
  capabilityIds: [DELIVERY_PROJECTS_VIEW_CAPABILITY_ID]
497
750
  },
498
751
  {
499
- id: "operations",
752
+ id: OPERATIONS_FEATURE_ID,
500
753
  label: "Operations",
501
754
  description: "Operational resources, topology, and orchestration visibility",
502
755
  enabled: true,
503
756
  color: "violet",
504
757
  entityIds: [],
505
- surfaceIds: ["operations.organization-graph", "operations.command-view"],
758
+ surfaceIds: [
759
+ OPERATIONS_ORGANIZATION_GRAPH_SURFACE_ID,
760
+ "operations.command-view",
761
+ "operations.overview",
762
+ "operations.resources",
763
+ "operations.command-queue",
764
+ "operations.sessions",
765
+ "operations.task-scheduler"
766
+ ],
506
767
  resourceIds: [],
507
768
  capabilityIds: ["operations.organization-graph", "operations.command-view"]
508
769
  },
509
770
  {
510
- id: "monitoring",
771
+ id: MONITORING_FEATURE_ID,
511
772
  label: "Monitoring",
512
773
  enabled: true,
513
774
  entityIds: [],
514
- surfaceIds: [],
775
+ surfaceIds: [
776
+ "monitoring.activity-log",
777
+ "monitoring.execution-logs",
778
+ "monitoring.execution-health",
779
+ "monitoring.cost-analytics",
780
+ "monitoring.notifications"
781
+ ],
515
782
  resourceIds: [],
516
783
  capabilityIds: []
517
784
  },
518
785
  {
519
- id: "settings",
786
+ id: SETTINGS_FEATURE_ID,
520
787
  label: "Settings",
521
788
  enabled: true,
522
789
  entityIds: [],
523
- surfaceIds: [],
790
+ surfaceIds: [
791
+ "settings.account",
792
+ "settings.appearance",
793
+ "settings.organization",
794
+ "settings.credentials",
795
+ "settings.api-keys",
796
+ "settings.webhooks",
797
+ "settings.deployments"
798
+ ],
524
799
  resourceIds: [],
525
800
  capabilityIds: []
526
801
  },
527
802
  {
528
- id: "seo",
803
+ id: SEO_FEATURE_ID,
529
804
  label: "SEO",
530
805
  enabled: false,
531
806
  entityIds: [],
@@ -590,8 +865,8 @@ function resolveOrganizationModel(override) {
590
865
  }
591
866
 
592
867
  // src/organization-model/foundation.ts
593
- function createFoundationOrganizationModel(branding) {
594
- const canonical = resolveOrganizationModel({ branding });
868
+ function createFoundationOrganizationModel(override) {
869
+ const canonical = resolveOrganizationModel(override);
595
870
  function requireCoreSurface(surfaceId) {
596
871
  const surface = canonical.navigation.surfaces.find((candidate) => candidate.id === surfaceId);
597
872
  if (!surface) {
@@ -614,6 +889,7 @@ function createFoundationOrganizationModel(branding) {
614
889
  label: "Settings",
615
890
  path: "/settings/account",
616
891
  surfaceType: "settings",
892
+ enabled: true,
617
893
  icon: "settings",
618
894
  featureId: "settings",
619
895
  featureIds: ["settings"],
@@ -645,4 +921,4 @@ function createFoundationOrganizationModel(branding) {
645
921
  };
646
922
  }
647
923
 
648
- export { DEFAULT_ORGANIZATION_MODEL, DELIVERY_PROJECTS_VIEW_CAPABILITY_ID, FeatureSchema, OrganizationModelSchema, PROJECTS_FEATURE_ID, PROJECTS_INDEX_SURFACE_ID, createFoundationOrganizationModel, defineOrganizationModel, resolveOrganizationModel };
924
+ export { CRM_FEATURE_ID, CRM_PIPELINE_SURFACE_ID, DEFAULT_ORGANIZATION_MODEL, DELIVERY_PROJECTS_VIEW_CAPABILITY_ID, FeatureSchema, LEAD_GEN_FEATURE_ID, LEAD_GEN_LISTS_SURFACE_ID, MONITORING_FEATURE_ID, OPERATIONS_FEATURE_ID, OPERATIONS_ORGANIZATION_GRAPH_SURFACE_ID, OrganizationModelSchema, PROJECTS_FEATURE_ID, PROJECTS_INDEX_SURFACE_ID, SEO_FEATURE_ID, SETTINGS_FEATURE_ID, createFoundationOrganizationModel, defineOrganizationModel, resolveOrganizationModel };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elevasis/core",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "license": "MIT",
5
5
  "description": "Minimal shared constants across Elevasis monorepo",
6
6
  "sideEffects": false,
@@ -19,6 +19,10 @@
19
19
  "./organization-model": {
20
20
  "types": "./dist/organization-model/index.d.ts",
21
21
  "import": "./dist/organization-model/index.js"
22
+ },
23
+ "./entities": {
24
+ "types": "./dist/business/entities-published.d.ts",
25
+ "import": "./dist/business/entities-published.js"
22
26
  }
23
27
  },
24
28
  "devDependencies": {
@@ -13,6 +13,6 @@ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) as {
13
13
  describe('core publish surface', () => {
14
14
  it('publishes the curated @elevasis/core organization-model wrapper', () => {
15
15
  expect(packageJson.publishConfig?.name).toBe('@elevasis/core')
16
- expect(Object.keys(packageJson.publishConfig?.exports ?? {})).toEqual(['.', './organization-model'])
16
+ expect(Object.keys(packageJson.publishConfig?.exports ?? {})).toEqual(['.', './organization-model', './entities'])
17
17
  })
18
18
  })
@@ -0,0 +1,52 @@
1
+ # Entities
2
+
3
+ Published base entity contracts for the Elevasis platform. Each entity ships as a TypeScript interface, a matching Zod schema, and an inferred `Input` type, generic over a `<TMeta>` extension slot.
4
+
5
+ External projects extend these in `foundations/types/entities.ts` to attach project-specific metadata while keeping the canonical shape stable.
6
+
7
+ ## Published Exports
8
+
9
+ The published entry point exposes six entity contracts:
10
+
11
+ - `BaseProject<TMeta>`, `BaseProjectSchema`, `BaseProjectInput`
12
+ - `BaseMilestone<TMeta>`, `BaseMilestoneSchema`, `BaseMilestoneInput`
13
+ - `BaseTask<TMeta>`, `BaseTaskSchema`, `BaseTaskInput`
14
+ - `BaseDeal<TMeta>`, `BaseDealSchema`, `BaseDealInput`
15
+ - `BaseCompany<TMeta>`, `BaseCompanySchema`, `BaseCompanyInput`
16
+ - `BaseContact<TMeta>`, `BaseContactSchema`, `BaseContactInput`
17
+
18
+ Import them from the published subpath:
19
+
20
+ ```ts
21
+ import { BaseDealSchema, type BaseDeal } from '@elevasis/core/entities'
22
+ ```
23
+
24
+ ## Extension Pattern
25
+
26
+ Each base interface accepts a generic metadata type. Extend the schema with `.extend({ metadata: ... })` and infer the type with `BaseProject<z.infer<typeof MetaSchema>>`.
27
+
28
+ ```ts
29
+ import { z } from 'zod'
30
+ import { BaseProjectSchema, type BaseProject } from '@elevasis/core/entities'
31
+
32
+ const ProjectMetaSchema = z.object({
33
+ budget: z.number().int().nonnegative(),
34
+ clientPriority: z.enum(['low', 'medium', 'high'])
35
+ })
36
+
37
+ export const ProjectSchema = BaseProjectSchema.extend({ metadata: ProjectMetaSchema })
38
+ export type Project = BaseProject<z.infer<typeof ProjectMetaSchema>>
39
+ ```
40
+
41
+ Use the base shape as-is when no extension is needed:
42
+
43
+ ```ts
44
+ export const DealSchema = BaseDealSchema
45
+ export type Deal = BaseDeal
46
+ ```
47
+
48
+ ## Recipe
49
+
50
+ The full pattern is documented in the SDK scaffold bundle: `node_modules/@elevasis/sdk/reference/scaffold/recipes/extend-a-base-entity.md`.
51
+
52
+ The canonical template demo lives at `external/_template/foundations/types/entities.ts`.
@@ -0,0 +1,33 @@
1
+ import { describe, it, expect } from 'vitest'
2
+
3
+ describe('entities-published barrel', () => {
4
+ it('exports all base entity Zod schemas', async () => {
5
+ const barrel = await import('../entities-published')
6
+
7
+ expect(barrel.BaseProjectSchema).toBeDefined()
8
+ expect(barrel.BaseMilestoneSchema).toBeDefined()
9
+ expect(barrel.BaseTaskSchema).toBeDefined()
10
+ expect(barrel.BaseDealSchema).toBeDefined()
11
+ expect(barrel.BaseCompanySchema).toBeDefined()
12
+ expect(barrel.BaseContactSchema).toBeDefined()
13
+ })
14
+
15
+ it('round-trips a valid project through the barrel-exported schema', async () => {
16
+ const { BaseProjectSchema } = await import('../entities-published')
17
+
18
+ const validProject = {
19
+ id: '00000000-0000-0000-0000-000000000010',
20
+ organizationId: '00000000-0000-0000-0000-000000000001',
21
+ name: 'Website Rebuild',
22
+ kind: 'client_engagement',
23
+ status: 'active',
24
+ description: 'Rebuild the client website.',
25
+ metadata: { budget: 50000 },
26
+ createdAt: '2026-04-17T00:00:00.000Z',
27
+ updatedAt: '2026-04-17T00:00:00.000Z'
28
+ }
29
+
30
+ const result = BaseProjectSchema.safeParse(validProject)
31
+ expect(result.success).toBe(true)
32
+ })
33
+ })
@@ -0,0 +1,24 @@
1
+ // Public re-export barrel for @elevasis/core/entities (and @repo/core/entities internally)
2
+ export type {
3
+ BaseProject,
4
+ BaseMilestone,
5
+ BaseTask,
6
+ BaseDeal,
7
+ BaseCompany,
8
+ BaseContact,
9
+ BaseProjectInput,
10
+ BaseMilestoneInput,
11
+ BaseTaskInput,
12
+ BaseDealInput,
13
+ BaseCompanyInput,
14
+ BaseContactInput
15
+ } from './base-entities'
16
+
17
+ export {
18
+ BaseProjectSchema,
19
+ BaseMilestoneSchema,
20
+ BaseTaskSchema,
21
+ BaseDealSchema,
22
+ BaseCompanySchema,
23
+ BaseContactSchema
24
+ } from './base-entities'
@@ -34,9 +34,8 @@ import type {
34
34
  * Run: pnpm test attio-crud.integration.test.ts
35
35
  */
36
36
 
37
- const SKIP_TESTS = !process.env.SUPABASE_URL || !process.env.SUPABASE_SERVICE_KEY || !process.env.SECRETS_ENCRYPTION_KEY
38
-
39
- describe.skipIf(SKIP_TESTS)('Attio CRUD Integration Tests', () => {
37
+ // Attio integration currently unused -- tests skipped unconditionally.
38
+ describe.skip('Attio CRUD Integration Tests', () => {
40
39
  const adapter = new AttioAdapter()
41
40
  const organizationId = 'f9aa5a56-8c13-4cd1-9161-8827ae7b452b'
42
41
  const credentialName = 'elevasis-attio'
@@ -0,0 +1,105 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { createFoundationOrganizationModel } from '../foundation'
3
+
4
+ describe('createFoundationOrganizationModel', () => {
5
+ it('builds the foundation model from a branding-only override', () => {
6
+ const result = createFoundationOrganizationModel({
7
+ branding: { organizationName: 'Acme', productName: 'Acme OS', shortName: 'Acme' }
8
+ })
9
+
10
+ expect(result.canonical.branding.organizationName).toBe('Acme')
11
+ expect(result.homeLabel).toBe('Dashboard')
12
+ expect(result.model.navigation.defaultSurfaceId).toBe('operations')
13
+
14
+ const surfaces = result.model.navigation.surfaces
15
+ expect(surfaces.find((s) => s.id === 'crm')).toBeDefined()
16
+ expect(surfaces.find((s) => s.id === 'lead-gen')).toBeDefined()
17
+ expect(surfaces.find((s) => s.id === 'projects')).toBeDefined()
18
+ expect(surfaces.find((s) => s.id === 'operations')).toBeDefined()
19
+ expect(surfaces.find((s) => s.id === 'settings')).toBeDefined()
20
+
21
+ expect(result.quickAccessSurfaceIds).toContain('operations')
22
+ expect(result.quickAccessSurfaceIds).toContain('projects')
23
+ expect(result.quickAccessSurfaceIds).toContain('lead-gen')
24
+ expect(result.quickAccessSurfaceIds).toContain('crm')
25
+ })
26
+
27
+ it('passes deeper overrides through to the canonical model', () => {
28
+ const result = createFoundationOrganizationModel({
29
+ branding: { organizationName: 'Acme', productName: 'Acme OS', shortName: 'Acme' },
30
+ crm: {
31
+ pipelines: [
32
+ {
33
+ id: 'custom-pipeline',
34
+ label: 'Custom Pipeline',
35
+ description: 'A custom pipeline',
36
+ entityId: 'crm.deal',
37
+ stages: [
38
+ {
39
+ id: 'stage-1',
40
+ label: 'Stage 1',
41
+ color: 'blue',
42
+ order: 0,
43
+ semanticClass: 'open' as const,
44
+ surfaceIds: ['crm.pipeline'],
45
+ resourceIds: []
46
+ }
47
+ ]
48
+ }
49
+ ]
50
+ }
51
+ })
52
+
53
+ expect(result.canonical.crm.pipelines).toHaveLength(1)
54
+ expect(result.canonical.crm.pipelines[0]?.id).toBe('custom-pipeline')
55
+ })
56
+
57
+ it('exposes a working getOrganizationSurface lookup', () => {
58
+ const result = createFoundationOrganizationModel({
59
+ branding: { organizationName: 'Acme', productName: 'Acme OS', shortName: 'Acme' }
60
+ })
61
+
62
+ const crmSurface = result.getOrganizationSurface('crm')
63
+ expect(crmSurface).toBeDefined()
64
+ expect(crmSurface?.id).toBe('crm')
65
+ expect(crmSurface?.icon).toBe('crm')
66
+
67
+ expect(result.getOrganizationSurface('nonexistent')).toBeUndefined()
68
+ })
69
+
70
+ it('throws when a required core surface is missing', () => {
71
+ const override = {
72
+ features: [
73
+ {
74
+ id: 'crm',
75
+ label: 'CRM',
76
+ description: 'CRM workspace',
77
+ enabled: true,
78
+ color: 'blue',
79
+ entityIds: [],
80
+ surfaceIds: ['custom.home'],
81
+ resourceIds: [],
82
+ capabilityIds: []
83
+ }
84
+ ],
85
+ navigation: {
86
+ defaultSurfaceId: 'custom.home',
87
+ surfaces: [
88
+ {
89
+ id: 'custom.home',
90
+ label: 'Home',
91
+ path: '/home',
92
+ surfaceType: 'page' as const,
93
+ featureIds: ['crm'],
94
+ entityIds: [],
95
+ resourceIds: [],
96
+ capabilityIds: []
97
+ }
98
+ ],
99
+ groups: [{ id: 'primary', label: 'Primary', placement: 'primary', surfaceIds: ['custom.home'] }]
100
+ }
101
+ }
102
+
103
+ expect(() => createFoundationOrganizationModel(override)).toThrow(/Missing organization surface/)
104
+ })
105
+ })