@auto-engineer/narrative 0.21.2 → 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 (49) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +14 -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
@@ -41,9 +41,15 @@ function hasClientSpecIds(slice: Slice): boolean {
41
41
  }
42
42
 
43
43
  function hasDataIds(slice: Slice): boolean {
44
- if (!('server' in slice) || !slice.server?.data || !Array.isArray(slice.server.data)) return true;
44
+ if (!('server' in slice) || !slice.server?.data) return true;
45
45
 
46
- return slice.server.data.every((item) => {
46
+ // Validate the data wrapper has an ID
47
+ if (!hasValidId(slice.server.data)) return false;
48
+
49
+ // Validate each item in the items array
50
+ if (!Array.isArray(slice.server.data.items)) return true;
51
+
52
+ return slice.server.data.items.every((item) => {
47
53
  if (!hasValidId(item)) return false;
48
54
  if ('destination' in item && item._withState) {
49
55
  return hasValidId(item._withState);
package/src/index.ts CHANGED
@@ -2,10 +2,11 @@ import type { z } from 'zod';
2
2
 
3
3
  // Apollo GraphQL
4
4
  export { gql } from 'graphql-tag';
5
- export { DataSinkSchema, DataSourceSchema, MessageTargetSchema } from './schema';
5
+ export { DataSchema, DataSinkSchema, DataSourceSchema, MessageTargetSchema } from './schema';
6
6
  // Core types and utilities
7
7
  export type {
8
8
  Command,
9
+ Data,
9
10
  DataItem,
10
11
  DataSink,
11
12
  DataSinkItem,
@@ -29,7 +29,6 @@ describe('modelToNarrative', () => {
29
29
  } from '@auto-engineer/narrative';
30
30
  import type { Command, Event, State } from '@auto-engineer/narrative';
31
31
  import { AI, ProductCatalog } from '../server/src/integrations';
32
- import type { Products } from '../server/src/integrations';
33
32
  type EnterShoppingCriteria = Command<
34
33
  'EnterShoppingCriteria',
35
34
  {
@@ -51,6 +50,19 @@ type SuggestShoppingItems = Command<
51
50
  prompt: string;
52
51
  }
53
52
  >;
53
+ type Products = State<
54
+ 'Products',
55
+ {
56
+ products: {
57
+ productId: string;
58
+ name: string;
59
+ category: string;
60
+ price: number;
61
+ tags: Array<string>;
62
+ imageUrl: string;
63
+ }[];
64
+ }
65
+ >;
54
66
  type ShoppingItemsSuggested = Event<
55
67
  'ShoppingItemsSuggested',
56
68
  {
@@ -119,7 +131,7 @@ narrative('Seasonal Assistant', () => {
119
131
  }\`),
120
132
  )
121
133
  .server(() => {
122
- data([sink().event('ShoppingCriteriaEntered').toStream('shopping-session-\${sessionId}')]);
134
+ data({ items: [sink().event('ShoppingCriteriaEntered').toStream('shopping-session-\${sessionId}')] });
123
135
  specs('When shopper submits criteria, a shopping session is started', () => {
124
136
  rule('Valid criteria should start a shopping session', () => {
125
137
  example('User submits shopping criteria for children')
@@ -154,16 +166,18 @@ narrative('Seasonal Assistant', () => {
154
166
  });
155
167
  });
156
168
  command('selects items relevant to the shopping criteria').server(() => {
157
- data([
158
- sink()
159
- .command('SuggestShoppingItems')
160
- .toIntegration(AI, 'DoChat', 'command')
161
- .withState(source().state('Products').fromIntegration(ProductCatalog))
162
- .additionalInstructions(
163
- 'add the following to the DoChat: schemaName: Products, systemPrompt: use the PRODUCT_CATALOGUE_PRODUCTS MCP tool to get product data',
164
- ),
165
- sink().event('ShoppingItemsSuggested').toStream('shopping-session-\${sessionId}'),
166
- ]);
169
+ data({
170
+ items: [
171
+ sink()
172
+ .command('SuggestShoppingItems')
173
+ .toIntegration(AI, 'DoChat', 'command')
174
+ .withState(source().state('Products').fromIntegration(ProductCatalog))
175
+ .additionalInstructions(
176
+ 'add the following to the DoChat: schemaName: Products, systemPrompt: use the PRODUCT_CATALOGUE_PRODUCTS MCP tool to get product data',
177
+ ),
178
+ sink().event('ShoppingItemsSuggested').toStream('shopping-session-\${sessionId}'),
179
+ ],
180
+ });
167
181
  specs('When chat is triggered, AI suggests items based on product catalog', () => {
168
182
  rule('AI should suggest relevant items from available products', () => {
169
183
  example('Product catalog with matching items generates suggestions')
@@ -262,7 +276,7 @@ narrative('Seasonal Assistant', () => {
262
276
  }\`),
263
277
  )
264
278
  .server(() => {
265
- data([source().state('SuggestedItems').fromProjection('SuggestedItemsProjection', 'sessionId')]);
279
+ data({ items: [source().state('SuggestedItems').fromProjection('SuggestedItemsProjection', 'sessionId')] });
266
280
  specs('Suggested items are available for viewing', () => {
267
281
  rule('Items should be available for viewing after suggestion', () => {
268
282
  example('Item becomes available after AI suggestion event')
@@ -336,7 +350,7 @@ narrative('Seasonal Assistant', () => {
336
350
  });
337
351
  })
338
352
  .server(() => {
339
- data([sink().event('ItemsAddedToCart').toStream('shopping-session-\${sessionId}')]);
353
+ data({ items: [sink().event('ItemsAddedToCart').toStream('shopping-session-\${sessionId}')] });
340
354
  specs('When shopper accepts items, they are added to cart', () => {
341
355
  rule('Accepted items should be added to the shopping cart', () => {
342
356
  example('User selects all suggested items for cart')
@@ -753,19 +767,21 @@ narrative('Questionnaire Flow', 'QUEST-001', () => {});
753
767
  'query QuestionnaireProgress($participantId: ID!) {\n questionnaireProgress(participantId: $participantId) {\n questionnaireId\n participantId\n currentQuestionId\n remainingQuestions\n status\n answers {\n questionId\n value\n }\n }\n}',
754
768
  server: {
755
769
  description: '',
756
- data: [
757
- {
758
- target: {
759
- type: 'State',
760
- name: 'QuestionnaireProgress',
761
- },
762
- origin: {
763
- type: 'projection',
764
- name: 'Questionnaires',
765
- idField: 'questionnaire-participantId',
770
+ data: {
771
+ items: [
772
+ {
773
+ target: {
774
+ type: 'State',
775
+ name: 'QuestionnaireProgress',
776
+ },
777
+ origin: {
778
+ type: 'projection',
779
+ name: 'Questionnaires',
780
+ idField: 'questionnaire-participantId',
781
+ },
766
782
  },
767
- },
768
- ],
783
+ ],
784
+ },
769
785
  specs: [
770
786
  {
771
787
  type: 'gherkin',
@@ -1018,7 +1034,11 @@ narrative('Questionnaires', 'Q9m2Kp4Lx', () => {
1018
1034
  }\`),
1019
1035
  )
1020
1036
  .server(() => {
1021
- data([source().state('QuestionnaireProgress').fromProjection('Questionnaires', 'questionnaire-participantId')]);
1037
+ data({
1038
+ items: [
1039
+ source().state('QuestionnaireProgress').fromProjection('Questionnaires', 'questionnaire-participantId'),
1040
+ ],
1041
+ });
1022
1042
  specs(() => {
1023
1043
  rule('questionnaires show current progress', 'r1A3Bp9W', () => {
1024
1044
  example('a question has already been answered')
@@ -1497,30 +1517,32 @@ narrative('Multi Given Flow', 'MULTI-GIVEN', () => {
1497
1517
  },
1498
1518
  server: {
1499
1519
  description: 'Server for referenced states',
1500
- data: [
1501
- {
1502
- target: {
1503
- type: 'State',
1504
- name: 'QuestionnaireProgress',
1505
- },
1506
- origin: {
1507
- type: 'projection',
1508
- name: 'QuestionnaireProjection',
1509
- idField: 'participantId',
1510
- },
1511
- },
1512
- {
1513
- target: {
1514
- type: 'State',
1515
- name: 'QuestionnaireConfig',
1520
+ data: {
1521
+ items: [
1522
+ {
1523
+ target: {
1524
+ type: 'State',
1525
+ name: 'QuestionnaireProgress',
1526
+ },
1527
+ origin: {
1528
+ type: 'projection',
1529
+ name: 'QuestionnaireProjection',
1530
+ idField: 'participantId',
1531
+ },
1516
1532
  },
1517
- origin: {
1518
- type: 'database',
1519
- collection: 'ConfigStore',
1520
- query: { questionnaireId: '$questionnaireId' },
1533
+ {
1534
+ target: {
1535
+ type: 'State',
1536
+ name: 'QuestionnaireConfig',
1537
+ },
1538
+ origin: {
1539
+ type: 'database',
1540
+ collection: 'ConfigStore',
1541
+ query: { questionnaireId: '$questionnaireId' },
1542
+ },
1521
1543
  },
1522
- },
1523
- ],
1544
+ ],
1545
+ },
1524
1546
  specs: [
1525
1547
  {
1526
1548
  type: 'gherkin',
@@ -1622,10 +1644,12 @@ type QuestionnaireConfig = State<
1622
1644
  >;
1623
1645
  narrative('Referenced States Flow', 'REF-STATES', () => {
1624
1646
  query('query with database states', 'REF-SLICE').server(() => {
1625
- data([
1626
- source().state('QuestionnaireProgress').fromProjection('QuestionnaireProjection', 'participantId'),
1627
- source().state('QuestionnaireConfig').fromDatabase('ConfigStore', { questionnaireId: '$questionnaireId' }),
1628
- ]);
1647
+ data({
1648
+ items: [
1649
+ source().state('QuestionnaireProgress').fromProjection('QuestionnaireProjection', 'participantId'),
1650
+ source().state('QuestionnaireConfig').fromDatabase('ConfigStore', { questionnaireId: '$questionnaireId' }),
1651
+ ],
1652
+ });
1629
1653
  specs('Database State Rules', () => {
1630
1654
  rule('questionnaire config is available when referenced', 'RefState', () => {
1631
1655
  example('config from database is accessible')
@@ -2112,19 +2136,21 @@ narrative('Todo List Summary', 'TODO-001', () => {
2112
2136
  },
2113
2137
  server: {
2114
2138
  description: 'Summary server',
2115
- data: [
2116
- {
2117
- target: {
2118
- type: 'State',
2119
- name: 'TodoListSummary',
2120
- },
2121
- origin: {
2122
- type: 'projection',
2123
- name: 'TodoSummary',
2124
- singleton: true,
2139
+ data: {
2140
+ items: [
2141
+ {
2142
+ target: {
2143
+ type: 'State',
2144
+ name: 'TodoListSummary',
2145
+ },
2146
+ origin: {
2147
+ type: 'projection',
2148
+ name: 'TodoSummary',
2149
+ singleton: true,
2150
+ },
2125
2151
  },
2126
- },
2127
- ],
2152
+ ],
2153
+ },
2128
2154
  specs: [
2129
2155
  {
2130
2156
  type: 'gherkin',
@@ -2165,7 +2191,7 @@ type TodoListSummary = State<
2165
2191
  >;
2166
2192
  narrative('Todo Summary Flow', 'TODO-SUMMARY', () => {
2167
2193
  query('views todo summary', 'SUMMARY-SLICE').server(() => {
2168
- data([source().state('TodoListSummary').fromSingletonProjection('TodoSummary')]);
2194
+ data({ items: [source().state('TodoListSummary').fromSingletonProjection('TodoSummary')] });
2169
2195
  specs('Summary Rules', () => {});
2170
2196
  });
2171
2197
  });
@@ -2189,19 +2215,21 @@ narrative('Todo Summary Flow', 'TODO-SUMMARY', () => {
2189
2215
  },
2190
2216
  server: {
2191
2217
  description: 'Todo server',
2192
- data: [
2193
- {
2194
- target: {
2195
- type: 'State',
2196
- name: 'TodoState',
2197
- },
2198
- origin: {
2199
- type: 'projection',
2200
- name: 'Todos',
2201
- idField: 'todoId',
2218
+ data: {
2219
+ items: [
2220
+ {
2221
+ target: {
2222
+ type: 'State',
2223
+ name: 'TodoState',
2224
+ },
2225
+ origin: {
2226
+ type: 'projection',
2227
+ name: 'Todos',
2228
+ idField: 'todoId',
2229
+ },
2202
2230
  },
2203
- },
2204
- ],
2231
+ ],
2232
+ },
2205
2233
  specs: [
2206
2234
  {
2207
2235
  type: 'gherkin',
@@ -2242,7 +2270,7 @@ type TodoState = State<
2242
2270
  >;
2243
2271
  narrative('Todo Flow', 'TODO-FLOW', () => {
2244
2272
  query('views todo', 'TODO-SLICE').server(() => {
2245
- data([source().state('TodoState').fromProjection('Todos', 'todoId')]);
2273
+ data({ items: [source().state('TodoState').fromProjection('Todos', 'todoId')] });
2246
2274
  specs('Todo Rules', () => {});
2247
2275
  });
2248
2276
  });
@@ -2266,19 +2294,21 @@ narrative('Todo Flow', 'TODO-FLOW', () => {
2266
2294
  },
2267
2295
  server: {
2268
2296
  description: 'User project server',
2269
- data: [
2270
- {
2271
- target: {
2272
- type: 'State',
2273
- name: 'UserProjectState',
2274
- },
2275
- origin: {
2276
- type: 'projection',
2277
- name: 'UserProjects',
2278
- idField: ['userId', 'projectId'],
2297
+ data: {
2298
+ items: [
2299
+ {
2300
+ target: {
2301
+ type: 'State',
2302
+ name: 'UserProjectState',
2303
+ },
2304
+ origin: {
2305
+ type: 'projection',
2306
+ name: 'UserProjects',
2307
+ idField: ['userId', 'projectId'],
2308
+ },
2279
2309
  },
2280
- },
2281
- ],
2310
+ ],
2311
+ },
2282
2312
  specs: [
2283
2313
  {
2284
2314
  type: 'gherkin',
@@ -2321,7 +2351,9 @@ type UserProjectState = State<
2321
2351
  >;
2322
2352
  narrative('User Project Flow', 'USER-PROJECT-FLOW', () => {
2323
2353
  query('views user project', 'USER-PROJECT-SLICE').server(() => {
2324
- data([source().state('UserProjectState').fromCompositeProjection('UserProjects', ['userId', 'projectId'])]);
2354
+ data({
2355
+ items: [source().state('UserProjectState').fromCompositeProjection('UserProjects', ['userId', 'projectId'])],
2356
+ });
2325
2357
  specs('User Project Rules', () => {});
2326
2358
  });
2327
2359
  });
@@ -2345,19 +2377,21 @@ narrative('User Project Flow', 'USER-PROJECT-FLOW', () => {
2345
2377
  },
2346
2378
  server: {
2347
2379
  description: 'Summary server',
2348
- data: [
2349
- {
2350
- target: {
2351
- type: 'State',
2352
- name: 'TodoListSummary',
2353
- },
2354
- origin: {
2355
- type: 'projection',
2356
- name: 'TodoSummary',
2357
- singleton: true,
2380
+ data: {
2381
+ items: [
2382
+ {
2383
+ target: {
2384
+ type: 'State',
2385
+ name: 'TodoListSummary',
2386
+ },
2387
+ origin: {
2388
+ type: 'projection',
2389
+ name: 'TodoSummary',
2390
+ singleton: true,
2391
+ },
2358
2392
  },
2359
- },
2360
- ],
2393
+ ],
2394
+ },
2361
2395
  specs: [
2362
2396
  {
2363
2397
  type: 'gherkin',
@@ -2376,19 +2410,21 @@ narrative('User Project Flow', 'USER-PROJECT-FLOW', () => {
2376
2410
  },
2377
2411
  server: {
2378
2412
  description: 'Todo server',
2379
- data: [
2380
- {
2381
- target: {
2382
- type: 'State',
2383
- name: 'TodoState',
2384
- },
2385
- origin: {
2386
- type: 'projection',
2387
- name: 'Todos',
2388
- idField: 'todoId',
2413
+ data: {
2414
+ items: [
2415
+ {
2416
+ target: {
2417
+ type: 'State',
2418
+ name: 'TodoState',
2419
+ },
2420
+ origin: {
2421
+ type: 'projection',
2422
+ name: 'Todos',
2423
+ idField: 'todoId',
2424
+ },
2389
2425
  },
2390
- },
2391
- ],
2426
+ ],
2427
+ },
2392
2428
  specs: [
2393
2429
  {
2394
2430
  type: 'gherkin',
@@ -2407,19 +2443,21 @@ narrative('User Project Flow', 'USER-PROJECT-FLOW', () => {
2407
2443
  },
2408
2444
  server: {
2409
2445
  description: 'User project server',
2410
- data: [
2411
- {
2412
- target: {
2413
- type: 'State',
2414
- name: 'UserProjectTodos',
2415
- },
2416
- origin: {
2417
- type: 'projection',
2418
- name: 'UserProjectTodos',
2419
- idField: ['userId', 'projectId'],
2446
+ data: {
2447
+ items: [
2448
+ {
2449
+ target: {
2450
+ type: 'State',
2451
+ name: 'UserProjectTodos',
2452
+ },
2453
+ origin: {
2454
+ type: 'projection',
2455
+ name: 'UserProjectTodos',
2456
+ idField: ['userId', 'projectId'],
2457
+ },
2420
2458
  },
2421
- },
2422
- ],
2459
+ ],
2460
+ },
2423
2461
  specs: [
2424
2462
  {
2425
2463
  type: 'gherkin',
@@ -2494,15 +2532,17 @@ type UserProjectTodos = State<
2494
2532
  >;
2495
2533
  narrative('All Projection Types', 'ALL-PROJ', () => {
2496
2534
  query('views summary', 'SUMMARY-SLICE').server(() => {
2497
- data([source().state('TodoListSummary').fromSingletonProjection('TodoSummary')]);
2535
+ data({ items: [source().state('TodoListSummary').fromSingletonProjection('TodoSummary')] });
2498
2536
  specs('Summary Rules', () => {});
2499
2537
  });
2500
2538
  query('views todo', 'TODO-SLICE').server(() => {
2501
- data([source().state('TodoState').fromProjection('Todos', 'todoId')]);
2539
+ data({ items: [source().state('TodoState').fromProjection('Todos', 'todoId')] });
2502
2540
  specs('Todo Rules', () => {});
2503
2541
  });
2504
2542
  query('views user project todos', 'USER-PROJECT-SLICE').server(() => {
2505
- data([source().state('UserProjectTodos').fromCompositeProjection('UserProjectTodos', ['userId', 'projectId'])]);
2543
+ data({
2544
+ items: [source().state('UserProjectTodos').fromCompositeProjection('UserProjectTodos', ['userId', 'projectId'])],
2545
+ });
2506
2546
  specs('User Project Rules', () => {});
2507
2547
  });
2508
2548
  });
@@ -2955,13 +2995,15 @@ narrative('All Projection Types', 'ALL-PROJ', () => {
2955
2995
  client: { specs: [] },
2956
2996
  server: {
2957
2997
  description: 'Order server',
2958
- data: [
2959
- {
2960
- id: 'SINK-001',
2961
- target: { type: 'Event', name: 'OrderPlaced' },
2962
- destination: { type: 'stream', pattern: 'orders-stream' },
2963
- },
2964
- ],
2998
+ data: {
2999
+ items: [
3000
+ {
3001
+ id: 'SINK-001',
3002
+ target: { type: 'Event', name: 'OrderPlaced' },
3003
+ destination: { type: 'stream', pattern: 'orders-stream' },
3004
+ },
3005
+ ],
3006
+ },
2965
3007
  specs: [],
2966
3008
  },
2967
3009
  },
@@ -2999,13 +3041,15 @@ narrative('All Projection Types', 'ALL-PROJ', () => {
2999
3041
  client: { specs: [] },
3000
3042
  server: {
3001
3043
  description: 'Order server',
3002
- data: [
3003
- {
3004
- id: 'SOURCE-001',
3005
- target: { type: 'State', name: 'OrderState' },
3006
- origin: { type: 'projection', name: 'Orders', idField: 'orderId' },
3007
- },
3008
- ],
3044
+ data: {
3045
+ items: [
3046
+ {
3047
+ id: 'SOURCE-001',
3048
+ target: { type: 'State', name: 'OrderState' },
3049
+ origin: { type: 'projection', name: 'Orders', idField: 'orderId' },
3050
+ },
3051
+ ],
3052
+ },
3009
3053
  specs: [],
3010
3054
  },
3011
3055
  },
@@ -3042,12 +3086,14 @@ narrative('All Projection Types', 'ALL-PROJ', () => {
3042
3086
  client: { specs: [] },
3043
3087
  server: {
3044
3088
  description: 'Order server',
3045
- data: [
3046
- {
3047
- target: { type: 'Event', name: 'OrderPlaced' },
3048
- destination: { type: 'stream', pattern: 'orders-stream' },
3049
- },
3050
- ],
3089
+ data: {
3090
+ items: [
3091
+ {
3092
+ target: { type: 'Event', name: 'OrderPlaced' },
3093
+ destination: { type: 'stream', pattern: 'orders-stream' },
3094
+ },
3095
+ ],
3096
+ },
3051
3097
  specs: [],
3052
3098
  },
3053
3099
  },
@@ -3086,13 +3132,15 @@ narrative('All Projection Types', 'ALL-PROJ', () => {
3086
3132
  client: { specs: [] },
3087
3133
  server: {
3088
3134
  description: 'Order server',
3089
- data: [
3090
- {
3091
- target: { type: 'State', name: 'OrderState' },
3092
- origin: { type: 'projection', name: 'Orders', idField: 'orderId' },
3093
- _additionalInstructions: 'Filter by active orders only',
3094
- },
3095
- ],
3135
+ data: {
3136
+ items: [
3137
+ {
3138
+ target: { type: 'State', name: 'OrderState' },
3139
+ origin: { type: 'projection', name: 'Orders', idField: 'orderId' },
3140
+ _additionalInstructions: 'Filter by active orders only',
3141
+ },
3142
+ ],
3143
+ },
3096
3144
  specs: [],
3097
3145
  },
3098
3146
  },
@@ -3,7 +3,7 @@ import type { z } from 'zod';
3
3
  import type { CommandSlice, ExperienceSlice, Narrative, QuerySlice, Slice } from './index';
4
4
  import type { GivenTypeInfo } from './loader/ts-utils';
5
5
  import type { ClientSpecNode, ExampleSchema, RuleSchema, SpecSchema, StepSchema } from './schema';
6
- import type { DataItem, DataSink, DataSinkItem, DataSource, DataSourceItem } from './types';
6
+ import type { Data, DataItem, DataSink, DataSource } from './types';
7
7
 
8
8
  type Step = z.infer<typeof StepSchema>;
9
9
  type Example = z.infer<typeof ExampleSchema>;
@@ -281,28 +281,34 @@ export function setQueryRequest(request: string): void {
281
281
  slice.request = request;
282
282
  }
283
283
 
284
- export function setSliceData(data: DataItem[]): void {
284
+ export function setSliceData(data: Data): void {
285
285
  const slice = getCurrentSlice();
286
286
  if (!slice) throw new Error('No active slice');
287
- const sinks = data.filter((item): item is DataSinkItem => item.__type === 'sink');
288
- const sources = data.filter((item): item is DataSourceItem => item.__type === 'source');
287
+ if (!('server' in slice)) throw new Error('Data can only be set on slices with a server');
289
288
 
290
- if (slice.type === 'command') {
291
- // Command slices only have sinks in their data
292
- slice.server.data = sinks.length > 0 ? sinks : undefined;
293
- } else if (slice.type === 'query') {
294
- // Query slices only have sources in their data
295
- slice.server.data = sources.length > 0 ? sources : undefined;
296
- } else if (slice.type === 'react') {
297
- slice.server.data = data.length > 0 ? stripTypeDiscriminator(data) : undefined;
289
+ const items = data.items;
290
+ if (items.length === 0) {
291
+ slice.server.data = undefined;
292
+ return;
298
293
  }
294
+
295
+ // Strip __type discriminator (if present) and build the data object
296
+ const strippedItems = stripTypeDiscriminator(items);
297
+
298
+ slice.server.data = {
299
+ ...(data.id != null && data.id !== '' && { id: data.id }),
300
+ items: strippedItems,
301
+ };
299
302
  }
300
303
 
301
- function stripTypeDiscriminator(items: DataItem[]): (DataSink | DataSource)[] {
304
+ function stripTypeDiscriminator(items: (DataSink | DataSource | DataItem)[]): (DataSink | DataSource)[] {
302
305
  return items.map((item) => {
303
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
304
- const { __type, ...rest } = item;
305
- return rest as DataSink | DataSource;
306
+ if ('__type' in item) {
307
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
308
+ const { __type, ...rest } = item;
309
+ return rest as DataSink | DataSource;
310
+ }
311
+ return item as DataSink | DataSource;
306
312
  });
307
313
  }
308
314