@auto-engineer/narrative 1.134.0 → 1.135.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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +5 -5
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +21 -0
- package/dist/src/schema.d.ts +2062 -1
- package/dist/src/schema.d.ts.map +1 -1
- package/dist/src/schema.js +31 -2
- package/dist/src/schema.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/schema.specs.ts +222 -1
- package/src/schema.ts +40 -1
package/package.json
CHANGED
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"typescript": "^5.9.2",
|
|
27
27
|
"zod": "^3.22.4",
|
|
28
28
|
"zod-to-json-schema": "^3.22.3",
|
|
29
|
-
"@auto-engineer/file-store": "1.
|
|
30
|
-
"@auto-engineer/id": "1.
|
|
31
|
-
"@auto-engineer/message-bus": "1.
|
|
29
|
+
"@auto-engineer/file-store": "1.135.0",
|
|
30
|
+
"@auto-engineer/id": "1.135.0",
|
|
31
|
+
"@auto-engineer/message-bus": "1.135.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/node": "^20.0.0",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
|
-
"version": "1.
|
|
41
|
+
"version": "1.135.0",
|
|
42
42
|
"scripts": {
|
|
43
43
|
"build": "tsx scripts/build.ts",
|
|
44
44
|
"test": "vitest run --reporter=dot",
|
package/src/schema.specs.ts
CHANGED
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
ComponentDefinition,
|
|
4
|
+
Journey,
|
|
5
|
+
JourneyPlanning,
|
|
6
|
+
RegionEntry,
|
|
7
|
+
SceneClassification,
|
|
8
|
+
SceneRoute,
|
|
9
|
+
UI,
|
|
10
|
+
} from './schema';
|
|
3
11
|
import {
|
|
4
12
|
CommandSliceSchema,
|
|
13
|
+
ComponentDefinitionSchema,
|
|
5
14
|
DataSchema,
|
|
6
15
|
DataTargetSchema,
|
|
16
|
+
DesignSchema,
|
|
7
17
|
JourneyPlanningSchema,
|
|
8
18
|
JourneySchema,
|
|
9
19
|
modelSchema,
|
|
10
20
|
NarrativeNamesOnlySchema,
|
|
11
21
|
NarrativeSchema,
|
|
12
22
|
QuerySliceSchema,
|
|
23
|
+
RegionEntrySchema,
|
|
13
24
|
SceneClassificationSchema,
|
|
14
25
|
SceneRouteSchema,
|
|
26
|
+
UISchema,
|
|
15
27
|
} from './schema';
|
|
16
28
|
|
|
17
29
|
describe('CommandSliceSchema', () => {
|
|
@@ -527,3 +539,212 @@ describe('QuerySliceSchema', () => {
|
|
|
527
539
|
}
|
|
528
540
|
});
|
|
529
541
|
});
|
|
542
|
+
|
|
543
|
+
describe('RegionEntrySchema', () => {
|
|
544
|
+
it('should accept valid region entry with required fields', () => {
|
|
545
|
+
const result = RegionEntrySchema.safeParse({ id: 'hero-1', name: 'HeroBanner' });
|
|
546
|
+
expect(result.success).toBe(true);
|
|
547
|
+
if (result.success) {
|
|
548
|
+
expect(result.data).toEqual({ id: 'hero-1', name: 'HeroBanner' });
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
it('should accept optional slots', () => {
|
|
553
|
+
const result = RegionEntrySchema.safeParse({ id: 'card-1', name: 'Card', slots: { title: 'Hello' } });
|
|
554
|
+
expect(result.success).toBe(true);
|
|
555
|
+
if (result.success) {
|
|
556
|
+
expect(result.data.slots).toEqual({ title: 'Hello' });
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it('should reject missing id', () => {
|
|
561
|
+
const result = RegionEntrySchema.safeParse({ name: 'Card' });
|
|
562
|
+
expect(result.success).toBe(false);
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
it('should reject missing name', () => {
|
|
566
|
+
const result = RegionEntrySchema.safeParse({ id: 'card-1' });
|
|
567
|
+
expect(result.success).toBe(false);
|
|
568
|
+
});
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
describe('UISchema', () => {
|
|
572
|
+
it('should accept valid UI with required fields', () => {
|
|
573
|
+
const result = UISchema.safeParse({
|
|
574
|
+
layoutId: 'two-column',
|
|
575
|
+
mode: 'as-is',
|
|
576
|
+
regions: { main: [{ id: 'c-1', name: 'List' }] },
|
|
577
|
+
});
|
|
578
|
+
expect(result.success).toBe(true);
|
|
579
|
+
if (result.success) {
|
|
580
|
+
expect(result.data).toEqual({
|
|
581
|
+
layoutId: 'two-column',
|
|
582
|
+
mode: 'as-is',
|
|
583
|
+
regions: { main: [{ id: 'c-1', name: 'List' }] },
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
it('should accept all valid mode values', () => {
|
|
589
|
+
for (const mode of ['as-is', 'modify', 'custom'] as const) {
|
|
590
|
+
const result = UISchema.safeParse({ layoutId: 'l', mode, regions: {} });
|
|
591
|
+
expect(result.success).toBe(true);
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
it('should reject invalid mode', () => {
|
|
596
|
+
const result = UISchema.safeParse({ layoutId: 'l', mode: 'invalid', regions: {} });
|
|
597
|
+
expect(result.success).toBe(false);
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
it('should accept optional customizationNotes', () => {
|
|
601
|
+
const result = UISchema.safeParse({
|
|
602
|
+
layoutId: 'l',
|
|
603
|
+
mode: 'modify',
|
|
604
|
+
regions: {},
|
|
605
|
+
customizationNotes: 'Make header sticky',
|
|
606
|
+
});
|
|
607
|
+
expect(result.success).toBe(true);
|
|
608
|
+
if (result.success) {
|
|
609
|
+
expect(result.data.customizationNotes).toBe('Make header sticky');
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
it('should reject missing layoutId', () => {
|
|
614
|
+
const result = UISchema.safeParse({ mode: 'as-is', regions: {} });
|
|
615
|
+
expect(result.success).toBe(false);
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
it('should reject missing mode', () => {
|
|
619
|
+
const result = UISchema.safeParse({ layoutId: 'l', regions: {} });
|
|
620
|
+
expect(result.success).toBe(false);
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
it('should reject missing regions', () => {
|
|
624
|
+
const result = UISchema.safeParse({ layoutId: 'l', mode: 'as-is' });
|
|
625
|
+
expect(result.success).toBe(false);
|
|
626
|
+
});
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
describe('ComponentDefinitionSchema', () => {
|
|
630
|
+
it('should accept valid component definition', () => {
|
|
631
|
+
const result = ComponentDefinitionSchema.safeParse({
|
|
632
|
+
id: 'comp-1',
|
|
633
|
+
name: 'PriceTag',
|
|
634
|
+
category: 'commerce',
|
|
635
|
+
description: 'Displays a price',
|
|
636
|
+
slots: { price: { type: 'number' } },
|
|
637
|
+
template: '<span>{{price}}</span>',
|
|
638
|
+
});
|
|
639
|
+
expect(result.success).toBe(true);
|
|
640
|
+
if (result.success) {
|
|
641
|
+
expect(result.data).toEqual({
|
|
642
|
+
id: 'comp-1',
|
|
643
|
+
name: 'PriceTag',
|
|
644
|
+
category: 'commerce',
|
|
645
|
+
description: 'Displays a price',
|
|
646
|
+
slots: { price: { type: 'number' } },
|
|
647
|
+
template: '<span>{{price}}</span>',
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
it('should reject missing required fields', () => {
|
|
653
|
+
const result = ComponentDefinitionSchema.safeParse({ id: 'comp-1', name: 'PriceTag' });
|
|
654
|
+
expect(result.success).toBe(false);
|
|
655
|
+
});
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
describe('DesignSchema ui field', () => {
|
|
659
|
+
it('should accept design with optional ui', () => {
|
|
660
|
+
const result = DesignSchema.safeParse({
|
|
661
|
+
ui: {
|
|
662
|
+
layoutId: 'sidebar',
|
|
663
|
+
mode: 'as-is',
|
|
664
|
+
regions: { sidebar: [{ id: 'nav', name: 'NavMenu' }] },
|
|
665
|
+
},
|
|
666
|
+
});
|
|
667
|
+
expect(result.success).toBe(true);
|
|
668
|
+
if (result.success) {
|
|
669
|
+
expect(result.data.ui?.layoutId).toBe('sidebar');
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
it('should accept design without ui', () => {
|
|
674
|
+
const result = DesignSchema.safeParse({});
|
|
675
|
+
expect(result.success).toBe(true);
|
|
676
|
+
if (result.success) {
|
|
677
|
+
expect(result.data.ui).toBeUndefined();
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
describe('modelSchema design.components field', () => {
|
|
683
|
+
const minimalModel = {
|
|
684
|
+
variant: 'specs' as const,
|
|
685
|
+
narratives: [],
|
|
686
|
+
messages: [],
|
|
687
|
+
modules: [],
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
it('should accept model with design.components', () => {
|
|
691
|
+
const result = modelSchema.safeParse({
|
|
692
|
+
...minimalModel,
|
|
693
|
+
design: {
|
|
694
|
+
components: [
|
|
695
|
+
{
|
|
696
|
+
id: 'comp-1',
|
|
697
|
+
name: 'PriceTag',
|
|
698
|
+
category: 'commerce',
|
|
699
|
+
description: 'Displays a price',
|
|
700
|
+
slots: { price: { type: 'number' } },
|
|
701
|
+
template: '<span>{{price}}</span>',
|
|
702
|
+
},
|
|
703
|
+
],
|
|
704
|
+
},
|
|
705
|
+
});
|
|
706
|
+
expect(result.success).toBe(true);
|
|
707
|
+
if (result.success) {
|
|
708
|
+
expect(result.data.design?.components).toHaveLength(1);
|
|
709
|
+
expect(result.data.design?.components?.[0].name).toBe('PriceTag');
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
it('should accept model design without components', () => {
|
|
714
|
+
const result = modelSchema.safeParse({
|
|
715
|
+
...minimalModel,
|
|
716
|
+
design: {},
|
|
717
|
+
});
|
|
718
|
+
expect(result.success).toBe(true);
|
|
719
|
+
if (result.success) {
|
|
720
|
+
expect(result.data.design?.components).toBeUndefined();
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
describe('exported UI types', () => {
|
|
726
|
+
it('RegionEntry type matches RegionEntrySchema inference', () => {
|
|
727
|
+
const parsed = RegionEntrySchema.parse({ id: 'r-1', name: 'Header' });
|
|
728
|
+
const typed: RegionEntry = parsed;
|
|
729
|
+
expect(typed).toEqual({ id: 'r-1', name: 'Header' });
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
it('UI type matches UISchema inference', () => {
|
|
733
|
+
const parsed = UISchema.parse({ layoutId: 'l', mode: 'as-is', regions: {} });
|
|
734
|
+
const typed: UI = parsed;
|
|
735
|
+
expect(typed).toEqual({ layoutId: 'l', mode: 'as-is', regions: {} });
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
it('ComponentDefinition type matches ComponentDefinitionSchema inference', () => {
|
|
739
|
+
const parsed = ComponentDefinitionSchema.parse({
|
|
740
|
+
id: 'c-1',
|
|
741
|
+
name: 'Btn',
|
|
742
|
+
category: 'ui',
|
|
743
|
+
description: 'A button',
|
|
744
|
+
slots: {},
|
|
745
|
+
template: '<button/>',
|
|
746
|
+
});
|
|
747
|
+
const typed: ComponentDefinition = parsed;
|
|
748
|
+
expect(typed.name).toBe('Btn');
|
|
749
|
+
});
|
|
750
|
+
});
|
package/src/schema.ts
CHANGED
|
@@ -218,10 +218,39 @@ export const ImageAssetSchema = z
|
|
|
218
218
|
})
|
|
219
219
|
.describe('Image asset with optional generation metadata');
|
|
220
220
|
|
|
221
|
+
const RegionEntrySchema = z
|
|
222
|
+
.object({
|
|
223
|
+
id: z.string(),
|
|
224
|
+
name: z.string(),
|
|
225
|
+
slots: z.record(z.unknown()).optional(),
|
|
226
|
+
})
|
|
227
|
+
.describe('Component placed in a layout region');
|
|
228
|
+
|
|
229
|
+
const UISchema = z
|
|
230
|
+
.object({
|
|
231
|
+
layoutId: z.string(),
|
|
232
|
+
mode: z.enum(['as-is', 'modify', 'custom']),
|
|
233
|
+
regions: z.record(z.array(RegionEntrySchema)),
|
|
234
|
+
customizationNotes: z.string().optional(),
|
|
235
|
+
})
|
|
236
|
+
.describe('UI composition for a slice');
|
|
237
|
+
|
|
238
|
+
const ComponentDefinitionSchema = z
|
|
239
|
+
.object({
|
|
240
|
+
id: z.string(),
|
|
241
|
+
name: z.string(),
|
|
242
|
+
category: z.string(),
|
|
243
|
+
description: z.string(),
|
|
244
|
+
slots: z.record(z.unknown()),
|
|
245
|
+
template: z.string(),
|
|
246
|
+
})
|
|
247
|
+
.describe('Custom reusable component definition');
|
|
248
|
+
|
|
221
249
|
export const DesignSchema = z
|
|
222
250
|
.object({
|
|
223
251
|
imageAsset: ImageAssetSchema.optional().describe('Primary image asset for this entity'),
|
|
224
252
|
metadata: z.record(z.unknown()).optional().describe('Flexible design metadata'),
|
|
253
|
+
ui: UISchema.optional().describe('UI composition for this entity'),
|
|
225
254
|
})
|
|
226
255
|
.describe('Design fields for visual representation');
|
|
227
256
|
|
|
@@ -513,6 +542,10 @@ export const ClientServerNamesSchema = z
|
|
|
513
542
|
})
|
|
514
543
|
.describe('System with client/server descriptions for behavior planning');
|
|
515
544
|
|
|
545
|
+
const ModelDesignSchema = DesignSchema.extend({
|
|
546
|
+
components: z.array(ComponentDefinitionSchema).optional().describe('Custom reusable component definitions'),
|
|
547
|
+
}).describe('Model-level design fields with component definitions');
|
|
548
|
+
|
|
516
549
|
export const modelSchema = z
|
|
517
550
|
.object({
|
|
518
551
|
variant: z.literal('specs').describe('Full specification with all details'),
|
|
@@ -521,7 +554,7 @@ export const modelSchema = z
|
|
|
521
554
|
integrations: z.array(IntegrationSchema).optional(),
|
|
522
555
|
modules: z.array(ModuleSchema).describe('Modules for type ownership and file grouping'),
|
|
523
556
|
journeys: z.array(JourneySchema).optional(),
|
|
524
|
-
design:
|
|
557
|
+
design: ModelDesignSchema.optional().describe('Design fields for visual representation'),
|
|
525
558
|
})
|
|
526
559
|
.describe('Complete system specification with all implementation details');
|
|
527
560
|
|
|
@@ -551,6 +584,9 @@ export {
|
|
|
551
584
|
StepErrorSchema,
|
|
552
585
|
StepWithDocStringSchema,
|
|
553
586
|
StepWithErrorSchema,
|
|
587
|
+
RegionEntrySchema,
|
|
588
|
+
UISchema,
|
|
589
|
+
ComponentDefinitionSchema,
|
|
554
590
|
};
|
|
555
591
|
|
|
556
592
|
export type Model = z.infer<typeof modelSchema>;
|
|
@@ -577,3 +613,6 @@ export type SceneRoute = z.infer<typeof SceneRouteSchema>;
|
|
|
577
613
|
export type JourneyPlanning = z.infer<typeof JourneyPlanningSchema>;
|
|
578
614
|
export type ImageAsset = z.infer<typeof ImageAssetSchema>;
|
|
579
615
|
export type Design = z.infer<typeof DesignSchema>;
|
|
616
|
+
export type RegionEntry = z.infer<typeof RegionEntrySchema>;
|
|
617
|
+
export type UI = z.infer<typeof UISchema>;
|
|
618
|
+
export type ComponentDefinition = z.infer<typeof ComponentDefinitionSchema>;
|