@auto-engineer/narrative 0.21.0 → 0.22.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.
Files changed (89) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +23 -0
  3. package/dist/src/id/addAutoIds.d.ts.map +1 -1
  4. package/dist/src/id/addAutoIds.js +16 -11
  5. package/dist/src/id/addAutoIds.js.map +1 -1
  6. package/dist/src/id/hasAllIds.d.ts.map +1 -1
  7. package/dist/src/id/hasAllIds.js +8 -2
  8. package/dist/src/id/hasAllIds.js.map +1 -1
  9. package/dist/src/index.d.ts +2 -2
  10. package/dist/src/index.d.ts.map +1 -1
  11. package/dist/src/index.js +1 -1
  12. package/dist/src/index.js.map +1 -1
  13. package/dist/src/narrative-context.d.ts +2 -2
  14. package/dist/src/narrative-context.d.ts.map +1 -1
  15. package/dist/src/narrative-context.js +18 -15
  16. package/dist/src/narrative-context.js.map +1 -1
  17. package/dist/src/narrative.d.ts +2 -2
  18. package/dist/src/narrative.d.ts.map +1 -1
  19. package/dist/src/narrative.js +6 -3
  20. package/dist/src/narrative.js.map +1 -1
  21. package/dist/src/samples/seasonal-assistant.schema.json +73 -65
  22. package/dist/src/schema.d.ts +13937 -7286
  23. package/dist/src/schema.d.ts.map +1 -1
  24. package/dist/src/schema.js +9 -6
  25. package/dist/src/schema.js.map +1 -1
  26. package/dist/src/transformers/model-to-narrative/generators/flow.d.ts.map +1 -1
  27. package/dist/src/transformers/model-to-narrative/generators/flow.js +10 -4
  28. package/dist/src/transformers/model-to-narrative/generators/flow.js.map +1 -1
  29. package/dist/src/transformers/narrative-to-model/index.d.ts.map +1 -1
  30. package/dist/src/transformers/narrative-to-model/index.js +6 -3
  31. package/dist/src/transformers/narrative-to-model/index.js.map +1 -1
  32. package/dist/src/types.d.ts +4 -0
  33. package/dist/src/types.d.ts.map +1 -1
  34. package/dist/tsconfig.tsbuildinfo +1 -1
  35. package/package.json +4 -4
  36. package/src/getNarratives.specs.ts +30 -30
  37. package/src/id/addAutoIds.specs.ts +79 -61
  38. package/src/id/addAutoIds.ts +17 -11
  39. package/src/id/hasAllIds.specs.ts +100 -54
  40. package/src/id/hasAllIds.ts +8 -2
  41. package/src/index.ts +2 -1
  42. package/src/model-to-narrative.specs.ts +206 -158
  43. package/src/narrative-context.ts +22 -16
  44. package/src/narrative.ts +8 -4
  45. package/src/samples/seasonal-assistant.schema.json +73 -65
  46. package/src/schema.ts +10 -6
  47. package/src/transformers/model-to-narrative/generators/flow.ts +14 -5
  48. package/src/transformers/narrative-to-model/index.ts +6 -3
  49. package/src/types.ts +5 -0
  50. package/.turbo/turbo-format.log +0 -4
  51. package/.turbo/turbo-lint.log +0 -4
  52. package/.turbo/turbo-test.log +0 -14
  53. package/.turbo/turbo-type-check.log +0 -5
  54. package/dist/src/commands/export-schema-runner.d.ts +0 -3
  55. package/dist/src/commands/export-schema-runner.d.ts.map +0 -1
  56. package/dist/src/commands/export-schema-runner.js +0 -53
  57. package/dist/src/commands/export-schema-runner.js.map +0 -1
  58. package/dist/src/fluent-builder.specs.d.ts +0 -2
  59. package/dist/src/fluent-builder.specs.d.ts.map +0 -1
  60. package/dist/src/fluent-builder.specs.js +0 -28
  61. package/dist/src/fluent-builder.specs.js.map +0 -1
  62. package/dist/src/getNarratives.cache.specs.d.ts +0 -2
  63. package/dist/src/getNarratives.cache.specs.d.ts.map +0 -1
  64. package/dist/src/getNarratives.cache.specs.js +0 -234
  65. package/dist/src/getNarratives.cache.specs.js.map +0 -1
  66. package/dist/src/getNarratives.specs.d.ts +0 -2
  67. package/dist/src/getNarratives.specs.d.ts.map +0 -1
  68. package/dist/src/getNarratives.specs.js +0 -1294
  69. package/dist/src/getNarratives.specs.js.map +0 -1
  70. package/dist/src/id/addAutoIds.specs.d.ts +0 -2
  71. package/dist/src/id/addAutoIds.specs.d.ts.map +0 -1
  72. package/dist/src/id/addAutoIds.specs.js +0 -265
  73. package/dist/src/id/addAutoIds.specs.js.map +0 -1
  74. package/dist/src/id/hasAllIds.specs.d.ts +0 -2
  75. package/dist/src/id/hasAllIds.specs.d.ts.map +0 -1
  76. package/dist/src/id/hasAllIds.specs.js +0 -231
  77. package/dist/src/id/hasAllIds.specs.js.map +0 -1
  78. package/dist/src/model-to-narrative.specs.d.ts +0 -2
  79. package/dist/src/model-to-narrative.specs.d.ts.map +0 -1
  80. package/dist/src/model-to-narrative.specs.js +0 -2378
  81. package/dist/src/model-to-narrative.specs.js.map +0 -1
  82. package/dist/src/narrative-context.specs.d.ts +0 -2
  83. package/dist/src/narrative-context.specs.d.ts.map +0 -1
  84. package/dist/src/narrative-context.specs.js +0 -185
  85. package/dist/src/narrative-context.specs.js.map +0 -1
  86. package/dist/src/transformers/narrative-to-model/type-inference.specs.d.ts +0 -2
  87. package/dist/src/transformers/narrative-to-model/type-inference.specs.d.ts.map +0 -1
  88. package/dist/src/transformers/narrative-to-model/type-inference.specs.js +0 -167
  89. package/dist/src/transformers/narrative-to-model/type-inference.specs.js.map +0 -1
