@almadar/core 2.14.3 → 4.0.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.
package/dist/builders.js CHANGED
@@ -1,66 +1,181 @@
1
1
  import { z } from 'zod';
2
2
 
3
- // src/builders/layout-strategy.ts
4
- function hasSequentialChain(wiring) {
5
- if (wiring.length < 2) {
6
- return false;
7
- }
8
- const adjacency = /* @__PURE__ */ new Map();
9
- const allTargets = /* @__PURE__ */ new Set();
10
- for (const entry of wiring) {
11
- let targets = adjacency.get(entry.from);
12
- if (!targets) {
13
- targets = /* @__PURE__ */ new Set();
14
- adjacency.set(entry.from, targets);
15
- }
16
- targets.add(entry.to);
17
- allTargets.add(entry.to);
18
- }
19
- const roots = [];
20
- for (const from of adjacency.keys()) {
21
- if (!allTargets.has(from)) {
22
- roots.push(from);
23
- }
24
- }
25
- const starts = roots.length > 0 ? roots : [...adjacency.keys()];
26
- for (const start of starts) {
27
- let current = start;
28
- let length = 1;
29
- const visited = /* @__PURE__ */ new Set([current]);
30
- while (true) {
31
- const nexts = adjacency.get(current);
32
- if (!nexts || nexts.size === 0) break;
33
- let advanced = false;
34
- for (const next of nexts) {
35
- if (!visited.has(next)) {
36
- visited.add(next);
37
- current = next;
38
- length++;
39
- advanced = true;
40
- break;
41
- }
42
- }
43
- if (!advanced) break;
44
- }
45
- if (length >= 3) {
46
- return true;
47
- }
48
- }
49
- return false;
50
- }
51
- function detectLayoutStrategy(orbitals, eventWiring) {
52
- if (eventWiring && eventWiring.length > 0 && hasSequentialChain(eventWiring)) {
53
- return "wizard-flow";
54
- }
55
- const count = orbitals.length;
56
- if (count <= 1) {
57
- return "single";
58
- }
59
- if (count <= 4) {
60
- return "tabs";
61
- }
62
- return "sidebar";
63
- }
3
+ // src/types/orbital.ts
4
+ var FieldTypeSchema = z.enum([
5
+ "string",
6
+ "number",
7
+ "boolean",
8
+ "date",
9
+ "timestamp",
10
+ "datetime",
11
+ "array",
12
+ "object",
13
+ "enum",
14
+ "relation"
15
+ ]);
16
+ var RelationCardinalitySchema = z.enum([
17
+ "one",
18
+ "many",
19
+ "one-to-many",
20
+ "many-to-one",
21
+ "many-to-many"
22
+ ]);
23
+ var RelationConfigSchema = z.object({
24
+ entity: z.string().min(1, "Target entity is required"),
25
+ field: z.string().optional(),
26
+ cardinality: RelationCardinalitySchema.optional(),
27
+ onDelete: z.enum(["cascade", "nullify", "restrict"]).optional(),
28
+ // Legacy compatibility fields
29
+ foreignKey: z.string().optional(),
30
+ target: z.string().optional(),
31
+ type: RelationCardinalitySchema.optional()
32
+ }).transform((data) => {
33
+ const normalized = {
34
+ entity: data.entity || data.target || "",
35
+ cardinality: data.cardinality || data.type,
36
+ field: data.field,
37
+ onDelete: data.onDelete
38
+ };
39
+ return normalized;
40
+ });
41
+ var FieldFormatSchema = z.enum([
42
+ "email",
43
+ "url",
44
+ "phone",
45
+ "date",
46
+ "datetime",
47
+ "uuid"
48
+ ]);
49
+ var EntityFieldSchema = z.lazy(
50
+ () => z.object({
51
+ name: z.string().min(1, "Field name is required"),
52
+ type: FieldTypeSchema,
53
+ required: z.boolean().optional(),
54
+ default: z.unknown().optional(),
55
+ values: z.array(z.string()).optional(),
56
+ enum: z.array(z.string()).optional(),
57
+ format: FieldFormatSchema.optional(),
58
+ min: z.number().optional(),
59
+ max: z.number().optional(),
60
+ items: EntityFieldSchema.optional(),
61
+ relation: RelationConfigSchema.optional()
62
+ }).refine(
63
+ (field) => field.type !== "relation" || field.relation !== void 0,
64
+ { message: 'Relation config is required when type is "relation"', path: ["relation"] }
65
+ )
66
+ );
67
+ var ENTITY_ROLES = [
68
+ "player",
69
+ "enemy",
70
+ "npc",
71
+ "item",
72
+ "tile",
73
+ "projectile",
74
+ "effect",
75
+ "ui",
76
+ "decoration",
77
+ "vehicle"
78
+ ];
79
+ var EntityRoleSchema = z.enum(ENTITY_ROLES);
80
+ var VISUAL_STYLES = ["pixel", "vector", "hd", "1-bit", "isometric"];
81
+ var VisualStyleSchema = z.enum(VISUAL_STYLES);
82
+ var GAME_TYPES = [
83
+ "platformer",
84
+ "roguelike",
85
+ "top-down",
86
+ "puzzle",
87
+ "racing",
88
+ "card",
89
+ "board",
90
+ "shooter",
91
+ "rpg"
92
+ ];
93
+ var GameTypeSchema = z.enum(GAME_TYPES);
94
+ var AnimationDefSchema = z.object({
95
+ frames: z.union([z.array(z.number()), z.array(z.string())]),
96
+ fps: z.number().positive(),
97
+ loop: z.boolean()
98
+ });
99
+ var SemanticAssetRefSchema = z.object({
100
+ role: EntityRoleSchema,
101
+ category: z.string().min(1),
102
+ animations: z.array(z.string()).optional(),
103
+ style: VisualStyleSchema.optional(),
104
+ variant: z.string().optional()
105
+ });
106
+ z.object({
107
+ basePath: z.string(),
108
+ path: z.string(),
109
+ tiles: z.array(z.number()).optional(),
110
+ tileSize: z.number().positive().optional(),
111
+ files: z.array(z.string()).optional(),
112
+ animations: z.record(AnimationDefSchema).optional()
113
+ });
114
+ var AssetMappingSchema = z.object({
115
+ path: z.string(),
116
+ tiles: z.array(z.number()).optional(),
117
+ tileSize: z.number().positive().optional(),
118
+ files: z.array(z.string()).optional(),
119
+ animations: z.record(AnimationDefSchema).optional()
120
+ });
121
+ z.object({
122
+ gameType: GameTypeSchema,
123
+ style: VisualStyleSchema,
124
+ basePath: z.string(),
125
+ mappings: z.record(AssetMappingSchema)
126
+ });
127
+
128
+ // src/types/entity.ts
129
+ var EntityPersistenceSchema = z.enum([
130
+ "persistent",
131
+ "runtime",
132
+ "singleton",
133
+ "instance",
134
+ "local"
135
+ ]);
136
+ var OrbitalEntitySchema = z.object({
137
+ name: z.string().min(1, "Entity name is required"),
138
+ persistence: EntityPersistenceSchema.default("persistent"),
139
+ collection: z.string().optional(),
140
+ fields: z.array(EntityFieldSchema).min(1, "At least one field is required"),
141
+ instances: z.array(z.record(z.unknown())).optional(),
142
+ timestamps: z.boolean().optional(),
143
+ softDelete: z.boolean().optional(),
144
+ description: z.string().optional(),
145
+ visual_prompt: z.string().optional(),
146
+ assetRef: SemanticAssetRefSchema.optional()
147
+ });
148
+ var EntitySchema = OrbitalEntitySchema;
149
+ var ViewTypeSchema = z.enum([
150
+ "list",
151
+ "detail",
152
+ "create",
153
+ "edit",
154
+ "dashboard",
155
+ "custom"
156
+ ]);
157
+ var PageTraitRefSchema = z.object({
158
+ ref: z.string().min(1, "Trait ref is required"),
159
+ linkedEntity: z.string().optional(),
160
+ config: z.record(z.unknown()).optional()
161
+ });
162
+ z.object({
163
+ name: z.string().min(1, "Page name is required"),
164
+ path: z.string().min(1, "Page path is required").startsWith("/", "Path must start with /"),
165
+ primaryEntity: z.string().min(1, "Primary entity is required"),
166
+ traits: z.array(PageTraitRefSchema).min(1, "Page must have at least one trait"),
167
+ title: z.string().optional()
168
+ }).strict();
169
+ var OrbitalPageSchema = z.object({
170
+ name: z.string().min(1, "Page name is required"),
171
+ path: z.string().min(1, "Page path is required").startsWith("/", "Path must start with /"),
172
+ viewType: ViewTypeSchema.optional(),
173
+ title: z.string().optional(),
174
+ primaryEntity: z.string().optional(),
175
+ traits: z.array(PageTraitRefSchema).optional(),
176
+ isInitial: z.boolean().optional()
177
+ }).strict();
178
+ var PageSchema = OrbitalPageSchema;
64
179
  var UI_SLOTS = [
65
180
  // App slots
66
181
  "main",
@@ -244,9 +359,41 @@ var RequiredFieldSchema = z.object({
244
359
  z.object({
245
360
  ref: z.string().min(1),
246
361
  linkedEntity: z.string().optional(),
362
+ name: z.string().optional(),
363
+ events: z.record(
364
+ z.string().min(1, "events key (atom event name) must be non-empty"),
365
+ z.string().min(1, "events value (caller event name) must be non-empty")
366
+ ).optional(),
247
367
  config: z.record(z.record(z.unknown())).optional(),
248
- appliesTo: z.array(z.string()).optional()
249
- });
368
+ appliesTo: z.array(z.string()).optional(),
369
+ // Phase F.7: zod accepts an array (the inliner validates element
370
+ // shape). The full ListenDefinition shape isn't recursively encoded
371
+ // here because TraitReference is the call-site form — listen entries
372
+ // pasted in are already-resolved structured definitions, not nested
373
+ // overrides.
374
+ listens: z.array(z.unknown()).optional(),
375
+ emitsScope: z.enum(["internal", "external"]).optional(),
376
+ // Phase F.8: per-transition effects override. The keys are event
377
+ // names (the transition triggers AFTER renames). Values are arrays
378
+ // of SExpression-shaped data; the inliner validates the SExpression
379
+ // shape during application, so the schema accepts loose `unknown[]`.
380
+ effects: z.record(
381
+ z.string().min(1, "effects override key (event name) must be non-empty"),
382
+ z.array(z.unknown())
383
+ ).optional()
384
+ }).refine(
385
+ (ref) => {
386
+ if (!ref.events) return true;
387
+ for (const [from, to] of Object.entries(ref.events)) {
388
+ if (from.length === 0 || to.length === 0) return false;
389
+ }
390
+ return true;
391
+ },
392
+ {
393
+ message: 'TraitReference "events" entries must have non-empty atom and caller event names',
394
+ path: ["events"]
395
+ }
396
+ );
250
397
  var TraitSchema = z.object({
251
398
  name: z.string().min(1),
252
399
  description: z.string().optional(),
@@ -262,12 +409,20 @@ var TraitSchema = z.object({
262
409
  listens: z.array(TraitEventListenerSchema).optional(),
263
410
  ui: z.record(z.unknown()).optional()
264
411
  });
265
- z.union([
412
+ var TraitRefSchema = z.union([
266
413
  z.string().min(1),
267
414
  z.object({
268
415
  ref: z.string().min(1),
269
416
  config: z.record(z.unknown()).optional(),
270
- linkedEntity: z.string().optional()
417
+ linkedEntity: z.string().optional(),
418
+ name: z.string().optional(),
419
+ // Phase F.4: same non-empty refine as TraitReferenceSchema.events.
420
+ // Both schemas accept the same call-site argument shape, so the
421
+ // validators should agree.
422
+ events: z.record(
423
+ z.string().min(1, "events key (atom event name) must be non-empty"),
424
+ z.string().min(1, "events value (caller event name) must be non-empty")
425
+ ).optional()
271
426
  }),
272
427
  TraitSchema
273
428
  // Allow inline trait definitions
@@ -275,6 +430,426 @@ z.union([
275
430
  function isInlineTrait(traitRef) {
276
431
  return typeof traitRef === "object" && "name" in traitRef && !("ref" in traitRef);
277
432
  }
433
+ z.enum([
434
+ "healthcare",
435
+ "education",
436
+ "finance",
437
+ "ecommerce",
438
+ "real-estate",
439
+ "logistics",
440
+ "hospitality",
441
+ "hr-management",
442
+ "project-management",
443
+ "social",
444
+ "content-management",
445
+ "iot",
446
+ "analytics",
447
+ "game",
448
+ "custom"
449
+ ]);
450
+ var AGENT_DOMAIN_CATEGORIES = [
451
+ "game",
452
+ "business",
453
+ "dashboard",
454
+ "form",
455
+ "content",
456
+ "social",
457
+ "ecommerce",
458
+ "workflow"
459
+ ];
460
+ var AgentDomainCategorySchema = z.enum([...AGENT_DOMAIN_CATEGORIES]);
461
+ z.enum([
462
+ "platformer",
463
+ "shooter",
464
+ "puzzle",
465
+ "rpg",
466
+ "board",
467
+ "racing",
468
+ "fighting",
469
+ "tower-defense",
470
+ "endless-runner",
471
+ "simulation",
472
+ "arcade",
473
+ "adventure"
474
+ ]);
475
+ z.enum(["domain", "system"]);
476
+ z.enum([
477
+ "pending",
478
+ "active",
479
+ "completed",
480
+ "cancelled",
481
+ "error",
482
+ "suspended",
483
+ "blocked",
484
+ "domain_workflow",
485
+ "domain_status",
486
+ "system_loading",
487
+ "system_error",
488
+ "system_idle",
489
+ "system_editing",
490
+ "system_confirming"
491
+ ]);
492
+ z.enum([
493
+ "domain_action",
494
+ "domain_trigger",
495
+ "system_crud",
496
+ "system_navigation",
497
+ "system_form",
498
+ "system_ui"
499
+ ]);
500
+ z.enum([
501
+ "domain_core",
502
+ "domain_supporting",
503
+ "domain_reference",
504
+ "system_user",
505
+ "system_config",
506
+ "system_audit"
507
+ ]);
508
+ var DomainVocabularySchema = z.record(z.string(), z.string()).optional();
509
+ var UserPersonaSchema = z.object({
510
+ name: z.string().min(1),
511
+ role: z.string().optional(),
512
+ device: z.enum(["mobile", "tablet", "desktop"]).optional()
513
+ });
514
+ var DomainContextSchema = z.object({
515
+ request: z.string().min(1, "Original request is required"),
516
+ requestFragment: z.string().optional(),
517
+ category: AgentDomainCategorySchema,
518
+ subDomain: z.string().optional(),
519
+ personas: z.array(UserPersonaSchema).optional(),
520
+ vocabulary: DomainVocabularySchema.optional()
521
+ });
522
+ var UXHintsSchema = z.object({
523
+ flowPattern: z.string().optional(),
524
+ listPattern: z.string().optional(),
525
+ formPattern: z.string().optional(),
526
+ detailPattern: z.string().optional(),
527
+ relatedLinks: z.array(z.lazy(() => RelatedLinkSchema)).optional()
528
+ });
529
+ var RelatedLinkSchema = z.object({
530
+ relation: z.string().min(1),
531
+ label: z.string().min(1),
532
+ targetView: z.enum(["list", "detail"]).optional()
533
+ });
534
+ var SuggestedGuardSchema = z.object({
535
+ id: z.string().min(1),
536
+ description: z.string().min(1),
537
+ appliesTo: z.array(z.string().min(1))
538
+ });
539
+ var DesignPreferencesSchema = z.object({
540
+ style: z.enum(["minimal", "modern", "playful", "data-driven", "immersive"]).optional(),
541
+ primaryColor: z.string().regex(/^#[0-9A-Fa-f]{6}$/, "Must be valid hex color").optional(),
542
+ device: z.enum(["mobile", "tablet", "desktop", "all"]).optional(),
543
+ darkMode: z.boolean().optional(),
544
+ uxHints: UXHintsSchema.optional()
545
+ });
546
+ var ThemeTokensSchema = z.object({
547
+ colors: z.record(z.string(), z.string()).optional(),
548
+ radii: z.record(z.string(), z.string()).optional(),
549
+ spacing: z.record(z.string(), z.string()).optional(),
550
+ typography: z.record(z.string(), z.string()).optional(),
551
+ shadows: z.record(z.string(), z.string()).optional()
552
+ });
553
+ var ThemeVariantSchema = z.object({
554
+ colors: z.record(z.string(), z.string()).optional(),
555
+ radii: z.record(z.string(), z.string()).optional(),
556
+ spacing: z.record(z.string(), z.string()).optional(),
557
+ typography: z.record(z.string(), z.string()).optional(),
558
+ shadows: z.record(z.string(), z.string()).optional()
559
+ });
560
+ var ThemeDefinitionSchema = z.object({
561
+ name: z.string().min(1, "Theme name is required"),
562
+ tokens: ThemeTokensSchema,
563
+ variants: z.record(z.string(), ThemeVariantSchema).optional()
564
+ });
565
+ var ThemeRefStringSchema = z.string().regex(
566
+ /^[A-Z][a-zA-Z0-9]*\.theme$/,
567
+ 'Theme reference must be in format "Alias.theme" (e.g., "Ocean.theme")'
568
+ );
569
+ var ThemeRefSchema = z.union([
570
+ ThemeDefinitionSchema,
571
+ ThemeRefStringSchema
572
+ ]);
573
+ z.record(z.string(), z.record(z.string(), z.string())).optional();
574
+ var ALLOWED_CUSTOM_COMPONENTS = [
575
+ "div",
576
+ "span",
577
+ "button",
578
+ "a",
579
+ "p",
580
+ "h1",
581
+ "h2",
582
+ "h3",
583
+ "h4",
584
+ "h5",
585
+ "h6",
586
+ "header",
587
+ "footer",
588
+ "section",
589
+ "article",
590
+ "nav",
591
+ "main",
592
+ "aside",
593
+ "ul",
594
+ "ol",
595
+ "li",
596
+ "img",
597
+ "label",
598
+ "input",
599
+ "form"
600
+ ];
601
+ var CustomPatternDefinitionSchema = z.object({
602
+ type: z.literal("custom"),
603
+ component: z.enum(ALLOWED_CUSTOM_COMPONENTS),
604
+ className: z.string(),
605
+ slots: z.array(z.string()).optional(),
606
+ props: z.array(z.string()).optional()
607
+ });
608
+ z.record(z.string(), CustomPatternDefinitionSchema).optional();
609
+ var SERVICE_TYPES = ["rest", "socket", "mcp"];
610
+ z.enum(SERVICE_TYPES);
611
+ var RestAuthConfigSchema = z.object({
612
+ type: z.enum(["api-key", "bearer", "basic", "oauth2"]),
613
+ keyName: z.string().optional(),
614
+ location: z.enum(["query", "header"]).optional(),
615
+ secretEnv: z.string().optional()
616
+ });
617
+ var RestServiceDefSchema = z.object({
618
+ name: z.string().min(1, "Service name is required"),
619
+ type: z.literal("rest"),
620
+ description: z.string().optional(),
621
+ baseUrl: z.string().url("Base URL must be a valid URL"),
622
+ headers: z.record(z.string()).optional(),
623
+ auth: RestAuthConfigSchema.optional(),
624
+ timeout: z.number().positive().optional()
625
+ });
626
+ var SocketEventsSchema = z.object({
627
+ inbound: z.array(z.string()),
628
+ outbound: z.array(z.string())
629
+ });
630
+ var SocketServiceDefSchema = z.object({
631
+ name: z.string().min(1, "Service name is required"),
632
+ type: z.literal("socket"),
633
+ description: z.string().optional(),
634
+ url: z.string().url("WebSocket URL must be valid"),
635
+ events: SocketEventsSchema,
636
+ reconnect: z.object({
637
+ enabled: z.boolean(),
638
+ maxAttempts: z.number().positive().optional(),
639
+ delayMs: z.number().positive().optional()
640
+ }).optional()
641
+ });
642
+ var McpServiceDefSchema = z.object({
643
+ name: z.string().min(1, "Service name is required"),
644
+ type: z.literal("mcp"),
645
+ description: z.string().optional(),
646
+ serverPath: z.string().min(1, "Server path is required"),
647
+ capabilities: z.array(z.string()).min(1, "At least one capability is required"),
648
+ env: z.record(z.string()).optional()
649
+ });
650
+ var ServiceDefinitionSchema = z.discriminatedUnion("type", [
651
+ RestServiceDefSchema,
652
+ SocketServiceDefSchema,
653
+ McpServiceDefSchema
654
+ ]);
655
+ var ServiceRefStringSchema = z.string().regex(
656
+ /^[A-Z][a-zA-Z0-9]*\.services\.[a-zA-Z][a-zA-Z0-9]*$/,
657
+ 'Service reference must be in format "Alias.services.ServiceName" (e.g., "Weather.services.openweather")'
658
+ );
659
+ var ServiceRefObjectSchema = z.object({
660
+ ref: ServiceRefStringSchema,
661
+ description: z.string().optional(),
662
+ baseUrl: z.string().url("baseUrl override must be a valid URL").optional(),
663
+ headers: z.record(z.string()).optional(),
664
+ url: z.string().url("url override must be a valid URL").optional(),
665
+ serverPath: z.string().optional()
666
+ });
667
+ var ServiceRefSchema = z.union([
668
+ ServiceDefinitionSchema,
669
+ ServiceRefObjectSchema,
670
+ ServiceRefStringSchema
671
+ ]);
672
+
673
+ // src/types/orbital.ts
674
+ var UseDeclarationSchema = z.object({
675
+ from: z.string().min(1, "Import source is required"),
676
+ as: z.string().min(1, "Alias is required").regex(
677
+ /^[A-Z][a-zA-Z0-9]*$/,
678
+ 'Alias must be PascalCase (e.g., "Health", "GameCore")'
679
+ )
680
+ });
681
+ function isEntityReference(entity) {
682
+ return typeof entity === "string";
683
+ }
684
+ function isEntityCall(entity) {
685
+ return typeof entity === "object" && entity !== null && "extends" in entity;
686
+ }
687
+ var EntityRefStringSchema = z.string().regex(
688
+ /^[A-Z][a-zA-Z0-9]*\.entity$/,
689
+ 'Entity reference must be in format "Alias.entity" (e.g., "Goblin.entity")'
690
+ );
691
+ var EntityCallSchema = z.object({
692
+ extends: z.string().regex(
693
+ /^[A-Z][a-zA-Z0-9]*\.entity$/,
694
+ 'EntityCall "extends" must be in format "Alias.entity"'
695
+ ),
696
+ name: z.string().optional(),
697
+ fields: z.array(EntityFieldSchema).optional(),
698
+ persistence: EntityPersistenceSchema.optional(),
699
+ collection: z.string().optional()
700
+ }).refine(
701
+ (call) => {
702
+ if (!call.fields) return true;
703
+ const seen = /* @__PURE__ */ new Set();
704
+ for (const field of call.fields) {
705
+ if (seen.has(field.name)) return false;
706
+ seen.add(field.name);
707
+ }
708
+ return true;
709
+ },
710
+ {
711
+ message: 'EntityCall "fields" override list must not contain duplicate field names',
712
+ path: ["fields"]
713
+ }
714
+ );
715
+ var EntityRefSchema = z.union([
716
+ EntitySchema,
717
+ EntityRefStringSchema,
718
+ EntityCallSchema
719
+ ]);
720
+ var PageRefStringSchema = z.string().regex(
721
+ /^[A-Z][a-zA-Z0-9]*\.pages\.[A-Z][a-zA-Z0-9]*$/,
722
+ 'Page reference must be in format "Alias.pages.PageName" (e.g., "User.pages.Profile")'
723
+ );
724
+ var PageRefObjectSchema = z.object({
725
+ ref: PageRefStringSchema,
726
+ path: z.string().startsWith("/").optional(),
727
+ linkedEntity: z.string().optional(),
728
+ traits: z.array(TraitRefSchema).optional()
729
+ });
730
+ var PageRefSchema = z.union([
731
+ PageSchema,
732
+ PageRefStringSchema,
733
+ PageRefObjectSchema
734
+ ]);
735
+ z.string().regex(
736
+ /^([A-Z][a-zA-Z0-9]*\.traits\.)?[A-Z][a-zA-Z0-9]*$/,
737
+ 'Trait reference must be "TraitName" or "Alias.traits.TraitName"'
738
+ );
739
+ function parseEntityRef(ref) {
740
+ const match = ref.match(/^([A-Z][a-zA-Z0-9]*)\.entity$/);
741
+ if (!match) return null;
742
+ return { alias: match[1] };
743
+ }
744
+ z.object({
745
+ event: z.string().min(1),
746
+ triggers: z.string().min(1),
747
+ guard: z.string().optional()
748
+ });
749
+ var EventSourceSchema = z.object({
750
+ trait: z.string().min(1),
751
+ transition: z.string().optional(),
752
+ tick: z.string().optional()
753
+ });
754
+ var ComputedEventContractSchema = z.object({
755
+ event: z.string().min(1),
756
+ originalEvent: z.string().min(1),
757
+ source: EventSourceSchema,
758
+ description: z.string().optional(),
759
+ payload: z.array(EventPayloadFieldSchema).optional()
760
+ });
761
+ var ComputedEventListenerSchema = z.object({
762
+ event: z.string().min(1),
763
+ source: EventSourceSchema,
764
+ triggers: z.string().min(1),
765
+ guard: ExpressionSchema.optional(),
766
+ payloadMapping: z.record(z.string()).optional()
767
+ });
768
+ z.object({
769
+ name: z.string().min(1, "Orbital name is required"),
770
+ description: z.string().optional(),
771
+ visual_prompt: z.string().optional(),
772
+ // Import system
773
+ uses: z.array(UseDeclarationSchema).optional(),
774
+ // Theme & Services
775
+ theme: ThemeRefSchema.optional(),
776
+ services: z.array(ServiceRefSchema).optional(),
777
+ // Components (inline or reference)
778
+ entity: EntityRefSchema,
779
+ traits: z.array(TraitRefSchema),
780
+ pages: z.array(PageRefSchema),
781
+ // Event interface (trait-centric model) - computed by resolver
782
+ emits: z.array(ComputedEventContractSchema).optional(),
783
+ listens: z.array(ComputedEventListenerSchema).optional(),
784
+ // Filter for exposed events (trait-centric model)
785
+ exposes: z.array(z.string()).optional(),
786
+ // Context fields - persisted throughout orbital lifecycle
787
+ domainContext: DomainContextSchema.optional(),
788
+ design: DesignPreferencesSchema.optional(),
789
+ suggestedGuards: z.array(SuggestedGuardSchema).optional()
790
+ });
791
+
792
+ // src/builders/layout-strategy.ts
793
+ function hasSequentialChain(wiring) {
794
+ if (wiring.length < 2) {
795
+ return false;
796
+ }
797
+ const adjacency = /* @__PURE__ */ new Map();
798
+ const allTargets = /* @__PURE__ */ new Set();
799
+ for (const entry of wiring) {
800
+ let targets = adjacency.get(entry.from);
801
+ if (!targets) {
802
+ targets = /* @__PURE__ */ new Set();
803
+ adjacency.set(entry.from, targets);
804
+ }
805
+ targets.add(entry.to);
806
+ allTargets.add(entry.to);
807
+ }
808
+ const roots = [];
809
+ for (const from of adjacency.keys()) {
810
+ if (!allTargets.has(from)) {
811
+ roots.push(from);
812
+ }
813
+ }
814
+ const starts = roots.length > 0 ? roots : [...adjacency.keys()];
815
+ for (const start of starts) {
816
+ let current = start;
817
+ let length = 1;
818
+ const visited = /* @__PURE__ */ new Set([current]);
819
+ while (true) {
820
+ const nexts = adjacency.get(current);
821
+ if (!nexts || nexts.size === 0) break;
822
+ let advanced = false;
823
+ for (const next of nexts) {
824
+ if (!visited.has(next)) {
825
+ visited.add(next);
826
+ current = next;
827
+ length++;
828
+ advanced = true;
829
+ break;
830
+ }
831
+ }
832
+ if (!advanced) break;
833
+ }
834
+ if (length >= 3) {
835
+ return true;
836
+ }
837
+ }
838
+ return false;
839
+ }
840
+ function detectLayoutStrategy(orbitals, eventWiring) {
841
+ if (eventWiring && eventWiring.length > 0 && hasSequentialChain(eventWiring)) {
842
+ return "wizard-flow";
843
+ }
844
+ const count = orbitals.length;
845
+ if (count <= 1) {
846
+ return "single";
847
+ }
848
+ if (count <= 4) {
849
+ return "tabs";
850
+ }
851
+ return "sidebar";
852
+ }
278
853
 
279
854
  // src/builders/event-wiring.ts
280
855
  function findInlineTrait(orbitals, name) {
@@ -420,6 +995,16 @@ function ensureIdField(fields) {
420
995
  function plural(name) {
421
996
  return name + "s";
422
997
  }
998
+ function resolveEntityNameForBuilder(ref) {
999
+ if (isEntityReference(ref)) {
1000
+ return parseEntityRef(ref)?.alias ?? ref;
1001
+ }
1002
+ if (isEntityCall(ref)) {
1003
+ if (ref.name) return ref.name;
1004
+ return parseEntityRef(ref.extends)?.alias ?? ref.extends;
1005
+ }
1006
+ return ref.name;
1007
+ }
423
1008
  function makeEntity(opts) {
424
1009
  const fields = ensureIdField(opts.fields);
425
1010
  const persistence = opts.persistence ?? "runtime";
@@ -552,7 +1137,7 @@ function pipe(orbitals, events, appName) {
552
1137
  }
553
1138
  }
554
1139
  const pages = cloned.map((o, i) => {
555
- const entityName = typeof o.entity === "string" ? o.entity : o.entity.name;
1140
+ const entityName = resolveEntityNameForBuilder(o.entity);
556
1141
  const traitNames = o.traits.map((t) => t.name);
557
1142
  return {
558
1143
  name: `${entityName}Page`,