@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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +14 -0
- package/dist/src/id/addAutoIds.d.ts.map +1 -1
- package/dist/src/id/addAutoIds.js +16 -11
- package/dist/src/id/addAutoIds.js.map +1 -1
- package/dist/src/id/hasAllIds.d.ts.map +1 -1
- package/dist/src/id/hasAllIds.js +8 -2
- package/dist/src/id/hasAllIds.js.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/narrative-context.d.ts +2 -2
- package/dist/src/narrative-context.d.ts.map +1 -1
- package/dist/src/narrative-context.js +18 -15
- package/dist/src/narrative-context.js.map +1 -1
- package/dist/src/narrative.d.ts +2 -2
- package/dist/src/narrative.d.ts.map +1 -1
- package/dist/src/narrative.js +6 -3
- package/dist/src/narrative.js.map +1 -1
- package/dist/src/samples/seasonal-assistant.schema.json +73 -65
- package/dist/src/schema.d.ts +13937 -7286
- package/dist/src/schema.d.ts.map +1 -1
- package/dist/src/schema.js +9 -6
- package/dist/src/schema.js.map +1 -1
- package/dist/src/transformers/model-to-narrative/generators/flow.d.ts.map +1 -1
- package/dist/src/transformers/model-to-narrative/generators/flow.js +10 -4
- package/dist/src/transformers/model-to-narrative/generators/flow.js.map +1 -1
- package/dist/src/transformers/narrative-to-model/index.d.ts.map +1 -1
- package/dist/src/transformers/narrative-to-model/index.js +6 -3
- package/dist/src/transformers/narrative-to-model/index.js.map +1 -1
- package/dist/src/types.d.ts +4 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/getNarratives.specs.ts +30 -30
- package/src/id/addAutoIds.specs.ts +79 -61
- package/src/id/addAutoIds.ts +17 -11
- package/src/id/hasAllIds.specs.ts +100 -54
- package/src/id/hasAllIds.ts +8 -2
- package/src/index.ts +2 -1
- package/src/model-to-narrative.specs.ts +206 -158
- package/src/narrative-context.ts +22 -16
- package/src/narrative.ts +8 -4
- package/src/samples/seasonal-assistant.schema.json +73 -65
- package/src/schema.ts +10 -6
- package/src/transformers/model-to-narrative/generators/flow.ts +14 -5
- package/src/transformers/narrative-to-model/index.ts +6 -3
- package/src/types.ts +5 -0
package/src/id/hasAllIds.ts
CHANGED
|
@@ -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
|
|
44
|
+
if (!('server' in slice) || !slice.server?.data) return true;
|
|
45
45
|
|
|
46
|
-
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
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(
|
|
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
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
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
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
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
|
-
|
|
1627
|
-
|
|
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
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
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
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
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
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
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(
|
|
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
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
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
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
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
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
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(
|
|
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
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
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
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
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
|
-
|
|
3048
|
-
|
|
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
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
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
|
},
|
package/src/narrative-context.ts
CHANGED
|
@@ -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,
|
|
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:
|
|
284
|
+
export function setSliceData(data: Data): void {
|
|
285
285
|
const slice = getCurrentSlice();
|
|
286
286
|
if (!slice) throw new Error('No active slice');
|
|
287
|
-
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
slice.server.data =
|
|
293
|
-
|
|
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
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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
|
|