package/package.json CHANGED
@@ -23,9 +23,9 @@
23
23
  "typescript": "^5.9.2",
24
24
  "zod": "^3.22.4",
25
25
  "zod-to-json-schema": "^3.22.3",
26
- "@auto-engineer/file-store": "0.21.0",
27
- "@auto-engineer/id": "0.21.0",
28
- "@auto-engineer/message-bus": "0.21.0"
26
+ "@auto-engineer/id": "0.22.0",
27
+ "@auto-engineer/message-bus": "0.22.0",
28
+ "@auto-engineer/file-store": "0.22.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^20.0.0",
@@ -35,7 +35,7 @@
35
35
  "publishConfig": {
36
36
  "access": "public"
37
37
  },
38
- "version": "0.21.0",
38
+ "version": "0.22.0",
39
39
  "scripts": {
40
40
  "build": "tsx scripts/build.ts",
41
41
  "test": "vitest run --reporter=dot",
@@ -4,7 +4,7 @@ import { InMemoryFileStore } from '@auto-engineer/file-store';
4
4
  import { NodeFileStore } from '@auto-engineer/file-store/node';
5
5
  import { beforeEach, describe, expect, it } from 'vitest';
6
6
  import { getNarratives } from './getNarratives';
7
- import { type DataSource, type Example, type Model, modelToNarrative, type Narrative, type QuerySlice } from './index';
7
+ import { type Example, type Model, modelToNarrative, type Narrative, type QuerySlice } from './index';
8
8
  import { modelSchema } from './schema';
9
9
 
10
10
  const __filename = fileURLToPath(import.meta.url);
@@ -112,12 +112,12 @@ describe('getNarratives', (_mode) => {
112
112
  /query items\(\$itemId: String!\) {\s+items\(itemId: \$itemId\) {\s+id\s+description\s+}/,
113
113
  );
114
114
 
115
- const data = viewItemSlice?.server?.data as DataSource[] | undefined;
116
- if (!data || !Array.isArray(data)) throw new Error('No data found in view items slice');
115
+ const data = viewItemSlice?.server?.data;
116
+ if (!data || !Array.isArray(data.items)) throw new Error('No data found in view items slice');
117
117
 
118
- expect(data).toHaveLength(1);
119
- expect(data[0].target).toMatchObject({ type: 'State', name: 'items' });
120
- expect(data[0].origin).toMatchObject({ name: 'ItemsProjection', type: 'projection' });
118
+ expect(data.items).toHaveLength(1);
119
+ expect(data.items[0].target).toMatchObject({ type: 'State', name: 'items' });
120
+ expect(data.items[0].origin).toMatchObject({ name: 'ItemsProjection', type: 'projection' });
121
121
 
122
122
  const specs = viewItemSlice?.server?.specs;
123
123
  if (specs == null || specs.length === 0 || specs[0].feature === '')
@@ -224,7 +224,7 @@ describe('getNarratives', (_mode) => {
224
224
  f.slices.some((s) => {
225
225
  if (s.type === 'command' || s.type === 'query') {
226
226
  return (
227
- s.server.data?.some(
227
+ s.server.data?.items?.some(
228
228
  (d) =>
229
229
  ('destination' in d && d.destination?.type === 'integration') ||
230
230
  ('origin' in d && d.origin?.type === 'integration'),
@@ -1494,17 +1494,17 @@ flow('Projection Test', () => {
1494
1494
 
1495
1495
  if (summarySlice?.type !== 'query') return;
1496
1496
 
1497
- const data = summarySlice.server.data as DataSource[] | undefined;
1497
+ const data = summarySlice.server.data;
1498
1498
  expect(data).toBeDefined();
1499
- expect(data).toHaveLength(1);
1499
+ expect(data?.items).toHaveLength(1);
1500
1500
 
1501
- expect(data?.[0].origin).toMatchObject({
1501
+ expect(data?.items?.[0].origin).toMatchObject({
1502
1502
  type: 'projection',
1503
1503
  name: 'TodoSummary',
1504
1504
  singleton: true,
1505
1505
  });
1506
1506
 
1507
- expect(data?.[0].origin).not.toHaveProperty('idField');
1507
+ expect(data?.items?.[0].origin).not.toHaveProperty('idField');
1508
1508
  });
1509
1509
 
1510
1510
  it('should generate correct origin for regular projection with single idField', async () => {
@@ -1546,17 +1546,17 @@ flow('Projection Test', () => {
1546
1546
 
1547
1547
  if (todoSlice?.type !== 'query') return;
1548
1548
 
1549
- const data = todoSlice.server.data as DataSource[] | undefined;
1549
+ const data = todoSlice.server.data;
1550
1550
  expect(data).toBeDefined();
1551
- expect(data).toHaveLength(1);
1551
+ expect(data?.items).toHaveLength(1);
1552
1552
 
1553
- expect(data?.[0].origin).toMatchObject({
1553
+ expect(data?.items?.[0].origin).toMatchObject({
1554
1554
  type: 'projection',
1555
1555
  name: 'Todos',
1556
1556
  idField: 'todoId',
1557
1557
  });
1558
1558
 
1559
- expect(data?.[0].origin).not.toHaveProperty('singleton');
1559
+ expect(data?.items?.[0].origin).not.toHaveProperty('singleton');
1560
1560
  });
1561
1561
 
1562
1562
  it('should generate correct origin for composite projection with multiple idFields', async () => {
@@ -1598,17 +1598,17 @@ flow('Projection Test', () => {
1598
1598
 
1599
1599
  if (userProjectSlice?.type !== 'query') return;
1600
1600
 
1601
- const data = userProjectSlice.server.data as DataSource[] | undefined;
1601
+ const data = userProjectSlice.server.data;
1602
1602
  expect(data).toBeDefined();
1603
- expect(data).toHaveLength(1);
1603
+ expect(data?.items).toHaveLength(1);
1604
1604
 
1605
- expect(data?.[0].origin).toMatchObject({
1605
+ expect(data?.items?.[0].origin).toMatchObject({
1606
1606
  type: 'projection',
1607
1607
  name: 'UserProjects',
1608
1608
  idField: ['userId', 'projectId'],
1609
1609
  });
1610
1610
 
1611
- expect(data?.[0].origin).not.toHaveProperty('singleton');
1611
+ expect(data?.items?.[0].origin).not.toHaveProperty('singleton');
1612
1612
  });
1613
1613
 
1614
1614
  it('should validate all three projection patterns together', async () => {
@@ -1684,8 +1684,8 @@ flow('All Projection Patterns', () => {
1684
1684
 
1685
1685
  const summarySlice = projectionFlow.slices.find((s) => s.name === 'views summary');
1686
1686
  if (summarySlice?.type === 'query') {
1687
- const data = summarySlice.server.data as DataSource[] | undefined;
1688
- expect(data?.[0].origin).toMatchObject({
1687
+ const data = summarySlice.server.data;
1688
+ expect(data?.items?.[0].origin).toMatchObject({
1689
1689
  type: 'projection',
1690
1690
  name: 'TodoSummary',
1691
1691
  singleton: true,
@@ -1694,8 +1694,8 @@ flow('All Projection Patterns', () => {
1694
1694
 
1695
1695
  const todoSlice = projectionFlow.slices.find((s) => s.name === 'views todo');
1696
1696
  if (todoSlice?.type === 'query') {
1697
- const data = todoSlice.server.data as DataSource[] | undefined;
1698
- expect(data?.[0].origin).toMatchObject({
1697
+ const data = todoSlice.server.data;
1698
+ expect(data?.items?.[0].origin).toMatchObject({
1699
1699
  type: 'projection',
1700
1700
  name: 'Todos',
1701
1701
  idField: 'todoId',
@@ -1704,8 +1704,8 @@ flow('All Projection Patterns', () => {
1704
1704
 
1705
1705
  const userProjectSlice = projectionFlow.slices.find((s) => s.name === 'views user project todos');
1706
1706
  if (userProjectSlice?.type === 'query') {
1707
- const data = userProjectSlice.server.data as DataSource[] | undefined;
1708
- expect(data?.[0].origin).toMatchObject({
1707
+ const data = userProjectSlice.server.data;
1708
+ expect(data?.items?.[0].origin).toMatchObject({
1709
1709
  type: 'projection',
1710
1710
  name: 'UserProjectTodos',
1711
1711
  idField: ['userId', 'projectId'],
@@ -1770,15 +1770,15 @@ flow('Data Item IDs', () => {
1770
1770
  const commandSlice = dataIdsFlow.slices.find((s) => s.name === 'places order');
1771
1771
  if (commandSlice?.type === 'command') {
1772
1772
  const sinkData = commandSlice.server.data;
1773
- expect(sinkData).toHaveLength(1);
1774
- expect(sinkData?.[0].id).toBe('SINK-001');
1773
+ expect(sinkData?.items).toHaveLength(1);
1774
+ expect(sinkData?.items?.[0].id).toBe('SINK-001');
1775
1775
  }
1776
1776
 
1777
1777
  const querySlice = dataIdsFlow.slices.find((s) => s.name === 'views order status');
1778
1778
  if (querySlice?.type === 'query') {
1779
- const sourceData = querySlice.server.data as DataSource[] | undefined;
1780
- expect(sourceData).toHaveLength(1);
1781
- expect(sourceData?.[0].id).toBe('SOURCE-001');
1779
+ const sourceData = querySlice.server.data;
1780
+ expect(sourceData?.items).toHaveLength(1);
1781
+ expect(sourceData?.items?.[0].id).toBe('SOURCE-001');
1782
1782
  }
1783
1783
  });
1784
1784
  });
@@ -688,12 +688,14 @@ describe('addAutoIds', () => {
688
688
  server: {
689
689
  description: 'Test server',
690
690
  specs: [],
691
- data: [
692
- {
693
- target: { type: 'Event', name: 'TestEvent' },
694
- destination: { type: 'stream', pattern: 'test-stream' },
695
- },
696
- ],
691
+ data: {
692
+ items: [
693
+ {
694
+ target: { type: 'Event', name: 'TestEvent' },
695
+ destination: { type: 'stream', pattern: 'test-stream' },
696
+ },
697
+ ],
698
+ },
697
699
  },
698
700
  },
699
701
  ],
@@ -708,7 +710,8 @@ describe('addAutoIds', () => {
708
710
  const slice = result.narratives[0].slices[0];
709
711
 
710
712
  if ('server' in slice && slice.server?.data) {
711
- expect(slice.server.data[0].id).toMatch(AUTO_ID_REGEX);
713
+ expect(slice.server.data.id).toMatch(AUTO_ID_REGEX);
714
+ expect(slice.server.data.items[0].id).toMatch(AUTO_ID_REGEX);
712
715
  }
713
716
  });
714
717
 
@@ -728,12 +731,14 @@ describe('addAutoIds', () => {
728
731
  server: {
729
732
  description: 'Test server',
730
733
  specs: [],
731
- data: [
732
- {
733
- target: { type: 'State', name: 'TestState' },
734
- origin: { type: 'projection', name: 'TestProjection' },
735
- },
736
- ],
734
+ data: {
735
+ items: [
736
+ {
737
+ target: { type: 'State', name: 'TestState' },
738
+ origin: { type: 'projection', name: 'TestProjection' },
739
+ },
740
+ ],
741
+ },
737
742
  },
738
743
  },
739
744
  ],
@@ -748,7 +753,8 @@ describe('addAutoIds', () => {
748
753
  const slice = result.narratives[0].slices[0];
749
754
 
750
755
  if ('server' in slice && slice.server?.data) {
751
- expect(slice.server.data[0].id).toMatch(AUTO_ID_REGEX);
756
+ expect(slice.server.data.id).toMatch(AUTO_ID_REGEX);
757
+ expect(slice.server.data.items[0].id).toMatch(AUTO_ID_REGEX);
752
758
  }
753
759
  });
754
760
 
@@ -768,17 +774,19 @@ describe('addAutoIds', () => {
768
774
  server: {
769
775
  description: 'Test server',
770
776
  specs: [],
771
- data: [
772
- {
773
- id: 'SINK-001',
774
- target: { type: 'Command', name: 'TestCommand' },
775
- destination: { type: 'stream', pattern: 'test-stream' },
776
- _withState: {
777
- target: { type: 'State', name: 'TestState' },
778
- origin: { type: 'projection', name: 'TestProjection' },
777
+ data: {
778
+ items: [
779
+ {
780
+ id: 'SINK-001',
781
+ target: { type: 'Command', name: 'TestCommand' },
782
+ destination: { type: 'stream', pattern: 'test-stream' },
783
+ _withState: {
784
+ target: { type: 'State', name: 'TestState' },
785
+ origin: { type: 'projection', name: 'TestProjection' },
786
+ },
779
787
  },
780
- },
781
- ],
788
+ ],
789
+ },
782
790
  },
783
791
  },
784
792
  ],
@@ -793,7 +801,7 @@ describe('addAutoIds', () => {
793
801
  const slice = result.narratives[0].slices[0];
794
802
 
795
803
  if ('server' in slice && slice.server?.data) {
796
- const sink = slice.server.data[0];
804
+ const sink = slice.server.data.items[0];
797
805
  expect(sink.id).toBe('SINK-001');
798
806
  if ('destination' in sink && sink._withState) {
799
807
  expect(sink._withState.id).toMatch(AUTO_ID_REGEX);
@@ -815,18 +823,21 @@ describe('addAutoIds', () => {
815
823
  id: 'SLICE-001',
816
824
  server: {
817
825
  specs: [],
818
- data: [
819
- {
820
- id: 'EXISTING-SINK-001',
821
- target: { type: 'Event', name: 'TestEvent' },
822
- destination: { type: 'stream', pattern: 'test-stream' },
823
- },
824
- {
825
- id: 'EXISTING-SOURCE-001',
826
- target: { type: 'State', name: 'TestState' },
827
- origin: { type: 'projection', name: 'TestProjection' },
828
- },
829
- ],
826
+ data: {
827
+ id: 'EXISTING-DATA-001',
828
+ items: [
829
+ {
830
+ id: 'EXISTING-SINK-001',
831
+ target: { type: 'Event', name: 'TestEvent' },
832
+ destination: { type: 'stream', pattern: 'test-stream' },
833
+ },
834
+ {
835
+ id: 'EXISTING-SOURCE-001',
836
+ target: { type: 'State', name: 'TestState' },
837
+ origin: { type: 'projection', name: 'TestProjection' },
838
+ },
839
+ ],
840
+ },
830
841
  },
831
842
  },
832
843
  ],
@@ -841,8 +852,9 @@ describe('addAutoIds', () => {
841
852
  const slice = result.narratives[0].slices[0];
842
853
 
843
854
  if ('server' in slice && slice.server?.data) {
844
- expect(slice.server.data[0].id).toBe('EXISTING-SINK-001');
845
- expect(slice.server.data[1].id).toBe('EXISTING-SOURCE-001');
855
+ expect(slice.server.data.id).toBe('EXISTING-DATA-001');
856
+ expect(slice.server.data.items[0].id).toBe('EXISTING-SINK-001');
857
+ expect(slice.server.data.items[1].id).toBe('EXISTING-SOURCE-001');
846
858
  }
847
859
  });
848
860
 
@@ -862,12 +874,14 @@ describe('addAutoIds', () => {
862
874
  server: {
863
875
  description: 'Test server',
864
876
  specs: [],
865
- data: [
866
- {
867
- target: { type: 'Event', name: 'TestEvent' },
868
- destination: { type: 'stream', pattern: 'test-stream' },
869
- },
870
- ],
877
+ data: {
878
+ items: [
879
+ {
880
+ target: { type: 'Event', name: 'TestEvent' },
881
+ destination: { type: 'stream', pattern: 'test-stream' },
882
+ },
883
+ ],
884
+ },
871
885
  },
872
886
  },
873
887
  ],
@@ -882,7 +896,8 @@ describe('addAutoIds', () => {
882
896
  addAutoIds(model);
883
897
 
884
898
  if ('server' in originalSlice && originalSlice.server?.data) {
885
- expect(originalSlice.server.data[0].id).toBeUndefined();
899
+ expect(originalSlice.server.data.id).toBeUndefined();
900
+ expect(originalSlice.server.data.items[0].id).toBeUndefined();
886
901
  }
887
902
  });
888
903
 
@@ -900,20 +915,22 @@ describe('addAutoIds', () => {
900
915
  id: 'SLICE-001',
901
916
  server: {
902
917
  specs: [],
903
- data: [
904
- {
905
- target: { type: 'Event', name: 'Event1' },
906
- destination: { type: 'stream', pattern: 'stream1' },
907
- },
908
- {
909
- target: { type: 'Event', name: 'Event2' },
910
- destination: { type: 'stream', pattern: 'stream2' },
911
- },
912
- {
913
- target: { type: 'State', name: 'State1' },
914
- origin: { type: 'projection', name: 'Proj1' },
915
- },
916
- ],
918
+ data: {
919
+ items: [
920
+ {
921
+ target: { type: 'Event', name: 'Event1' },
922
+ destination: { type: 'stream', pattern: 'stream1' },
923
+ },
924
+ {
925
+ target: { type: 'Event', name: 'Event2' },
926
+ destination: { type: 'stream', pattern: 'stream2' },
927
+ },
928
+ {
929
+ target: { type: 'State', name: 'State1' },
930
+ origin: { type: 'projection', name: 'Proj1' },
931
+ },
932
+ ],
933
+ },
917
934
  },
918
935
  },
919
936
  ],
@@ -928,7 +945,8 @@ describe('addAutoIds', () => {
928
945
  const slice = result.narratives[0].slices[0];
929
946
 
930
947
  if ('server' in slice && slice.server?.data) {
931
- const ids = slice.server.data.map((d) => d.id);
948
+ expect(slice.server.data.id).toMatch(AUTO_ID_REGEX);
949
+ const ids = slice.server.data.items.map((d) => d.id);
932
950
  expect(ids[0]).toMatch(AUTO_ID_REGEX);
933
951
  expect(ids[1]).toMatch(AUTO_ID_REGEX);
934
952
  expect(ids[2]).toMatch(AUTO_ID_REGEX);
@@ -78,19 +78,25 @@ function processClientSpecs(slice: Slice): Slice {
78
78
  }
79
79
 
80
80
  function processDataItems(slice: Slice): Slice {
81
- if (!('server' in slice) || !slice.server?.data || !Array.isArray(slice.server.data)) return slice;
81
+ if (!('server' in slice) || !slice.server?.data) return slice;
82
82
 
83
83
  const modifiedSlice = structuredClone(slice);
84
- if ('server' in modifiedSlice && modifiedSlice.server?.data && Array.isArray(modifiedSlice.server.data)) {
85
- modifiedSlice.server.data = modifiedSlice.server.data.map((item) => {
86
- const itemCopy = { ...item };
87
- ensureId(itemCopy);
88
- if ('destination' in itemCopy && itemCopy._withState) {
89
- itemCopy._withState = { ...itemCopy._withState };
90
- ensureId(itemCopy._withState);
91
- }
92
- return itemCopy;
93
- });
84
+ if ('server' in modifiedSlice && modifiedSlice.server?.data) {
85
+ // Ensure the data wrapper has an ID
86
+ ensureId(modifiedSlice.server.data);
87
+
88
+ // Process items array if it exists
89
+ if (Array.isArray(modifiedSlice.server.data.items)) {
90
+ modifiedSlice.server.data.items = modifiedSlice.server.data.items.map((item) => {
91
+ const itemCopy = { ...item };
92
+ ensureId(itemCopy);
93
+ if ('destination' in itemCopy && itemCopy._withState) {
94
+ itemCopy._withState = { ...itemCopy._withState };
95
+ ensureId(itemCopy._withState);
96
+ }
97
+ return itemCopy;
98
+ });
99
+ }
94
100
  }
95
101
  return modifiedSlice;
96
102
  }
@@ -452,7 +452,7 @@ describe('hasAllIds', () => {
452
452
  });
453
453
 
454
454
  describe('data item ID validation', () => {
455
- it('should return false when data sink is missing an ID', () => {
455
+ it('should return false when data wrapper is missing an ID', () => {
456
456
  const model: Model = {
457
457
  variant: 'specs',
458
458
  narratives: [
@@ -468,13 +468,52 @@ describe('hasAllIds', () => {
468
468
  server: {
469
469
  description: 'Test server',
470
470
  specs: [],
471
- data: [
472
- {
473
- __type: 'sink',
474
- target: { type: 'Event', name: 'TestEvent' },
475
- destination: { type: 'stream', pattern: 'test-stream' },
476
- },
477
- ],
471
+ data: {
472
+ items: [
473
+ {
474
+ id: 'SINK-001',
475
+ target: { type: 'Event', name: 'TestEvent' },
476
+ destination: { type: 'stream', pattern: 'test-stream' },
477
+ },
478
+ ],
479
+ },
480
+ },
481
+ },
482
+ ],
483
+ },
484
+ ],
485
+ messages: [],
486
+ integrations: [],
487
+ modules: [],
488
+ };
489
+ expect(hasAllIds(model)).toBe(false);
490
+ });
491
+
492
+ it('should return false when data sink item is missing an ID', () => {
493
+ const model: Model = {
494
+ variant: 'specs',
495
+ narratives: [
496
+ {
497
+ name: 'Test Flow',
498
+ id: 'FLOW-001',
499
+ slices: [
500
+ {
501
+ type: 'command',
502
+ name: 'Test slice',
503
+ id: 'SLICE-001',
504
+ client: { specs: [] },
505
+ server: {
506
+ description: 'Test server',
507
+ specs: [],
508
+ data: {
509
+ id: 'DATA-001',
510
+ items: [
511
+ {
512
+ target: { type: 'Event', name: 'TestEvent' },
513
+ destination: { type: 'stream', pattern: 'test-stream' },
514
+ },
515
+ ],
516
+ },
478
517
  },
479
518
  },
480
519
  ],
@@ -487,7 +526,7 @@ describe('hasAllIds', () => {
487
526
  expect(hasAllIds(model)).toBe(false);
488
527
  });
489
528
 
490
- it('should return false when data source is missing an ID', () => {
529
+ it('should return false when data source item is missing an ID', () => {
491
530
  const model: Model = {
492
531
  variant: 'specs',
493
532
  narratives: [
@@ -503,13 +542,15 @@ describe('hasAllIds', () => {
503
542
  server: {
504
543
  description: 'Test server',
505
544
  specs: [],
506
- data: [
507
- {
508
- __type: 'source',
509
- target: { type: 'State', name: 'TestState' },
510
- origin: { type: 'projection', name: 'TestProjection' },
511
- },
512
- ],
545
+ data: {
546
+ id: 'DATA-001',
547
+ items: [
548
+ {
549
+ target: { type: 'State', name: 'TestState' },
550
+ origin: { type: 'projection', name: 'TestProjection' },
551
+ },
552
+ ],
553
+ },
513
554
  },
514
555
  },
515
556
  ],
@@ -538,18 +579,20 @@ describe('hasAllIds', () => {
538
579
  server: {
539
580
  description: 'Test server',
540
581
  specs: [],
541
- data: [
542
- {
543
- __type: 'sink',
544
- id: 'SINK-001',
545
- target: { type: 'Command', name: 'TestCommand' },
546
- destination: { type: 'stream', pattern: 'test-stream' },
547
- _withState: {
548
- target: { type: 'State', name: 'TestState' },
549
- origin: { type: 'projection', name: 'TestProjection' },
582
+ data: {
583
+ id: 'DATA-001',
584
+ items: [
585
+ {
586
+ id: 'SINK-001',
587
+ target: { type: 'Command', name: 'TestCommand' },
588
+ destination: { type: 'stream', pattern: 'test-stream' },
589
+ _withState: {
590
+ target: { type: 'State', name: 'TestState' },
591
+ origin: { type: 'projection', name: 'TestProjection' },
592
+ },
550
593
  },
551
- },
552
- ],
594
+ ],
595
+ },
553
596
  },
554
597
  },
555
598
  ],
@@ -578,20 +621,21 @@ describe('hasAllIds', () => {
578
621
  server: {
579
622
  description: 'Test server',
580
623
  specs: [],
581
- data: [
582
- {
583
- __type: 'sink',
584
- id: 'SINK-001',
585
- target: { type: 'Event', name: 'TestEvent' },
586
- destination: { type: 'stream', pattern: 'test-stream' },
587
- },
588
- {
589
- __type: 'source',
590
- id: 'SOURCE-001',
591
- target: { type: 'State', name: 'TestState' },
592
- origin: { type: 'projection', name: 'TestProjection' },
593
- },
594
- ],
624
+ data: {
625
+ id: 'DATA-001',
626
+ items: [
627
+ {
628
+ id: 'SINK-001',
629
+ target: { type: 'Event', name: 'TestEvent' },
630
+ destination: { type: 'stream', pattern: 'test-stream' },
631
+ },
632
+ {
633
+ id: 'SOURCE-001',
634
+ target: { type: 'State', name: 'TestState' },
635
+ origin: { type: 'projection', name: 'TestProjection' },
636
+ },
637
+ ],
638
+ },
595
639
  },
596
640
  },
597
641
  ],
@@ -620,19 +664,21 @@ describe('hasAllIds', () => {
620
664
  server: {
621
665
  description: 'Test server',
622
666
  specs: [],
623
- data: [
624
- {
625
- __type: 'sink',
626
- id: 'SINK-001',
627
- target: { type: 'Command', name: 'TestCommand' },
628
- destination: { type: 'stream', pattern: 'test-stream' },
629
- _withState: {
630
- id: 'SOURCE-001',
631
- target: { type: 'State', name: 'TestState' },
632
- origin: { type: 'projection', name: 'TestProjection' },
667
+ data: {
668
+ id: 'DATA-001',
669
+ items: [
670
+ {
671
+ id: 'SINK-001',
672
+ target: { type: 'Command', name: 'TestCommand' },
673
+ destination: { type: 'stream', pattern: 'test-stream' },
674
+ _withState: {
675
+ id: 'SOURCE-001',
676
+ target: { type: 'State', name: 'TestState' },
677
+ origin: { type: 'projection', name: 'TestProjection' },
678
+ },
633
679
  },
634
- },
635
- ],
680
+ ],
681
+ },
636
682
  },
637
683
  },
638
684
  ],
@@ -645,7 +691,7 @@ describe('hasAllIds', () => {
645
691
  expect(hasAllIds(model)).toBe(true);
646
692
  });
647
693
 
648
- it('should return true when slice has no data array', () => {
694
+ it('should return true when slice has no data', () => {
649
695
  const model: Model = {
650
696
  variant: 'specs',
651
697
  narratives: [