@auto-engineer/narrative 1.3.4 → 1.4.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 +6 -6
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +16 -0
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/loader/ts-utils.d.ts +2 -2
- package/dist/src/loader/ts-utils.d.ts.map +1 -1
- package/dist/src/loader/ts-utils.js +7 -1
- package/dist/src/loader/ts-utils.js.map +1 -1
- package/dist/src/schema.d.ts +226 -20
- package/dist/src/schema.d.ts.map +1 -1
- package/dist/src/schema.js +6 -3
- package/dist/src/schema.js.map +1 -1
- package/dist/src/transformers/model-to-narrative/generators/imports.d.ts.map +1 -1
- package/dist/src/transformers/model-to-narrative/generators/imports.js +1 -0
- package/dist/src/transformers/model-to-narrative/generators/imports.js.map +1 -1
- package/dist/src/transformers/model-to-narrative/generators/types.d.ts +1 -1
- package/dist/src/transformers/model-to-narrative/generators/types.d.ts.map +1 -1
- package/dist/src/transformers/model-to-narrative/generators/types.js +2 -1
- package/dist/src/transformers/model-to-narrative/generators/types.js.map +1 -1
- package/dist/src/transformers/narrative-to-model/index.js.map +1 -1
- package/dist/src/transformers/narrative-to-model/messages.d.ts +1 -1
- package/dist/src/transformers/narrative-to-model/messages.d.ts.map +1 -1
- package/dist/src/transformers/narrative-to-model/messages.js +9 -1
- package/dist/src/transformers/narrative-to-model/messages.js.map +1 -1
- package/dist/src/transformers/narrative-to-model/spec-processors.d.ts +22 -1
- package/dist/src/transformers/narrative-to-model/spec-processors.d.ts.map +1 -1
- package/dist/src/transformers/narrative-to-model/spec-processors.js +81 -0
- package/dist/src/transformers/narrative-to-model/spec-processors.js.map +1 -1
- package/dist/src/transformers/narrative-to-model/type-inference.d.ts +1 -1
- package/dist/src/transformers/narrative-to-model/type-inference.d.ts.map +1 -1
- package/dist/src/transformers/narrative-to-model/type-inference.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/index.ts +4 -0
- package/src/loader/ts-utils.ts +16 -10
- package/src/model-to-narrative.specs.ts +53 -0
- package/src/schema.ts +7 -2
- package/src/transformers/model-to-narrative/generators/imports.ts +1 -0
- package/src/transformers/model-to-narrative/generators/types.ts +4 -5
- package/src/transformers/narrative-to-model/index.ts +5 -5
- package/src/transformers/narrative-to-model/messages.ts +12 -3
- package/src/transformers/narrative-to-model/spec-processors.specs.ts +241 -0
- package/src/transformers/narrative-to-model/spec-processors.ts +91 -4
- package/src/transformers/narrative-to-model/type-inference.ts +4 -4
- package/src/types.ts +5 -0
|
@@ -7,9 +7,62 @@ import { preferNewFields } from './normalize';
|
|
|
7
7
|
|
|
8
8
|
const log = debug('auto:flow:spec-processors');
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Extracts the query name from a GraphQL request string.
|
|
12
|
+
* Supports both SDL strings and JSON-serialized AST.
|
|
13
|
+
*/
|
|
14
|
+
export function extractQueryNameFromRequest(request: string | undefined): string | null {
|
|
15
|
+
if (!request) return null;
|
|
16
|
+
const queryMatch = request.match(/query\s+(\w+)/i);
|
|
17
|
+
if (queryMatch) {
|
|
18
|
+
return queryMatch[1];
|
|
19
|
+
}
|
|
20
|
+
if (request.startsWith('{') && request.includes('"kind"')) {
|
|
21
|
+
try {
|
|
22
|
+
const ast = JSON.parse(request) as unknown;
|
|
23
|
+
if (
|
|
24
|
+
typeof ast === 'object' &&
|
|
25
|
+
ast !== null &&
|
|
26
|
+
'definitions' in ast &&
|
|
27
|
+
Array.isArray((ast as { definitions: unknown[] }).definitions)
|
|
28
|
+
) {
|
|
29
|
+
const definitions = (ast as { definitions: Array<{ kind?: string; name?: { value?: string } }> }).definitions;
|
|
30
|
+
const opDef = definitions.find((d) => d.kind === 'OperationDefinition');
|
|
31
|
+
if (opDef?.name?.value) {
|
|
32
|
+
return opDef.name.value;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
} catch {}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Detects if the "When" text in a query slice represents a query action (query name)
|
|
43
|
+
* rather than an event name.
|
|
44
|
+
*
|
|
45
|
+
* Detection is based solely on matching the query name extracted from slice.request.
|
|
46
|
+
* If no query name can be extracted, returns false (treats as event - safe default).
|
|
47
|
+
*
|
|
48
|
+
* @param whenText - The text from the "When" step
|
|
49
|
+
* @param slice - The slice object containing type and optional request field
|
|
50
|
+
* @returns true if the "When" text matches the query name from slice.request
|
|
51
|
+
*/
|
|
52
|
+
export function detectQueryAction(whenText: string, slice: { type: string; request?: string }): boolean {
|
|
53
|
+
if (slice.type !== 'query') return false;
|
|
54
|
+
if (!whenText) return false;
|
|
55
|
+
|
|
56
|
+
const queryName = extractQueryNameFromRequest(slice.request);
|
|
57
|
+
if (!queryName) return false;
|
|
58
|
+
|
|
59
|
+
// Exact match or case-insensitive match
|
|
60
|
+
return whenText === queryName || whenText.toLowerCase() === queryName.toLowerCase();
|
|
61
|
+
}
|
|
62
|
+
|
|
10
63
|
type TypeResolver = (
|
|
11
64
|
t: string,
|
|
12
|
-
expected?: 'command' | 'event' | 'state',
|
|
65
|
+
expected?: 'command' | 'event' | 'state' | 'query',
|
|
13
66
|
exampleData?: unknown,
|
|
14
67
|
) => { resolvedName: string; typeInfo: TypeInfo | undefined };
|
|
15
68
|
|
|
@@ -316,7 +369,7 @@ function processSingleWhen(
|
|
|
316
369
|
stateRef?: string;
|
|
317
370
|
exampleData?: unknown;
|
|
318
371
|
},
|
|
319
|
-
slice: { type: string },
|
|
372
|
+
slice: { type: string; request?: string },
|
|
320
373
|
resolveTypeAndInfo: TypeResolver,
|
|
321
374
|
messages: Map<string, Message>,
|
|
322
375
|
exampleShapeHints: ExampleShapeHints,
|
|
@@ -326,6 +379,23 @@ function processSingleWhen(
|
|
|
326
379
|
}
|
|
327
380
|
|
|
328
381
|
const originalCommandRef = when.commandRef;
|
|
382
|
+
|
|
383
|
+
// Check if this is a query action
|
|
384
|
+
// Query actions represent the act of executing the query - create a query message for them
|
|
385
|
+
if (detectQueryAction(originalCommandRef, slice)) {
|
|
386
|
+
log('DEBUG processSingleWhen: detected query action, creating query message:', originalCommandRef);
|
|
387
|
+
const { typeInfo } = resolveTypeAndInfo(originalCommandRef, 'query', when.exampleData);
|
|
388
|
+
const msg = createMessage(originalCommandRef, typeInfo, 'query');
|
|
389
|
+
const existing = messages.get(originalCommandRef);
|
|
390
|
+
if (!existing || preferNewFields(msg.fields, existing.fields)) {
|
|
391
|
+
messages.set(originalCommandRef, msg);
|
|
392
|
+
}
|
|
393
|
+
if (when.exampleData !== undefined) {
|
|
394
|
+
collectExampleHintsForData(originalCommandRef, when.exampleData, exampleShapeHints);
|
|
395
|
+
}
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
329
399
|
const expected = slice.type === 'command' ? 'command' : 'event';
|
|
330
400
|
|
|
331
401
|
log('DEBUG processSingleWhen:', {
|
|
@@ -418,7 +488,7 @@ function processCommandRefInArray(
|
|
|
418
488
|
stateRef?: string;
|
|
419
489
|
exampleData?: unknown;
|
|
420
490
|
},
|
|
421
|
-
slice: { type: string },
|
|
491
|
+
slice: { type: string; request?: string },
|
|
422
492
|
resolveTypeAndInfo: TypeResolver,
|
|
423
493
|
messages: Map<string, Message>,
|
|
424
494
|
exampleShapeHints: ExampleShapeHints,
|
|
@@ -428,6 +498,23 @@ function processCommandRefInArray(
|
|
|
428
498
|
}
|
|
429
499
|
|
|
430
500
|
const originalCommandRef = item.commandRef;
|
|
501
|
+
|
|
502
|
+
// Check if this is a query action (query name like ViewWorkoutPlan)
|
|
503
|
+
// Query actions represent the act of executing the query - create a query message for them
|
|
504
|
+
if (detectQueryAction(originalCommandRef, slice)) {
|
|
505
|
+
log('DEBUG processCommandRefInArray: detected query action, creating query message:', originalCommandRef);
|
|
506
|
+
const { typeInfo } = resolveTypeAndInfo(originalCommandRef, 'query', item.exampleData);
|
|
507
|
+
const msg = createMessage(originalCommandRef, typeInfo, 'query');
|
|
508
|
+
const existing = messages.get(originalCommandRef);
|
|
509
|
+
if (!existing || preferNewFields(msg.fields, existing.fields)) {
|
|
510
|
+
messages.set(originalCommandRef, msg);
|
|
511
|
+
}
|
|
512
|
+
if (item.exampleData !== undefined) {
|
|
513
|
+
collectExampleHintsForData(originalCommandRef, item.exampleData, exampleShapeHints);
|
|
514
|
+
}
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
|
|
431
518
|
const expected = slice.type === 'command' ? 'command' : 'event';
|
|
432
519
|
const { resolvedName, typeInfo } = resolveTypeAndInfo(originalCommandRef, expected, item.exampleData);
|
|
433
520
|
|
|
@@ -497,7 +584,7 @@ export function processWhen(
|
|
|
497
584
|
stateRef?: string;
|
|
498
585
|
exampleData?: unknown;
|
|
499
586
|
}>,
|
|
500
|
-
slice: { type: string },
|
|
587
|
+
slice: { type: string; request?: string },
|
|
501
588
|
resolveTypeAndInfo: TypeResolver,
|
|
502
589
|
messages: Map<string, Message>,
|
|
503
590
|
exampleShapeHints: ExampleShapeHints,
|
|
@@ -64,7 +64,7 @@ function tryMatchCandidates(candidates: TypeInfo[], dataKeys: Set<string>): stri
|
|
|
64
64
|
function tryResolveByExampleData(
|
|
65
65
|
candidates: TypeInfo[],
|
|
66
66
|
all: TypeInfo[],
|
|
67
|
-
expectedMessageType: 'command' | 'event' | 'state' | undefined,
|
|
67
|
+
expectedMessageType: 'command' | 'event' | 'state' | 'query' | undefined,
|
|
68
68
|
exampleData: unknown,
|
|
69
69
|
): string | null {
|
|
70
70
|
if (exampleData === null || typeof exampleData !== 'object' || exampleData === undefined) {
|
|
@@ -85,7 +85,7 @@ function tryResolveByExampleData(
|
|
|
85
85
|
|
|
86
86
|
function tryResolveByExpectedType(
|
|
87
87
|
candidates: TypeInfo[],
|
|
88
|
-
expectedMessageType: 'command' | 'event' | 'state' | undefined,
|
|
88
|
+
expectedMessageType: 'command' | 'event' | 'state' | 'query' | undefined,
|
|
89
89
|
): string | null {
|
|
90
90
|
if (!expectedMessageType || candidates.length === 0) {
|
|
91
91
|
return null;
|
|
@@ -98,7 +98,7 @@ function tryResolveByExpectedType(
|
|
|
98
98
|
function resolveFromCandidates(
|
|
99
99
|
candidates: TypeInfo[],
|
|
100
100
|
all: TypeInfo[],
|
|
101
|
-
expectedMessageType?: 'command' | 'event' | 'state',
|
|
101
|
+
expectedMessageType?: 'command' | 'event' | 'state' | 'query',
|
|
102
102
|
exampleData?: unknown,
|
|
103
103
|
): string | null {
|
|
104
104
|
if (candidates.length === 1) return candidates[0].stringLiteral;
|
|
@@ -112,7 +112,7 @@ function resolveFromCandidates(
|
|
|
112
112
|
export function resolveInferredType(
|
|
113
113
|
typeName: string,
|
|
114
114
|
flowTypeMap?: Map<string, TypeInfo>,
|
|
115
|
-
expectedMessageType?: 'command' | 'event' | 'state',
|
|
115
|
+
expectedMessageType?: 'command' | 'event' | 'state' | 'query',
|
|
116
116
|
exampleData?: unknown,
|
|
117
117
|
): string {
|
|
118
118
|
if (typeName !== 'InferredType' || flowTypeMap === undefined) return typeName;
|
package/src/types.ts
CHANGED
|
@@ -228,4 +228,9 @@ export type Event<
|
|
|
228
228
|
readonly kind?: 'Event';
|
|
229
229
|
};
|
|
230
230
|
|
|
231
|
+
export type Query<QueryType extends string, QueryData extends Record<string, unknown> = Record<string, unknown>> = {
|
|
232
|
+
type: QueryType;
|
|
233
|
+
data: QueryData;
|
|
234
|
+
};
|
|
235
|
+
|
|
231
236
|
export type ExtractStateData<T> = T extends State<string, infer Data, DefaultRecord | undefined> ? Data : never;
|